43

I'm hitting an issue that is WELL discussed in these forums, but none of the recommendations seem to be working for me so I'm looking for some full javascript that works when saved as an html file.

The issue is I keep hitting the OVER_QUERY_LIMIT error when trying to geocode > 11 locations on a Google Map using the V3 APIs called by Javascript. I understand that there is a limit to the rate at which you can call the geocoder (as well as the daily limit on total volume), so I need to introduce a pause in between each result in the array.

Any help very much appreciated.

Here is my code:

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
var geocoder;
var map;
var wait = false;


  function initialize() {
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(51.32, 0.5);



var myOptions = {
  zoom: 8,
  center: latlng,
  mapTypeId: google.maps.MapTypeId.ROADMAP
}
map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
codeAddress('KT16 8LA' + ', UK');
codeAddress('LS8 2LQ' + ', UK');
codeAddress('NE13 8AF' + ', UK');
codeAddress('KT12 2BE' + ', UK');
codeAddress('W1W 8AN' + ', UK');
codeAddress('EC3N 2LS' + ', UK');
codeAddress('BS9 3BH' + ', UK');
codeAddress('KA10 6LZ' + ', UK');
codeAddress('EC1V 9BW' + ', UK');
codeAddress('WD18 8YN' + ', UK');
codeAddress('HA3 6DQ' + ', UK');
codeAddress('W1U 3PL' + ', UK');
codeAddress('W1T 7QL' + ', UK');
codeAddress('W1S 1TD' + ', UK');
codeAddress('SW1X 8NX' + ', UK');
codeAddress('LE2 8ET' + ', UK');
codeAddress('BA3 4BH' + ', UK');
codeAddress('AL3 8JP' + ', UK');
codeAddress('DE55 4QJ' + ', UK');
codeAddress('W6 0QT' + ', UK');
codeAddress('LA1 1PP' + ', UK');
codeAddress('SW16 4DH' + ', UK');
codeAddress('WC2N 6DF' + ', UK');
codeAddress('RM6 6LS' + ', UK');
codeAddress('S25 3QZ' + ', UK');
codeAddress('WC2H 7LR' + ', UK');
codeAddress('BH24 1DW' + ', UK');
codeAddress('EC2N 6AR' + ', UK');
codeAddress('W1U 2FA' + ', UK');
codeAddress('B60 3DX' + ', UK');    
}

  function codeAddress(vPostCode) {
if (geocoder) {
  geocoder.geocode( { 'address': "'" + vPostCode + "'"}, function(results, status) {
    if (status == google.maps.GeocoderStatus.OK) {
      map.setCenter(results[0].geometry.location);
      var marker = new google.maps.Marker({
          map: map, 
          position: results[0].geometry.location
      });
    } else {
      alert("Geocode was not successful for the following reason: " + status);
    }
  });
}
}

</script>
<body style="margin:0px; padding:0px;" onload="initialize()">
<div id="map_canvas" style="width:100%; height:90%"></div>
</body>

EDIT: This is what I've tried to do to get it to pause/wait in the relevant section, but it doesn't do anything:

function codeAddress(vPostCode) {
    if (geocoder) {
    while (wait) { /* Just wait. */ };
      geocoder.geocode( { 'address': "'" + vPostCode + "'"}, function(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
          map.setCenter(results[0].geometry.location);
          var marker = new google.maps.Marker({
              map: map, 
              position: results[0].geometry.location
          });
        /* When geocoding "fails", see if it was because of over quota error: */
        } else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) { 
        wait = true;
        setTimeout("wait = true", 2000);
        //alert("OQL: " + status);
        } else {
          alert("Geocode was not successful for the following reason: " + status);
        }
      });
    }
  }
Artjom B.
  • 61,146
  • 24
  • 125
  • 222
