Van een producent van pennen kwam de vraag om een "vind de dichstbijzijnde winkel" optie in de site te bouwen. Het beheer van de lijst met winkels moet zo simpel mogelijk blijven. Daarom heb ik er voor gekozen dat de lijst met adressen aangeleverd wordt als een Excel sheet. Daarna worden met geocodeing via de Google Maps API de coordinaten bepaald, waarna we later de dichtstbijzijnde winkel kunnen berekenen in de database.
Voorbereidende stappen
De beheerder kan het sheet uploaden met een simpel formuliertje.
Met PHPExcel (van http://phpexcel.codeplex.com/) wordt het sheet uitgelezen en in de database geplaatst. De oude lijst met winkels wordt vooraf gewist.
De volgende stap is, om de locatie van de winkel te weten te komen. Het bepalen van de coordinaten in longitude en latitude is niet iets dat je wilt overlaten aan de beheerder van de site. Daarom wordt op basis van het adres aan Google maps gevraagd naar de coordinaten. Dit gebeurt eenmalig na het uploaden van de lijst.
In eerste instantie deed ik dat direct na het uploaden, maar met enkele tientallen winkels bleek dat proces toch al gauw een paar minuten te duren. Grote kans dus op "het lukt niet" reacties van de beheerders, of andere time-outs.
Daarom wordt aan het einde van het uploadscript een proces naar de achtergrond geschoten om deze taak verder af te ronden. De beheerder krijgt vervolgens een melding dat de upload gelukt is, en dat de gegevens over enkele minuten op de site beschikbaar zijn.
$command = ('/usr/bin/nohup /usr/bin/php ' . '../cli/adres2coord.php' .' > '. '/tmp/cli_adres2coord.'.time(). '.txt' .' & echo $!'); $PID = shell_exec($command);
met nohup wordt het php-proces in de achtergrond uitgevoerd, onafhankelijk van of het aanroepende script stopt of niet. Want normaal stop een child proces op een server, zodra de parent stopt.
Dit proces haalt uit de database alle adressen. Hierbij is ook het land van belang of zo volledig mogelijk te zijn richting Google.
Het process om een adres te converteren naar coordinaten wordt aangeduid met "geocoding". Google heeft een API beschikbaar (V2) die het toelaat om 15000 maal per dag een request te doen. Dat is meer dan voldoende voor onze lijst met winkels. Straks moet voor de bezoekers ook een locatie bepaald worden, maar we zouden erg vrolijk worden van 15000 bezoekers die hun dichtstbijzijnde verkooplocatie willen weten. Dat is dus meer dan genoeg.
Het request is simpel:
http://maps.google.com/maps/geo?q=de%20steeg%2015%20schimmert
UPDATE 19-6-2012:
Kennelijk heeft Google wat aangepast. Ofwel moet je voor bovenstaande aanroep ook een API-key meegeven (anders krijg je een foutcode 610 terug), ofwel moet je een andere aanroep gebruiken.
http://maps.googleapis.com/maps/api/geocode/json?address=De%20Steeg,+Schimmert&sensor=false
Mogelijk moet je daarop ook onderstaande regels om de longitude en latitude uit te lezen daarop aanpassen
/UPDATE
Hoe vollediger het adres, hoe nauwkeuriger je antwoord. Laat ik bijvoorbeeld de woonplaats weg, dan volgt er ook een antwoord, maar dat blijkt over een straat genaamd "Steeg" in het Duitse Swalmtal te gaan en niet over De Steeg in het Limburgse Schimmert.
De response is in JSON. Voor mijn toepassing is dat mooi. Het is ook mogelijk om dezelfde antwoorden in een XML bericht te ontvangen. In dat geval dien je &output=xml aan de url toe te voegen.
De data vraag ik op met
$data = json_decode(file_get_contents(sprintf($this->sUrlGoogleMaps, rawurlencode($this->sQuerystring))));
Als file_get_contents() geblokkeerd is op jouw server, dan zou de functie fsockopen() uitkomst moeten brengen. Het betreft een GET-request dus dat is tamelijk recht-toe-recht-aan.
Json_decode() verdeeld de inhoud mooi in een object. De coordinaten zijn daar vervolgens snel uit te halen:
$this->longitude = $data->Placemark[0]->Point->coordinates[0];
$this->latitude = $data->Placemark[0]->Point->coordinates[1];
Deze waarden gaan bij de winkel in de database en we kunnen gaan wachten op potentiële klanten die ons produkt zo dicht mogelijk bij hun huis willen gaan kopen.