Как мне геокодировать 20 адресов, не получая ответа OVER_QUERY_LIMIT?


87

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

Есть ли другой способ сделать это, кроме как заранее сохранить координаты?


это все еще так? Единственное ограничение, которое я вижу в документации: «ограничение на количество запросов в 2500 запросов на геолокацию в день». code.google.com/apis/maps/documentation/geocoding/…
Руссау,

6
Дело не в общем количестве запросов на пользователя в день, а в количестве запросов за короткий промежуток времени, например, при запросах в цикле.
Мишель ван Остерхаут,

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

@michielvoo Вы это решили? Если да, пожалуйста, помогите мне. Я получаю OVER_QUERY_LIMIT. Мой вопрос в ТАК. Fiddle
Prabs

Ответы:


85

Нет, на самом деле другого пути нет: если у вас много мест и вы хотите отобразить их на карте, лучшим решением будет:

  • получить широту + долготу с помощью геокодера при создании местоположения
  • хранить их в своей базе данных вместе с адресом
  • и используйте эти сохраненные координаты широты и долготы, когда хотите отобразить карту.

Это, конечно, с учетом того, что у вас намного меньше создания / изменения локаций, чем у вас есть консультации по локациям.


Да, это означает, что вам придется немного поработать при сохранении местоположений, но это также означает:

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

Мне любопытно, как вы можете быть уверены, что результаты верны по прошествии некоторого времени (скажем, месяца). Вы время от времени запрашиваете их повторно?
Крис

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

Я сохранил lat / long в БД и получил его из БД через AJAX в виде массива, но затем он должен снова передать цикл сценария Java, более того, я получил 173 местоположения из БД. Теперь он показывает мне тот же статус OVER_QUERY_LIMIT. Пожалуйста, посоветуйте ...
Прабху М.

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 Fiddle
Prabs

6

К сожалению, это ограничение сервиса Google Maps.

В настоящее время я работаю над приложением, использующим функцию геокодирования, и сохраняю каждый уникальный адрес для каждого пользователя. Я генерирую адресную информацию (город, улица, штат и т. Д.) На основе информации, возвращаемой картами Google, а затем сохраняю информацию о широте и долготе в базе данных. Это избавляет вас от необходимости перекодировать вещи и дает хорошо отформатированные адреса.

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


2

У меня та же проблема, пытаясь геокодировать 140 адресов.

Мое обходное решение - добавление usleep (100000) для каждого цикла следующего запроса геокодирования. Если статус запроса - OVER_QUERY_LIMIT, usleep увеличивается на 50000, и запрос повторяется, и так далее.

И, конечно же, все полученные данные (широта / долгота) хранятся в файле 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, поэтому комментарии могут сбивать с толку.

Использование довольно простое, однако подход немного отличается: вместо цикла и разрешения одного адреса за раз вам нужно будет передать массив адресов классу, и он будет обрабатывать поиск самостоятельно, возвращая обещание, которое при разрешении возвращает массив, содержащий все разрешенные (и неразрешенные) адреса.

Пример:

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
}

С помощью основного метода удержания:

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

Надеюсь, это поможет

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.