Javascript leaflet map to toggle "Show Your Location" - javascript

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.
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 © OpenStreetMap contributors, Imagery © Mapbox',
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 © OpenStreetMap contributors, Imagery © Mapbox',
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

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/

With much thanks to Falke Design I have used the suggestions in the answer above. My code now looks like this for the button I wanted:
var panbtn = L.easyButton({
states: [{
stateName: 'pauseAutoMove',
icon: 'fa-sign-in fa-lg',
title: 'Centre display at current Player', //Tooltip
onClick: function(btn, map) {
console.log("AutoMoveButton pressed");
panbtn.state('AutoMove');
mymap.panTo([latitude,longitude]);
}
}, {
stateName: 'AutoMove',
icon: 'fa-crosshairs fa-lg',
}]
}).addTo(mymap);
mymap.on("zoomstart", function (e) { currentAutoMove = true }); //Set flag, that currently map is moved by a zoom command
mymap.on("zoomend", function (e) { currentAutoMove = false }); //Remove flag again
mymap.on('movestart',(e)=>{ //Check if map is being moved
if(!currentAutoMove){ //ignore if it was a natural PlayerLoc Auto update
pauseAutoMove = true; //set flag to stop Auto moving map
console.log("Map moved");
panbtn.state('pauseAutoMove'); //change button style to remove crosshairs
}
});
and inside the updatemap function the code:
if(!pauseAutoMove){ //pan the map to follow the player unless it is on pause
currentAutoMove = true; //Set flag, that currently map is moved by a normal PlayerLoc Auto update
mymap.panTo([latitude,longitude]);
currentAutoMove = false; //Remove flag again
};
much thanks.

Related

How to add a click event to any of my markers which i have stored in an array

I'm working with leaflet js and I have an array in which i store my markers which are automatically added to the map after the location of the user has been obtained.
problem is that i want to add an onclick listener so that any marker that is clicked will run a function.
please help me out cuz i'm stuck right now.
//objects for markers
var allMarkers=[];
var AllMarkers = L.layerGroup(allMarkers);
var Allpoints=[{
"latitudes":9.4258946,"longitudes":-0.8842213, "names":"first", "eastings":556568, "northings":446445, "description": "aijaskj jnrkajra skjanjanek ", "elevations": 5668
},
{
"latitudes":9.4254946,"longitudes":-0.8812213, "names":"second"},
{
"latitudes":9.4054946,"longitudes":-0.8802213, "names":"third"},
{
"latitudes":9.4754946,"longitudes":-0.8712213, "names":"fourth"},
];
//automatically plot all previous markers
var point = L.point(9.2754946, -0.8912213);
q = 0;
while(q< Allpoints.length){
//create Marker here
_newMarker = L.marker(
[Allpoints[q].latitudes, Allpoints[q].longitudes],
{title: Allpoints[q].names,
riseOnHover: true,
} ).addTo(mymap);
allMarkers.push(_newMarker);
q++
}
//function to send back the details of the clicked marker to a paragraph in my index.htm
//PROBLEM
L.marker('click', markers, showMarkerDetails) //however the code immediately above does not work
function showMarkerDetails(){
$("#returnControlName").html(controlName);
$("#returnControlLocation").html(`${controlLat.toFixed(4)} , ${controlLong.toFixed(4)} `);
$("#returnControlEastings").html(controlEastings);
$("#returnControlName").html(controlNorthings);
$("#returnControlName").html(controlElevation);
$("#returnControlName").html(controlDescription);
}
With L.marker() you add new markers / points to the map, you can't add events like that to them. (L.marker('click', markers, showMarkerDetails) )
Change your code to:
//automatically plot all previous markers
var point = L.point(9.2754946, -0.8912213);
q = 0;
while(q< Allpoints.length){
//create Marker here
_newMarker = L.marker([Allpoints[q].latitudes, Allpoints[q].longitudes],
{title: Allpoints[q].names,
riseOnHover: true,
}).addTo(mymap);
_newMarker.informations = Allpoints[q]; // save the data to the marker to read it later out
_newMarker.on('click',showMarkerDetails); // add click event to the markers
allMarkers.push(_newMarker);
q++
}
function showMarkerDetails(e){
var layer = e.target // get the clicked marker
var infos = layer.informations;
console.log(infos.description);
$("#returnControlName").html(controlName);
$("#returnControlLocation").html(`${controlLat.toFixed(4)} , ${controlLong.toFixed(4)} `);
$("#returnControlEastings").html(controlEastings);
$("#returnControlName").html(controlNorthings);
$("#returnControlName").html(controlElevation);
$("#returnControlName").html(controlDescription);
}

Leaflet popups for specific base maps

