不規則な多角形の重心(ラベルポイント)を見つけるためのアルゴリズム


13

Googleマップで不規則な形状のポリゴンの重心(またはラベルポイント)を見つける必要があります。パーセル用にInfoWindowsを表示していますが、表面にあることが保証されているInfoWindowを固定する場所が必要です。以下の画像をご覧ください。

代替テキスト 代替テキスト

実際には、Google Maps固有のものは必要ありません。このポイントを自動的に見つける方法のアイデアを探しているだけです。

私の最初のアイデアは、平均の緯度と経度を取り、そこからランダムに配置して、ポリゴンと交差する点を見つけることにより、「偽」の重心を見つけることでした。ポイントインポリゴンコードはすでに持っています。これは私にはひどく「ハッキング」のようです。

ジオメトリを出力するサーバー側コードにはアクセスできないため、ST_PointOnSurface(the_geom)のようなことはできません。

回答:


6

早くて汚い:「偽」の重心がポリゴンにない場合、そのポイントに最も近い頂点を使用します。


私はこれについて考えていませんでした。理想的には、エッジではなく多角形のこのポイントが欲しいのですが、これは私が頼りにするものかもしれません。
ジェイソン

エッジポイントが見つかったら、そのポイントを中心とする小さな正方形とポリゴンを交差させ、交差の重心を選択できます。正方形が十分に小さい場合、これは内部ポイントであることが保証されます(もちろん、エッジに非常に近くなります)。
whuber

@Jason実際の重心を使用する場合、この問題に遭遇する可能性は低くなります。何かをJavaScriptにすばやく翻訳するのはそれほど難しくないはずです:github.com/cartesiananalytics/Pipra/blob/master/src/…–
Dandy

私のソリューション(偽の重心からの光線)はほとんどの場合機能しますが、このソリューションはおそらくそのシンプルさと、少なくともエッジでポイントを見つけることが保証されており、簡単にシフトできるため、おそらく最もうまくいくと思います非常に少ない労力でポリゴンの内側に配置されます。
ジェイソン

3

これを見たくなるかもしれません:http : //github.com/tparkin/Google-Maps-Point-in-Polygon

提示したケースに一致するレイキャスティングアルゴリズムを使用しているようです。

それについてのブログ投稿がここにあります。 http://appdelegateinc.com/blog/2010/05/16/point-in-polygon-checking/


サーバー側でこれを実装する場合、JTS(Java)とGeos(C)の両方がこの機能を実装します。
-DavidF

ええ、おそらく、「計算された」重心がポリゴン内にあるかどうかを判断するためのコードをすでに持っていることを追加すべきでした。実際に欲しいのは、ポリゴン内に重心を作成する方法です。
ジェイソン

3

(古い)ESRIアルゴリズムは重心を計算し、ポリゴンへの包含をテストした後、必要に応じてポリゴン内に収まるまで水平に移動します。(これは、プログラミング環境内で利用可能な基本的な操作に応じて、さまざまな方法で実行できます。)これにより、ポリゴンの視覚的中心にかなり近いラベルポイントが生成される傾向があります。


1

http://econym.org.uk/gmapから人気のあるepolyコードを拡張することで問題を解決しました。基本的に私がやったことは:

  • 「偽の重心」から始まり、すべての角と側面に広がる一連の光線を作成します(合計8)
  • 各レイの下に10,20,30 ...%ずつポイントを作成し、このポイントが元のポリゴンにあるかどうかを確認します

以下の拡張epolyコード:

