Search code examples
sql-servergeography

SQL Server geography testing if a point is within a bounds - what does STWithin() do?


I am testing various SQL geography type methods - specifically, I want to understand the difference between STContains() and STWithin().

According to the docs:

StWithin() - Returns 1 if a geography instance is spatially within another geography instance;

STContains() - Specifies whether the calling geography instance spatially contains the geography instance passed to the method.

I created a simple 1 km x 1 km POLYGON (a square actually) around Microsoft headquarters, visually, it looks like this:

geography bounds with center point

The data points used were as follows:

Center point: 47.6423318, -122.1391189

Polygon (Square) corner points:

SE: 47.6378402235794, -122.13244353271
NE: 47.6468233764206, -122.13244353271
NW: 47.6468233764206, -122.14577646729
SW: 47.6378402235794, -122.14577646729

The POLYGON was declared (using the left-hand rule) as follows, and checked if valid:

DECLARE @bounds geography;
SET @bounds = geography::STPolyFromText('POLYGON((-122.13244353271 47.6378402235794, -122.13244353271 47.6468233764206, -122.14577646729 47.6468233764206, -122.14577646729 47.6378402235794, -122.13244353271 47.6378402235794))', 4326 );

SELECT @bounds.STIsValid() AS 'STIsValid', @bounds.STIsClosed() AS 'STIsClosed';

SQL Returns:

STIsValid   STIsClosed
True        True

Next, I check if the center point is within the bounds (should be), as follows

DECLARE @point geography;
SET @point = geography::Point( 47.6423318, -122.1391189, 4326 );

SELECT @bounds.STContains( @point) AS 'STContains', 
       @bounds.STIntersects( @point ) AS 'STIntersects',
       @bounds.STOverlaps( @point ) AS 'STOverlaps',
       @bounds.STWithin( @point ) AS 'STWithin';

SQL Returns:

STContains  STIntersects    STOverlaps  STWithin
True        True            False       False

Note: I was expecting STWithin to be True, but it turns out the center point is NOT "within" the bounds?

Next, I check if the SW corner point is considered "in" bounds, as follows:

SET @point = geography::Point( 47.6378402235794, -122.14577646729, 4326 );

SELECT @bounds.STContains( @point) AS 'STContains', 
       @bounds.STIntersects( @point ) AS 'STIntersects',
       @bounds.STOverlaps( @point ) AS 'STOverlaps',
       @bounds.STWithin( @point ) AS 'STWithin';

SQL Returns:

STContains  STIntersects    STOverlaps  STWithin
False       True            False       False

Note: In this case, STContains() returns False (which is expected), but STIntersects() returns True; useful if you need to consider the edge points as being "in" bounds or not.

Last test - a point OUTSIDE of the bounds:

SET @point = geography::Point( 47.647, -122.13244353271, 4326 );

SELECT @bounds.STContains( @point) AS 'STContains', 
       @bounds.STIntersects( @point ) AS 'STIntersects',
       @bounds.STOverlaps( @point ) AS 'STOverlaps',
       @bounds.STWithin( @point ) AS 'STWithin';

SQL Returns:

STContains  STIntersects    STOverlaps  STWithin
False       False           False       False

In all of the tests above, that is, testing for a point INSIDE the bounds, on the EDGE of the bounds, and OUTSIDE the bounds, STWithin() returns False - What condition is required for STWithin() to return True? (Or, does STWithin() simply not work?)

Also, I was expecting STOverlaps() to return true in some cases, but if someone can comment on that method, it would help as well.

Any advice would be appreciated.


Solution

  • Ok, so it would seem that the following is true:

    @point.STWithin( @bounds ) == @bounds.STContains( @point );
    

    For example, the following query (testing if center point is within bounds):

    DECLARE @point geography;
    SET @point = geography::Point( 47.6423318, -122.1391189, 4326 );
    
    SELECT @bounds.STContains( @point) AS 'STContains', 
           @point.STWithin( @bounds ) AS 'STWithin';
    

    Returns:

    STContains  STWithin
    True        True