user1573571
  • 433
  • 1
  • 4
  • 5
  • I think if you made your request like $.getJSON... don't have limit – Diogo Cid Sep 01 '16 at 15:57
  • Possible duplicate of [OVER\_QUERY\_LIMIT while using google maps](http://stackoverflow.com/questions/3529746/over-query-limit-while-using-google-maps) – xomena Nov 06 '16 at 13:22

6 Answers6

45

Nothing like these two lines appears in Mike Williams' tutorial:

    wait = true;
    setTimeout("wait = true", 2000);

Here's a Version 3 port:

http://acleach.me.uk/gmaps/v3/plotaddresses.htm

The relevant bit of code is

  // ====== Geocoding ======
  function getAddress(search, next) {
    geo.geocode({address:search}, function (results,status)
      { 
        // If that was successful
        if (status == google.maps.GeocoderStatus.OK) {
          // Lets assume that the first marker is the one we want
          var p = results[0].geometry.location;
          var lat=p.lat();
          var lng=p.lng();
          // Output the data
            var msg = 'address="' + search + '" lat=' +lat+ ' lng=' +lng+ '(delay='+delay+'ms)<br>';
            document.getElementById("messages").innerHTML += msg;
          // Create a marker
          createMarker(search,lat,lng);
        }
        // ====== Decode the error status ======
        else {
          // === if we were sending the requests to fast, try this one again and increase the delay
          if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) {
            nextAddress--;
            delay++;
          } else {
            var reason="Code "+status;
            var msg = 'address="' + search + '" error=' +reason+ '(delay='+delay+'ms)<br>';
            document.getElementById("messages").innerHTML += msg;
          }   
        }
        next();
      }
    );
  }
