STIntersect geography,geometry datatype - sql-server

I have a circle and point, point intersects circle in geometry but not in geography.
DECLARE #circle GEOGRAPHY = GEOGRAPHY::Point(39.10591303215, 21.923140028856, 4120).STBuffer(500)
DECLARE #geogpoint GEOGRAPHY = GEOGRAPHY::Point(51.590294, 25.16387, 4120)
select #circle,#geogpoint.ToString(),#geogpoint,#circle.STIntersects(#geogpoint)
DECLARE #circle1 geometry = geometry::Point(39.10591303215, 21.923140028856, 4120).STBuffer(500)
DECLARE #geomgpoint geometry = geometry::Point(51.590294, 25.16387, 4120)
select #circle1,#geomgpoint.ToString(),#geomgpoint,#circle1.STIntersects(#geomgpoint)
I have lot of circle and point,problem is geometry is intersecting almost all and geography very few.

For Geography, the buffer is in units per the SRID. For 4120:
SELECT unit_of_measure FROM sys.spatial_reference_systems WHERE spatial_reference_id = 4120
gives 'metre'
You are therefore adding a 500m buffer to your point. Now, what's the distance between your two (unbuffered) points?
DECLARE #circle GEOGRAPHY = GEOGRAPHY::Point(39.10591303215, 21.923140028856, 4120)
DECLARE #geogpoint GEOGRAPHY = GEOGRAPHY::Point(51.590294, 25.16387, 4120)
SELECT #circle.STDistance(#geogpoint)
1410017.60306578 metres
which explains why STIntersects returns false.
For Geometery, you are working in 'units'. What's the distance between your two points?
DECLARE #circle1 geometry = geometry::Point(39.10591303215, 21.923140028856, 4120)
DECLARE #geomgpoint geometry = geometry::Point(51.590294, 25.16387, 4120)
select #circle1.STDistance(#geomgpoint)
12.898143234446 'units' (in this case degrees)
which is why your second query returns true.
Have a look at the section "Measurements in spatial data types"
https://msdn.microsoft.com/en-us/library/bb964711.aspx

Related

Using geography type value with STContains function

Ok first, I'm storing Lat/Long location in a column of type geography in my SQL Server table.
I try to get stores in a specific bounding box using STContains method, the only way I found to use the geography point, was by concatening the Lat/Long with STR convertion:
DECLARE #boundingBox geography;
SET #boundingBox = geography::Parse('POLYGON((...))');
...
SELECT Store.Id, Store.Name, Store.Location.Lat, Store.Location.Long,
#boundingBox.STContains(
geography::Parse('POINT(' + STR(Store.Location.Lat, 20, 12) + ' '
+ STR(Store.Location.Long, 20, 12) + ')'))
It works, but it's ugly and asking if there's a cleaner way to write this.
I setup 2 test Polygons geo and geo2. One using Parse method and the other using STPolyFromText method.
declare #geo geography
= geography::STPolyFromText('POLYGON ((73.250684 34.198599, 73.250598 34.199324, 73.250343 34.200021, 73.249927 34.200663, 73.249369 34.201226, 73.248688 34.201688, 73.247912 34.202031, 73.247069 34.202243, 73.246193 34.202314, 73.245317 34.202243, 73.244474 34.202031, 73.243698 34.201688, 73.243017 34.201226, 73.242458 34.200663, 73.242043 34.200021, 73.241788 34.199324, 73.241701 34.198599, 73.241788 34.197874, 73.242043 34.197177, 73.242458 34.196535, 73.243017 34.195972, 73.243698 34.19551, 73.244474 34.195167, 73.245317 34.194956, 73.246193 34.194884, 73.247069 34.194956, 73.247912 34.195167, 73.248688 34.19551, 73.249369 34.195972, 73.249927 34.196535, 73.250343 34.197177, 73.250598 34.197874, 73.250684 34.198599, 73.250684 34.198599))', 4326)
declare #geo2 geography
= geography::Parse('POLYGON ((73.250684 34.198599, 73.250598 34.199324, 73.250343 34.200021, 73.249927 34.200663, 73.249369 34.201226, 73.248688 34.201688, 73.247912 34.202031, 73.247069 34.202243, 73.246193 34.202314, 73.245317 34.202243, 73.244474 34.202031, 73.243698 34.201688, 73.243017 34.201226, 73.242458 34.200663, 73.242043 34.200021, 73.241788 34.199324, 73.241701 34.198599, 73.241788 34.197874, 73.242043 34.197177, 73.242458 34.196535, 73.243017 34.195972, 73.243698 34.19551, 73.244474 34.195167, 73.245317 34.194956, 73.246193 34.194884, 73.247069 34.194956, 73.247912 34.195167, 73.248688 34.19551, 73.249369 34.195972, 73.249927 34.196535, 73.250343 34.197177, 73.250598 34.197874, 73.250684 34.198599, 73.250684 34.198599))')
declare #outsidePoint geography
= geography::STPointFromText('POINT(-122.34900 47.65100)', 4326),
#insidePoint geography
= geography::STPointFromText('POINT(73.2438096 34.1989505)', 4326)
select
geo = #geo,
geoString = #geo.ToString(),
IsValid = #geo.STIsValid(),
doesContainOutsidePoint = #geo.STContains(#outsidePoint),
doesIntersectOutsidePoint = #geo.STIntersects(#outsidePoint),
doesContainInsidePoint = #geo.STContains(#insidePoint),
doesIntersectInsidePoint = #geo.STIntersects(#insidePoint)
select
geo = #geo2,
geoString = #geo2.ToString(),
IsValid = #geo2.STIsValid(),
doesContainOutsidePoint = #geo2.STContains(#outsidePoint),
doesIntersectOutsidePoint = #geo2.STIntersects(#outsidePoint),
doesContainInsidePoint = #geo2.STContains(#insidePoint),
doesIntersectInsidePoint = #geo2.STIntersects(#insidePoint)
Both of them seem to work for me - with the following results:
I can query from my Polygon table in the DB using both the STContains and STIntersects methods of the geography type.
So if .STContains is returning 0 for you that means the Point is not inside the box. Maybe you could post a sample Polygon and Point where it is returning 0 but should return 1 that might help.

