Wie kann ich 20 Adressen geocodieren, ohne eine Antwort von OVER_QUERY_LIMIT zu erhalten?

87

Wenn ich mit Google Geocoder v3 versuche, 20 Adressen zu geocodieren, erhalte ich ein OVER_QUERY_LIMIT, es sei denn, ich habe einen Zeitabstand von ~ 1 Sekunde, aber dann dauert es 20 Sekunden, bis alle meine Markierungen platziert sind.

Gibt es eine andere Möglichkeit, als die Koordinaten im Voraus zu speichern?

Michiel van Oosterhout
quelle
ist das noch der fall Die einzige Einschränkung, die ich in der Dokumentation sehe, ist: "Ein Abfragelimit von 2.500 Geolokalisierungsanforderungen pro Tag". code.google.com/apis/maps/documentation/geocoding/…
russau
6
Es geht nicht um die Gesamtzahl der Abfragen pro Benutzer und Tag, sondern um die Anzahl der Abfragen in kurzer Zeit, wie bei Abfragen in einer Schleife.
Michiel van Oosterhout
Wir haben eine Geschäftslizenz in unserem Shop und stoßen immer noch auf das Problem, dass nicht mehr als 10 Anfragen pro Sekunde bearbeitet werden können. Der einzige Unterschied zwischen einer Geschäftslizenz und einem regulären Entwickler besteht darin, dass wir maximal 100.000 Anrufe pro Tag tätigen.
Abhi
@michielvoo Hast du das gelöst? Wenn ja, dann helfen Sie mir bitte. Ich bekomme OVER_QUERY_LIMIT. Meine Frage in SO. Fiddle
Prabs

Antworten:

85

Nein, es gibt keinen anderen Weg: Wenn Sie viele Standorte haben und diese auf einer Karte anzeigen möchten, ist die beste Lösung:

  • Rufen Sie den Breiten- und Längengrad mit dem Geocoder ab, wenn ein Standort erstellt wird
  • Speichern Sie diese in Ihrer Datenbank neben der Adresse
  • und verwenden Sie diese gespeicherten Längen- und Breitengrade, wenn Sie die Karte anzeigen möchten.

Dies ist natürlich in Anbetracht dessen, dass Sie viel weniger Standorte erstellen / ändern als Konsultationen von Standorten.


Ja, es bedeutet, dass Sie beim Speichern der Speicherorte etwas mehr Arbeit leisten müssen - aber es bedeutet auch:

  • Sie können nach geografischen Koordinaten suchen
    • dh „ Ich möchte eine Liste von Punkten , die in der Nähe sind , wo ich bin jetzt
  • Das Anzeigen der Karte wird viel schneller sein
    • Auch mit mehr als 20 Standorten
  • Oh, und auch (last but not least) : das wird funktionieren ;-)
    • Es ist weniger wahrscheinlich, dass Sie das Limit von X Geocoder-Aufrufen in N Sekunden erreichen.
    • Und Sie werden weniger wahrscheinlich das Limit von Y-Geocoder-Anrufen pro Tag erreichen.
Pascal MARTIN
quelle
Ich bin gespannt, wie Sie sicher sein können, dass die Ergebnisse nach einiger Zeit (z. B. einem Monat) korrekt sind. Fragen Sie sie von Zeit zu Zeit erneut ab?
Chris
2
Wenn sich die Adresse (die Sie bereits in Ihrer Datenbank haben - sonst könnten Sie nicht geocodieren) nicht ändert, ist die Wahrscheinlichkeit ziemlich gering, dass sich der Breiten- / Längengrad ändert. Und natürlich sollten Sie bei jeder Änderung der Adresse den Geocoder erneut abfragen, um den Längen- und Breitengrad zu ermitteln, der der neuen Adresse entspricht.
Pascal MARTIN
Ich habe das lat / long in der Datenbank gespeichert und es über AJAX als Array aus der Datenbank abgerufen, aber es sollte dann erneut an eine Java-Skriptschleife übergeben werden, außerdem habe ich 173 Speicherorte von der Datenbank erhalten. Jetzt zeigt es mir den gleichen OVER_QUERY_LIMIT-Status. Bitte beraten Sie ...
Prabhu M
20

Sie müssen tatsächlich nicht eine volle Sekunde auf jede Anfrage warten. Ich habe festgestellt, dass ich, wenn ich zwischen jeder Anforderung 200 Millisekunden warte, die Antwort OVER_QUERY_LIMIT vermeiden kann und die Benutzererfahrung passabel ist. Mit dieser Lösung können Sie 20 Elemente in 4 Sekunden laden.

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

  setTimeout(function(){

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

  }, 200 * i);

}
gabeodess
quelle
5
aber (200 * i) bedeutet, dass die Pause zwischen jeder Anforderung zunimmt. Also auf 3. Anfrage ist es 600, dann 800 usw.
Roman
entferne einfach das '* i'
Chris
9
setTimeout führt es einmal aus. Wenn ich also richtig liege, plant (..., 200 * i) jeden Anruf, der durch 200 ms getrennt ist (wie oyatek kommentierte), was gabeodess erreichen wollte. Der Strom (..., 200) führt alle nach 200 ms gleichzeitig aus. Oder fehlt mir etwas?
Lepe
@gabeodess - Sie sollten stattdessen setIntervaldie Anzahl der benötigten Anfragen bearbeiten setTimeoutund diese festlegen 100- nur für den Fall, dass der Adressbetrag den Betrag irgendwann in der Zukunft verlängern wird 20.
Rob Scott
3
@gabeodess Ich habe Ihre Lösung ausprobiert, erhalte aber immer noch OVER_QUERY_LIMIT Fiddle
Prabs
6

