Processwire - Leaflet Map Marker Modul
Todo: Artikel überarbeiten (Beispiel Magnetic Music mit überarbeitetem Modul) Neuer Modulentwickler -> testen ob die neue Version gut funktioniert oder lieber eigene
Optionen für Karte was - wo? Evtl. viel über das Hauptskript MarkupLeafletMap.js einstellen statt zu viel über $options. Dokumentieren was wo. Evtl. Karteneinstellungen über js und Moduleinstellungen über options. Andererseits Einstellmöglichkeiten die man Steuern möchte über $options.
Siehe auch
https://processwire.com/talk/topic/9745-module-leaflet-map/?page=5 (Forum) https://github.com/gmclelland/FieldtypeLeafletMapMarker/blob/PW3/InputfieldLeafletMapMarker.module (Fork) https://leafletjs.com/ (Leaflet Hauptseite) https://leaflet-extras.github.io/leaflet-providers/preview/index.html (Karten Provider) https://github.com/Leaflet/Leaflet.markercluster (Doku für Cluster Modul) https://github.com/leaflet-extras/leaflet-providers
Überblick
Wird aus Zeitgründen vom Entwickler nicht sehr aktiv weitergepflegt. Allerdings gibt es ordentlichen Support im Forum und ein paar Forks auf Github.
Man kann für spezielle Anforderungen auch nur das Feld im Backend nutzen und Leaflet im Frontend selbst aufsetzen bzw. eigene Skripte nutzen. Das JavaScript für die Frontendausgabe im Modul findet man in:
MarkupLeafletMap.js (JavaScript für Frontend-Ausgabe) MarkupLeafletMap.module (Inline JS für Frontend )
Quickstart
- Benötigt die Konfiguration von $additionalHeaderData. Das ist einfach ein String der in _init.php angelegt und im Header eingebunden wird. Damit lassen sich Zusätzliche Daten per Template rendern.
- Feldname im Backend ist hier map_leaflet
- Achtung Bug (V2.8.1): Immer den Geocoder auf der Seite benutzen. Die Standarddaten aus der Feldkonfiguration werden nicht richtig übernommen (Adressfeld) und es gibt Ausgabefehler.
Beispiel
$map = wire('modules')->get('MarkupLeafletMap');
$additionalHeaderData = $map->getLeafletMapHeaderLines();
$mapMarkup = $map->render($page, 'map_leaflet ,array('markerColour' => 'green'));
$content .= '
<div class="content_bottom">
<!-- Button GM -->
<div class="gmroute" style="z-index: 30001;position: relative;height: 24px;right: 20px;top: 40px;">
<a class="gmroute-link" href="https://www.google.de/maps/dir//Lange+Str.+9+D-72829+Engstingen" target="_blank" style="float: right;">Route planen</a>
</div>
<!-- Map -->
<div class="col span_12">'.$mapMarkup.'</div>
</div>';
Wichtige Dateien - wo finde ich was
Todo -> für aktualisierung von leaflet notwendige Änderungen dokumentieren.
Karte einbinden und wichtige Modul-Optionen
Template Datei (z.B. my-map.php)
JavaScript für Kartendarstellung und Verarbeitung generell.
Wird über dein Templateskript eingebunden.
site/modules/FieldtypeLeafletMapMarker/MarkupLeafletMap.js (oder eigenes). Hier kann man auch eigene JavaScript Funktionen definieren.
JavaScript Inline Script
Erstellt die Instanz deiner Karte und verarbeitet Marker (z.T. mit Funktionen aus der MarkupLeafletMap.js).
Daten manuell auslesen und verarbeiten
Einfaches Beispiel das auch bei AJAX Seiten funktioniert und einen Button zur Google Maps Wegbeschreibung enthält.
// If AJAX put this in _init.php else uncomment next two lines
// $map = wire('modules')->get('MarkupLeafletMap');
// $additionalHeaderData = $map->getLeafletMapHeaderLines();
$myAdress = $page->map_leaflet->address; // outputs the address you entered
$myLat = $page->map_leaflet->lat; // outputs the latitude
$myLng = $page->map_leaflet->lng; // outputs the longitude
$myZoom = $page->map_leaflet->zoom; // outputs the zoom level
$mapMarkup = "
<div id='mleafletmap1'style='height:400px;'></div>
<script>
var mleafletmap1 = new jsMarkupLeafletMap();
mleafletmap1.setOption('zoom', $myZoom);
mleafletmap1.init('mleafletmap1', $myLat, $myLng, 'OpenStreetMap.Mapnik');
var default_marker_icon = L.AwesomeMarkers.icon({ icon: 'home', iconColor: 'white', prefix: 'fa', markerColor: 'darkblue' });
mleafletmap1.addMarkerIcon(default_marker_icon, $myLat, $myLng, '/kontakt/', 'Kontakt', '');
</script>
";
$content .= '
<div class="row">
<div class="gmroute" style="z-index: 30001;position: relative;height: 24px;right: 20px;top: 40px;">
<a class="gmroute-link" href="https://www.google.de/maps/dir//Meine+Str.+9+D-72829+Engstingen" target="_blank" style="float: right;">Route planen</a>
</div>
<div class="col-sm-12" style="height:400px;">'
.$mapMarkup.'
</div>
</div>
Geocoder
Das Modul verwendet an zwei Stellen einen Geocoder.
Am wichtigsten ist er wenn man ein Feld definiert hat. In der Karte gibt es eine kleine Lupe mit der man nach Adressen suchen kann. Dies wird realisiert über die JavaScript Datei:
Control.Geocoder.js
Sie enthält Code für verschiedene Geocoding Dienste. Standard ist Nominatim (von OpenStreetMap). Eventuell muss man diesen Code ab und zu anpassen, wenn sich die APIs der Anbieter ändern.
Die zweite Stelle ist die Eingabe der Default Adresse, wenn man ein Feld definiert. Er befindet sich in der Datei
LeafletMapMarker.js
Hier wird eine Klasse LeafletMapMarker definiert, die eine Funktion geocode() besitzt. Diese nutzt den Nominatim (OpenStreetMap) Dienst. Die Funktion läßt sich geschickt für eigene Zwecke missbrauchen. Man könnte evtl. beim Speichern einer Seite über einen Hook eine Geocodierung vornehmen wenn man eine Seite hat die bereits Adressfelder hat.
Marker
Woher kommen die Marker Popup-Daten?
Generell kommt das Inline Script, dass für die Optionen und Marker der Karte zuständig ist aus MarkupLeafletMap.module. In den Optionen wird angegeben woher (aus welchem Feld) die Inhalte für das Marker Popup kommen sollen:
Titel und URL kommen über die Optionen markerLinkField und markerTitleField
$url = $options['markerLinkField'] ? $page->get($options['markerLinkField']) : '';
$title = $options['markerTitleField'] ? $page->get($options['markerTitleField']) : '';
Der Inhalt selbst kommt über eine Callback-Funktion die man z.b.so definieren kann:
$options = array(
'popupFormatter' => function($page) {
$out[] = "<strong>Contact: $page->phone_number</strong>";
$out[] = "<img src=\"{$page->image->url}\" width=\"100\" height=\"100\" />"; // ** NB: Use escaped double quotes if HTML attributes needed **
return implode('<br/>', $out);
}
);
Erweiterung für Orte auf Referenzen
Beispiele
Siehe Webmynet Cloud für aktuelles Beispiel cloud.webmynet.de (Developer/ProcessWire/Module)
Multiple Markers
Die Render Funktion kann auch mit mehreren Seitenobjekten umgehen und erstellt dann eine Karte mit mehreren Markern.
$items = $pages->find("A SELECTOR THAT GETS YOUR PAGES WITH MARKER FIELDS");
echo $map->render($items, 'YOUR MARKER FIELD');
Multiple Markers von selektierten Seiten
Es sollen Orte (LeafletMapMarker) von Unterseiten geholt werden und aus diesen eine Map mit mehreren Markern generiert werden. Viele Marker auf engem Raum werden geclustert.
Voraussetzungen im Beispiel
- die Kindseiten werden über ihr template selektiert. Im Beispiel wird das durch ein Textfeld im Parent Template festgelegt (Feldname template_title)
- der Leaflet code wird nur bei Bedarf geladen. Das geschieht über eine Switch Anweisung im Repeater
- Leaflet Dateien sind lokal hinterlegt (siehe js und css Anweisungen).
mytemplate.php
$additionalHeaderData .= '
<!-- Styles supporting the use of Leaflet.js -->
<link rel="stylesheet" type="text/css" href="'.$config->urls->templates.'vendors/leaflet/leaflet.css" />
<link rel="stylesheet" type="text/css" href="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-markercluster/MarkerCluster.css" />
<link rel="stylesheet" type="text/css" href="'.$config->urls->templates.'styles/MarkerCluster.Custom.css" />
<!-- Scripts supporting the use of Leaflet.js -->
<script type="text/javascript" src="'.$config->urls->templates.'vendors/leaflet/leaflet.js"></script>
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-markercluster/leaflet.markercluster.js"></script>
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-providers/leaflet-providers.js"></script>
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/MarkupLeafletMap.js"></script>
<!-- Extend Leaflet with Awesome.Markers -->
<link rel="stylesheet" type="text/css" href="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-awesome-markers/leaflet.awesome-markers.css" />
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-awesome-markers/leaflet.awesome-markers.min.js"></script>
';
$map = wire('modules')->get('MarkupLeafletMap');
$options = array('markerColour' => 'green');
$mySelector = 'template=event';
$places = $pages->find($mySelector);
$mapMarkup = $map->render($places, 'location' ,$options);
Leaflet Skripte lokal einbinden (keine externen Skripte laden)
Dies sind die benötigten Header Zeilen. leaflet.css und leaflet.js sollte dann entsprechend vorhanden sein. Der Rest kommat aus dem Modul Verzeichnis.
Hinweis: Font Awesome muß ebenfalls vorhanden sein (Bisher - September 2018, die 4er Version).
$additionalHeaderData = '
<!-- Styles supporting the use of Leaflet.js -->
<link rel="stylesheet" type="text/css" href="'.$config->urls->templates.'vendors/leaflet/leaflet.css" />
<link rel="stylesheet" type="text/css" href="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-markercluster/MarkerCluster.css" />
<link rel="stylesheet" type="text/css" href="'.$config->urls->templates.'styles/MarkerCluster.Custom.css" />
<!-- Scripts supporting the use of Leaflet.js -->
<script type="text/javascript" src="'.$config->urls->templates.'vendors/leaflet/leaflet.js"></script>
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-markercluster/leaflet.markercluster.js"></script>
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-providers/leaflet-providers.js"></script>
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/MarkupLeafletMap.js"></script>
<!-- Extend Leaflet with Awesome.Markers -->
<link rel="stylesheet" type="text/css" href="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-awesome-markers/leaflet.awesome-markers.css" />
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-awesome-markers/leaflet.awesome-markers.min.js"></script>
';
Scroll Zoom abschalten
Am einfachsten im Modul (Nachteil: Vorsicht beim Update)
Evtl. fürs Frontend könnte man auch im Nachhinein in einem eigenen Skript abschalten über die Funktion:
map.scrollWheelZoom.disable(); // map ist die Mapinstanz. Muß evtl. angepasst werden.
Im Frontend (Fieldtype)
/site/modules/FieldtypeLeafletMapMarker/MarkupLeafletMap.js
In MarkupLeafletMap.js die Option hinzufügen.
this.options = {
zoom: 10,
center: null,
scrollWheelZoom: false, // diese option einfügen
};
this._currentURL = '';
this.init = function(mapID, lat, lng, provider) {
if(lat != 0) this.map = L.map(mapID, {
center: [lat, lng],
zoom: this.options.zoom,
scrollWheelZoom: this.options.scrollWheelZoom // und dieses wenn nicht vorhanden
} );
L.tileLayer.provider(provider).addTo(this.map);
}
Im Backend
Hier muß mann den Code des Inputfield anpassen. Direkt in den Optionen hat das bei mir nicht funktioniert. Sieht so aus als ob nur der zoom statt alle Optionen gesetzt wird. Daher einfach nach der Instanzerzeugung der karte den ScrollWheelZoom abschalten:
FieldtypeLeafletMapMarker/InputfieldLeafletMapMarker.js
var map = L.map(document.getElementById(mapId)). setView([lat, lng], options.zoom); // nach dem hier
map.scrollWheelZoom.disable(); // dieses einfügen.
Modul Gesture Handling
Leaflet - Gesture Handling https://elmarquis.github.io/Leaflet.GestureHandling/ Bildet das Verhalten von Google Maps nach:
Desktop
- Karte Ignoriert Mausrad
- User wird Informiert mit ctrl+scrool Karte zoomen
Mobile
- Karte ignoriert Ein Finger Drag
- User wird über 2 Finger Pan informiert
In ProcessWire einbauen
- Unterordner 'dist' in 'Leaflet.GestureHandling' umbenennen und im FieldtypeLeafletMapMarker Modul unter assets ablegen
- anpassen des JS für die Frontendausgabe
/site/modules/FieldtypeLeafletMapMarker/MarkupLeafletMap.js
- Init Funktion anpassen:
this.init = function(mapID, lat, lng, provider) {
if(lat != 0) this.map = L.map(mapID, {
center: [lat, lng],
zoom: this.options.zoom,
scrollWheelZoom: this.options.scrollWheelZoom,
gestureHandling: true
} );
L.tileLayer.provider(provider).addTo(this.map);
}
- Einbinden von CSS und JS
<!-- Extend Leaflet with Gesture Handling --> <link rel="stylesheet" href="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/Leaflet.GestureHandling/leaflet-gesture-handling.min.css" type="text/css"> <script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/Leaflet.GestureHandling/leaflet-gesture-handling.min.js"></script>
Allgemein
<div id='map'></div>
<script>
var cities = new L.LayerGroup();
L.marker([39.61, -105.02]).bindPopup('This is Littleton, CO.').addTo(cities),
L.marker([39.74, -104.99]).bindPopup('This is Denver, CO.').addTo(cities),
L.marker([39.73, -104.8]).bindPopup('This is Aurora, CO.').addTo(cities),
L.marker([39.77, -105.23]).bindPopup('This is Golden, CO.').addTo(cities);
var mbAttr = 'Map data © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' +
'<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' +
'Imagery © <a href="http://mapbox.com">Mapbox</a>',
mbUrl = 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw';
var grayscale = L.tileLayer(mbUrl, {id: 'mapbox.light', attribution: mbAttr}),
streets = L.tileLayer(mbUrl, {id: 'mapbox.streets', attribution: mbAttr});
var map = L.map('map', {
center: [39.73, -104.99],
zoom: 10,
layers: [grayscale, cities]
});
var baseLayers = {
"Grayscale": grayscale,
"Streets": streets
};
var overlays = {
"Cities": cities
};
L.control.layers(baseLayers, overlays).addTo(map);
</script>
Popup über URL Parameter öffnen
https://stackoverflow.com/questions/29004617/open-leaflet-marker-using-url-parameter-not-working-now-that-markercluster-is-us
Weitere Kartenanbieter
Um andere Kartenprovider zu nutzen gibt es das Plugin leaflet-providers
https://github.com/leaflet-extras/leaflet-providers
Das ist standardmäßig im Ordner
modules/FieldtypeLeafletMapMarker/assets/leaflet-providers
Man benötigt nur die leaflet-providers.js Datei. Im Prinzip ist die Datei eine Liste von verschiedenen Anbietern und dazu notwendigen Einstellungen.
Einbindung in die Karte
Wichtig ist dass das JavaScript vor leaflet.js geladen wird:
<script type="text/javascript" src="'.$config->urls->siteModules.'FieldtypeLeafletMapMarker/assets/leaflet-providers/leaflet-providers.js"></script>
Man gibt die gewünschte Karte beim Einbinden des Moduls in ein Template über die Option provider an.
"provider" => 'OpenStreetMap.Mapnik', // Standardkarte
So lassen sich auch andere Anbieter nutzen:
"provider" => 'Stadia.AlidadeSmooth', // Achtung Stadia geht nur wenn man sich registriert
Funktionsweise
In der init Funtion wird
// ...
Stadia: {
url: 'https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.png',
options: {
maxZoom: 20,
attribution: '© <a href="https://stadiamaps.com/">Stadia Maps</a>, © <a href="https://openmaptiles.org/">OpenMapTiles</a> © <a href="http://openstreetmap.org">OpenStreetMap</a> contributors'
},
variants: {
AlidadeSmooth: {
url: 'https://tiles.stadiamaps.com/tiles/alidade_smooth/{z}/{x}/{y}{r}.png'
},
AlidadeSmoothDark: {
url: 'https://tiles.stadiamaps.com/tiles/alidade_smooth_dark/{z}/{x}/{y}{r}.png'
},
OSMBright: {
url: 'https://tiles.stadiamaps.com/tiles/osm_bright/{z}/{x}/{y}{r}.png'
},
Outdoors: {
url: 'https://tiles.stadiamaps.com/tiles/outdoors/{z}/{x}/{y}{r}.png'
}
}
},
// ...
Customization Examples
Marker direkt verlinken (kein PopUp)
var marker = L.marker([52, 12], {});
marker.url = 'www.google.com'
marker.on('click', function(){
window.location = (this.url);
});
Probleme beheben
Feld zeigt an Error Geocoding
File Cache komplett löschen (assets/cache/...)