wiki:DevWikiEmptyGeometry

Version 28 (modified by robe, 15 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_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

Note: See TracWiki for help on using the wiki.