Beispiel: https://webmapping21s.github.io/tirol/
bei let overlays
:
{
tracks: L.featureGroup(),
wikipedia: L.featureGroup()
}
bei let layerControl
:
{
"GPX-Tracks": overlays.tracks,
"Wikipedia-Artikel": overlays.wikipedia
}
unterhalb von let layerControl
:
overlays.tracks.addTo(map);
overlays.wikipedia.addTo(map);
die neue Funktion zum Zeichnen der Wikipedia-Marker schreiben wir gleich unterhalb des Blocks mit der Elevation-Control. Wir werden ihr später den gewünschten Kartenausschnitt im Argument bounds
übergeben:
const drawWikipedia = (bounds) => {
console.log(bounds);
};
die Funktion drawWikipedia
ist vorbereitet und wir können sie am Ende der gpxTrack.on("loaded")
Funktion aufrufen. Als Ausschnitt übergeben wir den Extent des geladenen gpx-Tracks.
gpxTrack.on("loaded", () => {
// ..
drawWikipedia(gpxTrack.getBounds());
});
in drawWikipedia
setzen wir dann über Template-Syntax die Eckkordinaten des bounds
-Objekts mit getNorth(), getSouth(), getEast(), getWest()
in die URL für den Aufruf des GeoNames-Webservices wikipediaBoundingBoxJSON ein. Im Gegensatz zum Demo-Beispiel verwenden wir https://secure.geonames.org
als Server. Als Sprache für die Wikipedia-Artikel wählen wir Deutsch (lang=de
), die Anzahl der gewünschten Artikel bestimmen wir mit 30 (maxRows=30
) und als username
verwenden wir den User, den wir uns bei https://www.geonames.org/ erstellt haben:
let url = `https://secure.geonames.org/wikipediaBoundingBoxJSON?north=${bounds.getNorth()}&south=${bounds.getSouth()}&east=${bounds.getEast()}&west=${bounds.getWest()}&username=webmapping&lang=de&maxRows=30`;
console.log(url);
der fertige Link lautet damit: https://secure.geonames.org/wikipediaBoundingBoxJSON?north=47.517664434770246&south=47.32579231609051&east=11.940078735351564&west=11.06254577636719&username=webmapping&lang=de&maxRows=30
mit der fetch-API
holen wir uns dann die Daten vom Webservice als JSON-Objekt ab
fetch(url).then(
response => response.json()
).then(jsonData => {
console.log(jsonData)
})
in einer for-of
Schleife zeichnen wir für jeden Eintrag im Array jsonData.geonames
einen Marker und hängen ihn an das Wikipedia-Overlay
for (let article of jsonData.geonames) {
let mrk = L.marker([article.lat, article.lng]);
mrk.addTo(overlays.wikipedia);
}
jeder Marker bekommt zusätzlich ein Popup in Template-Syntax mit Typ, Titel, Seehöhe, Beschreibung, Link zum Wikipedia-Artikel und Vorschaubild (sofern vorhanden)
let img = "";
if (article.thumbnailImg) {
img = `<img src="${article.thumbnailImg}" alt="thumbnail">`
}
mrk.bindPopup(`
<small>${article.feature}</small>
<h3>${article.title} (${article.elevation}m)</h3>
${img}
<p>${article.summary}</p>
<a target="wikipedia" href="https://${article.wikipediaUrl}">Wikipedia Artikel</a>
`)
die meisten Einträge im Wikipedia JSON-Objekt haben in der Eigenschaft feature
den Typ des Eintrags als Zeichenkette (z.B. waterbody, mountain, river etc.). Diese Typen übersetzen wir in unterschiedliche Icons der https://mapicons.mapsmarker.com/ Icon-collection. Für Einträge ohne Typ sehen wir ein generisches Info-Icon als default
vor. Das icons
-Objekt definieren wir vor der for-of
Schleife:
let icons = {
adm1st: "wikipedia_administration.png",
adm2nd: "wikipedia_administration.png",
adm3rd: "wikipedia_administration.png",
airport: "wikipedia_helicopter.png",
city: "wikipedia_smallcity.png",
glacier: "wikipedia_glacier-2.png",
landmark: "wikipedia_landmark.png",
railwaystation: "wikipedia_train.png",
river: "wikipedia_river-2.png",
mountain: "wikipedia_mountains.png",
waterbody: "wikipedia_lake.png",
default: "wikipedia_information.png",
};
for (let article of jsonData.geonames) {
// Marker zeichnen
}
innerhalb der for-of
Schleife ersetzen wir alle Artikel-Typen die wir nicht kennen (deshalb das “!” bei der if
-Abfrage) mit unserem generieschen Wert default
. Damit können wir uns darauf verlassen, dass für jeden Eintrag in article.feature
auch ein Icon verfügbar ist:
for (let article of jsonData.geonames) {
if (! icons[article.feature]) {
article.feature = "default";
}
// ..
}
das ermittelte Icon setzen wir dann als L.icon
beim Marker über folgende properties
ein:
iconUrl
: Pfad zum passenden Icon in icons[article.feature]
iconSize
: Array mit Breite und Höhe des Icons in Pixel. Hinweis: sobald iconSize
gesetzt ist, wird das Icon an der Koordinate zentriert. Fehlt iconsize
ist die Positionierung Links/Oben
iconAnchor
: Array mit Anfasspunkt zur Positionierung bezogen auf das Bild. Bei uns ist das die horizontale Mitte (16) und die Unterkante (37)
popupAnchor
: Array mit Anfasspunkt für das Popup bezogen auf die Koordinate. Nachdem das Icon mit der Spitze auf der Koordinate positioniert ist, müssen wir den popupAnchor
um die Höhe des Icons nach oben schieben um das Icon nicht zu verdecken
Details dazu findet ihr in der Leaflet-Dokumentation unter https://leafletjs.com/reference.html#icon-iconsize
let mrk = L.marker([article.lat, article.lng], {
icon: L.icon({
iconUrl: `icons/${icons[article.feature]}`,
iconSize: [32, 37],
iconAnchor: [16, 37],
popupAnchor: [0, -37]
})
});
Damit bei Zoom/Pan in der Karte auch immer wieder neue Wikipedia-Artikel geladen werden, müssen wir den Aufruf von drawWikipedia
mit den jeweiligen Karten-Events verknüpfen. Das geschieht über eine Callback-Funktion (map.on()
), die immer dann aufgerufen wird, wenn sich der Ausschnitt der Karte über Zoom (zoomend
) oder Pan (moveend
) verändert.
wir verschieben den alten drawWikipedia
-Aufruf ganz an das Ende des Scripts in eine neue Callback-Funktion und übergeben drawWikipedia
den jeweils letzten Ausschnitt der Karte. Als Trennzeichen für unsere beiden Events im Callback verwenden wir ein Leerzeichen:
// Wikipedia Icons zeichnen
map.on("zoomend moveend", () => {
drawWikipedia(map.getBounds());
})
damit werden immer wieder Artikel nachgeladen. Allerdings werden auch Artikel geladen, die wir schon einmal geladen haben. Um das zu verhindern, müssen wir uns merken, welche Artikel wir schon geladen haben.
dazu definieren wir oberhalb der drawWikipedia
Funktion ein (anfangs) leeres articleDrawn
-Objekt in dem wir über die jeweilige Wikipedia-URL festhalten werden, ob wir einen Artikel schon gezeichnet haben
let articleDrawn = {};
const drawWikipedia = () => {
// ....
}
ganz zu Beginn der for-of
Schleife entscheiden wir, ob wir zeichnen oder nicht. Wenn wir den Artikel schon gezeichnet haben, gehen wir über die Anweisung continue
direkt zum nächsten Eintrag in jsonData.geonames
weiter. Wenn wir den Artikel noch nicht kennen, merken wir ihn uns und führen der restlichen Code-Block zum Zeichnen des Markers aus:
for (let article of jsonData.geonames) {
if (articleDrawn[article.wikipediaUrl]) {
console.log("schon gesehen", article.wikipediaUrl);
continue;
} else {
articleDrawn[article.wikipediaUrl] = true;
}
// ...
}
in der console
erkennen wir, dass viele Artikel doppelt gezeichnet worden wären. Das liegt daran, dass zoomend
und moveend
auch gemeinsam auftreten können und natürlich daran, dass bei überlappenden Ausschnitten die selben Artikel als Resultat ankommen.
Zum Schluss bleibt noch, alle console.log()
-Aufrufe zu deaktivieren :-)