Đơn giản hóa đa giác lossless?


8

Có một thuật toán tiêu chuẩn / được đề xuất để đơn giản hóa một đa giác mà không thu hẹp bất kỳ ranh giới ban đầu nào của nó không?

Ngay bây giờ tôi đang sử dụng TopologyPreservingSimplifer trong JTS và gặp vấn đề sau này trong ứng dụng của mình khi tôi gặp phải các đa giác "mất mát". Lý tưởng nhất là tôi muốn tạo ra các đa giác đơn giản hóa nhỏ hơn thân tàu lồi nhưng vẫn là siêu khối của đa giác ban đầu của tôi.

giản thể

Cập nhật:

Cuối cùng tôi đã đưa ra một thuật toán không hoàn hảo được thừa nhận đặt một "trình bao bọc" xung quanh đa giác đầu vào, thu nhỏ nó cho đến khi không có diện tích vượt quá tỷ lệ phần trăm của tổng diện tích đầu vào, sau đó chạy một trình mô phỏng dòng có ngưỡng tốt hơn nhiều để loại bỏ bất kỳ điểm dư thừa dọc theo đường thẳng. Phụ thuộc 100% dữ liệu, nhưng tôi thấy khoảng 80% nén đỉnh với các vùng dư thừa tối thiểu. Tất cả thông tin phản hồi / ý kiến ​​đánh giá cao:

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. Tại sao bạn gọi nó là đơn giản hóa lossless ? Tôi nghĩ rằng nếu bạn đơn giản hóa một ranh giới, bạn sẽ mất chi tiết. 2. Bạn có thể đơn giản hóa các ranh giới và có các khu vực không mất mát , nhưng điều đó sẽ phá vỡ tiêu chí không thu hẹp ranh giới của bạn. 3. Tại sao bạn muốn cho phép ranh giới mở rộng và không thu hẹp? Hay tôi hiểu nhầm điều gì?
Martin F

1
Dữ liệu của tôi đại diện cho ranh giới chính trị. Tôi ổn với một phần mở rộng nhỏ của khu vực ban đầu nếu nó giúp giảm số lượng đỉnh. Tôi muốn tránh loại bỏ những người từ khu vực ban đầu. Chính xác của bạn, tôi quan tâm đến việc đơn giản hóa khu vực lossless .
dùng1538028

Câu trả lời:


6

Bạn có thể chỉ cần kết hợp với đa giác ban đầu sau khi đơn giản hóa.


1
Mặc dù điều này hoạt động, nó có thể tồi tệ hơn đa giác ban đầu!
whuber

Nó có thể tồi tệ hơn? Tôi không thể nghĩ ra một ví dụ tồi tệ hơn - đoán rằng có thể có một ví dụ. Nói chung, mặc dù nó sẽ là một sự đơn giản hóa được giới hạn bởi thân tàu lồi.
flitmonkey

1
Nó phụ thuộc vào thuật toán được sử dụng bởi "trình mô phỏng cấu trúc liên kết." Một số đơn giản hóa có thể không bảo tồn bất kỳ đỉnh nào dọc theo một cung, do đó, sự kết hợp của phiên bản đơn giản hóa với bản gốc nhất thiết sẽ có nhiều đỉnh hơn bản gốc. Do đó, để biết liệu khuyến nghị của bạn là hữu ích hay ngược lại, người ta sẽ cần phải hiểu các chi tiết của việc đơn giản hóa.
whuber

4
Đây có thể là một câu trả lời tốt cho câu hỏi "chính xác" đang được hỏi, nhưng tôi không chắc câu hỏi đúng đang được hỏi hoặc vì những lý do đúng đắn.
Martin F

1

Nếu TopologyPreservingSimplifer dựa trên thuật toán Douglas-Peucker, như đã nói ở vividsolutions (người tạo ra JTS), nó thường sẽ không thay đổi các khu vực đa giác. Tuy nhiên, mỗi đa giác phải có chuỗi kết quả của các khoản lãi và lỗ nhỏ (cân bằng tổng thể).

Nếu bạn đang tập trung vào một đa giác đơn, hoặc một nhóm đa giác nhỏ, và bạn cho phép chúng mở rộng nhưng không thu hẹp (với chi phí của hàng xóm của họ) thì bạn đang đưa ra sự thiên vị vào phân tích của mình.

phụ lục

Vì vậy, tôi tin rằng sự lựa chọn ban đầu của bạn, TopologyPreservingSimplifer, là giải pháp chính xác.


Đây là những bình luận tốt - nhưng họ đọc như bình luận thay vì trả lời cho câu hỏi. Nếu bạn có lẽ đang cố gắng (hơi khó hiểu) để đề xuất Douglas-Peucker làm giải pháp, vui lòng xem xét đưa ra đề xuất đó rõ ràng hơn một chút.
whuber

1
@whuber, tôi chắc chắn đã không cố gắng lách luật và thêm một kết luận theo lời khuyên của bạn - ngay cả sau khi cập nhật của OP không có gì để tôi hiểu vấn đề hoặc lý luận.
Martin F

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.