LeafletJS popups positions and markers add and remove issue - javascript

I am implementing displaying locations on openstreetmaps using leafletjs API. Here is the scenario, When the page loads a JS function is being triggered which will query DB to get current locations of all the devices and show them as markers on map. I am facing two major issues in it:
--> I am trying to bind popups with each marker so that it could be descriptive. In other words user should gain idea that of what device this marker for. The problem is that popups is being showed over the marker making marker non-visible. Here is the screenshot:
Marker is shown below(after closing one popup):
--> On this page, I am displaying devices names in a table. The first column of this table is checkbox. When user check a device its marker appears on the map as expected(working fine). After that if user unchecks that device then its marker disappears. But when user re-selects that device again then its marker doesn't appear.
Here is my code:
function OnPageLoad() {
var IDs = GetAllSelectedDeviceIDs();
LoadOpenStreetMap(IDs);
}
var map;
var markers = new Array();
function LoadOpenStreetMap(deviceIDs) {
for (var i = 0; i < deviceIDs.length; i++) {
$.ajax({
url: "../Database/getcurrentlocation.php",
cache: false,
type: "post",
data: "deviceId=" + deviceIDs[i] + "&r=" + Math.random(),
dataType: "html",
async: false,
success: function (data) {
if (data != null) {
var dataArray = data.split(/~/);
SetOpenStreetMap(dataArray[0], dataArray[1], deviceIDs[i], i, flags[i]);
}
}
});
}
deviceIDs = GetAllSelectedDeviceIDs();
setTimeout(function () { LoadOpenStreetMap(deviceIDs); }, 500);
}
function SetOpenStreetMap(lati, longi, deviceID, markerIndex, markerFlag) {
if (!map) {
map = L.map('map').setView([lati, longi], 12);
L.tileLayer('http://{s}.tile.cloudmade.com/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX/997/256/{z}/{x}/{y}.png', {
attribution: '',
maxZoom: 18
}).addTo(map);
}
if (markerFlag == 1) {
//need to add marker
if (!markers[markerIndex]) {
var popupLocation1 = new L.LatLng(lati, longi);
var popupContent1 = 'This is a nice popup.';
popup1 = new L.Popup();
popup1.setLatLng(popupLocation1);
popup1.setContent(popupContent1);
markers[markerIndex] = L.marker([lati, longi]).addTo(map);
markers[markerIndex].bindPopup(popupContent1);
map.addLayer(popup1);
}
else
markers[markerIndex].setLatLng([lati, longi]).update();
}
else {
//need to remove marker
if (markers[markerIndex]) {
map.removeLayer(markers[markerIndex]);
}
}
}
Any help would be appreciated.
Thanks.

To put the popup over the marker with some offset there exists an offset property:
if (markerFlag == 1) { //need to add marker
if (!markers[markerIndex]) {
var popupLocation1 = new L.LatLng(lati, longi);
var popupContent1 = 'This is a nice popup.';
popup1 = new L.Popup();
popup1.setLatLng(popupLocation1);
popup1.setContent(popupContent1);
popup1.offset = new L.Point(0, -20);
markers[markerIndex] = L.marker([lati, longi]).addTo(map);
/* markers[markerIndex].bindPopup(popupContent1); */
map.addLayer(popup1);
} else
markers[markerIndex].setLatLng([lati, longi]).update();
}

Set the offset inside the options object when creating the popup.
var options = {
offset: new L.Point(1, -20)
};
var popup = L.popup(options)
.setLatLng([ lat, lng ])
.setContent('Your popup content goes here!')
.openOn(map)
;
The "options" object is the first object that is passed to the L.popup()

To show a popup over a marker, only call the bindPopup method on the marker.
This is shown in the QuickStart.
You do not need to instantiate a popup, yourself.

Related

Markkers are displayed twice with Leaflet search

