Jak geokodować 20 adresów bez otrzymywania odpowiedzi OVER_QUERY_LIMIT?


87

Używając Google Geocoder v3, jeśli spróbuję geokodować 20 adresów, otrzymam OVER_QUERY_LIMIT, chyba że ustawię je w odstępie około 1 sekundy, ale wtedy zajmuje to 20 sekund, zanim wszystkie moje znaczniki zostaną umieszczone.

Czy jest na to inny sposób niż wcześniejsze zapisanie współrzędnych?


czy nadal tak jest? Jedyne ograniczenie, jakie widzę w dokumentacji, to: „limit zapytań do 2500 żądań geolokalizacji dziennie”. code.google.com/apis/maps/documentation/geocoding/ ...
russau

6
Nie chodzi o całkowitą liczbę zapytań na użytkownika dziennie, chodzi o liczbę zapytań w krótkim czasie, na przykład podczas wykonywania zapytań w pętli.
Michiel van Oosterhout

Posiadamy licencję biznesową w naszym sklepie i nadal mamy problem z nieobsługiwaniem więcej niż 10 żądań na sekundę. Jedyna różnica między licencją biznesową a zwykłym programistą polega na tym, że mamy bardzo limit 100 000 połączeń dziennie.
abhi

@michielvoo Czy rozwiązałeś ten problem? Jeśli tak, to uprzejmie pomóż mi. Otrzymuję OVER_QUERY_LIMIT. Moje pytanie w SO. Fiddle
Prabs

Odpowiedzi:


85

Nie, tak naprawdę nie ma innego wyjścia: jeśli masz wiele lokalizacji i chcesz je wyświetlić na mapie, najlepszym rozwiązaniem jest:

  • pobierz szerokość i długość geograficzną za pomocą geokodera, gdy zostanie utworzona lokalizacja
  • przechowuj je w swojej bazie danych wraz z adresem
  • i użyj tych zapisanych szerokości i długości geograficznej, kiedy chcesz wyświetlić mapę.

Wynika to oczywiście z faktu, że tworzenie / modyfikowanie lokalizacji jest o wiele mniejsze niż w przypadku konsultacji lokalizacji.


Tak, oznacza to, że będziesz musiał wykonać trochę więcej pracy podczas zapisywania lokalizacji - ale oznacza to również:

  • Będziesz mógł wyszukiwać według współrzędnych geograficznych
    • tj. „ Chcę uzyskać listę punktów, które są blisko miejsca, w którym jestem teraz
  • Wyświetlanie mapy będzie znacznie szybsze
    • Nawet z ponad 20 lokalizacjami
  • Aha, a także (na koniec) : to zadziała ;-)
    • Jest mniej prawdopodobne, że przekroczysz limit X wywołań geokodera w ciągu N sekund.
    • Z mniejszym prawdopodobieństwem osiągniesz limit dziennych połączeń z geokoderem Y.

Ciekaw jestem, skąd można mieć pewność, że wyniki są prawidłowe po jakimś czasie (powiedzmy po miesiącu). Czy od czasu do czasu odpytujesz je ponownie?
Chris

2
Jeśli adres (który już masz w swojej bazie danych - w przeciwnym razie nie byłbyś w stanie geokodować) nie zmienia się, istnieje niewielkie prawdopodobieństwo, że szerokość / długość geograficzna ulegnie zmianie. I oczywiście za każdym razem, gdy adres jest modyfikowany, należy ponownie zapytać geokodera, aby uzyskać szerokość i długość geograficzną odpowiadające nowemu adresowi.
Pascal MARTIN

Przechowałem lat / long w DB i pobieram je z DB przez AJAX jako tablicę, ale powinno to być ponownie przekazane do pętli skryptu java, co więcej, otrzymałem 173 lokalizacje z DB. Teraz pokazuje mi ten sam stan OVER_QUERY_LIMIT. Proszę o radę ...
Prabhu M

20

W rzeczywistości nie musisz czekać pełnej sekundy na każde żądanie. Zauważyłem, że jeśli czekam 200 milisekund między każdym żądaniem, jestem w stanie uniknąć odpowiedzi OVER_QUERY_LIMIT, a wrażenia użytkownika są zadowalające. Dzięki takiemu rozwiązaniu załadujesz 20 pozycji w 4 sekundy.

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

  setTimeout(function(){

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

  }, 200 * i);

}

5
ale (200 * i) oznacza, że ​​przerwa między każdym żądaniem rośnie. Więc na trzecie żądanie to 600, potem 800 itd.
Roman

