0

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.

1
  • geocoder.geocode seems async, so the middle iteration seems likely to always run at the end (e.g. after "end-of-iteration"). Commented Jul 3, 2017 at 4:01

3 Answers 3

1

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the comment but it has exact same behaviour as the way I had it. Its going thorugh each iteration and then at the very end its doing the 'middle" part.
0

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)

Comments

0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.