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?

Michiel van Oosterhout
źródło
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.
Pascal MARTIN
źródło
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);

}
gabeodess
źródło
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.

Zachary Wright
źródło
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.

szary
źródło
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;)

briosheje
źródło
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

Hugues
źródło