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


83

与えられた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))

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

ここに画像の説明を入力してください

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

回答:


70

これはとても良い質問です。少し前に、同じアルゴリズムをc#に実装しました。アルゴリズムは、2つのポリゴンの共通の輪郭を構築します(つまり、穴のないユニオンを構築します)。ここにあります。


ゴール

手順1.ポリゴンを説明するグラフを作成します。

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

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

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

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

ステップ2.作成されたグラフを確認します

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

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

手順3.左下の頂点を見つけます。

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

左下のポイント

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

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

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

2つのベクトルを計算します。現在のエッジのvector1と、次にアクセスされていない各エッジのvector2です(図を参照)。

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

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

その結果、最大角度のエッジ(および対応する次の頂点)が得られます。

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

備考

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

2
スカラー積を比較するには、スカラー積を計算する前にベクトルを正規化する必要があることに注意してください(つまり、ベクトル座標をその長さで除算します)。とにかく、この答えをありがとう。
eyalzba 2017年

このアルゴリズムには名前がありますか、それともあなた自身が作成したものですか?
アンドレス・オビエド

どこかで読んだのですが、どこでいつか覚えていません=)
xtmq18年

注:穴のないポリゴンユニオンを参照してください。これは別の図を示しています。2つのポリゴンが重なっていますが、どちらもカバーしていない「穴」があります。そこに@ xtmqさんのコメントあたりのように、このアルゴリズムは、その穴(それがあるにもかかわらず、「塗りつぶし」ではないのいずれかの入力ポリゴンの一部)。代わりに、これらの穴を穴として「保持」したい場合は、(a)穴を計算し、(b)「穴のセット」を返します[一部のグラフィックシステム/モードでは、これらの穴を出力ポリゴンセットに含めることができます引き出されたときに、そして穴になります] ...。
ToolmakerSteve

2
...「(a)穴を計算する」を行うには、「ステップ4.共通の輪郭を作成する」で一度も訪れたことがない点を探します。これらのポイントの1つを使用して、穴を「開始」します。メインの出力ポリゴンですでに使用されているポイントを除いて、同様の「輪郭」アルゴリズムを実行します。結果のポリゴンは「穴」です。すべてのポイントがポリゴンまたは穴に含まれるまで繰り返します。
toolmakerSteve 2018年

11

これは挑戦的ですがよく理解されているトピックであり、「ポリゴンに対する正規化されたブール演算」という名前で呼ばれることがよくあります。あなたは、で見えるかもしれません 。このMathOverflowの答え(から下図含み、アランMurtaのクリッピングライブラリをOPさんはピンクの組合で、)結合します


      BooleanOps



2
この男は文字通りこれについて本を書いた;)
コンスタンティン

6

どのポイントが内部にある判別する必要があります。これらのポイントを削除した後、「外側」のポイントの1つのセットを他のセットに挿入できます。挿入ポイント(たとえば、右の図の矢印がある場所)は、入力セットからポイントを削除する必要がある場所です。


1
バークにリンクするための+1。30秒遅く、私はあなたをそれに打ち負かしたでしょう:)
David Seiler

4

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

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

次に:和集合の形を作ります。AとBのすべての頂点、および交点を取得して、最終的な形状に含まれる頂点を除外することができます。これらの点を見つけるには、Bの内側にあるAの頂点と、Aの内側にあるBの頂点を見つけることができるように見えます。


はい、本当の問題は、2つの追加された交点をどのように計算するかです。
Pacerier 2013年


2

BSPツリーを使用して見た解決策をここで説明します

基本的には、ポリゴンBの内側にあるポリゴンAのエッジ(部分的なエッジを含み、 BSPツリーます)のます。次に、あなたが定義することができるA  /  B〜(〜のようにA  / \〜B逆転〜表すポリゴンの巻線)、/組合および/ \表し交差点を示しています。


2

これは非常に古い質問ですが、Union_、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;
}

1
必要に応じて、ポリゴンを「修正」することを忘れないでください。参照してくださいstackoverflow.com/questions/22258784/...
anumi

1

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


2
「国をグループ化するとき、重複がないことを願っています」...すべての国が自国または近隣諸国の国境に同意しているわけではありませんが、同意した場合はよいでしょう。
frustratedWithFormsDesigner 2010

2
@FrustratedWithFormsDesignerは確かですが、ほとんどの地図製作者は、紛争地域を政治的同盟国に割り当てるか、それ自体が別のエンティティとして割り当てます。そのため、私のアルゴリズムをナイーブと表現しています...
Rowland Shaw


1

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

AngusJohnsonの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)

クリッパーは、ここでは、C ++で直接提供されています:angusj.com/delphi/clipper.php
Catskul
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.