Group,
I have table columns ProductID, latitude,longitude,timestampGMT and looking for a query to count how many Id's are within this bounding box or envelope.
Any helpful suggestions.
SQL Server 2008 supports GEOGRAPHY datatype.
You should store lat, lon in a single column of this datatype, create a SPATIAL index over it and use it in a query:
SELECT m.*
FROM mytable
ON coords.STDistance(#mypoint) <= #mydistance
You are looking for the "Great Circle" distance formula
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=81360
Should point you to the correct SQL algorithm
This question shouldn't be a community wiki. Switch it if you can. Either way, here is your answer.
#Quassnoi gave a great solution for SQL 2008, but you're asking for 2003, right? 2003 doesn't have the same geography support as 2008. You'll have to roll your own as I did. It's not hard, depending on the level of accuracy that you require. Here's a scalar-function that I came up with based on a popular formula for calculating distance between two sets of coordinates:
-- =====================================================================
-- Author: Byron Sommardahl
-- Create date: June 15, 2007
-- Description: Calculates the distance between two sets of coordinates.
-- ======================================================================
CREATE FUNCTION [dbo].[Distance]
(
#lat1 float,
#long1 float,
#lat2 float,
#long2 float
)
RETURNS float
AS
BEGIN
RETURN (3958*3.1415926*sqrt((#lat2-#lat1)*(#lat2-#lat1) + cos(#lat2/57.29578)*cos(#lat1/57.29578)*(#long2-#long1)*(#long2-#long1))/180);
END
GO
To use this, just feed in your coords using a SELECT:
SELECT dbo.Distance(MyPlace.Lat, MyPlace.Long, TheirPlace.Lat, TheirPlace.Long);
Then, you could just check whether a product is within a certain radius of your focal point. Not exactly bounding box, but it gets you moving in the right direction.
You may also want to take a look at this:
SQL Server ZipCode Latitude Longitude Proximity Distance Search
Related
I have tables in SQL Server management studio containing the location of individuals by date/time along several months. The tables have the following fields: AnimalID, Date/Time, Lat, Long, Global ID. I am trying to calculate and return the distance between each pair of points in order of its movement without manually entering in the lat and long each time.There are many posts here about calculating distance between two points but I'm trying to run a query that will calculate the distance between each pair in consecutive order. Some of my tables have hundreds of locations.
My values might look like:
`MD001 10/9/2019 1:00:00PM 40.73995 -111.8739
MD001 10/9/2019 6:00:00PM 40.75068 -111.8782
MD001 10/9/2019 10:00:00PM 40.74900 -111.89100`
I want to know the distance between 1:00PM and 6:00PM and then from 6:00PM and 10:00PM, and so forth. I want to accomplish this in SQL Server so that I can query out outliers in the data. Your insight is much appreciated. I also do not want to create a new field in this table.
The algorithm to calculate the distance between points is called Harvesine Formula
To calculate the distance between 2 points in SQL Server you have 2 options:
POINT 1 = 151.209030,-33.877814
POINT 2 = 144.971431, -37.808694
Option 1. You can do your own implementation of the harvesine formula:
select
2 * 6371 * asin(sqrt(POWER((sin(radians((-37.808694 - -33.877814) / 2))),2) + cos(radians(-33.877814)) * cos(radians(-37.808694)) * POWER((sin(radians((144.971431 - 151.209030) / 2))),2)))
Note this will give you the distance in kilometer. This is defined by the multiplier 6371. To get the distance in miles replace 6371 by 3959
If you do a search on the harvesine formula + sql you can find more in depth details about this implementation.
Option 2.. Use SQL Server built-in functions.
In order to do that you'll need to convert your lat and long columns to geography datatype and then use the STDistance function to calculate the actual distance.
The statement below should give you an idea to get started:
select
cast('POINT(151.209030 -33.877814)' as geography).STDistance(cast('POINT(144.971431 -37.808694)' as geography)) as distance_in_meters,
cast('POINT(151.209030 -33.877814)' as geography).STDistance(cast('POINT(144.971431 -37.808694)' as geography)) / 1000 as distance_in_km
The default result will be in meters.
Note there's a slight difference between these 2 options when they are applied to the same coordinates. So if you need precision then you might want to do some further investigation on why that is.
I have a list of 13000 places (with latitude and longitude) --- in table : place.
I have a list of 22000 polygons ---- in another table called place_polygon.
I need to try and resolve the pois to the polygons that they belong to.
This is the query that I wrote :
select * from stg_place.place a
left join stg_place.place_polygon b on
ST_Within(ST_GeomFromText('SRID=4326;POINT('||a.longitude||' '||a.latitude||')'),b.geom);
also tried :
select * from stg_place.place a
left join stg_place.place_polygon b on
ST_Intersects(ST_GeomFromText('SRID=4326;POINT('||a.longitude||' '||a.latitude||')'),b.geom);
It's running forever.
But, if I put a filter in the query, then it runs very fast for a single record.
select * from stg_place.place a
left join stg_place.place_polygon b on
ST_Within(ST_GeomFromText('SRID=4326;POINT('||a.longitude||' '||a.latitude||')'),b.geom)
where a.id = <id>;
I also tried writing a stored procedure and tried to loop through a cursor to only do for one record at a time. That also didn't help. The program ran overnight with no signs of ending.
Is there a better way to solve this? (not necessarily in postgis, but in python geopy etc... ? )
(Should I consider indexing the tables?)
First of all use geography type for your data instead of lat long columns. Why geography, not geometry? Because you use SRID=4326 and with geography type, it will be much easier if you want for example calculate distance in meters then with geometry type which will calculate in degrees for this SRID.
To create geography with your lat long column use function st_setsrid(st_makepoint(long,lat),4326)::geography
Ok. Now answering your question on your actual structure
I have a list of 13000 places (with latitude and longitude) --- in table : place. I have a list of 22000 polygons ---- in another table called place_polygon. I need to try and resolve the pois to the polygons that they belong to.
This is the query that I wrote :
select *
from stg_place.place a
left join stg_place.place_polygon b on
ST_DWithin (st_setsrid(st_make_point(long,lat),4326),b.geom,0);
I used ST_DWithin() instead of ST_Within() because on an older version of Postgres+PostGIS (for sure 9.6 and below) it guarantees of using a spatial index on geoms if created.
I don't think something is right want max and min lat long of the 100 meter buffer.
SELECT ST_Asgeojson(
ST_Expand(
ST_GeomFromEWKT('SRID=4326;POINT(-88.33 36.33)')
,100
)
);
Think it is calculating by 100 degrees of lat and long.
This gis.se question shows what I'm wanting to do. Answer 5 graphically depicts what I'm wanting to do.
I'm trying to get the
coordinates: [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]`
to add it to the map.
Yes, of course... As in documentation of ST_Expand
Units are in the units of the spatial reference system in use denoted
by the SRID.
EPSG:4326 is in degrees so it's exanding by 100 degrees.
I suggest you use ST_Buffer function with geography datatype:
SELECT ST_Asgeojson(ST_Buffer((ST_GeomFromEWKT('SRID=4326;POINT(-88.33 36.33)'))::geography,100));
And if you need rectangle you have to add ST_Envelope with geography::geometry
SELECT ST_Asgeojson(ST_Envelope(ST_Buffer((ST_GeomFromEWKT('SRID=4326;POINT(-88.33 36.33)'))::geography,100)::geometry));
If you need to use this in select from table you might need to reproject your geometry because cast ::geography needs a LatLon CRS, so it'll look like this:
SELECT ST_Asgeojson(ST_Envelope(ST_Buffer((ST_Transform(x.the_geom,4326)::geography,100)::geometry));
I am working on a web app with Google Map that I’d like to display a “coverage area”/’ geographical area” by creating a polygon overlay of a given set of geo coordinates/points.
The “coverage area” can consist of thousands of the geo coordinates (Longitude and Latitude data stored in a table in sql server). Ideally, I’d like to calculate the Convex Hull points from the sql server database (2008 R2) so I can pass the results (points) to the Google Map to create the polygon overlay.
The sample here (http://www.geocodezip.com/v3_map-markers_ConvexHull.asp) is exactly what I am looking for, except that I’d like to get the hull points on the right-panel straight from the SQL server if possible. The reason is that I may have to process thousands of the geo coordinates. I’d rather not to retrieve a huge amount of data from the database and then send to the client using JavaScript to calculate the convex hull points.
Any help will be very much appreciated!!!
Thank you.
You didn't mention what version you're on, but there's a built in ConvexHullAggregate starting in SQL 2012 that should do exactly what you're looking for.
Here's an extension of the example in the linked to documentation that gets the coordinates of the corners of the convex hull. It assumes that you have a table of numbers laying around (a pretty useful thing in my experience).
with cte as (
SELECT City, geography::ConvexHullAggregate(SpatialLocation) AS Hull
FROM Person.Address
WHERE City in ('Ottawa', 'Burnaby')
group by City
)
select City, Number, Edge.Long as Long, Edge.Lat as Lat
from cte
cross apply (
select Number, Hull.STPointN(Number) as Edge
from dbadmin.dbo.Numbers
where Number < Hull.STNumPoints()
) as HullEdges
I created a very simple polygon in the middle of Germany to demonstrate my problem.
You can visualize it in geojsonlint using the following GeoJSON
{"type":"Polygon","coordinates":[[
[10.439844131469727,51.17460781257472],
[10.430574417114258,51.1753073564544],
[10.429565906524658,51.17179607723465],
[10.438792705535889,51.170706315523866],
[10.439372062683105,51.17267055874809],
[10.43975830078125,51.17439256616884],
[10.439844131469727,51.17460781257472]]]G}
When calculating the surface with online tools (e.g. http://www.daftlogic.com/projects-google-maps-area-calculator-tool.htm, but I tried several),
I get the following numbers (these are based on a similar drawing of the polygon, but not the exact same one, as I couldn't copy it over to these tools):
276583.39 m²
0.28 km²
68.35 acres
27.66 hectares
2977118.86 feet²
0.08 square nautical miles
Now I want to calculate these areas using POSTGIS, but I always get wrong and not matching numbers.
First I started without transformation using the examples given here:
http://postgis.net/docs/ST_Area.html
SELECT ST_Area(the_geom) As sqft, ST_Area(the_geom)*POWER(0.3048,2) As sqm
FROM (SELECT ST_GeomFromText('
POLYGON ((51.17460781257472 10.439844131469727,
51.1753073564544 10.430574417114258,
51.17179607723465 10.429565906524658,
51.170706315523866 10.438792705535889,
51.17267055874809 0.439372062683105,
51.17439256616884 10.43975830078125,
51.17460781257472 10.439844131469727))',4326) ) As foo(the_geom);
--> sqft = 3.52643124351653e-05 and sqm = 3.27616182873666e-06
How can I interprete these numbers?
Then I tried to transform it to WGS 84 / UTM zone 33N 32633
SELECT ST_Area(the_geom) As sqft, ST_Area(the_geom)*POWER(0.3048,2) As sqm
FROM (SELECT ST_Transform(ST_GeomFromText('
POLYGON ((51.174661624019286 10.440187454223633,
51.17067940750161 10.438899993896484,
51.17197097486416 10.429544448852539,
51.17536116708255 10.430488586425781,
51.174661624019286 10.440187454223633))',4326),32633) ) As foo(the_geom);
--> sqft = 662918.939349234 and sqm = 61587.1847391195
But even these numbers don't come close.
The coordinates of the polygon were accidentally loaded as lat,lon instead of lon, lat.
http://postgis.net/2013/08/18/tip_lon_lat
says
In spatial databases spatial coordinates are in x = longitude, and y = latitude
I converted the coordinates into EPSG: 31467, see epsg:31467 which is projected to meters and applies to the area of Germany covered by your geometry.
select st_area(st_transform(st_setsrid(st_geomfromtext('POLYGON((10.439844131469727
51.17460781257472,10.430574417114258 51.1753073564544,10.429565906524658
51.17179607723465,10.438792705535889 51.170706315523866, 10.439372062683105
51.17267055874809, 10.43975830078125 51.17439256616884, 10.439844131469727
51.17460781257472))'),4326),31467));
and got the answer: 274442.27 m2 which is within 0.007% of your original answer.
Measurements are usually more accurate in projected coordinate systems that use a geoid appropriate to that region. If you run this query on the spatial reference system table in Postgis for that projection:
select * from spatial_ref_sys where srid=31467;
you will see some more details, such as the fact that it uses the Bessel 1841 spheroid.
EDIT: your original geojson has coordinates in x/y, but for some reason you flipped them when putting them into Postgis.