Guest post: Sviluppare e testare applicazioni geolocalizzate con GPS Emulator – Seconda Parte

Questa è la seconda parte dell’articolo scritto da Matteo Pagani, MVP Device Application Development.

Dopo l’introduzione vista nell’articolo precedente, siamo pronti ora per realizzare una semplice applicazione che fa uso dei servizi di geolocalizzazione e che andremo a testare utilizzando GPS Emulator.

Location

In questo esempio utilizzeremo i servizi di localizzazione per mostrare a video una serie di informazioni sulla nostra posizione, ovvero:

· Lo stato del segnale

· Data e ora della rilevazione

· Latitudine

· Longitudine

· L’indirizzo

La vista che andremo a definire in questo progetto è molto semplice: si tratta infatti di una serie di TextBlock, inseriti all’interno di una griglia, in cui andremo a valorizzare le informazioni recuperate utilizzando l’oggetto di tipo GeoCoordinateWatcher. La prima colonna contiene un etichetta di riferimento, mentre la seconda il valore vero e proprio.

         <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
             <Grid>
                 <Grid.ColumnDefinitions>
                     <ColumnDefinition Width="240" />
                     <ColumnDefinition Width="240" />
                 </Grid.ColumnDefinitions>
                 <Grid.RowDefinitions>
                     <RowDefinition Height="50" />
                     <RowDefinition Height="50" />
                     <RowDefinition Height="50" />
                     <RowDefinition Height="50" />
                     <RowDefinition Height="50" />
                     <RowDefinition Height="50" />
                 </Grid.RowDefinitions>
                 <TextBlock Text="Status" Grid.Column="0" Grid.Row="1" />
                 <TextBlock x:Name="txtStatus" Grid.Column="1" Grid.Row="1" />
                 <TextBlock Text="Timestamp" Grid.Column="0" Grid.Row="2" />
                 <TextBlock x:Name="txtTimeStamp" Grid.Column="1" Grid.Row="2" />
                 <TextBlock Text="Latitude" Grid.Column="0" Grid.Row="3" />
                 <TextBlock x:Name="txtLatitude" Grid.Column="1" Grid.Row="3"/>
                 <TextBlock Text="Longitude" Grid.Column="0" Grid.Row="4" />
                 <TextBlock x:Name="txtLongitude" Grid.Column="1" Grid.Row="4" />
                 <TextBlock x:Name="txtAddress" Grid.ColumnSpan="2" Grid.Row="5" />
             </Grid>
         </Grid> 

Veniamo ora al codice sorgente vero e proprio della vista. Innanzitutto, come visto in precedenza, definiamo e istanziamo, in fase di inizializzazione della pagina, il nostro oggetto di tipo GeoCoordinateWatcher in base alla configurazione che stiamo utilizzando.

         IGeoPositionWatcher<GeoCoordinate> watcher;
  
         public Location()
         {
             InitializeComponent();
             
 #if DEBUG
             watcher = new GpsEmulatorClient.GeoCoordinateWatcher();
 #else
             watcher = new System.Device.Location.GeoCoordinateWatcher();
 #endif
             watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
             watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
 );
         }

Fin qui non dovrebbe esserci nulla di nuovo: abbiamo inizializzato l’oggetto e ci siamo sottoscritti ai due eventi PositionChanged e StatusChanged. Vediamo cosa succede ogni qualvolta lo stato dei servizi di localizzazione cambia:

         void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
         {
             txtStatus.Text = e.Status.ToString();
         }

Quello che facciamo è molto semplice: tra gli argomenti di ritorno dell’evento c’è un oggetto di tipo GeoPositionStatusChangedEventArgs, che contiene lo stato corrente del segnale GPS. Quello che facciamo è semplicemente visualizzare nel TextBlock di nome txtStatus il valore dello stato.

Vediamo invece cosa succede quando è la posizione rilevata dall’oggetto a cambiare:

 void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
         {
             txtTimeStamp.Text = e.Position.Timestamp.ToString();
             txtLatitude.Text = e.Position.Location.Latitude.ToString();
             txtLongitude.Text = e.Position.Location.Longitude.ToString();
         }

Anche in questo caso l’evento ha un argomento di ritorno, questa volta di tipo GeoPositionChangedEventArgs, che contiene una proprietà Position, la quale definisce:

