I'm an experienced Postgres / PostGIS user fiddling with SQL Server / Azure SQL Database spatial capabilities for the first time.
Banana Polygon
For the banana polygon pictured above, I believe there are two primary ways to represent it (ignoring winding order):
- A single outer ring (crescent/banana) that self-intersects at a single point
- An outer ring (square) and inner ring (triangle hole) that touch at one point
It is my understanding that only form 2 is OGC compliant as rings cannot self-intersect.
It seems to me that an Azure SQL Database considers both of the above forms as a valid geometry.
My question is this: can form 1 be converted to form 2 in Azure SQL Database using spatial functions?
MakeValid() doesn't seem to alter form 1 since it is already considered valid. (Whereas in PostGIS, form 1 is considered invalid and ST_MakeValid will convert it to form 2).
My purpose for converting from form 1 to form 2 is to provide OGC-compliant polygons to downstream applications after exporting to shapefile. (While the ESRI shapefile format also specifies that rings should not self-intersect, it does not actually enforce that.)
Form 1 - IsValidDetailed returns Valid
SELECT ( geometry::STGeomFromText('POLYGON ((-80.3 25.9, -80.3 25.8, -80.2 25.8, -80.2 25.9, -80.25 25.9, -80.225 25.85, -80.275 25.85, -80.25 25.9, -80.3 25.9))', 0 ) ).IsValidDetailed() --24400: Valid
Form 2 - IsValidDetailed returns Valid
SELECT ( geometry::STGeomFromText('POLYGON ((-80.3 25.9, -80.3 25.8, -80.2 25.8, -80.2 25.9, -80.3 25.9), (-80.25 25.9, -80.225 25.85, -80.275 25.85, -80.25 25.9))', 0 ) ).IsValidDetailed() --24400: Valid
I used SRID 0 here, but as expected I get the same results with 4326, 4269, etc.
I also get the same results (as expected) with MULTIPOLYGON instead of POLYGON.
As expected, I get the same results regardless of winding order, as winding order is meaningless for geometry (but not geography) in Azure SQL Database
MakeValid does not alter form 1 at all
SELECT ( geometry::STGeomFromText('POLYGON ((-80.3 25.9, -80.3 25.8, -80.2 25.8, -80.2 25.9, -80.25 25.9, -80.225 25.85, -80.275 25.85, -80.25 25.9, -80.3 25.9))', 0 ) ).MakeValid().STAsText() -- >> shifted for comparison POLYGON ((-80.3 25.9, -80.3 25.8, -80.2 25.8, -80.2 25.9, -80.25 25.9, -80.225 25.85, -80.275 25.85, -80.25 25.9, -80.3 25.9))
Buffering by 0 also does not alter form 1 at all
SELECT ( geometry::STGeomFromText('POLYGON ((-80.3 25.9, -80.3 25.8, -80.2 25.8, -80.2 25.9, -80.25 25.9, -80.225 25.85, -80.275 25.85, -80.25 25.9, -80.3 25.9))', 0 ) ).STBuffer(0.0).STAsText() -- >> shifted for comparison POLYGON ((-80.3 25.9, -80.3 25.8, -80.2 25.8, -80.2 25.9, -80.25 25.9, -80.225 25.85, -80.275 25.85, -80.25 25.9, -80.3 25.9))
Also, while we use ogr2ogr to export these polygons to shapefiles and an ogr2ogr solution is available here - How to to validate geometries using ogr2ogr - I'm looking for an in-database solution using spatial functions.