SQL Server Spatial convert from Linestring to Polygon

Can someone help me with convert SQL Server Geometry Shape object to Geometry Polygon object.
I have have a Shape object which is of "Ohio state plan south" coordinate with SRID 32123 and I want to convert it to Geometry Polygon with SRID 3857
This is what I have so far... when I am running it getting below error.
Error: "24119: The Polygon input is not valid because the start and end points of the exterior ring are not the same. Each ring of a polygon must have the same start and end points."
DECLARE #predata varchar(MAX) = 'LINESTRING (544447.66750002524 219649.36696754955, 544446.556000026 219690.21900326665, 544443.99000002549 219720.61000326276, 544435.09000002721 219836.23000326566, 544431.66000001959 219896.72000326123, 544430.0600000252 219924.97000326123, 544422.56000002555 219999.65000326186, 544419.05563904892 220098.02782277763)';
DECLARE #data geometry;
DECLARE #linestring geometry = #predata;
DECLARE #srid int;
SET #predata = REPLACE(#predata, 'LINESTRING', 'POLYGON(') + ')';
SELECT #predata AS PolygonString
SET #data = geometry::STGeomFromText(#predata, 3857);
SELECT #data AS GeographyPolygon
The error is telling you what the problem is. That is, polygons need to start and end with the same point. Your linestring does not (starts at 544447.66750002524 219649.36696754955, ends at 544419.05563904892 220098.02782277763). I was able to get it to work by taking the starting point of the linestring and appending it to the end. If this is a one-time operation, that should suffice.
As agreed with the previous answer please simply use your scripts with a bit change shown as below:
--start point should be same as end point
DECLARE #predata varchar(MAX) = 'LINESTRING (
544447.66750002524 219649.36696754955,
544446.556000026 219690.21900326665,
544443.99000002549 219720.61000326276,
544435.09000002721 219836.23000326566,
544431.66000001959 219896.72000326123,
544430.0600000252 219924.97000326123,
544422.56000002555 219999.65000326186,
544447.66750002524 219649.36696754955)';
--end point should be same as start point
DECLARE #data geometry;
DECLARE #linestring geometry = #predata;
DECLARE #srid int;
SET #predata = REPLACE(#predata, 'LINESTRING', 'POLYGON(') + ')';
SELECT #predata AS PolygonString
SET #data = geometry::STGeomFromText(#predata, 3857);
SELECT #data AS GeographyPolygon

Why simple sql geography polygon fill whole Earth?

