Exibir e ocultar marcadores do Google Maps em Adobe Flex

Recentemente no novo projeto da equipe tivemos a necessidade de criar um mapa para plotar uma série de elementos geo referenciados. Para tanto utilizamos a API do Google Maps para Flash. Uma das funcionalidades desenhadas foi a de exibir e ocultar os elementos de acordo com alguma característica.
O conceito utilizado na ferramenta foi de trazer todos os elementos e carregá-los em memória através de uma variável XML e em seguida criar os elementos sobre o mapa de acordo com as características, mudando por exemplo o ícone. Porém ao pressionar o botão para ocultar um tipo de elemento gostaríamos de resgatar os marcadores do mapa daquele tipo de elemento específico e aplicar um visible = false.
Depois de muito buscar, aparentemente não existe um método que retorne diretamente da classe Map os marcadores criados sobre ele. Dessa forma a solução seria “limpar” todos os marcadores do mapa e voltar a populá-los, aplicando filtros sobre o xml para que  apenas determinados elementos fossem plotados. A solução não pareceu ser a melhor, pois requer um certo nível de processamento cada vez que se solicite uma atualização.

Depois de um pouco de pesquisas na net, encontrei a solução que descreverei abaixo.

Vamos considerar como fonte de dados o seguinte arquivo xml:

<ListaClientes>
<Cliente nome='Sabor Mineiro' tipo='Restaurante' lat='-23.506779' lon='-46.647123'/>
<Cliente nome='Casa da Massa' tipo='Restaurante' lat='-23.527674' lon='-46.668667'/>
<Cliente nome='Tanque Cheio' tipo='Posto de Gasolina' lat='-23.505392' lon='-46.623842'/>
<Cliente nome='Posto da Freguesia' tipo='Posto de Gasolina' lat='-23.486505' lon='-46.695907'/>
<Cliente nome='Última opção' tipo='Posto de Gasolina' lat='-23.528078' lon='-46.639978'/>
<Cliente nome='Banco do Porquinho' tipo='Banco' lat='-23.504796' lon='-46.606681'/>
<Cliente nome='Colchão Cheio' tipo='Banco' lat='-23.477944' lon='-46.605554'/>
<Cliente nome='Árvore do Dinheiro' tipo='Banco' lat='-23.534688' lon='-46.673334'/>
</ListaClientes>

No nosso exemplo estaremos plotando sobre o gráfico clientes de uma determinada empresa onde esses clientes são agrupados com uma categoria tipo, que classifica os clientes de acordo com sua atividade.

Ao invés de apenas consumir o xml e agregar os marcadores sobre o mapa, vamos guardar em memória as instâncias dos marcadores, para que os mesmos possam ser acessados futuramente. Para isso criamos um Array do tipo Object onde serão armazenados grupos de tipos de clientes. Neste exemplo possuímos três grupos de clientes: Restaurantes, Postos de Gasolina e Bancos. Cada posição do array armazenará duas informações: a cor do marcador para a categoria e um array de marcadores. Dessa forma teremos um array dentro de outro, ou seja, um conjunto de marcadores dentro de um conjunto de tipos de clientes. Assim ficou nosso array:

private var categorias:Object =
{"Restaurante": {
  "color": 0xFF0000,
  "markers": []},
 "Posto de Gasolina": {
  "color": 0x0000FF,
  "markers": []},
 "Banco": {
  "color": 0x00FF00,
  "markers": []}
};

Após consumir o xml, criamos um laço para percorrer todas as tags e para cada uma invocar o seguinte método, onde serão criados os marcadores e estes adicionados ao array de categorias:

public function createMarker(latlng:LatLng, nome:String, tipo:String):void {
   var markerOptions:MarkerOptions = new MarkerOptions({});
   var fillStyle:FillStyle = new FillStyle({color: categorias[tipo].color});
   markerOptions.fillStyle = fillStyle;
    
   var marker:Marker = new Marker(latlng, markerOptions);
   var html:String = "<b>" + nome + "</b>";
   marker.addEventListener(MapMouseEvent.CLICK, function(e:MapMouseEvent):void {
      marker.openInfoWindow(new InfoWindowOptions({contentHTML:html}));
   });
   categorias[tipo].markers.push(marker);
   map.addOverlay(marker);
}

Para definir a cor dor marcadores, por exemplo, acessamos a variável “color” do grupo de categoria específico dessa forma: “categorias[tipo].color” e após criar o marcador, sua instência é armazenada no array dessa forma: “categorias[tipo].markers.push(marker)”

Ao final definimos uma série de botões do tipo Toogle para que ao clicar seus respectivos tipos de clientes sejam ocultos ou exibidor. Para isso resgatamos o array de marcadores do grupo de categoria específica, percorremos os marcadores e setamos cada um como visible true ou false.

