Postgis nearest coordinates - postgresql

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.

Related

How to fix 'BOOM! Could not generate outside point' error when using ST_Distance_Spheroid with different SRS in PostGIS

I am trying to calculate the nearest feature distance from a point using PostGIS ST_Distance_Spheroid function.
Case 1:
The point and the feature collection are in the same SRS and I am expecting the distance to be in meters.
Here is my query:
SELECT
name,
ST_DistanceSpheroid(geom, ST_GeomFromText('POINT(-2.0363502486833616 52.688988284010456)', 4326), 'SPHEROID["WGS 84",6378137,298.257223563]' ) AS distance_m
FROM green_belt
ORDER BY geom <-> ST_GeomFromText('POINT(-2.0363502486833616 52.688988284010456)', 4326)
LIMIT 1;
The above query is working as expected. Here is the result:
Case 2:
When the SRS are different for the point and the feature collection, I am using the below query:
SELECT
name,
ST_DistanceSpheroid(geom, ST_Transform(ST_GeomFromText('POINT(-2.0363502486833616 52.688988284010456)', 4326), 27700), 'SPHEROID["WGS 84",6378137,298.257223563]' ) AS distance_m
FROM aonb
ORDER BY geom <-> ST_Transform(ST_GeomFromText('POINT(-2.0363502486833616 52.688988284010456)', 4326), 27700)
LIMIT 1;
But in this case, I am getting an error:
ERROR: BOOM! Could not generate outside point!
CONTEXT: SQL function "st_distance_spheroid" statement 2
SQL state: XX000
I even tried by transforming the coordinates and use it in the first query to get the distance:
SELECT
name,
ST_DistanceSpheroid(geom, ST_GeomFromText('POINT(357980.44 331724.97)', 27700), 'SPHEROID["Airy 1830",6377563.396,299.3249646]' ) AS distance_m
FROM aonb
ORDER BY geom <-> ST_GeomFromText('POINT(357980.44 331724.97)', 27700) LIMIT 1;
Even with this query, I got the same error
ERROR: BOOM! Could not generate outside point!
SQL state: XX000

What is wrong with my ST_Within query - query result contains point twice although it exists only once

I have two point tables, tab_1 and tab_2. I want to query all points from the first table that are probably the same points from the table 2. So i give the points from table 2 a buffer. Then I want to get the points from table 1 and query from table 2 within a 30 m buffer. My problem is, I get the points from table 1 and table 2 twice. But point 1 from table 1 exists only once and point 1 from table 2 also only once.
My query is:
with
"points1" as
(
select id, geom from tab_1
)
,
"points2" as
(
select id, geom from tab_2
)
select "points1".*, "points2".* from "points1", "points2"
where
st_within(st_transform("points1".geom, 31468), st_buffer(st_transform("points2".geom, 31468), 30)) = true;
id_tab1
geom
id_tab2
geom
st_distance
767074270
POINT (11.6968379 48.132722)
16455
POINT (11.69707 48.13265)
19.041083533921977
767074270
POINT (11.6968379 48.132722)
16455
POINT (11.69707 48.13265)
19.041083533921977
The query should be give only one result:
id_tab1
geom
id_tab2
geom
st_distance
767074270
POINT (11.6968379 48.132722)
16455
POINT (11.69707 48.13265)
19.041083533921977
Is my query wrong?
STEP 1. Query
SELECT *
FROM tab_1
JOIN tab_2
ON ST_DWithin
( ST_Transform(tab_1.geom, 31468)
, ST_Transform(tab_2.geom, 31468)
, 30
)
STEP 2. Spatial index
Most likely, the query cannot use the spatial index (even if it exists) and the function ST_DWithin() properly (ST_Transform() does not allow using an existing spatial index).
Solution - create new spatial indexes for EPSG:31468
CREATE
INDEX tab_1_geom_31468_idx
ON tab_1
USING GIST (ST_Transform(geom, 31468))
;
CREATE
INDEX tab_2_geom_31468_idx
ON tab_2
USING GIST (ST_Transform(geom, 31468))
;

Recursive PostGIS query

