Comment géocoder 20 adresses sans recevoir de réponse OVER_QUERY_LIMIT?


87

En utilisant Google Geocoder v3, si j'essaie de géocoder 20 adresses, j'obtiens un OVER_QUERY_LIMIT à moins que je ne les chronomètre à ~ 1 seconde d'intervalle, mais cela prend 20 secondes avant que mes marqueurs soient tous placés.

Existe-t-il un autre moyen de le faire, autre que de stocker les coordonnées à l'avance?


Est-ce toujours le cas? La seule restriction que je vois dans la documentation est: "une limite de requêtes de 2 500 demandes de géolocalisation par jour". code.google.com/apis/maps/documentation/geocoding/…
russau

6
Il ne s'agit pas du nombre total de requêtes par utilisateur et par jour, mais du nombre de requêtes dans un court laps de temps, comme lors d'une requête en boucle.
Michiel van Oosterhout le

Nous avons une licence commerciale dans notre boutique et nous rencontrons toujours ce problème de ne pas pouvoir traiter plus de 10 demandes par seconde. La seule différence entre une licence commerciale et un développeur régulier est que nous avons une limite de 100 000 appels par jour.
abhi

@michielvoo Avez-vous résolu ce problème? Si oui, merci de bien vouloir m'aider. J'obtiens OVER_QUERY_LIMIT. Ma question dans SO. Fiddle
Prabs

Réponses:


85

Non, il n'y a pas vraiment d'autre moyen: si vous avez de nombreux emplacements et que vous souhaitez les afficher sur une carte, la meilleure solution est de:

  • récupérer la latitude + la longitude, à l'aide du géocodeur, lors de la création d'un emplacement
  • stocker ceux-ci dans votre base de données, à côté de l'adresse
  • et utilisez ces latitude + longitude stockées lorsque vous souhaitez afficher la carte.

Ceci, bien sûr, étant donné que vous avez beaucoup moins de création / modification d'emplacements que de consultations d'emplacements.


Oui, cela signifie que vous devrez faire un peu plus de travail lors de l'enregistrement des emplacements - mais cela signifie aussi:

  • Vous pourrez effectuer une recherche par coordonnées géographiques
    • c'est-à-dire " Je veux une liste de points proches de l'endroit où je suis maintenant "
  • L'affichage de la carte sera beaucoup plus rapide
    • Même avec plus de 20 emplacements dessus
  • Oh, et aussi (last but not least) : cela fonctionnera ;-)
    • Vous atteindrez moins probablement la limite d'appels de géocodeur X en N secondes.
    • Et vous atteindrez moins probablement la limite des appels de géocodeur Y par jour.

Je suis curieux de savoir comment vous pouvez être sûr que les résultats sont corrects après un certain temps (disons, un mois). Les réinterrogez-vous de temps en temps?
Chris

2
Si l'adresse (que vous avez déjà dans votre base de données - sinon vous ne pourriez pas géocoder) ne change pas, les chances sont assez faibles que la latitude / longitude change. Et, bien sûr, chaque fois que l'adresse est modifiée, vous devez ré-interroger le géocodeur, pour obtenir la latitude + la longitude qui correspondent à la nouvelle adresse.
Pascal MARTIN

J'ai stocké le lat / long dans la base de données et je l'ai récupéré à partir de la base de données via AJAX en tant que tableau, mais il devrait ensuite être passé à nouveau à une boucle de script java, plus j'ai reçu 173 emplacements de la base de données. Maintenant, il me montre le même statut OVER_QUERY_LIMIT. S'il vous plaît conseils ...
Prabhu M

20

Vous n'avez pas à attendre une seconde complète pour chaque demande. J'ai trouvé que si j'attends 200 millisecondes entre chaque demande, je peux éviter la réponse OVER_QUERY_LIMIT et l'expérience utilisateur est passable. Avec cette solution, vous pouvez charger 20 éléments en 4 secondes.

