SQL Server 2008 GEOGRAPHY STDistance() value - sql

I am using geography.STDistance() to return the distance between two single point locations. I'm curious as to which measurement is used for the return value? Is it in KM's, miles or perhaps some other?
I'm getting results back upwards of 250k but i've no idea if im doing something wrong with my TSQL as these are historical locations(i.e. they no longer exist) so I can't just do a quick lookup.
declare #p1 geography
declare #p2 geography
SELECT #p1 = Location from tblLocations where Id = 1
SELECT #p2 = Location from tblLocations where Id = 2
select #p1.STDistance(#p2)

I think the return measurement depends upon the Spatial Reference Identifiers (SRIDs)
of your geography data type. The default is 4326 which is in meters. There' a table in the DB you can check Select * from sys.spatial_reference_systems

Just to cover people arriving here looking for the answer when using STDistance with GEOMETRY types, the result is "expressed in the same unit of measurement as the coordinate values themselves' (from 'Beginning Spatial with SQL Server 2008') which for WGS84 / SRID 4326 data is in Degrees.
The following SQL should run on SQL Server 2008 R2 and above. (Source of location data for Edinburgh Waverley and London Charing Cross stations bing maps):
DECLARE #edinGeom GEOMETRY = GEOMETRY::STGeomFromText('POINT(-3.1917 55.9517)', 4326)
DECLARE #cxGeom GEOMETRY = GEOMETRY::STGeomFromText('POINT(-0.1252 51.5083)', 4326)
SELECT #edinGeom.STDistance(#cxGeom), sqrt(square(3.1917-0.1252) + square(55.9517-51.5083)) AS 'Distance from Pythagoras';
DECLARE #MetersPerMile FLOAT = 1609.344;
DECLARE #edinGeog GEOGRAPHY = GEOGRAPHY::STGeomFromText('POINT(-3.1917 55.9517)', 4326)
DECLARE #cxGeog GEOGRAPHY = GEOGRAPHY::STGeomFromText('POINT(-0.1252 51.5083)', 4326)
SELECT #edinGeog.STDistance(#cxGeog), #edinGeog.STDistance(#cxGeog)/#MetersPerMile;
The results for the first 3 lines using GEOMETRY types are:
STDistance Geom: 5.39881707506376, Distance From Pythagoras: 5.39881707506376
The results for the GEOGRAPHY types are:
STDistance Geog: 534226.761544321, Converted to Miles: 331.953119745885
The '331 miles' or so from the GEOGRAPHY calculation with conversion ties in nicely with that shown on Bing maps as the distance between two points (clearly this is not a proof of anything, but it suggests similar underlying calculations).
The numbers output by the GEOMETRY calculation hopefully demonstrate that the result is very clearly in degrees, with the value apparently being calculated using pythagoras (the underlying calculations would be more complex if we were getting the distance between points and polygons).

Related

Problem with ST_AREA regarding coordinate system

I want to select all polygons with area more than 350 square kilometers from my initial polygons and tried ST_Area. But my polygons are in WKID: 25832 (UTM 32N) and I read that ST_Area always calculates in WGS 84 (EPSG 4326)....
I tried it like this but did not work.
DROP TABLE IF EXISTS projekt."Final_Selection";
CREATE TABLE projekt."Final_Selection" AS
SELECT (st_area/1000000), geom
FROM projekt."New_Pot" WHERE st_area>350 ;
and like this:
DROP TABLE IF EXISTS projekt."Final_Selection";
CREATE TABLE projekt."Final_Selection" AS
SELECT (st_area(ST_Transform(geom,25832)))/10000000, geom
FROM projekt."New_Pot" WHERE st_area>350;
Does anyone have an advice for me? Thx in advance!
If you want to select geometries by their size in sqm use ST_Area with geography instead of geometry:
For geography types by default area is determined on a spheroid with units in square meters.
SELECT * FROM projekt."New_Pot"
WHERE ST_Area(ST_Transform(geom,4326)::geography) >= 350000
Note: keep in mind that using ST_Area in the WHERE clause might slow down your query significantly, as the area is being calculated in query time. If it isn't a one time query, consider creating an index with the area pre-calculated, e.g.
CREATE INDEX idx_geom ON projekt."New_Pot" (ST_Area(ST_Transform(geom,4326)::geography));
See also: Calculate the percentage of polygon inside the buffer in postgresql
In case you want to query polygons within a given radius: the easiest way would be to convert your geometry column to geography and from there use ST_DWithin, which will calculate the distance in metres when used with geography parameters:
SELECT * FROM projekt."New_Pot"
WHERE
ST_DWithin(
ST_Transform(geom,4326)::geography, -- your geometry converted to geography
ST_MakePoint(1,52)::geography, -- a given point
350000) -- a 350km buffer
Related post: Getting all Buildings in range of 5 miles from specified coordinates

