Як мені геокодувати 20 адрес, не отримавши відповіді OVER_QUERY_LIMIT?


87

Використовуючи Google Geocoder v3, якщо я намагаюся геокодувати 20 адрес, я отримую OVER_QUERY_LIMIT, якщо не визначу час, щоб вони складали ~ 1 секунду, але тоді потрібно 20 секунд, перш ніж всі мої маркери будуть розміщені.

Чи є інший спосіб зробити це, окрім збереження координат заздалегідь?


це все ще так? Єдине обмеження, яке я бачу в документації: "обмеження на запит 2500 запитів геолокації на день". code.google.com/apis/maps/documentation/geocoding/…
russau

6
Справа не в загальній кількості запитів на одного користувача за день, а в кількості запитів за короткий проміжок часу, як, наприклад, при запиті в циклі.
Michiel van Oosterhout

У нас є ліцензія на ведення бізнесу в нашому магазині, і ми все ще стикаємося з цією проблемою, оскільки не можемо обробити більше 10 запитів в секунду. Єдина відмінність між бізнес-ліцензією та звичайним розробником полягає в тому, що у нас дуже обмежено 100 000 дзвінків на день.
abhi

@michielvoo Ви це вирішили? Якщо так, то будь ласка, допоможіть мені. Я отримую OVER_QUERY_LIMIT. Моє запитання SO. Скрипка
Прабс

Відповіді:


85

Ні, насправді не існує іншого шляху: якщо у вас багато місць і ви хочете відобразити їх на карті, найкращим рішенням буде:

  • отримати географічну широту + довготу за допомогою геокодера, коли створюється місце
  • зберігайте їх у своїй базі даних поряд з адресою
  • і використовуйте збережені широту + довготу, коли ви хочете відобразити карту.

Це, звичайно, зважаючи на те, що у вас набагато менше створюється / змінюється місцезнаходження, ніж у вас є консультації щодо місцезнаходжень.


Так, це означає, що вам доведеться трохи більше попрацювати, зберігаючи місця, але це також означає:

  • Ви зможете шукати за географічними координатами
    • тобто " я хочу список точок, які знаходяться поблизу того місця, де я зараз перебуваю "
  • Відображення карти буде набагато швидшим
    • Навіть маючи на ньому понад 20 локацій
  • Ох, і ще (останнє, але не менш важливе) : це спрацює ;-)
    • Ви менш імовірно досягнете межі викликів геокодера X за N секунд.
    • І ви з меншою ймовірністю досягнете межі Y дзвінків геокодера на день.

Мені цікаво, як ви можете бути впевнені в правильності результатів через деякий час (скажімо, місяць). Ви повторно запитуєте їх раз у раз?
Кріс

2
Якщо адреса (яку ви вже маєте у своїй БД - інакше ви б не змогли здійснити геокодування) не зміниться, досить низька ймовірність зміни широти / довготи. І, звичайно, кожного разу, коли адресу змінюється, вам слід повторно запитувати геокодер, щоб отримати широту + довготу, які відповідають новій адресі.
Паскаль МАРТІН

Я зберігав lat / long у БД і отримував його з БД за допомогою AJAX як масив, але потім він повинен був би знову передатися в цикл сценарію Java, ще більше, я отримав 173 розташування від БД. Тепер він показує мені той самий статус OVER_QUERY_LIMIT. Будь ласка, порада ...
Prabhu M

20

Насправді вам не доведеться чекати цілу секунду для кожного запиту. Я виявив, що якщо я зачекаю 200 мілісекунд між кожним запитом, я зможу уникнути відповіді OVER_QUERY_LIMIT, і взаємодія з користувачем буде прохідною. За допомогою цього рішення ви можете завантажити 20 предметів за 4 секунди.

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

  setTimeout(function(){

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

  }, 200 * i);

}

5
але (200 * i) означає, що пауза між кожним запитом збільшується. Отже, за 3-м запитом це 600, потім 800 і т. Д.
Роман

просто видаліть "* i"
Кріс,