I've made a map, based on a geojson file and with clustered markers.
Then I tried to add the leaflet-search plugin.
The search feature works : when I search somtehing, it opens the good popup (informations are generated by "complex" routines).
But now I have my markers displayed twice : the ones I previously created, then those displayed by the search plugin.
How to make leaflet-search to not display its own markers ?
I hope I was clear enough. Bellow here is a sample of my code (I tried to made it readable) :
var geojsonFeature = { mygeojsondata };
// Runs several function to generate an "information page" for each feature
function createPopupInfo(feature, layer) {
var pop = render_name(feature);
//
// ...
}
var nbfeatures = 0;
var layer1 = new L.geoJSON(geojsonFeature, {
onEachFeature: createPopupInfo,
pointToLayer: function (feature, latlng) {
nbfeatures++;
var marker = L.marker(latlng)
arrayOfLatLngs.push(latlng);
marker.on("add", function (event) {
// Retrieve the layer through event.target http://leafletjs.com/reference-1.0.0.html#event-target
event.target.openPopup();
var latLngs = [marker.getLatLng()];
var markerBounds = L.latLngBounds(latLngs);
map.fitBounds(markerBounds);
});
map.maxBoundsViscosity = 0;
return marker;
}
});
var searchControl = new L.Control.Search({
layer: layer1,
propertyName: 'search_index',
marker: false,
moveToLocation: function (latlng, title, map) {
map.setView(latlng, 17);
}
});
searchControl.on('search:locationfound', function (e) {
if (e.layer._popup)
e.layer.openPopup();
}).on('search:collapsed', function (e) {
layer1.eachLayer(function (layer) { //restore feature color
layer1.resetStyle(layer);
});
});
// Clustering
var markers = L.markerClusterGroup();
markers.addLayer(layer1);
map.addLayer(markers);
When the search finds something, harness that event to remove the layer with all the markers:
searchControl.on('search:locationfound', function (e) {
if (e.layer._popup) e.layer.openPopup();
markers.removeLayer(layer1)
})
Of course you'll also want to add these markers back in when you close the search:
searchControlon('search:collapsed', function (e) {
markers.addLayer(layer1);
layer1.eachLayer(function (layer) { //restore feature color
layer1.resetStyle(layer);
});
});
I would say its good UX to also add them all back in when the search comes up empty, but theres' no obvious event for that with leaflet-search.
I found what didn't work, I must pass the "clustered layer" :
var searchControl = new L.Control.Search({
layer: markers,
propertyName: 'search_index',
...
Sources :
https://gis.stackexchange.com/questions/310797/using-l-control-search-and-l-markerclustergroup
https://github.com/stefanocudini/leaflet-search/issues/166
And another example :
http://embed.plnkr.co/46VJcp/

Refreshing google maps markers and infoBubbles using ajax

I am using asp mvc along with jquery and google maps api to display and update locations on a map.
My aim:
Use markers to point to a number of locations
Provide a small amount of information on those location using info bubbles info bubble repo
OnClick refresh markers and info bubbles
What I've achieved so far:
I am able to load the map, place the pointers and display the info bubbles. After a few hours of struggling I have also managed to refresh the markers when the ajax call is made.
My problem:
After the first update the markers refresh correctly but the info bubbles remain (I cannot work out how to clear them)
New bubbles are displayed along with the new markers BUT these bubbles do not hold any data
After the second update all markers disappear, but all info bubbles remain
Initial load
After call 1 (rent button clicked)
After call 2 (buy button clicked)
somePartialView.cshtml
#Html.HiddenFor(m => m.LatLng, new { #id = "latLng" })
<div id="map" class="google-maps">
<div id="map-canvas"></div>
</div>
Javascript
NB: While testing I am hard coding the lat and lng values in the viewModel so I know each time a call is made only four values will be returned, which Is why I have hard coded four string into the contentString array.
$(document).ready(function () {
var map;
var iterator = 0;
var markers = [];
var markerLatLng = [];
var contentString = [
'<div class="infobox-inner"><img src="assets/img/icon-1.png" alt=""/><span>Sarkkara Villa</span></div>',
'<div class="infobox-inner"><a href="08_Properties_Single.html"><img src="assets/img/icon-2.png" alt=""/><span>Sarkkara Flat</span></div>',
'<div class="infobox-inner"><a href="08_Properties_Single.html"><img src="assets/img/icon-3.png" alt=""/><span>Sarkkara Commercial</span></div>',
'<div class="infobox-inner"><img src="assets/img/icon-4.png" alt=""/><span>Sarkkara Appartment</span></div>'
];
var latlng = $("#latLng").val();
var aryOfLatLng = latlng.split(';');
loadMarkers(aryOfLatLng);
function loadMarkers(ary) {
$.each(ary, function (index, value) {
if (value !== "") {
var values = value.split(',')
var loc = new google.maps.LatLng(Number(values[0]), Number(values[1]));
markerLatLng.push(loc);
}
})
}
function initialize() {
map = new google.maps.Map(document.getElementById('map-canvas'), {
scrollwheel: false,
zoom: 12,
center: new google.maps.LatLng(52.520816, 13.410186) //Berlin
});
setTimeout(function () {
drop();
}, 1000);
}
// animate markers
function drop() {
for (var i = 0; i < markerLatLng.length; i++) {
setTimeout(function () {
addMarker();
}, i * 400);
}
}
function addMarker() {
var marker = new google.maps.Marker({
position: markerLatLng[iterator],
map: map,
icon: 'assets/img/marker.png',
draggable: false
//,animation: google.maps.Animation.DROP
});
markers.push(marker);
var infoBubble = new InfoBubble({
map: map,
content: contentString[iterator],
position: markerLatLng[iterator],
disableAutoPan: true,
hideCloseButton: true,
shadowStyle: 0,
padding: 0,
borderRadius: 3,
borderWidth: 1,
borderColor: '#74d2b2',
backgroundColor: '#ffffff',
backgroundClassName: 'infobox-bg',
minHeight: 35,
maxHeight: 230,
minWidth: 200,
maxWidth: 300,
arrowSize: 5,
arrowPosition: 50,
arrowStyle: 0
});
setTimeout(function () {
infoBubble.open(map, marker);
}, 200);
google.maps.event.addListener(marker, 'click', function () {
if (!infoBubble.isOpen()) {
infoBubble.open(map, marker);
}
else {
infoBubble.close(map, marker);
}
});
iterator++;
}
google.maps.event.addDomListener(window, 'load', initialize);
$("#rent").click(function () {
ajaxRequest("/Map/_IsForSale", false)
})
$("#buy").click(function () {
ajaxRequest("/Map/_IsForSale", true)
})
function ajaxRequest(targetUrl, data) {
$.ajax({
cache: false,
url: targetUrl,
type: "POST",
data: { 'isForSale': data },
success: function (data) {
successCallBack(data);
},
error: function (request, status, error) {
alert(error)
}
});
}
// Removes the markers from the map, but keeps them in the array. It will hide all markers.
function clearMarkers() {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(null);
}
}
function successCallBack(data) {
clearMarkers();
var latlng = data.substring(data.indexOf("value=") + 7, data.indexOf(";\""));
var ary = latlng.split(';');
$.each(ary, function (index, value) {
if (value !== "") {
var values = value.split(',')
var loc = new google.maps.LatLng(Number(values[0]), Number(values[1]));
markerLatLng.push(loc);
}
})
drop();
}
});
1. After the first update the markers refresh correctly but the info bubbles remain (I cannot work out how to clear them)
Just like how you created a markers array to store your created markers to later clear with your #clearMarkers. I'd do similar for the infoBubbles, e.g. #clearInfoBubbles. Make an infoBubbles array to store info bubbles. According to the source code (see line 968) in the info bubble repo, you can close an info bubble with InfoBubble#close. Call your #clearInfoBubbles where necessary. In your #successCallBack perhaps.
2. New bubbles are displayed along with the new markers BUT these bubbles do not hold any data
You are creating infoBubble here like this
var infoBubble = new InfoBubble({
map: map,
content: contentString[iterator],
...
});
with contentString, which is only hardcoded to be 4 elements long.
By the time #initialize is finished, iterator will be equal to 4. The subsequent call to #addMarker (when the 1st rent button is clicked) will try to create an info bubble with contentString[4], however, that is undefined. iterator is never reset to 0.
3. After the second update all markers disappear, but all info bubbles remain
All info bubbles remain because they're never closed.
I'm not sure what your "buy" request is giving back to you. I'd check to see that the response is returning the data that you're expecting. My guess is that no new markers are getting created at this point.

Google Maps v3 markers not refreshing

So I have 3 Divs with hidden Lat Lng inputs, and some ajax pagination to change them out. On the initial load, I have a script that turns each one of the three pairs of Lat Lng inputs into a marker and pushes them into an array. All of this works good.
Now, when I update the 3 divs with my script file, and then try to use the provided v3 API method to clear and redraw the markers, I get the same spots on the map. And then, if I tell it to go back to page one results, it does delete the page 1 markers and I get the markers from page 2 on my map.
Here is the javascript:
var map;
var markers = [];
function initialize() {
var mapOptions = {
center: new google.maps.LatLng(37.09024, -96.712891),
zoom: 3
};
map = new google.maps.Map(document.getElementById("map-canvas"),
mapOptions);
setRGBmarkers();
}
function setRGBmarkers() {
markers.push(new google.maps.Marker({
position: new google.maps.LatLng(
Number(document.getElementById("address-0-Lat").value),
Number(document.getElementById("address-0-Lng").value)
),
map: map
}));
//removed other markers for brevity
}
function setAllMap(map) {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(map);
}
}
function clearMarkers() {
setAllMap(null);
}
function deleteMarkers() {
clearMarkers();
markers = [];
}
var getPage = function () {
var $a = $(this);
var options = {
url: $a.attr("href"),
type: "get"
};
$.ajax(options).done(function (data) {
var target = $a.parents("div.pagedList").attr("data-nerd-target");
$(target).replaceWith(data);
});
deleteMarkers();
setRGBmarkers();
alert('done');
return false;
}
$(".body-content").on("click", ".pagedList a", getPage);
So it successfully goes out and gets the results. I'm guessing it somehow is running delete and set before its actually done replacing the markers so its setting the 'unreplaced data' again, hence why going back to page one results finally in page 2's markers showing up? Here's a snippet of what the div looks like if needed:
<div class="panel-body">
Event Description
<input id="address-0-Lat" type="hidden" value="34.0519079">
<input id="address-0-Lng" type="hidden" value="-118.24389300000001">
</div>
Well, Anto is correct, and upon investigating the jQuery documentation for the ajax() function, I see the correct place to put the code would be like so:
var getPage = function () {
var $a = $(this);
var options = {
url: $a.attr("href"),
type: "get"
};
$.ajax(options).done(function (data) {
var target = $a.parents("div.pagedList").attr("data-nerd-target");
$(target).replaceWith(data);
deleteMarkers();
setRGBmarkers();
alert('done');
});
return false;
}
$(".body-content").on("click", ".pagedList a", getPage);
Where the 'done' function is executed once the response comes back. Documenation and examples can be found here: http://api.jquery.com/jquery.ajax/

