From: Regina Obe Date: Tue, 5 Apr 2011 22:26:45 +0000 (+0000) Subject: Get rid of some tests in _ST_ConcaveHull and only attempt to make a polygon if the... X-Git-Tag: 2.0.0alpha1~1796 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=3cc1d006147cddceecbe3e5607556db5d641ff68;p=postgis Get rid of some tests in _ST_ConcaveHull and only attempt to make a polygon if the line is simple. ST_Covers is causing me quite a bit of greif as a testing tool. don't recall so many topo node this and that in GEOS 3.2 ST_Covers. Also add in some regress tests for ST_ConcaveHull -- more to come. git-svn-id: http://svn.osgeo.org/postgis/trunk@7006 b70326c6-7e19-0410-871a-916f4a2858ee --- diff --git a/postgis/postgis.sql.in.c b/postgis/postgis.sql.in.c index e54368971..2059c8251 100644 --- a/postgis/postgis.sql.in.c +++ b/postgis/postgis.sql.in.c @@ -5087,23 +5087,26 @@ CREATE OR REPLACE FUNCTION ST_MinimumBoundingCircle(geometry) -- Contributed by Regina Obe and Leo Hsu -- Availability: 2.0.0 ----------------------------------------------------------------------- -CREATE OR REPLACE FUNCTION _ST_ConcaveHull(param_inputgeom geometry) +CREATE OR REPLACE FUNCTION _st_concavehull(param_inputgeom geometry) RETURNS geometry AS $$ DECLARE vexhull GEOMETRY; var_resultgeom geometry; + var_inputgeom geometry; vexring GEOMETRY; cavering GEOMETRY; cavept geometry[]; seglength double precision; var_tempgeom geometry; + scale_factor integer := 1; i integer; BEGIN -- First compute the ConvexHull of the geometry vexhull := ST_ConvexHull(param_inputgeom); + var_inputgeom := param_inputgeom; --A point really has no concave hull IF ST_GeometryType(vexhull) = 'ST_Point' OR ST_GeometryType(vexHull) = 'ST_LineString' THEN RETURN vexhull; @@ -5111,6 +5114,12 @@ $$ -- convert the hull perimeter to a linestring so we can manipulate individual points vexring := CASE WHEN ST_GeometryType(vexhull) = 'ST_LineString' THEN vexhull ELSE ST_ExteriorRing(vexhull) END; + IF abs(ST_X(ST_PointN(vexring,1))) < 1 THEN --scale the geometry to prevent stupid precision errors - not sure it works so make low for now + scale_factor := 100; + vexring := ST_Scale(vexring, scale_factor,scale_factor); + var_inputgeom := ST_Scale(var_inputgeom, scale_factor, scale_factor); + --RAISE NOTICE 'Scaling'; + END IF; seglength := ST_Length(vexring)/least(ST_NPoints(vexring)*2,1000) ; vexring := ST_Segmentize(vexring, seglength); @@ -5119,7 +5128,7 @@ $$ ARRAY( SELECT - ST_ClosestPoint(param_inputgeom, pt ) As the_geom + ST_ClosestPoint(var_inputgeom, pt ) As the_geom FROM ( SELECT ST_PointN(vexring, n ) As pt, n FROM @@ -5129,34 +5138,27 @@ $$ ) ) ; + - var_resultgeom := ST_MakePolygon(ST_MakeLine(geom)) + var_resultgeom := ST_MakeLine(geom) FROM ST_Dump(cavering) As foo; + + IF ST_IsSimple(var_resultgeom) THEN + var_resultgeom := ST_MakePolygon(var_resultgeom); + --RAISE NOTICE 'is Simple: %', var_resultgeom; + ELSE /** will not result in a valid polygon -- just return convex hull **/ + --RAISE NOTICE 'is not Simple: %', var_resultgeom; + var_resultgeom := ST_ConvexHull(var_resultgeom); + END IF; - IF NOT ST_IsValid(var_resultgeom) THEN - --RAISE NOTICE '_ST_Concavehull invalid %', ST_AsText(var_resultgeom); - var_tempgeom := ST_BuildArea(var_resultgeom); -- try to make valid - IF NOT ST_IsValid(var_tempgeom) THEN - var_resultgeom := ST_Buffer(var_resultgeom,ST_Length(cavering)/1000, 'quad_segs=3'); -- try to make valid - END IF; - --if still invalid or doens't contain the geometry just return convex hull - IF NOT ST_IsValid(var_resultgeom) or ST_GeometryType(var_resultgeom) <> 'ST_Polygon' THEN - var_resultgeom := ST_ConvexHull(param_inputgeom); - ELSIF ST_GeometryType(param_inputgeom) ILIKE '%Geometry%' THEN - IF EXISTS(SELECT geom FROM ST_Dump(param_inputgeom) WHERE NOT ST_Covers(var_resultgeom,geom) ) THEN - --we have to explode inputgeom since geos doesn't support geometrycollections for containment check - var_resultgeom := ST_ConvexHull(param_inputgeom); - END IF; - ELSIF NOT ST_Contains(var_resultgeom, param_inputgeom) THEN - var_resultgeom := ST_ConvexHull(param_inputgeom); - END IF; + IF scale_factor > 1 THEN -- scale the result back + var_resultgeom := ST_Scale(var_resultgeom, 1/scale_factor, 1/scale_factor); END IF; RETURN var_resultgeom; END; $$ - LANGUAGE 'plpgsql' IMMUTABLE STRICT - COST 100; + LANGUAGE plpgsql IMMUTABLE STRICT; CREATE OR REPLACE FUNCTION ST_ConcaveHull(param_geom geometry, param_pctconvex float, param_allow_holes boolean) RETURNS geometry AS $$ diff --git a/regress/Makefile.in b/regress/Makefile.in index 380da3688..1c930d7c1 100644 --- a/regress/Makefile.in +++ b/regress/Makefile.in @@ -86,7 +86,8 @@ TESTS = \ tickets \ remove_repeated_points \ split \ - relate + relate \ + concave_hull # TESTS += wmsservers_old diff --git a/regress/concave_hull.sql b/regress/concave_hull.sql new file mode 100644 index 000000000..9b26a0167 --- /dev/null +++ b/regress/concave_hull.sql @@ -0,0 +1,42 @@ +-- $Id$ +-- Tests to confirm the concave hull area is <= convex hull and +-- covers the original geometry (can't use covers because always gives topo errors with 3.3 +SELECT + 'ST_ConcaveHull MultiPolygon 0.95', ST_Area(ST_Intersection(geom,ST_ConcaveHull( + geom, 0.95) )) = ST_Area(geom) As encloses_geom, + (ST_Area(ST_ConvexHull(geom)) + - ST_Area(ST_ConcaveHull(geom, 0.95))) < (0.95 * ST_Area(ST_ConvexHull(geom) ) ) As reached_target +FROM ST_Union(ST_GeomFromText('POLYGON((175 150, 20 40, + 50 60, 125 100, 175 150))'), + ST_Buffer(ST_GeomFromText('POINT(110 170)'), 20) + ) As geom; + +SELECT + 'ST_ConcaveHull Lines 0.80', ST_Intersection(geom,ST_ConcaveHull( + geom, 0.80) ) = geom As encloses_geom, + (ST_Area(ST_ConvexHull(geom)) + - ST_Area(ST_ConcaveHull(geom, 0.80))) < (0.80 * ST_Area(ST_ConvexHull(geom) ) ) As reached_target + +FROM ST_GeomFromText('MULTILINESTRING((106 164,30 112,74 70,82 112,130 94, + 130 62,122 40,156 32,162 76,172 88), +(132 178,134 148,128 136,96 128,132 108,150 130, +170 142,174 110,156 96,158 90,158 88), +(22 64,66 28,94 38,94 68,114 76,112 30, +132 10,168 18,178 34,186 52,184 74,190 100, +190 122,182 148,178 170,176 184,156 164,146 178, +132 186,92 182,56 158,36 150,62 150,76 128,88 118))') As geom; + +-- test holes vs. no holes - holes should still enclose but have smaller area than no holes -- +SELECT + 'ST_ConcaveHull Lines 0.80 holes', ST_Intersection(geom,ST_ConcaveHull( + geom, 0.80, true) ) = geom As encloses_geom, + ST_Area(ST_ConcaveHull(geom, 0.80, true)) < ST_Area(ST_ConcaveHull(geom, 0.80)) As reached_target + +FROM ST_GeomFromText('MULTILINESTRING((106 164,30 112,74 70,82 112,130 94, + 130 62,122 40,156 32,162 76,172 88), +(132 178,134 148,128 136,96 128,132 108,150 130, +170 142,174 110,156 96,158 90,158 88), +(22 64,66 28,94 38,94 68,114 76,112 30, +132 10,168 18,178 34,186 52,184 74,190 100, +190 122,182 148,178 170,176 184,156 164,146 178, +132 186,92 182,56 158,36 150,62 150,76 128,88 118))') As geom; diff --git a/regress/concave_hull_expected b/regress/concave_hull_expected new file mode 100644 index 000000000..f81537cf1 --- /dev/null +++ b/regress/concave_hull_expected @@ -0,0 +1,3 @@ +ST_ConcaveHull MultiPolygon 0.95|t|t +ST_ConcaveHull Lines 0.80|t|t +ST_ConcaveHull Lines 0.80 holes|t|t