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

Posted

技术标签:

【中文标题】Google Maps API v3 中的 OVER_QUERY_LIMIT:如何在 Javascript 中暂停/延迟以减慢速度?【英文标题】:OVER_QUERY_LIMIT in Google Maps API v3: How do I pause/delay in Javascript to slow it down? 【发布时间】:2012-08-01 08:04:39 【问题描述】:

我遇到了一个在这些论坛中讨论得很好的问题,但似乎没有一个建议对我有用,所以我正在寻找一些完整的 javascript,在保存为 html 文件时可以工作。

问题是,当我尝试使用 Javascript 调用的 V3 API 对 Google 地图上的 11 个以上位置进行地理编码时,我一直遇到 OVER_QUERY_LIMIT 错误。我知道您可以调用地理编码器的速率是有限制的(以及总交易量的每日限制),所以我需要在数组中的每个结果之间引入一个暂停。

非常感谢任何帮助。

这是我的代码:

<script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>
<script type="text/javascript">
var geocoder;
var map;
var wait = false;


  function initialize() 
geocoder = new google.maps.Geocoder();
var latlng = new google.maps.LatLng(51.32, 0.5);



var myOptions = 
  zoom: 8,
  center: latlng,
  mapTypeId: google.maps.MapTypeId.ROADMAP

map = new google.maps.Map(document.getElementById("map_canvas"), myOptions);
codeAddress('KT16 8LA' + ', UK');
codeAddress('LS8 2LQ' + ', UK');
codeAddress('NE13 8AF' + ', UK');
codeAddress('KT12 2BE' + ', UK');
codeAddress('W1W 8AN' + ', UK');
codeAddress('EC3N 2LS' + ', UK');
codeAddress('BS9 3BH' + ', UK');
codeAddress('KA10 6LZ' + ', UK');
codeAddress('EC1V 9BW' + ', UK');
codeAddress('WD18 8YN' + ', UK');
codeAddress('HA3 6DQ' + ', UK');
codeAddress('W1U 3PL' + ', UK');
codeAddress('W1T 7QL' + ', UK');
codeAddress('W1S 1TD' + ', UK');
codeAddress('SW1X 8NX' + ', UK');
codeAddress('LE2 8ET' + ', UK');
codeAddress('BA3 4BH' + ', UK');
codeAddress('AL3 8JP' + ', UK');
codeAddress('DE55 4QJ' + ', UK');
codeAddress('W6 0QT' + ', UK');
codeAddress('LA1 1PP' + ', UK');
codeAddress('SW16 4DH' + ', UK');
codeAddress('WC2N 6DF' + ', UK');
codeAddress('RM6 6LS' + ', UK');
codeAddress('S25 3QZ' + ', UK');
codeAddress('WC2H 7LR' + ', UK');
codeAddress('BH24 1DW' + ', UK');
codeAddress('EC2N 6AR' + ', UK');
codeAddress('W1U 2FA' + ', UK');
codeAddress('B60 3DX' + ', UK');    


  function codeAddress(vPostCode) 
if (geocoder) 
  geocoder.geocode(  'address': "'" + vPostCode + "'", function(results, status) 
    if (status == google.maps.GeocoderStatus.OK) 
      map.setCenter(results[0].geometry.location);
      var marker = new google.maps.Marker(
          map: map, 
          position: results[0].geometry.location
      );
     else 
      alert("Geocode was not successful for the following reason: " + status);
    
  );



</script>
<body style="margin:0px; padding:0px;" onload="initialize()">
<div id="map_canvas" style="width:100%; height:90%"></div>
</body>

编辑:这是我试图让它在相关部分暂停/等待,但它没有做任何事情:

function codeAddress(vPostCode) 
    if (geocoder) 
    while (wait)  /* Just wait. */ ;
      geocoder.geocode(  'address': "'" + vPostCode + "'", function(results, status) 
        if (status == google.maps.GeocoderStatus.OK) 
          map.setCenter(results[0].geometry.location);
          var marker = new google.maps.Marker(
              map: map, 
              position: results[0].geometry.location
          );
        /* When geocoding "fails", see if it was because of over quota error: */
         else if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT)  
        wait = true;
        setTimeout("wait = true", 2000);
        //alert("OQL: " + status);
         else 
          alert("Geocode was not successful for the following reason: " + status);
        
      );
    
  

【问题讨论】:

