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 вы получите одну точку).