Google Maps (v3): Schicke Marker, Lösung für überlappene Marker und schönere Infofenster mit CSS

In meinem aktuellen Projekt für das EJW Hessen war eine der Hauptaufgaben das Rendern einer verschönerten Google Map (in der neusten Version 3) mittels der JavaScript API.

Anforderungen:

  • eigene, schöne Markergrafiken (google.maps.MarkerImage)
  • optimierte, per CSS stylebare Infofenster (InfoBubble)
  • Lösung für das Problem der überlappenen Marker. Wie erreicht der User Marker, wenn viele übereinander (auf einer Koordinate) liegen? (OverlappingMarkerSpiderfier)
  • Drop Animationseffekt beim Setzen der Marker (google.maps.Animation.DROP)
  • Suchbox zum Anzeigen einer Adresse (google.maps.Geocoder)
  • Filter mittels Links

Resultat

Das Resultat lässt sich auf der Website des EJW Hessen begutachten:

EJW Hessen Google Maps

Lösung

Alle diese Anforderungen wurden im folgenden Script berücksichtigt:

var map, infoBubble, styledcontent, oms;
var content = [];
var geocoder = new google.maps.Geocoder();
var default_zoom = 9;
var search_zoom = 14;
var global_filter = '';
var search_placeholder = 'Straße, Ort';

$(document).ready(function() {
    if ($('#map_canvas').length != 0) {
			
        var mapCenter = new google.maps.LatLng(50.22120, 8.63525); // Default
        map = new google.maps.Map(document.getElementById('map_canvas'), {
          zoom: default_zoom,
          center: mapCenter,
          mapTypeId: google.maps.MapTypeId.ROADMAP,
		  scrollwheel: false
        });
		oms = new OverlappingMarkerSpiderfier(map, {markersWontMove: true, markersWontHide: true, keepSpiderfied: true});
		
		// ------------------------------------------
		// function to show all relevant markers. filtered by 'filter'-parameter if set :-)
		runQuery = function(elements, filter) {
		
			// Is filter set?		
			var filter = (typeof(filter) != 'undefined') ? filter : '';

			// create new filter
			var current_element = elements + filter; 
			
			// first of all, clear all markers already there:
			deleteOverlays();
	
			// loop through all given elements:
			$(current_element).each(function(index){
		
				// are coordinates set?
				var current_koordinaten_xy = ($(this).children('.koordinaten').text() != '') ? $(this).children('.koordinaten').text() : '';
				var current_koordinaten = '';

				// put together information for the bubble
				var gruppe = new Array();
				gruppe['foo'] = 'bar';
				content[index] = gruppe; // neue gruppe zum array aller gruppen hinzufügen
				
				// if there are coordinates: split them in x/y, create google position and add marker
				if(current_koordinaten_xy != ''){
					current_koordinaten_xy = current_koordinaten_xy.split(',');
					current_koordinaten = new google.maps.LatLng(current_koordinaten_xy[0], current_koordinaten_xy[1]);
					
					addMarker(current_koordinaten, content, index, $(this).children('.fachgruppe').text()); // do everything						
				}
				
			});
		}
		
		
		// ------------------------------------------
		// Info Bubble Default Settings:
		infoBubble = new InfoBubble({
					map: map,
					maxWidth: 290,
					maxHeight: 320,					
					shadowStyle: 0,
					padding: 0,
					backgroundColor: '#fff',
					borderRadius: 5,
					arrowSize: 20,
					borderWidth: 0,
					arrowPosition: 20,
					backgroundClassName: 'ejw-gruppe-infowindow',
					arrowStyle: 2,
					hideCloseButton: false
		});		
		
		
		// ------------------------------------------
		// for starters: show all
		runQuery('#adressenliste li');
		
		// ------------------------------------------
		// now filter by filters:
		$('#filters a').click(function(){
			var current_filter = $(this).attr('data-option-value');
			if(current_filter != 0) {
				global_filter = '.' + current_filter; // put together new global filter
			}else{
				global_filter = '';
			}
			
			// toggle '.selected'-class:
			$('.selected').removeClass('selected');
			$(this).toggleClass('selected');
			
			// add new markers:
			runQuery('#adressenliste li', global_filter);

			// if search box is not empty, add address marker as well:
			if( $('#finder-adresse').val() != '' && $('#finder-adresse').val() != search_placeholder){			
				codeAddress( $('#finder-adresse').val(), false);
			}
			
			// prevent default action from happening (link href):
			return false; 
		});

		
		// ------------------------------------------
		// find addresses by search and center map to this position:
		$('#finder-submit').click(function(){
		
			// clear markers and start over again:
			runQuery('#adressenliste li', global_filter);
			
			// geocode given address and add marker:
			codeAddress( $('#finder-adresse').val() );
			
			// zoom in
			map.setZoom(search_zoom);
			
		});
		
		// empty search box on focus if the value is equal to the placeholder text:
		$('#finder-adresse').val(search_placeholder);
		$('#finder-adresse').focus(function(){
			if ($(this).val() == search_placeholder) {
				$(this).val('');
			}
		});
		$('#finder-adresse').blur(function(){
			if ($(this).val() == '') {
				$(this).val(search_placeholder);
			}
		});
		
	}
	

	// ------------------------------------------
	// only add infobubble to the marker when it's clicked:
	oms.addListener('click', function(marker) {
		infoBubble.setContent(marker.content);	
		infoBubble.open(map, marker);
	});	
	
});

