calculate all latitude and longitudes in a metric area - sql

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

Related

How to get longitude and latitude for a place within certain distance from a known longitude and latitude

I want to find a place (longitude and latitude) with distance less than 10 km from a known longitude and latitude using BigQuery SQL. Is there any possible query for this?
I read your request as saying that given a geospatial point, you wish to query for anything within less than a 10km radius of that point. Here's two ways to solve this:
Using ST_BUFFER
You could use the ST_BUFFER function which similarly takes an argument of the radius to use around a point, but instead uses a segmented circle with 8 segments by default.
SELECT *
FROM `table`
WHERE ST_CONTAINS(
ST_BUFFER(
ST_GEOPOINT(longitude, latitude),
10 * 1000), -- Radius argument is expressed in meters
YourGeoPointColumn)
Using ST_BUFFERWITHTOLERANCE
You might use ST_BUFFERWITHTOLERANCE that replaces the segmented circle with tolerance instead of circle segments.
SELECT *
FROM `table`
WHERE ST_CONTAINS(
ST_BUFFERWITHTOLERANCE(
ST_GEOPOINT(longitude, latitude),
10 * 1000, -- Radius argument is expressed in meters
1), -- Tolerance of 1% of the buffer radius, expressed in meters
YourGeoPointColumn)
ST_Distance function should work here, like this:
with data as (
select 1 id, st_geogpoint(-122, 47) as geo
union all
select 2 id, st_geogpoint(-121, 47) as geo
)
select * from data
where st_distance(geo, st_geogpoint(-122.1, 47)) < 10000
id geo
------------------
1 POINT(-122 47)
Another way to write the distance condition is
ST_DWithin(geo, st_geogpoint(-122.1, 47), 10000)
If something does not work, please provide sample data and what data you expect in the results but is missing.

Why is PostGIS stacking all points on top of each other? (Using ST_DWithin to find all results within 1000m radius)

