Postgis - как упростить начальную точку многоугольника

Я использую ST_Simplify для упрощения некоторых полигонов. Это хорошо работает с большинством из них, но у меня есть несколько полигонов с ненужной начальной точкой, которые я хотел бы удалить.

Вот пример с многоугольником, который можно упростить:

SELECT ST_AsText(ST_Simplify(ST_GeomFromText(
    'POLYGON((0 0, 5 0, 10 0, 10 10, 0 10, 0 0))'
    ), 1))

А вот пример с эквивалентным многоугольником (одинаковые точки, разные начальные точки), который нельзя упростить:

SELECT ST_AsText(ST_Simplify(ST_GeomFromText(
    'POLYGON((5 0, 10 0, 10 10, 0 10, 0 0, 5 0))'
    ), 1))

Я не ожидаю, что алгоритм Дугласа-Пекера (ST_Simplify) изменит начальную или конечную точку многоугольника. Что я должен сделать, чтобы упростить второй многоугольник?

РЕДАКТИРОВАТЬ У меня может быть несколько вогнутых многоугольников:

SELECT ST_AsText(ST_Simplify(ST_GeomFromText(
    'POLYGON((5 0, 10 0, 8 8, 10 10, 0 10, 0 0, 5 0))'
    ), 1))

3 ответа

Решение

Следуя примеру Джона Барсы, добавив дистанционный тест для упрощения точки закрытия и сделав так, чтобы он работал для всех геометрий, я получил следующее решение:

CREATE OR REPLACE FUNCTION simplify_geometry(geom geometry, tolerance float)
  RETURNS geometry AS
$BODY$

DECLARE geomType text;
DECLARE simplified geometry;
DECLARE testSegment geometry;
DECLARE simplifiedElements geometry[];

BEGIN

  geomType := GeometryType(geom);

  IF ST_IsEmpty(geom) THEN
    RETURN geom;
  ELSIF geomType ~ '(MULTI|COLLECTION)' THEN
    simplifiedElements := array(SELECT simplify_geometry((ST_Dump(geom)).geom, tolerance));
    RETURN ST_Collect(simplifiedElements);
  ELSIF geomType = 'LINESTRING' THEN
    simplified := ST_Simplify(geom, tolerance);

    IF ST_IsClosed(simplified) THEN
      testSegment := ST_MakeLine(ST_PointN(simplified, 2), ST_PointN(simplified, ST_NPoints(simplified)-1));
      IF ST_Distance(ST_StartPoint(simplified), testSegment) < tolerance THEN
        simplified := ST_RemovePoint(simplified, 0);
        simplified := ST_RemovePoint(simplified, ST_NPoints(simplified)-1);
        simplified := ST_AddPoint(simplified, ST_StartPoint(simplified));
      END IF;
    END IF;

    RETURN simplified;
  ELSIF geomType = 'POLYGON' THEN
    simplifiedElements := array(SELECT simplify_geometry(ST_ExteriorRing((ST_DumpRings(geom)).geom), tolerance));
    RETURN ST_MakePolygon(simplifiedElements[1], simplifiedElements[2:array_length(simplifiedElements,1)]);
  ELSE
    RETURN ST_Simplify(geom, tolerance);
  END IF;

END
$BODY$  LANGUAGE plpgsql IMMUTABLE

Как насчет использования ST_ConvexHull? Это должно быть идеально для этого случая:

SELECT ST_AsText(ST_ConvexHull(ST_GeomFromText(
    'POLYGON((5 0, 10 0, 10 10, 0 10, 0 0, 5 0))'
    )))

Эта функция делает то, что вы изначально просили. Общая идея состоит в том, чтобы снова вставить 2-ю точку, а затем изменить порядок строк, чтобы это была новая начальная точка, а затем упростить эту новую упорядоченную строку. Это может быть сделано более элегантно, возможно, проверка на повторяющиеся точки и поиск точки вставки, применение геометрических типов и проверка на правильность, но я считаю, что это делает то, что вы просили.

CREATE OR REPLACE FUNCTION simplify_movestart(geom geometry, tolerance float)  
  RETURNS geometry AS
$BODY$ 

declare linestring geometry;
declare dumped_points geometry[];
declare arr_len int;
declare x int;
declare fixed_geom geometry default 'LINESTRING EMPTY';

begin

  --get linestring from original polyon's outer ring
  linestring := st_exteriorring(geom);
  --add a new repeated point after start point 
  linestring := st_addpoint(linestring, st_pointn(linestring,2),2);

  --dump point to array
  dumped_points:=array(select (st_dumppoints(linestring)).geom);
  arr_len:= array_length(dumped_points,1);

  --create new linestring, starting from new start point
  for x in 3..arr_len  loop
     fixed_geom := st_addpoint(fixed_geom, dumped_points[x]);
  end loop;

  --add 1st and 2nd points at end
  fixed_geom := st_addpoint(fixed_geom, dumped_points[1], arr_len-2);
  fixed_geom := st_addpoint(fixed_geom, dumped_points[2], arr_len-1);

  --debug stuff
  raise notice 'new linestring : % ', st_astext(linestring);
  raise notice 'rearranged geom: %', st_astext(fixed_geom);
  --show simplified start point
  raise notice 'simplfied geom : %', st_astext(st_simplify(fixed_geom, $2));

  --return new, simplified polygon
  return st_makepolygon(st_simplify(fixed_geom, $2));

 end
$BODY$  LANGUAGE plpgsql VOLATILE

Тестирование на исходных полигонах дает:

select st_astext(simplify_movestart(st_geomfromtext('POLYGON((5 0, 10 0, 10 10, 0 10, 0 0, 5 0))'),1));
NOTICE:  new linestring : LINESTRING(5 0,10 0,10 0,10 10,0 10,0 0,5 0) 
NOTICE:  rearranged geom: LINESTRING(10 0,10 10,0 10,0 0,5 0,5 0,10 0)
NOTICE:  simplfied geom : LINESTRING(10 0,10 10,0 10,0 0,10 0)
POLYGON((10 0,10 10,0 10,0 0,10 0))

Примечание: я добавил параметр допуска, так как вы должны установить его намного меньше 1, чтобы работать с полигоном, который вы разместили в комментариях, чтобы упростить его корректно (с 1 вы получите одну точку).

Другие вопросы по тегам