Geographische Karten einbinden mit Drupal und Leaflet - Alternative zu Google Maps

Bild des Benutzers Regina Oswald

Kürzlich bekamen wir eine Anfrage für die Umsetzung einer Karte mit Leaflet und Openstreetmaps.
Da es ja diverse Gründe gibt, sich nicht vom Lizenz-Modell von Google abhängig zu machen, haben wir die Anfrage zum Anlass genommen, die Darstellung der Lieferanten auf Deutschland-Karten auf der Webseite über Gartenstauden und Baumschul-Pflanzen auf Leaflet und Openstreetmap umzustellen.

Leaflet  ist natürlich nicht das einzige geeignete Modul. Einen Vergleich von Mapping-Modulen findet sich hier.
Für die Leaflet-Open-Source-Library in JavaScript spricht, daß sie sehr schnell und für mobile Endgeräte geeignet und somit zukunftsfähig ist.
Außerdem gibt es eine gute Dokumentation und eine lebendige Comunity, die es erlaubt, Sonderwünsche zu programmieren.

Die reine Darstellung der Lieferanten auf der Karte wäre ganz einfach mit Drupal-Boardmitteln umzusetzen.
Nun ist die Übersichtlichkeit auf der Karte unbefriedigend, wenn viele Einträge (Marker) in einem engen lokalen Bereich auftauchen.
Dies versucht man durch verschiedene Cluster-Methoden zu umgehen.
Clustering bedeutet, daß Marker, die in einer bestimmten Entfernung liegen, bei einem bestimmten Zoomfaktor zusammen gefasst werden und erst beim Zoomen in die Karte werden die einzelnen Marker dargestellt.

In unserem Fall soll das Clustering nicht von der Entfernung der Marker abhängen, sondern es sollen beim Aufruf der Karte die Grenzen der Bundesländer gezeigt werden und ein Kreis mit der Anzahl der Lieferanten für dieses Bundesland.
Beim MouseOver soll das Bundesland farbig hervorgehoben werden. 
Bei einem Click auf das Bundesland wird dieses vergrößert dargestellt und die Betriebe als einzelne Marker dargestellt.

Leaflet Map mit Clustering per Bundesland und MouseOver

Beispiele für Clustering sind nur für das Standard-Cluster-Verfahren abhängig von der Entfernung der Marker zu finden.
Das funktioniert mit dem Cluster-Modul für Leaflet unter Drupal.
Für das Clustering pro Bundesland und die farbige Darstellung kommt deshalb individuelles JavaScript (jQuery) und das GeoDB-Modul von Leaflet zum Einsatz.

Im Folgenden beschreibe ich die Schritte, die für die Umsetzung notwendig sind:

1. Module installieren:

Diese Leaflet- Module werden aktiviert:
 

 Leflet Module Drupal

2. Library für Leaflet installieren

Die JavaScript Dateien von Leaflet werden vom Download-Verzeichnis der Leaflet- Homepage geholt und in das Verzeichnis sites/all/libraries geschoben.

3. Inhaltstyp Pflanzen-Lieferanten wird um folgende Felder ergänzt:

Hiermit werden mehrere Felder für die Adress-Informationen angelegt.
Das Feld ersetzt also die Informationen, die bisher in Location gespeichert sind.

Leaflet Addressfield Content Type
 

Geocoder ermittelt die Geo-Koordinaten aus dem Adress-Feld.

Unter "Geocode from field" geben wir das soeben angelegte Feld  "Adresse" an.
Unter "Geocoder" wird "Google Geocoder" gewählt.


Leaflet Geocoder Content Type

 

Nun benötigen wir noch ein Feld, das aufnimmt, welchem Bundesland unsere Adresse angehört.

Leaflet Bundesland Content Type

Die Felder werden alle als Pflichtfeld angelegt.

4. Beim Inhaltstyp Ansicht der neuen Felder ändern:
 

Geocoder:

Leaflet Geocoder Ansicht Content Type
 

Hier kann man einfach ein eigenes Icon für den Marker hinterlegen, wenn man nicht den Drupal-Wassertropfen verwenden möchte.
Da wir hier die Einstellungen für die Karte einrichten, die in der Detailansicht gezeigt wird, benötigen wir kein PopUp auf dem Marker, da alle Infos bereits auf der Seite stehen.
 

5. Daten aus bisherigem Adressfeld in die neuen Felder übertragen

Um die Ansicht der Karte zu testen, übertragen wir bei einem Datensatz händisch die Daten.
Nun sehen wir unsere Karte noch in einem zu starken Zoom. Das ändern wir nachher im JS.

Da sich niemand findet, der die ganzen Daten händisch verpflegen möchte und das Modul Feeds bereits eingerichtet ist, bauen wir uns eine View, die die bisherigen Adressdaten (Nid,Straße, PLZ, Ort) in einer CSV-Datei zurück gibt.