google.maps.Polygon.prototype.Centroid = function() {
var p = this;
var b = this.Bounds();
var c = new google.maps.LatLng((b.getSouthWest().lat()+b.getNorthEast().lat())/2,(b.getSouthWest().lng()+b.getNorthEast().lng())/2);
if (!p.Contains(c)){
    var fc = c; //False Centroid
    var percentages = [0.1,0.2,0.3,0.4,0.5,0.6,0.7,0.8,0.9]; //We'll check every 10% down each ray and see if we're inside our polygon
    var rays = [
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getNorthEast().lat(),fc.lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(fc.lat(),b.getNorthEast().lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getSouthWest().lat(),fc.lng())]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(fc.lat(),b.getSouthWest().lng())]}),
        new google.maps.Polyline({path:[fc,b.getNorthEast()]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getSouthWest().lat(),b.getNorthEast().lng())]}),
        new google.maps.Polyline({path:[fc,b.getSouthWest()]}),
        new google.maps.Polyline({path:[fc,new google.maps.LatLng(b.getNorthEast().lat(),b.getSouthWest().lng())]})
    ];
    var lp;
    for (var i=0;i<percentages.length;i++){
        var percent = percentages[i];
        for (var j=0;j<rays.length;j++){
            var ray = rays[j];
            var tp = ray.GetPointAtDistance(percent*ray.Distance()); //Test Point i% down the ray
            if (p.Contains(tp)){
                lp = tp; //It worked, store it
                break;
            }
        }
        if (lp){
            c = lp;
            break;
        }
    }
}
return c;}

まだ少しハッキーですが、動作するようです。


この方法は、一部の曲がりくねったポリゴンでは失敗します。たとえば、ポリライン{{0、9}、{10、20}、{9、9}、{20、10}、{9、0}、{20、-10}、{9、-9}をバッファリングします、{10、-20}、{0、-9}、{-10、-20}、{-9、-9}、{-20、-10}、{-9、0}、{-20、 10}、{-9、9}、{-10、20}、{0,9}}未満1/2。たとえば、ダンディのQADメソッドと比較しても非効率です。
whuber

1

それを行う別の「ダーティ」アルゴリズム:

  • ジオメトリの境界ボックスを取得します (Xmax, Ymax, Xmin, Ymin)

  • ( Xmin+rand*(Xmax-Xmin), Ymin+rand*(Ymax-Ymin) ) ジオメトリ内でランダムなポイントが見つかるまでループしますGoogle-Maps-Point-in-Polygonを使用


+1これは、2回目のヒットのかなりの可能性があるためです。ユーザーを困らせないように毎回「ランダム」が再現可能である限り、これも有効なソリューションです。有効なポイントにすぐにヒットしない可能性は、特に推測ポイントから始める場合は特に少ないです。
ダンディ

1
@Dandy:実際、場合によってはこれは非常に貧弱なアルゴリズムになる可能性があります。たとえば、狭い斜めのスライバーを考えてみましょう。これらは実際には存在し(たとえば、道路間口の長い区画)、境界ボックスの0.1%未満(場合によってははるかに少ない)を容易に占有できます。この手法でこのようなポリゴンをヒットする(95%の確信がある)ことを合理的に確認するには、約3,000回の反復が必要です。
whuber

@Whuber:悪いスタート地点を選んだ場合は、完了するまでに時間がかかる可能性があります。仮想的にクリックの95%がより望ましいジオメトリであると考える場合、これは5%の時間の問題にすぎない可能性があります。また、別のGIS.seの質問と同様に、パフォーマンスが目的である場合、単一のソリューションは存在しないため、ヒューリスティックに基づいて戦術を変更することをお勧めします。3000回の繰り返しでこれを実行する理由はありません。いつでも10後にQADに救済できます。場所が望ましい場合があるので、これを数回繰り返して試してみる価値があると思います。
ダンディ

@Dandy:しかし、QADソリューションの問題は何ですか?元のトライアルラベルポイントからポリゴンの内部バッファー内の最も近い頂点に移動することで、少し変更することもできます。QADのままですが、元のフィーチャの内部の場所に着陸することが保証されています。ところで、すぐに救済するあなたの戦略は良いものです。このようなランダムプローブをコーディングするときはいつでも、フィーチャの面積と境界ボックスの面積の比率を事前計算し、それを使用して成功までの予想時間を見つけ、それが長くなる可能性がある場合はすぐにユーザーに警告します。
whuber

@Whuber面積率ヒューリスティックは、面積を計算するときに重心を計算するだけなので、素晴らしいアイデアです。私のQADソリューションの問題に関して:それは端にあります。あなたが言うようにそのポイントを選択してバッファリングすると、その「小さな」半径はその狭いセクションの長さよりも大きくなる可能性があります。常にコーナーケースがあります。とにかくUIが乱雑になり、ジオメトリが不明瞭になるバルーンを作成するだけです。おそらく最高または最低の頂点を選択することをお勧めします。
ダンディ

