google maps infowindow displays the same content - google-maps

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.

Related

Google Maps - Trigger a click event on a data layer at a specific point (long/lat)

I am loading a google map with GeoJSON, and after the map is loaded and the data layers are applied, I would like to trigger a click event at a specific point automatically for the user. Subsequently, they can click all over the map to interact with it.
So for the automatic load part, I tried something like this:
var x = new google.maps.LatLng(myLongitude, myLatitude);
google.maps.event.trigger(map.data, 'click', WHAT_GOES_HERE?);
but I can't figure out what goes in the last part of that function. The corresponding function for clicking is this:
map.data.addListener('click', function (event) {
...
code
...
}
The event fires, but event is null of course. I need (event) to be populated with a feature (that's what the type is expected to be) but I can't seem to figure out how to get the feature from a long/lat.
So I load my data layers, I have my long/lat, but I can't seem to retrieve a feature from the long/lat. Any suggestions on how to retrieve this?
A (on)Click looks like this (let's say we put a marker there):
map.addListener('click', function (event) {
var position = {lat: event.latLng.lat(), lng: event.latLng.lng()}
//alert(JSON.stringify(position));
var marker = new google.maps.Marker({position: position, map: map});
});
So let's reverse this.
let's say you heve buttons that invoke a click on Brussels or Paris
<input type="button" value="click on Brussels" onclick="clickOnMap(50.85, 4.35)">
<input type="button" value="click on Paris" onclick="clickOnMap(48.84, 2.35)">
<script>
function clickOnMap(lat, lng) {
var event = {latLng: {}};
event.latLng.lat = function() { return lat };
event.latLng.lng = function() { return lng };
google.maps.event.trigger(map, 'click', event);
}
</script>
Clicking on the button will have the same effect as clicking on the map, on the same coordinates.
And of course you can call the function clickOnMap automatically.
Are you helped with this?
Edit: let's say you need more properties of event, like event.feature.getProperty("name");
Try adding something like this:
I don't know what else is expected, but you can keep adding properties like this.
function clickOnMap(lat, lng, name) {
var event = {latLng: {}, feature: {}};
event.latLng.lat = function() { return lat };
event.latLng.lng = function() { return lng };
event.feature.getProperty = function(property) { return name; };
google.maps.event.trigger(map, 'click', event);
}

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.

need to add multiple markers using custom google map api

i was checking out the google map api to integrate in my website.
made this page with what ever i could understand so far.
everything is working fine but there is just one thing, that i want three markers on the same map.
when i am adding more than one markers then the map stops working.
test link : http://goo.gl/X9q92s
you will have a better understanding if u see my link.
this is the code that i got from google map api.
and i edited it to get grey scale map with my desired marker box.
i just want to add two more....
Please help.
<script>
function initialize() {
var myLatlng = new google.maps.LatLng(-25.363882,131.044922);
var mapOptions = {
zoom: 4,
center: myLatlng
}
var map = new google.maps.Map(document.getElementById('map-canvas'), mapOptions);
var marker = new google.maps.Marker({
position: myLatlng,
map: map,
title: 'Hello World!'
});
}
google.maps.event.addDomListener(window, 'load', initialize);
</script>
You should place your "new marker" code into its own function, like so:
function LoadMarkers(name, lat, lng) {
var MarkerLatLng = new google.maps.LatLng(lat, lng);
var MarkerOption = { map: map, position: MarkerLatLng, title: name};
var Marker = new google.maps.Marker(MarkerOption);
}
Putting this into its own function allows you to "refresh" the markers with ease, by simply invoking the function with a timer or some other event. A program I'm working on refreshes the map every few seconds, as my data source is constantly changing with new/removed/updated records that should be reflected immediately on the map. I think this is a great way to do this.
Then, in your program, you can create a loop that shoots the information for each marker in by invoking the LoadMarkers function. I've recently fallen in love with SqlDataReader.
Your loop would iterate through a SqlDataReader and each record read will invoke the script like so:
InvokeScript("LoadMarkers", New Object() {name, lat, lng})
This is a great moment to also add an InfoWindow for each marker.
var infowindow = new google.maps.InfoWindow(
{
content: "Content here"
});
As well as a click listener for the InfoWindows. ;)
google.maps.event.addListener(Marker, 'click', function () {
typeof infoWindowsOpenCurrently !== 'undefined' && infoWindowsOpenCurrently.close(); //If there is an InfoWindow currently open, close it
infowindow.open(map, Marker); //Open a new one for the selected marker
infoWindowsOpenCurrently = infowindow; //Set the new info window to the temporary variable
});
Some might not like this method of using a loop. I like it because I can "personalize" each marker for each record, while personalizing each of their InfoWindows too. In my code above, assume that "name" is a unique ID that lets you specify a specific marker for later use, such as identifying which marker was clicked and which InfoWindow is currently open.

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);
});
}

Google Maps Utility Library v3 - Infoboxes not closing

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).