Google Maps Utility Library v3 - Infoboxes not closing - google-maps

I'm using Infobox from the Google Maps Utility Library V3 and I'm having a bit of trouble removing all Infoboxes in one go without keeping track of all the infoboxes or adding an event listen.
I'm currently using the following code.
function initMarkers(map, markerData) {
for(var i=0; i<markerData.length; i++) {
var iconimage = "markers/" + trackall + ".png";
marker = new google.maps.Marker({
map: map,
draggable: false,
position: markerData[i].latLng,
visible: true,
icon: iconimage
}),
boxText = document.createElement("div"),
//these are the options for all infoboxes
infoboxOptions = {
content: boxText
,disableAutoPan: true
,maxWidth: 0
,pixelOffset: new google.maps.Size(20, -75)
,zIndex: null
,boxStyle: {
background: "url('') no-repeat"
,opacity: 0.75
,width: "140px"
}
,closeBoxMargin: "10px 2px 2px 2px"
,closeBoxURL: ""
,infoBoxClearance: new google.maps.Size(1, 1)
,isHidden: false
,pane: "floatPane"
,enableEventPropagation: false
};
newMarkers.push(marker);
//define the text and style for all infoboxes
boxText.style.cssText = "border: 1px solid black; margin-top: 5px; background: #E0E7EF; padding: 5px;";
boxText.innerHTML = markerData[i].address + "<br>" + markerData[i].state;
//Define the infobox
newMarkers[i].infobox = new InfoBox(infoboxOptions);
//Open box when page is loaded
newMarkers[i].infobox.open(map, marker);
//Add event listen, so infobox for marker is opened when user clicks on it. Notice the return inside the anonymous function - this creates
//a closure, thereby saving the state of the loop variable i for the new marker. If we did not return the value from the inner function,
//the variable i in the anonymous function would always refer to the last i used, i.e., the last infobox. This pattern (or something that
//serves the same purpose) is often needed when setting function callbacks inside a for-loop.
google.maps.event.addListener(marker, 'click', (function(marker, i) {
return function() {
//newMarkers[i].infobox.open(map, this);
//map.panTo(markerData[i].latLng);
//newMarkers[i].infobox.close();
/}
})(marker, i));
}
return newMarkers;
}
//here the call to initMarkers() is made with the necessary data for each marker. All markers are then returned as an array into the markers variable
markers = initMarkers(map, [
{ latLng: pointall}
]);
In the above example I'm using the variable pointall which contains the ever changing marker information. The site is a flight tracking site so it tracks any number of aircraft at different locations constantly. Every time there's an update eg. new markers to plot, I use the following function to remove the old markers first.
function clearOverlays() {
if (newMarkers) {
for (var i = 0; i < newMarkers.length; i++ ) {
newMarkers[i].setMap(null);
}
}
}
The above removes all the markers but if I add
newMarkers[i].infobox.close();
It deletes the first marker but stops executing code.
Is there an easy way of just saying.. Close all open Infoboxes. I don't need to know which is which as they will all need to be closed. Currently the infoboxes are opened with the new marker but the old infobox is staying.
Note: I'm not advanced with javascript so be easy on me ;)
Any help would be appreciated.
Thanks
Edit: I'm not having much luck here. As suggested I've tried creating an array to hold the Infoboxes but I must be doing something wrong.
I've tried adding the following declaration:
ibArray = [];
I've then tried adding :
ibArray.push(marker);
I've added the above in several locations including immediately after pushing the marker into the newMarkers array but no matter where I place the code It's breaking the rest of my code and not displaying any infoboxes.
I suspect I've got the syntax incorrect, and I also can't work out where to place the code.
Can anyone else help?

You should do the same you're doing for the markers, ie keep track of the infoWindows and loop through the array, closing them (and removing them from the array).

Related

Faster way to add multiple markers on Google Maps v3 Javascript

