Online-Kartendienste gehören zu den Standard-Accessoires von Anwendern, die Dienstreisen, Wanderungen und Ausflüge planen oder abrechnen. Dieser Artikel zeigt, wie Programmierer die Karten-APIs von Google und Open Streetmap in Javascript-Dateien einbinden, die der Webserver ausliefert.
Reisekosten, Wanderung oder Fahrradtour: Heute organisiert der IT-kundige Anwender derlei mit Google Maps oder Open Streetmap. Die APIs der beiden Marktführer machen es Entwicklern leicht, das verfügbare Kartenmaterial in eigene Apps oder Webseiten zu integrieren. Ein einfaches HTML-Gerüst mit klickbarer Karte dient diesem Artikel als Beispiel für eine Entfernungsberechnung, wie sie mobile Nomaden etwa für die monatliche Spesenabrechnung brauchen.
Der augenfälligste Unterschied zwischen den Kartendiensten liegt zunächst im rechtlichen Rahmen: Google verwirrt mit unübersichtlichen Bedingungen. Die Rechte an den Karten und Luftbildern sind vielfältig, bestimmte Nutzungen kosten Geld, und Google behält es sich vor, die übertragenen Daten zu nutzen [1]. Open Streetmap verspricht eine “freie, editierbare Karte der gesamten Welt, die von Menschen wie Dir erstellt wird” [2]. Freie Software, die die Daten nutzt, gibt es zuhauf – fürs mobile Gerät bietet sich als Alternative zu Google Maps etwa Navfree [3] an, das OSM-Karten auch offline auf den Androiden bringt.
Wer die eigene Webseite mit Karten ausstatten will, greift auf die Javascript-Client-APIs der Dienste zu. Die kleinen Skripte laufen im Browser und benötigen meist keine Serverkomponente, abgesehen vielleicht von einem sinnvollen HTTP-Proxy für externe Dienste.
Dieser Artikel vergleicht die beiden Ansätze, das Google-Maps-API [4] und den Zugriff auf Open Streetmap durch das Open-Layers-API [5] anhand einer kleinen Webanwendung, die Fahrzeit und Distanz zwischen zwei Orten ermittelt. Mit dem Kartendienst definiert der Anwender Anfangs- und Endpunkt der Reise durch simple Mausklicks auf einer Karte, eingebettet in die HTML-Datei aus Listing 1.
Listing 1
template-index.html
01 <head> 02 <script src="./map.js"></script> <!-- hier die Skripte einbinden --> 03 <body onload="initialize()"> 04 <div id="mapPanel" style="width: 75%; height: 100%; float: left;"></div> 05 <div id="routePanel" style="width: 20%; height: 100%; float: left; padding-left: 10px;"> 06 <h3>Startpunkt</h3> <div id="routeStart">-</div> 07 <h3>Endpunkt</h3> <div id="routeEnd">-</div> 08 <h3>Distanz</h3> <div id="routeLength">-</div> 09 <h3>Zeit</h3> <div id="routeTime">-</div> 10 </div>
Google Maps
Die zweite Zeile dieses Template bindet die benötigte Javascript-Bibliothek »map.js« ein, die den eigentlichen Zugriff auf die APIs abwickelt. Listing 2 zeigt den benötigten Verweis auf Googles Dienst: Dabei reicht eine Zeile, wobei der Parameter »v=3.10« die Version des API festlegt und so spätere Überraschungen vermeiden hilft. »sensor=false« gibt an, dass der Client keinen Sensor zur eigenen Ortsbestimmung verwendet.
Listing 2
google.script
01 <script src="https://maps-api-ssl.google.com/maps/api/js?v=3.10&sensor=false"></script>
Im »body« der HTML-Datei folgen Bereiche für Karte und Text. Der Javascript-Teil wird später anhand der »id« -Tags erkennen, wo er seinen Inhalt einfügen soll. Beim ersten Aufruf startet der Browser die Funktion »initialize()« aus »map.js« (Listing 3). In Zeile 12 ruft die Funktion »google.maps.Map()« mit entsprechenden »mapOptions« auf. Der Benutzer erhält so im Kartenbereich der Webseite eine auf Deutschland zentrierte Karte vom Typ »ROADMAP« – der Standard-Straßenansicht (Abbildung 1). Neben den Kartentypen »SATELLITE« , »HYBRID« und »TERRAIN« lassen sich hier auch diverse eigene Stile verwenden ([6], [7]).
Listing 3
map.js (Google)
01 var map;
02 var markers;
03
04 function initialize() {
05 markers = [];
06
07 var mapOptions = { zoom: 5,
08 center: new google.maps.LatLng(52, 8),
09 mapTypeId: google.maps.MapTypeId.ROADMAP
10 };
11
12 map = new google.maps.Map(document.getElementById('mapPanel'), mapOptions);
13 google.maps.event.addListener(map, 'click', function(event) {
14 placeMarker(event.latLng);
15 });
16 }
17
18 function useResult(response, stat) {
19 markers[1].setTitle(response.rows[0].elements[0].distance.text);
20
21 document.getElementById('routeStart').innerHTML=response.originAddresses;
22 document.getElementById('routeEnd').innerHTML=response.destinationAddresses;
23 document.getElementById('routeLength').innerHTML=
24 response.rows[0].elements[0].distance.text;
25 document.getElementById('routeTime').innerHTML=
26 response.rows[0].elements[0].duration.text;
27 }
28
29 function placeMarker(location) {
30 if (markers.length >= 2 && markers[0] !== null && markers[1] !== null) {
31 for (var i = 0; i < markers.length; ++i) {
32 markers[i].setMap(null);
33 }
34 markers = [];
35 }
36
37 var marker = new google.maps.Marker({ position: location, map: map });
38 markers.push(marker);
39
40 if (markers.length === 2) {
41 var service = new google.maps.DistanceMatrixService();
42 service.getDistanceMatrix({
43 destinations : [ markers[1].getPosition() ],
44 origins : [ markers[0].getPosition() ],
45 travelMode : google.maps.TravelMode.DRIVING
46 }, useResult);
47 }
48 }
Die weiteren Zeilen von »initalize()« definieren die Funktion, die Markierungen setzt, wenn der Nutzer mit der Maus klickt: Bei einem Klick in die Karte speichert die Funktion »placeMarker()« die Position und zeigt ein rotes Icon an. Beim zweiten Klick gibt der Dienst »DistanceMatrixService()« das Ergebnis zurück, was ab Zeile 18 den Inhalt der rechten Spalte der Webseite füllt.
Durch das Lesen der API-Dokumentation oder Herumstöbern in den Werten mit einem Javascript-Debugger wie beispielsweise Firebug lassen sich die gewünschten Elemente im Rückgabewert finden. Abbildung 1 zeigt dann das fertige Ergebnis.
Der nächste Klick löscht die zwei alten Markierungen und setzt einen neuen Ausgangspunkt für die geplante Route. »google.maps.TravelMode.DRIVING« in Zeile 45 fordert eine Route für Automobile an. Alternativen zum »DRIVING« – Modus sind »BICYCLING« , »TRANSIT« oder »WALKING« .
OSM mit Open Layers
Der Einsatz von Open Layers und Open Streetmap erfordert etwas mehr Vorarbeit. Listing 4 zeigt, dass neben Open Layers noch eine weitere Bibliothek aus dem HTML-Template aufzurufen ist: Jquery [8] für die Kommunikation mit dem Yours-Dienst für die Fahrroutenberechnung (Yet another Open Streetmap Route Service, [9]) und dem Dienst für die Adresskodierung (Nominatim, [10]). Mit 75 zu 50 Zeilen fällt das OSM-Javascript (Listing 5) auch deutlich länger aus, und das, obwohl das Beispiel einige Funktionen nur gekürzt zeigt. Die ausführliche Versionen findet sich unter [11].
Listing 5
map.js (OSM, gekürzt)
01 var PROXY_HOST="http://localhost:6779/cgi-bin/proxy.cgi?url="
02 var map;
03 var markers;
04 var markerLayer;
05
06 function initialize() {
07 markers = [];
08 mapOptions = {};
09
10 map = new OpenLayers.Map("mapPanel", mapOptions);
11 markerLayer = new OpenLayers.Layer.Markers( "Markers" );
12 map.addLayer(new OpenLayers.Layer.OSM());
13 map.addLayer(markerLayer);
14
15 map.events.register("click", map, function(e) {
16 placeMarker(map.getLonLatFromPixel(e.xy)); });
17
18 //CENTER MAP HERE
19 }
20
21 function queryLocationInfo(lonlat, infoCallback) {
22 var addrUrl = "http://nominatim.openstreetmap.org/reverse/?addressdetails=1&format=json";
23 addrUrl += "&lon=" + lonlat.lon;
24 addrUrl += "&lat=" + lonlat.lat;
25
26 $.getJSON(addrUrl, infoCallback);
27 }
28
29 function queryRoutingInfo(startPoint, endPoint) {
30 var routeUrl = PROXY_HOST + "http://www.yournavigation.org/api/1.0/gosmore.php";
31 routeUrl += "?flat=" + startPoint.lat; // ATTENTION "&" or "?" it depends
32 routeUrl += "&flon=" + startPoint.lon;
33 routeUrl += "&tlat=" + endPoint.lat;
34 routeUrl += "&tlon=" + endPoint.lon;
35 routeUrl += "&v=motorcar";
36 routeUrl += "&fast=1";
37 routeUrl += "&geometry=0";
38 routeUrl += "&format=geojson";
39
40 $.getJSON(routeUrl, function (result) {
41 document.getElementById('routeLength').innerHTML=result.properties.distance;
42 document.getElementById('routeTime').innerHTML=result.properties.traveltime;
43 });
44 }
45
46 function placeMarker(location) {
47 if (markers.length >= 2 && markers[0] !== null && markers[1] !== null) {
48 for (var i = 0; i < markers.length; ++i) {
49 markerLayer.removeMarker(markers[i]);
50 }
51 markers = [];
52 }
53
54 var size = new OpenLayers.Size(21,25);
55 var offset = new OpenLayers.Pixel(-(size.w/2), -size.h);
56 var icon = new OpenLayers.Icon('http://www.openlayers.org/dev/img/marker.png', size, offset);
57 var marker = new OpenLayers.Marker(location, icon);
58 markerLayer.addMarker(marker);
59 markers.push(marker);
60
61 if (markers.length === 2) {
62 var p1 = markers[0].lonlat;
63 p1.transform(map.getProjectionObject(),
64 new OpenLayers.Projection("EPSG:4326"));
65 var p2 = markers[1].lonlat;
66 p2.transform(map.getProjectionObject(),
67 new OpenLayers.Projection("EPSG:4326"));
68
69 queryLocationInfo(markers[0].lonlat, function(response) {
70 document.getElementById('routeStart').innerHTML=response.display_name; });
71 queryLocationInfo(markers[1].lonlat, function(response)
72 document.getElementById('routeEnd').innerHTML=response.display_name; });
73 queryRoutingInfo(markers[0].lonlat, markers[1].lonlat);
74 }
75 }
Listing 4
osm.script
01 <script src="http://openlayers.org/api/OpenLayers.js"></script> 02 <script src="http://code.jquery.com/jquery-1.8.2.js"></script>
Gleich in der ersten Zeile fällt die Angabe eines Proxys auf. Er ist erforderlich, um eine Sicherheitsfunktion moderner Browser zu umgehen. Die Same-Origin-Policy soll verhindern, dass Angreifer via Javascript Rechner anfragen, die nicht zur gleichen Domäne gehören. Google schafft es, diese Einschränkung zu umgehen, aber einige andere Netzdienste sind nicht so clever.
Als Beispiel für einen solchen Dienst steuert das Skript Yours an, um dessen Routingfunktionen zu nutzen. Geeignete Proxy-Skripte sind in den Wikis von Open Layers und Open Streetmap verlinkt. Die verwenden jedoch unterschiedliche Konventionen, das dort empfohlene »proxy.cgi« beispielsweise erwartet in Zeile 31 (Listing 5) ein »&« , kein »?« .
Python-Webserver
Wer das OSM-Beispiel testen will, muss einen Webserver aufsetzen, der »map.js« , »index.html« und »proxy.cgi« anbietet. Ungeduldige probieren den minimalistischen Python-Webserver aus Listing 6. Den startet »python simpleserver.py« in dem Verzeichnis mit den drei Dateien, danach findet sich die Anwendung auf Port 6779 des Localhost.
Listing 6
simpleserver.py
01 import SocketServer
02 import SimpleHTTPServer
03 import urllib
04
05 PORT = 6779
06 PREFIX = "/cgi-bin/proxy.cgi?url="
07
08 class Proxy(SimpleHTTPServer.SimpleHTTPRequestHandler):
09 def do_GET(self):
10 self.send_response(200)
11 self.end_headers()
12 #print "path = ", self.path
13 if self.path[0:len(PREFIX)] == PREFIX:
14 self.copyfile(urllib.urlopen(self.path[len(PREFIX):]), self.wfile)
15 else:
16 self.copyfile(open("." + self.path), self.wfile)
17
18 httpd = SocketServer.ForkingTCPServer(('', PORT), Proxy)
19 print "serving at port", PORT
20 httpd.serve_forever()
Die Funktion »initalize()« in Listing 5 leistet Vergleichbares wie ihre Namensschwester im Google-Skript. Die vom Mausklick ausgelöste »placeMarker()« -Funktion ab Zeile 46 enthält zwei Besonderheiten. Zum einen initialisiert sie die Markierung ausführlicher, zum anderen muss das Skript die resultierenden Punkte erst in Latitude-Longitude-Koordinaten projizieren. Erst mit den resultierenden Werten ruft es dann die anderen Dienste auf. Das geschieht in den Funktionen »queryLocationInfo()« (ab Zeile 21) für die Adressenkodierung und »queryRoutingInfo()« (ab Zeile 29) für die Routenberechnung.
Jquery übernimmt die Kommunikation mit den jeweiligen APIs und parst die Ergebnisse in eine Datenstruktur. Wer Zeile 37 weglässt, erhält sogar die Geometrie für die errechnete Route. Und in Zeile 35 kann er natürlich auch ein anderes Fortbewegungsmittel als ein Auto eintragen. Darüber hinaus lassen sich mit dem Parameter »instructions« auch gleich die Routinganweisungen ausgeben. Weitere mögliche Parameter des Yours-Routing-API finden sich in der entsprechende Wiki-Seite [9].
Formate, Koordinaten, Verfügbarkeit
Die Grundfunktionen zur Navigation in der Karte gleichen sich bei den APIs. Bei beiden ist eine Zoomfunktion per Mausrad verfügbar, ein Zoomwerkzeug zum Heranholen eines per Maus ausgewählten Bereichs gibt es aber nur bei Open Layers. Das bietet an vielen Stellen mehr professionelle GIS-Unterstützung, beispielsweise bei der Integration verschiedener Dienste und Daten.
Dazu gehört die Anzeige von OSM-, Bing-, Yahoo- und Google-Karten genauso wie die Unterstützung professioneller und standardisierter Dienste wie Web Map Service (WMS), Web Feature Service (WFS), Web Map Tile Service (WMTS) sowie propietärer professioneller Anbieter wie ESRIs Arc GIS Mapping Services. Auch kommt es mit zahlreichen Datenformaten wie KML, Geo RSS, Geo Json, Json und GPX. Details nennt die Open-Layers-API-Dokumentation [12].
Google fokussiert die Unterstützung der eigenen Dienste und beschränkt sich auf KML, Geo RSS und einige Google-Angebote wie Heatmap-, Traffic- und Bicycle-Layer. Open Layers erlaubt Dienste und Daten in verschiedenen Koordinatensystemen, während Google standardmäßig nur die geografischen Koordinaten mit Breite und Länge sowie zwei Arten von Kartenkoordinaten auf Basis der Mercator-Projektion [13] bietet.
Beide Javascript-APIs unterstützen mit Firefox, Internet Explorer und Safari die wichtigsten Webbrowser. Bei Open Layers sind etwas ältere Versionen noch offiziell dabei [14], dafür tauchen Chrome und Browser für Touchdevices nicht auf, die Google als Hersteller von Android natürlich auf dem Radar hat [15].
Dass der freie Navigationsdienst Yours weniger gut verfügbar ist als Google, verwundert nicht – im Test vor Redaktionsschluss versagte das Routing leider immer wieder. Dafür gibt es bei Yours aber Software, mit der sich eigene Dienste aufsetzen lassen, auch für Karten und die Positionierung. Der eigene Server hat Vorteile, vor allem wenn es darum geht, die Eigenschaften der Karte wie Projektion, grafischer Stil und Einbindung der Schichten anzupassen. Das wird immer wieder nötig, vor allem wenn beim Kunden schon Daten vorliegen, die der Entwickler einbinden soll, oder es tiefer gehende Anforderungen an Darstellung oder Datenschutz gibt.
Sowohl Google als auch OSM bemühen sich um eine gute Dokumentation und ansprechende Beispiele. Open Layers liegt dabei leicht hinten. So sind Teile des Google-Maps-API bereits ins Deutsche übersetzt, während es bei Open Layers zwar nur englischen Text gibt, aber notfalls auch voller Zugriff auf den Quelltext möglich ist.
Fazit
Beim Google-Maps-Javascript-API fällt der Einstieg deutlich leichter als bei Open Layers. Skripte sind bis zu einem Drittel kürzer, der Programmierer bekommt alles aus einer Hand. Dabei kann er auf gut dokumentierte und integrierte Dienste und Bibliotheken zurückgreifen.
Sobald es aber anspruchsvollere, gewerbliche Anwendungen betrifft, stellt die Vielfalt der freien Softwareprodukte um Open Layers und OSM einen erheblichen Vorteil dar (Abbildung 2). Hier können Entwickler hinter die Kulissen blicken und die Eigenschaften der gewählten Lösung genau inspizieren. Bei Google hingegen steht dann eine genauere Auseinandersetzung mit den Nutzungsbedingungen und Preisen an. (mfe)
Infos
- Googles Regeln: http://www.google.com/policies/privacy/
- Open Streetmap: http://www.openstreetmap.org
- Navfree: http://www.navmii.com
- Google-Maps-API:https://developers.google.com/maps/
- Open Layers: http://openlayers.org
- Google-Javascript-Basics: https://developers.google.com/maps/documentation/javascript/basics?hl=de
- Googles Styling-Definitionen: https://developers.google.com/maps/documentation/javascript/styling?hl=de
- Jquery: http://jquery.com
- OSM-Routenberechnung: http://wiki.openstreetmap.org/wiki/YOURS
- Adresskodierung: http://wiki.openstreetmap.org/wiki/DE:Nominatim
- OSM-Skript: ftp://ftp.intevation.de/geospatial/linuxmagazin/2012/12/
- Open-Layers-API-Dokumentation: http://dev.openlayers.org/releases/OpenLayers-2.12/doc/apidocs/files/OpenLayers/Layer-js.html, http://dev.openlayers.org/releases/OpenLayers-2.12/doc/apidocs/files/OpenLayers/Format-js.html
- Koordinaten: https://developers.google.com/maps/documentation/javascript/maptypes?hl=de#MapCoordinates
- Browser-Support bei OSM: http://trac.osgeo.org/openlayers/wiki/Browsers
- Browser-Support bei Google: https://developers.google.com/maps/faq?hl=de#browsersupport








