KML Dateien in leaflet darstellen

Okay, alles klar danke für den Tipp.
Hab das gesamte Verzeichnis nun mal testweise auf einen “richtigen” Webserver hochgeladen. Und siehe da, es funktioniert! :slight_smile:

Eine .csv Datei konnte ich gerade einlesen. KML funktioniert noch nicht - schade. Ich generiere mir über Excel .kml Dateien die ich anschließend in der App maps.me einlese. Wäre natürlich schön, wenn ich die gleiche .kml auch in leaflet einlesen könnte.

Hat da jemand schon mit gearbeitet? Oder kann mir beim CSV Import weiterhelfen? Momentane erstellt mir leaflet nur die Marker. Ich würde aber gern noch per Mausklick Informationen hinterlegen (im besten Fall eine Tabelle).
Zudem würde ich gerne die einzelnen importieren DSV oder KML Dateien über solch einen Ebenenbutton ein- und ausblenden können.

Es gibt auch ganz einfache Server, die man in der Kommandozeile im gewünschten Verzeichnis starten kann:
https://github.com/mrdoob/three.js/wiki/How-to-run-things-locally#run-local-server

axelr hat ja in #4 und #9 schon gezeigt, dass es prinzipiell geht. Wenn es bei dir nicht funktioniert, muss man eben schauen warum nicht, siehe #8.

Das nennt sich Popup und kann auch HTML enthalten. Hier ein Beispiel, das ich gefunden habe (ist zwar für Mapbox JS, der omnivore-Teil müsste aber so auch mit Leaflet gehen):
https://www.mapbox.com/mapbox.js/example/v1.0.0/omnivore-kml-tooltip/
Siehe auch
https://leafletjs.com/reference-1.3.4.html#layer-bindpopup

