Spaces. Smartes Cloud Hosting für anspruchsvolle Webprojekte. Loslegen und Spaces testen. Von Mittwald.
Rene Schmidt 2. September 2009

Tipps & Tricks mit OpenStreetMap

Wenn „Google Maps“ Windows ist, dann könnte man „OpenStreetMap“ (OSM) als das Linux unter den Web-Kartendiensten ansehen. Tausende Freiwillige setzen Zeit und Geld ein, um freie Software zu erschaffen – in diesem Fall eine freie Weltkarte. Dabei ist OSM, wie Linux auch, in manchen Aspekten dem Platzhirschen unterlegen und in manchen besser.

Zum Thema „OpenStreetMap“ (OSM) behandle ich nun drei Themen:

  1. OSM via HTTPS
  2. Geocoding für OSM
  3. W3C-Geolocation

Als Spielwiese stelle ich ein paar Skripte bereit (mehr dazu am Ende des Artikels). Zum einen sind das JavaScripte, mit denen eine so genannte Slippy-Map im Browser dargestellt wird. Das ähnelt in Grundzügen einer von „Google Maps“ her bekannten interaktiven Karte, abzüglich Funktionen wie Satellitenkarten, Street View, Routenplanung und so weiter. Zum anderen stelle ich zwei Proxy-Server in Form von PHP-Skripten bereit, die Inhalte zwischenspeichern und sich um die Kommunikation mit OSM- und CloudMade-Servern kümmern.

OpenStreetMap über HTTPS

Wollten Sie schon einmal einen Kartendienst über HTTPS verwenden? Nutzer sehen in der Regel dann das:

unsichere elemente

Für gewöhnlich werden die Karten-Kacheln über HTTP geladen, daher erscheint diese Warnung. In der Praxis möchte man seine Besucher oder Nutzer nicht mit solchen Warnungen verunsichern oder auf die Nerven gehen. Einen Kartendienst über HTTP zu nutzen, geht also nicht so ohne weiteres.

Google bietet zwar für zahlungswillige Kunden auch Kacheln über HTTPS an – aber das dürfte für die meisten nicht in Frage kommen. Eine andere Alternative wäre, einen eigenen OSM-Server aufzusetzen. Aber auch das ist in den meisten Fällen überdimensioniert. Einfach nur Karten-Kacheln per HTTP übertragen, ohne Protokollbruch und ohne Warnung – das muss doch gehen?

So geht’s

Wir schalten zwischen Anzeige der Karte und dem OSM-Server ein Proxy-Skript. Alle Kachel-Anfragen des Skripts OpenStreetMap.js werden auf ein Proxy-Skript umgelenkt, nach folgendem Muster:

