All about Markers in the HERE Maps API for JavaScript
In this blog post, we’ll walk you through three ways of customizing and adding markers within HERE Maps API for JavaScript. The most useful scenario in a mapping application is showing the points of interest (POIs) on a map. These are represented as markers.
Understanding Markers
Markers are objects within the HERE Maps API for JavaScript that help in visually identifying locations on a map. They are defined by the geographical point as Latitude and Longitude, and by the icon representing it. Panning the map changes the position of the marker on the screen, but its size remains constant on changing the zoom level.
The API offers two types of markers:
- normal marker (
H.map.Marker
) – static image as icons - DOM marker (
H.map.DomMarker
) – dynamic content as HTML or SVG
Adding an image as a Marker
We’ll use the regular marker (H.map.Marker
) for this purpose. Marker
objects accept instances of H.map.Icon
, which can be PNG, JPEG, or SVG icons. The ‘icon’ property of a marker is optional; the API provides a default icon for markers that don’t have a custom icon assigned.
Here is an example of adding an image as a marker:
var coffeeImage = new H.map.Icon('coffee.png')
const coffeeMarker = new H.map.Marker(coffeeShop, { icon: coffeeImage });
map.addObject(coffeeMarker);
Replace 'coffee.png' with the actual path to your icon.
Adding a dynamic marker
For scenarios that need interactive or animated elements within markers, such as animated GIFs, SVGs, or hover effects, the DomMarker
class is useful. It uses the H.map.DomIcon
.
Here is an example of adding a bird as a gif using dynamic marker:
<body>
<div id="gif-container">
<img src="bird.gif" alt="Bird Marker">
</div>
</body>
<script>
...
const gifContainer = document.getElementById('gif-container');
var icon = new H.map.DomIcon(gifContainer)
birdMarker = new H.map.DomMarker(Boston, {icon: icon});
map.addObject(birdMarker);
...
</script>
In the documentation, you’ll also find an example of adding an animated SVG of a bouncing ball to the map.
Marker Clustering
The marker’s size does not change when you zoom in or out of the map. This behavior can result in performance degradation when all the points (also called “markers”) are visible at lower zoom levels. It is likely you will want to cluster the points that are near each other, so that, when zoomed in, instead of overlapping and obscuring each other, you maintain a single visualization. That’s what the marker cluster does. It’s an on-the-fly algorithm that will aggregate points as one, if they are close by at a certain zoom level. The points that are not collapsed as one are still visible on the map as “noise points”.
The steps to implement clustering include:
- Include the clustering module in the <head> section of the HTML file.
- Create a clustering data set.
- Display clusters on a map.
- Customize the icons for noise points and clusters.
Here is the complete code for implementing the above.
<!DOCTYPE html>
<html lang="en">
<head>
<title>Markers - HERE Maps API for JavaScript</title>
<meta name="viewport" charset="UTF-8" content="initial-scale=1.0, width=device-width" />
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-core.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-service.js"></script>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-ui.js"></script>
<link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
<script type="text/javascript" charset="utf-8" src="https://js.api.here.com/v3/3.1/mapsjs-clustering.js"></script>
</head>
<body>
<div id="mapContainer" style="align-self: center; width: 100vw; height: 80vh;"></div>
<div id="gif-container">
<img src="bird.gif" alt="Bird Marker">
</div>
</body>
<script>
// Initialize platform with JS API KEY
var platform = new H.service.Platform({
apikey: "YOUR API KEY"
});
var Boston = {lat:42.35866, lng:-71.05674};
var coffeeShop = {lat:42.3544238527005, lng:-71.06364536193098};
// initializing default layers for the map
var defaultLayers = platform.createDefaultLayers();
// rendering map within the container on the page
var map = new H.Map(
document.getElementById('mapContainer'),
defaultLayers.vector.normal.map,
{
zoom: 13.5, // Initial zoom level of map
center: Boston
});
// Create the default UI:
var ui = H.ui.UI.createDefault(map, defaultLayers);
// initialize basic map events
var mapEvents = new H.mapevents.MapEvents(map);
// Initialize for map behaviour on events - pan, zoom and pinch-to-zoom
var behavior = new H.mapevents.Behavior(mapEvents);
// Add event listener
map.addEventListener('tap', function(evt) {
// Log 'tap' event
let pointer = evt.currentPointer;
});
//1. Custom Markers (icons and logos)
var coffeeImage = new H.map.Icon('coffee.png')
const coffeeMarker = new H.map.Marker(coffeeShop, { icon: coffeeImage });
map.addObject(coffeeMarker);
//2. Animated marker
const gifContainer = document.getElementById('gif-container');
var icon = new H.map.DomIcon(gifContainer)
birdMarker = new H.map.DomMarker(Boston, {icon: icon});
map.addObject(birdMarker);
//3. Clustering markers
var dataPoints = [];
dataPoints.push(new H.clustering.DataPoint(42.349670019103996, -71.08415202987929)); // Blue Bottle Coffee: 42.349670019103996, -71.08415202987929
dataPoints.push(new H.clustering.DataPoint(42.337875655581044, -71.0754017798143)); // Jaho Coffee Roaster & Wine Bar: 42.337875655581044, -71.0754017798143
dataPoints.push(new H.clustering.DataPoint(42.341747420783186, -71.08158036136719)); // Render Coffee: 42.341747420783186, -71.08158036136719
dataPoints.push(new H.clustering.DataPoint(42.35077270744188, -71.13234593479136)); // Pavement Coffeehouse: 42.35077270744188, -71.13234593479136
dataPoints.push(new H.clustering.DataPoint(42.3669486722285, -71.10267504837441)); // Caffe Nero: 42.3669486722285, -71.10267504837441
dataPoints.push(new H.clustering.DataPoint(42.365823954221135, -71.05552765985979)); // Caffe Vittoria: 42.365823954221135, -71.05552765985979
// SVG to use for noise icons
var coffeeShops ='<svg fill="#000000" height="64px" width="64px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-48.99 -48.99 587.88 587.88" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0" transform="translate(0,0), scale(1)"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="19.596"> <g> <g> <g> <path d="M318.9,411.9c17.1-23.7,32.3-53.3,43.9-86.7c12.4,7,26.4,10.9,40.4,10.9c47.8,0,86.7-42,86.7-93.3s-38.9-93.3-86.7-93.3 c-3.5,0-6.6,0.4-10.1,0.8c0-5.4,0.4-10.9,0.4-16.3H25.7c0,113.9,29.6,215.1,74.7,278.1h218.5V411.9z M392.4,170 c3.5-0.8,7.4-1.2,10.9-1.2c36.9,0,66.9,33.1,66.9,73.5s-30,73.6-66.9,73.6c-12.4,0-24.1-3.5-34.2-10.5 C381.5,264.9,389.7,219,392.4,170z"></path> <polygon points="22.9,484.3 464.7,484.3 487.3,431.4 0,431.4 "></polygon> <path d="M116.7,102.8c5.4,0,9.7-4.3,9.7-9.7V52.6c0-5.4-4.3-9.7-9.7-9.7c-5.4,0-9.7,4.3-9.7,9.7V93 C106.6,98.1,111.2,102.8,116.7,102.8z"></path> <path d="M209.6,102.8c5.4,0,9.7-4.3,9.7-9.7V15.3c0-5.4-4.3-9.7-9.7-9.7s-9.7,4.3-9.7,9.7v77.8 C199.9,98.1,204.2,102.8,209.6,102.8z"></path> <path d="M302.6,102.8c5.4,0,9.7-4.3,9.7-9.7V52.6c0-5.4-4.3-9.7-9.7-9.7s-9.7,4.3-9.7,9.7V93C292.8,98.1,297.1,102.8,302.6,102.8 z"></path> </g> </g> </g> </g><g id="SVGRepo_iconCarrier"> <g> <g> <g> <path d="M318.9,411.9c17.1-23.7,32.3-53.3,43.9-86.7c12.4,7,26.4,10.9,40.4,10.9c47.8,0,86.7-42,86.7-93.3s-38.9-93.3-86.7-93.3 c-3.5,0-6.6,0.4-10.1,0.8c0-5.4,0.4-10.9,0.4-16.3H25.7c0,113.9,29.6,215.1,74.7,278.1h218.5V411.9z M392.4,170 c3.5-0.8,7.4-1.2,10.9-1.2c36.9,0,66.9,33.1,66.9,73.5s-30,73.6-66.9,73.6c-12.4,0-24.1-3.5-34.2-10.5 C381.5,264.9,389.7,219,392.4,170z"></path> <polygon points="22.9,484.3 464.7,484.3 487.3,431.4 0,431.4 "></polygon> <path d="M116.7,102.8c5.4,0,9.7-4.3,9.7-9.7V52.6c0-5.4-4.3-9.7-9.7-9.7c-5.4,0-9.7,4.3-9.7,9.7V93 C106.6,98.1,111.2,102.8,116.7,102.8z"></path> <path d="M209.6,102.8c5.4,0,9.7-4.3,9.7-9.7V15.3c0-5.4-4.3-9.7-9.7-9.7s-9.7,4.3-9.7,9.7v77.8 C199.9,98.1,204.2,102.8,209.6,102.8z"></path> <path d="M302.6,102.8c5.4,0,9.7-4.3,9.7-9.7V52.6c0-5.4-4.3-9.7-9.7-9.7s-9.7,4.3-9.7,9.7V93C292.8,98.1,297.1,102.8,302.6,102.8 z"></path> </g> </g> </g> </g></svg>';
var noiseIcon = new H.map.Icon(coffeeShops, {
size: { w: 20, h: 20 },
anchor: { x: 10, y: 10},
});
// SVG to use for cluster icons
var coffeeClusterSvg = '<svg fill="green" height="{diameter}" width="{diameter}" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-48.99 -48.99 587.88 587.88" xml:space="preserve"><g id="SVGRepo_bgCarrier" stroke-width="0" transform="translate(0,0), scale(1)"></g><g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round" stroke="#CCCCCC" stroke-width="19.596"> <g> <g> <g> <path d="M318.9,411.9c17.1-23.7,32.3-53.3,43.9-86.7c12.4,7,26.4,10.9,40.4,10.9c47.8,0,86.7-42,86.7-93.3s-38.9-93.3-86.7-93.3 c-3.5,0-6.6,0.4-10.1,0.8c0-5.4,0.4-10.9,0.4-16.3H25.7c0,113.9,29.6,215.1,74.7,278.1h218.5V411.9z M392.4,170 c3.5-0.8,7.4-1.2,10.9-1.2c36.9,0,66.9,33.1,66.9,73.5s-30,73.6-66.9,73.6c-12.4,0-24.1-3.5-34.2-10.5 C381.5,264.9,389.7,219,392.4,170z"></path> <polygon points="22.9,484.3 464.7,484.3 487.3,431.4 0,431.4 "></polygon> <path d="M116.7,102.8c5.4,0,9.7-4.3,9.7-9.7V52.6c0-5.4-4.3-9.7-9.7-9.7c-5.4,0-9.7,4.3-9.7,9.7V93 C106.6,98.1,111.2,102.8,116.7,102.8z"></path> <path d="M209.6,102.8c5.4,0,9.7-4.3,9.7-9.7V15.3c0-5.4-4.3-9.7-9.7-9.7s-9.7,4.3-9.7,9.7v77.8 C199.9,98.1,204.2,102.8,209.6,102.8z"></path> <path d="M302.6,102.8c5.4,0,9.7-4.3,9.7-9.7V52.6c0-5.4-4.3-9.7-9.7-9.7s-9.7,4.3-9.7,9.7V93C292.8,98.1,297.1,102.8,302.6,102.8 z"></path> </g> </g> </g> </g><g id="SVGRepo_iconCarrier"> <g> <g> <g> <path d="M318.9,411.9c17.1-23.7,32.3-53.3,43.9-86.7c12.4,7,26.4,10.9,40.4,10.9c47.8,0,86.7-42,86.7-93.3s-38.9-93.3-86.7-93.3 c-3.5,0-6.6,0.4-10.1,0.8c0-5.4,0.4-10.9,0.4-16.3H25.7c0,113.9,29.6,215.1,74.7,278.1h218.5V411.9z M392.4,170 c3.5-0.8,7.4-1.2,10.9-1.2c36.9,0,66.9,33.1,66.9,73.5s-30,73.6-66.9,73.6c-12.4,0-24.1-3.5-34.2-10.5 C381.5,264.9,389.7,219,392.4,170z"></path> <polygon points="22.9,484.3 464.7,484.3 487.3,431.4 0,431.4 "></polygon> <path d="M116.7,102.8c5.4,0,9.7-4.3,9.7-9.7V52.6c0-5.4-4.3-9.7-9.7-9.7c-5.4,0-9.7,4.3-9.7,9.7V93 C106.6,98.1,111.2,102.8,116.7,102.8z"></path> <path d="M209.6,102.8c5.4,0,9.7-4.3,9.7-9.7V15.3c0-5.4-4.3-9.7-9.7-9.7s-9.7,4.3-9.7,9.7v77.8 C199.9,98.1,204.2,102.8,209.6,102.8z"></path> <path d="M302.6,102.8c5.4,0,9.7-4.3,9.7-9.7V52.6c0-5.4-4.3-9.7-9.7-9.7s-9.7,4.3-9.7,9.7V93C292.8,98.1,297.1,102.8,302.6,102.8 z"></path> </g> </g> </g> </g></svg>';
// Create a clustered data provider and a theme implementation
var clusteredDataProvider = new H.clustering.Provider(dataPoints, {
theme: {
getClusterPresentation: function(cluster) {
// Use cluster weight to change the icon size
var weight = cluster.getWeight(),
// Calculate circle size
radius = weight * 5,
diameter = radius * 2,
// Replace variables in the icon template
svgString = coffeeClusterSvg.replace(/\{radius\}/g, radius).replace(/\{diameter\}/g, diameter);
// Create an icon
// Note that we create a different icon depending from the weight of the cluster
clusterIcon = new H.map.Icon(svgString, {
size: {w: diameter, h: diameter},
anchor: {x: radius, y: radius}
}),
// Create a marker for the cluster
clusterMarker = new H.map.Marker(cluster.getPosition(), {
icon: clusterIcon,
// Set min/max zoom with values from the cluster, otherwise
// clusters will be shown at all zoom levels
min: cluster.getMinZoom(),
max: cluster.getMaxZoom()
});
// Bind cluster data to the marker
clusterMarker.setData(cluster);
return clusterMarker;
},
getNoisePresentation: function(noisePoint) {
// Create a marker for noise points:
var noiseMarker = new H.map.Marker(noisePoint.getPosition(), {
icon: noiseIcon,
// Use min zoom from a noise point to show it correctly at certain zoom levels
min: noisePoint.getMinZoom()
});
// Bind noise point data to the marker:
noiseMarker.setData(noisePoint);
return noiseMarker;
}
}
});
// Create a layer that includes the data provider and its data points:
var layer = new H.map.layer.ObjectLayer(clusteredDataProvider);
map.addLayer(layer);
</script>
</html>
Conclusion
Using markers in a strategic way lets you make your maps more engaging and informative. Remember to reuse icons between markers to improve map performance, especially if you are creating numerous markers. If you are working with a 3D map, you can explore the option of using elevated markers with the WebGL rendering engine to highlight a location. Try the different image formats and transform your maps into something more compelling!
Have your say
Sign up for our newsletter
Why sign up:
- Latest offers and discounts
- Tailored content delivered weekly
- Exclusive events
- One click to unsubscribe