KML Dateien in leaflet darstellen

Die Dokumentation auf der Seite bei leaflet bezieht sich aber immer nur darauf, wenn ich den Marker direkt einbinde und nicht über den Weg des Import mit omnivore gehe.

Schaue ich bei omnivore in der Dokumentation finde ich das hier:
https://github.com/mapbox/leaflet-omnivore#custom-layers

var customLayer = L.geoJson(null, {
    // http://leafletjs.com/reference.html#geojson-style
    style: function(feature) {
        return { color: '#f00' };
    }
});

Wie müsste ich den Code denn einfügen und wo? Und wenn ich das “customLayer” durch die Layerbenennung ersetze, die ich umfärben möchte, funktioniert es auch nicht.

Mir bereitet es Schwierigkeiten zu verstehen wie ich die in der leaflet Dokumentation angegebenen Codezeilen einsetze, wenn ich meine Marker doch per omnivore importiere.

Kann mir jemand weiterhelfen?

Wie wär’s, wenn du eh omnivore nutzt, dass du dann auch die “mapbox.js” mit einbindest? Dann kannst du ohne weiteres “L.mapbox.marker.icon” nutzen. Wenn du das nicht willst, dann wird es in der Tat nicht so einfach, das normale Leaflet Marker blau in etwas anderes zu verwandeln, da ist der Aufwand definitiv höher.

Übrigens, wie du die Marker importierst ist egal, zum Schluß und letztendlich handelt es sich immer im L.icon, siehe Dokumentation

Ob man Mapbox.js verwenden soll, ist vermutlich Ansichtssache. Es gibt viele schöne Beispiele und den Marker-Stil ändern geht wohl einfacher. Andererseits ist es halt eine spezielle Erweiterung zu Leaflet und macht die Sache noch komplizierter, weil noch eine weitere Bibliothek dazu kommt, mit der sich auch weniger auskennen. Ich hätte zum Beispiel keine Lust, Fragen dazu zu beantworten. Vielleicht kann man sich aber dann ja an den Mapbox Support wenden.

Um mit Standard-Leaflet die Marker Farbe zu ändern oder ein inneres Symbol zu setzen, gibt es diese Optionen:

Plugins, z.B.:
Leaflet.awesome-markers
Leaflet.ExtraMarkers

CircleMarker - Farbe, Radius, usw. konfigurierbar:
Using GeoJSON with Leaflet - Abschnit “pointToLayer”

Eigene Marker Icons definieren - etwas aufwendiger:
Markers With Custom Icons

Versuche gerade dieses Plugin zu installieren:
https://github.com/lvoogdt/Leaflet.awesome-markers

Die .css Dateien (vom Plugin + fontawesome) habe ich jeweils im Head Bereich eingebaut.
.js (awesome-markers) im Body Bereich.

Ich möchte gerne die fontawesome Icons nutzen. Die setze ich in einem anderen Bereich auch schon ein.

Ich habe mal einen Testmarker erstellt (siehe Abschnitt “Marker erstellen”).
Dieser wird erzeugt, in rot dargestellt. Jedoch wird das gewünschte Icon nicht eingeblendet.
Dieses Icon sollte erscheinen:

https://fontawesome.com/icons/ambulance?style=solid

Es erscheint aber das Rechteck, welches man desöfteren sieht wenn eine Grafikdatei nicht vorhanden ist. Ich tippe darauf, dass ich die .css Datei von fontawesome nicht korrekt eingebaut habe. Jedoch schlägt momentan jeder Versuch fehl.

Nun möchte ich natürlich den Markern, die ich mit omnivore erstelle die entsprechende Farbe + Icon von fontawesome geben. Das schlägt leider auch fehl. Egal ob ich das "{icon: redMarker} hinter die .kml Datei packe oder vor dem .addTo(map) Ausdruck. Es wird nicht umgesetzt.

<!DOCTYPE html>
<html>
<head>
    <title>Maps</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="/desktop/leaflet/leaflet.css"/>
	<link rel="stylesheet" href="https://code.ionicframework.com/ionicons/1.5.2/css/ionicons.min.css">
	<link rel="stylesheet" href="plugins/awesome_markers/dist/leaflet.awesome-markers.css">
	<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
