web-dev-qa-db-ja.com

複雑なポリゴンを結合するにはどうすればよいですか?

2つのポリゴンがある場合:

POLYGON((1 0, 1 8, 6 4, 1 0))
POLYGON((4 1, 3 5, 4 9, 9 5, 4 1),(4 5, 5 7, 6 7, 4 4, 4 5))

結合(結合されたポリゴン)を計算するにはどうすればよいですか?

enter image description here

デイブの例 はSQLサーバーを使用してユニオンを生成しますが、コードで同じことを行う必要があります。実際の数学を公開する任意の言語の数式またはコード例を探しています。国を動的に地域に組み合わせた地図を作成しようとしています。ここで関連する質問をしました。 地理的形状のグループ化

73
grenade

これは非常に良い質問です。少し前に同じアルゴリズムをc#に実装しました。アルゴリズムは、2つのポリゴンの共通の輪郭を作成します(つまり、穴のない結合を作成します)。ここにあります。


Goal

ステップ1.ポリゴンを記述するグラフを作成します。

入力:最初のポリゴン(nポイント)、2番目のポリゴン(mポイント)。出力:グラフ。頂点-交点のポリゴンポイント。

交差点を見つける必要があります。両方のポリゴン[O(n * m)]のすべてのポリゴンサイドを反復処理し、交差点を見つけます。

  • 交差点が見つからない場合は、頂点を追加してエッジに接続します。

  • 交差点が見つかった場合は、開始点までの長さで並べ替え、すべての頂点(開始点、終了点、交差点)を追加し、それらを(並べ替え済みの順序で)Edgeに接続します。 Graph

ステップ2.構築されたグラフを確認する

グラフの作成時に交点が見つからなかった場合、次のいずれかの条件があります。

  1. Polygon1にはpolygon2が含まれています-polygon1を返します
  2. Polygon2にはpolygon1が含まれています-polygon2を返します
  3. Polygon1とpolygon2は交差しません。 polygon1 AND polygon2を返します。

ステップ3.左下の頂点を見つけます。

最小のxおよびy座標(minx、miny)を見つけます。次に、(minx、miny)とポリゴンのポイント間の最小距離を見つけます。このポイントは左下のポイントになります。

Left-bottom point

ステップ4.共通の輪郭を作成します。

左下のポイントからグラフを走査し始め、そこに戻るまで続けます。最初に、すべてのエッジを未訪問としてマークします。すべての反復で、次のポイントを選択し、訪問済みとしてマークする必要があります。

次の点を選択するには、反時計回り方向に最大の内角を持つエッジを選択します。

2つのベクトルを計算します。現在のEdgeのvector1と、次の未訪問の各Edgeのvector2です(図を参照)。

私が計算するベクトルの場合:

  1. スカラー積(ドット積)。ベクトル間の角度に関連する値を返します。
  2. ベクトル積(クロス積)。新しいベクトルを返します。このベクトルのz座標が正の場合、スカラー積は反時計回りの方向に直角を与えます。それ以外の場合(z座標は負)、ベクトル間の取得角度を360-スカラー積からの角度として計算します。

その結果、最大の角度を持つEdge(および対応する次の頂点)を取得します。

渡された各頂点を結果リストに追加します。結果リストはユニオンポリゴンです。 Vectors

備考

  1. このアルゴリズムにより、複数のポリゴンをマージして、ポリゴンのペアに繰り返し適用できます。
  2. 多くのベジェ曲線と線で構成されるパスがある場合は、最初にこのパスを平坦化する必要があります。
56
xtmq

これはやりがいがありますが、よく理解されているトピックであり、「ポリゴンに対する正規化されたブール演算」という名前で呼ばれることがよくあります。 このMathOverflowの回答 を見ると、下の図が含まれています( Alan Murtaのクリッピングライブラリ から)、OPのピンクのユニオン結合


BooleanOps
8
Joseph O'Rourke