$(items).each(function(i, item){

  setTimeout(function(){

    geoLocate("my address", function(myLatlng){
      ...
    });

  }, 200 * i);

}

5
mais (200 * i) signifie que la pause entre chaque demande augmente. Donc, à la troisième demande, c'est 600, puis 800 etc.
Roman

il suffit de supprimer le '* i'
Chris

9
setTimeout l'exécutera une fois. Donc, si j'ai raison, (..., 200 * i) planifiera chaque appel séparé de 200 ms (comme l'a commenté oyatek), ce que gabeodess voulait réaliser. Le courant (..., 200) les exécutera tous en même temps après 200 ms. Ou est-ce que je manque quelque chose?
lepe

@gabeodess - vous devriez faire setIntervalsur le nombre de demandes nécessaires, au lieu de setTimeout, et le régler sur 100- juste au cas où le montant de l'adresse augmenterait à un moment donné le 20montant.
Rob Scott

3
@gabeodess J'ai essayé votre solution mais je reçois toujours OVER_QUERY_LIMIT Fiddle
Prabs

6

Malheureusement, il s'agit d'une restriction du service Google Maps.

Je travaille actuellement sur une application utilisant la fonction de géocodage, et j'enregistre chaque adresse unique par utilisateur. Je génère les informations d'adresse (ville, rue, état, etc.) en fonction des informations renvoyées par Google Maps, puis enregistre également les informations lat / long dans la base de données. Cela vous évite d'avoir à recoder les choses et vous donne des adresses joliment formatées.

Une autre raison pour laquelle vous voulez faire cela est qu'il existe une limite quotidienne sur le nombre d'adresses qui peuvent être géocodées à partir d'une adresse IP particulière. Vous ne voulez pas que votre candidature échoue pour une personne pour cette raison.


2

Je suis confronté au même problème en essayant de géocoder 140 adresses.

Ma solution de contournement a été d'ajouter usleep (100000) pour chaque boucle de la prochaine demande de géocodage. Si l'état de la demande est OVER_QUERY_LIMIT, la durée d'utilisation est augmentée de 50000 et la demande est répétée, et ainsi de suite.

Et bien sûr, toutes les données reçues (lat / long) sont stockées dans un fichier XML pour ne pas exécuter la requête à chaque chargement de la page.


1
Votre réponse est vague, faites-vous référence côté serveur ou s'agit-il de javascript, si c'est ce dernier, usleep n'est pas une fonction et serait donc incorrect, si c'est le premier, alors je vous suggère de modifier votre réponse pour l'indiquer explicitement est côté serveur pour éviter toute ambiguïté.
t0mm13b

1

ÉDITER:

J'ai oublié de dire que cette solution est en pure js, la seule chose dont vous avez besoin est un navigateur qui prend en charge les promesses https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise


Pour ceux qui ont encore besoin d'accomplir cela, j'ai écrit ma propre solution qui combine des promesses avec des délais d'attente.

Code:

/*
    class: Geolocalizer
        - Handles location triangulation and calculations.
        -- Returns various prototypes to fetch position from strings or coords or dragons or whatever.
*/

var Geolocalizer = function () {
    this.queue          = [];     // queue handler..
    this.resolved       = [];
    this.geolocalizer = new google.maps.Geocoder();  
};

Geolocalizer.prototype = {
    /*
        @fn: Localize
        @scope: resolve single or multiple queued requests.
        @params: <array> needles
        @returns: <deferred> object
    */
    Localize: function ( needles ) {
        var that = this;
        // Enqueue the needles.
        for ( var i = 0; i < needles.length; i++ ) {
            this.queue.push(needles[i]);
        }
        // return a promise and resolve it after every element have been fetched (either with success or failure), then reset the queue.
        return new Promise (
            function (resolve, reject) {
                that.resolveQueueElements().then(function(resolved){
                  resolve(resolved);
                  that.queue    = [];
                  that.resolved = [];
                });
            }
        );
    },

    /*
        @fn: resolveQueueElements
        @scope: resolve queue elements.
        @returns: <deferred> object (promise)
    */

    resolveQueueElements: function (callback) {
        var that = this;
        return new Promise(
            function(resolve, reject) {
                // Loop the queue and resolve each element.
                // Prevent QUERY_LIMIT by delaying actions by one second.
                (function loopWithDelay(such, queue, i){
                    console.log("Attempting the resolution of " +queue[i-1]);
                    setTimeout(function(){
                        such.find(queue[i-1], function(res){
                           such.resolved.push(res); 
                        });
                        if (--i) {
                            loopWithDelay(such,queue,i);
                        }
                    }, 1000);
                })(that, that.queue, that.queue.length);

                // Check every second if the queue has been cleared.
                var it = setInterval(function(){
                    if (that.queue.length == that.resolved.length) {
                        resolve(that.resolved);
                        clearInterval(it);
                    }
                }, 1000);
            }
        );
    },

    /*
        @fn: find
        @scope: resolve an address from string
        @params: <string> s, <fn> Callback
    */
    find: function (s, callback) {
        this.geolocalizer.geocode({
            "address": s
        }, function(res, status){
           if (status == google.maps.GeocoderStatus.OK) {
               var r = {
                   originalString:  s,
                   lat: res[0].geometry.location.lat(),
                   lng: res[0].geometry.location.lng()
               };
               callback(r);
           }
            else {
                callback(undefined);
                console.log(status);
                console.log("could not locate " + s);
            }
        });
    }
};

Veuillez noter que c'est juste une partie d'une plus grande bibliothèque que j'ai écrite pour gérer les trucs de Google Maps, donc les commentaires peuvent être déroutants.

L'utilisation est assez simple, mais l'approche est légèrement différente: au lieu de boucler et de résoudre une adresse à la fois, vous devrez passer un tableau d'adresses à la classe et elle gérera la recherche par elle-même, renvoyant une promesse qui , une fois résolu, renvoie un tableau contenant toutes les adresses résolues (et non résolues).

Exemple:

var myAmazingGeo = new Geolocalizer();
var locations = ["Italy","California","Dragons are thugs...","China","Georgia"];
myAmazingGeo.Localize(locations).then(function(res){ 
   console.log(res); 
});

Sortie de la console:

Attempting the resolution of Georgia
Attempting the resolution of China
Attempting the resolution of Dragons are thugs...
Attempting the resolution of California
ZERO_RESULTS
could not locate Dragons are thugs...
Attempting the resolution of Italy

Objet renvoyé:

entrez la description de l'image ici

Toute la magie se produit ici:

(function loopWithDelay(such, queue, i){
                    console.log("Attempting the resolution of " +queue[i-1]);
                    setTimeout(function(){
                        such.find(queue[i-1], function(res){
                           such.resolved.push(res); 
                        });
                        if (--i) {
                            loopWithDelay(such,queue,i);
                    }
                }, 750);
            })(that, that.queue, that.queue.length);

Fondamentalement, il boucle chaque élément avec un délai de 750 millisecondes entre chacun d'eux, donc toutes les 750 millisecondes, une adresse est contrôlée.

J'ai fait quelques tests supplémentaires et j'ai découvert que même à 700 millisecondes, j'obtenais parfois l'erreur QUERY_LIMIT, alors qu'avec 750, je n'ai eu aucun problème.

Dans tous les cas, n'hésitez pas à modifier les 750 ci-dessus si vous vous sentez en sécurité en gérant un délai inférieur.

J'espère que cela aidera quelqu'un dans un proche avenir;)


0

Je viens de tester Google Geocoder et j'ai le même problème que vous. J'ai remarqué que je n'obtiens le statut OVER_QUERY_LIMIT qu'une fois toutes les 12 demandes.J'attends donc 1 seconde (c'est le délai minimum pour attendre) Cela ralentit l'application mais moins d'attendre 1 seconde à chaque demande

info = getInfos(getLatLng(code)); //In here I call Google API
record(code, info);
generated++; 
if(generated%interval == 0) {
holdOn(delay); // Every x requests, I sleep for 1 second
}

Avec la méthode de base holdOn:

private void holdOn(long delay) {
        try {
            Thread.sleep(delay);
        } catch (InterruptedException ex) {
            // ignore
        }
    }

J'espère que ça aide

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.