我想如果你提出像 $.getJSON 这样的请求...没有限制 OVER_QUERY_LIMIT while using google maps的可能重复 【参考方案1】:

Mike Williams 的教程中没有类似这两行的内容:

    wait = true;
    setTimeout("wait = true", 2000);

这是一个版本 3 的端口:

http://acleach.me.uk/gmaps/v3/plotaddresses.htm

相关的代码是

  // ====== Geocoding ======
  function getAddress(search, next) 
    geo.geocode(address:search, function (results,status)
       
        // If that was successful
        if (status == google.maps.GeocoderStatus.OK) 
          // Lets assume that the first marker is the one we want
          var p = results[0].geometry.location;
          var lat=p.lat();
          var lng=p.lng();
          // Output the data
            var msg = 'address="' + search + '" lat=' +lat+ ' lng=' +lng+ '(delay='+delay+'ms)<br>';
            document.getElementById("messages").innerHTML += msg;
          // Create a marker
          createMarker(search,lat,lng);
        
        // ====== Decode the error status ======
        else 
          // === if we were sending the requests to fast, try this one again and increase the delay
          if (status == google.maps.GeocoderStatus.OVER_QUERY_LIMIT) 
            nextAddress--;
            delay++;
           else 
            var reason="Code "+status;
            var msg = 'address="' + search + '" error=' +reason+ '(delay='+delay+'ms)<br>';
            document.getElementById("messages").innerHTML += msg;
             
        
        next();
      
    );
  

【讨论】:

也适用于路由,当批处理只是在增加延迟后递归到对 Google 的批处理调用。 这是什么..@Andrew Leach 你能告诉我这些延迟和下一个地址是什么吗..你能解决这个问题吗Fiddle delay 是以毫秒为单位的延迟; nextAddress 是地址计数器:如果地址失败,取消设置增量并增加延迟,以便再次尝试。完整的实现在我的网站上给出的链接:你的小提琴中没有这样的代码。 第一个结果后是否可以设置地图中心? @PaulLeclerc 我的示例仍然适用于我,尽管地理编码器已更改,因此实际找到的地址更少。如果您使用共享 IP 地址,则查询限制在该 IP 地址上的所有用户之间共享。这意味着移动网络的用户在开始之前可能会违反限制。【参考方案2】:

这个问题的一般答案是:

不要在每次加载页面时对已知位置进行地理编码。对它们进行离线地理编码并使用生成的坐标在您的页面上显示标记。

限制的存在是有原因的。

如果您无法离线对位置进行地理编码,请参阅 Mike Williams 的 v2 教程中的 this page (Part 17 Geocoding multiple addresses),该教程描述了一种方法,将其移植到 v3 API。

【讨论】:

感谢您的回复。我无法对它们进行离线地理编码,但您引用的页面没有提供任何我可以应用的示例。我真正需要的只是一个可以工作的延迟/暂停步骤。 你看到了吗:view-source:econym.org.uk/gmap/example_geomulti.htm,它是为 v2 API 编写的,但这个概念应该适用于 v3 API。 已经尝试过这个概念,但没有成功。它可能在 V2 和 V3 中运行良好,但我无法让它成功应用于上述 Javascript。 您可以发布一个指向您的地图(或 jsfiddle)的链接,您试图让它工作但失败了,也许有人可以看到您哪里出错了。 是的,根据上面的代码,它适用于前 80 次页面浏览量,然后使用免费使用中断。 3300 页面浏览量,如果您付费的话。【参考方案3】:

我在这里加载了 2200 个标记。添加 2200 个位置大约需要 1 分钟。 https://jsfiddle.net/suchg/qm1pqunz/11/

//function to get random element from an array
    (function($) 
        $.rand = function(arg) 
            if ($.isArray(arg)) 
                return arg[$.rand(arg.length)];
             else if (typeof arg === "number") 
                return Math.floor(Math.random() * arg);
             else 
                return 4;  // chosen by fair dice roll
            
        ;
    )(jQuery);