private function toggleCategory(type:String):void {
   for (var i:Number = 0; i < categorias[type].markers.length; i++) {
      var marker:Marker = categorias[type].markers[i];
      if (!marker.visible) {
         marker.visible = true;
      } else {
         marker.visible = false;
      }
   }
}

A seguir o código completo do Application:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="<a href="http://www.adobe.com/2006/mxml">http://www.adobe.com/2006/mxml</a>" layout="absolute" xmlns:ns1="com.google.maps.*">

 <mx:Script>
  <![CDATA[
   import com.google.maps.MapType;
   import com.google.maps.InfoWindowOptions;
   import com.google.maps.overlays.Marker;
   import com.google.maps.styles.FillStyle;
   import com.google.maps.overlays.MarkerOptions;
   import com.google.maps.MapMouseEvent;
   import com.google.maps.controls.ZoomControl;
   import com.google.maps.LatLng;
   import com.google.maps.MapEvent;
   import mx.controls.Alert;
   import mx.rpc.events.FaultEvent;
   import mx.rpc.events.ResultEvent;
   import com.google.maps.Map;
  
   private var map:Map;
   private var xmlPredios:XML;
   
   private var categorias:Object =
   {  "Restaurante": {
     "color": 0xFF0000,
     "markers": []},
    "Posto de Gasolina": {
     "color": 0x0000FF,
     "markers": []},
    "Banco": {
     "color": 0x00FF00,
     "markers": []}
   };

   
   public function onCanvasMapInitialize(event:Event):void {
          map = new Map();
          map.percentHeight = 100;
          map.percentWidth = 100;
          map.key = "xxxxxxxxxxxxxx";
          map.addEventListener(MapEvent.MAP_READY, onMapReady);
          cvsMapa.addChild(map);
   }
   
   private function onMapReady(event:Event):void {
          map.enableScrollWheelZoom();
          map.enableContinuousZoom();
          map.setCenter(new LatLng(-23.516066, -46.643229), 12,MapType.NORMAL_MAP_TYPE);
          map.addControl(new ZoomControl());
         
          carregarXml();
   }
   
   private function carregarXml():void {
    var xmlString:URLRequest = new URLRequest("xml_clientes.xml");
       var xmlLoader:URLLoader = new URLLoader(xmlString);
          xmlLoader.addEventListener("complete", consumirXml);

   }

   public function consumirXml(event:Event):void {
    var xmlAgencias:XML = new XML(event.target.data);
          var markers:XMLList = xmlAgencias.Cliente;
          var markersCount:int = markers.length();
         
          for (var i:Number = 0; i < markersCount; i++) {
              var marker:XML = markers[i];
              var name:String = <a href="mailto:marker.@nome">marker.@nome</a>;
              var type:String = <a href="mailto:marker.@tipo">marker.@tipo</a>;
              var latlng:LatLng = new LatLng(<a href="mailto:marker.@lat">marker.@lat</a>, <a href="mailto:marker.@lon">marker.@lon</a>);
              createMarker(latlng, name, type);
          }
      }
  
   public function createMarker(latlng:LatLng, name:String, type:String):void {
    var markerOptions:MarkerOptions = new MarkerOptions({});
    var fillStyle:FillStyle = new FillStyle({color: categorias[type].color});
    markerOptions.fillStyle = fillStyle;
       
    var marker:Marker = new Marker(latlng, markerOptions);
    var html:String = "<b>" + name + "</b>";
    marker.addEventListener(MapMouseEvent.CLICK, function(e:MapMouseEvent):void {
     marker.openInfoWindow(new InfoWindowOptions({contentHTML:html}));
    });
    categorias[type].markers.push(marker);
    map.addOverlay(marker);
   }

   private function toggleCategory(type:String):void {
    for (var i:Number = 0; i < categorias[type].markers.length; i++) {
     var marker:Marker = categorias[type].markers[i];
     if (!marker.visible) {
      marker.visible = true;
     } else {
      marker.visible = false;
     }
    }
   }
   
   
  ]]>
 </mx:Script>
 
 <mx:Button x="10" y="10" label="Restaurantes" toggle="true" selected="true" click="toggleCategory('Restaurante')"/>
 <mx:Button x="121" y="10" label="Postos de Gasolina" toggle="true" selected="true" click="toggleCategory('Posto de Gasolina')"/>
 <mx:Button x="262" y="10" label="Bancos" toggle="true" selected="true" click="toggleCategory('Banco')"/>
 
 <mx:Canvas id="cvsMapa" initialize="onCanvasMapInitialize(event)" bottom="10" top="40" left="10" right="10">
 
 </mx:Canvas>
 
</mx:Application>