Aus einer geeigneten OpenSource-Quelle besorgen wir uns eine Tabelle mit Bundesland->PLZ-Zuordnung.
Die beiden CSV Dateien kopieren wir in zwei Tabellenblätter eines Open Office-Calc (bzw. Excel) Dokuments und schreiben eine Formel, die zu jedem Nid über die PLZ das Bundesland ermittelt.
Davon erzeugen wir eine CSV-Datei und erstellen ein passendes Feeds-Mapping zum Import der Daten.
Nun sind unsere bisherigen Datensätze der Lieferanten mit den Daten im Adressfeld und im Bundesland befüllt.

Dieses Verfahren wird bei anderen Installationen überflüssig sein oder von dem beschriebenen Vorgehen abweichen.
 

6. View bauen für die Karte
Wir legen eine einfache Inhalts-View an mit Selektion auf den Inhaltstyp Pflanzen- Lieferanten und wählen Leaflet Map als Format. 
Die weiteren Settings machen wir erst, wenn wir Felder angelegt haben, woran uns auch der Hinweis erinnert, wir sollten mindestens ein Feld mit Geo-Koordinaten bereit stellen.

Die Grundkonfiguration des Felds geocoder sieht so aus:
 

Leaflet Geocoder Konfiguration in der View

zusätzlich legen wir noch Felder für Titel (als Link), Strasse, PLZ und Ort an.
In einem zusätzlichen Feld überschreiben wir den Wert mit einem String aus diesen Werten.

Der String sieht dann so aus:

<b>[title]</b> <div class="pop_field_lieferanten_strasse">[field_lieferanten_strasse]</div>
<div class="pop_adresse">[field_lieferanten_plz] [field_lieferanten_ort]</div>
<div class="pop_field_bundesland">[field_bundesland]</div>
 

Das Feld heißt title_1.
Nun gehen wir in die Settings vom Format Leaflet Map und machen dort die fehlenden Einstellungen: 

Leaflet PopUp Konfiguration in der View
 

Das Titel Field bezieht sich auf das erste Feld Titel, das wir angelegt haben.
In Description steht der zweite Titel, der mit dem zusammengebauten String überschrieben wird. Bei der Map wählen wir wieder Openstreetmap und als Höhe 550px.
Diese Höhe ist gewählt, weil dann Deutschland bei einem Zoomwert von 6 genau hinein passt.

Unter Point Icons geben wir den Pfad auf ein eigenes Marker-icon an:
 

Leaflet PopUp Konfiguration in der View

 

Nun geben wir noch den Pfad an, unter dem wir die Seite mit der Karte sehen möchten und schauen uns das Ganze an.
Die Karte wird in der korrekten Größe auf Deutschland optimiert angezeigt und ebenso die Marker mit unserem eigenen Icon.

Da sehr viele Icons auf relativ kleinem Raum angezeigt werden, ist die Karte noch unübersichtlich.

Deshalb geht es jetzt an die Feinarbeit mit dem Ziel, zuerst nur Umrisse von den Bundesländern zu zeigen.
Jedes Bundesland soll einen Marker enthalten, der die Anzahl der Betriebe in diesem Bundesland wieder gibt (Clustering).

Für das Cluster-Verfahren bringt Leaflet sogar ein eigenes Modul mit (
Leaflet Markercluster ) .
Dieses Modul bietet Clustering nach Distanz. D.h. Man kann die Entfernung angeben, unter der benachbarte Marker geclustert angezeigt werden. 
Beim Zoomen in die Karte werden die Marker dann jeweils ungeclustert gezeigt.

Wie oben schon geschrieben ist dieses Modul für unser Vorhaben nicht geeignet.
Deshalb werden weitere Feinheiten per JavaScript
 (Jquery) umgesetzt.

Feinarbeiten per JavaScript (Jquery) 
Dafür bietet Leaflet eine ausführliche Doku zur Library. Noch hilfreicher sind allerdings die versch. Beispiele im Netz, die wir nach und nach zusammen gesammelt haben.

Folgende Aufgaben wurden per JS gelöst:

  • Definiere Koordinaten + Zoom, auf die auch bei Klick auf die Karte ausserhalb DE zurueckgesprungen wird
map.setView(new L.LatLng(initalLat, initalLng), initalZoom);
  • Gruppen von Layern bilden, die später unterschiedliche behandelt werden können
    z.B. alle Marker, alle Layer für Bundesländer, alle Cluster-Layer
  • Default Styles und Styles für MouseOver initialisieren:
var defaultStyle = {
   color: "#2262CC",
   weight: 1,
   opacity: 0.6,
   fillOpacity: 0.2,
   fillColor: "#2262CC"
};
  • Eine Schleife machen über alle GeoJSON Features mit Bundesländern in Deutschland.
    Diese Datei wurde aus einer OpenSource-Quelle bezogen und lokal abgelegt.
