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

Posted

技术标签:

【中文标题】如何在未收到 OVER_QUERY_LIMIT 响应的情况下对 20 个地址进行地理编码?【英文标题】:How do I Geocode 20 addresses without receiving an OVER_QUERY_LIMIT response? 【发布时间】:2011-01-26 00:40:25 【问题描述】:

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

除了提前保存坐标外,还有其他方法吗?

【问题讨论】:

还是这样吗?我在文档中看到的唯一限制是:“每天 2,500 个地理定位请求的查询限制”。 code.google.com/apis/maps/documentation/geocoding/… 这不是关于每个用户每天的查询总数,而是关于短时间内的查询次数,例如循环查询时。 我们的店铺有营业执照,但仍然遇到每秒处理不了10个以上请求的问题。营业执照和普通开发人员之间的唯一区别是我们每天的调用次数上限为 100,000 次。 @michielvoo 你解决了吗?如果是,请帮助我。我得到 OVER_QUERY_LIMIT 。 My question 在 SO。 Fiddle 【参考方案1】:

不,实际上没有其他方法:如果您有很多位置并想在地图上显示它们,最好的解决方案是:

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

当然,这是考虑到您创建/修改位置的次数比咨询位置的次数要少得多。

是的,这意味着您在保存位置时需要做更多的工作——但这​​也意味着:

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

【讨论】:

我很好奇,经过一段时间(比如一个月)后,您如何确定结果是否正确。你会每隔一段时间重新查询一次吗? 如果地址 (您已经在数据库中拥有 - 否则您将无法进行地理编码) 没有改变,那么纬度的可能性非常低/经度应该改变。当然,每次修改地址时,都要重新查询地理编码器,得到新地址对应的纬度+经度。 我已将纬度/经度存储到数据库中,并通过 AJAX 作为数组从数据库中检索它,但它应该再次传递给 java 脚本循环,此外我已从数据库中收到 173 个位置.现在它向我显示了相同的 OVER_QUERY_LIMIT 状态。请指教...【参考方案2】:

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

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

  setTimeout(function()

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

  , 200 * i);


【讨论】:

但是 (200 * i) 意味着每个请求之间的暂停正在增加。所以第三次请求是 600,然后是 800 等等。 只需删除'* i' setTimeout 将执行一次。因此,如果我是正确的, (... , 200 * i) 将安排每个呼叫间隔 200 毫秒(正如 oyatek 评论的那样),这就是 gabeodess 想要实现的目标。当前 (... , 200) 将在 200 毫秒后同时执行所有这些。还是我错过了什么? @gabeodess - 你应该在需要的请求数量上使用setInterval,而不是setTimeout,并将其设置为100 - 以防将来某个时候地址数量扩展20 数量。 @gabeodess 我已经尝试过您的解决方案,但仍然收到 OVER_QUERY_LIMIT Fiddle【参考方案3】:

很遗憾,这是对 Google 地图服务的限制。

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

您想要这样做的另一个原因是,每天可以从特定 IP 地址进行地理编码的地址数量存在限制。你不希望你的申请因为这个原因而失败。

【讨论】:

【参考方案4】:

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

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

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

【讨论】:

你的答案很模糊,你指的是服务器端还是这个javascript,如果是后者,usleep not 是一个函数,因此不正确,如果是前者,那么我建议您修改答案以明确说明这是服务器端以避免歧义。【参考方案5】:

编辑:

忘了说这个解决方案是纯js,你唯一需要的是支持promises的浏览器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);
            
        );
    
;

请注意,它只是我为处理谷歌地图而编写的更大库的一部分,因此 cmets 可能会造成混淆。

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

例子:

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。

希望这在不久的将来对某人有所帮助;)

【讨论】:

【参考方案6】:

我刚刚测试了 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
        
    

希望对你有帮助

【讨论】:

以上是关于如何在未收到 OVER_QUERY_LIMIT 响应的情况下对 20 个地址进行地理编码?的主要内容,如果未能解决你的问题,请参考以下文章

使用 rxjava 避免 OVER_QUERY_LIMIT

Google Maps API v3 中的 OVER_QUERY_LIMIT:如何在 Javascript 中暂停/延迟以减慢速度?

状态:'OVER_QUERY_LIMIT'

谷歌地图 API V3 Over_Query_Limit

在未安装的组件上调用 setState() [重复]

无法在未调用 Looper.prepare() 的线程内创建处理程序