Simplificação sem polígonos sem perdas?

8

Existe um algoritmo padrão / recomendado para simplificar um polígono sem reduzir nenhum de seus limites originais?

No momento, estou usando TopologyPreservingSimplifer no JTS e encontrando problemas mais tarde no meu aplicativo quando encontro polígonos "com perdas". Idealmente, eu gostaria de produzir polígonos simplificados que são menores que o casco convexo, mas permanecem um superconjunto do meu polígono original.

simplificado

Atualizar:

Acabei criando um algoritmo reconhecidamente imperfeito que coloca um "invólucro" em torno do polígono de entrada, reduz-o até que nenhuma área em excesso exceda uma porcentagem da área total da entrada e, em seguida, executa um simplificador de linha com um limite muito mais fino para remover quaisquer pontos redundantes ao longo de linhas retas. 100% dependente de dados, mas estou vendo cerca de 80% de compactação de vértices com áreas mínimas em excesso. Todos os comentários / comentários apreciados:

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;
    }
}

}

user1538028
fonte
5
1. Por que você chamaria isso de simplificação sem perdas ? Acho que se você está simplificando um limite, está perdendo detalhes. 2. Você pode simplificar os limites e ter áreas sem perdas , mas isso quebraria o critério de não diminuir os limites. 3. Por que você deseja permitir que os limites se expandam e não encolhem? Ou entendo algo errado?
Martin F
1
Meus dados representam limites políticos. Eu estou bem com uma pequena extensão da área original, se isso ajudar a diminuir a contagem de vértices. Eu quero evitar abater pessoas da área original. Você está correto, estou interessado na simplificação da área sem perdas .
user1538028

Respostas:

6

Você pode simplesmente se unir ao polígono original após a simplificação.

flitmonkey
fonte
1
Embora isso funcione, pode ser pior que o polígono original!
whuber
Pode ser pior? Não consigo pensar em um exemplo que seja pior - acho que pode haver um. Em geral, embora seja uma simplificação limitada pelo casco convexo.
flitmonkey
1
Depende do algoritmo usado pelo "simplificador de topologia". Alguns simplificadores podem não preservar nenhum dos vértices ao longo de um arco; portanto, a união da versão simplificada com o original terá necessariamente mais vértices que o original. Assim, para saber se sua recomendação é útil ou o contrário, seria necessário entender os detalhes da simplificação.
whuber
4
Essa pode ser uma boa resposta para a pergunta "exata" que está sendo feita, mas não sei se a pergunta certa está sendo feita ou pelos motivos certos.
Martin F
1

Se o TopologyPreservingSimplifer for baseado no algoritmo Douglas-Peucker, como diz em vividsolutions (criadores do JTS), geralmente não mudará as áreas dos polígonos. Cada polígono deve , no entanto, ter seqüências resultantes de pequenos ganhos e perdas (equilibrando-se no geral).

Se você estiver focando em um único polígono ou em um pequeno grupo de polígonos, e permitir que eles expandam, mas não encolhem (às custas de seus vizinhos), você estará introduzindo um viés em sua análise.

termo aditivo

Portanto, acredito que sua escolha original, TopologyPreservingSimplifer, é a solução correta.

Martin F
fonte
Esses são bons comentários - mas eles são lidos como comentários em vez de uma resposta para a pergunta. Se talvez você esteja tentando (de maneira um pouco cautelosa) propor Douglas-Peucker como a solução, considere fazer essa recomendação um pouco mais claramente.
whuber
1
@whuber, eu definitivamente não estava tentando ser cauteloso e acrescentei uma conclusão conforme o seu conselho - mesmo após a atualização do OP, que não acrescenta nada à minha compreensão do problema ou do raciocínio.
Martin F