KML Dateien in leaflet darstellen

Zu CORS: https://de.wikipedia.org/wiki/Cross-Origin_Resource_Sharing
Das Javascript fühlt sich nicht dazu berechtigt, einen file:-Link auf diesem Pfad zu öffnen.

Wieso wird die kml mit einem absoluten Pfad von D:/ aus angesprochen?

–ks

Also,

sieht mir doch sehr nach Windows aus :slight_smile:

–ks

Ich arbeite momentan zu 100% lokal unter Windows 10. Webbrowser Google Chrome. Ja, die html Datei öffne ich per Doppelklick.

Liegt da der Fehler?

Ja … die Zeiten, als man HTML Dateien direkt vom Explorer geöffnet und dann auch noch Dat(ei)en (und dann auch noch per file://) nachgeladen hat sind definitiv schon länger vorbei. Schonmal was von XAMPP gehört?

Das läuft nicht. Das läuft auch in ganz vielen anderen Fällen nicht (PHP oder so was). Zur Web-Entwicklung brauchst du zwingend einen lokalen Webserver, damit eine HTTP-Umgebung vorliegt.

Lokaler Webserver ist kein Hexenwerk, es muss gar kein Apache sein (obwohl das 2fellos der beste ist), ich hatte zu Zeiten meiner HTML-Entwicklung einen Janaserver laufen. Ist schnell installiert, Konfig lässt sich durchklicken, in zehn Minuten haste das Ding am Start.

Die Seite öffnest du dann in deinem Browser mit http://127.0.0.1/relativer/pfad/zum/ordner/ab/document/root/des/servers/seite.html.

Wenn das nicht gleich klappt, spuckt Google dir sicher Millionen Tutorials zur Einrichtung eines lokalen Webservers aus.

–ks

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