Polymer 1.0 / leaflet-map 1.0 Map not consistently returning to given point on button click - leaflet

I am trying to implement a map using Polymer 1.0 and leaflet-map 1.0, which has a button that when clicked should pan the map to center on the current geolocation.
The map ('mymap') and 'geoButton' are elements on the same level in main index page. I have something that sort of works, but inconsistently.
The map is set up as so (for now the initial center is hard-coded):
<leaflet-map id="mymap" latitude="40.707149800" longitude="-74.002101900" zoom="14">
<leaflet-geolocation id="locator" enable-high-accuracy latitude="{{latitude}}" longitude="{{longitude}}" watch="true">
</leaflet-geolocation>
...
In the geoButton element, I assign the locator lat and long to the map lat and long when the button is clicked:
mymap.latitude = locator.latitude;
mymap.longitude = locator.longitude;
In leaflet-core.html (which has the properties and events carried over from Leaflet.js) both the latitude and longitude properties have an observer method '_viewChanged':
_viewChanged: function(newValue, oldValue) {
if (this.map) {
this.async(function() {
this.map.setView(L.latLng(this.latitude, this.longitude), this.zoom);
console.log("new lat? " +this.latitude );
console.log("new long? " +this.longitude );
});
}
}
The problem is that the map does not always reset to the new center. If I pan the map far away from the initial point then click the geoButton, it will re-center (a 'viewreset' event is called). Also, the map will reset to the correct center if I drag the map at all after clicking the geoButton.
If I remove the async function in the observer method, and call 'this.map.setView' directly, the map will actually re-center to the geolocated point correctly. But in this case, if I pan the map, it will 'bounce' back to the geolocated latitude (the longitude will remain wherever I pan the map). I think this is because '_viewChanged' is called twice, but the second time the latitude has not changed from the first call.
Can I perhaps force some kind of reset or refresh on the map in the observer method before call this.map/.setView (which is what seems to happen when I drag the map after calling that method)? Or, is there something else I am missing here?
After some more testing, I notice that if I comment out:
// mymap.latitude = locator.latitude;
then the map will zoom to the correct (geolocated) longitude, but will remain at whatever latitude the map was set to before the button click...the same is true if I comment out the longitude line instead; then the map pans to the correct (geolocated) latitude and to the current longitude.
In other words, I can correctly pass one updated property, but not two...both use the same observer method.

I found a solution. The key was to add a function to the leaflet-core element so that I could pass the new latitude and longitude at the same time, then call that method in the event handler for the button:
(in leaflet-core.html)
setToPoint: function(newLat, newLong){
if (this.map) {
this.async(function() {
this.map.setView(L.latLng(newLat, newLong), this.zoom, {pan: {animate: true}});
this.map.invalidateSize();
});
}
},
(in geobutton element - tap callback)
mymap.setToPoint(locator.latitude, locator.longitude);

Related

Is there a way in leaflet to get the coordinates of a map click when clicking on a polygon?

It is easy enough to get the lat lng of a map click using something like:
map.on('click', function (e) {
coords= e.latlng.lat + ", " + e.latlng.lng;
});
But if there are shapes on the map the function doesn't get called if you click a place covered by a shape.
Ultimately I want to produce a popup window triggered when a shape is clicked and populated with information based on lat/long.
Welcome to SO!
You could bind your event listener on your shapes as well (possibly through an L.FeatureGroup to avoid having to bind to each individual shape), and you can even use that event listener to fire the "click" event on the map as well.
var shapes = L.featureGroup().addTo(map);
shapes.addLayer(/* some vector shape */); // As many times as individual shapes
shapes.on("click", function (event) {
shapecoords.innerHTML = event.latlng.toString();
map.fire("click", event); // Trigger a map click as well.
});
Demo: http://jsfiddle.net/ve2huzxw/40/

Leaflet Maps Marker Popup Open on Hashtag from URL

I am passing a latlng from a link on another page to my map. My map has the markers based on this latlng. So what I am passing is exactly what the map markers are using to show on the map.
var hash = window.location.hash;
var hashless = hash.replace("#", "");
var ll = hashless.split(",", 2);
map.setView(new L.LatLng(ll[0], ll[1]), 14);
This is working great, but I am struggling at getting the marker to show it's infowindow. I would like only the link clicked on to be the marker that is showing it's popup. I am able to zoom right to the marker, but not able to get the popup to work in a reasonable way.
I have tried a few things, but map.getBounds() seems to be the one I should be working with, unless you can ID a marker based on Lat/Lng. Can you?
I am using bounds in the map.on function.... but none of the events seems to really work well. Originally thought LOAD would do it, but no results... viewreset, mousemove, etc... all show the popup in this simple function, but it is not really any good as it flickers etc... when moving things around:
map.on('viewreset', function () {
// Construct an empty list to fill with onscreen markers.
console.log("YA");
var inBounds = [], bounds = map.getBounds();
datalayer.eachLayer(function (marker) {
if (bounds.contains(marker.getLatLng())) {
inBounds.push(marker.options.title);
marker.openPopup();
}
});
});
How can I set the marker.openPopup() from my link to the current marker at that lat/lng passed to it?