Dazu einen Layer Switcher anlegen und z.B. den/die KML-Layer (“runLayer”) in der “.on(‘ready’, function() {” als Overlay hinzufügen, siehe:
https://leafletjs.com/reference-1.3.4.html#control-layers-addoverlay

Edit: Oder einfacher, den Layer Switcher zum Schluß anlegen und die KML-Layer als “overlays” eintragen (warten auf ‘ready’ sollte dazu nicht nötig sein):
https://leafletjs.com/reference-1.3.4.html#control-layers

Okay, bin schon wieder eine Ecke weiter.
KML Dateien kann ich nun auch importieren.

Habe nun das Problem, dass ich wohl so einen layer switch hinzufügen kann, aber ich habe direkt zwei untereinander? Brauch eigtl. nur einen :smiley: Zudem habe ich keine Boxen zum “anhaken” sondern nur dieses kleinen Kreise. Ich glaube man nennt sie checkboxes und radioboxes.

Ich möchte aber durchaus mehrere KML Ebenen einblenden und nicht nur eine.

<!DOCTYPE html>
<html>
<head>
    <title>Meine Karte</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="/desktop/leaflet/leaflet.css"/>
</head>
<body>
    <div id="map" style="height: 900px"></div>
    <script src="/desktop/leaflet/leaflet.js"></script>
	<script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.2.0/leaflet-omnivore.min.js'></script>	
    <script>
        var map = L.map('map').setView([51.864, 8.615], 13);
        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 pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml')
		var ansaugstellen = omnivore.kml('/desktop/kml/ansaugstellen_google.kml')

			.on('ready', function() {
			map.fitBounds(pumpenstationen.getBounds());

			pumpenstationen.eachLayer(function(layer) {
            layer.bindPopup(layer.feature.properties.description);
			
		var overlays = {
		"Pumpenstationen": pumpenstationen,
		"Ansaugstellen": ansaugstellen		};
			L.control.layers(overlays).addTo(map);			

        });
    })
	
</script>
</body>
</html>

Die Initialisierung des Layer Control selbst muss außerhalb der ‘ready’ Event Funktion stehen, das soll nicht für jeden KML Layer ausgeführt werden, also den “var overlays = …” Abschnitt ganz ans Ende außerhalb der Klammern, vor das “” verschieben.

Der Konstruktor erwartet zwei Parameter, zuerst die Base Layer (hier leer), dann die Overlays:


L.control.layers({}, overlays).addTo(map);	

Vielen Dank für die Hilfe. Läuft soweit.
Ein Problem habe ich noch und zwar wird das PopUp nur an einer Ebene angehangen.
Liegt vermutl. an diesem Codeabschnitt:

			pumpenstationen.eachLayer(function(layer) {
            layer.bindPopup(layer.feature.properties.description);

Ich weiß nur noch nicht, wie ich es hinbekomme, dass bei jedem Layer die description ausgelesen wird.

Gibt es zudem die Möglichkeit die Layer standardmäßig zu aktivieren? Wenn ich die Karte nun öffne, muss ich alle Layer erst per Mausklick aktivieren.

Ist mir oben schon aufgefallen, dachte aber das wäre gekürzt: das “.on(‘ready’, …” muss für jeden Layer – also auch “ansaugstellen” – aufgerufen und die Referenzen auf die Layer Variable entsprechend angepasst werden. Wenn es noch mehr Layer werden, könnte man das auch in einer gemeinsamen Funktion generalisieren.

Zum Verständnis: beim Aufruf von “omnivore.kml” wird erst mal nur ein leerer Layer initialisiert und zurückgegeben und das Laden der Daten von der übergebenen URL passiert dann asynchron im Hintergrund. Auf die Daten im Layer kann man erst zugreifen, wenn sie vollständig geladen sind, dafür gibt es die Benachrichtigung über den “ready” Event.

Damit das “map.fitBounds” nicht nur auf die zuletzt geladenen Daten zoomt, müsste man noch die Ausdehnung aller bisher geladenen Layer berücksichtigen (oder einfach weglassen), z.B. über “extend”:
https://leafletjs.com/reference-1.3.4.html#latlngbounds-extend

Da fehlt dann noch ein “.addTo(map);”, um die Layer der Karte hinzuzufügen, siehe z.B.:
https://github.com/mapbox/leaflet-omnivore#async–events

So recht komme ich nicht mehr überall mit.
Also es kommen definitiv noch mehr Layer hinzu. Also macht eine gemeinsame Funktion Sinn. Das “.on('ready[…]” jedes mal zu wiederholen macht den Code nur unnötig lang.
Wie müsste der Code dann lauten?

Layer automatisch einblenden funktioniert nun auch.

<!DOCTYPE html>
<html>
<head>
    <title>Maps</title>
    <meta charset="utf-8" />
    <link rel="stylesheet" href="/desktop/leaflet/leaflet.css"/>
</head>
<body>
    <div id="map" style="height: 900px"></div>
    <script src="/desktop/leaflet/leaflet.js"></script>
	<script src='https://api.mapbox.com/mapbox.js/plugins/leaflet-omnivore/v0.2.0/leaflet-omnivore.min.js'></script>	
    <script>
        var map = L.map('map').setView([52.035, 6.824], 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 pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml')
			.addTo(map)
		var ansaugstellen = omnivore.kml('/desktop/kml/ansaugstellen.kml')
			.addTo(map)
		
			.on('ready', function() {
			
			ansaugstellen.eachLayer(function(layer) {
            layer.bindPopup(layer.feature.properties.description);
        });
    })
				.on('ready', function() {
			
			pumpenstationen.eachLayer(function(layer) {
            layer.bindPopup(layer.feature.properties.description);
        });
    })

		var overlays = {
			"Pumpenstationen": pumpenstationen,
			"Ansaugstellen": ansaugstellen		};
				L.control.layers({}, overlays).addTo(map);	
</script>
</body>
</html>

Mit Schleife, aber ungetestet:


var pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml')
	.addTo(map)
var ansaugstellen = omnivore.kml('/desktop/kml/ansaugstellen.kml')
	.addTo(map);

// alle die du mit Popup haben willst hier aufführen
var kmls = [pumpenstationen, ansaugstellen];

	map.on('ready', function() {
	
		// in Schleife alle oben in kmls gespeicherten durchgehen
		for (var i=0;i<kmls.length;i++) {
			kmls[i].eachLayer(function(layer) {
				layer.bindPopup(layer.feature.properties.description);
			});
		}
	})

Erklärung: alle Layer die du mit Popup versehen willst speicherst du in dem Array kmls, das du dann in der for-Schleife nacheinander durchgehst, d.h. die Variable i zählt immer hoch, im ersten Durchlauf ist i=0, damit ist kmls[0] dann pumpenstationen.

edit2: OK, wenn man sichergehen will, dass die KML-Dateien alle tatsächlich eingelesen sind bevor man die Popups anbindet (und das sollte mensch wohl), dann wirds etwas komplizierter. Keine Ahnung, ob das nötig ist, aber sauberer/sicherer ist es (wieder ungetestet):


var popupBindenWennAlleGeladen = function() {
	// eins ist geladen worden
	fertigGeladeneKMLs++;
	// wenn jetzt alle geladen sind, dann popups binden
	if(fertigGeladeneKMLs == kmls.length) {
		// in Schleife alle durchgehen
		for (var i=0;i<kmls.length;i++) {
			kmls[i].eachLayer(function(layer) {
				layer.bindPopup(layer.feature.properties.description);
			});
		}
	} else {
		// noch nicht alle geladen => nix tun
	}
}

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);

// alle die du mit Popup haben willst hier aufführen
var kmls = [pumpenstationen, ansaugstellen];

// zu anfang ist keins davon geladen
var fertigGeladeneKMLs = 0;


Danke. Den zweiten Code konnte ich erfolgreich einbauen.
Für die unterschiedlichen Ebenen würde ich gerne unterschiedliche Farben definieren.

Habe deswegen den Code folgendermaßen angepasst:

        var pumpenstationen = omnivore.kml('/desktop/kml/pumpenstationen.kml')
            .addTo(map).on('ready', popupBindenWennAlleGeladen) 
                this.eachLayer(function(marker){
                'marker-color': '##8a2be2',
                }));


In der Console gibt er die Fehlermeldung, dass ihm das token “:” hinter “marker-color” nicht passt.
Komme jedoch nicht zu einer Lösung. Habe mich an dieser Anleitung versucht zu halten:

https://www.mapbox.com/mapbox.js/example/v1.0.0/markers-from-csv-custom-style/

Desweiteren wollte ich eine Clusterung einbauen. Code wie folgt eingefügt:

		var unterflurhydranten_vr = omnivore.kml('/desktop/kml/unterflurhydranten_vr.kml')
			.addTo(map).on('ready', popupBindenWennAlleGeladen);
			var markers = L.markerClusterGroup({
			showCoverageOnHover: false,
			maxClusterRadius: 50
		});

Quelle: https://bl.ocks.org/wboykinm/9374994
Auch hier erhalte ich Fehlermeldungen. “L.markerClusterGroup” is not a function. Und anschließend zerschießt er die Funktion “PopUp wenn alle geladen”

Bitte achte genau und peinlichst auf die Klammersetzung! Desweiteren dürfte das Komma hinter dem Farbwert auch schon zu viel sein :wink:
und


marker.setIcon(L.mapbox.marker.icon(

fehlt gänzlich … wenn schon copy&paste, dann ordentlich :stuck_out_tongue:

Nachtrag: also L.mapbox.marker natürlich nur dann, wenn du auch das mapbox Plugin nutzt, wenn nicht, dann ohne mapbox und dann solltest du dir auch eher die Beispiele direkt unter https://leafletjs.com/examples.html angucken

Du hast dir das “leaflet-markercluster” Plugin geladen bzw. es via script eingebunden?

Nachtrag: gibt’s auch eine schöne Erklärung direkt bei leafletjs.com, siehe Link oben.

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