jQuery.getJSON(url, function (json) {
  L.geoJson(json, {
   onEachFeature: function (feature, layer) {
   //für jedes Feature (Bundesland) werden bestimmte Methoden aufgerufen
    //Z.B. Fabe bei Mouseover
    layer.on('mouseover', function (e) {
      this.setStyle(MouseOverStyle);  
    });   
  }
 }).addTo(map);
}
 
Mit addTo(map) wird jeder erzeugte Layer an die vorhandene Karte hinzugefügt.
Ebenfalls für jedes Bundesland wird die Anzahl der Marker in diesem Bundesland ermittelt und mit?
myTextLabel = L.marker(textLatLng, {
   title: countmarker+' '+betrieb+' in '+bundesland,
   icon: L.divIcon({ 
     className: 'cluster-labels cluster-labels-'+ID,
     html: countmarker
   }),
   draggable: true,
   zIndexOffset: 1000,
}).addTo(map);

angehängt.

Bei einem Klick auf das jeweilige Bundesland wird (u.a.) dieses Text-Label mit der Anzahl der Betriebe auf Unsichtbar gesetzt und dafür die Marker für jeden einzelnen Betrieb angezeigt.
Für all diese Funktionen werden die oben vorinitalisierten Layer-Gruppen verwendet und in einer Schleife durchlaufen.

  • Map auf Ursprungs-Ansicht zurück setzen, sobald jemand auf einen Punkt außerhalb von Deutschland klickt
 map.on({
     click: function() {
//Map wieder mit Ausgangs Koord. und Zoom positionieren
map.setView(new L.LatLng(initalLat, initalLng), initalZoom);
//Marker alle verschwinden lassen
for( i in markerlayer._layers){
markerlayer._layers[i]._icon.style.display = 'none';
}
//Alle Clustermarker wieder sichtbar stellen
for( i in clusterlayer._layers){
clusterlayer._layers[i]._icon.style.display = 'Block';
}
//Polygone in Defaultstyle versetzen
for( i in geolayer._layers){
geolayer._layers[i].setStyle(defaultStyle);
}
     }
});
 

Bitte schaut Euch das JavaScript einfach im laufenden Betrieb an, um die genaue Syntax zu finden.
 

Hier die verschiedenen Seiten, auf denen Leaflet-Karten eingebunden sind:

Anzeige der Lieferanten auf der Deutschlandkarte

Übersicht über die Lieferanten mit Detail-Karte
 

7 comments

Bild des Benutzers maen

Glückwunsch für die Umsetzung! Gefällt mir!

Bild des Benutzers Regina Oswald

Danke!

Bild des Benutzers Susanna Künzl

... eine super Alternative zu Google Maps.

Bild des Benutzers wla

Besonders, daß alle jQuery Scripte aufgeführt werden, finde toll. Das ist nämlich immer ein ziemlicher Aufwand, die zusammen zu suchen oder selbst zu erstellen. Ich habe so eine Anwendung bisher zwar noch nicht selbst benötigt, aber man weiß ja nie, was auf einmal hochkommt.

Beste Grüße
Werner

Bild des Benutzers Regina Oswald

Hallo Werner,
wie geschrieben, ist nicht das gesamte JavaScript im Blog aufgeführt.
Letztendlich kann man sowieso nur Denkanstöße geben und je nach abweichender Anforderung wird man nicht ums Suchen drum rum kommen.
Aber es ist ja schon mal viel wert, wenn man weiß, was mit Leaflet geht und daß sich die Suche unbedingt rentiert. ;-)
Viele Grüße,
Regina

Bild des Benutzers Tobias Stöber

Servus,

wirklich eine schöne Lösung ... so etwas ähnliches könnte ich akut ohne Drupal gebrauchen.

Mal schauen was man davon lernen, sich abschauen kann. In dem fertigen Beispiel /

Anwendungsfall http://www.gartenstauden.de/gartencenter-vor-ort-karte gibt es nur ein Manko

bzgl. der Detailinfo, wenn man auf einen Standort klickt: Bei den PLZ die mit 0 beginnen

(Thüringen, Sachsen, Brandenburg) wird die führende 0 einfach unterschlagen, vermutlich

weil die PLZ (fälschlicherweise) als Integer gespeichert bzw. ausgegeben wird?

Beste Grüße, Tobias

Bild des Benutzers admin

Hallo Thomas,

danke für den Hinweis mit den führenden NULLEN.

Da muß evt. das Feld anders formatiert werden.

Sicher kannst Du Einiges auch ohne Drupal durchführen, ist ja nur JavaScript.
Für die Darstellung auf der Karte und FIlterung ist das Views-Modul von Drupal natürlich eine tolle Sache.
Gruß, Regina