Interactive Geofencing and Location Search with HERE Maps API for JavaScript
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.
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.
<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:
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.
// 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.
// 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:
// 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.
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:
<!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!
X @heredev
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