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

IT技术 javascript geocoding google-maps
2021-02-03 09:04:10

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

除了预先存储坐标之外,还有其他方法可以做到吗?

6个回答

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

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

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


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

  • 您将能够按地理坐标进行搜索
    • 即“我想要一个靠近我现在位置的点的列表
  • 显示地图会快很多
    • 即使上面有 20 多个位置
  • 哦,还有(最后但并非最不重要的):这会起作用;-)
    • 您不太可能在 N 秒内达到 X 个地理编码器调用的限制。
    • 而且您不太可能达到每天 Y 次地理编码器调用的限制。
我很好奇您如何确定经过一段时间(比如一个月)后结果是正确的。您是否每隔一段时间重新查询一次?
2021-04-03 09:04:10
我已将经纬度存储到数据库中,并通过 AJAX 作为数组从数据库中检索它,但它应该再次传递给 java 脚本循环,更多的是我从数据库收到了 173 个位置。现在它向我显示了相同的 OVER_QUERY_LIMIT 状态。请指教...
2021-04-03 09:04:10
如果地址(您的数据库中已有地址——否则您将无法进行地理编码)没有改变,那么纬度/经度发生变化的可能性很小。而且,当然,每次修改地址时,您都应该重新查询地理编码器,以获取新地址对应的纬度+经度。
2021-04-06 09:04:10

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

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

  setTimeout(function(){

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

  }, 200 * i);

}
@gabeodess - 您应该setInterval处理所需请求的数量,而不是setTimeout,并将其设置为100- 以防万一地址数量在将来的某个时候扩展20数量。
2021-03-22 09:04:10
setTimeout 将执行一次。因此,如果我是对的, (... , 200 * i) 将安排每个调用间隔 200 毫秒(如 oyatek 评论的那样),这正是 gabeodess 想要实现的。当前 (... , 200) 将在 200 毫秒后同时执行所有这些。或者我错过了什么?
2021-03-27 09:04:10
但是 (200 * i) 意味着每个请求之间的停顿正在增加。所以在第三次请求时,它是 600,然后是 800,等等。
2021-04-03 09:04:10
只需删除'* i'
2021-04-07 09:04:10
@gabeodess 我已经尝试过你的解决方案,但仍然遇到 OVER_QUERY_LIMIT Fiddle
2021-04-10 09:04:10

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

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

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

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

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

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

你的回答含糊不清,你指的是服务器端还是这个javascript,如果是后者,usleep不是一个函数,因此是不正确的,如果是前者,那么我建议你修改你的答案以明确说明这一点是服务器端以避免歧义。
2021-03-31 09:04:10

编辑:

忘了说这个方案是纯js的,你唯一需要的是一个支持promise 的浏览器https://developer.mozilla.org/it/docs/Web/JavaScript/Reference/Global_Objects/Promise


对于那些仍然需要完成此类任务的人,我编写了自己的解决方案,将 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

返回的对象:

在此处输入图片说明

整个魔术发生在这里:

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

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