Andrew Leach
  • 12,945
  • 1
  • 40
  • 47
  • Also works great for routing, when batching just recurse into that batch call to Google after increasing delay. – Brent May 28 '14 at 16:42
  • what is this.. @Andrew Leach would you kindly tell me what are these delay and next address..Could you please solve this [Fiddle](http://jsfiddle.net/0v4oo3fn/2/) – Prabs May 01 '15 at 11:16
  • `delay` is delay in milliseconds; `nextAddress` is the address counter: if an address fails, unset the increment and increase the delay in order that it can be tried again. The full implementation is on my site at the link given: you have nothing like this code in your Fiddle. – Andrew Leach May 01 '15 at 11:23
  • Is it possible to set the center of the map after the first result? – user43251 May 18 '15 at 13:08
  • @user43251 Of course. Just put the call to do that in the right place. The full implementation is at the link given; this code is just what introduces the delays. – Andrew Leach May 18 '15 at 13:22
  • Thanks, I couldn't seem to find the right place though..? – user43251 May 18 '15 at 13:26
  • @user43251 New line 48, after the call to `createMarker`: `if (nextAddress==1) {map.setCenter(new google.maps.LatLng(lat,lng))};` – Andrew Leach May 18 '15 at 13:39
  • @AndrewLeach Where I can see the full code for this implementation?. – Carmoreno Sep 26 '16 at 22:44
  • @CarMoreno There's a link in the answer. Just `View Source`. – Andrew Leach Sep 26 '16 at 22:47
  • Try it in 2017 Jan', not working. Still have the same OVER_QUERY_LIMIT error even with the delay. – Paul Leclerc Jan 05 '17 at 10:45
  • 1
    @PaulLeclerc My example still works for me, although the geocoder has changed so fewer addresses are actually found. If you are using a shared IP address, the query limit is shared between all users on that IP address. That means that users of mobile networks can fall foul of the limit before they start. – Andrew Leach Jan 05 '17 at 10:48
  • Yes sry it finally works after some custom updates. I believed that it was because the post and the code are old and the GoogleMap rules may have changed to be more restrictive. Thank you. – Paul Leclerc Jan 05 '17 at 13:38
  • I'm hitting the maps API service on postman and its humanly not possible to hit the service more than 2-3 times a second, I do this continuously over a time period of, say 10 seconds, should I still expect to see this error? – Awani Jan 29 '18 at 10:08
  • @Awani There are many causes, including sharing IP addresses; and Google may change their limiting criteria from time to time. It would be advisable to implement something like this to cater for the unexpected happening whenever you are submitting a series of requests. – Andrew Leach Jan 29 '18 at 13:22
  • @AndrewLeach http://acleach.me.uk/gmaps/v3/plotaddresses.htm link not working. `Mixed Content: The page at 'https://acleach.me.uk/gmaps/v3/plotaddresses.htm' was loaded over HTTPS, but requested an insecure script 'http://maps.google.com/maps/api/js?sensor=false'. This request has been blocked; the content must be served over HTTPS.` – Bishan Apr 23 '18 at 06:33
  • @Bishan I have updated all my files (many of which were eight years old) and the particular issue of mixed content should be fixed. – Andrew Leach Apr 26 '18 at 17:37
  • thank you everyone this has helped me and my team – kevin lopez Feb 15 '22 at 16:25
9

The general answer to this question is:

Don't geocode known locations every time you load your page. Geocode them off-line and use the resulting coordinates to display the markers on your page.

The limits exist for a reason.

If you can't geocode the locations off-line, see this page (Part 17 Geocoding multiple addresses) from Mike Williams' v2 tutorial which describes an approach, port that to the v3 API.

geocodezip
  • 158,664
  • 13
  • 220
  • 245
  • Thanks for the response. I can't geocode them off-line, but the page you've referenced doesn't give any example I can apply. All I really need is a delay/pause step that will work. – user1573571 Aug 03 '12 at 10:11
  • Did you see this: view-source:http://econym.org.uk/gmap/example_geomulti.htm, it is written for the v2 API, but the concept should apply to the v3 API. – geocodezip Aug 03 '12 at 10:26
  • Have tried that concept but without success. It may well work in V2 and V3 but I can't get it to apply successfully to the above Javascript. – user1573571 Aug 03 '12 at 11:21
  • You could post a link to your map (or a jsfiddle) where you tried to get it to work and failed, maybe someone can see where you are going wrong. – geocodezip Aug 03 '12 at 11:40
  • Yep, based on the above code it would work for the first 80-ish page views and then break using the free usage. 3300 page views if your paying for it. – Mark Broadhurst May 07 '14 at 13:35
4

Here I have loaded 2200 markers. It takes around 1 min to add 2200 locations. https://jsfiddle.net/suchg/qm1pqunz/11/

//function to get random element from an array
    (function($) {
        $.rand = function(arg) {
            if ($.isArray(arg)) {
                return arg[$.rand(arg.length)];
            } else if (typeof arg === "number") {
                return Math.floor(Math.random() * arg);
            } else {
                return 4;  // chosen by fair dice roll
            }
        };
    })(jQuery);

//start code on document ready
$(document).ready(function () {
    var map;
    var elevator;
    var myOptions = {
        zoom: 0,
        center: new google.maps.LatLng(35.392738, -100.019531), 
        mapTypeId: google.maps.MapTypeId.ROADMAP
    };
    map = new google.maps.Map($('#map_canvas')[0], myOptions);

    //get place from inputfile.js
    var placesObject = place;
    errorArray = [];

  //will fire 20 ajax request at a time and other will keep in queue
    var queuCounter = 0, setLimit = 20; 

  //keep count of added markers and update at top
  totalAddedMarkers = 0;

  //make an array of geocode keys to avoid the overlimit error
    var geoCodKeys = [
                    'AIzaSyCF82XXUtT0vzMTcEPpTXvKQPr1keMNr_4',
                    'AIzaSyAYPw6oFHktAMhQqp34PptnkDEdmXwC3s0',
                    'AIzaSyAwd0OLvubYtKkEWwMe4Fe0DQpauX0pzlk',
                    'AIzaSyDF3F09RkYcibDuTFaINrWFBOG7ilCsVL0',
                    'AIzaSyC1dyD2kzPmZPmM4-oGYnIH_0x--0hVSY8'                   
                ];

  //funciton to add marker
    var addMarkers = function(address, queKey){
        var key = jQuery.rand(geoCodKeys);
        var url = 'https://maps.googleapis.com/maps/api/geocode/json?key='+key+'&address='+address+'&sensor=false';

        var qyName = '';
        if( queKey ) {
            qyName = queKey;
        } else {
            qyName = 'MyQueue'+queuCounter;
        }

        $.ajaxq (qyName, {
            url: url,
            dataType: 'json'
        }).done(function( data ) {
                    var address = getParameterByName('address', this.url);
                    var index = errorArray.indexOf(address);
                    try{
                        var p = data.results[0].geometry.location;
                        var latlng = new google.maps.LatLng(p.lat, p.lng);
                        new google.maps.Marker({
                            position: latlng,
                            map: map
                        });
                        totalAddedMarkers ++;

            //update adde marker count
                        $("#totalAddedMarker").text(totalAddedMarkers);
                        if (index > -1) {
                            errorArray.splice(index, 1);
                        }
                    }catch(e){
                        if(data.status = 'ZERO_RESULTS')
                            return false;

            //on error call add marker function for same address
            //and keep in Error ajax queue
                        addMarkers( address, 'Errror' );
                        if (index == -1) {
                            errorArray.push( address );
                        }
                    }
        });

    //mentain ajax queue set
        queuCounter++;
        if( queuCounter == setLimit ){
            queuCounter = 0;
        }
    }

  //function get url parameter from url string
    getParameterByName = function ( name,href )
    {
      name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
      var regexS = "[\\?&]"+name+"=([^&#]*)";
      var regex = new RegExp( regexS );
      var results = regex.exec( href );
      if( results == null )
        return "";
      else
        return decodeURIComponent(results[1].replace(/\+/g, " "));
    }

  //call add marker function for each address mention in inputfile.js
    for (var x = 0; x < placesObject.length; x++) {
        var address = placesObject[x]['City'] + ', ' + placesObject[x]['State'];
        addMarkers(address);
    }
});
Matheno
  • 4,112
  • 6
  • 36
  • 53
sachin gavas
  • 177
  • 6
  • 2
    The ToS has certain restrictions you should be aware of: "No use beyond transaction limits and usage policies. If your Maps API Implementation generates a high volume of transactions, Google reserves the right to set transaction limits. Google also reserves the right to set other usage policies in the Documentation from time to time. If you want to engage in use outside these transaction limits or usage policies, you can purchase more usage capacity through the Maps API Standard pricing plan, or you can contact the Google Maps sales team for licensing options to address your needs." – xomena Nov 07 '16 at 11:08
  • @sachingavas I am getting name.replace is not a function error message – QWERTY Dec 16 '17 at 12:22
3

Using "setInterval" & "clearInterval" fixes the problem:

function drawMarkers(map, markers) {
    var _this = this,
        geocoder = new google.maps.Geocoder(),
        geocode_filetrs;

    _this.key = 0;

    _this.interval = setInterval(function() {
        _this.markerData = markers[_this.key];

        geocoder.geocode({ address: _this.markerData.address }, yourCallback(_this.markerData));

        _this.key++;

        if ( ! markers[_this.key]) {
            clearInterval(_this.interval);
        }

    }, 300);
}
Ronen
  • 31
  • 1
1

this post was made a while ago, but it provides an answer that did not solve the problem regarding reaching the limit of requests in an iteration for me, so I publish this, to help who else has not served.

My environment happened in Ionic 3.

Instead of making a "pause" in the iteration, I ocurred the idea of ​​iterating with a timer, this timer has the particularity of executing the code that would go in the iteration, but will run every so often until it is reached the maximum count of the "Array" in which we want to iterate.

In other words, we will consult the Google API in a certain time so that it does not exceed the limit allowed in milliseconds.

// Code to start the timer
    this.count= 0;
    let loading = this.loadingCtrl.create({
      content: 'Buscando los mejores servicios...'
    });
    loading.present();
    this.interval = setInterval(() => this.getDistancias(loading), 40);
// Function that runs the timer, that is, query Google API
  getDistancias(loading){
    if(this.count>= this.datos.length){
      clearInterval(this.interval);
    } else {
      var sucursal = this.datos[this.count];
      this.calcularDistancia(this.posicion, new LatLng(parseFloat(sucursal.position.latitude),parseFloat(sucursal.position.longitude)),sucursal.codigo).then(distancia => {
    }).catch(error => {
      console.log('error');
      console.log(error);
    });
    }
    this.count += 1;
  }
  calcularDistancia(miPosicion, markerPosicion, codigo){
    return new Promise(async (resolve,reject) => {
      var service = new google.maps.DistanceMatrixService;
      var distance;
      var duration;
      service.getDistanceMatrix({
        origins: [miPosicion, 'salida'],
        destinations: [markerPosicion, 'llegada'],
        travelMode: 'DRIVING',
        unitSystem: google.maps.UnitSystem.METRIC,
        avoidHighways: false,
        avoidTolls: false
      }, function(response, status){
        if (status == 'OK') {
          var originList = response.originAddresses;
          var destinationList = response.destinationAddresses;
          try{
            if(response != null && response != undefined){
              distance = response.rows[0].elements[0].distance.value;
              duration = response.rows[0].elements[0].duration.text;
              resolve(distance);
            }
          }catch(error){
            console.log("ERROR GOOGLE");
            console.log(status);
          }
        }
      });
    });
  }

I hope this helps!

I'm sorry for my English, I hope it's not an inconvenience, I had to use the Google translator.

Regards, Leandro.

0

You are using setTimeout wrong way. The (one of) function signature is setTimeout(callback, delay). So you can easily specify what code should be run after what delay.

var codeAddress = (function() {
    var index = 0;
    var delay = 100;

    function GeocodeCallback(results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            map.setCenter(results[0].geometry.location);
            new google.maps.Marker({ map: map, position: results[0].geometry.location, animation: google.maps.Animation.DROP });
            console.log(results);
        }
        else alert("Geocode was not successful for the following reason: " + status);
    };

    return function(vPostCode) {
        if (geocoder) setTimeout(geocoder.geocode.bind(geocoder, { 'address': "'" + vPostCode + "'"}, GeocodeCallback), index*delay);
        index++;
    };
})();

This way, every codeAddress() call will result in geocoder.geocode() being called 100ms later after previous call.

I also added animation to marker so you will have a nice animation effect with markers being added to map one after another. I'm not sure what is the current google limit, so you may need to increase the value of delay variable.

Also, if you are each time geocoding the same addresses, you should instead save the results of geocode to your db and next time just use those (so you will save some traffic and your application will be a little bit quicker)

Buksy
  • 11,571
  • 9
  • 62
  • 69
  • How can I implement this code into my code? https://jsfiddle.net/395rdhd8/ I tried a lot of things, but without any luck. `STATUS OVER_QUERY_LIMIT` is showing up before my `STATUS.OK` in my console. But why? Thanks. – Appel Mar 03 '17 at 15:12
  • your usage of setTimeout is wrong, setTimeout is not putting javascript in sleep for X seconds, it is used to run specified function X seconds later – Buksy Mar 05 '17 at 22:09
  • How could I fix it? Javascript is not my strongest skill. Thanks! – Appel Mar 06 '17 at 07:49
  • One of the options is to create recursive function which would call itself if request wasnt successfull, something like this: https://jsfiddle.net/395rdhd8/1/ – Buksy Mar 07 '17 at 12:36