1

I have an app working in node.js that locates a user and plots their location on a leaflet map. When others connect it shows them too. It keeps their marker live as long as they move the mouse.

I would like to change a couple of things but not sure where to start:

  1. Update the users marker when they move.
  2. Keep the marker live as long as the connection is live, not as long as they are moving the mouse or moving in general as they may stop on the jorney.

I know I can use the watch feature but not sure where to add it.I will include the application.js code with is where the map locate and placing the marker code is.

The application.js

$(function() {
    // generate unique user id
    var userId = Math.random().toString(16).substring(2,15);
    var socket = io.connect('/');
    socket.emit('little_newbie', username);
    var map;


    var info = $('#infobox');
    var doc = $(document);

    // custom marker's icon styles
    var tinyIcon = L.Icon.extend({
        options: {
            shadowUrl: '../assets/marker-shadow.png',
            iconSize: [25, 39],
            iconAnchor:   [12, 36],
            shadowSize: [41, 41],
            shadowAnchor: [12, 38],
            popupAnchor: [0, -30]
        }
    });
    var redIcon = new tinyIcon({ iconUrl: '../assets/marker-red.png' });
    var yellowIcon = new tinyIcon({ iconUrl: '../assets/marker-yellow.png' });

    var sentData = {};
    var connects = {};
    var markers = {};
    var active = false;

    socket.on('load:coords', function(data) {
        if (!(data.id in connects)) {
            setMarker(data);
        }

        connects[data.id] = data;
        connects[data.id].updated = $.now();
    });

    // check whether browser supports geolocation api
    if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(positionSuccess, positionError, { enableHighAccuracy: true });
    } else {
        $('.map').text('Your browser is out of fashion, there\'s no geolocation!');
    }

    function positionSuccess(position) {
        var lat = position.coords.latitude;
        var lng = position.coords.longitude;
        var acr = position.coords.accuracy;


        // mark user's position
        var userMarker = L.marker([lat, lng], {
            icon: redIcon
        });
        // uncomment for static debug
        // userMarker = L.marker([51.45, 30.050], { icon: redIcon });

        // load leaflet map

    map = L.map('map').fitWorld();

    L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {
        maxZoom: 18,
        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>',
        id: 'mapbox.streets'
    }).addTo(map);

        userMarker.addTo(map);
        userMarker.bindPopup('<p>You are here ' + username + '</p>').openPopup();

        var emit = $.now();
        // send coords on when user is active
        doc.on('mousemove', function() {
            active = true;

            sentData = {
                id: userId,
                active: active,
                coords: [{
                    lat: lat,
                    lng: lng,
                    acr: acr,
                    name: username
                }]
            };

            if ($.now() - emit > 1) {
                socket.emit('send:coords', sentData);
                emit = $.now();
            }
        });
    }

    doc.bind('mouseup mouseleave', function() {
        active = false;
    });

    // showing markers for connections
    function setMarker(data) {
        for (var i = 0; i < data.coords.length; i++) {

            var num = +$("#count").text() + 1;
            $("#count").text(num);

            var marker = L.marker([data.coords[i].lat, data.coords[i].lng], { icon: yellowIcon }).addTo(map);
            marker.bindPopup('<p>One more external user is here!</p>' + data.coords[i].name);
            markers[data.id] = marker;
        }
    }

    // handle geolocation api errors
    function positionError(error) {
        var errors = {
            1: 'Authorization fails', // permission denied
            2: 'Can\'t detect your location', //position unavailable
            3: 'Connection timeout' // timeout
        };
        showError('Error:' + errors[error.code]);
    }

    function showError(msg) {
        info.addClass('error').text(msg);

        doc.click(function() {
            info.removeClass('error');
        });
    }

    // delete inactive users every 15 sec
    setInterval(function() {
        for (var ident in connects){
            if ($.now() - connects[ident].updated > 200000) {
            var num = +$("#count").text() - 1;
            $("#count").text(num);
                delete connects[ident];
                map.removeLayer(markers[ident]);
            }
        }
    }, 15000);
});
5
  • Cool question! If I interpret Geolocation.watchPosition() correctly, you can bind it to each of your users and it will call the success function whenever your user moves. You would use a similar function there as what is now your onMousemove function. Nov 20, 2018 at 7:42
  • I assume the clients connect via a browser on a portable device? I think that an operating system such as Android can hibernate apps that are sent to the background. So when your user leaves the browser the connection might be disconnected. Nov 20, 2018 at 7:48
  • Hi Stefan, yes via a browser at the moment and what you mention at the beginning is what I am trying to achieve but keep hitting brick walls! any help would be great Nov 20, 2018 at 10:12
  • You have to post code that you tried to implement and that doesn't work. If there are error messages, include those too. Nov 20, 2018 at 12:06
  • I have changed it that many times, the closest I got to it working was adding a new marker when user moved but it kept the old one and stopped after about 3 new markers. So what I need for now is just to keep them live whilst they are connected and remove marker when they have disconnected. How do I do this? Do I have to create arrays of connected users or is there an easier way?? Nov 20, 2018 at 18:32

1 Answer 1

0

Fully working, rewritten. Just adding it to show how it should of been done from the start and to help anyone else that wants to do something similar

server.js

var http = require('http');
var Static = require('node-static');
var app = http.createServer(handler);
var io = require('socket.io').listen(app);
var port = 8080;
var active = false;
var update = false;
var users = [];

var files = new Static.Server('./public');

function handler(request, response) {
    request.on('end', function() {
        files.serve(request, response);
    }).resume();
}

