Marker zu einem Satellitenbild hinzufügen

In meinem vorherigen Blogbeitrag habe ich euch gezeigt, wie ihr einen Marker für eine GPS Koordinate zu einer SVG-Bild hinzufügen könnt. In diesem Falle musstet ihr die x/y-Positionen berechnen und konntet dann einen SVG-Kreis oder einen Marker-Pfad zeichnen. Aber was machen wir, wenn wir ein Satellitenbild haben – oder eine andere Pixelgrafik?

Lösung: verwendet auch hier ein SVG 😉

OK, nicht einfach irgendein SVG. Die Technik, die wir hier verwenden werden, könnte man als „moderne Image-Map“ bezeichnen. Diejenigen von euch, die schon länger Websites bauen, werden vermutlich noch den <map> Element kennen, mit dessen Hilfe man eine <area> auf einem <img> anklickbar machen konnte. Die Formen waren dabei aber recht eingeschränkt und bei weitem nicht so flexibel und präzise, wie es mit SVG-Pfaden möglich ist. Statt also eine traditionelle Image-Map zu verwenden, setzen wir ein (leeres) SVG ein, welches wir dann über das Bild legen, um eine „anklickbare Karte“ zu erstellen. Nun, genau genommen erstellen wir nur ein anklickbares SVG, das als Overlay über dem eigentlichen Bild liegt, aber es erzielt genau das Ergebnis, das wir haben wollen. Für diesem Blogbeitrag habe ich folgendes Satellitenbild von Berlin ausgesucht:

NASA Goddard Space Flight Center from Greenbelt, MD, USA, Berlin, Germany – Flickr – NASA Goddard Photo and Video1CC BY 2.0

Die Bildgrenzen berechnen

Wie ich bereits im vorherigen Beitrag erwähnt habe, müsst ihr erst einmal die Grenzen der Karte herausfinden, auf die ihr dann die Marker platzieren möchtet. Wir fangen mit den Grenzen für das Bild an, was der Größe der Pixelgrafik entspricht:

// Init PixelGeocoder using WGS84 and Mercato projection.
$pixel_geocoder = new PixelGeocoder( 'EPSG:4326', 'EPSG:3857' );
// Set boundaries for the map.
$pixel_geocoder->image_boundaries = [
	'xmin' => 0,
	'xmax' => 2400,
	'ymin' => 0,
	'ymax' => 1800,
];

Jetzt müssen wir die GPS-Grenzen des Bildes finden. Diese haben wir für das Bild natürlich nicht. Um sie zu erhalten, habe ich mir einige markante Punkte am Rand der Karte gesucht, die ich dann auf Google Maps finden konnte. So habe ich die folgenden GPS-Koordinaten bekommen, die ich dann als Referenzpunkte verwenden konnte:

$map_edges = [
	[ 13.0467623, 52.5594922 ], // West.
	[ 13.1993623, 52.6484712 ], // North.
	[ 13.5841963, 52.4416892 ], // East.
	[ 13.2766553, 52.4069153 ], // South.
];

$pixel_geocoder->setDstBoundaries(
	$map_edges,
	false,
	true
);

Nachdem wir nun die Grenzen definiert haben, können wir erneut die Koordinaten für das Brandenburger Tor berechnen:

// Calculate the coordinates.
$bb_gate_lat     = 13.3777041;
$bb_gate_lng     = 52.5162746;
$bb_gate_dst_arr = $pixel_geocoder->transformGPStoMapProjection( $bb_gate_lat, $bb_gate_lng );
$bb_gate_coords  = $pixel_geocoder->calculateCoordinatesToPixel( $bb_gate_dst_arr[0], $bb_gate_dst_arr[1] );

var_dump( bb_gate_coords );
/**
 * array(2) {
 *   [0]=>
 *   float(1477.8750879177708)
 *   [1]=>
 *   float(986.3143837577029)
 * }
 */

Aber wie bekommen wir das nun als Marker in einem Overlay über unser Satellitenbild? Hier kommt nun unsere SVG-Image-Map ins Spiel.

Erstellen einer SVG-Image-Map

Eine SVG-Imag-Map ist im Grunde eine leere „SVG-Leinwand“, auf die wir Dinge platzieren können. Wir definieren also ein einzelnes SVG Element mit der gleichen Höhe und Breite wie unser Satellitenbild. Das Bild selbst setzen wir als Geschwister-Element daneben und umschließen beides mit einem Container-Element und setzen ein paar Styles:

<div class="image-map">
	<img class="image-map-background" src="./Berlin-Germany-Flickr-NASA-Goddard-Photo-and-Video1.jpg" alt="Berlin NASA image"/>
	<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="dynamic-map" width="2400" height="1800" viewBox="0 0 2400 1800"></svg>
</div>

Um das SVG über dem Bild zu platzieren, definieren wir die Höhe und Breite für den Container (dieser sollte das gleiche „Seitenverhältnis“ wie das Bild haben):

.image-map {
	position: relative;
	width: 600px;
	height: 450px;
}

.image-map-background,
.dynamic-map {
	max-width: 100%;
	height: auto;
}

.dynamic-map {
	position: absolute;
	top: 0;
	left: 0;
}

