Calculate Convex Hull of points from a set of (lat/long) points - sql-server

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

Related

Retrieving raster data by geographic location using Landsat and PostGIS

The project I am working on requires that I retrieve Landsat raster data at specific geographic (lon/lat) locations. After sifting through some tutorials and experimenting with GDAL, PostGIS, and QGIS, I successfully imported a GeoTIFF Landsat image into a PostGIS raster table and accessed values by geographic location from that table. However, there were a few issues in the result:
I do not understand the coordinate system being used by QGIS in its interface, as they range in the hundred thousands
The raster loaded into QGIS off the coast of Spain, rather than on top of Maine, USA as it was supposed to.
Here's some information about my process. I am fairly new to GIS in general, so I am almost certain theres a blatant error to be found here:
Download Landsat 8 GeoTIFF file from USGS GloVis
Rename the band 5 image to something more friendly to command ninja with.
Create postgres database for raster tables and run CREATE EXTENSION postgis;
Run gdalinfo LSSampleB5.TIF, printing the following output:
Driver: GTiff/GeoTIFF
Files: LSSampleB5Test2.TIF
Size is 7871, 7971
Coordinate System is:
PROJCS["WGS 84 / UTM zone 19N",
GEOGCS["WGS 84",
DATUM["WGS_1984",
SPHEROID["WGS 84",6378137,298.257223563,
AUTHORITY["EPSG","7030"]],
AUTHORITY["EPSG","6326"]],
PRIMEM["Greenwich",0,
AUTHORITY["EPSG","8901"]],
UNIT["degree",0.0174532925199433,
AUTHORITY["EPSG","9122"]],
AUTHORITY["EPSG","4326"]],
PROJECTION["Transverse_Mercator"],
PARAMETER["latitude_of_origin",0],
PARAMETER["central_meridian",-69],
PARAMETER["scale_factor",0.9996],
PARAMETER["false_easting",500000],
PARAMETER["false_northing",0],
UNIT["metre",1,
AUTHORITY["EPSG","9001"]],
AXIS["Easting",EAST],
AXIS["Northing",NORTH],
AUTHORITY["EPSG","32619"]]
Origin = (318285.000000000000000,5216715.000000000000000)
Pixel Size = (30.000000000000000,-30.000000000000000)
Metadata:
AREA_OR_POINT=Point
Image Structure Metadata:
INTERLEAVE=BAND
Corner Coordinates:
Upper Left ( 318285.000, 5216715.000) ( 71d23'37.53"W, 47d 4'44.12"N)
Lower Left ( 318285.000, 4977585.000) ( 71d18' 9.77"W, 44d55'42.53"N)
Upper Right ( 554415.000, 5216715.000) ( 68d16'58.41"W, 47d 6' 6.11"N)
Lower Right ( 554415.000, 4977585.000) ( 68d18'36.69"W, 44d56'58.62"N)
Center ( 436350.000, 5097150.000) ( 69d49'20.56"W, 46d 1'29.87"N)
Band 1 Block=7871x1 Type=UInt16, ColorInterp=Gray
I interpretted this output as EPSG 4326 format (which may be my crime), so I ran the following command to import the GeoTIFF as a PostGIS raster:
raster2pgsql -s 4326 -I LSSampleB5.TIF -F -t 50x50 -d | psql -U postgres rastertest
This successfully imported a new table. I then used QGIS to get a visual intuition of what was going on.
Under Database -> DB Manager -> PostGIS -> rastertest -> public I added my lssampleb5 to the canvas.
I created a new XYZ Connection in QGIS to add Google satillite hybrid images for reference. The url I used was https://mt1.google.com/vt/lyrs=y&x={x}&y={y}&z={z} with min and max zoom of 0 and 19 respectively.
Here is where I took note of the fact that the lssample layer landed off the coast of Spain on the Google Hybrid map.
I made sure both layers were on EPSG 4326 projection, no change.
Not too discouraged to move on, I tried a database query to get a single pixel value. Since my sample data landed near Spain, I used QGIS to sample a valid coordinate pair near there for the query. The query was:
SELECT rid, ST_Value(rast, 1, ST_SetSRID(ST_Point(448956,5041439), 4326)) as b5
FROM lssampleb5
WHERE ST_Intersects(rast, ST_SetSRID(ST_Point(448956,5041439), 4326)::geometry, 1);
This returned a valid row ID and an ST_VALUE of 5776. Trying coordinates outside the range displayed by QGIS resulted in no returned entries, which isn't unexpected.
So, first of all, I do not know what QGIS is using for its coordinate system. It's definitely not longitude and latitude in a raw form, but from my understanding, EPSG 4326 is supposed to be a geographic projection.
Second, I don't know why QGIS is misplacing the Landsat scene in the wrong place, or where in the process the scene was not transformed properly.
Join us at GIS SE, that´s the place for GIS related Q/A!
To help you out here:
Indeed, your crime was the CRS. The top level PROJCRS tag is the
key here, it reads out "WGS 84 / UTM zone 19N" from the data, with
the EPSG reference at the bottom (AUTHORITY["EPSG","32619"]]).EPSG:32619 is a UTM projected CRS based on the WGS84 geoid (datum) and with units in meter, defined as the projected distance to the corresponding reference meridians (Easting) and the equator (Northing). Since you defined the wrong CRS during import (i.e. EPSG:4326), the inherent coordinate values of the raster were treated as degree´s and the whole thing placed to the other end of the world. Run UpdateRasterSRID (SELECT UpdateRasterSRID(<shema_name>, <your_raster_table>, rast, 32619);) to set the raster's metadata to the correct CRS and reload the layer.
As for QGIS: it uses the CRS that you tell it to use. QGIS comes with a very handy on-the-fly reprojection feature (OTF, check out the general man page on 'working with projections' here) that lets you define an arbitrary CRS for your data to be projected and displayed (i.e. it reprojects the data's CRS into the defined one in memory, the data's metadata stays untouched).You can find the quick link button to the OTF settings in the bottom right corner of the GUI; set it to your desired SRID (e.g. 4326) (you´ll notice how the visual representation of your data changes according to the chosen projection. also the displayed coordinates will use the CRS units, e.g. decimal degrees for WGS84).

Ride Sharing App - finding surrounding origins and destinations

I looked at several other SO questions that seem somewhat related, but not quite what i need (or i'm just not smart enough to connect the dots).
Working on an app for a client. Their database holds the origin and destination of people that are traveling, limited (i believe) to just places in US and Canada, and a date when the trip will take place. The records are updated regularly. Call these "trips."
Users come to the site, and enter an origin and destination city, and a radius for each, indicating how far away from their desired origin/destination cities they are willing to travel in order to make their trip.
The job of the app is to find any/all trips that are already in the database, that are closest to the origin and destination that the user needs to travel.
My original thought was to find all origin cities in the database that are within the radius of the user's desired origin, then use that recordset to search the destination cities in the database for any/all cities within the radius of the user's desired destination.
I also need a decent (preferably free... low budget project here) API that can help look up the city geographic location and perform the actual radius calculation... I think.
Is what I'm looking to do even close to the best options? It looks like the hardest part will be finding all the existing cities in the database that are within the radius of the user's desired cities - which is a bit of a twist on a more simple query of just "find all cities in the radius of X city".
So, this is KINDA like an Uber situation, except the Uber driver is deciding what the trip parameters are, and the user just needs to know which Uber drivers are going from/to the places nearest those of the user (on the specified date, to boot).
Right now, users are just looking things up at a state level - BC to NY, and reading down rows of data looking at rides to find the ones that seem closest to what they need.
Thanks in advance, for any clever insights you smart folks might have!
Declare #DriverLat float = 41.744068
Declare #DriverLng float = -71.315024
Declare #Within int = 20
Select *
From (
Select Distinct
A.ZipCode
,A.CityName
,A.StateCode
,Miles = [dbo].[udf-Geo-Calc-Miles] (#DriverLat,#DriverLng,A.Lat,A.Lng)
From [dbo].[ZipCodes] A
Where CityType = 'D'
and ZipType = 'S'
) A
Where Miles <= #Within
Order By Miles
Returns
The UDF
CREATE Function [dbo].[udf-geo-Calc-Miles] (#Lat1 float,#Lng1 float,#Lat2 Float,#Lng2 float)
Returns Float as
Begin
Declare #Miles Float = (Sin(Radians(#Lat1)) * Sin(Radians(#Lat2))) + (Cos(Radians(#Lat1)) * Cos(Radians(#Lat2)) * Cos(Radians(#Lng2) - Radians(#Lng1)))
Return Case When #Miles is null then 0 else abs((3958.75 * Atan(Sqrt(1 - power(#Miles, 2)) / #Miles))) end
End

How can we improve the speed of the function st_worldtorastercoordx(rast,geom)

1 Background
As the wireless communication technology is developing faster and faster, the Long Term Evolution networks are widely installed all over the word. Since the Long Term Evolution network is based on wide-band radio access technology, there is only one radio carrier available for most of the operator. Since the frequency resource is limited, many telecom operators use same frequency TD-LTE networks. In the same frequency TD-LTE networks, overlapping coverage seriously impact download speed. RF optimization is a conventional method of controlling overlapping coverage. However, the network optimization resources are always limited, we must evaluate the wireless network coverage and quality as a whole, and identify the problems in priority.
Drive test data is widely used to evaluate the wireless network coverage and quality. And we will future associate the Drive test data with the map. Traditionally, we use a desktop software MapInfo to analysis the Drive test data point with the street and road, which is a very heavy labor task. Recently, we find a new approach the PostGIS middleware, we could transfer the city maps into Raster data, we can we could future associate the Drive test data point with the city map raster data , just like we associate the Drive test data point with the polygon object.
In this article, we will describe how to associate the map points to Raster. and my question is how can we improve the speed if the function st_worldtorastercoordx(rast,geom) while there are more than 10000*10000 pixels in the raster object. This question is associated with the middle-ware PostGIS Raster object.
2 Creat Drive-test data point object
2.1 creat point object field
In previous object, we use the kettle middle ware to up-load the Drive-test data file into the Postgres-SQL database. In the database, the destination table is atu_sample, now we will update the table to add a geometry object “point “.
Here comes the command:
alter table atu_sample add column geom geometry(point,4326)
2.2 assign value to point
In the Drive-test data file, we have the longtitude and latitude to represent the spatial position, now we will convert them to a geometry object “point “
Here comes the command:
update atu_sample set geom =
st_geomfromtext('POINT('||longtitude||' '||latitude||')', 4326);
3 Creat Raster data
In order to associate the point object to Raster data, we must first set up a Raster Data. In this article, we will convert the city map which is a vector data into a Raster data.
3.1 Import Wuhan city Boundary polygon
3.1.1 Creat shape file
In this project, we will import the Wuhan city Boundary polygon into the PostGIS database, and Wuhan city is the capital city of Hubei Province, China. It depends on the MAPinfo format transfer function,the GUI entry can be seen in the following diagram:
From the picture we can see, the format of the target file is the ESRI Shape file , which is the default format which the PostGIS could support.
3.1.2 Creat PG scripts
Then we will transfer the Wuhan city map which is in the ESRI shape file into the PostGIS scripts. It depends on the tool shp2pgsql,which is a plug-in component of PostGIS.
Here comes the command :
shp2pgsql –W “GBK” -s 4326 wh_region.shp > 123.txt
here -s 4326 represent the coordinate system parameter, which will be valuable in future spatial object。
3.1.3 Script execution
Now, we will open the PGADMIN, and transfer the Script into the PostGIS database finally we get a talbe with the name wuhan。When the script is executed, we have the spatial data table in the postGIS。
3.2 Measuring the height and width of Wuhan boundary
3.2.1 Drawing ConvexHull of Wuhan city
enter image description here
Figure 2-4: ConvexHull of Wuhan city
In the figure 2-4, we draw the ConvexHull of Wuhan city, which will surround the Wuhan City boundary.
3.2.2 To measure height and width via ruler使用ruler工具
In the Mapinfo, we have a tool with the name Ruler, which can be used to measure the height and width of the ConvexHull of Wuhan city.
enter image description here
Figure 2-5 to measure height and width
Finally we get the data:132.7KM and 155.5KM.
3.3 Creat the Wuhan Raster data
In our project, we will creat the pixel of 100*100 meters. Therefore we will first calculate the parameter to divide the height and width of the ConvexHull of Wuhan city. According to the data which we have measured previously, the height 132.7KM and width 155.5KM, therefore we could calculate the equally divide parameter are 1327 and 1555.
3.3.1 Creat Raster Persistence table
Now we will creat a table which could storage the Raster data. Here comes the SQL command:
CREATE TABLE myrasters
(
rid integer NOT NULL,
rast raster,
CONSTRAINT myrasters_pkey PRIMARY KEY (rid)
);
3.3.2 Insert RASTER record
In the previous steps, we have the geomtry object form the table wh_region and the qually divide parameter, we could creat the raster instance with the function ST_AsRaster.
Here comes the command:
insert into myrasters SELECT gid,ST_AsRaster( geom ,1327, 1555, '8BUI') from wh_region;
4 Associa te the point to Raster
4.1 Add Drive test point RASTER coordinate data field
We will find out the relation of the point and Raster, that is to say we should find out which pixel a certain point is located in. in order to record this relation we first add two data field x integer and y integer to the table atu_sample.
Here comes the command:
alter table atu_sample add column x integer;
alter table atu_sample add column y integer;
4.2 Raster mapping
With the function st_worldtorastercoordx(rast,geom) and st_worldtorastercoordy(rast,geom), we could easily find out which pixel a certain point is located in and get the pixel coordinate data. Then we will update the tabe
Here comes the command:
update atu_sample set x =st_worldtorastercoordx(rast,geom) from myrasters;
update atu_sample set y =st_worldtorastercoordy(rast,geom) from myrasters;
Geom:
4.3 Performance problem of Raster mapping
In the above example , we have 12000 points and a 1327* 1555 raster data . it takes more than 160 Seconds to finish the task. In the future project, we will have more than 1200000 points , how could we fix the Performance problem?
For 3.3 please refer to this article. You don't have to measure the dimensions of your area to convert it to raster. Make sure you produce a tiled raster coverage with quite small tiles.
For 4: You don't have to compute the position of your points into the raster. Create points with ST_MakePoint() from your table of coordinates and make sure they are in the proper coordinate system. Then create a new table with the points projected into the same coordinate system as the raster table with ST_Transform() and index this table. Then query the pixel values with a query like this:
SELECT ST_Value(rast, geom) val
FROM rastertable, geomtable
WHERE ST_Intersects(rast, geom)

I tried all ways, but still my area is calculated wrongly in Postgis

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.

2D Binding Box Query

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

Resources