I'm trying to create a geography polygon in sqlserver:
declare #poly geography = geography::STGeomFromText('POLYGON ((37.1834472346309 55.9627364130318, 37.1843162703518 55.9615689125515, 37.1847990679744 55.9603833217405, 37.1861294436458 55.9590893612134, 37.1858183074 55.9583972249632, 37.1860757994655 55.9580361054639, 37.1851960349086 55.9576990575455, 37.1834901499751 55.9592217684712, 37.1819451975825 55.9611416225876, 37.1812692809108 55.9626581795365, 37.1834472346309 55.9627364130318))', 4326)
select #poly
And result looks like this:
Why my polygon fill whole Earth?
I have an another polygon, and it's looks ok:
declare #poly2 geography = geography::STGeomFromText('POLYGON ((37.1880241973734 55.964234021191, 37.1895369632579 55.9640354359623, 37.1900519473887 55.9640655246991, 37.1912964923716 55.9645830472949, 37.1928736312724 55.9647996825904, 37.193699751649 55.9648598588536, 37.1936568363047 55.965563914075, 37.1913823230601 55.9656120541331, 37.1905669315196 55.966273973827, 37.1896227939463 55.9666891721997, 37.1887323005534 55.9674172628857, 37.1882924182749 55.9681333056029, 37.1881851299143 55.9689095049396, 37.1880027397013 55.9692163701344, 37.1877452476359 55.9693788271893, 37.1874341113901 55.9694570470084, 37.1870371444559 55.9694690808126, 37.186693821702 55.9694510301049, 37.1863504989481 55.9693246749138, 37.1861144645548 55.9691682345792, 37.1860179050303 55.9689696747783, 37.185974989686 55.9687711139541, 37.1862324817515 55.9675737103232, 37.186693821702 55.9673631078402, 37.1868332965708 55.9667192588449, 37.1874555690622 55.9663883042339, 37.1876165016031 55.9657384214668, 37.1881529434061 55.9652269319451, 37.1880134685373 55.964577029591, 37.1880241973734 55.964234021191))', 4326)
select #poly2
I trying this query in different versions of SqlServer (SqlExpress 2017 and SqlServer Professional 2016)- the results are same.
There are 2 ways to interpret polygons and it seems the order of the points you are supplying SQL Server with is not the one that it expects, so it's picking the area of the "outside" rather than the "inside".
This is the right to left rule, and the simple fix is to call native function ReorientObject() before selecting the polygon.
declare #poly geography = geography::STGeomFromText('POLYGON ((37.1834472346309 55.9627364130318, 37.1843162703518 55.9615689125515, 37.1847990679744 55.9603833217405, 37.1861294436458 55.9590893612134, 37.1858183074 55.9583972249632, 37.1860757994655 55.9580361054639, 37.1851960349086 55.9576990575455, 37.1834901499751 55.9592217684712, 37.1819451975825 55.9611416225876, 37.1812692809108 55.9626581795365, 37.1834472346309 55.9627364130318))', 4326)
select #poly.ReorientObject()
Note ReorientObject() is not supported with sql < 2012 the solution i came up is to draw right to left instead on left to right
If we specify points in clockwise direction, it takes all the points in the entire world except the region of the polygon whereas if we specify in counter clockwise direction, we get the expected region 😃.
Right to left
DECLARE #polygon geography = 'POLYGON((10 10, 30 10, 30 30, 10 10))';
SELECT #polygon
Left to right
DECLARE #polygon geography = 'POLYGON((10 10, 30 30, 30 10, 10 10))';
SELECT #polygon

Wrong area in STArea() in SQL Server

I am trying to count the surface (STArea ()) of a geometry saved as a WKT, but I get the wrong value.
I mention that using geography did not help. I also noticed, that in Postgis area is counted properly when I set use_spheroid=false.
DECLARE #g geometry = geometry::STGeomFromText('POLYGON((21.5601775022348 52.1813862660549,21.5601461469143 52.1813295549141,21.559853091877 52.1813993365177,21.5597665803865 52.1812613330339,21.5593037698201 52.1813692515792,21.5592677194965 52.1813021172669,21.5592607711581 52.1813034375092,21.5592188506351 52.1812334620465,21.5591248909245 52.1812561912586,21.5590395906618 52.1811137531269,21.5587491625113 52.1811808662478,21.5589985172752 52.1816470941761,21.5595646783963 52.1815219107667,21.5601775022348 52.1813862660549))', 4326);
select #g;
select #g.STArea()
In geography, the left hand side is the interior as you traverse the polygon. So you have specified the inverse of the polygon you want. With geometry it doesn't matter, as the interior is always the the bounded area.
Try
DECLARE #g geography = geography::STGeomFromText('POLYGON((21.5601775022348 52.1813862660549,21.5601461469143 52.1813295549141,21.559853091877 52.1813993365177,21.5597665803865 52.1812613330339,21.5593037698201 52.1813692515792,21.5592677194965 52.1813021172669,21.5592607711581 52.1813034375092,21.5592188506351 52.1812334620465,21.5591248909245 52.1812561912586,21.5590395906618 52.1811137531269,21.5587491625113 52.1811808662478,21.5589985172752 52.1816470941761,21.5595646783963 52.1815219107667,21.5601775022348 52.1813862660549))', 4326);
set #g = #g.ReorientObject()
select #g, #g.STArea()
outputs
2381.30781687796

How to select all points near a line?