po prostu usuń „* i”
Chris

9
setTimeout wykona go raz. Więc jeśli mam rację, (..., 200 * i) zaplanuje każde połączenie oddzielone 200 ms (jak skomentował oyatek), co chciała osiągnąć Gabeodess. Obecny (..., 200) wykona je wszystkie w tym samym czasie po 200ms. A może coś mi brakuje?
lepe

@gabeodess - powinieneś zrobić setIntervalna liczbie potrzebnych żądań, zamiast setTimeouti ustawić to na 100- na wypadek, gdyby kwota adresu w przyszłości zwiększyła tę 20kwotę.
Rob Scott

3
@gabeodess Wypróbowałem twoje rozwiązanie, ale nadal otrzymuję OVER_QUERY_LIMIT Fiddle
Prabs

6

Niestety jest to ograniczenie usługi map Google.

Obecnie pracuję nad aplikacją korzystającą z funkcji geokodowania i zapisuję każdy unikalny adres dla każdego użytkownika. Generuję informacje adresowe (miasto, ulica, województwo itp.) Na podstawie informacji zwracanych przez mapy Google, a następnie zapisuję również informacje o długości i szerokości geograficznej w bazie danych. Zapobiega to konieczności ponownego kodowania i zapewnia ładnie sformatowane adresy.

Innym powodem, dla którego chcesz to zrobić, jest dzienny limit liczby adresów, które można geokodować z określonego adresu IP. Nie chcesz, aby Twoja aplikacja została odrzucona przez osobę z tego powodu.


2

Mam ten sam problem, próbując geokodować 140 adresów.

Moim rozwiązaniem było dodanie usleep (100000) dla każdej pętli następnego żądania geokodowania. Jeśli status żądania to OVER_QUERY_LIMIT, uśpienie jest zwiększane o 50000 i żądanie jest powtarzane i tak dalej.

I dlatego wszystkie otrzymane dane (szerokość / długość) są przechowywane w pliku XML, aby nie uruchamiać żądania za każdym razem, gdy strona się ładuje.


1
Twoja odpowiedź jest niejasna, czy odnosisz się do po stronie serwera, czy też jest to javascript, jeśli to drugie, usleep nie jest funkcją i dlatego byłoby niepoprawne, jeśli jest to pierwsze, proponuję zmienić odpowiedź, aby wyraźnie to stwierdzić jest po stronie serwera, aby uniknąć niejasności.
t0mm13b

1

EDYTOWAĆ:

Zapomniałem powiedzieć, że to rozwiązanie jest w czystym js, jedyne, czego potrzebujesz, to przeglądarka obsługująca obietnice https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise


Dla tych, którzy wciąż muszą to osiągnąć, napisałem własne rozwiązanie, które łączy obietnice z limitami czasu.

Kod:

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

Zwróć uwagę, że to tylko część większej biblioteki, którą napisałem do obsługi map Google, dlatego komentarze mogą być mylące.

Użycie jest dość proste, jednak podejście jest nieco inne: zamiast zapętlać i rozwiązywać jeden adres na raz, będziesz musiał przekazać do klasy tablicę adresów, która sama obsłuży wyszukiwanie, zwracając obietnicę, która , po rozwiązaniu, zwraca tablicę zawierającą cały rozwiązany (i nierozwiązany) adres.

Przykład:

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

Wyjście konsoli:

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

Obiekt zwrócony:

wprowadź opis obrazu tutaj

Cała magia dzieje się tutaj:

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

Zasadniczo zapętla każdy element z opóźnieniem 750 milisekund między każdym z nich, stąd co 750 milisekund adres jest kontrolowany.

Wykonałem kilka dalszych testów i odkryłem, że nawet po 700 milisekundach czasami otrzymywałem błąd QUERY_LIMIT, podczas gdy przy 750 nie miałem żadnego problemu.

W każdym razie możesz edytować powyższe 750, jeśli uważasz, że jesteś bezpieczny, obsługując mniejsze opóźnienie.

Mam nadzieję, że to pomoże komuś w najbliższej przyszłości;)


0

Właśnie przetestowałem Google Geocoder i mam ten sam problem, co ty. Zauważyłem, że otrzymuję status OVER_QUERY_LIMIT tylko raz na 12 żądań, więc czekam 1 sekundę (to jest minimalne opóźnienie oczekiwania) Spowalnia aplikację, ale mniej niż 1 sekundę na każde żądanie

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
}

W przypadku podstawowej metody holdOn:

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

Mam nadzieję, że to pomoże

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.