//start code on document ready
$(document).ready(function () 
    var map;
    var elevator;
    var myOptions = 
        zoom: 0,
        center: new google.maps.LatLng(35.392738, -100.019531), 
        mapTypeId: google.maps.MapTypeId.ROADMAP
    ;
    map = new google.maps.Map($('#map_canvas')[0], myOptions);

    //get place from inputfile.js
    var placesObject = place;
    errorArray = [];

  //will fire 20 ajax request at a time and other will keep in queue
    var queuCounter = 0, setLimit = 20; 

  //keep count of added markers and update at top
  totalAddedMarkers = 0;

  //make an array of geocode keys to avoid the overlimit error
    var geoCodKeys = [
                    'AIzaSyCF82XXUtT0vzMTcEPpTXvKQPr1keMNr_4',
                    'AIzaSyAYPw6oFHktAMhQqp34PptnkDEdmXwC3s0',
                    'AIzaSyAwd0OLvubYtKkEWwMe4Fe0DQpauX0pzlk',
                    'AIzaSyDF3F09RkYcibDuTFaINrWFBOG7ilCsVL0',
                    'AIzaSyC1dyD2kzPmZPmM4-oGYnIH_0x--0hVSY8'                   
                ];

  //funciton to add marker
    var addMarkers = function(address, queKey)
        var key = jQuery.rand(geoCodKeys);
        var url = 'https://maps.googleapis.com/maps/api/geocode/json?key='+key+'&address='+address+'&sensor=false';

        var qyName = '';
        if( queKey ) 
            qyName = queKey;
         else 
            qyName = 'MyQueue'+queuCounter;
        

        $.ajaxq (qyName, 
            url: url,
            dataType: 'json'
        ).done(function( data ) 
                    var address = getParameterByName('address', this.url);
                    var index = errorArray.indexOf(address);
                    try
                        var p = data.results[0].geometry.location;
                        var latlng = new google.maps.LatLng(p.lat, p.lng);
                        new google.maps.Marker(
                            position: latlng,
                            map: map
                        );
                        totalAddedMarkers ++;

            //update adde marker count
                        $("#totalAddedMarker").text(totalAddedMarkers);
                        if (index > -1) 
                            errorArray.splice(index, 1);
                        
                    catch(e)
                        if(data.status = 'ZERO_RESULTS')
                            return false;

            //on error call add marker function for same address
            //and keep in Error ajax queue
                        addMarkers( address, 'Errror' );
                        if (index == -1) 
                            errorArray.push( address );
                        
                    
        );

    //mentain ajax queue set
        queuCounter++;
        if( queuCounter == setLimit )
            queuCounter = 0;
        
    

  //function get url parameter from url string
    getParameterByName = function ( name,href )
    
      name = name.replace(/[\[]/,"\\\[").replace(/[\]]/,"\\\]");
      var regexS = "[\\?&]"+name+"=([^&#]*)";
      var regex = new RegExp( regexS );
      var results = regex.exec( href );
      if( results == null )
        return "";
      else
        return decodeURIComponent(results[1].replace(/\+/g, " "));
    

  //call add marker function for each address mention in inputfile.js
    for (var x = 0; x < placesObject.length; x++) 
        var address = placesObject[x]['City'] + ', ' + placesObject[x]['State'];
        addMarkers(address);
    
);

【讨论】:

ToS 有一些您应该注意的限制:“不得使用超出交易限制和使用政策。如果您的 Maps API 实施产生大量交易,Google 保留设置交易限制的权利。 Google 还保留不时在文档中设置其他使用政策的权利。如果您想在这些交易限制或使用政策之外进行使用,您可以通过 Maps API 标准定价计划购买更多使用容量,或者您可以请联系 Google 地图销售团队,了解满足您需求的许可选项。” @sachingavas 我收到 name.replace 不是函数错误消息【参考方案4】:

使用“setInterval”和“clearInterval”可以解决问题:

function drawMarkers(map, markers) 
    var _this = this,
        geocoder = new google.maps.Geocoder(),
        geocode_filetrs;

    _this.key = 0;

    _this.interval = setInterval(function() 
        _this.markerData = markers[_this.key];

        geocoder.geocode( address: _this.markerData.address , yourCallback(_this.markerData));

        _this.key++;

        if ( ! markers[_this.key]) 
            clearInterval(_this.interval);
        

    , 300);

【讨论】:

【参考方案5】:

这篇文章是不久前发表的,但它提供的答案并没有解决我在迭代中达到请求限制的问题,所以我发布了这篇文章,以帮助其他没有服务的人。

我的环境发生在 Ionic 3 中。

我没有在迭代中“暂停”,而是产生了使用timer进行迭代的想法,这个计时器具有执行将在迭代中进行的代码的特殊性,但会每隔一段时间运行一次直到达到我们要迭代的“数组”的最大计数。