I have a linestring:
LINESTRING( -43.0965167 -22.8808585,-43.0980368 -22.8807975,-43.0986518
-22.8807735,-43.0990955 -22.8807701,-43.0991492 -22.8807697,-43.1005956
-22.8807353,-43.1013221 -22.8807107,-43.1016904 -22.8807003,-43.1019484
-22.8806902,-43.1020398 -22.8806866,-43.102591 -22.8806801,-43.1029336
-22.8806666,-43.1036051 -22.8806402,-43.1039349 -22.8806272,-43.1042967
-22.880613,-43.1061912 -22.8805398 )
Now I can select all points at a distance "x" from this line ( around the line ). I already tried Find the nearest points along the linestring in specified distance limit and order with no success ( always take a lot of points in a box like distribution ). This is a piece of code I started to write:
select p.way,p.name from planet_osm_point p where ST_DWithin(
ST_Transform( theLineGeom, 4326), ST_Transform( p.way,4326 ), 0.9)
limit 50;
I think you are getting lots of points because ST_DWithin uses the unit of the geometries srid to perform the calculation.
boolean ST_DWithin(geometry g1, geometry g2, double precision distance_of_srid);
So you are getting all the points with 0.9 degrees from your linestring.
You could use
boolean ST_DWithin(geography gg1, geography gg2, double precision distance_meters);
converting your geometries to geography.
Otherwise you could use another srid.
Solved. I just get too many points because the limit was too big. If I have 2 points near the route and tell the query to give me the first 50 then 48 points will be far from the line but inside the bounding box. This is why the box like distribution I saw.
select * from pointscanner('<geomRoute_WILL_BE_HERE>') where round(distance::numeric, 5) < 500 /* meters */
CREATE OR REPLACE FUNCTION public.pointscanner( IN routeGeometry text)
RETURNS TABLE(way geometry, name text, distance double precision, tags hstore, operator text, admin_level text, z_order integer) AS
$BODY$
DECLARE
geomRoute geometry;
routeBB box2d;
BEGIN
geomRoute = ST_GeomFromText('MULTILINESTRING(
(-43.1761935 -22.90642,-43.1755606 -22.9064116,-43.1754828 -22.9064106,-43.1753675 -22.9064091),
(-43.176696 -22.9064062,-43.1761935 -22.90642),(-43.176805 -22.9064027,-43.176696 -22.9064062),
(-43.1776911 -22.9049927,-43.1776625 -22.9050849,-43.1775227 -22.9055527,-43.1775045 -22.9056041,-43.1774504 -22.9057582),
(-43.1782012 -22.9036858,-43.1778613 -22.9045254,-43.1777841 -22.9047287,-43.1777446 -22.9048326,-43.1777179 -22.9049164,-43.1776911 -22.9049927),
(-43.1772218 -22.9063929,-43.1771252 -22.9063944,-43.1771118 -22.9063946,-43.1769202 -22.9064008,-43.176805 -22.9064027),
(-43.1774504 -22.9057582,-43.1773182 -22.9061373,-43.1772868 -22.9062205,-43.1772218 -22.9063929),
(-43.1834587 -22.9028997,-43.1833984 -22.9028862,-43.1830022 -22.9027778,-43.1825446 -22.9026278,-43.1821151 -22.9024891,-43.1816491 -22.9023374,-43.1815912 -22.9023186,-43.1813461 -22.9022388),
(-43.1793173 -22.901581,-43.1791549 -22.9015418,-43.179105 -22.9015365,-43.1790457 -22.9015482,-43.1790174 -22.9015689,-43.178994 -22.9016082),
(-43.1788184 -22.9020931,-43.1786726 -22.9025241,-43.1786518 -22.9025769,-43.1786063 -22.9026931),
(-43.1813461 -22.9022388,-43.1806028 -22.9019969,-43.1805417 -22.901977,-43.1799074 -22.9017705),
(-43.1799074 -22.9017705,-43.1797658 -22.9017244,-43.1793732 -22.9015989,-43.1793173 -22.901581),
(-43.178994 -22.9016082,-43.1788184 -22.9020931),(-43.1784337 -22.9031366,-43.1782012 -22.9036858),
(-43.1786063 -22.9026931,-43.17848 -22.9030174,-43.178461 -22.9030662,-43.1784337 -22.9031366))',4326);
routeBB := ST_Extent(geomRoute);
RETURN QUERY
SELECT pt.way, pt.name, ST_Distance( geomRoute, ST_Transform(pt.way,4326) ) * 111195 as distance,
pt.tags, pt.operator, pt.admin_level, pt.z_order
FROM planet_osm_point pt
where routeBB && ST_Transform(pt.way,4326);
END; $BODY$
LANGUAGE plpgsql VOLATILE

Resources