[postgis-tickets] r15336 - #3589, Orientation checking and forcing fuctions
Daniel Baston
dbaston at gmail.com
Thu Mar 16 17:28:07 PDT 2017
Author: dbaston
Date: 2017-03-16 17:28:07 -0700 (Thu, 16 Mar 2017)
New Revision: 15336
Added:
trunk/regress/orientation.sql
trunk/regress/orientation_expected
Modified:
trunk/NEWS
trunk/doc/reference_accessor.xml
trunk/doc/reference_editor.xml
trunk/liblwgeom/cunit/cu_libgeom.c
trunk/liblwgeom/liblwgeom.h.in
trunk/liblwgeom/lwgeom.c
trunk/liblwgeom/lwpoly.c
trunk/liblwgeom/lwtriangle.c
trunk/postgis/lwgeom_functions_analytic.c
trunk/postgis/postgis.sql.in
trunk/regress/Makefile.in
Log:
#3589, Orientation checking and forcing fuctions
Modified: trunk/NEWS
===================================================================
--- trunk/NEWS 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/NEWS 2017-03-17 00:28:07 UTC (rev 15336)
@@ -5,6 +5,7 @@
- #3599, Geobuf output support via ST_AsGeobuf (Björn Harrtell)
- #3661, Mapbox vector tile output support via ST_AsMVT (Björn Harrtell / CartoDB)
+ - #3689, Add orientation checking and forcing functions (Dan Baston)
PostGIS 2.3.0
2016/09/26
Modified: trunk/doc/reference_accessor.xml
===================================================================
--- trunk/doc/reference_accessor.xml 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/doc/reference_accessor.xml 2017-03-17 00:28:07 UTC (rev 15336)
@@ -872,6 +872,129 @@
</refsection>
</refentry>
+ <refentry id="ST_IsPolygonCCW">
+ <refnamediv>
+ <refname>
+ ST_IsPolygonCCW
+ </refname>
+ <refpurpose>
+ Returns true if all exterior rings are oriented counter-clockwise and all interior rings are oriented clockwise.
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>
+ boolean
+ <function>ST_IsPolygonCCW</function>
+ </funcdef>
+ <paramdef>
+ <type>geometry</type>
+ <parameter>geom</parameter>
+ </paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsection>
+ <title>Description</title>
+
+ <para>
+ Returns true for (Multi)Polygons that use a counter-clockwise
+ orientation for their exterior ring, and a clockwise direction
+ for all interior rings.
+ </para>
+
+ <para>
+ Returns true for all non-polygonal geometries.
+ </para>
+
+ <note>
+ <para>
+ If a polygonal geometry does not use reversed orientation
+ for interior rings (i.e., if one or more interior rings
+ are oriented in the same direction as an exterior ring)
+ then both ST_IsPolygonCW and ST_IsPolygonCCW will return false.
+ </para>
+ </note>
+
+ <para>&Z_support;</para>
+ <para>&M_support;</para>
+
+ </refsection>
+
+ <refsection>
+ <title>See Also</title>
+ <para>
+ <xref linkend="ST_ForcePolygonCW" />,
+ <xref linkend="ST_ForcePolygonCCW" />,
+ <xref linkend="ST_IsPolygonCW" />
+ </para>
+ </refsection>
+ </refentry>
+
+ <refentry id="ST_IsPolygonCW">
+ <refnamediv>
+ <refname>
+ ST_IsPolygonCW
+ </refname>
+ <refpurpose>
+ Returns true if all exterior rings are oriented clockwise and all interior rings are oriented counter-clockwise.
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>
+ boolean
+ <function>ST_IsPolygonCW</function>
+ </funcdef>
+ <paramdef>
+ <type>geometry</type>
+ <parameter>geom</parameter>
+ </paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsection>
+ <title>Description</title>
+
+ <para>
+ Returns true for (Multi)Polygons that use a clockwise
+ orientation for their exterior ring, and a counter-clockwise direction
+ for all interior rings.
+ </para>
+
+ <para>
+ Returns true for all non-polygonal geometries.
+ </para>
+
+ <note>
+ <para>
+ If a polygonal geometry does not use reversed orientation
+ for interior rings (i.e., if one or more interior rings
+ are oriented in the same direction as an exterior ring)
+ then both ST_IsPolygonCW and ST_IsPolygonCCW will return false.
+ </para>
+ </note>
+
+ <para>&Z_support;</para>
+ <para>&M_support;</para>
+ </refsection>
+
+ <refsection>
+ <title>See Also</title>
+ <para>
+ <xref linkend="ST_ForcePolygonCW" />,
+ <xref linkend="ST_ForcePolygonCCW" />,
+ <xref linkend="ST_IsPolygonCW" />
+ </para>
+ </refsection>
+ </refentry>
+
<refentry id="ST_IsClosed">
<refnamediv>
<refname>ST_IsClosed</refname>
Modified: trunk/doc/reference_editor.xml
===================================================================
--- trunk/doc/reference_editor.xml 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/doc/reference_editor.xml 2017-03-17 00:28:07 UTC (rev 15336)
@@ -449,6 +449,54 @@
</refsection>
</refentry>
+ <refentry id="ST_ForcePolygonCCW">
+ <refnamediv>
+ <refname>
+ ST_ForcePolygonCCW
+ </refname>
+ <refpurpose>
+ Orients all exterior rings counter-clockwise and all interior rings clockwise.
+ </refpurpose>
+ </refnamediv>
+
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>
+ geometry
+ <function>ST_ForcePolygonCCW</function>
+ </funcdef>
+ <paramdef>
+ <type>geometry</type>
+ <parameter>geom</parameter>
+ </paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsection>
+ <title>Description</title>
+
+ <para>
+ Forces (Multi)Polygons to use a counter-clockwise orientation for
+ their exterior ring, and a clockwise orientation for their interior
+ rings. Non-polygonal geometries are returned unchanged.
+ </para>
+
+ <para>&Z_support;</para>
+ <para>&M_support;</para>
+ </refsection>
+
+ <refsection>
+ <title>See Also</title>
+ <para>
+ <xref linkend="ST_ForcePolygonCW" />,
+ <xref linkend="ST_IsPolygonCCW" />,
+ <xref linkend="ST_IsPolygonCW" />
+ </para>
+ </refsection>
+ </refentry>
+
<refentry id="ST_Force_Collection">
<refnamediv>
<refname>ST_ForceCollection</refname>
@@ -531,7 +579,54 @@
</refsection>
</refentry>
+ <refentry id="ST_ForcePolygonCW">
+ <refnamediv>
+ <refname>
+ ST_ForcePolygonCW
+ </refname>
+ <refpurpose>
+ Orients all exterior rings clockwise and all interior rings counter-clockwise.
+ </refpurpose>
+ </refnamediv>
+ <refsynopsisdiv>
+ <funcsynopsis>
+ <funcprototype>
+ <funcdef>
+ geometry
+ <function>ST_ForcePolygonCW</function>
+ </funcdef>
+ <paramdef>
+ <type>geometry</type>
+ <parameter>geom</parameter>
+ </paramdef>
+ </funcprototype>
+ </funcsynopsis>
+ </refsynopsisdiv>
+
+ <refsection>
+ <title>Description</title>
+
+ <para>
+ Forces (Multi)Polygons to use a clockwise orientation for
+ their exterior ring, and a counter-clockwise orientation for their interior
+ rings. Non-polygonal geometries are returned unchanged.
+ </para>
+
+ <para>&Z_support;</para>
+ <para>&M_support;</para>
+ </refsection>
+
+ <refsection>
+ <title>See Also</title>
+ <para>
+ <xref linkend="ST_ForcePolygonCCW" />,
+ <xref linkend="ST_IsPolygonCCW" />,
+ <xref linkend="ST_IsPolygonCW" />
+ </para>
+ </refsection>
+ </refentry>
+
<refentry id="ST_ForceSFS">
<refnamediv>
<refname>ST_ForceSFS</refname>
@@ -585,12 +680,18 @@
<refsection>
<title>Description</title>
- <para>Forces the orientation of the vertices in a polygon to follow the
- Right-Hand-Rule. In GIS terminology, this means that the area that is bounded by the
+ <para>Forces the orientation of the vertices in a polygon to follow a
+ Right-Hand-Rule, in which the area that is bounded by the
polygon is to the right of the boundary. In particular, the exterior ring is
orientated in a clockwise direction and the interior rings in a counter-clockwise
- direction.</para>
+ direction. This function is a synonym for <xref linkend="ST_ForcePolygonCW" /></para>
+ <note>
+ <para>
+ The above definition of the Right-Hand-Rule conflicts with definitions used in other contexts. To avoid confusion, it is recommended to use ST_ForcePolygonCW.
+ </para>
+ </note>
+
<para>Enhanced: 2.0.0 support for Polyhedral surfaces was introduced.</para>
<para>&Z_support;</para>
<para>&P_support;</para>
@@ -613,7 +714,12 @@
<refsection>
<title>See Also</title>
- <para><xref linkend="ST_BuildArea"/>,
+ <para>
+ <xref linkend="ST_ForcePolygonCCW"/>,
+ <xref linkend="ST_ForcePolygonCW"/>,
+ <xref linkend="ST_IsPolygonCCW"/>,
+ <xref linkend="ST_IsPolygonCW"/>,
+ <xref linkend="ST_BuildArea"/>,
<xref linkend="ST_Polygonize"/>,
<xref linkend="ST_Reverse"/></para>
</refsection>
Modified: trunk/liblwgeom/cunit/cu_libgeom.c
===================================================================
--- trunk/liblwgeom/cunit/cu_libgeom.c 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/liblwgeom/cunit/cu_libgeom.c 2017-03-17 00:28:07 UTC (rev 15336)
@@ -741,6 +741,7 @@
/* counterclockwise, must be reversed */
geom = lwgeom_from_wkt("POLYGON((0 0, 10 0, 10 10, 0 10, 0 0))", LW_PARSER_CHECK_NONE);
+ CU_ASSERT_FALSE(lwgeom_is_clockwise(geom));
lwgeom_force_clockwise(geom);
in_ewkt = "POLYGON((0 0,0 10,10 10,10 0,0 0))";
out_ewkt = lwgeom_to_ewkt(geom);
@@ -752,6 +753,7 @@
/* clockwise, fine as is */
geom = lwgeom_from_wkt("POLYGON((0 0, 0 10, 10 10, 10 0, 0 0))", LW_PARSER_CHECK_NONE);
+ CU_ASSERT_TRUE(lwgeom_is_clockwise(geom));
lwgeom_force_clockwise(geom);
in_ewkt = "POLYGON((0 0,0 10,10 10,10 0,0 0))";
out_ewkt = lwgeom_to_ewkt(geom);
@@ -763,6 +765,7 @@
/* counterclockwise shell (must be reversed), mixed-wise holes */
geom = lwgeom_from_wkt("POLYGON((0 0,10 0,10 10,0 10,0 0),(2 2,2 4,4 2,2 2),(6 2,8 2,8 4,6 2))", LW_PARSER_CHECK_NONE);
+ CU_ASSERT_FALSE(lwgeom_is_clockwise(geom));
lwgeom_force_clockwise(geom);
in_ewkt = "POLYGON((0 0,0 10,10 10,10 0,0 0),(2 2,4 2,2 4,2 2),(6 2,8 2,8 4,6 2))";
out_ewkt = lwgeom_to_ewkt(geom);
@@ -774,6 +777,7 @@
/* clockwise shell (fine), mixed-wise holes */
geom = lwgeom_from_wkt("POLYGON((0 0,0 10,10 10,10 0,0 0),(2 2,4 2,2 4,2 2),(6 2,8 4,8 2,6 2))", LW_PARSER_CHECK_NONE);
+ CU_ASSERT_FALSE(lwgeom_is_clockwise(geom));
lwgeom_force_clockwise(geom);
in_ewkt = "POLYGON((0 0,0 10,10 10,10 0,0 0),(2 2,4 2,2 4,2 2),(6 2,8 2,8 4,6 2))";
out_ewkt = lwgeom_to_ewkt(geom);
Modified: trunk/liblwgeom/liblwgeom.h.in
===================================================================
--- trunk/liblwgeom/liblwgeom.h.in 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/liblwgeom/liblwgeom.h.in 2017-03-17 00:28:07 UTC (rev 15336)
@@ -1218,6 +1218,9 @@
extern void lwpoly_force_clockwise(LWPOLY *poly);
extern void lwtriangle_force_clockwise(LWTRIANGLE *triangle);
+extern int lwgeom_is_clockwise(LWGEOM *lwgeom);
+extern int lwpoly_is_clockwise(LWPOLY *poly);
+extern int lwtriangle_is_clockwise(LWTRIANGLE *triangle);
extern void interpolate_point4d(POINT4D *A, POINT4D *B, POINT4D *I, double F);
Modified: trunk/liblwgeom/lwgeom.c
===================================================================
--- trunk/liblwgeom/lwgeom.c 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/liblwgeom/lwgeom.c 2017-03-17 00:28:07 UTC (rev 15336)
@@ -59,6 +59,35 @@
}
}
+/** Check clockwise orientation on LWGEOM polygons **/
+int
+lwgeom_is_clockwise(LWGEOM *lwgeom)
+{
+ switch (lwgeom->type)
+ {
+ case POLYGONTYPE:
+ return lwpoly_is_clockwise((LWPOLY *)lwgeom);
+
+ case TRIANGLETYPE:
+ return lwtriangle_is_clockwise((LWTRIANGLE *)lwgeom);
+
+ case MULTIPOLYGONTYPE:
+ case COLLECTIONTYPE:
+ {
+ int i;
+ LWCOLLECTION* coll = (LWCOLLECTION *)lwgeom;
+
+ for (i=0; i < coll->ngeoms; i++)
+ if (!lwgeom_is_clockwise(coll->geoms[i]))
+ return LW_FALSE;
+ return LW_TRUE;
+ }
+ default:
+ return LW_TRUE;
+ return LW_FALSE;
+ }
+}
+
/** Reverse vertex order of LWGEOM **/
void
lwgeom_reverse(LWGEOM *lwgeom)
Modified: trunk/liblwgeom/lwpoly.c
===================================================================
--- trunk/liblwgeom/lwpoly.c 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/liblwgeom/lwpoly.c 2017-03-17 00:28:07 UTC (rev 15336)
@@ -284,6 +284,24 @@
}
+int
+lwpoly_is_clockwise(LWPOLY *poly)
+{
+ int i;
+
+ if ( lwpoly_is_empty(poly) )
+ return LW_TRUE;
+
+ if ( ptarray_isccw(poly->rings[0]) )
+ return LW_FALSE;
+
+ for ( i = 1; i < poly->nrings; i++)
+ if ( !ptarray_isccw(poly->rings[i]) )
+ return LW_FALSE;
+
+ return LW_TRUE;
+}
+
void
lwpoly_release(LWPOLY *lwpoly)
{
Modified: trunk/liblwgeom/lwtriangle.c
===================================================================
--- trunk/liblwgeom/lwtriangle.c 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/liblwgeom/lwtriangle.c 2017-03-17 00:28:07 UTC (rev 15336)
@@ -109,6 +109,12 @@
ptarray_reverse(triangle->points);
}
+int
+lwtriangle_is_clockwise(LWTRIANGLE *triangle)
+{
+ return !ptarray_isccw(triangle->points);
+}
+
void
lwtriangle_reverse(LWTRIANGLE *triangle)
{
Modified: trunk/postgis/lwgeom_functions_analytic.c
===================================================================
--- trunk/postgis/lwgeom_functions_analytic.c 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/postgis/lwgeom_functions_analytic.c 2017-03-17 00:28:07 UTC (rev 15336)
@@ -55,6 +55,8 @@
Datum ST_MinimumBoundingRadius(PG_FUNCTION_ARGS);
Datum ST_MinimumBoundingCircle(PG_FUNCTION_ARGS);
Datum ST_GeometricMedian(PG_FUNCTION_ARGS);
+Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS);
+Datum ST_IsPolygonCW(PG_FUNCTION_ARGS);
static double determineSide(const POINT2D *seg1, const POINT2D *seg2, const POINT2D *point);
@@ -1282,3 +1284,57 @@
PG_RETURN_POINTER(result);
}
+/**********************************************************************
+ *
+ * ST_IsPolygonCW
+ *
+ **********************************************************************/
+
+PG_FUNCTION_INFO_V1(ST_IsPolygonCW);
+Datum ST_IsPolygonCW(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED* geom;
+ LWGEOM* input;
+ bool is_clockwise;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ geom = PG_GETARG_GSERIALIZED_P(0);
+ input = lwgeom_from_gserialized(geom);
+
+ is_clockwise = lwgeom_is_clockwise(input);
+
+ lwgeom_free(input);
+ PG_FREE_IF_COPY(geom, 0);
+
+ PG_RETURN_BOOL(is_clockwise);
+}
+
+/**********************************************************************
+ *
+ * ST_IsPolygonCCW
+ *
+ **********************************************************************/
+
+PG_FUNCTION_INFO_V1(ST_IsPolygonCCW);
+Datum ST_IsPolygonCCW(PG_FUNCTION_ARGS)
+{
+ GSERIALIZED* geom;
+ LWGEOM* input;
+ bool is_ccw;
+
+ if (PG_ARGISNULL(0))
+ PG_RETURN_NULL();
+
+ geom = PG_GETARG_GSERIALIZED_P_COPY(0);
+ input = lwgeom_from_gserialized(geom);
+
+ lwgeom_reverse(input);
+ is_ccw = lwgeom_is_clockwise(input);
+
+ lwgeom_free(input);
+ PG_FREE_IF_COPY(geom, 0);
+
+ PG_RETURN_BOOL(is_ccw);
+}
Modified: trunk/postgis/postgis.sql.in
===================================================================
--- trunk/postgis/postgis.sql.in 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/postgis/postgis.sql.in 2017-03-17 00:28:07 UTC (rev 15336)
@@ -1235,6 +1235,20 @@
LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
COST 10;
+-- Availability: 2.4.0
+CREATE OR REPLACE FUNCTION ST_IsPolygonCW(geometry)
+ RETURNS boolean
+ AS 'MODULE_PATHNAME','ST_IsPolygonCW'
+ LANGUAGE 'c' IMMUTABLE STRICT
+ COST 10;
+
+-- Availability: 2.4.0
+CREATE OR REPLACE FUNCTION ST_IsPolygonCCW(geometry)
+ RETURNS boolean
+ AS 'MODULE_PATHNAME','ST_IsPolygonCCW'
+ LANGUAGE 'c' IMMUTABLE STRICT
+ COST 10;
+
-- Availability: 2.0.0
CREATE OR REPLACE FUNCTION ST_DistanceSpheroid(geom1 geometry, geom2 geometry,spheroid)
RETURNS FLOAT8
@@ -1467,6 +1481,20 @@
LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
COST 1; -- reset cost, see #3675
+-- Availability: 2.4.0
+CREATE OR REPLACE FUNCTION ST_ForcePolygonCW(geometry)
+ RETURNS geometry
+ AS 'MODULE_PATHNAME', 'LWGEOM_force_clockwise_poly'
+ LANGUAGE 'c' IMMUTABLE STRICT _PARALLEL
+ COST 15;
+
+-- Availability: 2.4.0
+CREATE OR REPLACE FUNCTION ST_ForcePolygonCCW(geometry)
+ RETURNS geometry
+ AS $$ SELECT @extschema at .ST_Reverse(@extschema at .ST_ForcePolygonCW($1)) $$
+ LANGUAGE SQL IMMUTABLE STRICT _PARALLEL
+ COST 15;
+
-- Availability: 1.2.2
CREATE OR REPLACE FUNCTION ST_ForceRHR(geometry)
RETURNS geometry
Modified: trunk/regress/Makefile.in
===================================================================
--- trunk/regress/Makefile.in 2017-03-16 13:08:38 UTC (rev 15335)
+++ trunk/regress/Makefile.in 2017-03-17 00:28:07 UTC (rev 15336)
@@ -102,6 +102,7 @@
minimum_bounding_circle \
normalize \
operators \
+ orientation \
out_geometry \
out_geography \
polygonize \
Added: trunk/regress/orientation.sql
===================================================================
--- trunk/regress/orientation.sql (rev 0)
+++ trunk/regress/orientation.sql 2017-03-17 00:28:07 UTC (rev 15336)
@@ -0,0 +1,65 @@
+-- pg24
+
+-- Non-applicable types
+SELECT '1', ST_IsPolygonCCW('POINT (0 0)');
+SELECT '2', ST_IsPolygonCCW('MULTIPOINT ((0 0), (1 1))');
+SELECT '3', ST_IsPolygonCCW('LINESTRING (1 1, 2 2)');
+SELECT '4', ST_IsPolygonCCW('MULTILINESTRING ((1 1, 2 2), (3 3, 0 0))');
+SELECT '5', ST_IsPolygonCW('POINT (0 0)');
+SELECT '6', ST_IsPolygonCW('MULTIPOINT ((0 0), (1 1))');
+SELECT '7', ST_IsPolygonCW('LINESTRING (1 1, 2 2)');
+SELECT '8', ST_IsPolygonCW('MULTILINESTRING ((1 1, 2 2), (3 3, 0 0))');
+
+-- NULL handling
+SELECT '101', ST_IsPolygonCW(NULL::geometry);
+SELECT '102', ST_IsPolygonCCW(NULL::geometry);
+SELECT '103', ST_ForcePolygonCW(NULL::geometry);
+SELECT '104', ST_ForcePolygonCCW(NULL::geometry);
+
+-- EMPTY handling
+SELECT '201', ST_AsText(ST_ForcePolygonCW('POLYGON EMPTY'::geometry));
+SELECT '202', ST_AsText(ST_ForcePolygonCCW('POLYGON EMPTY'::geometry));
+SELECT '203', ST_IsPolygonCW('POLYGON EMPTY'::geometry);
+SELECT '204', ST_IsPolygonCCW('POLYGON EMPTY'::geometry);
+
+-- Preserves SRID
+SELECT '301', ST_SRID(ST_ForcePolygonCW('SRID=4269;POLYGON ((0 0, 1 1, 1 0, 0 0))'));
+SELECT '302', ST_SRID(ST_ForcePolygonCCW('SRID=4269;POLYGON ((0 0, 1 0, 1 1, 0 0))'));
+
+-- Single polygon, ccw exterior ring only
+SELECT '401', ST_IsPolygonCW('POLYGON ((0 0, 1 0, 1 1, 0 0))');
+SELECT '402', ST_IsPolygonCCW('POLYGON ((0 0, 1 0, 1 1, 0 0))');
+
+-- Single polygon, cw exterior ring only
+SELECT '403', ST_IsPolygonCW('POLYGON ((0 0, 1 1, 1 0, 0 0))');
+SELECT '404', ST_IsPolygonCCW('POLYGON ((0 0, 1 1, 1 0, 0 0))');
+
+-- Single polygon, ccw exterior ring, cw interior rings
+SELECT '405', ST_IsPolygonCW('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 1 1), (5 5, 5 7, 7 7, 5 5))');
+SELECT '406', ST_IsPolygonCCW('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 1 1), (5 5, 5 7, 7 7, 5 5))');
+
+-- Single polygon, cw exterior ring, ccw interior rings
+SELECT '407', ST_IsPolygonCW( 'POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 2, 1 2, 1 1), (5 5, 7 7, 5 7, 5 5))');
+SELECT '408', ST_IsPolygonCCW('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 2, 1 2, 1 1), (5 5, 7 7, 5 7, 5 5))');
+
+-- Single polygon, ccw exerior ring, mixed interior rings
+SELECT '409', ST_IsPolygonCW( 'POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 1 1), (5 5, 7 7, 5 7, 5 5))');
+SELECT '410', ST_IsPolygonCCW('POLYGON ((0 0, 10 0, 10 10, 0 10, 0 0), (1 1, 1 2, 2 2, 1 1), (5 5, 7 7, 5 7, 5 5))');
+
+-- Single polygon, cw exterior ring, mixed interior rings
+SELECT '411', ST_IsPolygonCW( 'POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 2, 1 2, 1 1), (5 5, 5 7, 7 7, 5 5))');
+SELECT '412', ST_IsPolygonCCW('POLYGON ((0 0, 0 10, 10 10, 10 0, 0 0), (1 1, 2 2, 1 2, 1 1), (5 5, 5 7, 7 7, 5 5))');
+
+-- MultiPolygon, ccw exterior rings only
+SELECT '413', ST_IsPolygonCW( 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((100 0, 101 0, 101 1, 100 0)))');
+SELECT '414', ST_IsPolygonCCW('MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((100 0, 101 0, 101 1, 100 0)))');
+
+-- MultiPolygon, cw exterior rings only
+SELECT '415', ST_IsPolygonCW( 'MULTIPOLYGON (((0 0, 1 1, 1 0, 0 0)), ((100 0, 101 1, 101 0, 100 0)))');
+SELECT '416', ST_IsPolygonCCW('MULTIPOLYGON (((0 0, 1 1, 1 0, 0 0)), ((100 0, 101 1, 101 0, 100 0)))');
+
+-- MultiPolygon, mixed exterior rings
+SELECT '417', ST_IsPolygonCW( 'MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((100 0, 101 1, 101 0, 100 0)))');
+SELECT '418', ST_IsPolygonCCW('MULTIPOLYGON (((0 0, 1 0, 1 1, 0 0)), ((100 0, 101 1, 101 0, 100 0)))');
+
+
Added: trunk/regress/orientation_expected
===================================================================
--- trunk/regress/orientation_expected (rev 0)
+++ trunk/regress/orientation_expected 2017-03-17 00:28:07 UTC (rev 15336)
@@ -0,0 +1,36 @@
+1|t
+2|t
+3|t
+4|t
+5|t
+6|t
+7|t
+8|t
+101|
+102|
+103|
+104|
+201|POLYGON EMPTY
+202|POLYGON EMPTY
+203|t
+204|t
+301|4269
+302|4269
+401|f
+402|t
+403|t
+404|f
+405|f
+406|t
+407|t
+408|f
+409|f
+410|f
+411|f
+412|f
+413|f
+414|t
+415|t
+416|f
+417|f
+418|f
More information about the postgis-tickets
mailing list