title: SQL Server geography testing if a point is within a bounds - what does STWithin() do?
body: <p>I am testing various SQL geography type methods - specifically, I want to understand the difference between <code>STContains()</code> and <code>STWithin()</code>.</p>
  
  <p>According to the docs:</p>
  
  <p><a href="https://docs.microsoft.com/en-us/sql/t-sql/spatial-geography/stwithin-geography-data-type?view=sql-server-ver15" rel="noreferrer"><code>StWithin()</code></a> - Returns 1 if a geography instance is spatially within another geography instance; </p>
  
  <p><a href="https://docs.microsoft.com/en-us/sql/t-sql/spatial-geography/stcontains-geography-data-type?view=sql-server-ver15" rel="noreferrer"><code>STContains()</code></a> - Specifies whether the calling geography instance spatially contains the geography instance passed to the method.</p>
  
  <p>I created a simple 1 km x 1 km POLYGON (a square actually) around Microsoft headquarters, visually, it looks like this:</p>
  
  <p><a href="https://i.stack.imgur.com/uL650.png" rel="noreferrer"><img src="https://i.stack.imgur.com/uL650.png" alt="geography bounds with center point"></a></p>
  
  <p>The data points used were as follows:</p>
  
  <pre><code>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
  </code></pre>
  
  <p>The POLYGON was declared (using the left-hand rule) as follows, and checked if valid:</p>
  
  <pre><code>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';
  </code></pre>
  
  <p>SQL Returns:</p>
  
  <pre><code>STIsValid   STIsClosed
  True        True
  </code></pre>
  
  <p>Next, I check if the center point is within the bounds (should be), as follows</p>
  
  <pre><code>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';
  </code></pre>
  
  <p>SQL Returns:</p>
  
  <pre><code>STContains  STIntersects    STOverlaps  STWithin
  True        True            False       False
  </code></pre>
  
  <p><strong>Note:</strong> I was expecting <code>STWithin</code> to be <code>True</code>, but it turns out the center point is NOT "within" the bounds?</p>
  
  <p>Next, I check if the SW corner point is considered "in" bounds, as follows:</p>
  
  <pre><code>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';
  </code></pre>
  
  <p>SQL Returns:</p>
  
  <pre><code>STContains  STIntersects    STOverlaps  STWithin
  False       True            False       False
  </code></pre>
  
  <p><strong>Note:</strong> In this case, <code>STContains()</code> returns <code>False</code> (which is expected), but <code>STIntersects()</code> returns <code>True</code>; useful if you need to consider the edge points as being "in" bounds or not.</p>
  
  <p>Last test - a point OUTSIDE of the bounds:</p>
  
  <pre><code>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';
  </code></pre>
  
  <p>SQL Returns:</p>
  
  <pre><code>STContains  STIntersects    STOverlaps  STWithin
  False       False           False       False
  </code></pre>
  
  <p>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, <code>STWithin()</code> returns <code>False</code> - What condition is required for <code>STWithin()</code> to return <code>True</code>? (Or, does <code>STWithin()</code> simply not work?)</p>
  
  <p>Also, I was expecting <code>STOverlaps()</code> to return true in some cases, but if someone can comment on that method, it would help as well.</p>
  
  <p>Any advice would be appreciated.</p>