</head>
<body>
    <div id="map" style="height: 900px"></div>
    <script src="leaflet/leaflet.js"></script>
	<script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.2.0/leaflet-omnivore.min.js'></script>
	<script src="plugins/awesome_markers/dist/leaflet.awesome-markers.js"></script>
    <script>
        var map = L.map('map').setView([X, Y], 12);
        mapLink = 
            '<a href="https://openstreetmap.org">OpenStreetMap</a>';
        L.tileLayer(
            'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: 'Map data &copy; ' + mapLink,
            maxZoom: 20,
            }).addTo(map);
		var popupBindenWennAlleGeladen = function() {			
			fertigGeladeneKMLs++;							// eins ist geladen worden
			if(fertigGeladeneKMLs == kmls.length) {			// wenn jetzt alle geladen sind, dann popups binden
			for (var i=0;i<kmls.length;i++) {				// in Schleife alle durchgehen
			kmls[i].eachLayer(function(layer) {
			layer.bindPopup(layer.feature.properties.description);
								});
							}
						} else {							// noch nicht alle geladen => nix tun
						}
					}
// Marker erstellen
		var redMarker = L.AwesomeMarkers.icon({
			icon: 'fas fa-ambulance',
			markerColor: 'red'
			});
		  L.marker([51.941196,4.512291], {icon: redMarker}).addTo(map);				
	
// KML Dateien einbinden
		var pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml', {icon: redMarker})
			.addTo(map).on('ready', popupBindenWennAlleGeladen);
		var ansaugstellen = omnivore.kml('/desktop/kml/ansaugstellen.kml')
			.addTo(map).on('ready', popupBindenWennAlleGeladen);
		var unterflurhydranten_vr = omnivore.kml('/desktop/kml/unterflurhydranten_vr.kml')
			.addTo(map).on('ready', popupBindenWennAlleGeladen)
// Layer auffuehren, die PopUp Info bekommen sollen
		var kmls = [pumpenstationen, ansaugstellen, unterflurhydranten_vr];
// zu anfang ist keins davon geladen
		var fertigGeladeneKMLs = 0;
		var overlays = {
			"Pumpenstationen": pumpenstationen,
			"Ansaugstellen": ansaugstellen,
			"Unterflurhydranten": unterflurhydranten_vr	
			};
				L.control.layers({}, overlays).addTo(map);	
</script>
</body>
</html>

Wild geraten: evtl. leaflet.awesome-markers.css nach fontawesome einbinden?

In solchen Fällen finde ich es hilfreich, erst mal in einem minimalen Beispiel in einer separaten Datei zu testen. Z.B. nur HTML + Fontawesome, dann nur einfacher AwesomeMarkers Marker.

Okay, Icon wird nun korrekt eingebunden.
Fehlerursache war eine fehlerhafte Icon Bezeichnung. Das Präfix muss getrennt aufgehört werden, wie folgt:

// Marker erstellen
		var redMarker = L.AwesomeMarkers.icon({
			icon: 'ambulance',
			prefix: 'fa',
			markerColor: 'red'
			});
		  L.marker([***,***], {icon: redMarker}).addTo(map);				

So… Nun muss ich das noch mit Omnivore hinbekommen. Hat da jemand Tipps?

leaflet-omnivore liefert ja schon Marker, über die in


