developer tip

OVER_QUERY_LIMIT 응답을받지 않고 20 개 주소를 지오 코딩하려면 어떻게해야합니까?

optionbox 2020. 9. 10. 07:42
반응형

OVER_QUERY_LIMIT 응답을받지 않고 20 개 주소를 지오 코딩하려면 어떻게해야합니까?


Google Geocoder v3를 사용하여 20 개의 주소를 지오 코딩하려고하면 ~ 1 초 간격으로 시간을 설정하지 않으면 OVER_QUERY_LIMIT가 표시되지만 마커가 모두 배치되기까지 20 초가 걸립니다.

좌표를 미리 저장하는 것 외에 다른 방법이 있습니까?


아니요, 다른 방법은 없습니다. 위치가 많고지도에 표시하려는 경우 가장 좋은 방법은 다음과 같습니다.

  • 위치가 생성 될 때 지오 코더를 사용하여 위도 + 경도를 가져옵니다.
  • 주소와 함께 데이터베이스에 저장
  • 지도를 표시하고 싶을 때 저장된 위도 + 경도를 사용합니다.

물론 이것은 위치에 대한 상담보다 위치 생성 / 수정이 훨씬 적다는 것을 고려할 때입니다.


예, 위치를 저장할 때 좀 더 많은 작업을해야하지만 다음을 의미하기도합니다.

  • 지리적 좌표로 검색 할 수 있습니다.
    • 즉 " 지금있는 위치에 가까운 지점 목록을 원합니다. "
  • 지도 표시가 훨씬 빨라집니다.
    • 20 개 이상의 위치가있는 경우에도
  • 오, 그리고 또한 (마지막으로 중요하지만) : 이것은 작동합니다 ;-)
    • N 초 내에 X 지오 코더 호출 제한에 도달 할 가능성이 적습니다.
    • 그리고 하루에 Y 지오 코더 호출 한도에 도달 할 가능성이 적습니다.

실제로 각 요청에 대해 1 초를 기다릴 필요가 없습니다. 각 요청 사이에 200 밀리 초를 기다리면 OVER_QUERY_LIMIT 응답을 피할 수 있고 사용자 경험이 통과 할 수 있다는 것을 알았습니다. 이 솔루션을 사용하면 4 초 내에 20 개의 항목을로드 할 수 있습니다.

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

  setTimeout(function(){

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

  }, 200 * i);

}

안타깝게도 이것은 Google지도 서비스의 제한 사항입니다.

현재 지오 코딩 기능을 사용하는 응용 프로그램에서 작업 중이며 각 고유 주소를 사용자별로 저장하고 있습니다. Google지도에서 반환 한 정보를 기반으로 주소 정보 (시, 거리, 주 등)를 생성 한 다음 위도 / 경도 정보도 데이터베이스에 저장합니다. 이렇게하면 코드를 다시 코딩 할 필요가 없으며 멋진 형식의 주소를 얻을 수 있습니다.

이 작업을 수행하려는 또 다른 이유는 특정 IP 주소에서 지오 코딩 할 수있는 주소 수에 일일 제한이 있기 때문입니다. 그런 이유로 신청이 실패하는 것을 원하지 않습니다.


140 개의 주소를 지오 코딩하려는 동일한 문제에 직면 해 있습니다.

내 해결 방법은 다음 지오 코딩 요청의 각 루프에 대해 usleep (100000)추가하는 것 입니다. 요청 상태가 OVER_QUERY_LIMIT이면 usleep이 50000 증가하고 요청이 반복됩니다.

그리고 수신 된 모든 데이터 (위도 / 경도)는 페이지가로드 될 때마다 요청을 실행하지 않도록 XML 파일에 저장됩니다.


편집하다:

이 솔루션은 순수 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지도를 처리하기 위해 작성한 더 큰 라이브러리의 일부일 뿐이므로 댓글이 혼란 스러울 수 있습니다.

Usage is quite simple, the approach, however, is slightly different: instead of looping and resolving one address at a time, you will need to pass an array of addresses to the class and it will handle the search by itself, returning a promise which, when resolved, returns an array containing all the resolved (and unresolved) address.

Example:

var myAmazingGeo = new Geolocalizer();
var locations = ["Italy","California","Dragons are thugs...","China","Georgia"];
myAmazingGeo.Localize(locations).then(function(res){ 
   console.log(res); 
});

Console output:

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

Object returned:

enter image description here

The whole magic happens 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);

Basically, it loops every item with a delay of 750 milliseconds between each of them, hence every 750 milliseconds an address is controlled.

I've made some further testings and I've found out that even at 700 milliseconds I was sometimes getting the QUERY_LIMIT error, while with 750 I haven't had any issue at all.

In any case, feel free to edit the 750 above if you feel you are safe by handling a lower delay.

Hope this helps someone in the near future ;)


I have just tested Google Geocoder and got the same problem as you have. I noticed I only get the OVER_QUERY_LIMIT status once every 12 requests So I wait for 1 second (that's the minimum delay to wait) It slows down the application but less than waiting 1 second every request

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
}

With the basic holdOn method :

private void holdOn(long delay) {
        try {
            Thread.sleep(delay);
        } catch (InterruptedException ex) {
            // ignore
        }
    }

Hope it helps

참고URL : https://stackoverflow.com/questions/2419219/how-do-i-geocode-20-addresses-without-receiving-an-over-query-limit-response

반응형