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


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

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

6 84

6 ответов:

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

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

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


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

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

на самом деле вам не нужно ждать полной секунды для каждого запроса. Я обнаружил, что если я жду 200 миллисекунд между каждым запросом, я могу избежать ответа OVER_QUERY_LIMIT, и пользовательский опыт сносен. С помощью этого решения вы можете загрузить 20 элементов за 4 секунды.

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

  setTimeout(function(){

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

  }, 200 * i);

}

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

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

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

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

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

и причины все полученные данные (lat/long) хранятся в XML-файле, чтобы не запускать запрос каждый раз, когда страница загружается.

EDIT:

забыл сказать, что это решение находится в чистом 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); 
});

Я только что протестировал 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
        }
    }

надеюсь, что это помогает