Snippets

PowUnity powunity-map-sample

Created by Maximilian Loy
{
  "name": "powunity-map-sample",
  "authors": [
    "Maximilian Loy <maximilian.loy@gmail.com>"
  ],
  "description": "",
  "main": "",
  "license": "MIT",
  "homepage": "",
  "private": true,
  "ignore": [
    "**/.*",
    "node_modules",
    "bower_components",
    "test",
    "tests"
  ],
  "dependencies": {
    "reconnectingWebsocket": "^1.0.0",
    "leaflet.locatecontrol": "^0.62.0",
    "moment": "^2.18.1"
  }
}
<!DOCTYPE html>
<html>

<head>
    <meta charset='utf-8' />
    <title>PowUnity GPS</title>
    <meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no" />
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.5.1/dist/leaflet.css" />
    <link href="bower_components/L.Control.Credits/dist/leaflet-control-credits.css" rel="stylesheet">
    <link href="leaflet-text-icon.css" rel="stylesheet" />
    <script src="https://unpkg.com/leaflet@1.5.1/dist/leaflet.js"></script>
    <script src="bower_components/moment/min/moment-with-locales.min.js"></script>
    <script src="bower_components/L.Control.Credits/dist/leaflet-control-credits.js"></script>
    <script src="leaflet-text-icon.js"></script>
    <style>
        body {
            margin: 0;
            padding: 0;
        }

        #map {
            position: absolute;
            top: 0;
            bottom: 0;
            width: 100%;
        }

        .leaflet-left .leaflet-control {
            border: 2px solid rgba(0, 0, 0, 0.2);
        }

        .leaflet-credits-showlink {
            text-decoration: underline !important;
        }

        .leaflet-status-control {
            background-color: white;
            opacity: 0.90;
            border-radius: 5px;
            border: 4px solid rgba(0, 0, 0, 0.1);
            padding: 12px;
            font-size: 20px;
        }

        .status-icon {
            height: 16px;
            width: 16px;
            padding: 5px;
        }

        .leaflet-div-icon {
            background: transparent;
            border: none;
        }

        .leaflet-marker-icon .number {
            position: relative;
            top: -37px;
            font-size: 12px;
            width: 25px;
            text-align: center;
        }
    </style>
</head>

