How LINK Transport Powers Just-In-Time Logistics with HERE WeGo Pro
Mohini Todkari — 11 June 2025
7 min read
29 March 2024
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.
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
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.
Red coffee cup as regular Marker
Bird gif as DomMarker
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.
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 iconsvar 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 iconsvar 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 implementationvar 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>
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!
Mohini Todkari
Sr. Developer Evangelist
Share article
Why sign up:
Latest offers and discounts
Tailored content delivered weekly
Exclusive events
One click to unsubscribe