Hands On

Updating a Map with JavaScript on an Interval to Reflect Server-Side Changes

By Nic Raboy | 13 June 2019

About a week ago I had written a tutorial around showing a map with data that I had collected within HERE XYZ. The tutorial was on the basis that I was collecting GPS data of my current route and then later showing it on the map. However, what if I wanted others to view my current route as it happens?

We’re going to see how to use simple JavaScript to continuously request data from HERE XYZ to update on a map.

Requesting HERE XYZ Data on an Interval

When it comes to JavaScript, there are several approaches to refresh the screen if you’re not able to use an event listener, websocket, or long-polling request. You could refresh the entire page using an HTML <meta> tag, you could reload the page with JavaScript, or you could perform tasks within the page on an interval.

With maps, refreshing the entire page could be costly towards your API transactions. Instead it makes more sense to do things on an interval and only update what’s necessary. To do this we can make use of the JavaScript setInterval method. This method looks something like this:

setInterval(() => {
    // Repeating logic here...
}, 1000)

In the above example, we would repeat our function every 1000 milliseconds. To put things into perspective, if we’ve already rendered a map, we might make an HTTP request on an interval and draw new map objects on the map.

Take the following example which makes use of Leaflet.js and HERE XYZ:

<html>
    <head>
        <link rel="stylesheet" href="https://unpkg.com/leaflet@1.4.0/dist/leaflet.css" />
    </head>
    <body style="margin: 0">
        <div id="map" style="width: 100vw; height: 100vh"></div>
        <script src="https://unpkg.com/leaflet@1.4.0/dist/leaflet.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.18.0/axios.min.js"></script>
        <script src="https://cdnjs.cloudflare.com/ajax/libs/geojson/0.5.0/geojson.min.js"></script>
        <script>
            const start = async () => {
                const tiles = "https://1.base.maps.api.here.com/maptile/2.1/maptile/newest/normal.day/{z}/{x}/{y}/512/png8?app_id={appId}&app_code={appCode}";
                const map = new L.Map("map", {
                    center: [37, -121],
                    zoom: 11,
                    layers: [L.tileLayer(tiles, { appId: "HERE_APP_ID", appCode: "HERE_APP_CODE" })]
                });
                var polyline, sourceMarker, destinationMarker;
                setInterval(async () => {
                    const response = await axios({
                        method: "GET",
                        url: "https://xyz.api.here.com/hub/spaces/" + "HERE_XYZ_SPACE_ID" + "/search",
                        params: {
                            access_token: "HERE_XYZ_TOKEN"
                        }
                    });
                    map.eachLayer(layer => {
                        if(!layer._url) {
                            layer.remove();
                        }
                    });
                    const polylineData = [];
                    response.data.features.forEach(feature => {
                        let position = feature.geometry.coordinates;
                        polylineData.push({ lat: position[1], lng: position[0] });
                    });
                    polyline = new L.Polyline(polylineData, { weight: 5 });
                    sourceMarker = new L.Marker(polylineData[0]);
                    destinationMarker = new L.Marker(polylineData[polylineData.length - 1]);
                    polyline.addTo(map);
                    sourceMarker.addTo(map);
                    destinationMarker.addTo(map);
                    const bounds = new L.LatLngBounds(polylineData);
                    map.fitBounds(bounds);
                }, 5000);
            }
            start();
        </script>
    </body>
</html>

If the above code looks familiar to you, it is because you probably saw it in my previous example. However, my previous example did not refresh to render new data on the map.

In my example above, I am using setInterval to make an HTTP request with axios, clear the map of all objects except the HERE map tiles, and redraw the polylines and markers based on the new information.

Conclusion

You just saw how to use a simple setInterval JavaScript method to continuously request new data from HERE XYZ and display it on a map. If given the opportunity, websockets, event listeners, and long-polling would be the most ideal to prevent requests for data that might not have changed, but this is the next best thing.