POSTGRESQL - Polygon function for geolocations - sql

I have 2 tables User_places where I can see for each user their home location defined by 2 separate attributes: longitude and latitude.
And I have second table Neighborhoods with attribute 'Area', that defines each neighborhood as polygon - jsonb format - "[{"latitude":XXXXX,"longitude":YYYYY},{"latitude":ZZZZZ,"longitude":AAAAA},{"latitude":BBBBBB,"longitude":CCCCC},{"latitude":DDDDD,"longitude":EEEEE}]".
Does anybody know how to check whether particular user lives in given neighborhood in Postgresql?

SELECT
user_id,
ST_Contains(
ST_GeomFromText(
(select
replace(replace(replace(replace(replace(area::text,']','))'),'[','POLYGON(('),'}', ''),',"longitude":',' '),'{"latitude":','')
from neighbourhoods
limit 1), 4326)
(ST_SetSRID(ST_MakePoint(latitude, longitude),4326))
) as polygon_check
from user_places

I will assume you have Postgis installed on your copy of postgresql? As this has all the Geometry and geographical function extensions to postgresql.
You could use ST_Contains to check if a point is contained in a given polygon, this returns a boolean.
select ST_Contains(neigbourhood.polygon,User_place.geom)
If you don't have a geom point value in the User_places table then this can be created using the ST_SetSRID function and ST_MakePoint function.
For example - SELECT ST_SetSRID(ST_MakePoint(LAT, LON),SRID);
where SRID is the reference for the projection you are using for example 4326 for WGS 84 `
I would recommend in postgresql make sure all of your Geographical tables have geoms in them, as it makes comparisons much easier.
For reference a good place to get answers for geographic and geometry database based questions is https://gis.stackexchange.com/

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

Find a POINT( , ) within a Polygon BigQuery SQL

I have two tables, one is a neighborhood table that lists the name of the neighborhood as well as neighborhood polygon with the format GEOGRAPHY.
In my second table I have the location data of a number of trees, including lattitude and longitude as FLOAT64 data types - I can convert these to a geography point using ST_GEOGPOINT.
What I am looking to do, is use the Lat and long from the trees table, and write a query that will return the name of the neighborhood of the cooresponding polygon that the point falls within
Any help is very appreciated
I found the solution via a join!
SELECT
t.tree_id,
t.species,
t.address,
t.plant_type,
st_geogpoint(longitude, latitude) as point,
nh.neighborhood
FROM `bigquery-public-data.san_francisco_trees.street_trees` AS t
JOIN `bigquery-public-data.san_francisco_neighborhoods.boundaries` AS nh
ON ST_WITHIN(st_geogpoint(t.longitude, t.latitude), nh.neighborhood_geom)
where longitude IS NOT NULL and latitude IS NOT NULL

How to find distance between two points using latitude and longitude

I have a ROUTES table which has columns SOURCE_AIRPORT and DESTINATION_AIRPORT and describes a particular route that an airplane would take to get from one to the other.
I have an AIRPORTS table which has columns LATITUDE and LONGITUDE which describes an airports geographic position.
I can join the two tables using columns which they both share called SOURCE_AIRPORT_ID and DESTINATION_AIRPORT_ID in the routes table, and called IATA in the airports table (a 3 letter code to represent an airport such as LHR for London Heathrow).
My question is, how can I write an SQL query using all of this information to find, for example, the longest route out of a particular airport such as LHR?
I believe I have to join the two tables, and for every row in the routes table where the source airport is LHR, look at the destination airport's latitude and longitude, calculate how far away that is from LHR, save that as a field called "distance", and then order the data by the highest distance first. But in terms of SQL syntax i'm at a loss.
This would have probably been a better question for the Mathematics Stack Exchange, but I’ll provide some insight here. If you are relatively farmiliar with trigonometry, I’m sure you could understand the implementation given this resource: https://en.m.wikipedia.org/wiki/Haversine_formula. You are looking to compute the distance between two point on the surface of a sphere in terms of their distance across its surface (not a straight line, you can’t travel through the Earth).
The page displays this formula:
https://wikimedia.org/api/rest_v1/media/math/render/svg/a65dbbde43ff45bacd2505fcf32b44fc7dcd8cc0
Where
• φ1, φ2 are the latitude of point 1 and latitude of point 2 (in radians),
• λ1, λ2 are the longitude of point 1 and longitude of point 2 (in radians).

If you data is in degrees, you can simply convert to radians by multiplying by pi/180
There is a formula called great circle distance to calculate distance between two points. You probably can load is as a library for your operating system. Forget the haversine, our planet is not a perfect sphere.
If you use this value often, save it in your routes table.
I think you're about 90% there in terms of the solution method. I'll add in some additional detail regarding a potential SQL query to get your answer. So there's 2 steps you need to do to calculate the distances - step 1 is to create a table containing joining the ROUTES table to the AIRPORTS table to get the latitude/longitude for both the SOURCE_AIRPORT and DESTINATION_AIRPORT on the route. This might look something like this:
SELECT t1.*, CONVERT(FLOAT, t2.LATITUDE) AS SOURCE_LAT, CONVERT(FLOAT, t2.LONGITUDE) AS SOURCE_LONG, CONVERT(FLOAT, t3.LATITUDE) AS DEST_LAT, CONVERT(FLOAT, t3.LONGITUDE) AS DEST_LONG, 0.00 AS DISTANCE_CALC
INTO ROUTE_CALCULATIONS
FROM ROUTES t1 LEFT OUTER JOIN AIRPORTS t2 ON t1.SOURCE_AIRPORT_ID = t2.IATA
LEFT OUTER JOIN AIRPORTS t3 ON t1.DESTINATION_AIRPORT_ID = t3.IATA;
The resulting output should create a new table titled ROUTE_CALCULATIONS made up of all the ROUTES columns, the longitude/latitude for both the SOURCE and DESTINATION airports, and a placeholder DISTANCE_CALC column with a value of 0.
Step 2 is calculating the distance. This should be a relatively straightforward calculation and update.
UPDATE ROUTE_CALCULATIONS
SET DISTANCE_CALC = 2 * 3961 * asin(sqrt((sin(radians((DEST_LAT- SOURCE_LAT) / 2))) ^ 2 + cos(radians(SOURCE_LAT)) * cos(radians(DEST_LAT)) * (sin(radians((DEST_LONG- SOURCE_LONG) / 2))) ^ 2))
And that should give the calculated distance in the DISTANCE_CALC table for all routes seen in the data. From there you should be able to do whatever distance-related route analysis you want.

How can I return all the rows in a PostgreSQL/PostGIS table within a radius of Xkm provided by a longitude and latitude value?

I'm trying to have a go at learning about PostgreSQL and in particular, it's PostGIS extension and the benefits with regards to geographic spatial features it provides. I've loaded a PostgreSQL DB with a table that contains 30,000 records of latitude, longitude and a price value (for houses) and I want to start querying the DB to return all the rows that would be in a radius of Xkm of a particular latitude and longitude.
I've hit a brick wall as to how I might run this type of query as I've found the documentation to be quite limited online and I've found no similar attempts at this method of querying online.
Some methods I've tried:
SELECT *
FROM house_prices
WHERE ST_DWithin( ST_MakePoint(53.3348279,-6.269547099999954)) <= radius_mi *
1609.34;
This prompts the following error:
ERROR: function st_dwithin(geometry) does not exist
Another attempt:
SELECT * FROM house_prices ST_DWithin( 53.3348279, -6.269547099999954, 5); <-- A latitude value, longitude value and 5 miles radius
This prompts the following error:
ERROR: syntax error at or near "53.3348279"
Could anyone point me in the right direction/ know of some documentation I could look at?
** Edit **
Structure and set up of database and table in pgAdmin4
The first query has an invalid number of parameters. The function ST_DWithin expects at least two geometries and the srid distance,
and optionally a Boolean parameter indicating the usage of a spheroid (see documentation).
The second query is missing a WHERE clause and has the same problem as the first query.
Example from documentation:
SELECT s.gid, s.school_name
FROM schools s
LEFT JOIN hospitals h ON ST_DWithin(s.the_geom, h.the_geom, 3000)
WHERE h.gid IS NULL;
Perhaps something like this would be what you want to achieve:
SELECT *
FROM house_prices h
WHERE ST_DWithin(ST_MakePoint(53.3348,-6.2695),h.geom,h.radius_mi * 1609.34)
Also pay attention to the order of the coordinates pair (x,y or y,x), otherwise you might easily land on the sea with these coordinates ;-)
EDIT: Taking into account that there is no geometry on the table, so the points are stored in two different columns, longitude and latitude:
SELECT *
FROM house_prices
WHERE ST_DWithin(ST_MakePoint(longitude,latitude),ST_MakePoint(53.3348,-6.2695),1609.34)

Spatial SQL query showing parcels containing centroid of building

I am trying to write a query that selects parcels that contain the centroid of a certain building code (bldg_code = 3).
The parcels are listed in the table "city.zoning" and contains a column for a PIN, geometry, and area of each parcel. The table "buildings" contains a column for bldg_type and bldg_code indicating the building type and its corresponding code. The building type of interest for this query has a bldg_code of 3.
So far I've developed a query that shows parcels that interact with the building type of interest:
select a.*
from city.zoning a, username.buildings b
where b.bldg_code = 3 and sdo_anyinteract(a.geom,b.geom) = 'TRUE';
Any ideas?
You can use SDO_GEOM.SDO_CENTROID (documentation) to find the centroid of a geometry.
Note that the centroid provided by this function is the mathematical centroid only and may not always lie inside the geometry, for example, if your polygon is L shaped. SpatialDB Adviser has a good article on this, but here's a quick illustration:
If this isn't a problem for you and you don't need that level of accuracy, just use the built-in, but if you do consider this to be a problem (as I did in the past), then SpatialDB Adviser has a standalone PL/SQL package that corrrectly calculates centroids.
Depending on your performance needs, you could calculate the centroids on-the-fly and just use them in your query directly, or alternatively, add a centroid column to the table and compute and cache the values with application code (best case) or trigger (worst case).
Your query would look something like this:
SELECT a.*
FROM city.zoning a
JOIN username.buildings b ON sdo_contains(a.geom, b.centroid) = 'TRUE'
WHERE b.bldg_code = 3
Note that this is using SDO_CONTAINS on the basis of the a.geom column being spatially indexed and a new column b.centroid that has been added and populated (note - query not tested). If the zoning geometry is not spatially indexed, then you would need to use SDO_GEOM.RELATE, or index the centroid column and invert the logic to use SDO_INSIDE.