Skip to main content
APIs 8 min read

Interactive Geofencing and Location Search with HERE Maps API for JavaScript

geofenceHero

Geofencing technology has transformed how we interact with geographical boundaries within digital applications. By leveraging the HERE Maps API for JavaScript, developers can create sophisticated, interactive maps that not only allow users to define dynamic geofences but also search for specific locations and receive real-time suggestions. This guide walks you through the process of building such an application, highlighting key features of the HERE Maps API and demonstrating how they can be used to enhance user engagement and location awareness.

geofence

Setting Up the Environment

The first step in creating our interactive map involves setting up our HTML environment. This includes linking to the HERE Maps API for JavaScript libraries, defining our map display, and preparing a control panel for user interactions.

Copied
        <html>
<head>
    <title>Interactive Geofencing and Location Search on HERE Map</title>
    <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>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-harp.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
    <link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
    <style>
        html, body { margin: 0; padding: 0; height: 100% }
        #map { width: 100%; height: 90%; }
        #control-panel { text-align: center; margin: 10px; }
        #searchInput { width: 300px; }
        #suggestions { position: absolute; top: 50px; left: 10px; background: white; }
    </style>
</head>
<body>
    <div id="control-panel">
        <input type="text" id="searchInput" placeholder="Search location...">
        <button id="clear-btn" class="shape-btn">Clear</button>
    </div>
    <div id="map"></div>
    <div id="suggestions"></div>
</body>
</html>
  

Initializing the Map

With our HTML set up, the next step is initializing the map. This involves creating a new map instance, setting its center, zoom level, and integrating it with the HERE Maps API for interactive functionality:

Copied
        var platform = new H.service.Platform({
    apikey: "yourapikey"
});
var engineType = H.Map.EngineType['HARP'];
        var defaultLayers = platform.createDefaultLayers({
        	engineType: engineType,
               pois: true
        });
        var map = new H.Map(document.getElementById('map'),
            defaultLayers.vector.normal.map, {
            center: {lat: 52.53086, lng: 13.38469},
            zoom: 18,
            engineType: H.Map.EngineType['HARP'],
            pixelRatio: window.devicePixelRatio || 1
        });

        var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
        var ui = H.ui.UI.createDefault(map, defaultLayers);

  

Implementing Real-Time Geofencing

The core functionality of our application lies in the ability to create and adjust geofences in real-time. This is achieved by allowing users to place markers on the map. These markers define the vertices of our geofence polygon.

Adding Draggable Markers

Markers are added to the map with a tap event. Each marker is draggable, enabling users to adjust the geofence shape dynamically.

Copied
        // Function to add a draggable marker
function addDraggableMarker(coord) {
    var icon = new H.map.Icon(svgMarkup);
    var marker = new H.map.Marker(coord, { icon: icon, volatility: true });
    marker.draggable = true;
    map.addObject(marker);
    markers.push(marker);
}

  

Updating the Geofence Shape

As markers are added or moved, the geofence shape updates in real-time. This involves drawing a polygon that connects all markers, closing the loop if the first and last markers are within a specified distance.

Copied
        // Function to update the geofence shape
        function updateGeofenceShape() {
            if (geofenceShape) {
                map.removeObject(geofenceShape);
            }

            if (markers.length > 1) {
                var lineString = new H.geo.LineString();
                markers.forEach(marker => lineString.pushPoint(marker.getGeometry()));

                // Close the shape if the last marker is near the first marker
                if (markers[markers.length - 1].getGeometry().distance(markers[0].getGeometry()) * 1000 < someThresholdDistance) {
                    lineString.pushPoint(markers[0].getGeometry());
                }

                geofenceShape = new H.map.Polygon(lineString, {
                    style: {
                        fillColor: 'rgba(55, 85, 170, 0.4)',
                        lineWidth: 2,
                        strokeColor: 'rgba(55, 85, 170, 1)'
                    }
                });
                map.addObject(geofenceShape);
            }
        } 

  

Enhancing User Interaction with Search and Autosuggest

To enhance user experience, our application includes a search feature with autosuggestions. As users type a location, autosuggestions appear, guiding them to precise locations quickly.

Handling Search and Displaying Suggestions

The searchService object from the HERE API is used to fetch location suggestions based on user input. Selected suggestions adjust the map view accordingly:

Copied
        // Autosuggest functionality
        function startSearch(query) {
            if (query.trim() === '') {
                clearSuggestions();
                return;
            }

            searchService.autosuggest({
                q: query,
                at: map.getCenter().lat + ',' + map.getCenter().lng
            }, (result) => {
                displaySuggestions(result.items);
            }, (error) => {
                console.error('Autosuggest error:', error);
            });
        } 

  

Clearing the Geofence and Markers

Finally, users can clear the existing geofence and markers with a simple button click, resetting the application state and allowing for a new geofence to be created.

Copied
        document.getElementById('clear-btn').addEventListener('click', clearGeofence);
  

Please see the full html code below to understand the integration and flow of functionalities in a single, cohesive application:

Copied
        <!DOCTYPE html>
<html>
<head>
    <title>Real-time Geofence Shape Creation on HERE Map</title>
    <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>
<script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-harp.js"></script>
    <script type="text/javascript" src="https://js.api.here.com/v3/3.1/mapsjs-mapevents.js"></script>
    <link rel="stylesheet" type="text/css" href="https://js.api.here.com/v3/3.1/mapsjs-ui.css" />
    <style>
        html, body { margin: 0; padding: 0; height: 100% }
        #map { width: 100%; height: 90%; }
        #control-panel { text-align: center; margin: 10px; }
        #searchInput { width: 300px; }
        #suggestions { position: absolute; top: 50px; left: 10px; background: white; }
    </style>
