I have downloaded a shapefile from Ordinance Survey website, and imported it into SQL using ShpToSql.exe, here the setup
This imports fine.
Now i want to query what region a particular longitude and latitude is within.
Using the following
Postcode: WD25 7LR (Harry Potter Studios :D)
Latitude: 51.6910751568794
Longitude: -0.418128358906299
I thought i could write something like
DECLARE #g geography
set #g = geography::Point(51.6910751568794, -0.418128358906299, 4326)
select [name] from region where #g.STWithin(geom) is not null
But that returns an error message of Operand type clash: geometry is incompatible with geography
So i tried to change the data type to geometry, so code looks like this
declare #g geometry
set #g = geometry::Point(51.6910751568794, -0.418128358906299, 4326)
select [name] from region where #g.STWithin(geom) = 1
But no results are returned.
Can someone help me with this please. Just want to know if a longitude and latitude is within a particular POLYGON.
UPDATE:
I have tried to import the .shp file using Geography data type, but this gives an error message when i try to import it
And even if i still try to import it, i them get a message like this for every shape in the shp file
SQL Server will not allow comparisons between geometry and geography data types.
If you are not concerned with being exact, you could perform your original query with the point as a geometry type. This will work reasonably well in most small cases where you aren't comparing odd shapes and across hemispheres.
DECLARE #g geometry
set #g = geometry::Point(51.6910751568794, -0.418128358906299, 4326)
select [name] from region where #g.STWithin(geom) is not null
Otherwise you will need to import the data as a geography type and based on your note there might be some malformed data in the shapefile.
Related
I use 2 ways if calculating distance between coordinates, difference between them is quite big (more than 400 m). Do you know why? And which one of all ways of calculating is the most accurate?
1st:
DECLARE #source geography = 'POINT(53.9202690124512 14.2586479187012)'
DECLARE #target geography = 'POINT(53.8970128 14.2387088)'
SELECT #source.STDistance(#target)
2nd:
SELECT geography::Point(53.9202690124512, 14.2586479187012, 4326).STDistance(geography::Point(53.8970128, 14.2387088, 4326))
geography::Point expects the latitude and longitude to be passed in that order. WKT's POINT expects longitude and latitude to be passed in that order. See the results of:
DECLARE #wkt_source geography = 'POINT(53.9202690124512 14.2586479187012)'
DECLARE #wkt_target geography = 'POINT(53.8970128 14.2387088)'
select #wkt_source.STAsText() as wkt_source, #wkt_target.STAsText() as wkt_target
declare #point_source geography = geography::Point(53.9202690124512, 14.2586479187012, 4326)
declare #point_target geography = geography::Point(53.8970128, 14.2387088, 4326)
select #point_source.STAsText() as point_source, #point_target.STAsText() as point_target
So you will need to swap one or other around to get consistent results (depending on which lat/lng is correct).
DECLARE #g123 geometry = geometry::STGeomFromText('LINESTRING(-116.13047257831279 56.30017316623788, -116.12913641697267 56.300147332468164, -116.12779547120535 56.299607730102615, -116.124571914419 56.29911596359641, -116.1192082382492 56.299140730412596, -116.11778756789873 56.29966295365294, -116.11782543098967 56.30010153331982, -116.11560636945568 56.300912282474926)', 4326);
If i am using these coordinates and then used STIsValid() method, it returns 1 as valid. But in another set of linestring when I was using same STIsValid() it gives me 0 as invalid. below is my another set of linestring which is invalid according to the STIsValid().
DECLARE #g geometry = geometry::STGeomFromText('LINESTRING(-115.53767705050582 53.22672158956674, -115.53765868534818 53.22671128738061, -115.53767705050582 53.22672158956674, -115.53654381140187 53.226143048362516, -115.53339315469746 53.22253902683848, -115.53152182202682 53.22190044473827, -115.5230860935366 53.21472328981251, -115.52130569462724 53.20876969345053, -115.51409690401672 53.200303872725705, -115.51289773935069 53.19938946371285, -115.50194164539707 53.192345671197565, -115.48749425897319 53.18330927377852, -115.48660896207417 53.18226350516222, -115.47864624419178 53.177401797081366, -115.47740128431087 53.17682485911916, -115.46443612708893 53.16871949656764, -115.45674907970934 53.16393156693292, -115.44008277099036 53.148539644479875, -115.43876927594148 53.14733292030516, -115.42056871209088 53.13594112513783, -115.41810867029544 53.13418489856417, -115.41569669016428 53.13245707290928, -115.40702454305763 53.126296683826, -115.40060737841817 53.12421338884663, -115.3933730945182 53.11839374047024, -115.39110711038367 53.11570405230429, -115.38971149045324 53.11399817649696, -115.37762498169889 53.10661108600986, -115.37227436164345 53.105117030992915, -115.36926227096991 53.10426983449939, -115.364528013777 53.10174737901163, -115.36239004945196 53.100546579648075, -115.53770036268578 53.22673349109732, -115.53767705050582 53.22672158956674, -115.55271685609667 53.23602696078559, -115.55524236980497 53.238300471678045, -115.55611787866064 53.24257818908546, -115.55632746280297 53.242985670788634, -115.55734856610832 53.24858981971976, -115.55760951412194 53.24900780760328, -115.55767943988195 53.24919395033266, -115.557773056911 53.24944273957723, -115.55844649693005 53.250495186947575, -115.56047267939273 53.253683474193465, -115.56172434789217 53.25567849911704, -115.56915822397356 53.265061269624596, -115.57226795433193 53.269060314219125, -115.58054632493382 53.27965622974886, -115.58312853491297 53.283002280874825, -115.60422619519927 53.305529442840346, -115.60787170325098 53.30881342553875, -115.60941844316844 53.31154271134389, -115.55279421451736 53.23608965992021, -115.54973236975353 53.233483842184285, -115.53767705050582 53.22672158956674)', 4326);
Your line is invalid because it is closed and crosses itself. See this page from the PostGIS docs (I'm sure there is one for SQL Server too as it is also OGC compliant).
Does anybody know what is the problem with my query. I am trying to calculate area using geographical coordinates, but result seems to be too small to be true. 0.00118 sqm. Can anybody help?
SELECT ST_Area(the_geom) As sqm
FROM (SELECT
ST_GeomFromText('POLYGON
(
(14.604514925547997 121.0968017578125,
14.595212295624522 121.08512878417969,
14.567302046916149 121.124267578125,
14.596541266841905 121.14761352539062,
14.604514925547997 121.0968017578125)
)',4326) ) As foo(the_geom)
How accurate should be the calculation?
A solution is to cast GEOMETRY to GEOGRAPHY, which is acceptably accurate for the most use cases:
SELECT ST_Area(the_geom::GEOGRAPHY ) As sqm
FROM (SELECT
ST_GeomFromText('POLYGON
(
(14.604514925547997 121.0968017578125,
14.595212295624522 121.08512878417969,
14.567302046916149 121.124267578125,
14.596541266841905 121.14761352539062,
14.604514925547997 121.0968017578125)
)',4326) ) As foo(the_geom)
The geography type automatically converts degrees to meters.
Depending on your scenario you could also use directly the geography constructor St_GeographyFromText, which accept a WKT string as argument, very similar to ST_GeomFromText
ST_GeographyFromText('POLYGON((14.604514925547997 121.0968017578125,
14.595212295624522 121.08512878417969,
14.567302046916149 121.124267578125,
14.596541266841905 121.14761352539062,
14.604514925547997 121.0968017578125))'
)
I have a collection of lat/long pairs that specify the coordinates of property. An example of such would be this:
(-97.7157864909875 30.406523562241, -97.7165355983733 30.4068866164923, -97.716787930159 30.4064954568619, -97.7168253595963 30.4065335493939, -97.7168568837126 30.4065754656014, -97.7168819885038 30.4066205220471, -97.716901410079 30.4066619534041, -97.7169158736828 30.4066896672646, -97.7169327718411 30.4067163205123, -97.716952004751 30.4067417571518, -97.7169734604111 30.4067658281043, -97.7176225812739 30.4057595267431, -97.7168902352123 30.4054046115321, -97.7164489526149 30.4060840883432, -97.7163106372336 30.4061380633337, -97.7158545237879 30.4059170368804, -97.7158294889129 30.4060371739058, -97.7158101406138 30.4061580975546, -97.7157965112558 30.4062796056817, -97.7157886236446 30.4064014951642, -97.7157864909875 30.406523562241)
Lets say I have maybe 30K or so of these in a table.
Then I have a single lat/lon pair and I need to know which of the polygons (could be more than one) does this point fall inside of.
I have very limited experience with spatial types and I am trying ensure I can properly define indexes on this to gain the speed I need with this query.
Any help you can offer to get me headed in the right direction is greatly appreciated.
I believe I can do it like this:
DECLARE #CurrentPosition geography
declare #Line geography
DECLARE #result geography
SET #CurrentPosition = geography::STGeomFromText('POINT(lat lon)', 4326)
set #Line = geography::STGeomFromText('LINESTRING(point collection)', 4326)
SELECT #result = #Line.STIntersection(#CurrentPosition)
select #result.ToString()
This will help with the actual is it inside the line or not - but the question I am struggling with is how to define the selection statement to perform the above transaction when I am searching the table for the items that intersect.
Thanks
Environment: SQL Server 2012
I'm using an online tool, the only one I could find so far, to plot polygons and points on the earth. http://www.birdtheme.org/useful/googletool.html
I have two tables. One stores "areas" as polygons and the other table stores points amongst other things irrelevant to my question.
For simplicity, I'll just reduce my scenario to sql variables.
In the query below, I'm using the geography data type for well known points of interest.
I drew a polygon around Robben Island, a point in Robben Island and a point in Alcatraz.
DECLARE #robben_island geography = ('POLYGON((18.351803 -33.788421,18.382788 -33.787494,18.386736 -33.820515,18.354464 -33.822369,18.351803 -33.788421))')
DECLARE #point_in_robben_island geography= ('POINT(18.369226 -33.80554)')
DECLARE #point_in_alcatraz geography= ('POINT(-122.423401 37.827006)')
SELECT #robben_island.STContains(#point_in_robben_island) --returns 'False', but it's not what I expected
SELECT #robben_island.STContains(#point_in_alcatraz) --returns 'True', but it's not what I expected
This query above, if I understand it correctly, tells me that my #point_in_robben_island is not contained in #robben_island, rather my #point_in_alcatraz exists in #robben_island which as we all know, is not true.
Now when I change the data types from geography to geometry, everything works fine, but I'm afraid that if I continue using the geometry data type I might come across a few gotchas. I'm just wondering if I won't be negatively affected by fact that geometry doesn't quite account for earth's curvature. touch wood.
DECLARE #robben_island geometry = ('POLYGON((18.351803 -33.788421,18.382788 -33.787494,18.386736 -33.820515,18.354464 -33.822369,18.351803 -33.788421))')
DECLARE #point_in_robben_island geometry= ('POINT(18.369226 -33.80554)')
DECLARE #point_in_alcatraz geometry= ('POINT(-122.423401 37.827006)')
SELECT #robben_island.STContains(#point_in_robben_island) --returns 'True' as it should
SELECT #robben_island.STContains(#point_in_alcatraz) --returns 'False' as it should
Now my question is, why does the geography data type return unexpected results while geometry works as expected? Thank you very much.
The geography type is a little bit more restrictive than geometry. It can't cross different hemispheres and the outer ring must be drawn counter-clockwise.
Unfortunately (some find this a good thing), SQL Server 2012 no longer throws an error when you create the invalid geography. You need to invert the order of the points in the Roben Island geometry, like:
DECLARE #robben_island geography = ('POLYGON((18.351803 -33.788421, 18.354464 -33.822369,18.386736 -33.820515, 18.382788 -33.787494, 18.351803 -33.788421))')
DECLARE #point_in_robben_island geography= ('POINT(18.369226 -33.80554)')
DECLARE #point_in_alcatraz geography= ('POINT(-122.423401 37.827006)')
SELECT #robben_island.STContains(#point_in_robben_island) --returns 'True'
SELECT #robben_island.STContains(#point_in_alcatraz) --returns 'False'