<body>
    <div id="map"></div>
    <script>
        // https://stackoverflow.com/questions/31221088/how-to-calculate-the-distance-of-a-polyline-in-leaflet-like-geojson-io
        L.Polyline = L.Polyline.include({
            getDistance: function (system) {
                // distance in meters
                var mDistanse = 0,
                    length = this._latlngs.length;
                for (var i = 1; i < length; i++) {
                    mDistanse += this._latlngs[i].distanceTo(this._latlngs[i - 1]);
                }
                // optional
                if (system === 'imperial') {
                    return mDistanse / 1609.34;
                } else {
                    return mDistanse / 1000;
                }
            }
        });
        L.controlStatus = function (options) {
            return new L.StatusControl(options);
        }

        L.StatusControl = L.Control.extend({
            initialize: function (options) {
                L.setOptions(this, options);
            },
            onAdd: function (map) {
                this._map = map;
                
                var container = L.DomUtil.create('div', 'leaflet-status-control', container);
                if (this.options.width) container.style.paddingRight = this.options.width + 'px';
                if (this.options.height) container.style.height = this.options.height + 'px';
                
                var speedContainer = L.DomUtil.create('div', '', container);
                L.DomUtil.create('span', 'fas fa-tachometer-alt status-icon', speedContainer);
                var distanceContainer = L.DomUtil.create('div', '', container);
                L.DomUtil.create('span', 'fas fa-ruler-horizontal status-icon', distanceContainer);
                var timeContainer = L.DomUtil.create('div', '', container);
                L.DomUtil.create('span', 'fas fa-clock status-icon', timeContainer);
                
                this._speed = L.DomUtil.create('span', '', speedContainer);
                this._speed.innerHTML = '0 km/h';
                this._distance = L.DomUtil.create('span', '', distanceContainer);
                this._distance.innerHTML = '0 km';
                this._time = L.DomUtil.create('span', '', timeContainer);
                this._time.innerHTML = '';

                return container;
            },
            setDistance: function (distance) {
                this._distance.innerHTML = parseInt(distance, 0) + ' km';
            },
            setSpeed: function (speed) {
                this._speed.innerHTML = Math.round(speed) + ' km/h';
            },
            setTime: function (time) {
                this._time.innerHTML = moment(time).format('lll');
            }
        });
        var traccarUrl = 'https://traccar.powunity.com/api/';
        var mapboxToken = '<TODO-add-me>';
        var TRACCAR_WS_URL = 'wss://traccar.powunity.com/api/socket';
        var traccarAccount = 'email=<TODO-add-me>&password=<TODO-add-me>';
        var userPassword = '<TODO-add-me>:<TODO-add-me>';
        var mapboxTileLayer =
            'https://api.mapbox.com/styles/v1/mapbox/light-v9/tiles/256/{z}/{x}/{y}?access_token={accessToken}'
        var pinBaseUrl = 'https://api.mapbox.com/v4/marker/pin-l-';
        var deviceIdFilter = null;

        // var defaultCenter = [47.011789, 10.288365];
        var defaultCenter = null;
        var markers = {};
        var polylines = {};

        function createSession(cb) {
            var xhr = new XMLHttpRequest();
            xhr.open('POST', traccarUrl + 'session', true);
            xhr.withCredentials = true;
            xhr.setRequestHeader('Authorization', 'Basic ' + btoa(userPassword));
            xhr.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
            xhr.setRequestHeader('Disable-Payload-Encoding', 'true');
            xhr.onreadystatechange = () => processResponse(xhr, cb);
            xhr.send(traccarAccount);
        }

        function initWebsocket() {
            console.log('initWebsocket')
            var socket = new WebSocket(TRACCAR_WS_URL + '?token=1234');
            socket.onmessage = (event) => {
                var data = JSON.parse(event.data);
                if (data.positions) {
                    data.positions
                        .filter(position => !trackerFilter || (trackerFilter && position.deviceId === deviceIdFilter))
                        .forEach(position => updatePosition(position));
                }
            };
            socket.onerror = (err) => console.error('Failed connecting to websocket: ' + JSON.stringify(err));
        }

        function updatePosition(position) {
            var latLng = new L.LatLng(position.latitude, position.longitude);
            if (!markers[position.deviceId]) {
                // markers[position.deviceId] = L.marker(latLng).addTo(map);
                // var icon = new L.TextIcon({
                //     text: '12',
                //     color: 'red'
                // });
                // markers[position.deviceId] = L.marker(latLng, {
                //     icon: icon
                // }).addTo(map);
                // map.panTo(latLng);
            } else {
                markers[position.deviceId].setLatLng(latLng);
            }
            if (polylines[position.deviceId]) {
                polylines[position.deviceId].addLatLng(latLng);
                statusCtrl.setDistance(polylines[position.deviceId].getDistance());
            }
            map.panTo(latLng);
            statusCtrl.setSpeed(position.speed * 1.852);
            statusCtrl.setTime(position.deviceTime);
        }

        function processResponse(xhr, cb) {
            if (xhr.readyState === 4 && xhr.status === 200) {
                var response = JSON.parse(xhr.responseText);
                cb(response);
            } else if (xhr.status !== 200) {
                // console.log('xhr.readyState', xhr.readyState);
                // console.log('xhr.status', xhr.status);
                console.error(xhr);
            }
        }

        function getDevices(cb) {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', traccarUrl + 'devices', true);
            xhr.setRequestHeader('Authorization', 'Basic ' + btoa(userPassword));
            xhr.onreadystatechange = () => processResponse(xhr, cb);
            xhr.send();
        }

        function getPositionById(positionId, cb) {
            var xhr = new XMLHttpRequest();
            var url = traccarUrl + 'positions?positionId=' + positionId;
            xhr.open('GET', url, true);
            xhr.setRequestHeader('Authorization', 'Basic ' + btoa(userPassword));
            xhr.setRequestHeader('Accept', 'application/json');
            xhr.onreadystatechange = () => processResponse(xhr, cb);
            xhr.send();
        }

        function getPositions(deviceId, from, to, cb) {
            var xhr = new XMLHttpRequest();
            var url = traccarUrl + 'positions?deviceId=' + deviceId + '&from=' + from + '&to=' + to;
            xhr.open('GET', url, true);
            xhr.setRequestHeader('Authorization', 'Basic ' + btoa(userPassword));
            xhr.setRequestHeader('Accept', 'application/json');
            xhr.onreadystatechange = () => processResponse(xhr, cb);
            xhr.send();
        }
        
        function getPosition(deviceId, positionId, cb) {
            var xhr = new XMLHttpRequest();
            var url = traccarUrl + 'positions?deviceId=' + deviceId + '&id=' + positionId;
            xhr.open('GET', url, true);
            xhr.setRequestHeader('Authorization', 'Basic ' + btoa(userPassword));
            xhr.setRequestHeader('Accept', 'application/json');
            xhr.onreadystatechange = () => processResponse(xhr, cb);
            xhr.send();
        }

        var urlParams = new URLSearchParams(window.location.search);
        var trackerFilter = urlParams.get('tracker');
        var zoom = urlParams.get('zoom') || 14;
        var center = (urlParams.get('center') && urlParams.get('center').split(',')) || defaultCenter;
        var map = L.map('map', {
            center: center,
            zoom: zoom,
            zoomControl: false
        });
        L.tileLayer(mapboxTileLayer, {
            accessToken: mapboxToken
        }).addTo(map);
        // L.control.locate().addTo(map);

        L.controlCredits({
            image: 'https://storage.googleapis.com/powunity-pictures/powunity-white_82x58.png',
            link: 'https://powunity.com/?utm_source=Live-Karte&utm_medium=Banner&utm_campaign=Rikscha',
            text: 'GPS Tracking<br/>powered by<br/>PowUnity GmbH',
            width: 82,
            height: 60
        }).addTo(map);

        var statusCtrl = L.controlStatus({
            position: 'bottomright'
            // width: 82,
            // height: 60
        }).addTo(map);

        getDevices((devices) => {
            devices.forEach((tracker) => {
                if (trackerFilter && tracker.uniqueId && tracker.uniqueId !== trackerFilter) {
                    console.log('skipping ' + tracker.uniqueId + ' due to tracker filter', trackerFilter);
                } else {
                    if (trackerFilter) {
                        deviceIdFilter = tracker.id;
                    }
                    getPosition(
                        tracker.id,
                        tracker.positionId,
                        (position) => {
                            var latLng = new L.LatLng(position[0].latitude, position[0].longitude);
                            var icon = new L.TextIcon({
                                text: tracker.name.slice(0, 1),
                                color: 'blue'
                            });
                            markers[tracker.id] = L.marker(latLng, {
                                icon: icon,
                                zIndexOffset: 100
                            }).addTo(map);
                            updatePosition(position[0]);
                            if (!center) {
                                center = latLng;
                                map.setView(center);
                            }
                        });
                    
                    getPositions(
                        tracker.id,
                        moment().startOf('day').toISOString(),
                        moment().toISOString(),
                        (positions) => {
                            polylines[tracker.id] = L.polyline(positions.map(position => new L.LatLng(position.latitude, position.longitude)), {
                                color: '#860000'
                            }).addTo(map);
                        });
                }
            });

            setTimeout(() => {
                createSession((result) => {
                    console.log('createSession', result)
                    initWebsocket();
                });
            }, 1000)
        });
    </script>
</body>

</html>

Comments (0)

HTTPS SSH

You can clone a snippet to your computer for local editing. Learn more.