I'm trying to add a lot of markers into Google Map. I'm already passing the data to the server to make clusters on "busy" areas to help keep the number of markers down.
Here is my code:
markers.forEach(function(item, i){
if (item.count) {
// this is a cluster one, so add the respective icon along with the number as a label
// check we have a value for this. If we don't it means that someone has 2 markers with the exact same lat/lng, so lets ignore it!
if (item.coordinate) {
var location = new google.maps.LatLng(item.coordinate[0], item.coordinate[1]); // lat,lng of cluster
var cluster_icon;
if (item.count < 10) {
cluster_icon = icon_markers_1;
} else if (item.count < 30) {
cluster_icon = icon_markers_2; //
} else if (item.count < 50) {
cluster_icon = icon_markers_3; //
} else if (item.count < 100) {
cluster_icon = icon_markers_4; //
} else {
cluster_icon = icon_markers_5; //
}
window.VARS.markers[i] = new google.maps.Marker({
position: location,
//label: String(item.count),
title: "lat:"+item.coordinate[0]+ ",lng: " + item.coordinate[1],
label: {
text: String(item.count),
color: "#fff",
fontSize: "16px",
fontWeight: "bold"
},
map: window.VARS.Google_Map_Modal,
icon: cluster_icon
});
window.VARS.markers[i].addListener('click', function() {
//console.dir(window.VARS.markers[i].getPosition().lat());
var zoom = window.VARS.Google_Map_Modal.getZoom();
zoom++;
if (zoom <= 20) {
window.VARS.Google_Map_Modal.setZoom(zoom++)
}
window.VARS.Google_Map_Modal.setCenter(this.getPosition());
});
}
} else {
var link = window.VARS.links_stored[item.link_id];
// this is an actual marker (not cluster), so lets add it to the map
var location = new google.maps.LatLng(link.latitude, link.longitude); // lat,lng of cluster
var dataPhoto = link.image_small;
var marker = new google.maps.Marker({
position: location,
title: link.title,
the_row: i,
linkid: link.linkid,
map: window.VARS.Google_Map_Modal
});
window.VARS.markers.push(marker);
window.VARS.marker_vals.push(item);
//bounds.extend(latLng);
marker.addListener('click', function() {
// do some stuff
});
}
});
Is there a better way to do this, rather than one by one? I read that you could "batch add" to the map - but I can't seem to find any documentation to support this.
Instead of re-inventing the wheel by implementing a custom clustering logic, you can use the one provided by Google Maps.
https://developers.google.com/maps/documentation/javascript/marker-clustering
Adding markers one by one makes the map incredibly slow. MarkerClusterer avoids this issue by creating an array of markers but not adding them to the map.
The markers are added together at the end when you initialize the MarkerClusterer by passing the marker array.
var markerCluster = new MarkerClusterer(map, markers,
{imagePath: 'https://developers.google.com/maps/documentation/javascript/examples/markerclusterer/m'});
This is extremely quick and efficient, allowing addition of thousands of markers without too much of a performance hit.

google maps infowindow displays the same content

So google maps infowindow always displays the content of the last map item. Based on some research I stumbled accross this. However it still doesn't work for me here is my code.
function bindInfoWindow(marker, map, infowindow, html) {
marker.addListener('click', function () {
infowindow.setContent(html);
infowindow.open(map, this);
});
}
for (var x = 0; x < filtered_pins.length; x++) {
var links = filtered_pins[x].description + 'read more..';
links_arr.push(links);
$.getJSON('http://maps.googleapis.com/maps/api/geocode/json?address=' + filtered_pins[x].city + '&sensor=false', null, function (data) {
//console.log('4th'+ links);
var p = data.results[0].geometry.location
var latlng = new google.maps.LatLng(p.lat, p.lng);
var marker = new google.maps.Marker({
position: latlng,
map: map,
});
var infowindow = new google.maps.InfoWindow();
//marker.addListener('click', function () {
//infoWindow.setContent(links);
// infowindow.open(map, this);
//});
bindInfoWindow(marker, map, infowindow, links);
my_markers.push(marker);
});
}
I have gone through quite a number of related items on Stackoverflow but they don't seem to be of service.They all seem to already have access to the latlang so their structure is different. I have to use the .getJson method to retrieve an addresses latlang first then create markers.
Each iteration of your for loop calls $.getJSON(), however there is only a single links variable that every iteration shares. The for loop runs to completion before any of the $.getJSON() calls ever call your callback function. So all of them use the last value that was assigned into links.
It really is exactly the same problem as in the question you linked to; in the code in that question the problem variable is club. The reason the solution from that question didn't work is that your code has the $.getJSON() call which is asynchronous, just as the click handler is asynchronous.
To fix this, you can simply move the entire loop body into a function. The local variables inside that function will all be unique to each iteration of the loop.
The use of filtered_pins[x] seems OK here, but this could also be a problem if it were used inside the $.getJSON() callback, and it makes the code simpler to only have that at one point, where addMarker() is called.
And at this point you don't need bindInfoWindow() to be a separate function, so I moved it inline.
I also made a couple of other little changes to keep the line lengths shorter.
for (var i = 0; i < filtered_pins.length; i++) {
addMarker( filtered_pins[i] );
}
function addMarker( pin ) {
var links = pin.description +
'read more..';
links_arr.push(links);
var url = 'http://maps.googleapis.com/maps/api/geocode/json?address=' +
pin.city + '&sensor=false';
$.getJSON( url, null, function (data) {
var p = data.results[0].geometry.location
var latlng = new google.maps.LatLng(p.lat, p.lng);
var marker = new google.maps.Marker({
position: latlng,
map: map,
});
var infowindow = new google.maps.InfoWindow();
marker.addListener('click', function () {
infowindow.setContent(links);
infowindow.open(map, this);
});
my_markers.push(marker);
});
}
I'm not sure what the links_arr and my_markers arrays are used for, so there could be some problems there as well, depending.
In modern browsers, you could also fix this by using let instead of var for your variables inside the loop. Then you wouldn't need the addMarkers() function. But I think this separate function makes the code cleaner, and it's still compatible with old browsers.

