How can I create numbered map markers in Google Maps V3?
JavascriptGoogle MapsGoogle Maps-Api-3Javascript Problem Overview
I'm working on a map that has multiple markers on it.
These markers use a custom icon, but I'd also like to add numbers on top. I've seen how this has been accomplished using older versions of the API. How can I do this in V3?
*Note -- the "title" attribute creates a tooltip when you mouseover the marker, but I want something that will be layered on top of the custom image even when you're not hovering on top of it.
Here's the documentation for the marker class, and none of these attributes seem to help: http://code.google.com/apis/maps/documentation/v3/reference.html#MarkerOptions
Javascript Solutions
Solution 1 - Javascript
Unfortunately it's not very easy. You could create your own custom marker based on the OverlayView class (an example) and put your own HTML in it, including a counter. This will leave you with a very basic marker, that you can't drag around or add shadows easily, but it is very customisable.
Alternatively, you could add a label to the default marker. This will be less customisable but should work. It also keeps all the useful things the standard marker does.
You can read more about the overlays in Google's article Fun with MVC Objects.
Edit: if you don't want to create a JavaScript class, you could use Google's Chart API. For example:
Numbered marker:
http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=7|FF0000|000000
Text marker:
http://chart.apis.google.com/chart?chst=d_map_spin&chld=1|0|FF0000|12|_|foo
This is the quick and easy route, but it's less customisable, and requires a new image to be downloaded by the client for each marker.
Solution 2 - Javascript
This how I do it in V3:
I start by loading the google maps api and within the callback method initialize()
I load MarkerWithLabel.js that I found here:
function initialize() {
$.getScript("/js/site/marker/MarkerWithLabel.js#{applicationBean.version}", function(){
var mapOptions = {
zoom: 8,
center: new google.maps.LatLng(currentLat, currentLng),
mapTypeId: google.maps.MapTypeId.ROADMAP,
streetViewControl: false,
mapTypeControl: false
};
var map = new google.maps.Map(document.getElementById('mapholder'),
mapOptions);
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < mapData.length; i++) {
createMarker(i+1, map, mapData[i]); <!-- MARKERS! -->
extendBounds(bounds, mapData[i]);
}
map.fitBounds(bounds);
var maximumZoomLevel = 16;
var minimumZoomLevel = 11;
var ourZoom = defaultZoomLevel; // default zoom level
var blistener = google.maps.event.addListener((map), 'bounds_changed', function(event) {
if (this.getZoom(map.getBounds) > 16) {
this.setZoom(maximumZoomLevel);
}
google.maps.event.removeListener(blistener);
});
});
}
function loadScript() {
var script = document.createElement('script');
script.type = 'text/javascript';
script.src = "https://maps.googleapis.com/maps/api/js?v=3.exp&libraries=places&sensor=false&callback=initialize";
document.body.appendChild(script);
}
window.onload = loadScript;
</script>
I then create the markers with createMarker()
:
function createMarker(number, currentMap, currentMapData) {
var marker = new MarkerWithLabel({
position: new google.maps.LatLng(currentMapData[0], currentMapData[1]),
map: currentMap,
icon: '/img/sticker/empty.png',
shadow: '/img/sticker/bubble_shadow.png',
transparent: '/img/sticker/bubble_transparent.png',
draggable: false,
raiseOnDrag: false,
labelContent: ""+number,
labelAnchor: new google.maps.Point(3, 30),
labelClass: "mapIconLabel", // the CSS class for the label
labelInBackground: false
});
}
Since I added mapIconLabel class to the marker I can add some css rules in my css:
.mapIconLabel {
font-size: 15px;
font-weight: bold;
color: #FFFFFF;
font-family: 'DINNextRoundedLTProMediumRegular';
}
And here is the result:
Solution 3 - Javascript
I don't have enough reputation to comment on answers but wanted to note that the Google Chart API has been deprecated.
From the API homepage:
> The Infographics portion of Google Chart Tools has been officially > deprecated as of April 20, 2012.
Solution 4 - Javascript
You may want to download a set of numbered icons from the sources provided at this site:
Then you should be able to do the following:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
<title>Google Maps Demo</title>
<script type="text/javascript"
src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
function initialize() {
var myOptions = {
zoom: 11,
center: new google.maps.LatLng(-33.9, 151.2),
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map"), myOptions);
var locations = [
['Bondi Beach', -33.890542, 151.274856, 4],
['Coogee Beach', -33.923036, 151.259052, 5],
['Cronulla Beach', -34.028249, 151.157507, 3],
['Manly Beach', -33.80010128657071, 151.28747820854187, 2],
['Maroubra Beach', -33.950198, 151.259302, 1]
];
for (var i = 0; i < locations.length; i++) {
var image = new google.maps.MarkerImage('marker' + i + '.png',
new google.maps.Size(20, 34),
new google.maps.Point(0, 0),
new google.maps.Point(10, 34));
var location = locations[i];
var myLatLng = new google.maps.LatLng(location[1], location[2]);
var marker = new google.maps.Marker({
position: myLatLng,
map: map,
icon: image,
title: location[0],
zIndex: location[3]
});
}
}
</script>
</head>
<body style="margin:0px; padding:0px;" onload="initialize();">
<div id="map" style="width:400px; height:500px;"></div>
</body>
</html>
Screenshot from the above example:
Note that you can easily add a shadow behind the markers. You may want to check the example at the Google Maps API Reference: Complex Markers for more info about this.
Solution 5 - Javascript
This has now been added to the Mapping documentation and requires no third party code.
You can combine these two samples:
https://developers.google.com/maps/documentation/javascript/examples/marker-labels
https://developers.google.com/maps/documentation/javascript/examples/icon-simple
To get code like this:
var labelIndex = 0;
var labels = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ123456789';
function initialize() {
var bangalore = { lat: 12.97, lng: 77.59 };
var map = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 12,
center: bangalore
});
// This event listener calls addMarker() when the map is clicked.
google.maps.event.addListener(map, 'click', function(event) {
addMarker(event.latLng, map);
});
// Add a marker at the center of the map.
addMarker(bangalore, map);
}
// Adds a marker to the map.
function addMarker(location, map) {
// Add the marker at the clicked location, and add the next-available label
// from the array of alphabetical characters.
var marker = new google.maps.Marker({
position: location,
label: labels[labelIndex],
map: map,
icon: 'image.png'
});
}
google.maps.event.addDomListener(window, 'load', initialize);
Note that if you have more than 35 markers, this method will not work as the label only shows the first character (using A-Z and 0-9 makes 35). Please vote for this Google Maps Issue to request that this restriction be lifted.
Solution 6 - Javascript
I did this using a solution similar to @ZuzEL.
Instead of use the default solution (http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=7|FF0000|000000), you can create these images as you wish, using JavaScript, without any server-side code.
Google google.maps.Marker accepts Base64 for its icon property. With this we can create a valid Base64 from a SVG.
[![enter image description here][1]][1] [1]: http://i.stack.imgur.com/jIxuN.png
You can see the code to produce the same as this image in this Plunker: http://plnkr.co/edit/jep5mVN3DsVRgtlz1GGQ?p=preview
var markers = [
[1002, -14.2350040, -51.9252800],
[2000, -34.028249, 151.157507],
[123, 39.0119020, -98.4842460],
[50, 48.8566140, 2.3522220],
[22, 38.7755940, -9.1353670],
[12, 12.0733335, 52.8234367],
];
function initializeMaps() {
var myLatLng = {
lat: -25.363,
lng: 131.044
};
var map = new google.maps.Map(document.getElementById('map_canvas'), {
zoom: 4,
center: myLatLng
});
var bounds = new google.maps.LatLngBounds();
markers.forEach(function(point) {
generateIcon(point[0], function(src) {
var pos = new google.maps.LatLng(point[1], point[2]);
bounds.extend(pos);
new google.maps.Marker({
position: pos,
map: map,
icon: src
});
});
});
map.fitBounds(bounds);
}
var generateIconCache = {};
function generateIcon(number, callback) {
if (generateIconCache[number] !== undefined) {
callback(generateIconCache[number]);
}
var fontSize = 16,
imageWidth = imageHeight = 35;
if (number >= 1000) {
fontSize = 10;
imageWidth = imageHeight = 55;
} else if (number < 1000 && number > 100) {
fontSize = 14;
imageWidth = imageHeight = 45;
}
var svg = d3.select(document.createElement('div')).append('svg')
.attr('viewBox', '0 0 54.4 54.4')
.append('g')
var circles = svg.append('circle')
.attr('cx', '27.2')
.attr('cy', '27.2')
.attr('r', '21.2')
.style('fill', '#2063C6');
var path = svg.append('path')
.attr('d', 'M27.2,0C12.2,0,0,12.2,0,27.2s12.2,27.2,27.2,27.2s27.2-12.2,27.2-27.2S42.2,0,27.2,0z M6,27.2 C6,15.5,15.5,6,27.2,6s21.2,9.5,21.2,21.2c0,11.7-9.5,21.2-21.2,21.2S6,38.9,6,27.2z')
.attr('fill', '#FFFFFF');
var text = svg.append('text')
.attr('dx', 27)
.attr('dy', 32)
.attr('text-anchor', 'middle')
.attr('style', 'font-size:' + fontSize + 'px; fill: #FFFFFF; font-family: Arial, Verdana; font-weight: bold')
.text(number);
var svgNode = svg.node().parentNode.cloneNode(true),
image = new Image();
d3.select(svgNode).select('clippath').remove();
var xmlSource = (new XMLSerializer()).serializeToString(svgNode);
image.onload = (function(imageWidth, imageHeight) {
var canvas = document.createElement('canvas'),
context = canvas.getContext('2d'),
dataURL;
d3.select(canvas)
.attr('width', imageWidth)
.attr('height', imageHeight);
context.drawImage(image, 0, 0, imageWidth, imageHeight);
dataURL = canvas.toDataURL();
generateIconCache[number] = dataURL;
callback(dataURL);
}).bind(this, imageWidth, imageHeight);
image.src = 'data:image/svg+xml;base64,' + btoa(encodeURIComponent(xmlSource).replace(/%([0-9A-F]{2})/g, function(match, p1) {
return String.fromCharCode('0x' + p1);
}));
}
initializeMaps();
#map_canvas {
width: 100%;
height: 300px;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
</head>
<body>
<div id="map_canvas"></div>
</body>
<script src="script.js"></script>
</html>
In this demo I create the SVG using D3.js, then transformed SVG to Canvas, so I can resize the image as I want and after that I get Base64 from canvas' toDataURL method.
All this demo was based on my fellow @thiago-mata code. Kudos for him.
Solution 7 - Javascript
How about this? (year 2015)
-
Get a custom marker image.
var imageObj = new Image(); imageObj.src = "/markers/blank_pin.png";
-
Create a
canvas
inRAM
and draw this image on itimageObj.onload = function(){ var canvas = document.createElement('canvas'); var context = canvas.getContext("2d"); context.drawImage(imageObj, 0, 0); }
-
Write anything above it
context.font = "40px Arial"; context.fillText("54", 17, 55);
-
Get raw data from canvas and provide it to Google API instead of URL
var image = { url: canvas.toDataURL(), }; new google.maps.Marker({ position: position, map: map, icon: image });
Full code:
function addComplexMarker(map, position, label, callback){
var canvas = document.createElement('canvas');
var context = canvas.getContext("2d");
var imageObj = new Image();
imageObj.src = "/markers/blank_pin.png";
imageObj.onload = function(){
context.drawImage(imageObj, 0, 0);
//Adjustable parameters
context.font = "40px Arial";
context.fillText(label, 17, 55);
//End
var image = {
url: canvas.toDataURL(),
size: new google.maps.Size(80, 104),
origin: new google.maps.Point(0,0),
anchor: new google.maps.Point(40, 104)
};
// the clickable region of the icon.
var shape = {
coords: [1, 1, 1, 104, 80, 104, 80 , 1],
type: 'poly'
};
var marker = new google.maps.Marker({
position: position,
map: map,
labelAnchor: new google.maps.Point(3, 30),
icon: image,
shape: shape,
zIndex: 9999
});
callback && callback(marker)
};
});
Solution 8 - Javascript
Google Maps version 3 has built-in support for marker labels. No need to generate your own images anymore or implement 3rd party classes. Marker Labels
Solution 9 - Javascript
It's quite feasible to generate labeled icons server-side, if you have some programming skills. You'll need the GD library at the server, in addition to PHP. Been working well for me for several years now, but admittedly tricky to get the icon images in synch.
I do that via AJAX by sending the few parameters to define the blank icon and the text and color as well as bgcolor to be applied. Here's my PHP:
header("Content-type: image/png");
//$img_url = "./icons/gen_icon5.php?blank=7&text=BB";
function do_icon ($icon, $text, $color) {
$im = imagecreatefrompng($icon);
imageAlphaBlending($im, true);
imageSaveAlpha($im, true);
$len = strlen($text);
$p1 = ($len <= 2)? 1:2 ;
$p2 = ($len <= 2)? 3:2 ;
$px = (imagesx($im) - 7 * $len) / 2 + $p1;
$font = 'arial.ttf';
$contrast = ($color)? imagecolorallocate($im, 255, 255, 255): imagecolorallocate($im, 0, 0, 0); // white on dark?
imagestring($im, $p2, $px, 3, $text, $contrast); // imagestring ( $image, $font, $x, $y, $string, $color)
imagepng($im);
imagedestroy($im);
}
$icons = array("black.png", "blue.png", "green.png", "red.png", "white.png", "yellow.png", "gray.png", "lt_blue.png", "orange.png"); // 1/9/09
$light = array( TRUE, TRUE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE, FALSE); // white text?
$the_icon = $icons[$_GET['blank']]; // 0 thru 8 (note: total 9)
$the_text = substr($_GET['text'], 0, 3); // enforce 2-char limit
do_icon ($the_icon, $the_text,$light[$_GET['blank']] );
It's invoked client-side via something like the following: var image_file = "./our_icons/gen_icon.php?blank=" + escape(icons[color]) + "&text=" + iconStr;
Solution 10 - Javascript
My two cents showing how to use the Google Charts API to solve this problem.
Solution 11 - Javascript
Based on @dave1010 answer but with updated https
links.
Numbered marker:
https://chart.googleapis.com/chart?chst=d_map_pin_letter&chld=7|FF0000|000000
Text marker:
https://chart.googleapis.com/chart?chst=d_map_spin&chld=1|0|FF0000|12|_|Marker
Solution 12 - Javascript
You can use Marker With Label option in google-maps-utility-library-v3.
Just refer https://code.google.com/p/google-maps-utility-library-v3/wiki/Libraries
Solution 13 - Javascript
I discovered the best way to do it. Use Snap.svg to create the svg and then use the function toDataURL() that creates the graphics data to include as icon. Note that I use the SlidingMarker class for the marker that gives me nice movement of the marker. With Snap.svg you can create any kind of graphics and your map will look fantastic.
var s = Snap(100, 100);
s.text(50, 50, store.name);
// Use more graphics here.
var marker = new SlidingMarker({
position: {lat: store.lat, lng: store.lng},
map: $scope.map,
label: store.name, // you do not need this
title: store.name, // nor this
duration: 2000,
icon: s.toDataURL()
});
Solution 14 - Javascript
EASIEST SOLUTION - USE SVG
Works in: in IE9, IE10, FF, Chrome, Safari
(if you are using other browsers please "Run code snippet" and place a comment)
No external dependencies besides Google Maps API!
This is quite easy provided that you have your icon in .svg format. If that is the case just add appropriate text element and change its content to fit your needs with JS.
Add something like this to your .svg
code (this is text "section" which will be later changed with JS):
<text id="1" fill="#20539F" font-family="NunitoSans-ExtraBold, Nunito Sans" font-size="18" font-weight="600" letter-spacing=".104" text-anchor="middle" x="50%" y="28">1</text>
Example: (partially copied from @EstevãoLucas)
Important:
Use correct <text>
tag properties. Note text-anchor="middle" x="50%" y="28"
which center longer numbers (more info: https://stackoverflow.com/questions/5546346/how-to-place-and-center-text-in-an-svg-rectangle)
Use encodeURIComponent()
(this probably ensures compatibility with IE9 and 10)
// Most important part (use output as Google Maps icon)
function getMarkerIcon(number) {
// inline your SVG image with number variable
var svg = '<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="40" height="40" viewBox="0 0 40 40"> <defs> <rect id="path-1" width="40" height="40"/> <mask id="mask-2" width="40" height="40" x="0" y="0" fill="white"> <use xlink:href="#path-1"/> </mask> </defs> <g id="Page-1" fill="none" fill-rule="evenodd"> <g id="Phone-Portrait---320" transform="translate(-209 -51)"> <g id="Group" transform="translate(209 51)"> <use id="Rectangle" fill="#FFEB3B" stroke="#F44336" stroke-width="4" mask="url(#mask-2)" xlink:href="#path-1"/> <text id="1" fill="#20539F" font-family="NunitoSans-ExtraBold, Nunito Sans" font-size="18" font-weight="600" letter-spacing=".104" text-anchor="middle" x="50%" y="28">' + number + '</text> </g> </g> </g> </svg>';
// use SVG without base64 see: https://css-tricks.com/probably-dont-base64-svg/
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);
}
// Standard Maps API code
var markers = [
[1, -14.2350040, -51.9252800],
[2, -34.028249, 151.157507],
[3, 39.0119020, -98.4842460],
[5, 48.8566140, 2.3522220],
[9, 38.7755940, -9.1353670],
[12, 12.0733335, 52.8234367],
];
function initializeMaps() {
var myLatLng = {
lat: -25.363,
lng: 131.044
};
var map = new google.maps.Map(document.getElementById('map_canvas'), {
zoom: 4,
center: myLatLng
});
var bounds = new google.maps.LatLngBounds();
markers.forEach(function(point) {
var pos = new google.maps.LatLng(point[1], point[2]);
new google.maps.Marker({
position: pos,
map: map,
icon: getMarkerIcon(point[0]),
});
bounds.extend(pos);
});
map.fitBounds(bounds);
}
initializeMaps();
#map_canvas {
width: 100%;
height: 300px;
}
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="style.css">
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
</head>
<body>
<div id="map_canvas"></div>
</body>
<script src="script.js"></script>
</html>
More info about inline SVG in Google Maps: https://robert.katzki.de/posts/inline-svg-as-google-maps-marker
Solution 15 - Javascript
Perhaps there are those still looking for this but finding Google Dynamic icons deprecated and other map-icon libraries just a little bit too ugly.
To add a simple marker with any number inside using a URL. In Google Drive using the Google My Maps, it creates numbered icons when using a map layer that is set to 'Sequence of Numbers' and then adding markers/points on the map.
Looking at the source code, Google has their own way of doing it through a URL:
https://mt.google.com/vt/icon/name=icons/onion/SHARED-mymaps-container-bg_4x.png,icons/onion/SHARED-mymaps-container_4x.png,icons/onion/1738-blank-sequence_4x.png&highlight=ff000000,0288D1,ff000000&scale=2.0&color=ffffffff&psize=15&text=56&font=fonts/Roboto-Medium.ttf
I haven't played extensively with it but by changing the hex color codes in the 'highlight' parameter(color parameter does not change the color as you may think), the 'text' value can be set to any string and you can make a nice round icon with any number/value inside. I'm sure the other parameters may be of use too.
One caveat with this approach, who knows when Google will remove this URL from the world!
Solution 16 - Javascript
$(document).ready(function() {
// initiate Google maps
initialize();
// make a .hover event
$('#markers_info .marker').hover(
// mouse in
function () {
// first we need to know which <div class="marker"></div> we hovered
var index = $('#markers_info .marker').index(this);
markers[index].setIcon(highlightedIcon());
},
// mouse out
function () {
// first we need to know which <div class="marker"></div> we hovered
var index = $('#markers_info .marker').index(this);
markers[index].setIcon(activeIcon());
}
);
});
/**
Google Maps stuff
*/
var markerData = [ // the order of these markers must be the same as the <div class="marker"></div> elements
{lat: 43.0628, lng: -89.4165, title: 'Manneken Pis'},
{lat: 43.0749, lng: -89.3927, title: 'Jeanneke Pis'},
{lat: 43.0731, lng: -89.4012, title: 'Grand Place'},
{lat: 43.0766, lng: -89.4125, title: 'Manneken Pis'},
{lat: 43.0775, lng: -89.4457, title: 'Jeanneke Pis'},
{lat: 43.0972, lng: -89.5043, title: 'Grand Place'},
{lat: 43.1351, lng: -89.4385, title: 'Manneken Pis'},
{lat: 43.1267, lng: -89.3203, title: 'Jeanneke Pis'}
];
var label = "1";
var map;
var markers = [];
var mapOptions = {
zoom: 12,
center: new google.maps.LatLng(43.0731,-89.4360),// United state,madison long lat
mapTypeId: google.maps.MapTypeId.ROADMAP
};
function initialize() {
map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
new google.maps.Size(49,100)
for (var i=0; i<markerData.length; i++) {
markers.push(
new google.maps.Marker({
position: new google.maps.LatLng(markerData[i].lat, markerData[i].lng),
title: markerData[i].title,
map: map,
label: {
text: label,
// Adding the custom label
fontFamily: 'Raleway, sans-serif' + label,
color: 'white',
fontSize: "16px",
fontweight:"bold"
},
icon: activeIcon()
})
);
}
}
// functions that return icons. Make or find your own markers.
function activeIcon() {
return {
path:
"M172.268 501.67C26.97 291.031 0 269.413 0 192 0 85.961 85.961 0 192 0s192 85.961 192 192c0 77.413-26.97 99.031-172.268 309.67-9.535 13.774-29.93 13.773-39.464 0z",
fillColor: "#2f8aac",
fillOpacity: 1,
strokeWeight: 0,
rotation: 0,
scale: 0.08,
anchor: new google.maps.Point(15, 30),
labelOrigin: new google.maps.Point(200, 200)
};
}
// functions that return icons. When hovering at the sidebar.
function highlightedIcon() {
return {
path:
"M172.268 501.67C26.97 291.031 0 269.413 0 192 0 85.961 85.961 0 192 0s192 85.961 192 192c0 77.413-26.97 99.031-172.268 309.67-9.535 13.774-29.93 13.773-39.464 0z",
fillColor: "#d22a15",
fillOpacity: 5,
strokeWeight: 0,
rotation: 0,
scale: 0.12,
anchor: new google.maps.Point(15, 30),
labelOrigin: new google.maps.Point(200, 200)
};
}
Solution 17 - Javascript
Here are custom icons with the updated "visual refresh" style that you can generate quickly via a simple .vbs script. I also included a large pre-generated set that you can use immediately with multiple color options: https://github.com/Concept211/Google-Maps-Markers
Use the following format when linking to the GitHub-hosted image files:
https://raw.githubusercontent.com/Concept211/Google-Maps-Markers/master/images/marker_[color][character].png
color red, black, blue, green, grey, orange, purple, white, yellow
character A-Z, 1-100, !, @, $, +, -, =, (%23 = #), (%25 = %), (%26 = &), (blank = •)
Examples:
https://raw.githubusercontent.com/Concept211/Google-Maps-Markers/master/images/marker_red1.png
https://raw.githubusercontent.com/Concept211/Google-Maps-Markers/master/images/marker_blue2.png
https://raw.githubusercontent.com/Concept211/Google-Maps-Markers/master/images/marker_green3.png