I have a db structure that is vanilla Postgres:
CREATE TABLE IF NOT EXISTS locations (
name text NOT NULL,
lat double precision NOT NULL,
lng double precision NOT NULL,
);
CREATE INDEX ON locations(lat,lng);
When I want to calculate all locations in a bounding box where I have the lower left and upper right corners I use the following query:
SELECT * FROM locations
WHERE lat >= min_lat AND
WHERE lat <= max_lat AND
WHERE lng >= min_lng AND
WHERE lng <= max_lng;
Now, I want to generate a bounding box given a point and use the bounding box result in the locations query. I'm using the following PostGIS query to generate a bounding box:
SELECT
ST_Extent(
ST_Envelope(
ST_Rotate(
ST_Buffer(
ST_GeomFromText('POINT (-87.6297982 41.8781136)',4326)::GEOGRAPHY,160934)::GEOMETRY,0)));
Result: BOX(-89.568160053866 40.4285062983089,-85.6903925527536 43.3273499289221)
However, I'm not sure how use the results from the PostGIS query bounding box into the vanilla lat / lng Postgres query in one call. Any ideas on how to merge the two? Preferably such that the index is preserved.
If you want to get the bbox coordinates as separated values you might wanna take a look at ST_XMax, ST_YMax, ST_XMin, ST_YMin. The following CTE, that embeds your query, should give you an idea:
WITH j (geom) AS (
SELECT
ST_Extent(ST_Envelope(
ST_Rotate(ST_Buffer(
ST_GeomFromText('POINT(-87.6297982 41.8781136)',4326)::GEOGRAPHY,160934)::GEOMETRY,0)))
)
SELECT
ST_XMax(geom),ST_YMax(geom),
ST_XMin(geom),ST_YMin(geom)
FROM j
st_xmax | st_ymax | st_xmin | st_ymin
-------------------+-----------------+-------------------+------------------
-85.6903925527536 | 43.327349928921 | -89.5681600538661 | 40.4285062983098
Side note: Storing geometry values as numbers might look straightforward but it is hardly ever the better choice - specially when dealing with polygons! So I would really suggest you to store these values as geometry or geography, which might seem complex at first a glance but definitely pays off on the long run.
This answer might shed a light on distance/containment queries involving polygons: Getting all Buildings in range of 5 miles from specified coordinates
Related
I have a Postgres table with some data created by using a shapefile. I need to find all records which intersect within 2 miles radius from a given latitude and longitude. I used some queries including the following one.
SELECT * FROM us_census_zcta WHERE ST_INTERSECTS(geom,
CIRCLE(POINT(40.730610, -73.935242), 2));
But none of them worked. What I am doing wrong here? How can I get the results I need?
The SRID is 4269 (NAD 83).
EDIT: After Mike Organek pointed me out that I have switched the lat-long in the above query. And then I tried a few things and the following query gave me 1 record.
SELECT * FROM us_census_zcta WHERE ST_INTERSECTS(geom::geometry,
ST_SETSRID(ST_POINT(-73.935242, 40.730610), 4269)::geometry);
But how can I use Circle and find records which intersect within 2 miles radius from that given lat-long?
What you're looking for is ST_DWithin, which will check if records intersect within a given buffer. In order to use it with miles you better cast the geometry column to geography, as it then computes distances in metres:
For geography: units are in meters and distance measurement defaults to use_spheroid=true. For faster evaluation use use_spheroid=false to measure on the sphere.
SELECT * FROM us_census_zcta
WHERE
ST_DWithin(
geom::geography,
ST_SetSRID(ST_MakePoint(-73.935242, 40.730610), 4269)::geography,
3218.688); -- ~2 miles
Keep in mind that this cast might affect query performance if the indexes aren't set properly.
See also: Getting all Buildings in range of 5 miles from specified coordinates
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
I am trying to develop a query in Postgis, where it can solve this problem:
I have a geometry and I wanna know which of the polygons that touches it, there is the highest contact area of this geometry. After I recognize this polygon I will take its value in a specific column and put this value in the same column but in my geometry.
Someone know how can I do that? I am a new user in postgresql/postgis.
As pointed out by #JGH in the comments, the overlapping area will be zero if you use ST_Touches alone. What you can do is to filter out only the geometries that do touch your reference geometry and then use ST_Intersection to get the intersection area, so that you can finally calculate the length of the intersection with ST_Length.
Data Sample
The geometry values depicted above are inside the CTE:
WITH j (id,geom) AS (
VALUES
(1,'POLYGON((-4.64 54.19,-4.59 54.19,-4.59 54.17,-4.64 54.17,-4.64 54.19))'),
(2,'POLYGON((-4.59 54.19,-4.56 54.19,-4.56 54.17,-4.59 54.17,-4.59 54.19))'),
(3,'LINESTRING(-4.65 54.19,-4.57 54.21)'),
(4,'POLYGON((-4.66 54.21,-4.60 54.21,-4.60 54.20,-4.66 54.20,-4.66 54.21))'),
(5,'POINT(-4.57 54.20)')
)
SELECT
id,
ST_Length(
ST_Intersection(
geom,
'POLYGON((-4.62 54.22,-4.58 54.22,-4.58 54.19,
-4.62 54.19,-4.62 54.22))')) AS touch_length
FROM j
WHERE
ST_Touches(
geom,
'POLYGON((-4.62 54.22,-4.58 54.22,-4.58 54.19,
-4.62 54.19,-4.62 54.22))')
ORDER BY touch_length DESC
LIMIT 1;
id | touch_length
----+---------------------
1 | 0.03000000000000025
(1 Zeile)
I'm storing myriad attributes from user-uploaded image files in a single table. The basic structure of the table is like this:
attrib_id | image_id | attrib_name | attrib_value
Alongside attributes like TAG and CAPTION, I'm storing LATITUDE and LONGITUDE of the image's location in the same manner. All the columns are of type varchar.
I'm trying to query for images associated with locations within a given bounding box - the inputs are upper and lower latitude, start and end longitude. The output of the query should be a list of image_ids that have a row with name=LATITUDE and value BETWEEN upper and lower latitude, as well as the same for longitude.
Since all the values are strings, and in the same columns, I don't really know where to start with this one.
While I'm willing to consider restructuring the table, my intuition tells me there's a way to accomplish this in SQL.
My database is currently MySql, but I'm likely to switch over to Postgres in the future, so I would prefer non-vendor specific solutions.
You can do something like this:
SELECT * FROM Table lat
INNER JOIN Table lon ON lat.image_id = lon.image_id
WHERE (lon.attrib_name = 'LONGITUDE' AND lon.attrib_value BETWEEN start AND end) AND
(lat.attrib_name = 'LATITUDE' AND lat.attrib_value BETWEEN upper AND lower)
Not sure if BETWEEN is supported in Postgres or whatever you migrate to so you can just use < and > operators.
I have a table of 320000 rows which contains lat/lon coordinate points. When a user selects a location my program gets the coordinates from the selected location and executes a query which brings all the points from the table that are near. This is done by calculating the distance between the selected point and each coordinate point from my table row. This is the query I use:
select street from locations
where ( ( (lat - (-34.594804)) *(lat - (-34.594804)) ) + ((lon - (-58.377676 ))*(lon - (-58.377676 ))) <= ((0.00124)*(0.00124)))
group by street;
As you can see the WHERE clause is a simple Pythagoras formula to calculate the distance between two points.
Now my problem is that I can not get an INDEX to be usable. I've tried with
CREATE INDEX indx ON location(lat,lon)
also with
CREATE INDEX indx ON location(street,lat,lon)
with no luck. I've notice that when there is math operation with lat or lon, the index is not being called . Is there any way I can optimize this query for using an INDEX so as to gain speed results?
Thanks in advance!
The problem is that the sql engine needs to evaluate all the records to do the comparison (WHERE ..... <= ...) and filter the points so the indexes don’t speed up the query.
One approach to solve the problem is compute a Minimum and Maximum latitude and longitude to restrict the number of record.
Here is a good link to follow: Finding Points Within a Distance of a Latitude/Longitude
Did you try adjusting the page size? A table like this might gain from having a different (i.e. the largest?) available page size.
PRAGMA page_size = 32768;
Or any power of 2 between 512 and 32768. If you change the page_size, don't forget to vacuum the database (assuming you are using SQLite 3.5.8. Otherwise, you can't change it and will need to start a fresh new database).
Also, running the operation on floats might not be as fast as running it on integers (big maybe), so that you might gain speed if you record all your coordinates times 1 000 000.
Finally, euclydian distance will not yield very accurate proximity results. The further you get from the equator, the more the circle around your point will flatten to ressemble an ellipse. There are fast approximations which are not as calculation intense as a Great Circle Distance Calculation (avoid at all cost!)
You should search in a square instead of a circle. Then you will be able to optimize.
Surely you have a primary key in locations? Probably called id?
Why not just select the id along with the street?
select id, street from locations
where ( ( (lat - (-34.594804)) *(lat - (-34.594804)) ) + ((lon - (-58.377676 ))*(lon - (-58.377676 ))) <= ((0.00124)*(0.00124)))
group by street;