How do I use the great circle distance calculation in T-SQL - sql-server

How can i control the latitude and the longitude of the Team and the Customer with reasonable error margin from the Job Table?
if the two values are close to eachother i will return it "true", if not "false"
Job Table (like this) :
JobID CustomerID TeamID
2 13 1
3 13 2
Team Table :
TeamID Latitude Longitude
1 41.019 28.965
2 42.019 27.165
Customer Table
ID Latitude Longitude
13 13.557 13.667

The function:
CREATE FUNCTION dbo.DictanceKM(#lat1 FLOAT, #lat2 FLOAT, #lon1 FLOAT, #lon2 FLOAT)
RETURNS FLOAT
AS
BEGIN
RETURN ACOS(SIN(PI()*#lat1/180.0)*SIN(PI()*#lat2/180.0)+COS(PI()*#lat1/180.0)*COS(PI()*#lat2/180.0)*COS(PI()*#lon2/180.0-PI()*#lon1/180.0))*6371
END

i assume you mean: how can i write a select statement to return true if two latitudes and longitudes are within x miles of each other... or something like that?
look up 'great_circle_distance'
write a query that links the customer to the team.
perform the great circle distance calculation on the two lats and longs.
compare this to you desired distance.
use decode or some similar construct to turn that into a 'True' or 'False' value.

SQL has a geography datatype and a method to calculate the distance between two. Covert your Lat, Long pairs to geography.
http://msdn.microsoft.com/en-us/library/microsoft.sqlserver.types.sqlgeography.stdistance.aspx
Then you could use a function to return a true or false.

Related

Expand a point by a specific number of miles

So I have a Geography type spatial column in SQL Server. This represents lat/long coordinates as a single point
Values look like this (as a string)
POINT (-96.63 32.97)
What I want to do is expand it into a circle/polygon by a specific number of miles.
I think STBuffer is the command I want, but I have no idea how to use it expand the radius by X number of miles.
My goal is to then later do STContains on it to see if some other geography object is contained inside of it. Something like this:
WHERE L.SPATIAL_OBJ.STContains(IBL.SPATIAL_OBJ) = 1
I figured it out. It looks like this.
I would do it as a function.
CREATE FUNCTION [dbo].[udf_mile_ring]
(
#lat FLOAT,
#lon FLOAT,
#miles int
)
RETURNS GEOGRAPHY
AS
BEGIN
DECLARE #meters FLOAT = #miles / 0.000621371;
RETURN GEOGRAPHY::Point(#lat, #lon, 4326).STBuffer(#meters);
END
GO

Fastest way to calculate distances between two coordinates?

We currently use the Geography type to calculate distance between a current location and the coordinates in our tsql table. Our code is based on this sqlauthority.com example.
Is there a faster way to retrieve the distance between two points? These calls will be done by a mobile phone app, so they should ideally be very fast.
After testing it with a distance I know, looping 100 times per batch and running the batch 15 times to make sure the 10 runs the client statistics stores in SSMS are cycled past initial query plan generation so it doesn't skew the results. Here are the averages of the remaining. The calculation method seems to be twice as fast as the geography option.
With a difference in distance returned of 0.0000000020044.
Calculation script used (returned miles: 41.9013152732833)
set nocount on;
declare
#lat1 float = 45.489614
,#lon1 float = -122.650021
,#lat2 float = 44.94404
,#lon2 float = -123.025739
select 3959.1825574 * acos(sin(#lat1/57.295779513082323) * sin(#lat2/57.295779513082323) + cos(#lat1/57.295779513082323) * cos(#lat2/57.295779513082323) * cos((#lon2-#lon1)/57.295779513082323)) distance_in_miles
GO 100
Geography script used (returned miles: 41.9013152752877)
set nocount on;
declare
#g geography = geography::Point(45.489614, -122.650021, 4326)
,#h geography = geography::Point(44.94404, -123.025739, 4326)
select #h.STDistance(#g) / 1609.344 distance_in_miles -- 1609.344 is meters in mile. STDistance = meters.
GO 100
Fair warning, doing it in a non-system function will still have unpredictable performance. I would recommend doing it inline for calculation.
Here's a raw calculation example.
Working example of inline syntax for miles. It is the easiest, most accurate and shortest syntax I could find.
adjusted for accuracy
if object_id('tempdb..#LatLongInfo','U') is not null
begin
drop table #LatLongInfo;
end;
create table #LatLongInfo (
lat1 float,
lon1 float,
lat2 float,
lon2 float
);
insert into #LatLongInfo
values (21, -76, 23, -72);
select
3959.1825574 * acos(sin(lat1/57.295779513082323) * sin(lat2/57.295779513082323) + cos(lat1/57.295779513082323) * cos(lat2/57.295779513082323) * cos((lon2-lon1)/57.295779513082323)) distance_in_miles
from #LatLongInfo;
Hope this helps. I used something like this to find the doctors within a given range for patients back when sql2000 was released, it's been a while. Google was a newborn, no maps, nothing but a search box and one button. You have me all nostalgic now...I remember reading this when I coded that the first time.

How to get the distance between points in my geom and entry point longitude and latitude

everyone, I have table name 'geom' and I would like to calculate the distance between points that exist in my table like 0101000020730800001DAB949EE95D4040E124CD1FD3F04340
and entry points longitude and latitude and I tried this
SELECT *
FROM postgis.cafeecoor
WHERE ST_Distance_Sphere(geom, ST_MakePoint(32.733792,39.865589)) <= 1 * 1609.34
You can use the geography datatype that returns distances in meters.
SELECT *, st_distance(geom::geography, ST_MakePoint(32.733792,39.865589)::geography)
FROM postgis.cafeecoor
For your example, it returns 1760.32533367 meters.
Depending how your geometry is saved (as a true geometry or as text, with or without a set projection), you might have to add a few extra steps, like creating the geometry and setting its coordinate system
SELECT *, st_distance(
st_setsrid(geom::geometry,4326)::geography,
ST_MakePoint(32.733792,39.865589)::geography)
FROM postgis.cafeecoor;

Convert Numeric value like 177200 to 1772.00 in T-SQL statement

In SQL Server, I have a query that returns a value of 177200. I need this value represented as 1772.00 as the last 2 digits are past the decimal. The query below is adding .00 to the end of the full value. I have no experience in this type of SQL statement. Any help would be appreciated.
SELECT
STR(SUM(ActualPrice), 10, 2) AS Total, Department
FROM
#DepartmentSalesData
GROUP BY
Department
The data type you're looking for is called numeric
SELECT CAST(SUM(ActualPrice) / 100.0 AS numeric(18, 2)) AS Total, ...
FROM ...
You're passing in a precision (18 in my example) and a scale 2 in my example, as requested by you.

How to create a circle in meters in postgis?

I would like to ask how to create a circle with radius=4km. I have tried the ST_Buffer function but it creates a larger circle. (I see the created circle by inserting its polygon into an new kml file.)
This is what i am trying.
INSERT INTO camera(geom_circle) VALUES(geometry(ST_Buffer(georgaphy(ST_GeomFromText('POINT(21.304116745663165 38.68607570952619)')), 4000)))
The center of the circle is a lon lat point but I don't know its SRID because I have imported it from a kml file.
Do I need the SRID in order to transform the geometries etc?
KML files are always lat/long and use SRID=4326. This SRID is implied if you use geography. Geography is a good way to mix-in the 4 km metric measure on lat/long data ... excellent you tried this!
Try this statement to fix up the casts, and use a parameterized point constructor:
SELECT ST_Buffer(ST_MakePoint(21.304116745663165, 38.68607570952619)::geography, 4000);
And if you need to cast this back to geometry, add a ::geometry cast to the end.
Update on accuracy
The previous answer internally re-projects the geometry (usually) to a UTM zone that the point fits within (see ST_Buffer). This may cause minor distortions if the point is on the edge of two UTM boundaries. Most folks won't care about the size of these errors, but it will often be several meters. However, if you require sub millimeter precision, consider building a dynamic azimuthal equidistant projection. This requires PostGIS 2.3's ST_Transform, and is adapted from another answer:
CREATE OR REPLACE FUNCTION geodesic_buffer(geom geometry, dist double precision,
num_seg_quarter_circle integer)
RETURNS geometry AS $$
SELECT ST_Transform(
ST_Buffer(ST_Point(0, 0), $2, $3),
('+proj=aeqd +x_0=0 +y_0=0 +lat_0='
|| ST_Y(ST_Centroid($1))::text || ' +lon_0=' || ST_X(ST_Centroid($1))::text),
ST_SRID($1))
$$ LANGUAGE sql IMMUTABLE STRICT COST 100;
CREATE OR REPLACE FUNCTION geodesic_buffer(geom geometry, dist double precision)
RETURNS geometry AS 'SELECT geodesic_buffer($1, $2, 8)'
LANGUAGE sql IMMUTABLE STRICT COST 100;
-- Optional warppers for geography type
CREATE OR REPLACE FUNCTION geodesic_buffer(geog geography, dist double precision)
RETURNS geography AS 'SELECT geodesic_buffer($1::geometry, $2)::geography'
LANGUAGE sql IMMUTABLE STRICT COST 100;
CREATE OR REPLACE FUNCTION geodesic_buffer(geog geography, dist double precision,
num_seg_quarter_circle integer)
RETURNS geography AS 'SELECT geodesic_buffer($1::geometry, $2, $3)::geography'
LANGUAGE sql IMMUTABLE STRICT COST 100;
A simple example to run one of the functions is:
SELECT geodesic_buffer(ST_MakePoint(21.304116745663165, 38.68607570952619)::geography, 4000);
And to compare the distances to each of the buffered points, here are the lengths of each geodesic (shortest path on an ellipsoid of revolution, i.e. WGS84). First this function:
SELECT count(*), min(buff_dist), avg(buff_dist), max(buff_dist)
FROM (
SELECT ST_Distance((ST_DumpPoints(geodesic_buffer(poi, dist)::geometry)).geom, poi) AS buff_dist
FROM (SELECT ST_MakePoint(21.304116745663165, 38.68607570952619)::geography AS poi, 4000 AS dist) AS f
) AS f;
count | min | avg | max
-------+----------------+-----------------+----------------
33 | 3999.999999953 | 3999.9999999743 | 4000.000000001
Compare this to ST_Buffer (first part of answer), that shows it's off by about 1.56 m:
SELECT count(*), min(buff_dist), avg(buff_dist), max(buff_dist)
FROM (
SELECT ST_Distance((ST_DumpPoints(ST_Buffer(poi, dist)::geometry)).geom, poi) AS buff_dist
FROM (SELECT ST_MakePoint(21.304116745663165, 38.68607570952619)::geography AS poi, 4000 AS dist) AS f
) AS f;
count | min | avg | max
-------+----------------+------------------+----------------
33 | 4001.560675049 | 4001.56585986067 | 4001.571105793

Resources