New to PostGIS/PostgreSQL...any help would be greatly appreciated!
I have two tables in a postgres db aliased as gas and ev. I'm trying to choose a specific gas station (gas.site_id=11949) and locate all EV/alternative fuel charging stations within a 1000m radius. When I run the following though, PostGIS returns a number of ev stations that are all stacked on top of each other in the map (see screenshot).
Anyone have any idea why this is happening? How can I get PostGIS to visualize the points within a 1000m radius of the specified gas station?
with myplace as (
SELECT gas.geom
from nj_gas gas
where gas.site_id = 11949 limit 1)
select myplace.*, ev.*
from alt_fuel ev, myplace
where ST_DWithin(ev.geom1, myplace.geom, 1000)
The function ST_DWithin does not compute distances in meters using geometry typed parameters.
From the documentation:
For geometry: The distance is specified in units defined by the
spatial reference system of the geometries. For this function to make
sense, the source geometries must both be of the same coordinate
projection, having the same SRID.
So, if you want compute distances in meters you have to use the data type geography:
For geography units are in meters and measurement is defaulted to
use_spheroid=true, for faster check, use_spheroid=false to measure
along sphere.
That all being said, you have to cast the data type of your geometries. Besides that your query looks just fine - considering your data is correct :-)
WITH myplace as (
SELECT gas.geom
FROM nj_gas gas
WHERE gas.site_id = 11949 LIMIT 1)
SELECT myplace.*, ev.*
FROM alt_fuel ev, myplace
WHERE ST_DWithin(ev.geom1::GEOGRAPHY, myplace.geom::GEOGRAPHY, 1000)
Sample data:
CREATE TABLE t1 (id INT, geom GEOGRAPHY);
INSERT INTO t1 VALUES (1,'POINT(-4.47 54.22)');
CREATE TABLE t2 (geom GEOGRAPHY);
INSERT INTO t2 VALUES ('POINT(-4.48 54.22)'),('POINT(-4.41 54.18)');
Query
WITH j AS (
SELECT geom FROM t1 WHERE id = 1 LIMIT 1)
SELECT ST_AsText(t2.geom)
FROM j,t2 WHERE ST_DWithin(t2.geom, j.geom, 1000);
st_astext
--------------------
POINT(-4.48 54.22)
(1 Zeile)
You are cross joining those tables and have PostgreSQL return the cartesian product of both when selecting myplace.* & ev.*.
So while there is only one row in myplace, its geom will be merged with every row of alt_fuel (i.e. the result set will have all columns of both tables in every possible combination of both); since the result set thus has two geometry columns, your client application likely chooses either the first, or the one called geom (as opposed to alt_fuel.geom1) to display!
I don't see that you are interested in myplace.geom in the result set anyway, so I suggest to run
WITH
myplace as (
SELECT gas.geom
FROM nj_gas gas
WHERE gas.site_id = 11949
LIMIT 1
)
SELECT ev.*
FROM alt_fuel AS ev
JOIN myplace AS mp
ON ST_DWithin(ev.geom1, mp.geom, 1000) -- ST_DWithin(ev.geom1::GEOGRAPHY, mp.geom::GEOGRAPHY, 1000)
;
If, for some reason, you also want to display myplace.geom along with the stations, you'd have to UNION[ ALL] the above with a SELECT * on myplace; note that you will also have to provide the same column list and structure (same data types!) as alt_fuel.* (or better, the other side of the UNION[ ALL]) in that SELECT!
Note the suggestions made by #JimJones about units; if your data is not projected in a meter based CRS (but in a geographic reference system; 'LonLat'), use the cast to GEOGRAPHY to have ST_DWithin consider the input as meter (and calculate using spheroidal algebra instead of planar (Euclidean))!
Resolved by using:
WITH
myplace as (
SELECT geom as g
FROM nj_gas
WHERE site_id = 11949 OR site_id = 11099 OR site_id = 11679 or site_id = 480522
), myresults AS (
SELECT ev.*
FROM alt_fuel AS ev
JOIN myplace AS mp
ON ST_DWithin(ev.geom, mp.g, 0.1))
select * from myresults```
Thanks so much for your help #ThingumaBob and #JimJones ! Greatly appreciate it.

SQL Server 2008 GEOGRAPHY STDistance() value

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).

Does this mySQL "spatial" query work in SQL Server 2008 as well?

Before I embark on a a pretty decent overhaul of my web app to use a spatial query, I'd like to know if this MySQL query works in SQL Server 2008:
SELECT id, ( 3959 * acos( cos( radians(37) ) * cos( radians( lat ) ) *
cos( radians( lng ) - radians(-122) ) + sin( radians(37) ) *
sin( radians( lat ) ) ) ) AS distance
FROM markers HAVING distance < 25
ORDER BY distance LIMIT 0 , 20;
Or is there a better way to do this in SQL Server 2008?
My database currently stores that lat/long of businesses near military bases in Japan. However, I'm querying the table to find businesses that contain the specified bases' id.
Biz table
----------------------
PK BizId bigint (auto increment)
Name
Address
Lat
Long
**FK BaseId int (from the MilBase table)**
A spatial query, based on having a center lat/long and given radius (in km) would be a better fit for the app and would open up some new possibilities.
Any help is greatly appreciated!
It looks like you're selecting the distance between two points. In SQL Server 2008, you can use the STDistance method of the geography data type. This will look something like this:
SELECT TOP 20
geography::STGeomFromText('POINT(-122.0 37.0)', 4326).STDistance(p)
FROM markers
WHERE geography::STGeomFromText('POINT(-122.0 37.0)', 4326).STDistance(p) < 25
ORDER BY geography::STGeomFromText('POINT(-122.0 37.0)', 4326).STDistance(p);
Where p would be a field of type geography instead of two separate decimal fields. You may probably also want to create a spatial index on your p field for better performance.
To use the geography data type, simply specify your field as geography in your CREATE TABLE:
CREATE TABLE markers (
id int IDENTITY (1,1),
p geography,
title varchar(100)
);
Inserting values into your markers table will now look like this:
INSERT INTO markers (id, p, title)
VALUES (
1,
geography::STGeomFromText('POINT(-122.0 37.0)', 4326),
'My Marker'
);
Where -122.0 is the longitude, and 37.0 is the latitude.
Creating a spatial index would look something like this:
CREATE SPATIAL INDEX ix_sp_markers
ON markers(p)
USING GEOGRAPHY_GRID
WITH ( GRIDS = (HIGH, HIGH, HIGH, HIGH),
CELLS_PER_OBJECT = 2,
PAD_INDEX = ON);
If you are only interested in retrieving points within 25 miles, then there is absolutely no need to use spherical or great circle math in the distance calculations... More than sufficient would be to just use the standard cartesian distance formula...
Where Square(Delta-X) + Square(Delta-Y) < 225
All you need to do is convert the difference in Latitudes and the difference in longitudes to mileages in whatever units you are using (statue miles naultical miles, whatever)
If u r using nautical miles each degree of latitude = 60 nm...
And each degree of Longitude is equal to 60 * cos(Latitude) nm
Here if both points are within 25 miles of one another, you don;t even need to worry about the difference between this factor from one point to the other...

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