Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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.

like image 234
bdcoder Avatar asked May 02 '20 02:05

bdcoder


1 Answers

What condition is required for STWithin() to return True? (Or, does STWithin() simply not work?)

STWithin, STContains : OGC standard methods, return 1 or 0, they indicate whether ALL the points of one instance exist entirely inside the other instance.

For your example, the point can exist within the polygon, but the polygon cannot exist within the point. Think of within as the "inverse" of contains: If x is within y then y contains x. --> If polygon contains point then point is within polygon:

SELECT 
@bounds.STContains( @point) AS 'bounds contains point',  --if this is true...
@point.STWithin( @bounds ) AS 'point is within bounds'; --...then this is also true

Also, I was expecting STOverlaps() to return true in some cases

This is a bit vague in the documentation (for geometry but it applies to geography too):

Remarks Two geometry instances overlap if the region representing their intersection has the same dimension as the instances do and the region does NOT equal either instance.

Point overlap with polygon --> the overlap is point (==equals either instance) --> 0.

You could test this by "cloning" the same spatial instance and check if it overlaps itself:

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 );
DECLARE @boundsclone geography=@bounds;

select @bounds.STOverlaps(@boundsclone), @boundsclone.STOverlaps(@bounds);
like image 154
lptr Avatar answered Oct 11 '22 18:10

lptr