Overlapping Marker Spiderfier Marker Icon When There are Multiple Markers at Same Location

Google Maps doesn't provide a way to break apart multiple markers that are at the same location. This can occur with a people or businesses at a multiple residency location such as an apartment building or professional services building. Depending at zoom level it can also occur at shopping malls, etc.
The way around that is to "spiderfy" them: when clicking on the first it breaks them out with a line to the location. This is done in Google Earth and George MacKerron wrote a package to do that for Google Maps. (https://github.com/jawj/OverlappingMarkerSpiderfier)
It can be integrated with markerclusterer, although it doesn't support marker clusterer's batch creation of markers.
My issue is that the application I'm working on wants to have specific icons for different types of activities. Spiderfier puts one of the markers on top. A person looking at the map has no way of knowing that there can be 10 or more other markers underneath the top marker.
Ideally, there would be a way to put a top marker that displays when there are multiple markers similar to the different icon in markercluster. It isn't a direct 1-to-1 since spiderfier also works when they are close but not exactly at the same location (default is 20 pixels) and markercluster has no provision for accessing multiple markers at the exact same location.
The ideal behavior would be have a special icon for spiders that broke into the spiderfied individual icons when clicked. Similar to markerclusterer, but without the zoom change and handling the same location. The special icon ideally would indicate how many other markers are at the spot, again like markerclusterer. The special icon could be hidden or become part of the spiderfied group.
Without some accommodation users would have no way of knowing multiple activities are at the location. They may even assume that the activity they want is not at that location because another activities marker is shown.
This is a plunker that has the problem: http://plnkr.co/edit/vimZNq?p=info
var markers = [];
var bounds = new google.maps.LatLngBounds();
for (var i = 0; i < 100; ++i) {
var latLng = new google.maps.LatLng(Math.floor(Math.random() * 10) / 10 + 39,
Math.floor(Math.random() * 10) / 10 - 100);
var marker = new google.maps.Marker({
position: latLng,
title: "marker " + i + " pos: " + latLng,
maxZoom: 8,
map: map
});
marker.desc = marker.getTitle();
bounds.extend(latLng);
markers.push(marker);
oms.addMarker(marker);
}
map.fitBounds(bounds);
var markerCluster = new MarkerClusterer(map, markers);
Thanks for your help,
David
Here's how I got it to work. Where map is a Gmap instance and oms is an Overlapping Marker Spiderfier instance. We're also using Marker Clusterer on the initial zoom which buys us a break.
map.addListener('zoom_changed', function() {
map.addListenerOnce('idle', function() {
// change spiderable markers to plus sign markers
// we are lucky here in that initial map is completely clustered
// for there is no init listener in oms :(
// so we swap on the first zoom/idle
// and subsequently any other zoom/idle
var spidered = oms.markersNearAnyOtherMarker();
for (var i = 0; i < spidered.length; i ++) {
// this was set when we created the markers
url = spidered[i].icon.url;
// code to manipulate your spidered icon url
};
});
});
oms.addListener('unspiderfy', function(markers) {
var spidered = markers;
for (var i = 0; i < spidered.length; i ++) {
url = spidered[i].icon.url;
// change it back
};
});
oms.addListener('click', function(marker) {
// put the clicked-on marker on top
// when oms un-spiders
marker.zIndex=999;
// set infowindow, panning etc.
});
I managed to match the following Versions:
MarkerClusterer 2.0.13
OverlappingMarkerSpiderfier 3.27
On every creation of a new Marker, i store the initialIconUrl in the Marker Object
var marker = new google.maps.Marker({
position: //some position
});
marker.setIcon(iconUrl);
marker.initialIconUrl = iconUrl;
When declaring the OverlappingMarkerSpiderfier, set the nearbyDistance to 0.001 (or some other very small value).
this.oms = new OverlappingMarkerSpiderfier(this.map, {
markersWontMove: true,
markersWontHide: true,
nearbyDistance: 0.001 //This will only spiderfy the Markers if they have the exact same position
});
Then, we need a listener on the maps 'idle' Event, to format the Markers manually.
I needed this because my SPIDERFIABLE Marker wouldn't show correctly on the first step, when transferring from the Clustered Marker to the seperate Markers.
var me = this;
google.maps.event.addListener(this.map, 'idle', function () {
me.oms.formatMarkers();
});
Listen to the oms 'format' Event and set the iconURL for Markers that are SPIDERFIABLE.
If the Marker is not spiderfiable, reset the Icon to the initial Url.
var spiderfiableIconUrl = //Whatever you need
this.oms.addListener('format', function (marker, status) {
var iconURL = status == OverlappingMarkerSpiderfier.markerStatus.SPIDERFIABLE
? spiderfiableIconUrl :
marker.initialIconUrl;
marker.setIcon(iconURL);
});
Hope this helps.
Some methods seems to be interesting like markersNearAnyOtherMarker but I cannot get it work.
An interesting way could be to use spiderfy and unspiderfy events and change marker when it's fired
overlappingMarkers = new OverlappingMarkerSpiderfier(map, overlapOptions);
overlappingMarkers.addListener('spiderfy', function (markers) {
markers.forEach(function (marker) {
marker.setLabel('*');
marker.setIcon(myNormalIcon);
})
})
overlappingMarkers.addListener('unspiderfy', function (markers) {
markers.forEach(function (marker) {
marker.setLabel(''+markers.length);
marker.setIcon(myOverlapIcon);
})
})
Unfortunatly, the unspiderfy event isn't fired until we open then close the overlap marker. If I find a conclusion to this solution I will update this post.

Google Maps API: Turn off icons in an array for certain zoom levels

I have multiple icons in an array with info boxes attached to each that have information for that location.
for(var i=0; i<markerData.length; i++) {
infomarker = new google.maps.Marker({
map: map,
draggable: false,
position: markerData[i].latLng,
visible: true,
icon: infoicon
}),
boxText = document.createElement("div"),
infoboxOptions = {
content: boxText,
etc.......
I also have some other symbols that have links to other pages. I managed to remove the other symbols at a certain zoom level
google.maps.event.addListener(map, 'zoom_changed', function() {
var zoom = map.getZoom();
if (zoom <= 10) {
louisvillepagemarker.setMap(null);
} else {
louisvillepagemarker.setMap(map);
}
});
I want to remove the icons put on by the array when the map is zoomed out. I played around with several attempts, but I am not succeeding.
create a MVCObject with a property map .
When you create the Markers bind their map-property to the map-property of this MVCObject.
On zoom_changed change only the map-property of this MVCObject to the desired value, the markers will inherit this property.

Google Maps API v3 Multiple Info Boxes not opening on click

Can somebody please explain why my Google Maps info windows are only opening at the bottom left marker?
See here for the fiddle http://jsfiddle.net/Vj3nw/7/
When I click on any of the markers, only the bottom-left one is opened. I would like the marker that is clicked to open.
I believe the problem is due to the loop below, and this question seems to have the same problem Setting onclick to use current value of variable in loop
"The problem you were hitting is that javascript doesn't use block scope. Instead all variables have a lifetime of their containing function. So there was only ever one a captured in all of the onclick functions and they all see it's final value."
I just can't quite translate that solution to mine.
Many thanks.
for (var i = 0; i < 4; i++) {
var marker = new google.maps.Marker({
position: flightPlanCoordinates[i],
icon: Symbol,
map: map
});
infowindow = new google.maps.InfoWindow();
infowindow.setContent("hello");
google.maps.event.addListener(marker, 'click', function () {
infowindow.open(map, marker);
});
}
Generating your markers and adding the listeners in their own function, called from the loop like the updated fiddle works.
function create_marker(i,flightPlanCoordinates,Symbol,map){
var marker = new google.maps.Marker({
position: flightPlanCoordinates[i],
icon: Symbol,
map: map
});
infowindow = new google.maps.InfoWindow();
infowindow.setContent("hello" + i);
google.maps.event.addListener(marker, 'click', function() {
infowindow.open(map,marker);
});
}