Як мені геокодувати 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.