I am trying to transform all the roundabouts in a city into simple intersections/crossroads (o -> +). As I am using OpenStreetMap for the initial topology, some roundabouts are not a circle but just the segments of the circle (Ex: https://www.openstreetmap.org/#map=18/43.34516/-8.41536).
In practice the problem is that I need the centroid of each roundabout and I get it almost in all cases but sometimes I get several centroids for the same roundabout (centroids of the arches, no the full roundabout).
I have achieved this:
select f.osm_id as fid, (select ST_CENTROID(ST_Buffer(ST_UNION(way),1)) as r_geom
from planet_osm_line d
where st_intersects(f.way, d.way) and junction = 'roundabout') as rotonda
from planet_osm_line f
where junction like 'roundabout';
But this does not resolve the problem, it only reduces it. I am not getting the full circle, just bigger segments of it.
So I guess I need a recursive query in order to do this until the number of geometries retrieved remains the same (the full circle). Any ideas about how to build this query?
I was looking for something like this (hope it helps others in need):
create table no_roundabouts as
with recursive roundabout(geom) as (--Recursive function to build closed circled roundabouts even with roundabouts mapped as differents arches.
SELECT ST_TRANSFORM(way,3857)
FROM planet_osm_line ways --Get all segments tagged as 'roundabout'
WHERE ways.junction = 'roundabout'
UNION ALL
SELECT ST_TRANSFORM(ST_UNION(ways.way, roundabout.geom),3857)
FROM roundabout, planet_osm_line ways -- Compose segments building greater arches of the roundabout until we have the full circle (My_segment + a touching segment that is no contained in my segment)
WHERE ways.junction = 'roundabout' and ST_INTERSECTS(roundabout.geom, ways.way) and not ST_CONTAINS(roundabout.geom, ways.way)
)
SELECT * FROM roundabout;
alter table no_roundabouts add column id bigserial; -- Add id to each line
delete from no_roundabouts a -- Delete repeated roundabouts generated during recursion
where exists (select geom from no_roundabouts b where ST_CONTAINS(b.geom, a.geom) and b.id > a.id);
--select count(*) from no_roundabouts WHERE ST_IsClosed(geom) = false;
update no_roundabouts set geom = ST_LINEMERGE(geom) where ST_ISCLOSED(geom) is false --Force closed roundabouts
-- Query replacing roundabouts with crossroads (linking each way in and out with the centroid of the roundabout)
SELECT ST_TRANSFORM(ST_ADDPOINT(y.way, ST_CENTROID(x.geom), 0),4326)
FROM no_roundabouts x JOIN planet_osm_line y ON ST_INTERSECTS(y.way, x.geom)
WHERE y.highway is not null and ST_INTERSECTS(x.geom, st_pointn(y.way,1)) and ST_CONTAINS(x.geom, y.way) = false
UNION
SELECT ST_TRANSFORM(ST_ADDPOINT(y.way, ST_CENTROID(x.geom), -1),4326)
FROM no_roundabouts x JOIN planet_osm_line y ON ST_INTERSECTS(y.way, x.geom)
WHERE y.highway is not null and ST_INTERSECTS(x.geom, ST_POINTN(y.way,-1)) and ST_CONTAINS(x.geom, y.way) = false;

Nearest places from a certain point

I have the following table
create table places(lat_lng point, place_name varchar(50));
insert into places values (POINT(-126.4, 45.32), 'Food Bar');
What should be the query to get all places close to particular lat/long?
gis is installed.
If you actually wanted to use PostGIS:
create table places(
lat_lng geography(Point,4326),
place_name varchar(50)
);
-- Two ways to make a geography point
insert into places values (ST_MakePoint(-126.4, 45.32), 'Food Bar1');
insert into places values ('POINT(-126.4 45.32)', 'Food Bar2');
-- Spatial index
create index places_lat_lng_idx on places using gist(lat_lng);
Now to find all of the places within 1 km (or 1000 m):
select *, ST_Distance(lat_lng, ST_MakePoint(-126.4, 45.32)::geography)
from places
where ST_DWithin(lat_lng, ST_MakePoint(-126.4, 45.32)::geography, 1000)
order by ST_Distance(lat_lng, ST_MakePoint(-126.4, 45.32)::geography);
select *
from places
where lat_lng <-> POINT(-125.4, 46.32) < 1
order by lat_lng <-> POINT(-125.4, 46.32)
Create an Indexing on a location field :
CREATE INDEX ON table_name USING GIST(location);
GiST index is capable of optimizing “nearest-neighbor” search :
SELECT * FROM table_name ORDER BY location <-> point '(-74.013, 40.711)' LIMIT 10;
Note: The point first element is longitude and the second element is latitude.

Simultaneous selecting from tables and functions which use values from these tables

I have 3 tables: lightnings, powerlines, masts.
The main fields:
lightnings.geo_belief - an ellipse of a probable hitting.
powerlines.geo_path - a geo polyline of powerline's path.
masts.geo_coordinates - a geo point of a mast placing.
The task:
To calculate lightning strokes that hit powerline's corridor (5000
meters - its radius, and it is generated as a geometry by function
powerline_corridor())
To get info about a powerline's mast, nearest to a respective lightning hit and to get the distance from lightning.geo_ellipse to masts.geo_coordinates.
So I can select lightnings:
SELECT l.*
FROM lightnings l
JOIN ( SELECT geo_path, powerline_corridor(geo_path, 5000::smallint) AS geo_zone
FROM powerlines WHERE id=1)
AS by_pl
ON ST_Intersects(by_pl.geo_zone, l.geo_belief)
Also I have got the function namos_nearest_mast(powerlines.id, lightnings.geo_belief):
CREATE OR REPLACE FUNCTION public.namos_nearest_mast (
powerline_id integer,
geo public.geometry
)
RETURNS public.obj_powerline_masts AS
$body$
SELECT *
FROM obj_powerline_masts
WHERE powerline_id=$1
ORDER BY $2 <-> geo_coordinates ASC
LIMIT 1
$body$
LANGUAGE 'sql';
Couldn't you suggest good solutions for selecting?
Following is all I've done by myself:
SELECT
t.*,
ROUND(st_distance(namos_transform_meters(m.geo_coordinates), namos_transform_meters(t.geo_belief))) AS dist_m
FROM obj_powerline_masts AS m
JOIN
(
SELECT
l.*,
(SELECT id FROM nearest_mast(1, l.geo_belief)) AS mast_id
FROM lightnings l
JOIN (SELECT geo_path, powerline_corridor(geo_path, 5000::smallint) AS geo_zone FROM powerlines WHERE id=1) AS by_pl ON ST_Intersects(by_pl.geo_zone, l.geo_belief)
LIMIT 50 OFFSET 50
) AS t
ON t.mast_id=m.id
But I'm not sure if it's an optimal solution. For instance, in PHP I can't apply dataProviders on such queries (which abstracts e.g. working with pagination), because of we can't affect on subqueries in a trivial way.