Como executar a suavização de linhas SIA ou Bezier no PostGIS?

9

Alguém pode fornecer um exemplo de SQL para suavizar cadeias de linhas da tabela postgis usando curvas de Bezier ou o algoritmo de Média Iterativa ( SIA )?

nextstopsun
fonte

Respostas:

6

Criei um script pequeno e ingênuo que converte LineStrings de entrada em CompoundCurves com base em algumas heurísticas.

O que faz:

  • Corta cantos afiados para criar resultados visualmente mais atraentes do que os dados originais.
  • Usa plpgsql. Não são necessárias extensões adicionais.
  • Aceita um "fator de suavização" opcional entre 0 e 100, além de uma geometria.

O que não faz:

  • Processa MultiLineStrings. Para qualquer outro tipo de geometria, ele simplesmente retorna a entrada.
  • Usa os valores de Z e M. Simplesmente os deixa cair. Use isso apenas para fins cartográficos 2D.
  • Cria resultados matematicamente corretos. Os resultados estão longe de estar corretos e podem até ser visualmente estéticos em alguns casos (por exemplo, cantos afiados). Eu não testei completamente. Sempre revise os resultados!
  • Corre rápido. Tenho certeza de que pode ser reescrito para uma forma muito mais otimizada.
  • Real alisamento. Existem algoritmos muito melhores (por exemplo, o Chaiken ou os mencionados na pergunta) para usar na suavização real. Essa resposta é direcionada a pessoas como eu, procurando uma abordagem PostGIS pura, criando automaticamente algum tipo de linha curva a partir de dados reais.

O script:

CREATE OR REPLACE FUNCTION CreateCurve(geom geometry, percent int DEFAULT 40)
    RETURNS geometry AS
$$
DECLARE
    result text;
    p0 geometry;
    p1 geometry;
    p2 geometry;
    intp geometry;
    tempp geometry;
    geomtype text := ST_GeometryType(geom);
    factor double precision := percent::double precision / 200;
    i integer;
BEGIN
    IF percent < 0 OR percent > 100 THEN
        RAISE EXCEPTION 'Smoothing factor must be between 0 and 100';
    END IF;
    IF geomtype != 'ST_LineString' OR factor = 0 THEN
        RETURN geom;
    END IF;
    result := 'COMPOUNDCURVE((';
    p0 := ST_PointN(geom, 1);
    IF ST_NPoints(geom) = 2 THEN
        p1:= ST_PointN(geom, 2);
        result := result || ST_X(p0) || ' ' || ST_Y(p0) || ',' || ST_X(p1) || ' ' || ST_Y(p1) || '))';
    ELSE
        FOR i IN 2..(ST_NPoints(geom) - 1) LOOP
            p1 := ST_PointN(geom, i);
            p2 := ST_PointN(geom, i + 1);
            result := result || ST_X(p0) || ' ' || ST_Y(p0) || ',';
            tempp := ST_Line_Interpolate_Point(ST_MakeLine(p1, p0), factor);
            p0 := ST_Line_Interpolate_Point(ST_MakeLine(p1, p2), factor);
            intp := ST_Line_Interpolate_Point(
                ST_MakeLine(
                    ST_Line_Interpolate_Point(ST_MakeLine(p0, p1), 0.5),
                    ST_Line_Interpolate_Point(ST_MakeLine(tempp, p1), 0.5)
                ), 0.5);
            result := result || ST_X(tempp) || ' ' || ST_Y(tempp) || '),CIRCULARSTRING(' || ST_X(tempp) || ' ' || ST_Y(tempp) || ',' || ST_X(intp) || ' ' ||
            ST_Y(intp) || ',' || ST_X(p0) || ' ' || ST_Y(p0) || '),(';
        END LOOP;
        result := result || ST_X(p0) || ' ' || ST_Y(p0) || ',' || ST_X(p2) || ' ' || ST_Y(p2) || '))';
    END IF;
    RETURN ST_SetSRID(result::geometry, ST_SRID(geom));
END;
$$
LANGUAGE 'plpgsql' IMMUTABLE;

Como ele retorna curvas em um tipo de geometria, se você deseja usá-lo em um GIS, como o QGIS, é necessário envolvê-lo nas funções do PostGIS, convertendo-as. A sintaxe de uso pretendida é:

SELECT ST_AsText(ST_CurveToLine(CreateCurve(geom))) AS geom FROM linestringtable;
Gabor Farkas
fonte
Este foi um salva-vidas! Obrigado pelo script. Parece que a suavização do Chaikin estará disponível como uma função do postgis 2.5 em diante, pelo que estou ansioso.
She_weeds # 15/18
1

Esta ainda é uma questão em aberto no PostGIS (e em outras ferramentas GIS), conforme declarado no livro "PostGIS in Action" no capítulo 2.2.6 "Geometrias curvas".

Aqui estão algumas referências a algoritmos e código:

Stefan
fonte
Eu adicionei os links postgis.17.x6 ...
Martin F
0

Você pode tentar converter suas cadeias de linhas em curvas com ST_LineToCurve e depois voltar para cadeias de linhas com ST_CurveToLine .

Você pode definir o número de segmentos por quarto de círculo que deseja em ST_CurveToLine.

FredB
fonte
O LineToCurve foi criado para entregar as saídas do CurveToLine, não para extrair curvas da entrada arbitrária.
Paul Ramsey
@PaulRamsey seria mais suave a suavização nas próximas versões do Postgis? Eu estive pensando em algo assim, por exemplo: webhelp.esri.com/arcgisdesktop/9.2/…
Gery