Use sql to find out if the radius of one point intersects the radius of another point

I've got locations stored in the database(SQL-server), their location is declared as geography and another column declaring it's radius.
Giving a geography(or longitude/latitude) as parameter I would like to get the locations with a radius intersecting the parameters radius (a constant value) in return.
Any information on how to solve this is appreciated.
First note that the radii intercept each other if and only if the sum of the radii is greater than the distance between the two points.
I'm not that familiar with the syntax but you want something like this - I'm making use of the STDistance function of the Transact SQL geography data type.
SELECT * FROM TABLE
WHERE TABLE.RADIUS + #radius >= #geog.STDistance(TABLE.GEOGRAPHY);

Get Distance in Meters instead of degrees in Spatialite

I have the following query:
select distance(GeomFromText('POINT(8 49)',4326),GeomFromText('LINESTRING(8.329969 49.919323,8.330181 49.919468)',4326))
this gives me 0.97 degrees. But I need it in meters and do not know which SRID to transform to.
Can somebody give me an example how to get the result in meters for spatialite?
The positions are all in Europe.
Just multiply the value in degrees by 111195 - this value is (Earth mean radius)*PI/180 - that is 'mean length of one great circle degree in meters on Earth's surface'.
The result obtained using this method is within 1% of the geodesic distance for the WGS84 ellipsoid.
EDIT
OK, my answer above still stands for the question: "how to convert arcs in degrees into lengths in meters", however, it's not the question you asked (should have asked).
I haven't used Spatialite professionally, so I assumed that your sample query indeed returns the 'length in degrees'. That's not true.
Unfortunately, it appears that Spatialite fails to calculate the distance in 'geographic sense'. Despite your geometries are defined with SRID 4326, it treats them as if they were on a plane.
Here's a simple proof:
select Distance(GeomFromText('POINT(0 0)',4326),GeomFromText('POINT(3 4)',4326));
returns 5.0.
It's a shame ...
Lets have a look at your original query:
select Distance(
GeomFromText('POINT(8 49)',4326),
GeomFromText('LINESTRING(8.329969 49.919323,8.330181 49.919468)',4326)
)
An equivalent query in MS SQL Server:
SELECT (geography::STGeomFromText('POINT(8 49)', 4326)).STDistance(geography::STGeomFromText('LINESTRING(8.329969 49.919323,8.330181 49.919468)', 4326));
gets you the correct result immediately: 105006.59673084648, in meters, and without any extra brouhaha.
So what are your options with Spatialite?
Indeed, as you said in comments, one option is to project your geometries, and calculate on those. Using SRID 3035 for Europe makes sense, too (if your locations are mostly in Germany, I'd consider SRID 25832).
select Distance(
Transform(GeomFromText('POINT(8 49)',4326),25832),
Transform(GeomFromText('LINESTRING(8.329969 49.919323,8.330181 49.919468)',4326),25832)
)
returns 104969.401605453.
As to your other sample (in comments):
select distance(
Transform(GeomFromText('POINT(8.328957 49.920900)',4326),3035),
Transform(GeomFromText('POINT(8.339665 49.918000)',4326),3035)
)
There's a simpler way to do it (if you have two POINTs, not a POINT and a LINESTRING): create a LINESTRING with your POINTs and use GeodesicLength function, like this:
select GeodesicLength(GeomFromText('LINESTRING(8.328957 49.920900, 8.339665 49.918000)',4326))
It returns 833.910006698673, as expected.
In SpatiaLite's functions reference guide, you can see there are two version of the Distance() function. One takes only two arguments and return the distance in CRS units, the other takes 3 arguments and return the distance in meters.
To get the distance in meters, simply pass a third argument to Distance:
sqlite> select Distance(MakePoint(0, 0), MakePoint(3, 4));
5.0
sqlite> select Distance(MakePoint(0, 0), MakePoint(3, 4), 1);
554058.923752633

calculate all latitude and longitudes in a metric area

I know there are many threads about calculating from lat/long to metric systems. This solves the first part of my problem.
The second (main part) of my problem is this:
I have google maps as map app.
I have a lat&long pair of a point
I have a a metric distance, lets say 30meters.
I need some point to do a sql query.
Why?
I have many records of POIs which can be located with this lat&long pair. I don't want to get all the results (reason: better performance).
Is there a way to do something like:
select * from myTable where lat> .... and lat < ... and long > ... and long < ...?
Is this possible? And if its, how do I get these values in ... ?
Regards!
Take a look at the article: Selecting points within a bounding circle
and the SO question: SQL Query for Performing Radius Search based on Latitude Longitude
These should get you started.
here is a sample query that returns the distances between a supplier lat/lng and a database of lat/lng pairs:
DECLARE
#Latitude float,
#Longitude float
SET #Latitude = 46.51257000000000
SET #Longitude = -84.33660900000000
DECLARE #SampleData TABLE
(
Name varchar(100),
Lat float,
Lng float
)
INSERT INTO #SampleData
(Name, Lat, Lng)
SELECT 'Point1', 46.52450048415351,-84.35223018530274
UNION
SELECT 'Point2', 46.51835838382206,-84.30279170874024
UNION
SELECT 'Point3', 46.522138220045285,-84.2622796237793
UNION
SELECT 'Point4', 46.54056115608927,-84.223140829834
SELECT
Name,
Lat,
Lng,
-- using spherical law of cosines, where 6371 is the earth's radius in km. Returns km.
Distance = ACOS(SIN(RADIANS(#Latitude))*SIN(RADIANS(Lat))+COS(RADIANS(#Latitude))*COS(RADIANS(Lat))*COS(RADIANS(Lng-#Longitude)))*6371
FROM
#SampleData

Incorrect Distances When Comparing geography values in SQL

I have a SQL database set up containing a number of fields one of which is a geography field called Coordinates. I have a need to search for all rows that are within a certain radius and to do that I am using the query
DECLARE #CurrentLocation geography;
SET #CurrentLocation = geography::Point(-84.505562, 39.137706, 4326)
SELECT * , Coordinates.STDistance(#CurrentLocation) AS Distance FROM tParkingLot
WHERE Coordinates.STDistance(#CurrentLocation )<= 200000
which gives me the following results
the first row is returning as expected as I used those coordinates as my center. However, when measuring the other two lots on google maps I find that the results should be closer to 1133.246 for row 2 and 74673.56 for row 3
I can see from other stackoverflow results that a common mistake is to insert data as lat/lon as opposed to lon/lat however I have already made and corrected that mistake and I cannot determine why I am getting distance results that are so far off from the actual measurements.
The cause of the problem is the order of latitude and longitude. Geography world traditionally uses (lat, lon) order, SQL world defined (x, y) order, which usually means (lon, lat) order.
Microsoft SQL Server Point constructor made confusing compromise: they use (x, y) order for Geometry, but (lat, lon) order for Geography:
Point ( Lat, Long, SRID ) [1]
Since your points are in Cincinnati rather than Antartica, swap argument order.
[1] https://learn.microsoft.com/en-us/sql/t-sql/spatial-geography/point-geography-data-type?view=sql-server-2017