9
setTimeout виконає його один раз. Отже, якщо я правильно, (..., 200 * i) запланує кожен дзвінок, розділений на 200 мс (як прокоментував oyatek), саме цього хотіла досягти gabeodess. Поточний (..., 200) виконає всі їх одночасно через 200 мс. Або мені чогось не вистачає?
lepe

@gabeodess - замість цього ви повинні визначати setIntervalкількість необхідних запитів setTimeoutі встановлювати його на 100- на випадок, якщо сума адреси колись у майбутньому збільшить 20суму.
Роб Скотт

3
@gabeodess Я спробував ваше рішення, але все ще отримую OVER_QUERY_LIMIT Скрипка
Прабс

6

На жаль, це обмеження служби Google Maps.

Зараз я працюю над додатком, що використовує функцію геокодування, і зберігаю кожну унікальну адресу для кожного користувача. Я генерую інформацію про адресу (місто, вулиця, штат тощо) на основі інформації, що повертається картами Google, а також зберігаю інформацію про ширину / довжину в базі даних. Це запобігає перекодуванню речей і дає вам добре відформатовані адреси.

Ще однією причиною, за якою ви хочете це зробити, є те, що існує щоденний ліміт кількості адрес, які можна геокодувати з певної IP-адреси. Ви не хочете, щоб із цієї причини ваша заявка зазнала невдачі.


2

Я стикаюся з тією ж проблемою, намагаючись геокодувати 140 адрес.

Моїм обхідним шляхом було додавання usleep (100000) для кожного циклу наступного запиту геокодування. Якщо статус запиту OVER_QUERY_LIMIT, usleep збільшується на 50000 і запит повторюється тощо.

І тому всі отримані дані (lat / long) зберігаються у файлі XML, щоб не запускати запит щоразу, коли сторінка завантажується.


1
Ваша відповідь розпливчаста, ви посилаєтесь на стороні сервера або це javascript, якщо це останній, usleep не є функцією, а отже, буде неправильним, якщо це перша, то я пропоную вам внести зміни у свою відповідь, щоб чітко зазначити це на стороні сервера, щоб уникнути двозначності.
t0mm13b

1

РЕДАГУВАТИ:

Забув сказати, що це рішення в чистому js, єдине, що вам потрібно, це браузер, який підтримує обіцянки https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise


Для тих, кому ще потрібно виконати таке, я написав власне рішення, яке поєднує обіцянки та тайм-аути.

Код:

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

Зверніть увагу, що це лише частина більшої бібліотеки, яку я написав для обробки матеріалів Google Maps, отже, коментарі можуть заплутати.

Використання досить просте, проте підхід дещо інший: замість циклу та вирішення однієї адреси за раз вам потрібно буде передати масив адрес класу, і він сам оброблятиме пошук, повертаючи обіцянку, яка , коли вирішено, повертає масив, що містить всю вирішену (і невирішену) адресу.

Приклад:

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

Вихід консолі:

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

Об'єкт повернено:

введіть тут опис зображення

Тут відбувається вся магія:

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

В основному, він циклічно повторює кожен елемент із затримкою в 750 мілісекунд між кожним з них, отже, кожні 750 мілісекунд управляється адресою.

Я зробив кілька подальших тестувань і виявив, що навіть через 700 мілісекунд іноді отримував помилку QUERY_LIMIT, тоді як із 750 у мене взагалі не було жодної проблеми.

У будь-якому випадку, не соромтеся редагувати 750 вище, якщо ви відчуваєте, що перебуваєте в безпеці, справляючись із меншою затримкою.

Сподіваюся, це допоможе комусь найближчим часом;)


0

Я щойно протестував Google Geocoder і отримав таку ж проблему, як і ви. Я помітив, що статус OVER_QUERY_LIMIT отримую лише раз на 12 запитів, тому я чекаю 1 секунду (це мінімальна затримка для очікування) Це уповільнює роботу програми, але менше, ніж очікує 1 секунду кожного запиту

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
}

З основним методом holdOn:

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

Сподіваюся, це допоможе

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.