so I'm making a website using leaflet with dozens of base maps. I want to incorporate information about each map that is only visible if the user wants it. To do this, I would like to make an overlay map with popups, but I want the popups to change depending on the base map selected by the user.
How would I go about doing this?
Thank You So Much
You need to either use a plugin that keeps track of the base maps for you (like active layers) or you need to do it yourself.
If you are using the Leaflet layers control, you can subscribe to the basemapchange event to do this easily.
You need two things: active base layer management (easy) and dynamic popups (not too hard)
To wit:
First, here is the event handler to track active base layer when it changes.
map.on("baselayerchange",
function(e) {
// e.name has the layer name
// e.layer has the layer reference
map.activeBaseLayer = e.layer;
console.log("base map changed to " + e.name);
});
Because using L.marker().bindPopup() creates the popup content right there and does not support callbacks, you must manually create the popups in response to click event by calling map.openPopup() with your dynamic html (dynamic because it uses a variable: the active basemap name)
marker.on("click", function(e) {
var html = "Current base layer: <br/><b>" + map.activeBaseLayer.options.name + "<b>";
map.openPopup(html,
e.latlng, {
offset: L.point(1, -24)
});
});
Here is a working example on JS fiddle: http://jsfiddle.net/4caaznsc/
Working code snippet also below (relies on Leaflet CDN):
// Create the map
var map = L.map('map').setView([39.5, -0.5], 5);
// Set up the OSM layer
var baseLayer1 = L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
name: "Base layer 1"
});
var baseLayer2 = L.tileLayer(
'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
name: "Base layer 2"
});
// add some markers
function createMarker(lat, lng) {
var marker = L.marker([lat, lng]);
marker.on("click", function(e) {
var html = "Current base layer: <br/><b>" + map.activeBaseLayer.options.name + "<b>";
map.openPopup(html,
e.latlng, {
offset: L.point(1, -24)
});
});
return marker;
}
var markers = [createMarker(36.9, -2.45), createMarker(36.9, -2.659), createMarker(36.83711, -2.464459)];
// create group to hold markers, it will be added as an overlay
var overlay = L.featureGroup(markers);
// show overlay by default
overlay.addTo(map);
// show features
map.fitBounds(overlay.getBounds(), {
maxZoom: 11
});
// make up our own property for activeBaseLayer, we will keep track of this when it changes
map.activeBaseLayer = baseLayer1;
baseLayer1.addTo(map);
// create basemaps and overlays collections for the layers control
var baseMaps = {};
baseMaps[baseLayer1.options.name] = baseLayer1;
baseMaps[baseLayer2.options.name] = baseLayer2;
var overlays = {
"Overlay": overlay
};
// create layers control
var layersControl = L.control.layers(baseMaps, overlays).addTo(map);
// update active base layer when changed
map.on("baselayerchange",
function(e) {
// e.name has the name, but it may be handy to have layer reference
map.activeBaseLayer = e.layer;
map.closePopup(); // any open popups will no longer be correct; take easy way out and hide 'em
});
#map {
height: 400px;
}
<script src="https://npmcdn.com/leaflet#0.7.7/dist/leaflet.js"></script>
<link href="https://npmcdn.com/leaflet#0.7.7/dist/leaflet.css" rel="stylesheet"/>
<div id="map"></div>

Hide markers from google maps when click an external link and only show its corresponding marker