·         Una proprietà DateTime di nome Timestamp, con data e ora della rilevazione.

·         Una proprietà Location di tipo GeoCoordinate, che contiene diverse informazioni sulla posizione rilevata. Oltre a latitudine e longitudine, troviamo parametri quali altezza, velocità corrente, precisione, ecc.

In questo esempio, ne ignoriamo la maggior parte e ci limitiamo a visualizzare a video, associandole ai rispettivi TextBlock, latitudine, longitudine e data e ora della rilevazione.

Manca solo una cosa: far partire l’oggetto di tipo GeoCoordinateWatcher, affinchè inizi ad acquisire i dati dal GPS. Per farlo, dobbiamo eseguire il metodo Start esposto dalll’oggetto watcher. Sta a noi definire quando vogliamo eseguirlo: nel progetto di esempio che troverete allegato all’articolo ho inserito un pulsante nell’application bar che, al click, esegue il metodo.

Siamo pronti per testare la nostra applicazione. Portiamoci nella cartella in cui abbiamo scompattato il GPS Emulator (ricordiamoci di ricompilare il progetto, affinchè venga applicata la modifica per utilizzare il corretto separatore dei decimali) ed eseguiamoli con permessi amministrativi (questo perchè l’applicazione apre un canale HTTP con l’emulatore tramite un servizio WCF).

Ecco come apparirà: 
  image 

Ora lanciamo la nostra applicazione e facciamo partire l’acquisizione dati (premendo il pulsante Start nell’application bar): se tutto è andato a buon fine, lo stato dell’applicazione (mostrato nell’angolo in basso a destra) passerà da No client connected (acompagnato da un semaforo rosso) a Client connected (accompagnato da un semaforo verde).

A questo punto, clicchiamo in un punto qualsiasi della mappa, posizionando così il cursore blu: lo stato del ricevitore GPS, mostrato nella nostra applicazione, passerà a Ready e le etichette con timestamp, latitudine e longitudine verranno aggiornate con le coordinate selezionate.

Possiamo mettere alla prova la nostra semplice applicazione definendo un percorso: per farlo, ci basta definire una serie di waypoint utilizzando il pulsante Add point. Ricordiamoci, prima di impostarlo, di definire alla sezione Time dopo quanto tempo rispetto al punto precedente si vuole che il nostro GPS virtuale passi per quel punto.

A questo punto, definito il percorso premiamo il pulsante Start nella sezione Simulation: il segnale blu inizierà a spostari seguendo il nostro percorso e nella nostra applicazione vedremo le varie informazioni aggiornarsi di volta in volta, in base alla posizione del segnale.

image

Come risolviamo l’indirizzo?

Vedere la nostra applicazione geolocalizzata prendere vita è stato sicuramente divertente: le informazioni che però abbiamo visualizzato a video in molti casi sono inutili, dato che spesso e volentieri abbiamo bisogno di tradurre le coordinate geografiche in un indirizzo più facilmente comprensibile.

Il framework ci mette a disposizione un classe chiamata CivicAddressResolver che serve proprio a questo: c’è però un piccolo problema al momento. Tale classe è disponibile tra le API di Windows Phone, ma non è attiva: questo significa che qualsiasi coordinata cercheremo di tradurre otterremo un indirizzo vuoto con la proprietà IsUnknown valorizzata a True.

Per ovviare a questo inconveniente, possiamo utilizzare uno dei servizi messi a disposizione da Bing per gli sviluppatori. Nello specifico, utilizzeremo un servizio che, tra le altre cose, ci da proprio la possibilità, data una coordinata geografica, di ottenere un indirizzo comprensibile.

Per farlo, ci basta fare clic con il tasto destro sul nostro progetto, scegliere Add Service Reference e aggiungere il servizio disponibile all’URL

http://dev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc

Diamo un nome al nostro servizio (ad esempio GeoCodeService) e diamo OK: Visual Studio genererà la classe proxy che ci permetterà di utilizzarlo all’interno del nostro progetto.

