view assets/livemap.html @ 91:18e5120ba5e5

livemap: Add hidden parameter `limit`
author Lewin Bormann <lbo@spheniscida.de>
date Tue, 08 Dec 2020 20:49:09 +0100
parents cb4ef5b61aeb
children c683521147c7
line wrap: on
line source

<html>
    <head>
        <link rel="stylesheet" href="thirdparty/leaflet.css" />
        <link rel="stylesheet" href="style.css" />
        <title>GeoHub: LiveMap</title>
    </head>
    <body>
    <script src="thirdparty/leaflet.js"></script>
    <script src="shared.js"></script>

        <span id="livemapTitle">GeoHub LiveMap</span>
    <div>
        <span id="inputFields">
            Client: <input type="text" value="" id="inputClient" class="field" />
            Secret: <input type="text" value="" id="inputSecret" class="field" />
            <input type="button" value="Go!" id="inputGoButton" onclick="buttonGoClicked()" />
        </span>
    </div>
    <div id="infoFields" class="timeChanged">
        Last Update: <span id="outputLastUpdate"> </span>
    </div>

    <div id="mapid"> </div>

    <script>
    // Set up map.
    const initial = [50, 10];
    var mapZoomed = false;
    var mymap = L.map('mapid').setView(initial, 5);

    var accessToken = 'pk.eyJ1IjoiZGVybWVzc2VyIiwiYSI6ImNraTdwZmUxZTE2OGgydW1oYTU5eW9qYm4ifQ.f9OxY_U78h6iefp-jN9-9w';

    L.tileLayer('https://api.mapbox.com/styles/v1/{id}/tiles/{z}/{x}/{y}?access_token={accessToken}', {
        attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors, <a href="https://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
        maxZoom: 18,
        id: 'mapbox/streets-v11',
        tileSize: 512,
        zoomOffset: -1,
        accessToken: accessToken,
    }).addTo(mymap);

    var url = new URL(window.location);
    var allMarkers = [];

    // Set the current location, called on every new point. Updates the marker and adds
    // a new point (former position).
    var current_marker = L.marker([0,0]).addTo(mymap);
    var current_circle = L.circle([0,0], {}).addTo(mymap);
    function setCurrentLocation(lat, lng, accuracy) {
        var oldCoord = current_marker.getLatLng();
        current_marker.setLatLng(L.latLng(lat, lng));
        // Show accuracy of current location
        const currentCircleProps = {
            color: 'aqua',
            fillColor: 'aqua',
            fillOpacity: 0.1,
            radius: accuracy ? accuracy : 3,
        };
        current_circle.remove();
        current_circle = L.circle([lat, lng], currentCircleProps).addTo(mymap);
        // Show last locations as blue points.
        addMarker(oldCoord.lat, oldCoord.lng, accuracy);
    }
    function addMarker(lat, lng, accuracy) {
        const circleProps = {
            color: 'blue',
            fillColor: 'blue',
            fillOpacity: 0.1,
            radius: accuracy ? accuracy : 3,
        };
        var circle = L.circle([lat, lng], circleProps);
        circle.addTo(mymap);
        allMarkers.push(circle);
    }

    // Remove all points from the map (except for the "current position" marker).
    function clearAllMarkers() {
        for (var i = 0; i < allMarkers.length; i++) {
            allMarkers[i].remove();
        }
    }

    // New points are available. Display them on the map and update the marker.
    function xhrcallback(xhr) {
        //console.log('xhrcallback called.', xhr.readyState, xhr.status);
        if (xhr.readyState === XMLHttpRequest.DONE && xhr.status == 200) {
            const response = xhr.response;
            //console.log("Client update for", response.client);
            if (response.client != getClient()) {
                console.log("Received outdated client update.");
                return;
            }
            if (response.client == getClient() && response.geo) {
                const features = response['geo']['features'];
                if (features.length == 0) {
                    return;
                }
                const lastfeature = features[0];
                console.log(`xhrcallback: ${features.length} elements received.`);
                // Backfill old circles. This happens when called from a
                // backfill() triggered request to `/last` or when a client sent
                // several points at once.
                if (features.length > 0) {
                    for (i = 1; i < features.length; i++) {
                        var coords = features[i]['geometry']['coordinates'];
                        addMarker(coords[1], coords[0], features[i].properties.accuracy);
                    }
                }

                var coords = lastfeature['geometry']['coordinates'];

                console.log('Received update:', coords, 'last:', response);
                setCurrentLocation(coords[1], coords[0], lastfeature.properties.accuracy);
                // 13 is a good default zoom for an updated point.
                mymap.setView([coords[1], coords[0]], mapZoomed ? mymap.getZoom() : 13);
                mapZoomed = true;

                updateUI(lastfeature.properties.time);
            }

            // Install next XHR.
            waitforupdate();
        } else {
            return;
        }
    };


    // Fetch the most recent point for this client and display them.
    function backfill(args) {
        var xhr = new XMLHttpRequest();
        var client = getClient();
        var secret = getSecret();
        if (!client) {
            return;
        }
        var limit = getLimit();
        var url = `../../geo/${client}/retrieve/last?secret=${secret}&limit=${limit}`;
        console.log('Requesting URL (backfill) ' + url);
        xhr.responseType = 'json';
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function() { xhrcallback(xhr); };
        xhr.send();
    }

    var lastXHR = null;
    // Ask for updates. This request will hang until timeout is reached or
    // an update arrives.
    function waitforupdate() {
        var xhr = new XMLHttpRequest();
        var client = getClient();
        var secret = getSecret();
        if (!client) {
            return;
        }
        var secretparam = secret == null ? '' : `secret=${secret}`;
        var url = `../../geo/${client}/retrieve/live?${secretparam}&timeout=30`;
        console.log('Requesting URL ' + url);
        xhr.responseType = 'json';
        xhr.open('GET', url, true);
        xhr.onreadystatechange = function() { xhrcallback(xhr) };
        xhr.send();
        lastXHR = xhr;
    }

    function updateUI(time) {
        if (time) {
            var timefield = document.getElementById('outputLastUpdate');
            var infofields = document.getElementById('infoFields');
            // This dance restarts the green "update" animation.
            infofields.style.animation = 'none';
            infofields.offsetHeight;
            infofields.style.animation = null;
            timefield.textContent = (new Date(time)).toString().replace(/\([\w\s]+\)/,'');
        }
    }
    // "Go!" was clicked - clear markers and fetch data for new source.
    function buttonGoClicked() {
        clearAllMarkers();
        if (lastXHR) {
            lastXHR.abort();
        }
        updateURL(getClient(), getSecret());
        // Accelerate display.
        backfill();
    }

    getClient();
    getSecret();
    // Once data is backfilled, we wait for the update.
    backfill();

    </script>

    </body>
</html>