8

これはある程度詳細な質問です。最初に状況を説明し、次に私の実装を説明し、最後に質問をして理解を深めてください。

4 月 4 日の時点で更新が追加され、問題は 1 つの保留中の問題に絞り込まれています。最新情報については、この質問の下部を参照してください。

TLDR;

Google Maps Directions API から返された長いルートがあり、そのルートの標高チャートが必要です。残念ながら、GET 経由で要求され、URL の最大長が 2.048 文字を超えているため、機能しません。リクエストを分割しました。Promises を使用して正しい処理順序を保証します。しかし、標高データはルート全体で常に完全であるとは限らず、常に正しい順序で表示されるとは限らず、常に指定されたパスに従っているとは限らず、標高間の位置が数 km にまたがることがあります。

序章;

Google Maps DirectionsService レスポンスの標高チャートを作成しようとしていますが、ルートが長すぎるという問題に直面しています (これは、overview_path ごとの LatLng の数ではなく、距離に関連していないようです)。これは、ElevationService が 経由でリクエストGETされ、URL の最大長が 2048 文字であることが原因です。この問題はSO here にも記載されています

実装;

overview_path私は、DirectionsService (プロパティ) によって返されたパスをバッチに分割し、結果 ( elevationsElevationService メソッドによって返されたgetElevationsAlongPath)を連結するために、Google よりも賢く (実際にはそうではありませんが、少なくともそれを回避する方法を見つけようとしている) と考えました。.

  • 最良の詳細レベルを取得するために、バッチごとに 512 サンプルで ElevationService をクエリします。
  • また、ElevationService はサンプルをパスの長さに分散させるため、バッチごとの最大数を設定しLatLng、フル パスを処理するために必要なバッチの数を確認します ( totalBatches = overview_path.length / maxBatchSize)。
  • 最終的にルート案内を均等に広げると、完全なルートについて同じレベルの詳細を取得しようとします ( batchSize = Math.ceil(overview_path.length / totalBatches))。

ElevationService は非同期的に動作しますが、最初に setTimout を使用し、現在は Promises を使用している他の SO ユーザーの助けを借りて、すべての要求が正しい順序で処理されるようにしています。

私のコード

var maxBatchSize = 200;
var currentBatch = 0;
var promise = Promise.resolve();
var totalElevationBatches = Math.ceil(directions.routes[0].overview_path.length / maxBatchSize);
var batchSize =  Math.ceil(directions.routes[0].overview_path.length / totalElevationBatches);

while(currentBatch < totalElevationBatches) {
    promise = addToChain(promise, currentBatch, batchSize);
    currentBatch++;
}

promise.then(function() {
    drawRouteElevationChart(); // this uses the routeElevations to draw an AreaChart
});

function getRouteElevationChartDataBatchPromise(batch, batchSize) {
    return new Promise(function(resolve, reject) {
        var elevator = new google.maps.ElevationService();
        var thisBatchPath = [];

        for (var j = batch * batchSize; j < batch * batchSize + batchSize; j++) {
            if (j < directions.routes[0].overview_path.length) {
                thisBatchPath.push(directions.routes[0].overview_path[j]);
            } else {
                break;
            }
        }

        elevator.getElevationAlongPath({
            path: thisBatchPath,
            samples: 512
        }, function (elevations, status) {
            if (status != google.maps.ElevationStatus.OK) {
                if(status == google.maps.ElevationStatus.OVER_QUERY_LIMIT) {
                    console.log('Over query limit, retrying in 250ms');

                    resolve(setTimeout(function() {
                        getRouteElevationChartDataBatchPromise(batch, batchSize);

                    }, 250));
                } else {
                    reject(status);
                }
            } else {
                routeElevations = routeElevations.concat(elevations);
                resolve();
            }
        });
    });
}

function addToChain(chain, batch, batchSize){
    return chain.then(function(){
        console.log('Promise add to chain for batch: ' + batch);
        return getRouteElevationChartDataBatchPromise(batch, batchSize);
    });
}

サイドノート;

サービスが持つ 8 つのウェイポイントの制限に対処するために、DirectionService のリクエストもバッチ処理していますが、8 つ以下のウェイポイントの問題にも直面しているため、これが問題ではないことを確認できます。

問題;

私が直面している問題は次のとおりです。

  • 標高データは常にルートの完全なパスに従っているとは限りません。つまり、チャートの最後の標高ポイントはルートの終点から (遠く) 離れています。
  • 次のタスクの実行を約束がまだ待っていないように見えるかのように、標高データがランダムな順序で表示されることがあります。
  • 標高データは、特定のバッチで提供された特定LatLngの に常に従うとは限りません(スクリーンショットを参照)。overview_path
  • 標高間距離データはたくさんあります。LatLngバッチごとに最大 200 秒の均等に一致するバッチ サイズの 512 サンプルを要求している間、複数の km にまたがることがあります。

従うように指示されたoverview_pathに従っていない標高データ

Promises を使用して (および setTimtout でタイミングを合わせる前に) ElevationService をバッチ処理すると、すべての問題が解決すると考えましたが、解決した唯一の問題は、2.048 文字のリクエスト URL を超えず、上記の新しい問題に直面していることです。

助けていただければ幸いです

また、250レップを入れたいと思います。すぐにこの質問に報奨金を出しますが、現時点ではそれは不可能です。後で報奨金を追加して、記載されている問題を解決した回答に報奨金を授与できますので、お気軽に返信してください。250レップ。報奨金は、私を正しい方向に向けてくれたあなたへの感謝を示すために授与されました.

読んで返信してくれてありがとう!

4 月 4 日に更新され、保留中の問題が 1 つ残っています (現時点でわかる限り)。

ランダムな順序での標高の問題に取り組みました