kmls[i].eachLayer(function(layer) {

iteriert wird. D.h. statt einen zusätzlichen neuen Marker anzulegen (“L.marker(…”) , vielleicht das einfach Icon austauschen mit Marker.setIcon (nicht getestet)?


layer.setIcon(redMarker);

Moment, der “zusätzliche Marker” In Beitrag mit dem Code

L.marker([***,***], {icon: redMarker}).addTo(map);

war nur zum testen ob das Fontawesome Icon geladen wird.
Marker sollen nur über omnivore gesetzt werden. Diese Icons der gesetzten Marker möchte ich ändern.

Habe ich dich richtig verstanden? Habe den Code von dir eingesetzt, jedoch wird jetzt der . in der nächste Zeile bemängelt.
Ich weiß nicht genau, was ich ersetzen muss


<!DOCTYPE html>
<html>
<head>
    <title>Maps</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="/desktop/leaflet/leaflet.css"/>
	<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.5.0/css/all.css" integrity="sha384-B4dIYHKNBt8Bc12p+WXckhzcICo0wtJAoU8YZTY5qE0Id1GSseTk6S+L3BlXeVIU" crossorigin="anonymous">
	<link rel="stylesheet" href="plugins/awesome_markers/dist/leaflet.awesome-markers.css">
</head>
<body>
    <div id="map" style="height: 900px"></div>
    <script src="leaflet/leaflet.js"></script>
	<script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.2.0/leaflet-omnivore.min.js'></script>
	<script src="plugins/awesome_markers/dist/leaflet.awesome-markers.js"></script>
    <script>
        var map = L.map('map').setView([X, Y], 12);
        mapLink = 
            '<a href="https://openstreetmap.org">OpenStreetMap</a>';
        L.tileLayer(
            'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: 'Map data &copy; ' + mapLink,
            maxZoom: 20,
            }).addTo(map);
		var popupBindenWennAlleGeladen = function() {			
			fertigGeladeneKMLs++;							// eins ist geladen worden
			if(fertigGeladeneKMLs == kmls.length) {			// wenn jetzt alle geladen sind, dann popups binden
			for (var i=0;i<kmls.length;i++) {				// in Schleife alle durchgehen
			kmls[i].eachLayer(function(layer.setIcon(redMarker)) {			
			layer.bindPopup(layer.feature.properties.description);
								});
							}
						} else {							// noch nicht alle geladen => nix tun
						}
					}
					
// Marker erstellen
		var redMarker = L.AwesomeMarkers.icon({
			icon: 'ambulance',
			prefix: 'fa',
			markerColor: 'red'
			});
// KML Dateien einbinden
		var pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml')
			.addTo(map).on('ready', popupBindenWennAlleGeladen);
		var ansaugstellen = omnivore.kml('/desktop/kml/ansaugstellen.kml')
			.addTo(map).on('ready', popupBindenWennAlleGeladen);
		var unterflurhydranten_vr = omnivore.kml('/desktop/kml/unterflurhydranten_vr.kml')
			.addTo(map).on('ready', popupBindenWennAlleGeladen)
// Layer auffuehren, die PopUp Info bekommen sollen
		var kmls = [pumpenstationen, ansaugstellen, unterflurhydranten_vr];
// zu anfang ist keins davon geladen
		var fertigGeladeneKMLs = 0;
		var overlays = {
			"Pumpenstationen": pumpenstationen,
			"Ansaugstellen": ansaugstellen,
			"Unterflurhydranten": unterflurhydranten_vr	
			};
				L.control.layers({}, overlays).addTo(map);	
</script>
</body>
</html>

Omnivore bietet da auch eine Dokumentation zu Custom Layers. Ich denke das könnte für mich passen, ich weiß aber nicht wie ich das umsetzen muss.

https://github.com/mapbox/leaflet-omnivore#custom-layers

Nein, ich meinte eben


L.marker([***,***], {icon: redMarker}).addTo(map);

mit


layer.setIcon(redMarker);

ersetzen.

Setzt dann aber für alle Layer das gleiche Icon.

Gibt es eine Eigenschaft in den KML Dateien, nach der man abfragen könnte, welches Icon gesetzt werden soll?

Das wäre eine Möglichkeit. Aber nicht über die style function, sondern mit der “pointToLayer” function Using GeoJSON with Leaflet - Abschnitt “pointToLayer”

Oder:

b) wie oben erwähnt in der Schleife über alle Layer nach Eigenschaft in den Daten (layer.feature.properties.?) unterscheiden
c) einen zweiten “.on(‘ready’, setMarkerIconX)” Handler je Layer, der jeweils ein anderes Marker Icon setzt

In der KML Datei gibt es eine Eigenschaft die die Farbe definieren könnte.
Allerdings sind diese KML primär für die App maps.me gestaltet, hier habe ich max. 7 vorgegebene Farben zur Auswahl.

Die gleichen Daten sollen nun noch als Webanwendung zur Verfügung stehen. Hier möchte ich mir schon den Luxus gönnen mehr Farben einzusetzen und auch verschiedene Icons.

Ich habe mich deshalb mal an “PointToLayer” versucht
https://leafletjs.com/examples/geojson/

// Marker erstellen
		var redMarker = L.AwesomeMarkers.icon({
			icon: 'ambulance',
			prefix: 'fa',
			markerColor: 'red'
			}); 
