Querying for Bounding Box in PostGIS Yields Unintended Result Set - postgresql

I create the data set of 1'000'000 uniformly random points in [0,1]x[0,1] with R and then export it as a CSV so I can load it into Postgres/PostGIS.
R:
N <- 1000000
df <- data.frame(id=1:N, lon=runif(N), lat=runif(N))
write.table(df,"/media/Volume/temp/random_points.csv",row.names=FALSE,sep=";")
# manually remove header from CSV
head(df)
# id lat lon
#1 1 0.9094180 0.1208861
#2 2 0.8009161 0.8499304
#3 3 0.9800282 0.3757218
#4 4 0.5795991 0.4551454
#5 5 0.8988043 0.7801994
#6 6 0.9456310 0.2343178
PostGIS:
create table random_points (id serial, lat float, lon float);
ALTER TABLE random_points ADD PRIMARY KEY (id);
# now I import CSV into table
ALTER TABLE random_points ADD COLUMN geom geometry(POINT,4326);
UPDATE random_points SET geom = ST_SetSRID(ST_MakePoint(lon,lat),4326);
CREATE INDEX idx_lon_lat ON random_points USING GIST(geom);
And now I would like to query for all points lying within the bounding box - powered by an index:
min lat = 0.342, max lat = 0.352, min lon = 0.793, max lon = 0.812
SELECT *
FROM random_points
WHERE random_points.geom && ST_MakeEnvelope(0.342, 0.352, 0.793, 0.812, 4326);
But not only do I get a result set of size 207'376 which is much more than what I get with R (177) - the expected value for the area would be 190.
Already the first record does not belong to the intended bounding box; even if I confused longitude and latitude somewhere:
4;0.579599140677601;0.45514538907446;"...138CE23F"
9;0.618269162718207;0.392739744856954;"...9DCC8E33F"
10;0.742938967887312;0.58326911740005;"...127C6E73F"
17;0.665668761124834;0.475526283029467;"...92284DE53F"
27;0.668456399813294;0.747356393141672;"...ACFE63E53F"
Any idea what I am doing wrong?
My intention is to benchmark querying points with a bounding box in PostGIS to evaluate when it makes sense to use PostGIS for that purpose from R as opposed to using data frame/table tools.

The arguments to ST_MakeEnvelope are in the order xmim, ymin, xmax, ymax. You're passing ymin, ymax, xmin, xmax.
http://postgis.refractions.net/docs/ST_MakeEnvelope.html

Related

Indexing issue in postgres

It is impossible to speed up the database due to indexing.
I create a table:
CREATE TABLE IF NOT EXISTS coordinate( Id serial primary key,
Lat DECIMAL(9,6),
Lon DECIMAL(9,6));
After that I add indexing:
CREATE INDEX indeLat ON coordinate(Lat);
CREATE INDEX indeLon ON coordinate(Lon);
Then the table is filled in:
INSERT INTO coordinate (Lat, Lon) VALUES(48.685444, 44.474254);
Fill in 100k random coordinates.
Now I need to return all coordinates that are included in a radius of N km from a given coordinate.
SELECT id, Lat, Lon
FROM coordinate
WHERE acos(sin(radians(48.704578))*sin(radians(Lat)) + cos(radians(48.704578))*cos(radians(Lat))*cos(radians(Lon)-radians(44.507112))) * 6371 < 50;
The test execution time is approximately 0.2 seconds, and if you do not do CREATE INDEX, the time does not change. I suspect that there is an error in the request, maybe you need to rebuild it somehow?
I'm sorry for my english
An index can only be used if the indexed expression is exactly what you have on the non-constant side of the operator. That is obviously not the case here.
For operations like this, you need to use the PostGIS extension. Then you can define a table like:
CREATE TABLE coordinate (
id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
p geography NOT NULL
);
and query like this:
SELECT id, p
FROM coordinate
WHERE ST_DWithin(p, 'POINT(48.704578 44.507112)'::geography, 50);
This index would speed up the query:
CREATE INDEX ON coordinate USING gist (p);

Create new column with coordinates from centroids using st_transform in PostGIS / PostgreSQL

I have geometry data that I'm trying to transform to lon lat and store in a new column. I have created a new column called 'coordinates' and written the following query:
UPDATE places_centroids
SET coordinates = st_y(st_transform(new_centroid, 4326)) AS lat,
st_x(st_transform(new_centroid, 4326)) AS lon;
But it returns: ERROR: ERROR: syntax error at or near
LINE 2: ...coordinates = st_y(st_transform(new_centroid, 4326)) AS lat,
However, it works fine when i writing the query with a select statement:
SELECT st_y(st_transform(new_centroid, 4326)) AS lat,
st_x(st_transform(new_centroid, 4326)) AS lon
FROM places_centroids;
Can anyone see what's wrong with my query?
You can create two columns, lat and lng for example of type double precision and do this:
UPDATE places_centroids
SET lat = st_y(st_transform(new_centroid, 4326)),
lng= st_x(st_transform(new_centroid, 4326));
or define coordinates as the native point type:
UPDATE places_centroids
SET coordinates = point(st_y(st_transform(new_centroid, 4326)),
st_x(st_transform(new_centroid, 4326)));
I personally prefer to store in two columns.
Best regards,
Bjarni

Query PostGIS radius giving me odd results