creating multiple infowindows in Bing Maps v7 and presenting data specific to pin

I'm currently trying to learn the Bing Maps API and currently trying to build a map with multiple pins that can be hovered to reveal a info window with that pins data. At the moment I'm experiencing a few problems. If anyone could help with the following points that would be great:
When I mouseleave a pin the info window doesn't get removed?
How can I use the data I retrieve in the addPin() to populate the
required info window?
In createInfoBox() I want to hide any active info window when the
map gets moved but this seems to get triggered even if the map isn't
moved?
If there are any improvements i can make please let me know
jsfiddle: http://jsfiddle.net/kyllle/vpepD/23/ and JS below
JS
var dealerMap = {
createInfoBox: function(infobox) {
var instance = this,
pushpin = infobox;
// Now create infowindows
var NewWindow = new Microsoft.Maps.Infobox(pushpin.getLocation(), {
title: 'title',
offset: new Microsoft.Maps.Point(-3, pushpin.getHeight() - 5),
zIndex: 999,
visible: true
});
//Display infowindow
instance.displayInfoBox(NewWindow, pushpin);
//Hide infowindow if map is moved - currently gets run without moving map
//Microsoft.Maps.Events.addHandler(dealerMap.myMap, 'viewchange', instance.hideInfoBox(NewWindow));
},
displayInfoBox: function(infobox, pin) {
var instance = this;
//Show updated infowindow
dealerMap.myMap.entities.push(infobox);
//Mouse out handler to remove window
Microsoft.Maps.Events.addHandler(pin, 'mouseleave', function() {
instance.hideInfoBox(NewWindow);
});
},
hideInfoBox: function(infobox) {
var instance = this;
console.log('this was called');
dealerMap.myMap.entities.remove(infobox);
},
addPin: function() {
var instance = this;
//make $.ajax json call
var response = data.dummy;
//on success make each pin with returned data
for (var i = 0, len = response.length; i < len; i++) {
var responseItem = response[i],
pinLocation = new Microsoft.Maps.Location(responseItem.lat, responseItem.long);
//Create new pin
var NewPin = new Microsoft.Maps.Pushpin(pinLocation, {
icon: 'http://www.kylehouston.com/testing/sportscar_' + responseItem.id +'.png',
width: 32,
height: 37
});
//Push new pin onto map
this.myMap.entities.push(NewPin);
//Event handlers to show and hide requested infowindow information
Microsoft.Maps.Events.addHandler(NewPin, 'mouseover', function(e) {
console.log(this);
dealerMap.createInfoBox(e.target);
});
}
},
init: function() {
var instance = this;
var mapOptions = {
credentials: "AvGoKWSuMorGQb5h4UyyatCBGmGzSZe7-dWQMzXt4qqz6mV_WCC5m-paxvQhednd",
center: new Microsoft.Maps.Location(37.09024, -95.712891),
zoom: 5,
enableClickableLogo: false,
enableSearchLogo: false
}
dealerMap.myMap = new Microsoft.Maps.Map(document.getElementById('mapDiv'), mapOptions);
//now add some pins
instance.addPin();
}
}
dealerMap.init();
});
I highly recommend only showing one infobox at a time on the map. Showing multiple infoboxes can crowd the map and make for a bad user experience. It also uses up a lot more resources. A better approach is to create a single infobox and reuse it for each pushpin as you want to display content. I wrote a blog post that shows how to do this here: http://rbrundritt.wordpress.com/2011/10/13/multiple-pushpins-and-infoboxes-in-bing-maps-v7/

