Google Maps deve il suo successo a tantissimi fattori, non ultima la possibilità offerta agli sviluppatori di integrare servizi geolocalizzati nelle applicazioni Web, grazie alle API. Le API (Application Programming Interface) sono librerie che fanno da "ponte" tra la nostra applicazione e le funzionalità offerte dal sistema Google Maps.
In questo articolo prendiamo confidenza con i concetti basilari delle API e l'implementazione di base sui progetti Flex. Ci occuperemo di come integrare le API Google Maps e il relativo oggetto all'interno dei nostri progetti Flex.
Le API per Flex e Flash richiedono come minimo la versione 9 di Flash o la versione 3 di Flex Builder, oppure l'utilizzo dell'SDK di Flex. Quest'ultimo è open source e quindi completamente gratuito, a differenza degli altri due che sono prodotti commerciali. La versione di prova di Flex Builder 3 è liberamente scaricabile da qui.
Per potere utilizzare le API di Google all'interno del nostro progetto occorre seguire i seguenti passi:
- Richiedere la "Google Maps API Key"
- Scaricare l'SDK (Software Development Kit)
- Creare un nuovo progetto facendo riferimento alle librerie dell'SDK
- Sviluppare l'interfaccia
In questo articolo ci occuperemo di tutto questo in modo molto dettagliato.
Perché le API di Google Maps funzionino è necessario che l'applicazione abbia un accesso ad internet. Le mappe e anche le componenti stesse delle API, sono scaricate dal sito di Google.
Richiedere la Google Maps Key
Per utilizzare le API di Google, come in genere accade per i servizi Web, dobbiamo identificarci con un codice, detto chiave. Prima di effettuare la richiesta è molto importante ricordare che la chiave è strettamente collegata al dominio che ospiterragrave; l'applicazione.
È possibile richiedere la propria chiave, una volta letti e accettati i termini e delle condizioni di utilizzo.
Il processo di generazione della chiave è immediato. La chiave visualizzata dovrà essere copiata e conservata: essa sarà poi utilizzata all'interno del nostro progetto.
Scaricare l'SDK
Il secondo passo consiste nello scaricare l'SDK di Google Maps. Una volta ottenuto l'SDK, estraiamo l'archivio in una cartella locale. Andrà benissimo la cartella dove flex pone il suo workspace, che per default è:
Path del workspace per Mac
Users/[nome utente]/Documenti/Flex Builder 3/
Path del workspace per Windows
C:Documents and Settings[nome utente]My DocumentsFlex Builder 3
L'SDK contiene due cartelle: lib
, che contiene le librerie per Flex e per Flash e docs
, in cui è presente la documentazione delle API.
Le librerie, in realtà, sono delle interfacce ai metodi delle API e quindi non contengono il codice dei metodi che invece rimane online sul sito di Google Maps. Questo per noi è un vantaggio perché significa che tutte le variazioni o correzioni fatte ai metodi delle API da parte di Google, saranno per noi completamente trasparenti: questi metodi verranno sempre scaricati dal sito all'avvio della nostra applicazione. Ovviamente ciò vale solo per i metodi delle API il cui riferimento è presente nella libreria.
Qualora Google rendesse disponibili dei nuovi metodi, non inclusi nella libreria, per poterli utilizzare dovremo scaricare nuovamente l'SDK, aggiornare i riferimenti nel nostro progetto, modificare i sorgenti dove intendiamo utilizzare i nuovi metodi, ricompilare il tutto e quindi ripubblicare il nostro componente sul sito.
Creazione del progetto
Avviamo il Flex e selezioniamo la voce di menu File > New > Flex Project
. Dalla finestra che appare inseriamo "GoogleMapTest" nel campo Project Name
e selezioniamo Web Application
come tipo di applicazione. Infine clicchiamo su Next
.
Indichiamo la cartella per le applicazioni compilate (lasciamo pure quella predefinita).
Ora possiamo importare le librerie di Google nel nostro progetto Flex. Scegliamo anzitutto la tab Library Path
, poi clicchiamo su Add SWC...
.
Cerchiamo tra le cartelle quella in cui abbiamo salvato i file dell'SDK e infine selezioniamo la libreria per Flex map_flex_x_x.swc(x_x
cambia a seconda della versione scaricata).
Sviluppo dell'Interfaccia
Una volta creato il progetto, aggiungiamo un nuovo componente di tipo Canvas
(da menu File > New > MXML Component
), lo chiamiamo MapHosts
e impostiamo le dimensioni a 600 px in larghezza e 400 px in altezza.
Scegliamo di lavorare in modalit?esing utilizzando la tab in alto a sinistra:
Dal pannello Components (solitamente situato in basso a sinistra dell'ambiente di sviluppo), trasciniamo sullo stage un oggetto Map
, lo troviamo nella cartella Custom
. Appare un messaggio che ci segnala che non abbiamo ancora impostato la chiave di Google, non c'è da preoccuparsi: la inseriremo dopo.
Sul pannello Flex Properties, solitamente in basso a destra dell'ambiente di sviluppo, indichiamo le proprietà del componente: inseriamo map
nel campo id
e impostiamo la modalità ridimensionamento automatico.
Figura 10. Pannello delle propriet?p>
Una volta dimensionato il componente passiamo alla modalità Source e, finalmente inseriamo la chiave Google Maps nella proprietà key
dell'oggetto Map
.
<?xml version="1.0" encoding="utf-8"?>
<mx:Canvas xmlns:mx="http://www.adobe.com/2006/mxml" width="640" height="400" xmlns:ns1="com.google.maps.*">
<ns1:Map left="0" right="0" top="0" bottom="0"
id="map" key="[la nostra key]"/>
</mx:Canvas>
Abbiamo praticamente finito, non ci rimane che importare il componente nel resto del progetto. Apriamo la pagina principale in modalità Design e trasciniamo dal pannello componenti il componente MapHost
, che abbiamo creato.
Mandiamo in esecuzione il progetto. Se non abbiamo commesso errori possiamo già utilizzare la mappa: pur non avendo inserito nessun codice né impostato nessuna proprietà sull'oggetto, la nostra applicazione risponde già agli eventi del mouse. In particolare facendo doppio click sulla finestra, si aumenterà lo zoom corrente e, trascinando il mouse tenendo premuto il pulsante sinistro, possiamo scorrere la mappa.
L'oggetto Map
In questa parte dell'articolo ci occupiamo dell'oggetto principale delle API di Google Maps: Map. In particolare vedremo:
- il codice per creare l'oggetto Maps e personalizzarlo
- come navigare la mappa attraverso i controlli predefiniti
Creazione e personalizzazione della mappa
L'oggetto Map eredita da flash.display.Sprite
e quindi è direttamente inseribile nello stage o in un qualsiasi oggetto contenitore, proprio come un qualunque controllo Flex.
Nella filosofia object oriented, quando si intende personalizzare un oggetto, in genere, si sceglie tra due comportamenti (pattern):
- creare l'oggetto ereditando dall'oggetto che si vuole personalizzare (subclass)
- inserire una istanza dell'oggetto da personalizzare in un nuovo oggetto (wrap)
Solitamente si preferisce il primo schema quando l'oggetto da personalizzare è stato creato da noi o quando si conosce molto bene, si sceglie l'altro quando viene meno una di queste condizioni o quando si vuole controllare le proprietà da esporre dell'oggetto incorporato.
Scegliamo il secondo pattern e creaimo una classe che estenda Canvas
e incorpori una istanza di Map
.
L'oggetto Map possiede un solo costruttore, quello di default (senza parametri). Quindi nessuna personalizzazione delle mappe potrà essere effettuata sul costruttore; invece vedremo come utilizzare il metodo setInitOptions
. La chiamata a questo metodo, deve essere effettuata dopo che la mappa sia stata preinizializzata.
Come facciamo a sapere quando l'oggetto è stato preinizializzato? In Flex, tutti gli oggetti che richiedono un utilizzo intenso di risorse, siano esse locali o remote, agiscono in modalità asincrona. Un oggetto contenitore lancia l'azione di un oggetto figlio e non aspetta che essa venga conclusa. L'oggetto figlio procede con l'azione finché non l'ha terminata poi manda un segnale all'oggetto contenitore: un evento. Questo evento era stato associato ad un metodo detto "di callback", in cui viene stabilito il comportamento del contenitore al termine dell'azione del figlio.
In questa lezione useremo due funzioni di callback associate a due azioni dell'oggetto Map
: useremo la prima, per sapere quando l'oggetto è stato preinizializzato, la seconda per sapere quando l'inizializzazione è terminata. Questi sono due tra i più importanti eventi dell' oggetto map da intercettare.
Il codice
Veniamo adesso al codice. Creiamo un un nuovo progetto Flex di tipo Web Application e importiamo le librerie Google Maps. Successivamente creiamo una nuova classe (MapHostScript
) che estenda mx.containers.Canvas
ed impostiamo it.html.googlemaps
come nome di package.
L'ambiente di sviluppo ci prepara il codice:
package it.html.googlemaps { import mx.containers.Canvas; public class MapHostScript extends Canvas { public function MapHostScript() { super(); } } }
Ora aggiungiamo gli import
necessari nell'header della classe, all'interno delle dichiarazioni del package.
package it.html.googlemaps { // la classe base import mx.containers.Canvas; // import delle classi di Google import com.google.maps.LatLng; import com.google.maps.Map; import com.google.maps.MapEvent; import com.google.maps.MapOptions; import com.google.maps.MapType; import com.google.maps.controls.ControlPosition; import com.google.maps.controls.MapTypeControl; import com.google.maps.controls.MapTypeControlOptions; import com.google.maps.controls.PositionControl; import com.google.maps.controls.ZoomControl; import com.google.maps.interfaces.IControl; // import della nozione di Punto import flash.geom.Point;
Ora aggiungiamo alla classe una variabile privata di istanza di tipo Map e la chiamiamo _map
, che inizializziamo nel costruttore, subito dopo la chiamata al costruttore della classe base. Qui inseriamo nella proprietà key
il valore della chiave ottenuta da Google.
public class MapHostScript extends Canvas { // Variabile di istanza di tipo Map private var _map:Map // Costruttore public function MapHostScript() { super(); // istanziamo _map _map = new Map(); // impostiamo la chiave ottenuta da Google _map.key="ABQIAAAALKrOMe- etc"
È importante impostare da subito la chiave per ottenere la corretta inizializzazione dell'oggetto ed evitare errori come quello in figura:
Sempre nel costruttore, aggiungiamo la mappa come "child" della nostra classe. Questo passaggio è fondamentale, se ce ne dimenticassimo non avremmo nessuna mappa da visualizzare.
// aggiungiamo la mappa al contenitore
this.addChild(_map);
Subito dopo impostiamo il listener dell'evento MAP_PREINITIALIZE della mappa e stabiliamo che sarà gestito da una funzione di callback, chiamata mappaPreInizializzata
.
// aggiungiamo il gestore dell'evento MAP_PREINITIALIZE
_map.addEventListener(MapEvent.MAP_PREINITIALIZE,mappaPreIinizializzata);
Per convenienza, eliminiamo anche la politica di visualizzazione delle eventuali barre di scorrimento orizzontali e verticali dell'oggetto Canvas
. Infine, impostiamo la dimensione della nostra mappa con il metodo setSize
a 600x600 pixel, per questo ci serviamo della nozione Point
che abbiamo importato all'inizio.
// disabilitiamo gli scroll orizzontale e verticale del Canvas this.horizontalScrollPolicy = "off"; this.verticalScrollPolicy = "off"; // impostiamo la dimensione della mappa _map.setSize(new Point(600,600)); }
Finalmente chiudiamo il costruttore e scriviamo il gestore dell'evento di pre inizializzazione. Definiamo mappaPreInizializzata
come metodo privato che accetti come parametro un evento di tipo MapEvent
e non ritorni valori (void
).
Il callback viene lanciato dopo che Google ha verificato la nostra chiave e ci ha permesso di utilizzare le mappe. È in questo momento possiamo utilizzare il metodo setInitOptions, per inizializzare i parametri della mappa.
Infatti setInitOptions
accetta come parametro un oggetto del tipo MapOptions
, in cui specificare caratteristiche come la posizione iniziale, lo zoom o l'utilizzo di tastiera e mouse per la navigazione della mappa. L'elenco di tutte le proprietà lo troviamo nella documentazione dell'SDK.
Nel nostro esempio utilizziamo le caratteristiche che ci sembrano fondamentali per usare una mappa:
- center, di tipo
LatLng
; - zoom, di tipo numerico;
- mapTypes, di tipo
Array
; - mapType, che è un valore della enumerazione
MapType
.
La proprietà center serve per impostare la coordinate iniziali della mappa. Il suo costruttore accetta tre parametri: i primi due sono rispettivamente la latitudine e la longitudine del punto; il terzo parametro, facoltativo, è di tipo booleano (true
per default) ed indica se deve essere controllata l'esattezza dei due parametri precedenti.
I valori corretti per la latitudine sono compresi tra -90 e 90 gradi , mentre per la longitudione i valori devono essere compresi tra -180 e 180 gradi.
Attraverso la proprietà mapTypes, possiamo impostare i tipi di mappa con cui l'utente potrà interagire (Satellite, Fisica, Ibrida, Normale); gli elementi dell'array devono contenere, ognuno, uno dei valori della enumerazione MapType desiderati.
Utilizzeremo i valori di questa proprietà nell'oggetto MapTypeControl
, che permettera di scegliere il tipo di mappa da visualizzare.
La proprieta mapType indica il tipo di mappa inizialmente visualizzata.
Ecco il nostro metodo per l'inizializzazione della mappa:
// Callaback di preinizializzazione private function mappaPreInizializzata(event:MapEvent):void { // creiamo l'oggetto che conterrà le nostre impostazioni var options:MapOptions = new MapOptions(); // la posizione iniziale della mappa var posizioneIniziale:LatLng = new LatLng(37.3, 14.3); // lo zoom var zoom:Number = 7; // i tipi di mappe possibili del nostro oggetto var mapTypes:Array = new Array(); mapTypes[0] = MapType.PHYSICAL_MAP_TYPE; mapTypes[1] = MapType.SATELLITE_MAP_TYPE; mapTypes[2] = MapType.HYBRID_MAP_TYPE; mapTypes[3] = MapType.NORMAL_MAP_TYPE; // impostiamo le proprietà dell'oggetto options.center = posizioneIniziale; options.zoom = zoom; options.mapTypes = mapTypes; // tipo di mappa iniziale options.mapType = MapType.SATELLITE_MAP_TYPE; // ecco la chiamata al metodo di inizializzazione _map.setInitOptions(options); }
Incorporare gli oggetti di controllo predefiniti
Vediamo adesso come rendere navigabile la mappa (zoom, scorrimento etc.) attraverso l'uso degli oggetti predefiniti del tutto simili a quelle riscontrati GoogleEarth. Verificando sulla documentazione dell'SDK, l'oggetto Map
può contenere oggetti che ereditano da ControlBase
ed implementano l'interfaccia IControl
.
I controlli inseribili sono quelli appartenenti al package com.google.maps.controls
e che, ovviamente, implementano l'interfaccia IControl
. Questi oggetti sono:
- MapTypeControl
- NavigationControl
- OverviewMapControl
- PositionControl
- ScaleControl
- ZoomControl
Ognuno di questi controlli, può essere inserito, dopo averne creato una istanza, come child dell'oggetto Map utilizzando il metodo addControl.
L'inserimento potrà avvenire in uno dei seguenti modi :
- nel costruttore dell'oggetto
MapHostScript
- in una funzione di callback richiamata allo scatenarsi dell'evento
MAP_READY
- a runtime
Come e quando inserire questi controlli è chiaramente una scelta progettuale. Volendo, si potrebbero creare a priori, nasconderli con il metodo setVisible
e visualizzarli a richiesta con una CheckBox o un bottone.
Il costruttore di ognuno di questi oggetti, accetta come parametro l'oggetto inizializzatore corrispondente (null per default). Con questo parametro possiamo variare il punto di ancoraggio (in basso a destra piuttosto che in alto a sinistra), lo stile, la dimensione dei bottoni che lo compongono etc.
Vediamo come inserire i controlli alla nostra mappa. Dobbiamo farlo quando rileviamo l'evento MAP_READY, perciò torniamo al costruttore del nostro oggetto e aggiungiamo un listener per questo evento e un metodo di callback che chiamiamo mappaInizializzata
.
// aggiungiamo il listener per sapere quando la // mappa è stata completamente inizializzata _map.addEventListener(MapEvent.MAP_READY, mappaInizializzata);
Infine scriviamo il metodo mappaInizializzata.
// Callback dell'evento MapReady private function mappaInizializzata(event:MapEvent):void { // dimostriamo la personalizzazione per l'oggetto MapTypeControl var optionMapType:MapTypeControlOptions = new MapTypeControlOptions(); optionMapType.position = new ControlPosition(ControlPosition.ANCHOR_TOP_RIGHT); optionMapType.buttonSize= new Point(60,15); optionMapType.buttonSpacing = new Point(1,1); optionMapType.buttonAlignment = MapTypeControlOptions.ALIGN_VERTICALLY; // creiamo l'oggetto passando i parametri di personalizzazione nel costruttore var mapTypeControl:MapTypeControl=new MapTypeControl(optionMapType); // aggiungiamo il controllo alla lista dei controlli dell mappa _map.addControl(mapTypeControl); // inseriamo gli altri oggetti inizializandoli con il costruttore di default // zoom Control var mapZoomControl:ZoomControl=new ZoomControl(); _map.addControl(mapZoomControl); // Position Control var mapPositionControl:PositionControl=new PositionControl(); _map.addControl(mapPositionControl); }
Abbiamo finito: possiamo testare il nostro componente. Selezioniamo il file principale della nostra applicazione e impostiamo la modalità Design. Dal pannello componenti, dovremmo vedere il nostro componente MapHostScript
(se così non fosse, effettuare un build del progetto e riprovare), selezioniamolo e trasciniamolo sulla pagina.
Posizioniamo il controllo a piacimento oppure impostiamo le proprietà x
e y
sul pannello proprietà.
All'avvio del progetto, se non ci sono errori, dovremmo vedere le mappa di tipo Satellite
con al centro la Sicilia.
Conclusioni
Google ha fatto le cose in grande: infatti sono disponibili, oltre al controllo Map
, una serie di controlli accessori che possono "arricchire" la nostra mappa (d'altra parte questi controlli sono già disponibili nella versione Ajax delle API Google Maps). L'utilizzo dettagliato di questi controlli sar?ggetto di ulteriori approfondimenti.
In questo articolo introduttivo abbiamo preferito un approccio semplicistico alle API per ragioni didattiche. Un progetto "serio" prevedrebbe la creazione di classi che incapsulino l'oggetto Map di Google o derivate direttamente da esso, esponendo le proprietà più importanti dell'oggetto come proprietà pubbliche, possibilmente compatibili con il binding di Flex, e con il dispatch di eventi custom.