// Custom Layer rot
		var redMarker = L.geoJson(null, {
		pointToLayer: function (feature) {
        return L.circleMarker(redMarker);

// KML Dateien einbinden
		var pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml', redMarker)
			.addTo(map).on('ready', popupBindenWennAlleGeladen);


Ich bekomm es einfach nicht hin :frowning:

Erst mal sind da Syntax-Fehler: es fehlen die schließenden Klammern der pointToLayer Funktion und zu L.geoJson. Und die Variable redMarker ist doppelt definiert.

Ein ver­nünf­tiger Editor könnte helfen, zumindest die groben Syntaxfehler gleich anzumeckern und die einander entsprechenden Klammern anzuzeigen, z.B. https://code.visualstudio.com/

Eine Vertiefung der JavaScript Grundlagen wäre vermutlich auch hilfreich, um den Code besser zu verstehen. Hast du da schon entsprechende Dokus/Einführungen zu JavaScript?

Ich nutze den Notepad++
Werde mir mal den verlinkten Editor ansehen.

Nein, ich hangel mich von Plugin Doku zu Plugin Doku. Und zum Teil halt bei der Leaflet Doku.
Würde der Code denn ansonsten funktionieren, also mangelt es “nur” an den Klammern oder sind dort sonst noch grobe Fehler?

Wäre interessant für mich einen lauffähigen Code zu sehen der halt das macht, was ich möchte. Daran könnte ich sicher ne Menge nachvollziehen.

Man kann den icon in omnivore ändern ohne zusätliche Bibliotheken.
http://www.roeltgen.com/foren/firechheForum64423-bunt.html
Das ist eine nachträgliche Änderung nach dem Laden der Dateien.
Den direkten Weg bekomme ich nicht hin, da ich nicht auf L.geoJSON() zugreifen kann.

<!DOCTYPE html>
<html>
<head>
    <title>firechheForum64423-bunt</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet-0.7/leaflet.css"/>
</head>
<body>
    <div id="map" style="height: 300px"></div>
    <script src="http://cdn.leafletjs.com/leaflet-0.7/leaflet.js"></script>
	<script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.2.0/leaflet-omnivore.min.js'></script>	
	<script>
		var myRedIcon = L.icon({
			iconUrl: 'kml/marker-red.png',
			iconSize:     [25, 41],
			iconAnchor:   [12, 41],
			popupAnchor:  [0, -35] 
		});
		
		var map = L.map('map').setView([51.864, 8.615], 13);
		mapLink = 
			'<a href="http://openstreetmap.org">OpenStreetMap</a>';
		L.tileLayer(
			'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
				attribution: 'Map data &copy; ' + mapLink,
				maxZoom: 20,
			}).addTo(map);
		
		var example = omnivore.kml('kml/2012-02-10.kml')
			.on('ready', function() {
				//map.fitBounds(this.getBounds());
				this.eachLayer(function(layer) {
					layer.bindPopup(layer.feature.properties.name);
				});
			})
			.addTo(map);
		
		var gipfel = omnivore.csv('kml/gipfel-mapboxTerminus.csv')
			.on('ready', function() {
				map.fitBounds(this.getBounds());
				this.eachLayer(function(layer) {
					layer.bindPopup(layer.feature.properties.name + " (" + layer.feature.properties.country + ")");
					if (layer instanceof L.Marker) {layer.setIcon(myRedIcon)};
				});
			})
			.addTo(map);
		
		var overlays = {
			"Example": example,
			"Gipfel": gipfel,
		};
		L.control.layers({}, overlays).addTo(map);	
		
	</script>
</body>
</html>

Ich hoffe, es hilft
Axel

Nein, das ist noch nicht ganz das, was wir wollen. Ich klinke mich aber für heute aus.

Es hilft schon, Code von anderen anzuschauen. Ich glaube halt, dass der Lerneffekt viel größer ist, wenn man sich selbst durchbeißt. Zudem gebe ich für meinen Teil hier gerne Hilfe zur Selbsthilfe, fertige Lösungen baue ich aber lieber für meine eigenen Projekte.

Zu JavaScript:
https://developer.mozilla.org/de/docs/Learn/JavaScript
(Links führen dort bei mir auf englische Seiten, ggf. oben umschalten)

@axelr:

Habe ich soweit hinbekommen mit einer Ausnahme:
Die Funktion “PopUpWennAlleGeladen” habe ich mit einem zusätzlichem .on(‘ready’)… nochmal unten drunter eingefügt.

Ist das so korrekt oder gibt es eine elegantere Lösung?

@ikonor

Vielen Dank für die Doku. Wird mir sicher weiterhelfen.
Denn die Möglichkeit Icons von Fontawesome einzubinden gefällt mir sehr.

		var unterflurhydranten_vr = omnivore.kml('/desktop/kml/unterflurhydranten_vr.kml')
			.addTo(map).on('ready', function() {
				map.fitBounds(this.getBounds());
				this.eachLayer(function(layer) {
					if (layer instanceof L.Marker) {layer.setIcon(myRedIcon)};
				});
			})
			.on('ready', popupBindenWennAlleGeladen);

Ich schau mal, ob ich zu der Custom Layer Variante mit pointToLayer aus #39 noch eine Schritt-für-Schritt Anleitung schreiben kann, zum besseren Verständnis.

Das liegt an der alten Version 0.7 bei dir. Die Funktion hieß da noch L.geoJson() und wurde inzwischen umbenannt, weil die Klasse L.GeoJSON heißt.

Das wäre wirklich sehr nett :slight_smile:

Zur Veranschaulichung dieses Beispiel (#39) mal Schritt für Schritt:

1. Omnivore Custom Layers Style Beispiel kopieren (Kommentar entfernt)


    var customLayer = L.geoJson(null, {
        style: function(feature) {
            return { color: '#f00' };
        }
    });

Da gehören auch die schließenden Klammern dazu, die bei dir in #39 fehlen, also die letzten beiden Zeilen von oben:


        }
    });