Google Maps: remember id of marker with open info window

I have a Google map that is showing a number of markers. When the user moves the map, the markers are redrawn for the new boundaries, using the code below:
GEvent.addListener(map, "moveend", function() {
var newBounds = map.getBounds();
for(var i = 0; i < places_json.places.length ; i++) {
// if marker is within the new bounds then do...
var latlng = new GLatLng(places_json.places[i].lat, places_json.places[i].lon);
var html = "blah";
var marker = createMarker(latlng, html);
map.addOverlay(marker);
}
});
My question is simple. If the user has clicked on a marker so that it is showing an open info window, currently when the boundaries are redrawn the info window is closed, because the marker is added again from scratch. How can I prevent this?
It is not ideal, because often the boundaries are redrawn when the user clicks on a marker and the map moves to display the info window - so the info window appears and then disappears again :)
I guess there are a couple of possible ways:
remember which marker has an open info window, and open it again when the markers are redrawn
don't actually re-add the marker with an open info window, just leave it there
However, both require the marker with an open window to have some kind of ID number, and I don't know that this is actually the case in the Google Maps API. Anyone?
----------UPDATE------------------
I've tried doing it by loading the markers into an initial array, as suggested. This loads OK, but the page crashes after the map is dragged.
<script type="text/javascript" src="{{ MEDIA_URL }}js/markerclusterer.js"></script>
<script type='text/javascript'>
function createMarker(point,html, hideMarker) {
//alert('createMarker');
var icon = new GIcon(G_DEFAULT_ICON);
icon.image = "http://chart.apis.google.com/chart?cht=mm&chs=24x32&chco=FFFFFF,008CFF,000000&ext=.png";
var tmpMarker = new GMarker(point, {icon: icon, hide: hideMarker});
GEvent.addListener(tmpMarker, "click", function() {
tmpMarker.openInfoWindowHtml(html);
});
return tmpMarker;
}
var map = new GMap2(document.getElementById("map_canvas"));
map.addControl(new GSmallMapControl());
var mapLatLng = new GLatLng({{ place.lat }}, {{ place.lon }});
map.setCenter(mapLatLng, 12);
map.addOverlay(new GMarker(mapLatLng));
// load initial markers from json array
var markers = [];
var initialBounds = map.getBounds();
for(var i = 0; i < places_json.places.length ; i++) {
var latlng = new GLatLng(places_json.places[i].lat, places_json.places[i].lon);
var html = "<strong><a href='/place/" + places_json.places[i].placesidx + "/" + places_json.places[i].area + "'>" + places_json.places[i].area + "</a></strong><br/>" + places_json.places[i].county;
var hideMarker = true;
if((initialBounds.getSouthWest().lat() < places_json.places[i].lat) && (places_json.places[i].lat < initialBounds.getNorthEast().lat()) && (initialBounds.getSouthWest().lng() < places_json.places[i].lon) && (places_json.places[i].lon < initialBounds.getNorthEast().lng()) && (places_json.places[i].placesidx != {{ place.placesidx }})) {
hideMarker = false;
}
var marker = createMarker(latlng, html, hideMarker);
markers.push(marker);
}
var markerCluster = new MarkerClusterer(map, markers, {maxZoom: 11});
</script>
You should probably create all your markers at an initial stage with your createMarker() method, and store the returned GMarker objects inside an array. Make sure to set the hide: true property in GMarkerOptions when you create your markers, so that they would be created as hidden by default.
Then instead of iterating through places_json.places, you could iterate through your new GMarker array. You would be able to get the coordinates of each marker with the GMarker.getLatLng() method, with which to check if each marker lies within the bounds.
Finally simply call GMarker.show() for markers that lie within the bounds, or GMarker.hide() to hide them.
You would eliminate the expensive destruction/creation of markers on each map movement. As a positive-side effect, this will also solve your GInfoWindow problem.
If you're using that many markers, make sure you use GMarkerManager. It's designed for many markers, with only a few visible at once.
http://mapki.com/wiki/Marker_Optimization_Tips

Categories

Resources