也就是说,我们会在一定的时间内查询 Google API,使其不超过允许的毫秒数限制。

// Code to start the timer
    this.count= 0;
    let loading = this.loadingCtrl.create(
      content: 'Buscando los mejores servicios...'
    );
    loading.present();
    this.interval = setInterval(() => this.getDistancias(loading), 40);
// Function that runs the timer, that is, query Google API
  getDistancias(loading)
    if(this.count>= this.datos.length)
      clearInterval(this.interval);
     else 
      var sucursal = this.datos[this.count];
      this.calcularDistancia(this.posicion, new LatLng(parseFloat(sucursal.position.latitude),parseFloat(sucursal.position.longitude)),sucursal.codigo).then(distancia => 
    ).catch(error => 
      console.log('error');
      console.log(error);
    );
    
    this.count += 1;
  
  calcularDistancia(miPosicion, markerPosicion, codigo)
    return new Promise(async (resolve,reject) => 
      var service = new google.maps.DistanceMatrixService;
      var distance;
      var duration;
      service.getDistanceMatrix(
        origins: [miPosicion, 'salida'],
        destinations: [markerPosicion, 'llegada'],
        travelMode: 'DRIVING',
        unitSystem: google.maps.UnitSystem.METRIC,
        avoidHighways: false,
        avoidTolls: false
      , function(response, status)
        if (status == 'OK') 
          var originList = response.originAddresses;
          var destinationList = response.destinationAddresses;
          try
            if(response != null && response != undefined)
              distance = response.rows[0].elements[0].distance.value;
              duration = response.rows[0].elements[0].duration.text;
              resolve(distance);
            
          catch(error)
            console.log("ERROR GOOGLE");
            console.log(status);
          
        
      );
    );
  

我希望这会有所帮助!

对不起,我的英语不好,希望不是不便,我不得不使用谷歌翻译。

问候,莱安德罗。

【讨论】:

【参考方案6】:

您以错误的方式使用setTimeout。 (之一)函数签名是setTimeout(callback, delay)。所以你可以很容易地指定在什么延迟之后应该运行什么代码。

var codeAddress = (function() 
    var index = 0;
    var delay = 100;

    function GeocodeCallback(results, status) 
        if (status == google.maps.GeocoderStatus.OK) 
            map.setCenter(results[0].geometry.location);
            new google.maps.Marker( map: map, position: results[0].geometry.location, animation: google.maps.Animation.DROP );
            console.log(results);
        
        else alert("Geocode was not successful for the following reason: " + status);
    ;

    return function(vPostCode) 
        if (geocoder) setTimeout(geocoder.geocode.bind(geocoder,  'address': "'" + vPostCode + "'", GeocodeCallback), index*delay);
        index++;
    ;
)();

这样,每次codeAddress() 调用都会导致geocoder.geocode() 在上一次调用后100 毫秒后被调用。

我还为标记添加了动画,因此您将获得一个很好的动画效果,标记被添加到一个接一个的地图中。我不确定当前的 google 限制是多少,因此您可能需要增加 delay 变量的值。

此外,如果您每次都对相同的地址进行地理编码,则应将地理编码的结果保存到您的数据库中,下次只需使用它们(这样您将节省一些流量并且您的应用程序会更快一点)

【讨论】:

如何将这段代码实现到我的代码中? jsfiddle.net/395rdhd8我尝试了很多东西,但没有任何运气。 STATUS OVER_QUERY_LIMIT 在我的控制台中出现在我的 STATUS.OK 之前。但为什么?谢谢。 你的setTimeout用法不对,setTimeout不是让javascript休眠X秒,它是用来在X秒后运行指定函数 我该如何解决? Javascript 不是我最强的技能。谢谢! 其中一个选项是创建递归函数,如果请求不成功,该函数将调用自身,如下所示:jsfiddle.net/395rdhd8/1

以上是关于Google Maps API v3 中的 OVER_QUERY_LIMIT:如何在 Javascript 中暂停/延迟以减慢速度?的主要内容,如果未能解决你的问题,请参考以下文章

可以吗? GMap API V2 中的 clearOverlays() 到 Google Maps API V3?

关闭 Google Maps API v3 中的所有信息窗口

如何更改 Google Maps API V3 中的图标颜色?

在 google maps api v3 中隐藏方向服务中的标记

Google Maps API v3:如何删除事件监听器?

Google Maps Javascript API V3中的旋转标记