Skip to main content
APIs 7 min read

All about Markers in the HERE Maps API for JavaScript

Markers in 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: 

Copied
          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. 

dynamic markers

Red coffee cup as regular Marker

Bird gif as DomMarker 

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:  

Copied
        <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”.  

marker clustering

The steps to implement clustering include: 

  1. Include the clustering module in the <head> section of the HTML file. 
  2. Create a clustering data set. 
  3. Display clusters on a map. 
  4. Customize the icons for noise points and clusters.  

Here is the complete code for implementing the above.  

Copied
        <!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! 

Mohini Todkari

Mohini Todkari

Sr. Developer Evangelist

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