如何在不收到OVER_QUERY_LIMIT响应的情况下对20个地址进行地理编码?

时间:2010-03-10 17:49:34

标签: javascript geocoding google-maps

使用谷歌地理编码器v3,如果我尝试对20个地址进行地理编码,我会得到一个OVER_QUERY_LIMIT,除非我将它们分开约1秒,但是我的标记全部放置需要20秒。

除了预先存储坐标之外,还有其他办法吗?

6 个答案:

答案 0 :(得分:83)

不,没有其他任何方式:如果你有很多地方并希望在地图上显示它们,最好的解决方案是:

  • 在创建位置时使用地理编码器获取纬度+经度
  • 将这些内容与地址
  • 一起存储在您的数据库中
  • 并在您想要显示地图时使用那些存储的纬度+经度。

当然,考虑到您创建/修改地点的次数比您在地点协商方面少得多。


是的,这意味着在保存位置时你将不得不做更多的工作 - 但这也意味着:

  • 您将能够按地理坐标进行搜索
    • 即。 “我想要一个我现在
    • 附近的点列表。”
  • 显示地图会快得多
    • 即使有超过20个位置
  • 哦,而且(最后但并非最不重要):这会起作用;-)
    • 您将不太可能在N秒内达到X地理编码器调用的限制。
    • 并且您不太可能达到每天Y地理编码器呼叫的限制。

答案 1 :(得分:20)

实际上,您不必为每个请求等待一整秒。我发现如果我在每个请求之间等待200毫秒,我就可以避免OVER_QUERY_LIMIT响应并且用户体验可以通过。使用此解决方案,您可以在4秒内加载20个项目。

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

  setTimeout(function(){

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

  }, 200 * i);

}

答案 2 :(得分:6)

不幸的是,这是Google地图服务的限制。

我目前正在使用地理编码功能处理应用程序,并且我将基于每个用户保存每个唯一地址。我根据谷歌地图返回的信息生成地址信息(城市,街道,州等),然后将纬度/经度信息保存在数据库中。这可以防止您不得不重新编码,并为您提供格式良好的地址。

您想要执行此操作的另一个原因是,可以从特定IP地址进行地理编码的地址数量每日限制。出于这个原因,您不希望您的应用程序因某个人而失败。

答案 3 :(得分:2)

我在尝试对140个地址进行地理编码时面临同样的问题。

我的解决方法是为下一个地理编码请求的每个循环添加 usleep(100000)。如果请求的状态为OVER_QUERY_LIMIT,则usleep将增加50000并重复请求,依此类推。

因为所有收到的数据(纬度/经度)都存储在XML文件中,不会在每次加载页面时都运行请求。

答案 4 :(得分: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);
            }
        });
    }
};

请注意,它只是我为处理谷歌地图而编写的更大图书馆的一部分,因此评论可能会令人困惑。

用法非常简单,但是,方法略有不同:您不需要一次循环和解析一个地址,而是需要将一个地址数组传递给类,它将自己处理搜索,返回一个promise,当被解析时,返回一个包含所有已解析(和未解析)地址的数组。

示例:

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

返回对象:

enter image description here

整个魔法发生在这里:

(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.

希望这可以在不久的将来帮助某人;)

答案 5 :(得分:0)

我刚刚测试了Google Geocoder并遇到了与您相同的问题。 我注意到我每12次请求只获得一次OVER_QUERY_LIMIT状态 所以我等了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
        }
    }

希望有所帮助