Search code examples
javascriptgoogle-mapsleaflet

Javascript leaflet map to toggle "Show Your Location"


I have a leaflet map that displays the area around where the person currently is (currentlocation), and typically I want the map to 'follow' the person as they travel. I am using a mymap.panTo command for this. This much is working fine. It updates the map every 5 seconds and pans to centre on the current person perfectly.

Occasionally though the person may want to scroll the map further afield to see something. This works ... until the 5 second counter kicks in and pans the map back to their location again. Annoying.

I have seen on various map apps a button/toggle on the map that the person can click on to stop the map following them. In fact usually it turns off if the map is shifted manually and then they'd click the toggle to pan back to their current location. Please see the image attached of a google map highlighting what they call a "Show Your Location" button. That's what I want.

snip of a Google Maps screen highlighting the "Show Your Location" button

But how is this done? Is this some sort of custom leaflet control that I cannot find? Or is it done fully programmatically somehow (and any sample code snippets?).

any help appreciated.

Below is the bit of code I use to display my map.

var streetmap = 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/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
      id: 'mapbox/streets-v11',
      accessToken: 'token code here' //redacted token
   }),
   satellite = 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/copyright">OpenStreetMap</a> contributors, Imagery © <a href="https://www.mapbox.com/">Mapbox</a>',
      id: 'mapbox/satellite-v9',
      accessToken: 'token code here' //redacted token   });
var baseMaps = {
     "Streetmap": streetmap,
     "Satellite": satellite
};
    
var mymap = L.map('mapid', { 
   center: [latitude,longitude],
   zoom: 17,
   layers: [streetmap] //default layer
}); 

var personicon = L.icon({
    iconUrl: '/js/personicon.png',
    iconSize: [20, 20]
    });
var playerLoc = new L.marker([latitude,longitude], {icon: personicon}) // set player location marker as a declared variable but don't put it on the map yet

elsewhere I have code to start the map (once variables are populated) and then keep updating location

   const interval = setInterval(function() {
         if (is_running) { // we need to know that there is data populated before showing or updating the map with it
         if (!map_started) {  //start the map only once 
        L.control.layers(baseMaps).addTo(mymap); //show choice of layer views
            L.control.scale().addTo(mymap); //show scale bar
        console.log("Create current player marker:",MYID,latitude,longitude); 
            playerLoc.setLatLng([latitude,longitude]).addTo(mymap).bindPopup(MYID); //update current player marker, and now show it on the map
                map_started=true;
             }; //start the map only once
         updatemap(); // for current player location and circle colour.
      }; //update only if is_running
          mymap.invalidateSize(); //reset map view
    }, 5000); // update map every 5 seconds 
function updatemap() {  // Update the current player location on map
    playerLoc.setLatLng([latitude,longitude]); //update current player marker instead of creating new ones
    mymap.panTo([latitude,longitude]); // pan the map to follow the player (TODO: Can we toggle pan mode?)
}; // end updatemap

this code all works fine. the mymap.panTo([latitude,longitude]); line is what needs to be wrapped in a condition "If pan is allowed, then do mymap.panTo([latitude,longitude]);" But surely there's a standard leaflet control or approach to this? I see this thing all the time elsewhere


Solution

  • You need to listen on the movestart event, to detect if the user moves the map. But this event is also triggered from the panTo function, so you need a flag to indicate if movestart is fired by interval or by the user.

    
    var currentAutoMove = false; // needed to check in `movestart` event-listener if moved from interval or by user
    var pauseAutoMove = false; // if true -> Stops moving map
    
    var latitude,longitude;
    setInterval(()=>{
        latitude = playerLoc.getLatLng().lat + 0.001;
        longitude = playerLoc.getLatLng().lng + 0.001;
        updatemap();
    }, 1000)
    
    
    function updatemap() {  // Update the current player location on map
        playerLoc.setLatLng([latitude,longitude]);
        
        if(!pauseAutoMove){
           currentAutoMove = true; // Set flag, that currently map is moved by interval
           map.panTo([latitude,longitude]);
           currentAutoMove = false; // Remove flag, that currently map is moved by interval
       }
    }
    
    
    map.on('movestart',(e)=>{
        console.log(e, currentAutoMove);
      if(!currentAutoMove){ // Check if map is moved by interval or by user
        pauseAutoMove = true; // set flag, to stop moving map
      }
    })
    
    // Start auto move again, if button clicked
    L.DomEvent.on(document.getElementById('toPos'),'click',(e)=>{
        pauseAutoMove = false; // reset flag, to stop moving map -> start moving map
      map.panTo([latitude,longitude]);
    })
    

    To create a Control / button to start auto move you only need to search in Google there are many examples, else you can use L.easybutton.

    Demo: https://jsfiddle.net/falkedesign/8akw3ust/