1

厳密に内部の場所を好むという最近の説明に照らして、多角形の境界上にもない、内側軸変換上の任意の点を選択できます。(MATのコードがない場合は、ポリゴンをネガティブバッファリングすることで近似できます。バイナリまたはセカント検索は、MATの一部に近い小さな内部ポリゴンをすばやく生成します。境界上の任意のポイントを使用します。)


ジオメトリのエッジを使用して、エッジが対象のポリゴンの内部に収まるようにすることについてあなたが言っていたことを理解しています。このエッジ/頂点を作成する方法がわかりません。私が考えることができる唯一のものは、関心のあるポイントから選択したポイントのセグメントの反対側のセグメントに垂直な光線を交差させることによって仮想三角形を作ることです。これらの2つのポイントの中点は、その仮想三角形の頂点になります。
ダンディ

@Dandy:それが核心です。GISがネイティブに実行する内容に応じて、これを実行する方法は多数あります。たとえば、正の長さのセットで元のフィーチャと交差するレイを見つけると、その交差は線分セグメントの結合されていない結合になります。これらのセグメントの中心を使用します。別の方法は、フィーチャ上の任意のポイントから開始し(QEDメソッドが達成することが望ましい中間点付近)、中心に小さなシンプルなポリゴン(正方形など)を作成し、元のフィーチャと交差させ、一意の接続を選択することですコンポーネント...
whuber

(継続)...開始点を含み、そのコンポーネントの中心を再帰的に選択します。GISでフィーチャの境界を記述する頂点のシーケンスをループできる場合、使用可能なメソッドが多数あります。負のバッファーがサポートされている場合、最大距離の内部ポイントのセット(MATのサブセットである「スケルトン」)を繰り返し見つけることができます。これは少し高価ですが、プログラムがかなり簡単で、優れたラベルポイントを生成します。
whuber

0

なぜ、重心を垂直(緯度)位置のみに使用しないのですか?次に、その緯度の平均経度選択して、ラベルを水平に配置できます。(このためには、特定の緯度にあるポリゴンエッジの経度値を見つける必要がありますが、問題はありません)。

また、Uシェイプ、およびより複雑なシェイプにも注意してください。:)おそらく、それらの場合、情報ウィンドウがそのように方向付けられているため、右端の経度のペアの平均を選択します(各ペアはポリゴンのスライスに対応します)?

これにより、ポジショニングをもう少し制御できます。たとえば、より多くのポリゴンを表示するために、情報ウィンドウを垂直方向に66または75%に配置すると便利です。(そうでない場合もあります!しかし、調整するノブがあります。)


0

ユーザーが選択した場合、ユーザーがクリックしたポイントを使用して選択するのはどうですか。


マウスクリックまたは非空間クエリで選択できるため、常に機能するとは限りません。
ジェイソン

0

私もこれを解決しようとしています。ポリゴンには、説明する内容に入る交差線がないという条件を課しました。

したがって、私のアプローチでは三角測量を使用します。ランダムな頂点を取ります(場合によっては、極端なN、E、W、またはSの頂点を取り、物事を単純化できます)。

この頂点から、1頂点離れた頂点に線を引きます。つまり、頂点が頂点3の場合、頂点3 + 2を見てください。

元の頂点からこの頂点までの線を作成します。構築された行:

  1. 他の線と交差しない
  2. その中点はポリゴンの外側にありません

次に、ポリゴン内にある三角形を作成しました。成功した頂点がn + 2だった場合、三角形は{n、n + 1、n + 2}になります。これを{v、v1、v2}と呼びます。そうでない場合は、次の頂点を試して、すべての頂点が試されるまで続けます。

三角形が見つかったら、頂点vからv1とv2の中点まで線を引き、その中心を見つけます。その線の中点は、三角形の内側と多角形の内側にあることが保証されています。

私はまだこれをコーディングしていませんが、交差する線のあるポリゴンが実際にはこれが機能しないエキゾチックな状態を引き起こすことがわかると思います。それがあなたが持っているポリゴンのタイプである場合、ポリゴン上の各ラインセグメントをテストし、交差していないことを確認する必要があります。交差する線分をスキップして、うまくいくと思います。


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.