How do i change my center for google maps with a link and remove other markers? i have this code
https://jsfiddle.net/m9ugbc7h/
So, i need to create a link for example
Ventura
In this case the function must change google maps center to focus the "ventura" marker and hide the other markes and when the user clicks on
Dolphinaris
the zoom will change and will hide every other marker and show only the ones of Dolphinaris
Thanks in advance
Make map visible outside of jQuery(document).ready.
Create var markers = []; array.
When crating markers, add custom property name to it, and push marker into markers array:
var marker = new google.maps.Marker({
position: new google.maps.LatLng(21.0241839, -86.8148164),
map: map,
visible: true,
icon: ventura,
name: 'ventura',
});
markers.push(marker);
On click, invoke resetMap() function:
Ventura
Inside resetMap function, set center and zoom to map, iterate markers, matching them by custom property name - matched one set visible, others set to invisible.
function resetMap(lat, lon, zoom, name) {
var newPos = new google.maps.LatLng(lat, lon);
map.setCenter(newPos);
map.setZoom(zoom);
markers.forEach(function(marker) {
if(marker.get('name') == name)
{
console.log('match');
marker.setVisible(true);
}
else
{
marker.setVisible(false);
}
});
Working fiddle: https://jsfiddle.net/m9ugbc7h/1/
EDIT:
Question: "is there any way to change smoothly the zoom and coordinates?"
Yes, use method:
panTo(latLng:LatLng|LatLngLiteral)
Changes the center of the map to the given LatLng. If the change is
less than both the width and height of the map, the transition will be
smoothly animated.
https://developers.google.com/maps/documentation/javascript/reference?csw=1
EDIT 2:
Implementing panTo is easy:
map.panTo(newPos);
instead of:
map.centerTo(newPos);
but as I have faced a bit 'flickering' effect due to hide/show markers that are close on the map, I have added some delay in functions invocation + markers show/hide:
function resetMap(lat, lon, zoom, name) {
var newPos = new google.maps.LatLng(lat, lon);
$.when( map.setZoom(zoom) ).done(function(){
$.when( map.panTo(newPos)).done(function(){
setMarkerVisibility(name);
});
});
}
And showing matched marker is now executed with 300 ms delay:
function setMarkerVisibility(name){
markers.forEach(function(marker) {
console.log(marker.get('name'));
if(marker.get('name') == name)
{
setTimeout(function(){ marker.setVisible(true); }, 300);
}
else
{
marker.setVisible(false);
}
});
}
It looks a bit smoother like this.
Working fiddle: https://jsfiddle.net/m9ugbc7h/3/

Update marker position json Leaflet and WebGLEarth

This question is related to this question: Updating Leaflet Marker Position Every x Seconds with JSON. Only I want to project the map created with Leaflet onto a 3D globe with WebGLEarth. Normally you can combine Leaflet and WebGLEarth by replacing L.map, L.marker and so on with WE.map, WE.marker in your original Leaflet code.
I would like to have the current location of the International Space Station projected on my 3D globe, so I replaced the L.marker in the update_position function of this code...
var cloudmadeUrl = 'http://{s}.mqcdn.com/tiles/1.0.0/sat/{z}/{x}/{y}.jpg';
var subDomains = ['otile1','otile2','otile3','otile4'];
var cloudmade = new L.TileLayer(cloudmadeUrl, {maxZoom: 11, subdomains: subDomains});
var map = new L.Map('map', {layers : [cloudmade]}).fitWorld();
var iss;
function update_position() {
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
console.log(data);
var latitude = data["iss_position"]["latitude"];
var longitude = data["iss_position"]["longitude"];
if (!iss) {
iss = L.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
}
iss.setLatLng([latitude,longitude]).update();
setTimeout(update_position, 1000);
});
}
update_position();
DEMO
...by WE.marker. Unfortunately, the updating of the position doesn't work anymore on my 3D globe, whereas it did work on the 2D map.
I tried adding
setInterval(update_position,2000);
just above update_position();, then the marker gets updated a few times (~5) and then suddenly stops. If I interact with my mouse on the globe by panning, the marker gets updated to its current position and gets updated a few times afterwards, and eventually stops again.
Main Question:
Does anyone know how to fix this so that I have a continuous marker update?
Additional Question:
Ideally I would like the International Space Station track to look like https://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=CZML.html&label=Showcases, with a colored path the station travelled in its last orbit around the earth. Any tips on how to do this?
Thanks in advance!
[Edit 23-1-2015:
I managed to update it continuously by making a new variable for iis, namely iis2. Why this works is not clear to my. Unfortunately, all 'old' markerpositions don't dissapear, so I get a blurred marker track.
var iss;
var iis2;
function update_position() {
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
console.log(data);
var latitude = data["iss_position"]["latitude"];
var longitude = data["iss_position"]["longitude"];
if (!iss) {
iss2 = WE.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
}
iss2.setLatLng([latitude,longitude]).update();
});
}
update_position();
setInterval(update_position,1000);
]
remove the marker
var iss;
var iis2;
function update_position() {
$.getJSON('http://open-notify-api.herokuapp.com/iss-now.json?callback=?', function(data) {
console.log(data);
map.removeLayer(iss2);
var latitude = data["iss_position"]["latitude"];
var longitude = data["iss_position"]["longitude"];
if (!iss) {
iss2 = WE.marker([latitude,longitude]).bindPopup("I am the ISS").addTo(map);
}
iss2.setLatLng([latitude,longitude]).update();
});
}
update_position();
setInterval(update_position,1000);

Leaflet - draggable marker and coordinates display in a field form

I have to do a draggable marker and its coordinates should be displayed in fields. It will be a part of a contact form in PHP. I created a draggable marker, help me what to do now.
var marker = L.marker(new L.LatLng(53.471, 18.744), {
draggable: true
}).addTo(map);
http://jsfiddle.net/xTh5U/
Here is example in Google Maps API, I need the same in Leaflet.
You should use the dragend event of L.Marker, so you known dragging has ended, then get the coordinates of the marker by using the getLatLng method of L.Marker. When you've fetched those you can assign them to the values of your text inputs.
marker.on('dragend', function (e) {
document.getElementById('latitude').value = marker.getLatLng().lat;
document.getElementById('longitude').value = marker.getLatLng().lng;
});
Working example on Plunker: http://plnkr.co/edit/iyMhaoAyllr2uNSOHhS9?p=preview
Came across this while looking for a similar solution. Forked the marked answer and took it a little further:
Both drag and click; and map centers on the marker.
Works in reverse as well (user-entered values in form fields can move
the marker).
Remembers the previous location marked by the user.
operative code:
marker.on('dragend', function (e) {
updateLatLng(marker.getLatLng().lat, marker.getLatLng().lng);
});
map.on('click', function (e) {
marker.setLatLng(e.latlng);
updateLatLng(marker.getLatLng().lat, marker.getLatLng().lng);
});
function updateLatLng(lat,lng,reverse) {
if(reverse) {
marker.setLatLng([lat,lng]);
map.panTo([lat,lng]);
} else {
document.getElementById('latitude').value = marker.getLatLng().lat;
document.getElementById('longitude').value = marker.getLatLng().lng;
map.panTo([lat,lng]);
}
}
See working example: http://plnkr.co/edit/PTFlun?p=preview

Categories

Resources