ロスレスポリゴンの単純化?


8

元の境界を縮小せずにポリゴンを単純化するための標準/推奨アルゴリズムはありますか?

現在、私はJTS内でTopologyPreservingSimpliferを使用しており、アプリケーションで後で「損失のある」ポリゴンに遭遇したときに問題が発生します。理想的には、凸包よりも小さいが、元のポリゴンのスーパーセットのままである簡略化されたポリゴンを作成したいです。

簡略化

更新:

私は最終的に、入力ポリゴンの周りに「ラッパー」を配置し、余分な領域が入力の合計領域のパーセンテージを超えなくなるまで縮小する確かに不完全なアルゴリズムを思いつきました。直線に沿った冗長ポイント。100%データ依存ですが、余分な領域を最小限に抑えて約80%の頂点を圧縮しています。感謝/すべてのフィードバック/コメント:

public class LosslessPolygonSimplifier {
protected final static Logger logger = Logger.getLogger(LosslessPolygonSimplifier.class.getName());

public static Polygon simplify(Polygon input) {
    final double AREA_THRESHOLD = 0.005; // allow excesses up to half a percent of total original area
    final double LINE_THRESHOLD = 0.0001; // fine threshold to strip straight lines
    try {
        if (!input.isSimple()) {
            logger.warning("Attempting to simplify complex polygon!");
        }
        Polygon simple = simplifyInternal(input, AREA_THRESHOLD, LINE_THRESHOLD);
        return simple;
    }
    catch (Exception e) {
        logger.log(Level.WARNING, "Failed to simplify. Resorting to convex hull.\n " + input.toText(), e);
        try {
            // worst case scenario - fall back to convex hull
            // probably a result of a bow-tie LINESTRING that doubles back on itself due to precision loss?
            return (Polygon) input.convexHull();
        }
        catch (Exception e2) {
            // Is this even possible? Polygons that cross the anti-meridian?
            logger.log(Level.SEVERE, "Failed to simplify to convex hull: " + input.toText(), e2);
            return input; // Garbage In, Garbage Out
        }
    }
}

// TODO avoid creating triangles on long straight edges
public static Polygon simplifyInternal(Polygon original, double areaThreshold, double lineThreshold) {
    GeometryFactory gf = new GeometryFactory();
    Geometry excesses, excess, keepTotal, keepA, keepB, chA, chB, keep = null, elim = null;
    Polygon simplified = null, wrapper = (Polygon) original.convexHull();
    try {
        boolean done = false;
        while (!done) {
            done = true;
            excesses = wrapper.difference(original);
            for (int i = 0; i < excesses.getNumGeometries(); i++) {
                excess = excesses.getGeometryN(i);
                if (excess.getArea() / original.getArea() > areaThreshold) {
                    done = false; // excess too big - try to split then shrink
                    keepTotal = excess.intersection(original);
                    keepA = gf.createGeometryCollection(null);
                    keepB = gf.createGeometryCollection(null);
                    for (int j = 0; j < keepTotal.getNumGeometries(); j++) {
                        if (j < keepTotal.getNumGeometries() / 2) {
                            keepA = keepA.union(keepTotal.getGeometryN(j));
                        }
                        else {
                            keepB = keepB.union(keepTotal.getGeometryN(j));
                        }
                    }
                    chA = keepA.convexHull();
                    chB = keepB.convexHull();
                    keep = gf.createMultiPolygon(null);
                    if (chA instanceof Polygon) {
                        keep = keep.union(chA);
                    }
                    if (chB instanceof Polygon) {
                        keep = keep.union(chB);
                    }
                    elim = excess.difference(keep);
                    wrapper = (Polygon) wrapper.difference(elim);
                }
            }
        }
        new Assert(wrapper.getArea() >= original.getArea());
        new Assert(wrapper.getArea() <= original.convexHull().getArea());
        simplified = (Polygon) com.vividsolutions.jts.simplify.TopologyPreservingSimplifier.simplify(wrapper, lineThreshold);
        new Assert(simplified.getNumPoints() <= original.getNumPoints());
        new Assert(simplified.getNumInteriorRing() == 0);
        new Assert(simplified.isSimple());
        return simplified;
    }
    catch (Exception e) {
        if (original.isSimple()) {
            StringBuilder sb = new StringBuilder();
            sb.append("Failed to simplify non-complex polygon!");
            sb.append("\noriginal: " + original.toText());
            sb.append("\nwrapper: " + (null == wrapper ? "" : wrapper.toText()));
            sb.append("\nsimplified: " + (null == simplified ? "" : simplified.toText()));
            sb.append("\nkeep: " + (null == keep ? "" : keep.toText()));
            sb.append("\nelim: " + (null == elim ? "" : elim.toText()));
            logger.log(Level.SEVERE, sb.toString());
        }
        throw e;
    }
}

}


5
1.なぜそれをロスレス単純化と呼ぶのですか?境界を単純化すると、詳細が失われると思います。2.境界を簡略化してロスのない領域を作成することもできますが、境界を縮小しないという基準を破ることになります。3.境界を拡大させ、縮小させないのはなぜですか。または私は何かを誤解していますか?
マーティンF

1
私のデータは政治的境界を表しています。頂点数を減らすのに役立つ場合は、元の領域を少し拡張しても問題ありません。元のエリアから人を殺すことは避けたいです。正解です。ロスレスエリアの単純化に興味があります。
user1538028 2013年

回答:


6

単純化した後、単純に元のポリゴンと結合できます。


1
これは機能しますが、元のポリゴンよりも悪い場合があります!
whuber

悪化する可能性はありますか?もっと悪い例は思いつきません-あるかもしれませんが。一般に、凸包によって制限されるのは単純化です。
flitmonkey 2013年

1
それは、「トポロジー単純化」によって使用されるアルゴリズムに依存します。一部の単純化子は、円弧に沿ったどの頂点も保持しない可能性があります。そのため、元のバージョンとの簡略化バージョンの結合は、元のバージョンよりも多くの頂点を持つ必要があります。したがって、あなたの推奨が有用か逆かを知るためには、簡略化の詳細を理解する必要があります。
whuber

4
これ、「正確な」質問が行われた場合の適切な回答かもしれませんが、適切な質問が行われたか、または適切な理由で行われたかはわかりません。
マーティンF

1

TopologyPreservingSimpliferがDouglas-Peuckerアルゴリズムに基づいている場合、それはvividsolutions(JTSの作成者)で述べているように、通常はポリゴン領域を変更しません。ただし、各ポリゴンに、小さなゲインとロスのシーケンスが必要です(全体としてバランスを取ります)。

単一のポリゴンまたはポリゴンの小さなグループに焦点を合わせており、それらを拡大することは可能であるが、縮小しないことを許可する場合(それらの近隣を犠牲にして)、分析にバイアスを導入しています。

補遺

したがって、元の選択であるTopologyPreservingSimpliferが正しい解決策であると思います。


これらは良いコメントですが、質問に対する回答ではなくコメントのように読みます。(やや慎重に)ソリューションとしてDouglas-Peuckerを提案しようとしている場合は、その推奨事項をもう少し明確にすることを検討してください。
whuber

1
@whuber、私は間違いなく慎重にしようとはせず、あなたのアドバイスに従って結論を追加しました-問題や推論に対する私の理解に何も追加しないOPの更新後でも。
マーティンF

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