Leider ist dies eine Einschränkung des Google Maps-Dienstes.

Ich arbeite derzeit an einer Anwendung mit der Geokodierungsfunktion und speichere jede eindeutige Adresse auf Benutzerbasis. Ich generiere die Adressinformationen (Stadt, Straße, Bundesland usw.) basierend auf den von Google Maps zurückgegebenen Informationen und speichere dann auch die Lat / Long-Informationen in der Datenbank. Dies verhindert, dass Sie Dinge neu codieren müssen, und gibt Ihnen gut formatierte Adressen.

Ein weiterer Grund, warum Sie dies tun möchten, besteht darin, dass die Anzahl der Adressen, die von einer bestimmten IP-Adresse aus geokodiert werden können, täglich begrenzt ist. Sie möchten aus diesem Grund nicht, dass Ihre Bewerbung für eine Person fehlschlägt.

Zachary Wright
quelle
2

Ich habe das gleiche Problem beim Versuch, 140 Adressen zu geocodieren.

Meine Problemumgehung bestand darin, für jede Schleife der nächsten Geokodierungsanforderung usleep (100000) hinzuzufügen . Wenn der Status der Anforderung OVER_QUERY_LIMIT lautet, wird der Schlaf um 50000 erhöht und die Anforderung wiederholt und so weiter.

Und natürlich werden alle empfangenen Daten (lat / long) in einer XML-Datei gespeichert, damit die Anforderung nicht jedes Mal ausgeführt wird, wenn die Seite geladen wird.

grau
quelle
1
Ihre Antwort ist vage, beziehen Sie sich auf die Serverseite oder handelt es sich um dieses Javascript. Wenn es das letztere ist, ist usleep keine Funktion und wäre daher falsch, wenn es das erstere ist, dann schlage ich vor, dass Sie Ihre Antwort ändern, um dies explizit anzugeben ist serverseitig, um Mehrdeutigkeiten zu vermeiden.
t0mm13b
1

BEARBEITEN:

Ich habe vergessen zu sagen, dass diese Lösung in reinem js vorliegt. Sie benötigen lediglich einen Browser, der Versprechen unterstützt. Https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise


Für diejenigen, die dies noch tun müssen, habe ich meine eigene Lösung geschrieben, die Versprechen mit Timeouts kombiniert.

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);
            }
        });
    }
};

Bitte beachten Sie, dass dies nur ein Teil einer größeren Bibliothek ist, die ich für Google Maps geschrieben habe. Daher können Kommentare verwirrend sein.

Die Verwendung ist recht einfach, der Ansatz ist jedoch etwas anders: Anstatt jeweils eine Adresse zu schleifen und aufzulösen, müssen Sie ein Array von Adressen an die Klasse übergeben, die die Suche selbst abwickelt und ein Versprechen zurückgibt, das Wenn aufgelöst, wird ein Array zurückgegeben, das alle aufgelösten (und nicht aufgelösten) Adressen enthält.

Beispiel:

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

Konsolenausgabe:

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

Zurückgegebenes Objekt:

Geben Sie hier die Bildbeschreibung ein

Die ganze Magie passiert hier:

(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);

Grundsätzlich durchläuft es jedes Element mit einer Verzögerung von 750 Millisekunden zwischen jedem Element, daher wird alle 750 Millisekunden eine Adresse gesteuert.

Ich habe einige weitere Tests durchgeführt und festgestellt, dass ich selbst bei 700 Millisekunden manchmal den Fehler QUERY_LIMIT bekam, während ich bei 750 überhaupt kein Problem hatte.

In jedem Fall können Sie die oben genannten 750 bearbeiten, wenn Sie sich durch eine geringere Verzögerung sicher fühlen.

Hoffe das hilft jemandem in naher Zukunft;)

briosheje
quelle
0

Ich habe gerade Google Geocoder getestet und habe das gleiche Problem wie Sie. Ich habe festgestellt, dass ich den Status OVER_QUERY_LIMIT nur einmal alle 12 Anforderungen erhalte. Ich warte also 1 Sekunde (das ist die minimale Wartezeit). Dies verlangsamt die Anwendung, aber weniger als 1 Sekunde bei jeder Anforderung

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
}

Mit der grundlegenden HoldOn-Methode:

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

Ich hoffe es hilft

Hugues
quelle