P4A framework: utilizzare il widget P4A Google Maps per implementare uno store locator

 

Questo post ha più di 1 anno. Il contenuto potrebbe essere obsoleto, non più completamente accessibile o mancante di alcune informazioni.

P4A esempio di Store LocatorEcco un esempio di utilizzazione del widget P4A Google Maps per creare una mappa di riferimenti (negozi, magazzini, ecc) che siano compresi in un certo raggio di distanza in Km, a partire da un indirizzo prestabilito.
Ho cercato di utilizzare il widget di Alberto Galanti senza effettuare nessuna modifica al codice originale. Ho scelto, quindi di implementare un metodo che fornisce la georeferenza della località che funge da centro, per poi effettuare, attraverso una query, la selezione degli indirizzi compresi nel raggio di distanze prestabilito.
L’esempio utilizzato, per comodità, è lo stesso indicato dalla guida di Google Maps in questo articolo.

I passi principali prima di procedere:

  • Scaricare il widget P4A Google Maps qui
  • Creare un archivio di riferimenti che abbiano un indirizzo nel formato accettato da Google Maps (Es.: “Piazza Venezia, Roma, Italia”)
  • Georeferenziare tutti gli indirizzi (trovare latitudine e longitudine)

Utilizzando MySQL, la struttura dell’archivio suggerita da Google è questa:

