3

入力された住所が事前定義されたサービス地域の一部であるかどうかを判断する、Google マップを使用した小さなアプリケーションを作成しています。

ユーザーが住所を入力すると、PHP スクリプトがGeocoding APIから緯度/経度を取得し、地域の頂点を構成する一連の座標 (Maps が生成した KML ファイルから取得) を使用してレイキャスティングを適用します。

問題は次のとおりです。これはほとんどの場合機能しますが、サービス地域の一部の住所は適格と誤って報告され、地域の他の住所は不適格と報告されます。最初は、これは Google マップの精度の問題だと思っていましたが、Geocoding サービスで住所から生成された座標は正確です。それはおそらく式と関係があります。

ここにあります(他の場所で見つけたコードに基づいています):

// $points is an array full of Point objects (the vertices), which contain lat/long variables
// $ctr is simply a counter that we will test to see if it's even/odd
for ($i = 0, $j = sizeof($points) - 1; $i < sizeof($points); $j = $i++) {
    $p1 = $points[$i];
    $p2 = $points[$j];
    // $test_point is the lat/long pair of the user's address
    if ($p1->lat < $test_point->lat && $p2->lat >= $test_point->lat ||  $p2->lat < $test_point->lat && $p1->lat >= $test_point->lat)  {
        if ($p1->long + ($test_point->lat - $p1->lat)/($p2->lat - $p1->lat)*($p2->long - $p1->long) < $test_point->long)  
            $ctr++;
    }
}

私がここに欠けているものはありますか?自力で公式を導き出してみて、数学はある程度理解できたのですが、これでGoogleマップのGPS座標を使っても大丈夫でしょうか?

何が誤って報告されるかについて実際のパターンはないようです。境界に近い住所やサービス エリアの隅にある住所などをテストしましたが、うまくいきませんでした。また、注目に値するのは、このサービス エリアは都市の比較的小さな地域であり、州や国全体のエリアとは異なります.

4

3 に答える 3

2

まあ....あなたの2番目の if() は、減算のいずれかが負の数になる可能性があるという事実を補償しません。座標が厳密に順序付けられている場合にのみ機能します。

更新: http://rosettacode.org/wiki/Ray-casting_algorithmnには、プロセスを詳細に説明するさまざまな言語のアルゴリズムがたくさんあります (残念ながら、PHP のバージョンはありません)。ソリューションに欠けているように見えるのは、ポリゴンの外側にあることが保証されているポイントを選択することです。経度/緯度を扱っているので、簡単なはずです。次に、ポリゴンが閉じていることを確認します (つまり、Google マップがまだそうしていない場合は、最後のポイントから最初のポイントに戻ります)。

于 2013-01-04T03:31:34.807 に答える
0

$points時計回り (または反時計回りの順序) でカバレッジ エリアを表す多角形の角が配列に含まれていると仮定すると、コードは正しいように見えます。基本的には、指定されたポイントから 180 度子午線まで真東に引かれた線と交差するポリゴン エッジの数をカウントしています。

わかりやすくするために、おそらく次のように書き直します。

$p0 = end($points);
foreach ( $points as $p1 ) {
    // ignore edges of constant latitude (yes, this is correct!)
    if ( $p0->lat != $p1->lat ) {
        // scale latitude of $test_point so that $p0 maps to 0 and $p1 to 1:
        $interp = ($test_point->lat - $p0->lat) / ($p1->lat - $p0->lat);
        // does the edge intersect the latitude of $test_point?
        // (note: use >= and < to avoid double-counting exact endpoint hits)
        if ( $interp >= 0 && $interp < 1 ) {
            // longitude of the edge at the latitude of the test point:
            // (could use fancy spherical interpolation here, but for small
            // regions linear interpolation should be fine)
            $long = $interp * $p1->long + (1 - $interp) * $p0->long;
            // is the intersection east of the test point?
            if ( $long < $test_point->long ) {
                // if so, count it:
                $ctr++;
            }
        }
    }
    $p0 = $p1;
}

地域の境界が 180 度子午線を超えると、このコードはあらゆる種類の興味深い方法で壊れることに注意してください。そのため、太平洋の真ん中にサービス地域がある場合は使用しないでください。

それでも問題が解決しない場合は、$points配列によって記述されたポリゴンを地図上にプロットしてみてください。いくつかのポイントが間違った順序でリストされている場合など、思ったように見えないことに気付くかもしれません。

于 2013-01-08T01:08:23.990 に答える
0

光線がシェイプに接している場合、このアルゴリズムにはバグがあります。それが発生する可能性がある場合は、テスト ポイントの緯度にイプシロンを追加するだけです (Ilmari のコードの 3 行目)。

if ($test_point->lat == $p0->lat)
    $test_point->lat += 0.0000000001;

http://rosettacode.org/wiki/Ray-casting_algorithm (修正された URL)も参照してください。

ありがとう。

于 2013-08-28T06:10:39.407 に答える