var url = [„tileproxy.php?layer=ID&path=path/${z}/${x}/${y}.png"];

Das Proxy-Skript tileproxy.php holt die Kacheln vom OSM-Server. Das dauert natürlich wesentlich länger, als OSM-Server direkt per JavaScript anzusprechen. Um dieses Problem anzugehen und die OSM-Server nicht übermäßig zu belasten, speichert das Skript die Kacheln auf dem Server.

Zusätzlich versieht der Proxy jede Kachel mit einer Prüfsumme, dem sogenannten Etag.

Der Browser fragt mit dem Etag, ob die Datei noch aktuell ist.

Der Browser fragt mit dem Etag, ob die Datei noch aktuell ist.

Das Etag wird vom Proxy-Skript als HTTP(S)-Header-Element gesendet. Der Browser merkt es sich.

Wenn der Browser später eine Kachel erneut anfordert, sendet er dieses Etag in seiner HTTP(S)-Anfrage mit und fragt den Proxy quasi „Ich habe hier eine Kachel mit diesem Etag – ist diese Kachel noch aktuell? Wenn nicht, schicke mir die Kachel!“ Das Proxy-Skript schaut im Server-Cache nach. Wenn es unter dem Etag eine aktuelle Kachel findet (ein sogenannter „Hit“), informiert es den Browser, dass die Kachel noch verwendet werden kann und sendet sie nicht noch einmal – das spart in der Summe viel Zeit und Datenvolumen. Findet das Skript die Kachel nicht oder aber eine „abgelaufene“ Kachel („Miss“), besorgt es sich die Kachel frisch von einem OSM-Server und übermittelt sie an den Browser.

Aus dem Browser-Cache sind Kacheln mit ca. 30ms sehr schnell geholt. Der Server liefert sie auch noch recht flott innerhalb ca. 81ms. Muss eine Kachel erst vom OSM-Server geholten werden, dauert es einige hundert Millisekunden.

Aus dem Browser-Cache sind Kacheln mit ca. 30ms sehr schnell geholt. Der Server liefert sie auch noch recht flott innerhalb ca. 81ms. Muss eine Kachel erst vom OSM-Server geholten werden, dauert es einige hundert Millisekunden.

Auf diese Weise gibt es zwei Cache-Ebenen, die aktiv genutzt werden: Den Cache im Browser und den Proxy-Cache auf dem Server. Erst, wenn eine Kachel in keinem der beiden Caches gespeichert ist, geht eine Anfrage zum OSM-Server raus.

Fazit: Prinzipiell funktioniert diese Methode auch mit proprietären Diensten wie Google Maps. Dort ist es zwar grundsätzlich erlaubt, Kacheln zwischenzuspeichern – aber nur mit Einschränkungen. In den Nutzungsbedingungen unter Punkt 10 „Licence Restrictions“ heißt es

10.3 (You must not) pre-fetch, cache, or store any Content, except that you may store limited amounts of Content for the purpose of improving the performance of your Maps API Implementation if you do so temporarily, securely, and in a manner that does not permit use of the Content outside of the Service;

Insbesondere der letzte (Halb-)Satz schränkt die Erlaubnis de facto auf einen geschlossenen Benutzerkreis ein, bei dem sich Nutzer erst anmelden müssen. Anderenfalls könnte man diese Bedingung kaum wirksam erfüllen.

Unser OSM-Proxy-Skript kann auch außerhalb von geschlossenen Benutzerkreisen und sowohl per HTTP als auch per HTTPS genutzt werden. Ohne den Protokoll-Bruch gibt es auch keine Browser-Warnung, dass „unsichere Elemente“ geladen werden. Trotz der einfachen Einrichtung können die Vorteile einer lokalen Kachel-Auslieferung genutzt werden, ohne allzu große Nachteile wie zum Beispiel durch das Aufsetzen eines eigenen OSM-Servers zu haben.

GeoCoding mit OSM

Was ist Georeferenzierung? Kurz gesagt: Georeferenzierung ist die Zuweisung von Koordinaten zu einer Adresse. Ein Beispiel: Der Ort Buxtehude hat die Koordinaten 53.477165 (Breite) und 9.703144 (Länge). Koordinaten benötigen wir, um auf der Karte einen gewünschten Ausschnitt zu zeigen.

OSM bietet, zumindest meiner Ansicht nach, derzeit (Juli 2009) keine wirklich praktisch nutzbare Georeferenzierung (engl. „Geocoding“) an. Eine einfache Suche nach „Jungfernstieg, Hamburg, Germany“ liefert beim NameFinder kein richtiges Ergebnis (Hinweis: jetzt — August 2009 — ist der Namefinder sogar offline): Die Suche führt dort nach Thüringen und nicht ins Zentrum von Hamburg.

Weil OSM derzeit kein Zuweisungsystem hat, das problemlos funktioniert und vor allem einfach ist, gehen wir den Umweg über die von CloudMade angebotene Geocoding-API. CloudMade arbeitet eng mit der OpenStreetMap-Gemeinschaft zusammen.

Wir schicken dem CloudMade-Server eine Adresse, dessen GeoCoding-Dienst parst sie, sucht die Koordinaten heraus und schickt sie uns. Mit diesen Koordinaten können wir auf der OSM-Karte den richtigen Ausschnitt anzeigen.

So geht’s

Wie schon bei tileproxy.php nutzen wir den Cache-Mechanismus PEAR::Cache_lite, um Resultate von fremden Servern zu speichern. Wir machen das in diesem Fall aber nicht nur aus Leistungsgründen, sondern weil wir CloudMade nicht übermäßig belasten wollen. Mit dem Cache können wir die Anzahl Abrufe klein halten. Und natürlich wollen wir wieder den HTTP/HTTPS-Protokollbruch und die Meldung „unsichere Elemente“ vermeiden.

Auch dieses Geocoder-Skript ist ein Proxy-Server und deswegen heißt es geocoderproxy.php. Ähnlich wie bei tileproxy.php sendet das Skript bei jeder Anfrage ein besonderes HTTP(S)-Header-Element namens X-OSMGEOCODERPROXY-CACHE. Findet es zu einer Anfrage ein gespeichertes Ergebnis, bekommt das Element den Wert „Hit“ (Treffer). Findet es kein Ergebnis, hat es den Wert „Miss“ (Fehlschlag) und holt von CloudMade das Ergebnis. Auf diese Weise kann man im Antwort-HTTP-Header sehen, zum Beispiel in Firebug, ob eine Datei aus dem Cache oder direkt vom Server des Anbieters kam.

Hier ist zu sehen, dass das geocoderproxy.php einen HIT meldet. Die Koordinaten dieser Adresse sind also noch im Proxy-Cache, so dass keine Anfrage an einen CloudMade-Server geschickt werden muss

Hier ist zu sehen, dass das geocoderproxy.php einen HIT meldet. Die Koordinaten dieser Adresse sind also noch im Proxy-Cache, so dass keine Anfrage an einen CloudMade-Server geschickt werden muss

Nun zentrieren wir die OSM-Karte auf die von CloudMade gelieferten Koordinaten. Dazu muss zunächst ein sogenanntes LonLat-Objekt erzeugt werden. Mit der Methode OpenLayers::setCenter() wird die Karte dann zentriert:

var lonLat = new OpenLayers.LonLat(
  d.ResultSet.Result.Longitude,
  d.ResultSet.Result.Latitude
).transform(map.displayProjection, map.projection);
map.setCenter (lonLat, 16);

Per JavaScript setzen wir an diese Koordinaten noch eine Markierung, die bei einem Linksklick die geparste Adresse in einer Sprechblase anzeigt:

var layer_text = d.ResultSet.Result.Address+' '
   +d.ResultSet.Result.City+' '
   +d.ResultSet.Result.State;
addMarker(layer_markers, lonLat, layer_text.replace(/^(\s+)(.*)(\s+)$/g, '$2'));

Nun sieht unsere OSM-Karte nicht mehr nur schön aus, sondern hat auch einen Nutzwert bekommen.

Fazit: Die Geocoding API von CloudMade ist flexibel. Allerdings ist sie nicht besonders schnell und liefert aufgrund der oft fehlenden Hausnummern im OSM-Datenbestand insbesondere bei langen Straßen keine genauen Koordinaten. Weiterhin gibt es Einschränkungen beim Parsen von Ortsangaben. Andere Dienste wie Google Maps/Yahoo Maps/Bing Maps haben mit Suchanfragen wie „21640 Horneburg“ keine Probleme. Lässt man Hausnummern und Postleitzahlen weg und begnügt man sich mit straßen-genauen Ergebnissen, liefert die GeoCoding-API von CloudMade meist brauchbare Ergebnisse.

Zum Vergleich ist auf der „Spielwiese“ noch eine GeoCoding-Suche mit Yahoo eingebaut. Beachten Sie bitte, dass die Nutzungsbedingungen von Yahoo es nicht erlauben, die Yahoo-GeoCoding-API mit anderen Diensten als Yahoo Maps zu nutzen.

W3C-Geolocation mit Firefox 3.5 und Google Chrome 2.0

Den ungefähren Standort eines Web-Surfers daheim oder am Arbeitsplatz herauszufinden ist nicht schwer. Es gibt kostenlose und kostenpflichtige Datenbanken, welche IP-Adressen bestimmten Orten zuweisen, beispielsweise GeoLite von Maxmind.

Mit einer solchen serverseitigen Geolocation kann man als Nutzer nicht verhindern, dass die eigene IP einem Ort zugeordnet werden kann. Ob das datenschutzrechtlich einwandfrei ist, ist zumindest eine Frage wert. Eine Entscheidung darüber überlasse ich an dieser Stelle Datenschützern und Juristen.

Datenschutzrechtlich – meiner Meinung nach – einwandfrei ist der Entwurf der Geolocation-API des W3C, mit dem man via JavaScript, also clientseitig, den ungefähren Standort eines Nutzers herausfinden kann. Der Nutzer kann sowohl in Firefox 3.5 als auch in Chrome 2.0 die W3C-Geolocation abschalten. Firefox 3.5 fragt sogar standardmäßig den Nutzer, ob er seinen Standort preisgeben will oder nicht. Er behält also die Kontrolle über seine Informationen.

Firefox 3.5 fragt artig, ob er „seinen“ Standort preisgeben darf.

Firefox 3.5 fragt artig, ob er „seinen“ Standort preisgeben darf.

Firefox ab Version 3.5 hat die API fest eingebaut. Chrome kann mit dessen eingebautem Gears die API nutzen. Nutzer anderer Browser (von einigen Handy-Browsern abgesehen, die ebenfalls Gears benötigen) schauen derzeit noch in die Röhre, so dass der Nutzen der API derzeit noch eingeschränkt ist. Ein nettes Gimmick ist die Funktion aber schon heute.

So geht’s

Ein W3C-Geolocation-Objekt wird in unserem Fall genau so benutzt wie die Gears-Geolocation-API. Der einzige Unterschied liegt in der Erzeugung des JavaScript-Geolocation-Objekts.

In Firefox 3.5 (und, ich nehme an, in zukünftig erscheinenden Browsern wird es auch so sein) wird das Objekt so erzeugt:

var gl = navigator.geolocation;

In Chrome läuft das so:

var gl = google.gears.factory.create('beta.geolocation');

Im weiteren Verlauf unseres JavaScripts ist die Verwendung in beiden Browsern gleich. Damit andere Browser nicht mit der Geolocation behelligt werden, habe ich eine kleine Browser-Abfrage eingebaut:

var ua = navigator.userAgent.toLowerCase().match(/(chrome|firefox)\/([0-9]+\.[0-9]+)/);
var gl = false;

if(navigator.geolocation || (ua && ua[1] && ua[2]))
{
	if(navigator.geolocation || (ua[1] == "firefox" && parseFloat(ua[2],10) >= 3.5))
	{
		var gl = navigator.geolocation;
	} else if (ua[1] == "chrome" && parseFloat(ua[2],10) >= 2.0)
	{
		var gl = google.gears.factory.create('beta.geolocation');
	}
}

Wenn im folgenden die Variable „gl“ noch den Wert „false“ hat, steht die W3C-Geolocation nicht zur Verfügung. Das lässt sich dann einfach abfragen mit

if(!gl) {
	alert('Geolocation steht nicht zur Verfügung');
     // Geolocation-Funktionen fuer 'alte' Browser abschalten
}

In meinem Beispiel wird bei einem Click-Event auf den Knopf #locateme die Methode getCurrentPosition() aufgerufen. Firefox 3.5 fragt daraufhin um Erlaubnis, die Information herauszugeben. Wie unter „Yahoo! GeoCoding API mit OSM“ beschrieben, wird dann ein LonLat-Objekt erzeugt, mit dem die Karte auf die ermittelten Koordinaten zentriert und eine Markierung gesetzt wird.

Fazit: Die W3C-Geolocation-API ist noch neu und wird außer von Firefox 3.5 und Google Chrome mit Gears noch von keinem anderen Desktop-Browser unterstützt. Das beschränkt den Nutzen derzeit noch auf Umgebungen mit festen Browser-Vorgaben, zum Beispiel Intranets.

Der Urheber des W3C-Geolocation-API-Entwurfs Google setzt die Technik bereits auf Google Maps ein. Ich nehme daher an, dass die API ein offizieller Standard werden wird und andere Browser-Hersteller nachziehen werden.

Einen hab ich noch

Gut, das hat nun nicht direkt etwas mit OpenStreetMap zu tun. :) Interessant ist’s aber dennoch: Die GeoCoding APIs der „drei großen“ Kartendienste sind nahezu perfekt. Könnte man meinen. Während sich Google Maps und Bing Maps tatsächlich keine auf Anhieb erkennbare Blöße geben, leistet sich Yahoo Maps bei der Georeferenzierung einen kleinen Schnitzer.