Ich nutze dabei jQuery, weil die Daten für die Marker (z.B. die Koordinaten) erst bei Aufruf der Website ausgelesen, also generiert, werden. Das ist sicherlich nicht sehr performant, kann aber auch nützlich sein, wenn man zum Beispiel unter der Karte eine Liste der gefilterten Einträge anzeigen möchte.

Die folgende Funktion fügt der Map einen Marker hinzu. Ihr wird die Position (als Google Map Koordinaten) sowie einige weitere Parameter speziell für das EJW-Projekt mitgegeben:

function addMarker(location, content, index, fachgruppe, openbubble) {

	// Is openbubble set?		
	var openbubble = (typeof(openbubble) != 'undefined') ? openbubble : true;

	// check for marker images:
	switch(fachgruppe) {
		case "HP":
			markerImg = new google.maps.MarkerImage('***.png');
			break;
		case "HMP":
			markerImg = new google.maps.MarkerImage('***.png');
			break;
		case "Standort":
			markerImg = new google.maps.MarkerImage('***.png');
			break;
		default:
			markerImg = new google.maps.MarkerImage('***.png');
			break;
	}
	
	// marker content
	if(fachgruppe == 'Standort') {
		styledcontent = '<div>Deine Standorteingabe.</div>';
	} else {
		styledcontent = "<div>...</div>";
	}

	var marker = new google.maps.Marker({
		position: location,
		map: map,		
		icon: markerImg,
		content: styledcontent,
		animation: google.maps.Animation.DROP
	});	
	
	// add to marker array
	oms.addMarker(marker);

	// if is standort, show bubble
	if(fachgruppe == 'Standort' && openbubble) {
		infoBubble.setContent(marker.content);	
		infoBubble.open(map, marker);
	}
	
}

Diese Funktion löscht alle Marker von der Map:

// Deletes all markers in the array by removing references to them
function deleteOverlays() {

	var markers = oms.getMarkers();
	
	if (markers) {
		for (i in markers) {
			markers[i].setMap(null);
		}
		markers.length = 0;
	}
  
	oms.clearMarkers();

}

Die nächste Funktion sorgt für die Geokodierung, also das Umsetzen einer Adresse zu Koordinaten:

function codeAddress(address, openbubble) {

	// Is openbubble set?
	var openbubble = (typeof(openbubble) != 'undefined') ? openbubble : true;

	if (geocoder) {
		  geocoder.geocode({
				'address': address,
				'region': 'de', 
	            'language': 'de_DE'}, function(results, status) {
			if (status == google.maps.GeocoderStatus.OK) {
			
				// set marker to this location
				addMarker(results[0].geometry.location, '', '', 'Standort', openbubble);
			
				// center to the given location:
				map.setCenter(results[0].geometry.location);
		
			} else {
			
				// hier könnte noch ein schönerer fehler hin:
				console.log("Geokodierung nicht erfolgreich: " + status + ".");
				return false;
			}
		  });
	}
}

Kommentare und Optimierungsvorschläge sind natürlich jederzeit willkommen! :-)

Empfehlen Sie diesen Artikel weiter!

Kommentare (2)

  1. Jan R
    Jan R 29.07.2012
    Mein Kompliment für diese tolle Lösung!!! :)
  2. Johannes B
    Johannes B 29.07.2012
    Also ich schreibe ja wirklich sehr, sehr selten Kommentare zu Blog-Posts, aber Ehre wem Ehre gebührt… großartige Lösung!

Neuen Kommentar schreiben