どのポイントが内側にあるかを決定する が必要です。これらのポイントを削除した後、「外部」ポイントのセットをもう一方に挿入できます。挿入ポイント(たとえば、右側の図に矢印がある場所)は、入力セットからポイントを削除する必要があった場所です。

6

良い質問!これを試したことはありませんが、今すぐ試してみます。

最初に、これら2つの形状が重複する場所を知る必要があります。これを行うには、ポリゴンAのすべてのエッジを見て、それが交差する場所とポリゴンBのエッジを確認します。この例では、2つの交差点が必要です。

次に、ユニオン形状を作成します。 AとBのすべての頂点、および交差点を取得し、最終形状に含まれる頂点を除外できます。これらの点を見つけるには、Bの内側にあるAの頂点と、Aの内側にあるBの頂点を見つけることができるように見えます。

gpc を試してください。

4
lhf

BSPツリーを使用して見た解決策は here で説明されています。

基本的に、多角形の内部にある多角形[〜#〜] a [〜#〜]のエッジの結合に関する交差を記述します[〜#〜] b [〜#〜](部分エッジを含み、 BSPツリー を使用して計算されます)。次に、[〜#〜] a [〜#〜]/[〜#〜] bを定義できます。 [〜#〜]as〜(〜A/ \〜B)、ここで〜は多角形の巻きの反転を示し、/は結合を示し、/ \は交差を示します。

2
nornagon

国をグループ化するとき、重複しないことを願っています-共有された頂点を探すかなり素朴なアルゴリズムを取ることができます-単純なビューは、1つのポリゴンのポイントを反復処理し、他のポリゴンのいずれかにあるかどうかを確認します、同じ次または前のポイントを共有して、一致するかどうかを確認します。次に、共有頂点を削除してユニオンを作成します

1
Rowland Shaw

今日、この同じ問題を解決する必要があり、このlibで解決策を見つけました: http://www.cs.man.ac.uk/~toby/alan/software/

Java、Obj-C、C#、Lua、pythonなど)を含む多くの言語実装 ここのリスト があります。

1
ademar111190

私は同じ問題に直面しており、次の方法を使用して問題を解決しました

Angus JohnsonのClipperライブラリ(ver。6.4.2)のC++翻訳用のCythonラッパー https://github.com/fonttools/pyclipper

pc = pyclipper.Pyclipper()
def get_poly_union(polygons):
    pc.AddPaths(polygons, pyclipper.PT_SUBJECT, True)
    solution = pc.Execute(pyclipper.CT_UNION, pyclipper.PFT_NONZERO, pyclipper.PFT_NONZERO)
    return solution[0]

print_image = image.copy()
solution = get_poly_union(polygons_array) 
#polygons_array=[polygon,polygon,polygon, ...,polygon] and polygon=[point,point,point...,point]

cv2.drawContours(print_image, [np.asarray(solution)], -1, (0, 255, 0), 2)

plt.imshow(print_image)
1

これは非常に古い質問ですが、 nion _ Boostの関数が機能しました。

以下のスニペットをご覧ください。

#include <iostream>
#include <vector>

#include <boost/geometry.hpp>
#include <boost/geometry/geometries/point_xy.hpp>
#include <boost/geometry/geometries/polygon.hpp>

#include <boost/foreach.hpp>


int main()
{
    typedef boost::geometry::model::polygon<boost::geometry::model::d2::point_xy<double> > polygon;

    polygon green, blue;

    boost::geometry::read_wkt(
        "POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", green);

    boost::geometry::read_wkt(
        "POLYGON((5 5, 5 15, 15 15, 15 5, 5 5))", blue);

    std::vector<polygon> output;
    boost::geometry::union_(green, blue, output);

    int i = 0;
    std::cout << "green || blue:" << std::endl;
    BOOST_FOREACH(polygon const& p, output)
    {
        std::cout << i++ << ": " << boost::geometry::area(p) << std::endl;

        for (int i = 0; i < p.outer().size(); i++)
        {
            std::cout << p.outer().at(i).x() << " " << p.outer().at(i).y() << std::endl;
        }
    }



    return 0;
}
0
Yonatan