Versuchen Sie mal, bei Yahoo Maps nach diesen Adressen zu suchen, und zwar nach dem dort vorgeschlagenen Muster „Adress, City, State“:

  • Reismühle, Hamburg, Germany
  • ABC-Straße, Hamburg, Germany

Yahoo Maps

Yahoo Maps findet nichts und bietet jeweils das Zentrum von Hamburg als Ersatz an. Mit etwas Tüfteln kann man jedoch herausfinden, dass Yahoo Maps Umlaute und Bindestriche gerne mit einem Kaufmanns-Und „&“ maskiert haben möchte:

  • Reism&hle, Hamburg, Germany
  • ABC&Straße, Hamburg, Germany

Erst, wenn man dem Suchmuster „Straße, Ort, Bundesland, Land“ folgt, funktioniert die Suche auch ohne diese seltsame Maskierung:

  • Reismühle, Hamburg, Hamburg, Germany
  • ABC-Straße, Hamburg, Hamburg, Germany

Deutlich besser ist die Benutzbarkeit, wenn man über den Menüpunkt „Language“ die Sprache manuell auf Deutsch umstellt — dann wird nämlich ein besseres Eingabemuster vorgeschlagen: „Straße/Hausnummer, Postleitzahl, Ort“. Warum nicht gleich so?

Natürlich hat man als normaler Nutzer oft weder die Zeit, die Kenntnisse noch die Geduld, diese ärgerliche Usability-Hürde zu nehmen und geht eben schnell rüber zu Google Maps oder Bing Maps.

Zu den Beispiel-Skripten

Wenn Sie selbst in das Thema eintauchen möchten, die Skripte stehen zum Download bereit. Beachten Sie bitte, dass Sie für das GeoCoding API-Schlüssel von YAHOO oder CloudMade benötigen. ™

Links

2 Kommentare

  1. Hey, wir haben genau das oben angesprochene Problem. Wir haben einen SSL-gesicherten Server, würden hier aber gerne die OSM darstellen. Wir haben jedoch keinen PHP Bereich auf unserem Server, so dass dieser Proxy so nicht funktioniert. Kann man den sich irgendwie auch in Javascript/Java bauen? Und wenn ja, wie?

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.