I have the following code and everytime I click on one of the markers, I get the first infowindow. I have 181 infowindows and 181 markers. Any help appreciated.
var i=0;
$.each(in_locations,function(k,v){
console.log(v);
var marker_latlng = new google.maps.LatLng(v.latitude, v.longitude);
markers[i] = new google.maps.Marker({
position: marker_latlng,
map: map,
title:"here is my title" + v.id
});
infowindows[i] = new google.maps.InfoWindow({
content: "here is some content string" + v.id,
});
google.maps.event.addListener(markers[i], 'click', function() {
alert('number of infowindows: ' + infowindows.length + ' and value is: ' + i);
infowindows[i].open(map,markers[i]);
});
i++;
});
thx
Does the alert tell you that the value is always 181? You need to wrap your handler declaration in a closure to stop it using the external value of i (which will always be 181 once the loop completes)
(function(i){
google.maps.event.addListener(markers[i], 'click', function() {
alert('number of infowindows: ' + infowindows.length + ' and value is: ' + i);
infowindows[i].open(map,markers[i]);
});
}(i));
Edit: Because you're using jQuery's $.each function, which gives each iteration its own scope (because each iteration is calling a function) you can also get around your issue by creating a local variable, say j, which grabs the external value of i.
var j = i;
google.maps.event.addListener(markers[i], 'click', function() {
alert('number of infowindows: ' + infowindows.length + ' and value is: ' + j);
infowindows[j].open(map,markers[j]);
});
(This works because every iteration a new j is created, whereas before you only had 1 i because you declared it outside the iterating function.)
UPDATED
demo: http://jsbin.com/obunap/5
$.each(in_locations, function(i, item){
console.log(i);
var marker_latlng = new google.maps.LatLng(item.latitude, item.longitude);
var markers = new google.maps.Marker({
position: marker_latlng,
map: map,
title:"here is my title" + item.id
});
var infowindows = new google.maps.InfoWindow({
content: "here is some content string" + item.id,
});
google.maps.event.addListener(markers, 'click', function() {
alert('number of infowindows: ' + in_locations.length + ' and value is: ' + i);
infowindows.open(map, markers);
});
});
explanation? you are already inside a loop $.each so you don't need to increment an i++ iterator
Related
I am currently developing a web application that would alert if a systems generated marker is outside a polygon, but my function is only working in 1 polygon, it does not work in other polygon. Is there something wrong in my loop or what did i miss ?
See image here: sample image
Here is my function.
function checkInPolygon(marker) {
var infowindow = new google.maps.InfoWindow();
for (var x = 0; x < mpa_polygon.length; x++) {
var html = google.maps.geometry.poly.containsLocation(marker.getPosition(), mpa_polygon[x]) ? 'inside' : 'outside';
console.log(mpa_polygon[x]);
}
infowindow.setContent(html);
infowindow.open(map, marker);
}
Full code implementation:
function displaySalesMan() {
var names = [];
var ids = [];
var dateMPA = [];
var longlat = [];
var header = [];
var loc = [
[]
];
var newdata = [];
$.ajax({
url: "/geofencing/GeofencingAPI.php",
type: "GET",
data: {
"type": "view_salesmanDashboard_gensan_COLOR",
"filter_Date": getDate()
},
dataType: "html",
crossDomain: true,
cache: false,
success: function(response) {
var data = JSON.parse(response);
if (response == 'No records are generated on selected salesman and date!') {
alert('No records are found in selected date!');
DeleteMarkers();
} else {
console.log(data.length);
for (var x = 0; x < data.length; x++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(data[x].latitude, data[x].longitude),
map: map,
icon: 'http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=%E2%80%A2|' + data[x].TERRCOLOR.substr(1)
});
>
i call the
function here with a parameter ** marker **
checkInPolygon(marker);
markers.push(marker);
google.maps.event.addListener(marker, 'click', (function(marker, x) {
return function() {
var contentString = "<div style='overflow: hidden; margin: 0 auto;'><div class='modal-header' style='text-align: center;'><div class=''><img style='height: 120px; width:120px;' alt='salesmanPic' src='img/jomar.png'></div></div>" +
"<div class='table-responsive'>" +
"<table class='table table-condensed'>" +
"<tr><td>Salesman:</td><td class='bold'>" + data[x].Salesman + "</td></tr>" +
"<tr><td>Customer:</td><td>" + data[x].Customer + "</td></tr>" +
"<tr><td>Location:</td><td>" + data[x].longitude + ' ' + data[x].latitude + "</td></tr>" +
"<tr><td>Delivery Date:</td><td>" + data[x].deliveryDate + "</td></tr>" +
"</table>" +
"</div>" +
"</div>";
infoWindow.setContent(contentString);
infoWindow.open(map, marker);
}
})(marker, x));
} //end for
} //else
} //on succjess
}) //ajax close tag
//checkerForSalesman(markers, mpa_polygon);
infoWindow = new google.maps.InfoWindow;
} //display salesman funciton
From what I understood of your code, for each marker you going to ask "is this in first polygon? yes o no" then you insert the answer in the infowindow of that marker...then going to ask for the same marker "is this in the second polygon? yes o no" and so on, always overwriting the previous answer for the previous polygon....that is why it only works for the last polygon inserted.
So the solution here would be find a way to keep separate answers for each polygon, or select which polygon are you aiming.
To be clear if a marker which is only one point, can only have 2 posibles states (true or false) and you have 3 polygons..that breaks the equation. cause for instance in the image you provided, all the markers shows good results if your reference is the red polygon...but you need to know which of the 3 polygons you are referring to.
Hope it makes sense
I am working on a project in Laravel.
This is my script that I have done to display a map with multiple markers with Google Maps. I have 2 json objects that I am retrieving one with the locations and one with autos. A location can have many autos, so it is a one to many relationship.
What i am trying to do is that in the infowindow.setContent to display a description of the location and the autos that belong to the location.
The map, the markers with the proper locations are displayed correctly. But in the infowindow.setContent are displayed the description and the title of the location and the last record of the autos that belong to that location.
When the script is run the console.log displays the information as it has to be. that means that my loop for autos is working fine but when i pass the result to the infowindow.setContent it reads only the last record.
This is my first time asking for help here, so excuse me if I'm not clear enough. I would be very grateful to anyone that responds to me.
My Script:
<script type="text/javascript">
var locations = #json($locations);
var autos = #json($autos);
var infowindow = new google.maps.InfoWindow();
var mymap = new GMaps({
el: '#mymap',
center: {lat: 41.323029, lng: 19.817856},
zoom:6
});
$.each( locations, function( index, value ){
mymap.addMarker({
lat: value.lat,
lng: value.long,
title: value.title,
click: function(e) {
for (var auto in autos)
{
if(autos[auto].location_id == value.id)
{
var autoBrands =autos[auto].brand;
console.log(autoBrands);
}
}
infowindow.setContent('<div><strong>' + autoBrands + '</strong><br></div>'+ '<div><strong>' + value.title + '</strong><br></div>' + '<div><strong>' + value.description + '</strong><br></div>');
infowindow.open(mymap, this);
}
});
});
</script>
It looks like you just reassigning value of autoBrands inside your loop. Why don't you create it as an array object? Something like
<script type="text/javascript">
var locations = #json($locations);
var autos = #json($autos);
var infowindow = new google.maps.InfoWindow();
var mymap = new GMaps({
el: '#mymap',
center: {lat: 41.323029, lng: 19.817856},
zoom:6
});
$.each( locations, function( index, value ){
mymap.addMarker({
lat: value.lat,
lng: value.long,
title: value.title,
click: function(e) {
var autoBrands = [];
for (var auto in autos)
{
if(autos[auto].location_id == value.id)
{
autoBrands.push(autos[auto].brand);
console.log(autoBrands);
}
}
infowindow.setContent('<div><strong>' + autoBrands.join(",") + '</strong><br></div>'+ '<div><strong>' + value.title + '</strong><br></div>' + '<div><strong>' + value.description + '</strong><br></div>');
infowindow.open(mymap, this);
}
});
});
Hope this helps!
You're looping over all your locations, adding a marker for each. However you only have one infowindow variable. On each iteration of the loop, you're updating what the content of the same infowindow will be, so it ends up just getting the content from the very last iteration.
Have a separate function that opens the infowindow in response to the user's click, something like this:
$.each( locations, function( index, value ){
mymap.addMarker({
lat: value.lat,
lng: value.long,
title: value.title,
click: function(e) {
for (var auto in autos)
{
if(autos[auto].location_id == value.id)
{
var autoBrands =autos[auto].brand;
console.log(autoBrands);
}
}
openInfowindow(
'<div><strong>' + autoBrands + '</strong><br></div>'+ '<div><strong>' + value.title + '</strong><br></div>' + '<div><strong>' + value.description + '</strong><br></div>',
this
);
}
});
});
function openInfowindow(content, marker)
{
infowindow.setContent(content);
infowindow.open(mymap, marker);
}
For now, on a button click I have it so that it takes in data from two textboxes, and uses it to
1) append tweets to a panel, and
2) drop pins on a map.
My next step is to have it so that on the button click, it geodecodes a location, and does the same thing. I feel like my jquery.click function is getting really big, and wanted to know if there was a standard way to "separate" it out to make it look prettier and more readable. Can you typically have javascript functions within a jquery file that are called upon, or what is the way to go?
Here is my current jquery file. As you can see it's very big but what happens is straight forward: searchbutton on click takes some values, and sets up a new map in that location, then I access my web server's information, append it to a panel, and also drop pins on a map.
$(function () {
$("#search-button").click(function() {
// variables for google maps
var LatValue = parseFloat($("#searchLat").val());
var LonValue = parseFloat($("#searchLon").val());
var myLatLng = {lat: LatValue, lng: LonValue};
var map = new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 12,
center: myLatLng
});
$.getJSON(
"http://localhost:3000/tw",
{
geoSearchWord: $("#searchme").val(),
geoSearchWordLat: $("#searchLat").val(),
geoSearchWordLon: $("#searchLon").val(),
geoSearchWordRad: $("#searchRadius").val()
}
).done(function (result) {
$("#fromTweets").empty();
console.log(result);
for (i = 0; i < result.statuses.length; i++) {
//Print out username and status
$("#fromTweets").append('<b>' + "Username: " + '</b>' + result.statuses[i].user.screen_name + '<br/>');
$("#fromTweets").append('<b>' + "Tweet: " + '</b>' + result.statuses[i].text + '<br/>');
$("#fromTweets").append('<b>' + "Created at: " + '</b>' + result.statuses[i].created_at + '<br/>');
if (result.statuses[i].geo !== null) {
//Print out the geolocation
$("#fromTweets").append('<b>' + "GeoLocation: " + '</b>' + "Lat: " + result.statuses[i].geo.coordinates[0] + " Lon: " + result.statuses[i].geo.coordinates[1] + '<br/>'+ '<br/>');
//dropping a new marker on the map for each tweet that has lat/lon values
//Multiplying by i * 0.0005 to space them out in case they are from the same gelocation while still holding
//the integrity of their location.
LatValue = parseFloat(result.statuses[i].geo.coordinates[0] + i*0.0005);
LonValue = parseFloat(result.statuses[i].geo.coordinates[1] + i*0.0005);
myLatLng = {lat: LatValue, lng: LonValue};
var newMarker = new google.maps.Marker({
position: myLatLng,
map: map,
animation: google.maps.Animation.DROP,
});
} else {
$("#fromTweets").append('<b>' + "GeoLocation: " + '</b>' + "Cannot be identified" + '<br/>' + '<br/>')
}
}
});
});
The most simple and obvious thing you can do it so split your code by extracting independent logical blocks to functions:
Just something like this:
var map;
function combineTweetsAjaxRequestData()
{
return {
geoSearchWord: $("#searchme").val(),
geoSearchWordLat: $("#searchLat").val(),
geoSearchWordLon: $("#searchLon").val(),
geoSearchWordRad: $("#searchRadius").val()
};
}
function createGMap()
{
return new google.maps.Map(document.getElementById('map-canvas'), {
zoom: 12,
center: {
lat: parseFloat($("#searchLat").val()),
lng: parseFloat($("#searchLon").val())
}
});
}
function createGMarker(coords)
{
var coordsFixed = {
lat: parseFloat(coords[0] + i * 0.0005),
lng: parseFloat(coords[1] + i * 0.0005)
};
return new google.maps.Marker({
position: coordsFixed,
map: map,
animation: google.maps.Animation.DROP,
});
}
function clearInfo() {
$("#fromTweets").empty();
}
function appendInfo(title, text)
{
$("#fromTweets").append('<b>' + title + ':</b> ' + text + '<br/>');
}
function processTweet(tw)
{
appendInfo('Username', tw.user.screen_name);
appendInfo('Tweet', tw.text);
appendInfo('Created at', tw.created_at);
if (tw.geo !== null) {
var twCoords = tw.geo.coordinates;
appendInfo('GeoLocation', "Lat: " + twCoords[0] + " Lon: " + twCoords[1]);
createGMarker(twCoords);
} else {
appendInfo('GeoLocation', "Cannot be identified")
}
}
function loadTweets() {
$.getJSON(
"http://localhost:3000/tw",
combineTweetsAjaxRequestData()
).done(function (result) {
clearInfo();
console.log(result);
result.statuses.forEach(processTweet);
});
}
$(document).ready(function () {
map = createGMap();
$("#search-button").click(function() {
loadTweets();
});
});
Now, it can be easily read as a text. Your code should be readable and understandable from the first glance. Even better, if a non-developer can read it and understand some basic concepts.
What happens when the page is loaded? We create a Google map control and load tweets
How do we load tweets? We make a AJAX request by combining request data from inputs
What happens when it is loaded? We clear out current information and process every tweet
How do we process a single tweet? We output some basic information. Then, we output geolocation if it is available. Otherwise, we output an error.
Now, if you need to add information to another source, you won't extend or modify your loadTweets method - you will extend or modify appendInfo method, because the logics of information output is encapsulated here.
I have a json object infoCentros which I use to construct the map, like this:
for ( var i = 0; i < infoCentros.length; i++ ) {
var centro = infoCentros[i];
var lat = centro.cordenadas.lat;
var lon = centro.cordenadas.long;
if (lat && lon) {
c++;
latlon = new google.maps.LatLng(lat, lon);
var moptions = {
position: latlon,
map: $project.gmap
}
moptions.icon = theme_uri + '/images/marker.png';
var marker = new google.maps.Marker(moptions);
$project.mapMarkers.push(marker);
google.maps.event.addListener(marker, 'click', function() {
$project.mapInfoWindow.setContent(
'<div class="sescam-info-window">' +
'<h3>' + centro.nombre + '</h3>' +
'<p>' + centro.lugar + '</h3>' +
'<p>Coordinador</p>' +
'<p>' + centro.coordinador.nombre + '</p>' +
'<p>' + centro.coordinador.email + '</p>' +
'<p>Responsable</p>' +
'<p>' + centro.responsable.nombre + '</p>' +
'<p>' + centro.responsable.email + '</p>'
+ '</div>'
);
$project.mapInfoWindow.open($project.gmap, marker);
});
$project.mapBounds.extend(latlon);
}
}
It seems to work fine, but if I have 5 markers, It doesn't matter which one I click, the infowindow always corresponds to the last item (position and content),
Any idea what am I missing? I thought that passing marker to the addListener would Do the trick..
Problem is with third argument of google.maps.event.addListener. That anonymous function contains the variable from the parent scope which when evaluated with marker click will always take the last value assigned. Look for closure for more details. However you may able to get the desired behaviour by using "bind" feature of javascript function prototype in below manner:
//inside for loop
google.maps.event.addListener(marker, 'click', handleMarkerClick.bind(undefined, marker, i));
//other codes if any...
defining handleClick
function handleMarkerClick(marker, index) {
if (typeof infowindow === 'undefined') {
infowindow = new google.maps.InfoWindow({});
}
var data = infoCentros[index]//helpful data
//create content with dynamic data
infowindow.setContent("dynamic content");
infowindow.open(marker.getMap(), marker);//modify as per your requirement
}
Duncan is right, he means you have to attach the listener outside of your for loop. It's not specific to Google Maps, it's how javascript works. Try searching for "javascript closures for loop" or visit this link - Closures in Javascript for a simple explanation.
I'm trying to create a group of markers with info box attached. However, no matter which marker I click, it always opens the info box of the last detailmarker. Anyone know why? Please help.
var stepDisplay = new google.maps.InfoWindow();
function AddDetailMarker(map, itinerary) {
var markers = [];
for (var i = 1; i < itinerary.Legs.length; i++) {
var position = new google.maps.LatLng(itinerary.Legs[i].BusStop.Latitude, itinerary.Legs[i].BusStop.Longitude);
var title = itinerary.Legs[i].BusStop.Code + ": " + itinerary.Legs[i].BusStop.Location + " " + itinerary.Legs[i].BusStop.Street + ", Quận " + itinerary.Legs[i].BusStop.Ward;
var detailmarker = new google.maps.Marker({
position: position,
map: map,
title: title,
icon: "/Content/img/customized_marker/" + "blue" + "/" + "bus-stop2" + ".png"
});
google.maps.event.addListener(detailmarker, 'click', function () {
stepDisplay.setContent(title);
stepDisplay.open(map, detailmarker);
});
markers[i-1] = detailmarker;
}
}
Edit: possible dublicate of Google maps infowindow showing on wrong marker. I've tried all the solutions I found here and none works.
Yes, this is exactly the same problem as the other one you linked to, and the solution for your code is the same - put the code to create each marker into a function, and call that function in your loop:
var stepDisplay = new google.maps.InfoWindow();
function AddDetailMarker(map, itinerary) {
for (var i = 1; i < itinerary.Legs.length; i++) {
addLegMarker( map, itinerary.Legs[i] );
}
}
function addLegMarker( map, leg ) {
var position = new google.maps.LatLng(leg.BusStop.Latitude, leg.BusStop.Longitude);
var title = leg.BusStop.Code + ": " + leg.BusStop.Location + " " + leg.BusStop.Street + ", Quận " + leg.BusStop.Ward;
var detailmarker = new google.maps.Marker({
position: position,
map: map,
title: title,
icon: "/Content/img/customized_marker/" + "blue" + "/" + "bus-stop2" + ".png"
});
google.maps.event.addListener(detailmarker, 'click', function () {
stepDisplay.setContent(title);
stepDisplay.open(map, detailmarker);
});
}
Do you see why that fixes it? The title and detailmarker are now specific to each invocation of addLegMarker(). In the original code, there was only a single copy of each of these variables, shared among all markers.