1
2
3
4
5
6
7
8
CREATE TABLE `markers` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(60) NOT NULL,
  `address` VARCHAR(80) NOT NULL,
  `lat` FLOAT(10,6) NOT NULL,
  `lng` FLOAT(10,6) NOT NULL,
  PRIMARY KEY  (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=latin1;

Per comodità potete scaricare l’sql completo dei dati di esempio di Google qui

Nel caso non si conoscano già le coordinate dei riferimenti, è possibile utilizzare il metodo suggerito in questo articolo sempre da Google. Questo sistema permette di aggiornare direttamente i dati di un database MySQL con le coordinate fornite da Google Maps per tutti gli indirizzi memorizzati.

La selezione dei record che sono compresi in un reggio di distanza in Km prefissato, avviene tramite una query che utilizza la formula della triangolazione sferica:
cos p = cos a cos b + sen a sen b cos φ,
per maggiori dettagli potete leggere questo post.

Ecco la query in pratica:

1
2
3
4
5
6
7
8
SELECT address, name, lat, lng, 
( 6371 * acos( cos( radians('Center_latitude')) * cos( radians( lat )) 
* cos( radians( lng ) - radians('Center_longitude')) 
+ sin( radians('Center_latitude'))
* sin( radians( lat )))) AS distance
FROM markers
HAVING distance < 'Radius'
ORDER BY distance

dove 'Center_latitude' è la latitudine del centro, 'Center_longitude' è la longitudine del centro e 'Radius' è il raggio in km dal centro.

Ed ecco dunque, il codice della maschera P4A:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
class GoogleMap extends P4A_Mask
{
  function GoogleMap()
  {
    parent::P4A_Mask();
    $this->setTitle("Google Map Store Locator Test");
    // Frame
    $this->build("p4a_frame", "frame");
    $this->frame->setWidth(800);
    //la chiave per localhost è disponibile nel file p4a_map.php
    $coordArray = $this->getLocationCoordinates("Mountain View, CA", 
                  "Inserire_chiave_Google"); 
    $radius = 12;
    // DB Source
    $this->build("p4a_db_source", "source");
    $query = "SELECT address, name, lat, lng, 
     CONCAT_WS('
', name, address) AS info,
     (6371 * acos(cos(radians('$coordArray[2]')) * cos(radians(lat))
     * cos( radians(lng) - radians('$coordArray[3]')) 
     + sin( radians('$coordArray[2]'))
     * sin( radians(lat)))) AS distance
     FROM markers
     HAVING distance < '$radius' ORDER BY distance"; $this->source->setQuery($query);
    $this->source->setPk("id");
    $this->source->load();
    // map
    $map =& $this->build("p4a_map","mappa");
    $map->setCenter("Mountain View, CA");
    $map->setZoom(10);
    $map->setWidth(750);
    $map->setHeight(500);
    // markers from datasource
    $map->setSourceAddressField("address");
    //$map->setSourceDescriptionField("distance");
    $map->setSourceDescriptionField("info");
    $map->setSource($this->source);
    // Layout
    $this->frame->anchorCenter($map);
    // Display
    $this->display("main", $this->frame);
   }
 /*
  * Return the coordinates of the location.
  * @param $location		valid Google Maps address of a location.
  * @param $googleMapKey		key for accessing Google Maps.	 
  * @return array  element[0] = status, element[2] = lat, element[3] = long
  */
 function getLocationCoordinates($location, $googleMapKey)
 {
  $base_url = "http://maps.google.com/maps/geo?output=csv&key=".$googleMapKey;
  $request_url = $base_url."&q=".urlencode($location);
  $csv = file_get_contents($request_url);
  if ($csv) {
    $csvSplit = split(",", $csv);
  }
  else{
    $csvSplit = array();
  }
    return $csvSplit;
  }
}

Note:

La query crea la sorgente dati per i markers, che vengono evidenziati automaticamente dalla classe p4a_map.
Al posto delle info, potete visualizzare la distanza di ogni marker dal centro (nell’esempio: “Mountain View, CA”) utilizzando l’istruzione: $map->setSourceDescriptionField("distance");.
Il metodo getLocationCoordinates() recupera le coordinate del centro per poter poi comporre la query.
La chiave per l’accesso a Google Map è quella valida per un server tipo “localhost” e la trovate all’interno del file: p4a_map.php che si trova nella directory libraries. Per avere quella per un dominio pubblico è necessario farla generare a Google Maps qui.
Non ho fatto controlli di errore, ad esempio per quanto riguarda la risposta del metodo getLocationCoordinates() sarebbe opportuno controllare che l’array non sia vuoto e anche lo status della risposta di Google Maps, e si dovrebbe anche limitare il risultato della query al numero di markers massimo gestiti dal widget (17 se non ricordo male…).

Download

Il widget con la maschera sopra descritta è scaricabile qui

Conclusioni:

Il risultato è già soddisfacente, anche se un po’ grezzo, ma credo ci siano molti margini di miglioramento per questo tipo di utilizzo di Google Maps in P4A. Spero di avere tempo per affinare ulteriormente sia questo programma che il widget, per renderlo utilizzabile anche per idee diverse! Suggerimenti e consigli sono molto ben accetti!!

Riferimenti ed approfondimenti:

Adobe Flex: programma per il calcolo delle distanze lossodromiche, ortodromiche ed altri parametri di navigazione

Gerardo MercatoreCon la scusa di dover imparare ad utilizzare Adobe Flex 3, ho cercato, invece del solito “Hello World”, di affrontare un esempio concreto e non troppo banale. Avendo già trattato l’argomento delle distanze geodetiche, ma solo sotto l’aspetto di circolo massimo che passa per due punti (ortodromia), ho voluto affrontare il problema anche sotto l’aspetto delle distanze lossodromiche, molto usate nella navigazione.
Chi va per mare, sa bene che cosa è una distanza lossodromica, per noi “terrestri” invece, la sola parola incute mistero. Per capire meglio l’arcano mi sono documentato e ho imparato che un geniale filosofo fiammingo del 1500, tal Gerardus Mercator allievo del matematico e cartografo Gemma Frisius, fu tra i primi a comprendere che una nave che segue sempre la stessa direzione indicata dalla bussola, non compie la distanza minore (circolo massimo) ma una curva denominata appunto lossodromica che appare come un’elica sferica sulla superficie del globo terrestre. Questa curva taglia i meridiani sempre con lo stesso angolo e questa è la differenza sostanziale rispetto all’ortodromia. Per facilitare il calcolo delle rotte. Mercator costruì una carta
(detta appunto di Mercatore) nella quale i meridiani sono rappresentati come linee rette tra loro parallele, dunque le curve lossodromiche sono pure rappresentate come linee rette mentre le ortodromiche vengono rappresentate come curve. Questa caratteristica la rende molto adatta al calcolo delle rotte per distanze brevi (minori di 500-1000 nm).

Fatta questa doverosa premessa, vediamo quali sono i parametri di navigazione più importanti per il calcolo della rotta:

  • Rv – Rotta vera. E’ l’angolo compreso tra la direzione del Nord e la traiettoria seguita dalla barca.
  • Percorso lossodromico in miglia – E’ la distanza percorsa dal punto di partenza e quello di arrivo tagliando i meridiani sempre con lo stesso angolo
  • Rotta iniziale quadrantale – E’ compresa in uno dei 4 quadranti, a partire da nord, oppure da sud ed è preceduta dal cardine N o S e seguita dal cardine E o W. Ha quindi un valore compreso tra 0° e 90°)
  • Percorso ortodromico in miglia – E’ l’arco di circolo massimo compreso tra i punti di partenza e di arrivo
  • Differenza percorso lossodromico-ortodromico

Il programma

screenshot del programma
screenshot del programma

Per la realizzazione del programma, ho utilizzato le formule che sono pubblicate in questo sito, in una una tabella sintetica. E’ anche possibile scaricare un foglio di calcolo Excel opportunamente predisposto all’utilizzo di queste formule. Tutti gli algoritmi che ho utilizzato nella classe che ho costruito, sono fedelmente ricavati da questo foglio di calcolo. Questo mi è stato molto utile perché mi sono potuto concentrare sul linguaggio di programmazione che per me era completamente nuovo. Inoltre ho potuto anche verificare la corrispondenza dei risultati.
Una caratterisitica di Adobe Flex è quella di mettere a disposizione un linguaggio di tag, derivato dall’XML per la rappresentazione
dell’interfaccia grafica che si chiama MXML, e un linguaggio vero e proprio di programmazione che si chiama Action script. Questo linguaggio è fortemente orientato agli oggetti perlomeno nella versione 3, ed è piuttosto rigoroso. Somiglia per alcuni aspetti a javascript e per molti altri a Java. La compilazione del progetto produce un file swf, che necessita per la sua esecuzione di una macchina virtuale (l’Actionscript Virtual Machine) questa è disponibile come plugin dei browser e si chiama Flash player. Con l’avvento di Adobe Air, è ora anche possibile creare applicazioni desktop (breve presentazione di AIR).

Potete usare il programma seguendo questo link (necessario il plugin Flash player). Il codice è disponibile facendo click con il tasto destro sull’applicazione, e selezionando dal menù la voce “view source”. Nella finestra che si apre, oltre all’esposizione del codice con syntax highlighting, è presente anche un link per poter scaricare l’intero progetto in formato compresso zip. Naturalmente, trattandosi della mia prima applicazione Flex, ci saranno sicuramente errori, …spero solo di forma! Per questo consiglio di scaricarvi anche il foglio di calcolo del navigatore Roberto Iori e fare qualche confronto sui risultati. Se trovate evidenti incongruenze, scrivetemi pure! Se invece le differenze sono molto piccole, questo può essere imputato a diversi fattori: gli arrotondamenti dei decimali, il modo in cui il processore numerico calcola i numeri in virgola mobile, ecc.

Conclusioni:

Ho cercato di unire l’utile al dilettevole, e ci sono riuscito! E’ stato dilettevole imparare cose nuove riguardo la navigazione, argomento davvero affascinante. E’ stato oltremodo utile iniziare a conoscere lo strumento di Adobe. La piattaforma è molto potente e soprattutto flessibile. Con l’ausilio di Adobe Flex Builder (che è una sorta di Eclipse modificato ad hoc) creare le interfacce grafiche con il drag’n’drop è molto semplice, inoltre l’ausilio del syntax highlighting e autocomplete fanno davvero comodo. Il builder di Adobe non è troppo costoso, soprattutto se paragonato ad altri prodotti rivali di casa Microsoft, costa 214,80 euro (Adobe Flex Builder 3 Standard). Poi, rinunciando alle comodità del builder, c’è anche a disposizione un SDK completamente gratuito.

Riferimenti ed approfondimenti:

Calcolare le distanze geodetiche fra comuni d’Italia in php

coordinate.pngIn un precedente articolo avevo pubblicato il codice di una funzione in ANSI C per il calcolo delle distanze sulla superficie terrestre. Poiché il C e il PHP sono due linguaggi dalla sintassi assai simile, ho deciso di fare la traduzione del codice. Inoltre, per dare un senso pratico al lavoro, ho trovato sul web l’archivio completo dei comuni italiani con le relative coordinate geografiche e su questo ho costruito un programmino PHP interattivo che permette di selezionare due comuni ed ottenere la relativa distanza geodetica, ovvero la lunghezza dell’arco massimo che unisce i due punti rappresentati dalle coordinate dei due comuni.
Va sottolineato che, come spiegato in questo ottimo articolo, per distanze molto piccole la formula che utilizzo fornisce un errore piuttosto grosso, quindi i risultati vanno presi come puramente indicativi.

Ecco il codice della funzione:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
function disgeod($latA, $lonA, $latB, $lonB){
  define ("R","6371");
  /* Converte i gradi in radianti */
  $lat_alfa = M_PI * $latA / 180;
  $lat_beta = M_PI * $latB / 180;
  $lon_alfa = M_PI * $lonA / 180;
  $lon_beta = M_PI * $lonB / 180;
  /* Calcola l'angolo compreso fi */
  $fi = abs($lon_alfa - $lon_beta);
  /* Calcola il terzo lato del triangolo sferico */
  $p = acos(sin($lat_beta) * sin($lat_alfa)
   + cos($lat_beta) * cos($lat_alfa) * cos($fi));
  /* Calcola la distanza sulla superficie terrestre R = ˜6371 Km */
  $d = $p * R;
  return($d);
}

E questa invece è la funzione che converte i gradi sessagesimali in decimali:

1
2
3
4
5
6
7
8
function deg2dec($d=0,$m=0,$s=0,$direction)
{
  $decimal=($d+($m/60)+($s/3600));
  //South latitudes and West longitudes need to return a negative result
  if (($direction=="S") or ($direction=="W"))
          { $decimal=$decimal*(-1);}
  return $decimal;
}

Per evitare di dover caricare le due select con tutti gli 8104 comuni, cosa che avrebbe rallentato il caricamento della pagina in modo improponibile, ho utilizzato uno script AJAX che provvede a popolare le select dinamicamente con i soli nomi dei comuni di una certa provincia preselezionata.

Potete provare il programma su questo link (Il programma è stato testato con Firefox 2 e con Internet Explorer 7)

Download

L’intero pacchetto (circa 1Mb) comprende anche l’archivio in formato sql, txt, xls e csv.

Conclusioni

Lo sviluppo di questo programmino è stato un esercizio interessante, che mi ha permesso di approfondire anche l’utilizzo e il funzionamento degli script AJAX sui quali credo che tornerò in futuro.

Riferimenti ed approfondimenti:

Calcolo della distanza geodetica tra due punti della superficie terrestre

La TerraDiversi anni fa, quando lavoravo al CNR, insieme a Bruno Fornari, un caro collega ed amico purtroppo scomparso, scrivemmo un programmino di “servizio” che utilizzammo per calcolare matrici di distanze terrestri (geodetiche).
Il programma è veramente semplice, più interessante è capire come si calcola la distanza tra due punti di una superficie sferica. Devo riconoscere che, al momento, ho avuto la sensazione che i miei studi scientifici non fossero serviti a molto, perché non avevo la più pallida idea di come si facesse! (Gli studi dell’OCSE-Pisa dicono il vero…) Per fortuna, i consigli di Bruno, fisico e scienziato di prim’ordine, mi hanno fornito la formula giusta.
Per il nostro scopo, non ci serviva una grande precisione, per cui l’assunto iniziale era che la terra fosse una sfera perfetta (in realtà non lo è, vi rimando ai link sotto per gli approfondimenti).
Dunque, osservando la figura in alto, diciamo che, in base alla trigonometria sferica (teorema di Eulero), tra i lati a, b e p del triangolo sferico ABP vale la relazione:

cos p = cos a cos b + sen a sen b cos φ

Ora, dette lat(A), lon(A), lat(B), lon(B), la latitudine e la longitudine dei punti A e B e, considerato che:

  • a = 90° – lat(B)
  • b = 90° – lat(A)
  • φ = lon(A)lon(B)

abbiamo tutti i dati per calcolare la lunghezza del lato p considerando il raggio della Terra approssimabile a R = 6371 km.

Ecco dunque il codice in linguaggio ANSI C:

dis_geod.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include 
/* Questa funzione calcola la distanza tra due punti 
sulla superficie terrestre, date le coordinate in
latitudine e longitudine espresse in
gradi decimali */
double disgeod (double latA, double lonA,
                double latB, double lonB)
{
      /* Definisce le costanti e le variabili */
      const double R = 6371;
      const double pigreco = 3.1415927;
      double lat_alfa, lat_beta;
      double lon_alfa, lon_beta;
      double fi;
      double p, d;
      /* Converte i gradi in radianti */
      lat_alfa = pigreco * latA / 180;
      lat_beta = pigreco * latB / 180;
      lon_alfa = pigreco * lonA / 180;
      lon_beta = pigreco * lonB / 180;
      /* Calcola l'angolo compreso fi */
      fi = fabs(lon_alfa - lon_beta);
      /* Calcola il terzo lato del triangolo sferico */
	  p = acos(sin(lat_beta) * sin(lat_alfa) + 
        cos(lat_beta) * cos(lat_alfa) * cos(fi));
      /* Calcola la distanza sulla superficie 
      terrestre R = ~6371 km */
      d = p * R;
      return(d);
}

Ed ecco il codice per il programma di interfaccia utente:

prodisge.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include 
#include "dis_geod.h"
double disgeod(double latA, double lonA, 
              double latB, double lonB);
void main (void)
{
	float latA, lonA, latB, lonB, distanza;
	printf("\n Inserisci la latitudine del punto A :\t");
	scanf ("%f", &latA);
	printf("\n Inserisci la longitudine del punto A :\t");
	scanf ("%f", &lonA);
	printf("\n Inserisci la latitudine del punto B :\t");
	scanf ("%f", &latB);
	printf("\n Inserisci la longitudine del punto B :\t");
	scanf ("%f", &lonB);
	distanza =  disgeod(latA, lonA, latB, lonB);
	printf("\n La distanza fra A e B e' : %f  km\n", distanza);
}

Ed ecco infine il risultato del calcolo della distanza fra Roma (lat: +41.91;, lon: +12.45) e Milano (lat: +45.48 lon: +09.18)

output del programma disgeod

Se provate il calcolo con altri programmi, potrete avere risultati un poco differenti, questo è dovuto alle approssimazioni usate per il raggio della Terra, per il valore di Π e per la conversione dei gradi sessagesimali in decimali.
Ovviamente, al programma originale fu aggiunto il codice necessario per costruire le matrici di distanze, che qui, per brevità, ho tagliato.

Download: (compilato per win32) prodisge.exe

Conclusioni:

Ormai, che i GPS ci parlano amichevolmente in auto, e Google Earth ci da tutte le informazioni che vogliamo con pochi click, programmini del genere sembrano della preistoria. Ma, a furia di avere sempre la “pappa pronta”, non ci annichiliremo un po’ troppo le meningi? Trovo che ogni tanto sarebbe bene fare qualche ripasso, tanto per capire anche come funziona ciò che si utilizza passivamente!

Riferimenti ed approfondimenti: