= 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_Union(geometry, empty) == geometry * ST_Union(empty, empty) == empty * ST_Intersection(geometry, empty) == geometry * 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) * ST_Contains(geometry, empty) == FALSE (SQL Server says False) * 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) * 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 ''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) SELECT Geometry::STGeomFromText('POLYGON EMPTY',4326).STRelate(Geometry::STGeomFromText('POLYGON EMPTY',4326),'FFFFFFFF2') => 1 }}}