Una volta completata l’operazione, siamo pronti per chiamare il servizio ogni qualvolta la posizione restituità dall’oggetto di tipo GeoCoordinateWatcher viene aggiornata. Ecco perciò che aggiungiamo all’evento watcher_PositionChanged visto in precedenza le seguenti righe di codice:

     GeocodeServiceClient client = new GeocodeServiceClient("BasicHttpBinding_IGeocodeService");
             ReverseGeocodeRequest request = new ReverseGeocodeRequest
                                                 {
                                                     Location = new GeoCodeService.Location
                                                         {
                                                             Latitude = e.Position.Location.Latitude,
                                                             Longitude = e.Position.Location.Longitude
                                                         },
                                                     Credentials = new Credentials
                                                                       {
                                                                           ApplicationId = bingMapKey
                                                                       }
                                                 };
             client.ReverseGeocodeCompleted += new EventHandler<ReverseGeocodeCompletedEventArgs>(client_ReverseGeocodeCompleted);
             client.ReverseGeocodeAsync(request);

Innanzitutto definiamo una nuova istanza del GeocodeServiceClient, ovvero il client generato da Visual Studio per comunicare con il servizio. Dato che quando aggiungiamo il servizio vengono configurate automaticamente diversi tipi di binding WCF, andiamo a specificare quale di queste vogliamo usare (nel nostro caso, BasicHttpBinding che è supportato da Windows Phone).

Dopodichè definiamo la richiesta che vogliamo inviare al servizio, istanziando un oggetto di tipo ReverseGeocodeRequest. Tale richiesta conterrà le seguenti informazioni:

·         Le coordinate del luogo che vogliamo convertire in un indirizzo: in questo caso, facciamo un’assegnazione manuale dato che l’oggetto ritornato come argomento del metodo PositionChanged è di tipo GeoCoordinate, mentre la proprietà che identifica la posizione esposta dal servizio è di tipo Location.

·         Una API key valida per l’utilizzo del servizio: si tratta di una chiave univoca che ci autorizza a chiamare il servizio. Nella prima parte di questo articolo abbiamo visto come ottenerla.

Infine, ci sottoscriviamo all’evento ReverseGeoCodeCompleted, che viene invocato nel momento in cui il servizio ha convertito le coordinate geografiche in un indirizzo.

Per eseguire il metodo vero e proprio chiamiamo il metodo ReverseGeocodeAsync, passando come parametro la richiesta di tipo ReverseGeocodeRequest creata in precedenza.

Vediamo ora il codice che viene eseguito nell’evento scatenato una volta che il servizio ha risolto l’indirizzo:

         void client_ReverseGeocodeCompleted(object sender, ReverseGeocodeCompletedEventArgs e)
         {
             if (e.Error == null && e.Result.Results.Count>0)
                 txtAddress.Text = e.Result.Results.FirstOrDefault().DisplayName;
         }

Se non ci sono errori e se l’oggetto Results contiene almeno un risultato, allora valorizziamo il TextBlock con il DisplayName, ovvero la visualizzazione estesa dell’indirizzo (comprensivo di via, numero civico, cap e città). Se abbiamo bisogno di maggiore granularità è a disposizione la proprietà Address, che definisce una serie di attributi più specifici, come il CAP, la città, il numero civico, ecc.

Rilanciamo ora l’applicazione e facciamo partire nuovamente l’acquisizione dati: selezioniamo un punto sul GPS Emulator (oppure facciamo partire la simulazione di un percorso) e potremo vedere come questa volta verrà visualizzato anche l’indirizzo corrispondente alla posizione scelta.

image 

Qualche indicazione sul progetto di esempio

Questo articolo è accompagnato da un progetto di esempio, che vi mostrerà in concreto quello di cui abbiamo parlato. Vi riporto alcune informazioni importanti:

· Il progetto implementa un altro scenario, di cui non abbiamo parlato nell’articolo, ma che sfrutta gli stessi servizi e la stessa architettura: un controllo Bing Maps per Windows Phone, nel quale andiamo a inserire un segnaposto in base alla posizione restituita da GPS Emulator.

· Il progetto include già GPS Emulator (ricompilato con il fix per i numeri decimali nelle coordinate geografiche) e la libreria GPSEmulatorClient.

· Nel file App.xaml trovate dichiarata una risorsa di tipo string identificata dalla chiave BingMapKey: qui dovrete inserire la Bing API Key che avete ottenuto sul portale sviluppatori di Bing. In automatico, verrà utilizzata in tutti i punti dove è necessaria.