Zu jeder öffnenden Klammer muss es auch eine entsprechende schließende Klammer geben, in umgekehrter Reihenfolge. Den Inhalt rückt man ein, so dass man die zusammengehörenden Zeilen mit der öffnenden (hinten) und schließenden geschweiften Klammer (vorne) leicht zuordnen kann.

Bei dieser oft üblichen kompakten Schreibweise oben werden die Parameter (mit Komma getrennte Übergabe-Werte in runden Klammern) einer Funktion an Ort und Stelle definiert. Auch über mehrere Zeilen, wenn eine Funktion oder ein Objekt übergeben wird.

Die Funktions-Parameter kann man aber auch erst Variablen zuweisen und dann die Variablen übergeben, das macht es etwas leserlicher. Hier das obige kompakte Beispiel aufgetrennt:


    var redStyle = function(feature) {
        return { color: '#f00' };
    };
    var options = {
        style: redStyle
    };
    var customLayer = L.geoJson(null, options);

Jeweils in einer Zeile:


    var redStyle = function(feature) { return { color: '#f00' }; };
    var options = { style: redStyle };
    var customLayer = L.geoJson(null, options);

Der “options” Parameter ist ein Objekt und kommt bei Leaflet öfter vor, hier für L.geoJSON.

Übung ;): Obige drei Zeilen wieder in das Ausgangsformat unter 1. bringen: Variablen-Referenzen durch Wert ersetzen, Zeilenumbruch nach öffnender und vor schließender geschweifter Klammer.

2. customLayer Parameter hinzufügen


    var pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml', null, customLayer)
        .addTo(map).on('ready', popupBindenWennAlleGeladen);

Bei deinem Code in #39 fehlt der “null” Parameter in der Mitte. Der ist aber wichtig, damit die Position und Reihenfolge der Parameter stimmt. Optionale Parameter können nur am Ende weggelassen werden, dazwischen muss dann eben “null” übergeben werden.

Der momentane Code sieht jetzt so aus und sollte ohne Fehler im Editor oder der Chrome Console laufen:


    var customLayer = L.geoJson(null, {
        style: function(feature) {
            return { color: '#f00' };
        }
    });

    var pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml', null, customLayer)
        .addTo(map).on('ready', popupBindenWennAlleGeladen);

Geändert hat sich noch nichts, da die “style” Option nur für Vektoren (Linien, Polygone und Punkte als CircleMarker) gilt, nicht aber für Standard-Marker mit Icon.

3. pointToLayer statt style

Jetzt ersetzen wir die “style” mit der “pointToLayer” Option, anhand des Beispiels aus dem GeoJSON Tutorial:


    var geojsonMarkerOptions = {
        radius: 8,
        fillColor: "#ff7800",
        color: "#000",
        weight: 1,
        opacity: 1,
        fillOpacity: 0.8
    };

    var customLayer = L.geoJson(null, {
      pointToLayer: function (feature, latlng) {
          return L.circleMarker(latlng, geojsonMarkerOptions);
      }
    });

Die “geojsonMarkerOptions” fügen wir nur kurz zum Testen ebenfalls hinzu, damit wir diesen Schritt auch gleich auführen können - die Pumpenstationen werden jetzt als Kreise dargestellt.

4. Awesome Markers statt CircleMarker

Diesen Schritt überlasse ich jetzt dir.

Hinweise: