Как мне геокодировать 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 выше, если вы чувствуете себя в безопасности, используя меньшую задержку.

Надеюсь, это поможет кому-то в ближайшем будущем;)

Briosheje
источник
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
        }
    }

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

Hugues
источник