Jetzt können wir unsere Marker hinzufügen. Ich hole mir dazu meistens eine Liste von Markern, entweder aus einem statischen Array oder aber mit einer WP_Query und einigen Meta-Feldern. Nehmen wir für unser Beispiel einfach ein statisches Array mit dem bereits berechneten Marker:

$markers = [
	[
		'name'  => 'brandenburg-gate',
		'title' => 'Brandenburg Gate',
		'x'	 => $bb_gate_coords[0],
		'y'	 => $bb_gate_coords[1],
		'url'   => 'https://en.wikipedia.org/wiki/Brandenburg_Gate',
	]
];

Da wir vorhaben, die SVG-Pfade und Links auszugeben, können wir hier ein kleines Marker-Template schreiben, das uns die Ausgabe etwas erleichtert:

$marker_markup = '
	<a xlink:title="%1$s" target="_parent" class="marker" id="%2$s" xlink:href="/%3$s/" transform="translate(%4$s,%5$s)">
		<path fill="#c10926" fill-rule="evenodd" d="m -0.266,-28.261 a 4.504,4.504 0 0 0 3.204,-1.343 4.613,4.613 0 0 0 1.327,-3.242 4.615,4.615 0 0 0 -1.327,-3.244 4.508,4.508 0 0 0 -3.204,-1.343 4.512,4.512 0 0 0 -3.206,1.343 4.619,4.619 0 0 0 -1.327,3.244 c 0,1.215 0.478,2.382 1.327,3.242 a 4.51,4.51 0 0 0 3.206,1.343 m -0.613,27.98 -8.895,-28.49 h 0.013 a 10.555,10.555 0 0 1 -0.818,-4.074 c 0,-2.77 1.086,-5.425 3.02,-7.381 a 10.251,10.251 0 0 1 7.294,-3.056 c 2.735,0 5.358,1.099 7.293,3.056 a 10.502,10.502 0 0 1 3.021,7.38 c 0,1.414 -0.284,2.798 -0.819,4.076 h 0.012 z" clip-rule="evenodd"/>
	</a>';

Fügen wir das ganze jetzt in einer Schleife innerhalb unseres SVG zusammen:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" class="dynamic-map" width="2400" height="1800" viewBox="0 0 2400 1800">
	<?php foreach ( $markers as $marker ) : ?>
		<?php
		printf(
			$marker_markup,
			$marker['title'],
			$marker['name'],
			$marker['url'],
			$marker['x'],
			$marker['y']
		);
		?>
	<?php endforeach; ?>
</svg>

Durch die printf() Funktion ist können wir die dynamischen Teile ins Template einfügen. Wenn ihr das in WordPress macht, dann stellt bitte sicher, dass ihr die esc_*() Escape-Funktionen für dynamische Werte verwendet.

Ich habe den gleichen Ansatz auch gewählt, um die „Karten-Grenzpunkte“ mit einem Cyan-gefärbten Kreis zu markieren. Das Resultat sieht dann wie folgt aus (dies hier ist nur ein Screenshot des Resultats):

NASA Goddard Space Flight Center from Greenbelt, MD, USA, Berlin, Germany – Flickr – NASA Goddard Photo and Video1, bearbeitet von Bernhard Kau, CC BY 2.0

Da die Marker recht klein waren, habe ich diese etwas hochskaliert. Da sich der Marker an der Spitze skaliert, kann das mit einer einzelnen Zeile CSS erreicht werden:

.marker path {
	transform: scale(5);
}

Fazit

In einer sehr ähnlichen Art und Weise haben wir ein SVG-Bild erstellt, das anklickbare Marker auf ein Satellitenbild legt. Für das Bild einer Stadt reicht hier wohl ein normales Satellitenbild aus. Für eine größere Region müsst ihr aber eines in der Mercator-Projektion (oder einer anderen von PROJ unterstützten Projektion) finden. Ich habe einige Zeit gebraucht, um ein geeignetes CC-lizensiertes Bild zu finden, das ich als Beispiel für diesen Blogbeitrag verwenden konnte.

Falls ihr auch diesen Code einmal selbst ausprobieren wollt, dann findet ihr einen neuen Branch mit den verschiedenen Teilen in einer kombinierten neuen PHP-Datei auf GitHub. Dort findet ihr auch die verwendeten Satellitenbilder.

Ich hoffe, dass euch dieser Beitrag einen weiteren schönen Weg zeigen konnte, mit dem ihr individuelle Karten erstellen könnt. Wir haben aber recht viel eigenen PHP-Code und eine externe Bibliothek verwenden. Im nächsten (und vermutlich auch letzten) Beitrag dieser Karten-Reihe, möchte ich euch eine Bibliothek vorstellen, die ich vielleicht schon verwendet habt, aber noch nicht in dieser Weise. Bleibt also weiter dabei, denn das Jahr mag bald zu Ende gehen, aber die Kalenderwoche 52 hat noch einen Tag im Jahr 2023! ?

Veröffentlicht von

Bernhard ist fest angestellter Webentwickler, entwickelt in seiner Freizeit Plugins, schreibt in seinem Blog über WordPress und andere Themen, treibt sich gerne bei den WP Meetups in Berlin und Potsdam herum und läuft nach Feierabend den ein oder anderen Halbmarathon.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert