Version 35 (modified by 13 years ago) ( diff ) | ,
---|
EMPTY GEOMETRY Handling
The semantics of NULL are well defined for us already in SQL standards. However, in the world of Geometry / Geography we have another entity which is not quite NULL nor quite fully defined: the empty geometry. Empty geometries can be created with things like intersection calls that have have no intersection, e.g. ST_Intersect('POINT(0 0)', 'POINT(1 1)') == POINT EMPTY. So, in the presence of empty, how should functions behave?
Ideally GEOMETRY EMPTY should behave more like 'zero' (0) than NULL - it is an expected, valid result of a successful, valid operation. In other words, dealing with EMPTY should require as little "special case" handling as possible in logical expressions making use of multiple geometry operations and/or comparisons. Maintaining compatibility with other spatial databases is probably valuable in its own right, and also because they have probably already done some thinking around these issues.
- ST_Buffer(empty, tolerance) == empty
- ST_Union(geometry, empty) == geometry
- ST_Union(empty, empty) == empty
- ST_Intersection(geometry, empty) == geometry
- fabian thinks this should be empty
- In set theory, A ∩ ∅ = ∅
- strk agrees with fabian
- fabian thinks this should be empty
- ST_Intersection(empty, empty) == empty
- ST_Difference(geometry, empty) == geometry
- ST_Difference(empty, geometry) == empty
- ST_Distance(geometry, empty) == NULL
- ST_DWithin(geometry, empty, tolerance) == FALSE
- chodgson thinks this should be null. It reduces to:
- ST_Distance( geometry, empty ) < tolerance
- NULL < tolerance
- NULL (by the SQL standard)
- strk thinks it should follow semantic of ST_Within (thus FALSE)
- chodgson thinks this should be null. It reduces to:
- ST_Contains(geometry, empty) == FALSE (SQL Server says False)
- fabian thinks this should be TRUE
- In set theory, every set contains ∅
- strk agrees with fabian
- fabian thinks this should be TRUE
- ST_Within(geometry, empty) == FALSE (SQL Server says False)
- ST_Contains(empty, geometry) == FALSE (SQL Server says False)
- ST_Within(empty, geometry) == FALSE (SQL Server says False)
- fabian thinks this should be TRUE
- In set theory, every set contains ∅
- strk agrees with fabian
- fabian thinks this should be TRUE
- ST_Contains(empty, empty) == FALSE (SQL Server says False)
- ST_Intersects(geometry, empty) == FALSE
- ST_Intersects(empty, empty) == FALSE (SQL Server says False)
- ST_Disjoint(empty, empty) == TRUE (SQL Server says True)
- ST_Disjoint(geometry, empty) == TRUE (SQL Server says True)
- ST_IsSimple(empty) == TRUE
- ST_IsValid(empty) == TRUE
- ST_NumGeometries(empty) == 0
- ST_NumRings(empty) == 0
- ST_NumPoints(empty) == 0
- ST_GeometryN(empty, n) == empty
- ST_RingN(geometry, n) == empty
- ST_Area(empty) == 0
- ST_Length(empty) == 0
Collections:
When working against collections that might contain both empty and non-empty members, if there is any non-empty member, ignore all the empty members and treat the collection as if the empty portions are not there. If the collection is entirely made of empty components, behave as above.
Note: It might make sense to try to maintain the geometric truisms when specifying the semantics around empty - ie. things like:
- intersects( A, B ) => !disjoint( A, B )
- A == B => intersects( A, B )
- contains( A, B ) && contains( B, A ) => A == B
If we don't maintain these we will probably need to complicate logical comparisons with special cases for empty - it may be the case that this is necessary, but it would be nice if it just 'came out in the wash'.
This suggests to me that perhaps:
- ST_Intersects( empty, empty ) == TRUE
- ST_Disjoint( empty, empty ) == FALSE
- ST_Distance( empty, empty) == 0
- ST_DWithin( empty, empty, tolerance) == true
Hmmm.. perhaps this isn't even possible generally, as:
- intersects( A, B ) => intersection( A, B ) != empty
but I think that we certainly want:
- intersection( empty, empty) == empty
SQL Server 2008
For reference and comparison, courtesy of Robe:
SELECT Geometry::STGeomFromText('POINT(1 2)',4326).STUnion(Geometry::STGeomFromText('POLYGON EMPTY',4326)) => POINT (1 2) SELECT (Geometry::STGeomFromText('POLYGON EMPTY',4326).STUnion(Geometry::STGeomFromText('POLYGON EMPTY',4326))).STAsText() => GEOMETRYCOLLECTION EMPTY SELECT (Geometry::STGeomFromText('POINT(1 3)',4326).STDifference(Geometry::STGeomFromText('POLYGON EMPTY',4326))).STAsText() => POINT (1 3) SELECT (Geometry::STGeomFromText('POLYGON EMPTY',4326).STDifference(Geometry::STGeomFromText('POLYGON EMPTY',4326))).STAsText() => GEOMETRY COLLECTION EMPTY SELECT (Geometry::STGeomFromText('POINT(1 3)',4326).STContains(Geometry::STGeomFromText('POLYGON EMPTY',4326))) => 0 SELECT (Geometry::STGeomFromText('POLYGON EMPTY',4326).STContains(Geometry::STGeomFromText('POLYGON EMPTY',4326))) => 0 SELECT (Geometry::STGeomFromText('POINT(1 3)',4326).STIntersects(Geometry::STGeomFromText('POLYGON EMPTY',4326))) => 0 SELECT (Geometry::STGeomFromText('POLYGON EMPTY',4326).STIntersects(Geometry::STGeomFromText('POLYGON EMPTY',4326))) => 0 SELECT (Geometry::STGeomFromText('POLYGON EMPTY',4326).STDisjoint(Geometry::STGeomFromText('POLYGON EMPTY',4326))) => 1 SELECT (Geometry::STGeomFromText('POINT(1 3)',4326).STDisjoint(Geometry::STGeomFromText('POLYGON EMPTY',4326))) => 1 SELECT Geometry::STGeomFromText('POLYGON EMPTY',4326).STIsSimple() => 1 SELECT Geometry::STGeomFromText('POLYGON EMPTY',4326).STIsValid() => 1 SELECT (Geometry::STGeomFromText('POLYGON EMPTY',4326).STDistance(Geometry::STGeomFromText('POLYGON EMPTY',4326))) => NULL SELECT (Geometry::STGeomFromText('POLYGON EMPTY',4326).STIntersection(Geometry::STGeomFromText('POLYGON EMPTY',4326))).STAsText() => GEOMETRY COLLECTION EMPTY --geometry contains empty SELECT Geometry::STGeomFromText('POINT(1 2)',4326).STContains(Geometry::STGeomFromText('POLYGON EMPTY',4326)) => 0 --geometry within empty SELECT Geometry::STGeomFromText('POINT(1 2)',4326).STWithin(Geometry::STGeomFromText('POLYGON EMPTY',4326)) => 0 --empty contains geometry SELECT Geometry::STGeomFromText('POLYGON EMPTY',4326).STContains(Geometry::STGeomFromText('POINT(1 2)',4326)) => 0 --empty within geometry SELECT Geometry::STGeomFromText('POLYGON EMPTY',4326).STWithin(Geometry::STGeomFromText('POINT(1 2)',4326)) => 0 SELECT Geometry::STGeomFromText('POINT(1 2)',4326).STContains(Geometry::STGeomFromText('POLYGON EMPTY',4326)) => 0 --geometry within empty SELECT Geometry::STGeomFromText('POINT(1 2)',4326).STWithin(Geometry::STGeomFromText('POLYGON EMPTY',4326)) => 0 --empty contains geometry SELECT Geometry::STGeomFromText('POLYGON EMPTY',4326).STContains(Geometry::STGeomFromText('POINT(1 2)',4326)) => 0 --empty within geometry SELECT Geometry::STGeomFromText('POLYGON EMPTY',4326).STWithin(Geometry::STGeomFromText('POINT(1 2)',4326)) => 0 -- empty relation to empty (evidentally its FFFFFFFF2) -- in otherwords exteriors, interiors and boundaries do not intersect except for exterior x exterior and that intersection is 2-d (infinite space) SELECT Geometry::STGeomFromText('POLYGON EMPTY',4326).STRelate(Geometry::STGeomFromText('POLYGON EMPTY',4326),'FFFFFFFF2') => 1