Custom Clickable Google Map Markers With Images

Our Scenario

We are working on an app called BeeSecure that is being created with the intention of reducing rural crime. Well, rural crime initially but will hopefully do much more than just rural. We have two different types of hardware devices, one cellular, and another LoRaWan (Long Range Wide Area Network). These devices send information like GPS coordinates, direction, altitude, temperature to our app and the app relays the information back to the customers. The main initiative is GPS tracking assets. Even the smallest movements triggers events which send information to our app so people will know when things are happening. So you can probably see where the mapping comes into play. When people open up their breadcrumb maps or current location maps they can see where things are or have been. Having all the markers set to the default red pin isn't very helpful for anyone when they are trying to figure out which point is which or which asset is which. What do we need? Ideally, a photo of each asset on the map in place of the pins.

BeeSecure Asset Mapping




HTMLMapMarker Class

Well let's get straight to it. Here is the Javascript class you will require. Put this in the same Javascript file where you are initializing your Google map. We came across this resource but had to change things up a little bit. Credit where credit is due. It helped us a ton!

/*
 * HTMLMapMarker Javascript class
 * Extends the Google Maps OverlayView class
 * Set up to accept our latlng, html for the div, and the map to attach it to as arguments
 */
class HTMLMapMarker extends google.maps.OverlayView
{	
	// Constructor accepting args
	constructor(args) {
		super();
		this.latlng = args.latlng;
		this.html = args.html;
		this.setMap(args.map);
	}

	// Create the div with content and add a listener for click events
	createDiv() {
		this.div = document.createElement('div');
		this.div.style.position = 'absolute';
		if (this.html) {
			this.div.innerHTML = this.html;
		}
		google.maps.event.addDomListener(this.div, 'click', event => {
			google.maps.event.trigger(this, 'click');
		});
	}

	// Append to the overlay layer
	// Appending to both overlayLayer and overlayMouseTarget which should allow this to be clickable
	appendDivToOverlay() {
		const panes = this.getPanes();
		panes.overlayLayer.appendChild(this.div);
		panes.overlayMouseTarget.appendChild(this.div);
	}

	// Position the div according to the coordinates
	positionDiv() {
		const point = this.getProjection().fromLatLngToDivPixel(this.latlng);
		if (point) {
			this.div.style.left = `${point.x}px`;
			this.div.style.top = `${point.y}px`;
		}
	}

	// Create the div and append to map
	draw() {
		if (!this.div) {
			this.createDiv();
			this.appendDivToOverlay();
		}
		this.positionDiv();
	}

	// Remove this from map
	remove() {
		if (this.div) {
			this.div.parentNode.removeChild(this.div);
			this.div = null;
		}
	}

	// Return lat and long object
	getPosition() {
		return this.latlng;
	}

	// Return whether this is draggable
	getDraggable() {
		return false;
	}
}



The CSS Styling

The following styles will give you something that looks like our marker in the image above. Put these in your stylesheet or within style tags on the map page.

/* Outside white border */
.asset-map-image-marker { 
	background-color: #ffffff; 
	border-radius: 5px; 
	cursor: pointer !important; 
	height: 40px;
	margin-left: -20px; /* margin-left = -width/2 */
	margin-top: -50px; /* margin-top = -height + arrow */ 
	padding: 0px; 
	position: absolute; 
	width: 40px; 
}

/* Arrow on bottom of container */
.asset-map-image-marker:after {
	border-color: #ffffff transparent; 
	border-style: solid; 
	border-width: 10px 10px 0; 
	bottom: -10px; 
	content: ''; 
	display: block; 
	left: 10px; 
	position: absolute; 
	width: 0;
}

/* Inner image container */
.asset-map-image-marker div.image {
	background-color: #cccccc; 
	background-position: center center; 
	background-size: cover; 
	border-radius: 5px; 
	height: 36px; 
	margin: 2px; 
	width: 36px;
}



Using The HTMLMapMarker Class

Now putting it to use with a Google map. Use this Javascript code and it should all come together.

/*
 * Initialize Map
 */
function initialize_map()
{
	var map = new google.maps.Map(document.getElementById('map'), {
		zoom: 18,
		mapTypeId: 'hybrid',
		mapTypeControlOptions: { mapTypeIds: ['roadmap', 'satellite', 'hybrid'] },
		fullscreenControl: false,
		mapTypeControl: true,
		streetViewControl: false,
		rotateControl: false,
		clickableIcons: false,
		zoomControlOptions: { position: google.maps.ControlPosition.RIGHT_TOP },
	});

	var image = 'https://blackatlascreative.com/wp-content/uploads/2019/02/blog-tardis.jpg';
    
	var marker = new HTMLMapMarker({
		latlng: new google.maps.LatLng(51.4921374, -0.1928784),
		map: map,
		html: '<div class="asset-map-image-marker"><div class="image" style="background-image: url(' + image + ')"></div></div>',
	});
		  	
	marker.addListener("click", () => {
		// Your Javascript to run on click
  		alert("You found the Tardis!");
	});
			
	map.setCenter(marker.getPosition());
}

Initialize The Map

Now we just need the div and a quick Javascript call in the page we want the map.

<div id="map"></div>
<script>
$( document ).ready(function() {
	google.maps.event.addDomListener(window, 'load', initialize_map());
});
</script>

Reminders

Don't forget to have a link to the Google Map API Javascript library and as of recently you will need an API Key for things to work properly. You can find information on the API Key here:

https://developers.google.com/maps/documentation/javascript/get-api-key

The Result

Posted on: February 15, 2019

No Comments Yet.

Leave a comment