</head>
<body>
    <div id="control-panel">
        <input type="text" id="searchInput" placeholder="Search location...">
        <button id="clear-btn" class="shape-btn">Clear</button>
    </div>
    <div id="map"></div>
    <div id="suggestions"></div>
    <script>
        var platform = new H.service.Platform({
            apikey: "yourapikey"
        });
        var engineType = H.Map.EngineType['HARP'];
        var defaultLayers = platform.createDefaultLayers({
        	engineType: engineType,
          pois: true
        });
        var map = new H.Map(document.getElementById('map'),
            defaultLayers.vector.normal.map, {
            center: {lat: 52.53086, lng: 13.38469},
            zoom: 18,
            engineType: H.Map.EngineType['HARP'],
            pixelRatio: window.devicePixelRatio || 1
        });

        var behavior = new H.mapevents.Behavior(new H.mapevents.MapEvents(map));
        var ui = H.ui.UI.createDefault(map, defaultLayers);
        var searchService = platform.getSearchService();
        var geofenceShape;
        var markers = [];
        var someThresholdDistance = 20; // 20 meters threshold

        // Function to update the geofence shape
        function updateGeofenceShape() {
            if (geofenceShape) {
                map.removeObject(geofenceShape);
            }

            if (markers.length > 1) {
                var lineString = new H.geo.LineString();
                markers.forEach(marker => lineString.pushPoint(marker.getGeometry()));

                // Close the shape if the last marker is near the first marker
                if (markers[markers.length - 1].getGeometry().distance(markers[0].getGeometry()) * 1000 < someThresholdDistance) {
                    lineString.pushPoint(markers[0].getGeometry());
                }

                geofenceShape = new H.map.Polygon(lineString, {
                    style: {
                        fillColor: 'rgba(55, 85, 170, 0.4)',
                        lineWidth: 2,
                        strokeColor: 'rgba(55, 85, 170, 1)'
                    }
                });
                map.addObject(geofenceShape);
            }
        }
// Function to add a draggable marker
function addDraggableMarker(coord) {
    var svgMarkup = '<svg width="24" height="24" xmlns="http://www.w3.org/2000/svg">' +
                    '<circle cx="12" cy="12" r="10" stroke="black" stroke-width="2" fill="white" />' +
                    '</svg>';

    var icon = new H.map.Icon(svgMarkup);
    var marker = new H.map.Marker(coord, { icon: icon, volatility: true });
    marker.draggable = true;
    map.addObject(marker);
    markers.push(marker);
}

        // Function to clear the geofence and markers
        function clearGeofence() {
            if (geofenceShape) {
                map.removeObject(geofenceShape);
                geofenceShape = null;
            }
            markers.forEach(marker => map.removeObject(marker));
            markers = [];
        }

        // Event listener for map click
        map.addEventListener('tap', function(evt) {
            var coord = map.screenToGeo(evt.currentPointer.viewportX, evt.currentPointer.viewportY);
            addDraggableMarker(coord);
            updateGeofenceShape();
        });

        // Event listener for marker drag
        map.addEventListener('dragstart', function(evt) {
            var target = evt.target;
            if (target instanceof H.map.Marker) {
                behavior.disable();
            }
        }, false);

        map.addEventListener('dragend', function(evt) {
            var target = evt.target;
            if (target instanceof H.map.Marker) {
                behavior.enable();
                updateGeofenceShape();
            }
        }, false);

        map.addEventListener('drag', function(evt) {
            var target = evt.target;
            if (target instanceof H.map.Marker) {
                target.setGeometry(map.screenToGeo(evt.currentPointer.viewportX, evt.currentPointer.viewportY));
            }
        }, false);

        // Event listener for the clear button
        document.getElementById('clear-btn').addEventListener('click', clearGeofence);

        // Autosuggest functionality
        function startSearch(query) {
            if (query.trim() === '') {
                clearSuggestions();
                return;
            }

            searchService.autosuggest({
                q: query,
                at: map.getCenter().lat + ',' + map.getCenter().lng
            }, (result) => {
                displaySuggestions(result.items);
            }, (error) => {
                console.error('Autosuggest error:', error);
            });
        }

        function clearSuggestions() {
            var suggestionsContainer = document.getElementById('suggestions');
            if (suggestionsContainer) {
                suggestionsContainer.innerHTML = '';
            }
        }

        function displaySuggestions(items) {
            var suggestionsContainer = document.getElementById('suggestions');
            if (suggestionsContainer) {
                suggestionsContainer.innerHTML = '';
                items.forEach(function(item) {
                    var div = document.createElement('div');
                    div.innerHTML = item.title;
                    div.onclick = function() {
                        map.setCenter(item.position);
                        map.setZoom(14);
                        suggestionsContainer.innerHTML = '';
                    };
                    suggestionsContainer.appendChild(div);
                });
            }
        }

        document.getElementById('searchInput').addEventListener('input', function(evt) {
            startSearch(evt.target.value);
        });
    </script>
</body>
</html>


  

Conclusion

By integrating the HERE Maps API for JS into our web application, we've created a dynamic platform for real-time geofencing. This allows users to define and adjust geofences interactively, providing a powerful tool for a variety of location-based services. Through this guide, we've seen how to set up the environment, implement the geofencing logic, and enhance user interaction with search and autosuggestions. The flexibility and real-time capabilities of the HERE Maps API for JS make it an excellent choice for developers looking to build sophisticated geolocation features in their applications.

Thanks for reading! Drop us a line if you have any feedback or questions below or come by is our Slack workspace

@heredev

Sachin Jonda

Sachin Jonda

Lead Onboarding Engineer

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