io.on('connection', (socket) => {
    var addedUser = false;
    socket.on('add user', (username) => {
        if (addedUser) return;
        socket.username = username;
        var new_count = users.length;
        console.log(new_count);
        var new_user = {
            username: username,
            active: active,
            lat: null,
            lng: null,
            update: false
        };

        users.push(new_user);
        console.log(users);
    });

    socket.on('new_coords', (data) => {
        var New_Details = {
            username: data.username,
            active: data.active,
            lat: data.new_lat,
            lng: data.new_lng,
            update: true
        };

        var checkuser = data.username;
        result = users.map(obj => obj.username).indexOf(checkuser) >= 0;

        if (result === true) {
            objIndex = users.findIndex((obj => obj.username == data.username));
            users[objIndex].lat = data.new_lat;
            users[objIndex].lng = data.new_lng;
            users[objIndex].active = data.active;
            users[objIndex].update = true;

            var to_send = {
                username: data.username,
                active: true,
                lat: data.new_lat,
                lng: data.new_lng,
                update: true
            };

            console.log(data.username + ' has just updated their location');
            var new_count = users.length;
            console.log(new_count);
            console.log(users);

            socket.broadcast.emit('updatecoords', to_send);

            objIndex = users.findIndex((obj => obj.username == data.username));
            users[objIndex].update = false;
        }
    });
         socket.on('load_init', (data) => {
        socket.emit('load_init', users);
    })

    socket.on('disconnect', () => {

        for (var i = 0; i < users.length; i++)
            if (users[i].username === socket.username) {
                socket.broadcast.emit('remove_marker', {
                    username: users[i].username
                });
                users.splice(i, 1);
                break;
            }
        var new_count = users.length;
        console.log(new_count);
        console.log('remove marker');
    });
});

app.listen(port);
console.log('Your server goes on localhost:' + port); 

and viewer.html

<!DOCTYPE html>
<html>
<head>
    <title>Multiple user tracker</title>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">
    <link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" />
    <script src="https://unpkg.com/[email protected]/dist/leaflet.js"></script>
    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
    <script src="/socket.io/socket.io.js"></script>
    <script>
        var username = prompt('What\'s your username?');
        var tinyIcon = L.Icon.extend({
            options: {
                shadowUrl: '../assets/marker-shadow.png',
                iconSize: [25, 39],
                iconAnchor: [12, 36],
                shadowSize: [41, 41],
                shadowAnchor: [12, 38],
                popupAnchor: [0, -30]
            }
        });

        var redIcon = new tinyIcon({
            iconUrl: '../assets/marker-red.png'
        });
        var yellowIcon = new tinyIcon({
            iconUrl: '../assets/marker-yellow.png'
        });
        socket = io.connect('/');
        socket.emit('add user', username);
    </script>
    <style>
        body {
            padding: 0;
            margin: 0;
        }

        html,
        body,
        #map {
            height: 100vh;
            width: 100vw;
        }
    </style>
</head>
<body>
    <div id="map"></div>

      <script>
        function initializeMapAndLocator() {       
            var markers = {};
            var mymarker;
            var map = L.map('map');
            googleStreets = L.tileLayer('https://{s}.google.com/vt/lyrs=m&x={x}&y={y}&z={z}', {
                maxZoom: 18,
                subdomains: ['mt0', 'mt1', 'mt2', 'mt3']
            }).addTo(map);

            map.locate({
                setView: true,
                maxZoom: 20
            });

            function onLocationFound(e) {
                var radius = e.accuracy / 2;
                map.locate({
                    setView: false,
                    timeout: 60000,
                    enableHighAccuracy: true
                });

                var newlatlng = e.latlng;
                const new_lat = newlatlng.lat;
                const new_lng = newlatlng.lng;

                var Details = {
                    username: username,
                    active: true,
                    new_lat: new_lat,
                    new_lng: new_lng,
                    update: true
                };

                socket.emit('new_coords', Details);
                if (map.hasLayer(mymarker)) {
                    map.removeLayer(mymarker);
                }

                mymarker = new L.Marker(e.latlng, {
                    icon: yellowIcon,
                    draggable: true
                });

                map.addLayer(mymarker);
                mymarker.bindPopup('<p>You are here ' + username + '</p>').openPopup();
            }

            map.on('locationfound', onLocationFound);

            socket.on('load_init', function(data) {
                for (var i = 0; i < data.length; i++) {
                    if (data[i].lat != null) {
                        markers[data[i].username] = new L.marker([data[i].lat, data[i].lng], {
                            draggable: true,
                            icon: redIcon
                        });
                        map.addLayer(markers[data[i].username]);
                        markers[data[i].username].bindPopup('Online :' + data[i].username);
                    }
                }
            });

            socket.on('updatecoords', function(data) {

                if (map.hasLayer(markers[data.username])) {
                    map.removeLayer(markers[data.username]);
                }

                markers[data.username] = new L.marker([data.lat, data.lng], {
                    draggable: true,
                    icon: redIcon
                });
                map.addLayer(markers[data.username]);
                markers[data.username].bindPopup(data.username + ' is on the move').openPopup();
            });

            socket.on('remove_marker', function(data) {
                map.removeLayer(markers[data.username]);
            });

        }
        socket.emit('load_init');

        initializeMapAndLocator();
    </script>
</body>
</html>
1
  • 1
    Could you explain your approach in plain text? If people have a similar problem they would have to read the whole code to possibly find out that it's not what they are searching for. Jan 12, 2019 at 11:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.