I am trying to put multiple markers on a Google map. I have JS that builds an array and passes it to the Google function that handles markers.
Problem is, when I try to access the supposed array, I just get the first character as if it's a string.
$(document).ready(function () {
// initialize map to center on florida.
initializeGMap();
var locations = [];
#foreach (var item in Model)
{
<text>
locations.push(#Html.Raw(Json.Encode("'" + item.Name + "'," + item.Location.Latitude + "," + item.Location.Longitude + "")));
</text>
}
addMarker(locations);
});
I've tried several (read: 20+) variations of this including JSON.stringify it before sending, after sending, etc. Here's the function it gets passed too:
function addMarker(locations) {
var locations = JSON.stringify(locations);
alert(locations + '\n' + locations[0][0] + '\n' + locations[0][1]);
var infowindow = new google.maps.InfoWindow();
var marker, i;
for (i = 0; i < locations.length; i++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(locations[i][1], locations[i][2]),
map: map
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent(locations[i][0]);
infowindow.open(map, marker);
}
})(marker, i));
}
alert("done");
}
When it gets to the line with 'locations[x][x]' all I ever get back in '[' which is the first character of the JSON string. It's not being treated at an array.
What am I missing?
I solved it via:
$(document).ready(function () {
// initialize map to center on florida.
initializeGMap();
// serialize model locations
var locationsToPass = #Html.Raw(Json.Encode(Model.Select(x => new { x.Name, x.Location.Latitude, x.Location.Longitude })));
addMarker(locationsToPass);
});
and in the receiving function:
function addMarker(locations) {
var infowindow = new google.maps.InfoWindow();
var marker, i;
for (i = 0; i < locations.length; i++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(locations[i].Latitude, locations[i].Longitude),
map: map
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent(locations[i].Name);
infowindow.open(map, marker);
}
})(marker, i));
}
}
The key to troubleshooting was using this to detect if I was passing an array or not:
variable.constructor === Array
which I got here.
Related
I have a map where initially the markers load coming from the database, Then i have a time based Ajax request which gets the records again after every 1 minute.
Following is the code where i am using setMapOnAll(null) as from the Google maps Documentation, But its not working.
success: function(data){
var positions = [];
$.each(data.riders, function(index, value) {
positions.push({
lat: value.rider_location.lat,
lng: value.rider_location.lng,
content: value.name,
id : value.id
});
});
map.setCenter({
lat: parseInt(positions[0].lat),
lng: parseInt(positions[0].lng)
});
var infowindow = new google.maps.InfoWindow();
var marker,
i;
setMapOnAll(null); //Remove the existing markers
while(positions.length){
positions.pop().setMap(null);
}
for (i = 0; i < positions.length; i++) {
marker = new google.maps.Marker({
position: new google.maps.LatLng(positions[i].lat,positions[i].lng),
map: map,
id : positions[i].id,
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
}
}) (marker, i));
}
}
How can i remove the existing markers before adding new ones.
there is method marker.setMap(null) as google docs https://developers.google.com/maps/documentation/javascript/markers
you need create array of markers then remove all from map in loop by call method marker.setMap(null)
var markers = [];
for (i = 0; i < positions.length; i++) {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(positions[i].lat,positions[i].lng),
map: map,
id : positions[i].id,
});
markers.push(marker);
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
}
}) (marker, i));
}
You should add this function to your code and call it where you add new markers then add new markers. This will delete the previous markers.
function setMapOnAll(map) {
for (var i = 0; i < markers.length; i++) {
markers[i].setMap(map);
}
}
Background to Question
I have an array which includes latitude and longitude values. I have the below code which places a marker for each iteration. I am using a Ruby gem Gon to pass values from the database to javascript. The below is working as expected:
function populateMap(map){
var index;
for (index = 0; index < gon.length; ++index) {
var latlng = new google.maps.LatLng(gon.murals[index].lat, gon.murals[index].long);
var marker = new google.maps.Marker({
position: latlng,
map: map
});
}
}
However I want to have an info window for each marker with the address. This is done by reverse geo-coding. https://developers.google.com/maps/documentation/javascript/examples/geocoding-reverse.
The below code works for reverse geocoding 1 marker:
function getReverseGeocodingData(geocoder, map, infowindow) {
var latlng = new google.maps.LatLng(gon.murals[0].lat, gon.murals[0].long);
geocoder.geocode({'location': latlng}, function(results, status) {
if (status === 'OK') {
if (results[1]) {
var marker = new google.maps.Marker({
position: latlng,
map: map
});
google.maps.event.addListener(marker, 'mouseover', function () {
infowindow.open(map, marker);
document.getElementById("address").innerHTML = results[1].formatted_address ;
});
} else {
window.alert('No results found');
}
} else {
window.alert('Geocoder failed due to: ' + status);
}
});
}
Actual Question
When I add the for loop to the reverse geo0code function it only places the marker of the last iteration.
function populateMapTest(map, geocoder, infowindow){
var index;
for (index = 0; index < gon.murals.length; ++index) {
var latlng = new google.maps.LatLng(gon.murals[index].lat, gon.murals[index].long);
alert("start of iteration: " + index);
geocoder.geocode({'location': latlng}, function(results, status){
alert("middle of iteration: " + index);
if (status === 'OK') {
if (results[1]) {
var marker = new google.maps.Marker({
position: latlng,
map: map
});
google.maps.event.addListener(marker, 'mouseover', function () {
infowindow.open(map, marker);
document.getElementById("address").innerHTML = results[1].formatted_address ;
});
} else {
window.alert('No results found');
}
} else {
window.alert('Geocoder failed due to: ' + status);
}
});
alert("end of iteration: " + index);
}
}
For each iteration the alerts are in the following order: Start of iteration, end of iteration, middle of iteration. It seems to be skipping over the code contained in the geocoder brackets till all the iterations are done. I think?
Any help appreciated.
This sounds like a class closure problem, which relates to the scope of a variable that is declared in a high scope but used in functions that are in a lower scope and persist longer than the higher scope where the variable was actually declared.
Change:
var index;
for (index = 0; index < gon.murals.length; ++index) {
to:
for (let index = 0; index < gon.murals.length; ++index) {
This will give index block level scope and each iteration of the loop will have its own value for index. Instead of all iterations of the loop sharing the same index value, each will get its own.
It does seem like a closure issue. But I think it could be because of the variable latlng instead of the index, (or both).
var latlng = new google.maps.LatLng(gon.murals[index].lat, gon.murals[index].long);
The latlng above is updated throughout the loop but eventually the function uses only the last iteration's latlng. The variables inside the closure (the "middle" function) are referenced and will all be updated to the last value when the function actually executes. (I guess a different way of thinking about it, is that it really only looks at the value during execution instead of declaration)
var marker = new google.maps.Marker({
position: latlng,
map: map
});
And at the end, the marker would just be created at the same position, index times.
As an example, the code below will print ten 9s even if you expect it to print an increasing x
function foo() {
for (let index = 0; index < 10; ++index) {
var x = index;
setTimeout(function bar() {
console.log(x)
}, 10)
}
}
foo()
But this will print it correctly if it was immediately invoked (but of course, this isn't an option for your case)
function foo() {
for (let index = 0; index < 10; ++index) {
var x = index;
setTimeout(function bar() {
console.log(x)
}(), 10)
}
}
foo()
You could move the latlng declaration inside the middle function. (Do check the value of the index too though, because that suffers the same issue)
How about this :
geocoder.geocode({'location': latlng}, (function(indexCopy){
return function(results, status) {
alert("middle of iteration: " + indexCopy);
if (status === 'OK') {
if (results[1]) {
var marker = new google.maps.Marker({
position: latlng,
map: map
});
google.maps.event.addListener(marker, 'mouseover', function () {
infowindow.open(map, marker);
document.getElementById("address").innerHTML = results[1].formatted_address ;
});
} else {
window.alert('No results found');
}
} else {
window.alert('Geocoder failed due to: ' + status);
}
};
})(index));
Just a thought...
I am developing a web page for viewing vehicle locations using gps data.
I have make the back end working fine with the help of Mr. Aruna a genius in stack Overflow. Now I need a help for updating my google map infowindow. marker is updating its location no issue with that. while clicking it is not updating is current speed and another info according to that.
Below is the code in
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
getMarkers();
function getMarkers() {
var infowindow = null;
$.get('/markers', {}, function (res, resp) {
console.dir(res);
for (var i = 0, len = res.length; i < len; i++) {
var content = res[i].name + " S1: " + res[i].speed * 1.6 + '<br />' + "D: " + res[i].lastupdate
infowindow = new google.maps.InfoWindow({
content: "A"
});
//Do we have this marker already?
if (markerStore.hasOwnProperty(res[i].id)) {
console.log('just move it...');
markerStore[res[i].id].setPosition(new google.maps.LatLng(res[i].position.lat, res[i].position.long));
//markerStore[res[i].id].setMap(map);
// Not sure below block and its not updating
google.maps.event.addListener(markerStore[res[i].id], 'click', (function (marker, content, infowindow) {
return function () {
infowindow.setContent(content);
infowindow.open(map, markerStore[res[i].id]);
};
})(markerStore[res[i].id], content, infowindow));
} else {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(res[i].position.lat, res[i].position.long),
title: res[i].name,
map: map
});
google.maps.event.addListener(marker, 'click', (function (marker, content, infowindow) {
return function () {
infowindow.setContent(content);
infowindow.open(map, marker);
};
})(marker, content, infowindow));
//var marker = new google.maps.Marker({
// position: new google.maps.LatLng(res[i].position.lat, res[i].position.long),
// title: res[i].name,
// map: map
//});
//google.maps.event.addListener(marker, 'click', (function (marker, content, infowindow) {
// return function () {
// infowindow.setContent(content);
// infowindow.open(map, marker);
// };
//})(marker, content, infowindow));
markerStore[res[i].id] = marker;
console.log(marker.getTitle());
}
}
window.setTimeout(getMarkers, INTERVAL);
}, "json");
}
Please help me ...
Your click event listener is called asynchronously, long after your for loop has completed. So the value of i is not what you expect.
This is easy to fix (assuming there are not other problems as well).
Take all of the code inside the for loop body and make it a function. I'll call the function addMarker( item ), but of course you can use any name you want.
Everywhere you have res[i] in that function, change it to item. Then shorten the for loop so it contains only a single line: addMarker( res[i] );.
So now your code looks like this:
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
getMarkers();
function getMarkers() {
var infowindow = null;
$.get('/markers', {}, function (res, resp) {
console.dir(res);
for (var i = 0, len = res.length; i < len; i++) {
addMarker( res[i] );
}
function addMarker( item ) {
var content = item.name + " S1: " + item.speed * 1.6 + '<br />' + "D: " + item.lastupdate
infowindow = new google.maps.InfoWindow({
content: "A"
});
//Do we have this marker already?
if (markerStore.hasOwnProperty(item.id)) {
console.log('just move it...');
markerStore[item.id].setPosition(new google.maps.LatLng(item.position.lat, item.position.long));
//markerStore[item.id].setMap(map);
// Not sure below block and its not updating
google.maps.event.addListener(markerStore[item.id], 'click', (function (marker, content, infowindow) {
return function () {
infowindow.setContent(content);
infowindow.open(map, markerStore[item.id]);
};
})(markerStore[item.id], content, infowindow));
} else {
var marker = new google.maps.Marker({
position: new google.maps.LatLng(item.position.lat, item.position.long),
title: item.name,
map: map
});
google.maps.event.addListener(marker, 'click', (function (marker, content, infowindow) {
return function () {
infowindow.setContent(content);
infowindow.open(map, marker);
};
})(marker, content, infowindow));
//var marker = new google.maps.Marker({
// position: new google.maps.LatLng(item.position.lat, item.position.long),
// title: item.name,
// map: map
//});
//google.maps.event.addListener(marker, 'click', (function (marker, content, infowindow) {
// return function () {
// infowindow.setContent(content);
// infowindow.open(map, marker);
// };
//})(marker, content, infowindow));
markerStore[item.id] = marker;
console.log(marker.getTitle());
}
}
window.setTimeout(getMarkers, INTERVAL);
}, "json");
}
I didn't check for any other errors in your code, but this will fix the specific problem you are asking about.
To learn more about this, read up on JavaScript closures.
i have two stores in database and am trying to get stores in google map marker is pointing that two stores.i have ng-click in that info window to pass id here ng-click is not working is there any idea to pass id through ng-click
.controller('MapCtrl', [
'$scope', '$http', '$location', '$window',
function ($scope, $http, $location, $window) {
$http.get('****').success(function (data, dealers, response) {
function initialize() {
var serverData = data;
$scope.locations = [];
for (var i = 0; i < serverData.length; i++) {
var modal = [
data[i].Store_Name, data[i].S_Location.Latitude, data[i].S_Location.Longitude, i, 'images/arrow.svg', data[i].S_Address];
$scope.locations.push(modal);
}
console.log($scope.locations);
//---------------------------------------------------------
//console i am getting like this
var locations = [
['nokia store', '12.971599', '77.594563', '1', 'images/arrow.svg.svg', '55a78953815356700bee698f'],
['samsung store', '12.9065534', '77.5774802', '2', 'images/arrow.svg.svg', '55a786d1815356700bee6982'], ];
//----------------------------------------------------------
var map = new google.maps.Map(document.getElementById('map'), {
zoom: 13,
center: new google.maps.LatLng(12.9667, 77.5667),
mapTypeId: google.maps.MapTypeId.ROADMAP
});
var infowindow = new google.maps.InfoWindow();
var marker, i;
for (i = 0; i < $scope.locations.length; i++) {
//console.log($scope.locations[i][1]);
marker = new google.maps.Marker({
position: new google.maps.LatLng($scope.locations[i][1], $scope.locations[i][2]),
map: map,
icon: $scope.locations[i][4],
animation: google.maps.Animation.DROP,
});
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent('<div class="marker-box"><div class="marker-title">' + $scope.locations[i][0] + '</div><input type="button" value="Book Now" name="Book Now" ng-click="getid(' + $scope.locations[i][5] + ') "/></div>');
infowindow.open(map, marker);
}
})(marker, i));
}
$scope.map = map;
}
$scope.getid(id) {
console.log(id);
}
});
since 3 days i'm trying i can't able to solve.help me out
So in your code:
google.maps.event.addListener(marker, 'click', (function (marker, i) {
return function () {
infowindow.setContent('<div class="marker-box"><div class="marker-title">' + $scope.locations[i][0] + '</div><input type="button" value="Book Now" name="Book Now" ng-click="getid(' + $scope.locations[i][5] + ') "/></div>');
infowindow.open(map, marker);
}
})(marker, i));
your call back function does not run until marker is clicked. By the time it's clicked, the scope probably does not know what "i" is. My guess is "i" becomes $scope.locations.length+1.
One things you can try:
put the above code in a secondary function, then call it from the main thread instead of running it directly from main thred. IE:
main code:{
//your code
for (i = 0; i < $scope.locations.length; i++){
function_name(parameter 1, i);
}
function function_name= above code;
Note the above is sudo code, please implement it and try it. Hope this solves it.
I have a FF error about the info window in GM. Here is the source code:
var lats;
var longs;
var k;
function initialize() {
//parentArray is an object where the elements of the parent page are stored
var parentArray = window.parent.params;
lats = parentArray["lat"].replace(/^\|+|\|+$/g, '').split("|");
longs = parentArray["long"].replace(/^\|+|\|+$/g, '').split("|");
k = parentArray["keys"].replace(/^\|+|\|+$/g, '').split("|");
var myLatlng = new google.maps.LatLng(parseFloat(lats[0]), parseFloat(longs[0]));
var myOptions = {
zoom: 20,
center: myLatlng,
mapTypeId: google.maps.MapTypeId.ROADMAP
}
var map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
//contentString is built based on the array passed by the parent page
for (var i = 0; i < lats.length; i++) {
var contentString = '<div id="content"' + i + '><b>' + k[i] + '</b>';
for (var f in parentArray)
if ((f !== "long") && (f !== "lat") && (f !== "keys") && (parentArray[f].substring(0, 1) !== "<")) {
contentString += '<br />' + f + ': ' + parentArray[f];
}
contentString += '<br /></div>';
var infowindow = new google.maps.InfoWindow({
content: contentString
});
var marker = new google.maps.Marker({
position: new google.maps.LatLng(parseFloat(lats[i]), parseFloat(longs[i])),
map: map,
title: 'Position'
});
createInfoWindow(marker, contentString);
function createInfoWindow(m, content) {
google.maps.event.addListener(m, 'click', function () {
infowindow.setContent(content);
infowindow.open(map, m);
});
}
}
}
params is an array with information and k is an array of keys for the markers on the google map. Does anybody know why do I have a FF error for this code?
Sample Data For params:
params['foo']: bar
params['keys']: "Start Position|End Position"
params['lat']: "12.5323703|13.5323703"
params['long']: "14.5786987|15.5786987"
EDIT:
The Error is: createInfoWindow is not defined
Thanks in advance,
Lajos Arpad.
You are defining your method inside a loop (this is bad on its own..) and you call the method before you define it ..
just moving the call below the definition fixes the issue..
function createInfoWindow(m, content) {
google.maps.event.addListener(m, 'click', function () {
infowindow.setContent(content);
infowindow.open(map, m);
});
}
createInfoWindow(marker, contentString);
Demo at http://jsfiddle.net/gaby/gdLVd/
But you should really move the definition of the createInfoWindow method somewhere else..
Better demo at http://jsfiddle.net/gaby/gdLVd/1/