web-dev-qa-db-ja.com

OVER_QUERY_LIMIT応答を受信せずに20の住所をジオコーディングするにはどうすればよいですか?

Google Geocoder v3を使用して、20個の住所をジオコーディングしようとすると、それらの間隔を1秒以下にしない限りOVER_QUERY_LIMITを取得しますが、マーカーがすべて配置されるまでに20秒かかります。

座標を事前に保存する以外の方法はありますか?

いいえ、実際には他の方法はありません。多くの場所があり、それらを地図に表示したい場合、最善の解決策は次のとおりです。

  • 場所が作成されたときに、ジオコーダーを使用して緯度と経度を取得する
  • それらを住所とともにデータベースに保存する
  • 地図を表示する場合は、保存されている緯度と経度を使用します。

もちろん、これは、ロケーションの相談よりもロケーションの作成/変更がはるかに少ないことを考慮しています。


はい、場所を保存するときにもう少し作業が必要になることを意味しますが、次のことも意味します。

  • 地理座標で検索できるようになります
    • つまり、「現在の場所の近くにあるポイントのリストが必要です
  • 地図の表示がはるかに高速になります
    • 20を超える場所がある場合でも
  • ああ、また、(最後ですが、少なくとも):これは動作します;-)
    • N秒以内にXジオコーダーコールの制限に達する可能性は低くなります。
    • また、1日あたりYジオコーダーコールの制限に達する可能性は低くなります。
84
Pascal MARTIN

実際には、リクエストごとに1秒間待つ必要はありません。各リクエストの間に200ミリ秒待機すると、OVER_QUERY_LIMIT応答を回避でき、ユーザーエクスペリエンスはまずまずであることがわかりました。このソリューションを使用すると、4秒間で20個のアイテムをロードできます。

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

  setTimeout(function(){

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

  }, 200 * i);

}
20
gabeodess

残念ながら、これはGoogleマップサービスの制限です。

現在、ジオコーディング機能を使用するアプリケーションで作業しており、ユーザーごとに一意の住所をそれぞれ保存しています。 Googleマップから返された情報に基づいて住所情報(都市、道路、州など)を生成し、緯度/経度情報もデータベースに保存します。これにより、コードを再コーディングする必要がなくなり、適切にフォーマットされたアドレスが提供されます。

これを行うもう1つの理由は、特定のIPアドレスからジオコーディングできるアドレスの数に毎日の制限があるためです。そのため、人のためにアプリケーションが失敗することは望ましくありません。

6
Zachary Wright

140個の住所をジオコーディングしようとすると、同じ問題に直面しています。

私の回避策は、次のジオコーディングリクエストのループごとにsleep(100000)を追加することでした。リクエストのステータスがOVER_QUERY_LIMITの場合、usleepが50000増加し、リクエストが繰り返されます。

そして、すべての受信データ(lat/long)は、ページがロードされるたびにリクエストを実行しないようにXMLファイルに保存されます。

2
gray

編集:

このソリューションは純粋なjsであることを忘れていましたが、必要なのはpromiseshttps:// 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マップなどを処理するために作成した大きなライブラリの一部にすぎないため、コメントが混乱する可能性があることに注意してください。

使い方は非常に簡単ですが、アプローチは少し異なります:一度に1つのアドレスをループして解決するのではなく、アドレスの配列をクラスに渡す必要があり、それ自体で検索を処理し、約束を返します、解決されると、すべての解決済み(および未解決)アドレスを含む配列を返します。

例:

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を自由に編集してください。

これが近い将来誰かを助けることを願っています;)

1
briosheje

Google Geocoderをテストしたところ、同じ問題が発生しました。 12リクエストごとにOVER_QUERY_LIMITステータスしか取得できないことに気づいたので、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
        }
    }

それが役に立てば幸い

0
Hugues