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
This question is related to
javascript
google-maps
google-maps-api-3
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
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.
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
1) Get a custom marker image.
var imageObj = new Image();
imageObj.src = "/markers/blank_pin.png";
2) Create a canvas
in RAM
and draw this image on it
imageObj.onload = function(){
var canvas = document.createElement('canvas');
var context = canvas.getContext("2d");
context.drawImage(imageObj, 0, 0);
}
3) Write anything above it
context.font = "40px Arial";
context.fillText("54", 17, 55);
4) 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)
};
});
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
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: 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)_x000D_
function getMarkerIcon(number) {_x000D_
// inline your SVG image with number variable_x000D_
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>';_x000D_
// use SVG without base64 see: https://css-tricks.com/probably-dont-base64-svg/_x000D_
return 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg);_x000D_
}_x000D_
_x000D_
// Standard Maps API code_x000D_
var markers = [_x000D_
[1, -14.2350040, -51.9252800],_x000D_
[2, -34.028249, 151.157507],_x000D_
[3, 39.0119020, -98.4842460],_x000D_
[5, 48.8566140, 2.3522220],_x000D_
[9, 38.7755940, -9.1353670],_x000D_
[12, 12.0733335, 52.8234367],_x000D_
];_x000D_
_x000D_
function initializeMaps() {_x000D_
var myLatLng = {_x000D_
lat: -25.363,_x000D_
lng: 131.044_x000D_
};_x000D_
_x000D_
var map = new google.maps.Map(document.getElementById('map_canvas'), {_x000D_
zoom: 4,_x000D_
center: myLatLng_x000D_
});_x000D_
_x000D_
var bounds = new google.maps.LatLngBounds();_x000D_
_x000D_
markers.forEach(function(point) {_x000D_
var pos = new google.maps.LatLng(point[1], point[2]);_x000D_
_x000D_
new google.maps.Marker({_x000D_
position: pos,_x000D_
map: map,_x000D_
icon: getMarkerIcon(point[0]), _x000D_
});_x000D_
_x000D_
bounds.extend(pos);_x000D_
});_x000D_
_x000D_
map.fitBounds(bounds);_x000D_
}_x000D_
_x000D_
initializeMaps();
_x000D_
#map_canvas {_x000D_
width: 100%;_x000D_
height: 300px;_x000D_
}
_x000D_
<!DOCTYPE html>_x000D_
<html>_x000D_
_x000D_
<head>_x000D_
<link rel="stylesheet" href="style.css">_x000D_
_x000D_
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script> _x000D_
</head>_x000D_
_x000D_
<body>_x000D_
<div id="map_canvas"></div>_x000D_
</body>_x000D_
_x000D_
<script src="script.js"></script>_x000D_
_x000D_
</html>
_x000D_
More info about inline SVG in Google Maps: https://robert.katzki.de/posts/inline-svg-as-google-maps-marker
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
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()
});
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.
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!
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.
My two cents showing how to use the Google Charts API to solve this problem.
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:
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.
You can see the code to produce the same as this image in this Plunker: http://plnkr.co/edit/jep5mVN3DsVRgtlz1GGQ?p=preview
var markers = [_x000D_
[1002, -14.2350040, -51.9252800],_x000D_
[2000, -34.028249, 151.157507],_x000D_
[123, 39.0119020, -98.4842460],_x000D_
[50, 48.8566140, 2.3522220],_x000D_
[22, 38.7755940, -9.1353670],_x000D_
[12, 12.0733335, 52.8234367],_x000D_
];_x000D_
_x000D_
function initializeMaps() {_x000D_
var myLatLng = {_x000D_
lat: -25.363,_x000D_
lng: 131.044_x000D_
};_x000D_
_x000D_
var map = new google.maps.Map(document.getElementById('map_canvas'), {_x000D_
zoom: 4,_x000D_
center: myLatLng_x000D_
});_x000D_
_x000D_
var bounds = new google.maps.LatLngBounds();_x000D_
_x000D_
markers.forEach(function(point) {_x000D_
generateIcon(point[0], function(src) {_x000D_
var pos = new google.maps.LatLng(point[1], point[2]);_x000D_
_x000D_
bounds.extend(pos);_x000D_
_x000D_
new google.maps.Marker({_x000D_
position: pos,_x000D_
map: map,_x000D_
icon: src_x000D_
});_x000D_
});_x000D_
});_x000D_
_x000D_
map.fitBounds(bounds);_x000D_
}_x000D_
_x000D_
var generateIconCache = {};_x000D_
_x000D_
function generateIcon(number, callback) {_x000D_
if (generateIconCache[number] !== undefined) {_x000D_
callback(generateIconCache[number]);_x000D_
}_x000D_
_x000D_
var fontSize = 16,_x000D_
imageWidth = imageHeight = 35;_x000D_
_x000D_
if (number >= 1000) {_x000D_
fontSize = 10;_x000D_
imageWidth = imageHeight = 55;_x000D_
} else if (number < 1000 && number > 100) {_x000D_
fontSize = 14;_x000D_
imageWidth = imageHeight = 45;_x000D_
}_x000D_
_x000D_
var svg = d3.select(document.createElement('div')).append('svg')_x000D_
.attr('viewBox', '0 0 54.4 54.4')_x000D_
.append('g')_x000D_
_x000D_
var circles = svg.append('circle')_x000D_
.attr('cx', '27.2')_x000D_
.attr('cy', '27.2')_x000D_
.attr('r', '21.2')_x000D_
.style('fill', '#2063C6');_x000D_
_x000D_
var path = svg.append('path')_x000D_
.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')_x000D_
.attr('fill', '#FFFFFF');_x000D_
_x000D_
var text = svg.append('text')_x000D_
.attr('dx', 27)_x000D_
.attr('dy', 32)_x000D_
.attr('text-anchor', 'middle')_x000D_
.attr('style', 'font-size:' + fontSize + 'px; fill: #FFFFFF; font-family: Arial, Verdana; font-weight: bold')_x000D_
.text(number);_x000D_
_x000D_
var svgNode = svg.node().parentNode.cloneNode(true),_x000D_
image = new Image();_x000D_
_x000D_
d3.select(svgNode).select('clippath').remove();_x000D_
_x000D_
var xmlSource = (new XMLSerializer()).serializeToString(svgNode);_x000D_
_x000D_
image.onload = (function(imageWidth, imageHeight) {_x000D_
var canvas = document.createElement('canvas'),_x000D_
context = canvas.getContext('2d'),_x000D_
dataURL;_x000D_
_x000D_
d3.select(canvas)_x000D_
.attr('width', imageWidth)_x000D_
.attr('height', imageHeight);_x000D_
_x000D_
context.drawImage(image, 0, 0, imageWidth, imageHeight);_x000D_
_x000D_
dataURL = canvas.toDataURL();_x000D_
generateIconCache[number] = dataURL;_x000D_
_x000D_
callback(dataURL);_x000D_
}).bind(this, imageWidth, imageHeight);_x000D_
_x000D_
image.src = 'data:image/svg+xml;base64,' + btoa(encodeURIComponent(xmlSource).replace(/%([0-9A-F]{2})/g, function(match, p1) {_x000D_
return String.fromCharCode('0x' + p1);_x000D_
}));_x000D_
}_x000D_
_x000D_
initializeMaps();
_x000D_
#map_canvas {_x000D_
width: 100%;_x000D_
height: 300px;_x000D_
}
_x000D_
<!DOCTYPE html>_x000D_
<html>_x000D_
_x000D_
<head>_x000D_
<link rel="stylesheet" href="style.css">_x000D_
_x000D_
<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>_x000D_
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>_x000D_
_x000D_
</head>_x000D_
_x000D_
<body>_x000D_
<div id="map_canvas"></div>_x000D_
</body>_x000D_
_x000D_
<script src="script.js"></script>_x000D_
_x000D_
</html>
_x000D_
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.
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;
Source: Stackoverflow.com