Leaflet.Draw save marker to DB

I'm trying to save marker to DB with fields
lat, lon , marker description.
If there is only one marker on the map everything is fine, but when I add a second one, my code generate 2 sql-s instead 1 and duplicates 1st added marker info.
This is my code:
map.on('draw:created', function (e) {
var type = e.layerType;
var layer = e.layer;
var marker_lat = layer._latlng.lat;
var marker_lng = layer._latlng.lng;
// Here I open Popup to save Marker discription
$('#point_description').modal('show');
// Here save marker lat, Lng + marker description
$(document).on('click', '#save_point', function () {
var point_description_text = $('#point_description_text').val();
insertPlaques(marker_lat, marker_lng, point_description_text, modal_window);
});
drawnItems.addLayer(layer);
});
Your 'draw:created' function is going to be called everytime a new shape is created by Leaflet.Draw. Everytime that method is called, a new on 'click' listener is added to the '#save_point' element. So the second time 'draw:created' is called (and the user clicks '#save_point'), two listeners will be invoked. The third time, three listeners will be invoked.
You should either remove your click listener using off() after the insertPlaques method is called, or consider using a method like one() to add your click listener instead of on() (https://api.jquery.com/one/)

Leaflet User Triggered Events

Is there any way to determine whether an event was triggered programmatically or by a user?
We want to reload marker listings when the map moves or zooms, but we are initially setting the bounds of the map with setBounds() (http://leafletjs.com/reference.html#rectangle-setbounds) which is also triggering the moveend (http://leafletjs.com/reference.html#map-moveend) and zoomend (http://leafletjs.com/reference.html#map-zoomend) events which is causing the markers to reload twice.
There seems to be a (undocumented) property on the event object called hard that gets set when the map is moved by setBounds and doesn't get set when the user drags the map or uses the cursors:
map.on('moveend', function (e) {
if (e.hard) {
// moved by bounds
} else {
// moved by drag/keyboard
}
});
Testcase here on Plunker: http://plnkr.co/edit/SloKuB?p=preview
As another option you could bind to event after you've set the bounds so it won't fire when you set the bounds and when you do want to set the bounds afterwards you could first unbind using .off and rebind again after setting with .on. Something like (untested/hacky):
function moveEndHandler () {
....
}
map.on('moveend', moveEndHandler);
function mySetBounds (bounds) {
map.off('moveEnd', moveEndHandler);
map.setBounds(bounds);
map.on('moveend', moveEndHandler);
}

How to prevent overlapping pushpins at the same geolocation with Bing Maps?

If you have 2 pushpins on 'London' at the same geolocation, is there anything in the API to move them apart so they are both visible?
I can only find documentation on their old map points API which had PreventIconCollisions, this is what I want but can't see any reference to this in the new API.
I am using the JavaScript API.
So if I understand correctly, you have similar information on the same location, it this correct?
In order to display both information, you will have two options:
Merge information in the textbox using an appropriate way to present the information inside this ui element (using your own tabbed infobox for example)
Decluster the point manually when you're at a certain level of zoom
There is no default property to set this and it would really messy to do this on many pushpins, but in the main idea, you would have to: detect viewchangeend event, if you're at a certain level of zoom (or higher zoom level) then you're declustering them (I call it decluter nearby pushpins).
// Bind pushpin mouseover.
Microsoft.Maps.Events.addHandler(pin, 'mouseover', function (e) {
var currentPin = e.target;
currentPin.setOptions({ visible: false });
var currentLocation = currentPin.getLocation().clone();
var currentPoint = bmGlobals.geo.map.tryLocationToPixel(currentLocation);
if (currentPin.associatedCluster.length == 2) {
// Display the first pushpin
var pinA = createPin(currentPin.associatedCluster[0]);
var locA = bmGlobals.geo.map.tryPixelToLocation(new Microsoft.Maps.Point(currentPoint.x - pinA.getWidth(), currentPoint.y));
pinA.setLocation(locA);
bmGlobals.geo.layerClusteredPin.push(pinA);
// Display the second pushpin
var pinB = createPin(currentPin.associatedCluster[1]);
var locB = bmGlobals.geo.map.tryPixelToLocation(new Microsoft.Maps.Point(currentPoint.x + pinB.getWidth(), currentPoint.y));
pinB.setLocation(locB);
bmGlobals.geo.layerClusteredPin.push(pinB);
}
});
I will try to write a bing maps module about this, but in the fact, you'll have to get your clustered pushpins (or your own pushpin that has two associated data object) and then you will have to set their position based on the rendering on the client side.
I know this question is really old, but if someone is looking for something similar (clustering the pins) here is a good start: http://rtsinani.github.io/PinClusterer/