I'm new to PostGIS and I'm trying to create a radius query. I have a table with a geometry field (position) and also have the latitude and longitude values on separate fields.
I'm trying to find points on a 10 km radius from lat: 40.753777, lon: -73.981568.
with:
SELECT postcode, lat, lon, st_asgeojson(position) geojson, ST_Distance(ST_MakePoint(lat, lon), ST_MakePoint(40.753777, -73.981568)) distance FROM addresses WHERE ST_DWithin(ST_MakePoint(lat, lon), ST_MakePoint(40.753777, -73.981568), 10000) order by id limit 10;
The results give me very far a way points. The same query with earth distance using the lat and lon directly give me much closer results.
SELECT postcode, lat, lon, st_asgeojson(position) geojson FROM addresses WHERE earth_box(ll_to_earth(40.753777, -73.981568), 10000) #> ll_to_earth(addresses.lat, addresses.lon) order by id limit 10;
But I really don't know if this is right either, what's wrong with the PostGIS query?
A few notes:
I think you swapped the lat and lon when you made a point, your line says ST_MakePoint(40.753777, -73.981568), but the definition is:
geometry ST_MakePoint(double precision x, double precision y);
Note: x is longitude and y is latitude
So it should have been ST_MakePoint(-73.981568, 40.753777) instead.
As a simple solution you can use ST_Distance_Spheroid function ( http://www.postgis.org/docs/ST_Distance_Spheroid.html ):
SELECT
postcode, lat, lon, st_asgeojson(position) AS geojson,
ST_Distance_Spheroid(
position,
ST_GeomFromText('POINT(-73.981568 40.753777)',
4326), 'SPHEROID["WGS 84",6378137,298.257223563]'
) as distance
FROM addresses
WHERE distance < 10000 LIMIT 10;
For a more precise distances, add a new column of type geography from you existing column position of type geometry:
ALTER TABLE addresses ADD COLUMN position_geog geography(Point,4326);
UPDATE addresses SET position_geog = position::geography;
CREATE INDEX ON addresses USING gist(position_geog);
-- and now you can use ST_DWITHIN with meters...

Postgis nearest coordinates

I'm trying to make a REST service that returns a list of places ordered by distance from the user coordinate. I found this query using postgis:
SELECT *
FROM your_table
ORDER BY your_table.geom <-> "your location..."
LIMIT 5;
But I'm not able to apply this to my actual database. I have a table that contains these columns:
title, address, description, latitude, longitude
all these values as Strings.
I'll be very happy if someone help me. Thx!
I dont know why, but ORDER BY <-> isnt exact. Sometime the closest link is on the 3rd position. So I get 101 element and then use distance to selected the closest one.
CREATE OR REPLACE FUNCTION map.get_near_link(
x numeric,
y numeric)
RETURNS TABLE(Link_ID int, distance int) AS
$BODY$
DECLARE
strPoint text;
BEGIN
strPoint = 'POINT('|| X || ' ' || Y || ')';
With CTE AS (
SELECT Link_ID,
TRUNC(ST_Distance(ST_GeomFromText(strPoint,4326), geom )*100000)::integer as distance
FROM map.vzla_seg S
ORDER BY
geom <-> ST_GeomFromText(strPoint, 4326)
LIMIT 101
)
SELECT *
FROM CTE
ORDER BY distance
LIMIT 5
In order to use PostGIS you have to enable the extension in the database. Ideally, you just run the CREATE EXTENSION postgis; command and it works. NOTE form the install page: DO NOT INSTALL it in the database called postgres. For more information visit the site.
Adding a geometry column (spatial data can be stored in this type of columns) to your table:
SELECT AddGeometryColumn(
'your_schema',
'your_table',
'geom', -- name of the column
4326, -- SRID, for GPS coordinates you can use this, for more information https://en.wikipedia.org/wiki/Spatial_reference_system
'POINT', -- type of geometry eg. POINT, POLYGON etc.
2 -- number of dimension (2 xy - 3 xyz)
);
UPDATE yourtable t SET t.geom = ST_SetSRID(ST_MakePoint(t.x, t.y), 4326)
-- the x and y is the latitude and longitude
Now you can use spatial queries on your table like this:
SELECT
*
FROM
your_table
ORDER BY
your_table.geom <-> ST_SetSRID(ST_MakePoint(x, y), 4326)
LIMIT 5;
NOTE: as others mentioned, below PostgreSQL 9.5 <-> isn't always reliable.

Insert GEOMETRY value using PostgreSQL 9.3

I want to insert GEOMETRY values into a table. For which I have a table with three columns as shown below:
Table: geo
create table geo
(
p1 float,
p2 float,
Paths GEOMETRY
);
Input values: I have following values
p1 = 22.9901232886963
p2 = 87.5953903123242
In SQL Server I used this:
INSERT INTO geo(Paths)
VALUES (geometry = geometry::STGeomFromText('POINT (22.9901232886963 87.5953903123242)'
,4326);
Question: Is there any function of GEOMETRY to calculate points in PostgreSQL 9.3 version?
This works for me by referring: http://www.postgresql.org/docs/current/static/functions-geometry.html.
The function:
point(point(double precision, double precision);
So I need to convert the function: using ::geometry
Finally the insert statement looks like:
insert into geo values(22.9901232886963 87.5953903123242,
point(22.9901232886963 87.5953903123242)::geometry);
Am I right?