Eigene Bilder in OpenStreetMap anzeigen
Die Kunsthalle Rostock hat nach einer Möglichkeit gesucht, verschiedene Open-Air Werke digital auf einer Webseite zu präsentieren. Gefragt getan… Mit OpenStreetMap geht das total super!
Hier ist ein Beispiel, bei dem ich das Logo von CODE AHOI in den Rostocker Stadthafen projiziert habe:
Dies ist ein Platzhalter
Um die Karte anzeigen zu können muss dein Browser Inhalte von OpenStreetMap nachladen. Die Datenschutzerklärung von OpenStreetMap findest du hier: wiki.osmfoundation.org/wiki/Privacy_Policy
Ist das nicht cool :)
Selber Machen!
Mit dem OpenLayers-Projekt ist das vergleichsweise einfach. Zunächst ist ein bisschen HTML-Grundgerüst notwendig: Wir …
- müssen OpenLayers als Abhängigkeiten einbinden,
- brauchen einen HTML-Container in dem die Karte angezeigt wird,
- und benötigen ein bisschen HTML Struktur, um das Pop-Up zu realisieren.
Ein Template könnte etwa wie folgt aussehen:
<body>
<div id="map" style="width:100%; height: 50vh">
<!-- hier wird die Karte angezeigt -->
</div>
<div id="popup" class="ol-popup">
<a href="#" id="popup-closer" class="ol-popup-closer">
<!-- kleines x um das Pop-Up zu schließen -->
</a>
<div id="popup-content">
<!-- Inhalte im Pop-Up -->
</div>
</div>
<!-- Bibliotheken des OpenLayers' Projekt herunterladen -->
<link rel="stylesheet" href="/path/to/openlayers/ol.css">
<script src="/path/to/openlayers/ol.js"></script>
</body>
Ein Icon-Feature pro Icon
Jetzt können wir mit der Magie beginnen. Für jedes Icon, dass in die Map soll, muss ein Icon-Feature erstellt werden. Für das Beispiel oben sieht das ungefähr so aus:
// erstelle ein Feature, dass später als Icon angezeigt wird
const iconFeature = new ol.Feature({
geometry: new ol.geom.Point(ol.proj.fromLonLat([12.1110,54.0966])),
info: {
headline: "OpenStreetMap AHOI",
body: "Das CODE AHOI Papierschiffchen schwimmt im Rostocker Stadthafen. [...]"
},
});
// style das Feature: hier wird ein Icon-Bild gesetzt
iconFeature.setStyle(new ol.style.Style({
image: new ol.style.Icon({
src: "https://codeahoi.de/s/articles/2020/openlayers-showcase.png"
})
}));
// alles zu einem Layer zusammenbauen
var vectorLayer = new ol.layer.Vector({
source: new ol.source.Vector ({
features: [iconFeature]
})
});
Im ersten Block werden ein paar Eigenschaften für das Icon gesetzt: Wo soll das Icon angezeigt werden? Welche Überschrift und welcher Text soll im Pop-up angezeigt werden?
Der zweite Block definiert dann, wie das Icon aussehen soll. Hier laden wir ein Bild vom CODE AHOI Webserver, dass dann in der Karte angezeigt werden soll..
Im dritten Block erzeugen wir eine neue Vektor-Ebene, die später über die Karte gelegt werden soll – die Spezialität von OpenLayers :)
Das sieht in diesem Fall vielleicht ein bisschen kompliziert aus für ein einzelnes Logo, lässt sich bei vielen Icons aber gut wegautomatisieren.
Karte laden
Im nächsten Schritt können wir die Karte laden. Als unterste Ebene wollen wir die Kacheln (Tiles) von OpenStreetMap nehmen, auf die wir dann unsere neue Ebene mit dem Icon legen. Die Map soll dann auf Level 15 gezoomt und im Rostocker Stadthafen zentriert werden. Koordinaten und Zoomlevel kann man aus der Karte auf openstreetmap.org entnehmen. Wenn, wie in diesem Fall, die Koordinaten des Icons mit dem Zentrum der Karte übereinstimmen, wird das Icon zentriert in der Karte angezeigt ;-)
var map = new ol.Map({
// die karte soll im container mit der ID 'map' angezeigt werden
target: 'map',
// die ebenen, die in der Karte angezeigt werden sollen
layers: [
new ol.layer.Tile({
// erstes Layer sind die Tiles von OpenStreetMap
source: new ol.source.OSM()
}),
// zweites Layer wird unser Icon Layer
vectorLayer
],
view: new ol.View({
// zentriere initial auf die Koordinaten [12.1110,54.0966] (Stadthafen Rostock)
center: ol.proj.fromLonLat([12.1110,54.0966]),
// und zoome auf level 15
zoom: 15
})
});
Die Karte wird jetzt ordentlich geladen und das Icon wird an der richtigen Stelle angezeigt.
Pop-Up implementieren
Damit sind wir also fast fertig, aber wir wollen ja noch ein kleines Pop-Up anzeigen, wenn ein Nutzer auf das Icon klickt! Dafür müssen wir natürlich auch noch ein bisschen Code schreiben:
var container = document.getElementById('popup');
var content = document.getElementById('popup-content');
var closer = document.getElementById('popup-closer');
// Das Pop-Up wird eine neue Ebene in OpenLayers
var popup = new ol.Overlay({
element: container,
autoPan: true,
autoPanAnimation: {
duration: 250
},
positioning: 'bottom-center',
stopEvent: false,
offset: [0, -25]
});
map.addOverlay(popup);
// Event-Listener für den Klick auf das x
closer.onclick = function() {
popup.setPosition(undefined);
return false;
};
// Event-Listener für einen Klick in die Karte: wird eines unserer Icons angeklickt -> Pop-Up anzeigen
map.on('click', function(evt) {
// wurde eins unserer Features angeklickt?
var feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) { return feature; });
// wenn ja, fülle das Pop-Up mit den Informationen aus dem Feature
if (feature) {
// wo soll das Pop-Up angezeigt werden?
var coordinates = feature.getGeometry().getCoordinates();
// was soll in dem Pop-Up stehen?
content.innerHTML = '<h2>' + feature["values_"]["info"]["headline"] + '</h2><p>' + feature["values_"]["info"]["body"] + "</p>";
// Pop-Up an der richtigen stelle anzeigen
popup.setPosition(coordinates);
} else {
// es wurde etwas anderes angeklickt -> Pop-Up schließen
popup.setPosition(undefined);
}
});
Das Pop-Up selbst können wir mit OpenLayers direkt als weitere Ebene (siehe zweiten Block) in der Karte realisieren – damit fühlt sich das Pop-Up nicht so aufdringlich an. Die nötigen Informationen für das Pop-Up haben wir oben direkt an unser Icon-Feature gespeichert. Diese können wir jetzt auslesen und anzeigen, wenn ein Nutzer das Icon anklickt (siehe letzter Block).
Styling
Die Logik ist fertig. Aber für das Makeup brauchen wir jetzt noch ein ordentliches Finishing ;-)
Das Styling ist natürlich sehr vom Layout drumherum und vielen subjektiven Faktoren abhängig.
Generell könnte man aber ein Schließen-Symbol in dem popup-closer
Container anzeigen und die Pop-Up-Blase ein bisschen anpassen.
Hier ist ein Beispiel:
/* x zum Schließen */
.ol-popup-closer {
text-decoration: none;
position: absolute;
top: 2px;
right: 8px;
}
.ol-popup-closer:after {
content: "✖";
}
/* Pop-Up-Blase mit kleinem Dreieck, das auf das Icon zeigt */
.ol-popup {
position: absolute;
background-color: white;
box-shadow: 0 1px 4px rgba(0,0,0,0.2);
padding: 15px;
border-radius: 10px;
border: 1px solid #cccccc;
bottom: 12px;
left: -50px;
min-width: 280px;
display: none;
}
.ol-popup:after, .ol-popup:before {
top: 100%;
border: solid transparent;
content: " ";
height: 0;
width: 0;
position: absolute;
pointer-events: none;
}
.ol-popup:after {
border-top-color: white;
border-width: 10px;
left: 48px;
margin-left: -10px;
}
.ol-popup:before {
border-top-color: #cccccc;
border-width: 11px;
left: 48px;
margin-left: -11px;
}
Komplette Lösung
Meinen Code für das Beispiel oben gibt es unverschleiert zum Download:
Das HTML-Gerüst ist mit dem Artikel drumherum in dem Fall ein bisschen komplexer, aber das könnt ihr euch ja selbstständig im Quellcode ansehen :)