ルート検索結果の動作に一貫性がないことに気付いたとき、いくつかの問題に取り組むことができました。これは明らかな理由で発生しました。非同期呼び出しがスケジュールされることが「約束」されていなかったため、順序が正しい場合もあれば、そうでない場合もありました。マーカーが正しく表示されていた (キャッシュされていた) ため、最初はこれに気づきませんでした。

上下間距離の問題に取り組みました

標高データを表示する div は幅が 300 ピクセルしかなく、多くのデータポイントが含まれていました。このように幅が狭いため、十分な数のポイントにカーソルを合わせることができず、互いに離れた位置にある標高ポイントがトリガーされました。

標高データがルートに沿って表示されない問題

どういうわけか、この問題も解決しましたが、幅が広いか、方向の順序が「有望」であるかどうかはわかりません。

保留中の問題: 標高データは必ずしも完全ではありません

唯一残っている問題は、標高データが常に完全なパスをカバーしているとは限らないことです。これは、コンソールにいくつかのメッセージを記録すると、すべての Promise-then が完了していない時点で標高グラフが描画されていることがわかるため、Promising ロジックのエラーが原因であると考えられます。 Query Limit エラーは、Google Maps API によって返されます。

Over Query Limit エラーが返されたときに同じチェーンを再起動するにはどうすればよいですか? 同じ関数を再度解決setTimeout(...)しないようにしましたが、単に起動しましたが、Promise は再起動されたバッチを解決していないようで、クエリ制限を超えていません。現在、これは私が設定した方法です(方向と仰角の両方について):

function getRouteElevationChartDataBatchPromise(batch, batchSize) {
    return new Promise(function(resolve, reject) {
        var elevator = new google.maps.ElevationService();
        var thisBatchPath = [];

        for (var j = batch * batchSize; j < batch * batchSize + batchSize; j++) {
            if (j < directions.routes[0].overview_path.length) {
                thisBatchPath.push(directions.routes[0].overview_path[j]);
            } else {
                break;
            }
        }

        elevator.getElevationAlongPath({
            path: thisBatchPath,
            samples: 512
        }, function (elevations, status) {
            if (status != google.maps.ElevationStatus.OK) {
                if(status == google.maps.ElevationStatus.OVER_QUERY_LIMIT) {
                    console.log('ElevationService: Over Query Limit, retrying in 200ms');

                    resolve(setTimeout(function() {
                        getRouteElevationChartDataBatchPromise(batch, batchSize);

                    }, 200));
                } else {
                    reject(status);
                }
            } else {
                console.log('Elevations Count: ' + elevations.length);
                routeElevations = routeElevations.concat(elevations);
                resolve();
            }
        });
    });
}
4

3 に答える 3

0

実装部分で提起された懸念、補足説明、および列挙された問題はすべてGoogle Maps Elevation APIでカバーされています。完全なドキュメントは、標高データについて地球上の位置をクエリするためのシンプルなインターフェイスを提供し、標高要求、パラメーターの使用、位置、パス、標高応答の指定など、発生したすべての問題に対処します。

概要で説明した懸念事項については、Google Maps Elevation API には標準およびプレミアムの使用制限があります。これらの制限は、Google Maps Elevation API の悪用を防ぐために適用されます。Google Maps Elevation API の使用制限では、使用制限と割り当てを増やすオプションについて詳しく説明しています。

あなたの懸念に対処する可能性のあるドキュメントからの追加メモ:

  1. 複数のポイントを通過すると、標高データが粗くなることに注意してください。ポイントの最も正確な標高値を取得するには、個別にクエリを実行する必要があります。
  2. リクエストされた正確な場所での正確な標高測定値が Google にない場合、サービスは補間を行い、最も近い 4 つの場所を使用して平均値を返します。
  3. 位置リクエストと同様に、path パラメータは一連の緯度と経度の値を指定します。ただし、位置リクエストとは異なり、パスは頂点の順序付けられたセットを指定します。頂点で標高データを返すのではなく、パス リクエストはパスの長さに沿ってサンプリングされ、各サンプルは互いに等距離にあります。
  4. Google Maps Elevation API は、可能な限り高い精度の単一ポイント クエリのデータを返します。複数の場所を含むバッチ クエリでは、精度の低いデータが返される場合があります。
于 2016-04-01T01:01:33.507 に答える
0

最後に残った問題も、この SO の質問の助けを借りて解決されました: How to re-run a javascript promise when failed? . したがって、jfriend00がこの質問に回答した場合、賞金を彼に与えることができます。

関数が statusで解決されることを確認するは、次のようにPromiseロジックを関数内に配置し、その関数を呼び出す必要がありました。OKOVER_QUERY_LIMITany other status

function getRouteElevationChartDataBatchPromise(batch, batchSize) {
    return new Promise(function(resolve, reject) {
        function run(batch, batchSize) {
            var elevator = new google.maps.ElevationService();
            var thisBatchPath = [];

            for (var j = batch * batchSize; j < batch * batchSize + batchSize; j++) {
                if (j < directions.routes[0].overview_path.length) {
                    thisBatchPath.push(directions.routes[0].overview_path[j]);
                } else {
                    break;
                }
            }

            elevator.getElevationAlongPath({
                path: thisBatchPath,
                samples: 512
            }, function (elevations, status) {
                if(status == google.maps.ElevationStatus.OK) {
                    routeElevations = routeElevations.concat(elevations);
                    resolve();
                } else if (status == google.maps.ElevationStatus.OVER_QUERY_LIMIT) {                        
                    setTimeout(function () {
                        run(batch, batchSize);
                    }, 200);
                } else {
                    reject(status);
                }
            });
        }

        run(batch, batchSize);
    });
}
于 2016-04-05T11:18:30.023 に答える