Recursive PostGIS query - postgresql

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;

Related

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))
;

Joining one table twice in postgresql

I have two columns in the same table that I want to join in Postgresql but for some reason I’m getting this error. Don’t know how to figure it out. Please help.
[42P01] ERROR: relation "a" does not exist
Position: 10
X table contains two pools(ABC,XYZ), ids, numbers and description. If an ID exists in one pool but not in the other, it should update description column to “ADD”. Pools need to be joined on number.
UPDATE A
SET A.Description = 'ADD'
FROM X AS A
LEFT JOIN X AS B ON B.number = A.number
AND B.id = 'ABC'
WHERE A.id = 'XYZ'
AND B.number IS NULL
AND A.Description IS NULL;
With standard SQL you can't do a join as part of an update, but what you can do is include a subquery to select the id's to update. The subquery can contain a join. I'm not entirely clear on what you're actually trying to accomplish, but you could do something like this:
UPDATE x SET description='ADD' WHERE number IN (
SELECT a.number FROM x AS a
LEFT OUTER JOIN x AS b ON a.number=b.number AND a.id='XYZ' AND b.id='ABC'
WHERE b.number IS NULL
);
This will join the table x with itself and will select (and update) any numbers's that don't have a matching number in the 'ABC' and 'XYZ' zone.
PostgreSQL does have a UPDATE FROM syntax that does let you update with complex subqueries. It's more flexible but it's non-standard. You can find an example of this type of query here.

select radius from ST_MinimumBoundingRadius

I want to get just radius value from ST_MinimumBoungingRadius.
Something like this (from postgresql documentation) works just fine:
SELECT radius
FROM
ST_MinimumBoundingRadius('MULTIPOINT(1 2,3 8,5 6)')
So I don't understand, why doesn't work similar query on existing table:
SELECT radius
FROM
(SELECT
ST_MinimumBoundingRadius(ST_Collect(geom)) minrad
FROM a) b
Result of this query is ERROR: column "radius" does not exist
Is there any way to extract just radius value?
The main difference is that in the first case you are calling the function in the FROM clause while in the second it is in the select clause. In the first case, the result is made of two column while in the later it is a string aggregation of all columns.
You can fix it by using the function in the FROM clause again, using either a double-parenthesis or a lateral join:
SELECT radius
FROM ST_MinimumBoundingRadius((SELECT ST_Collect(geom)
FROM a)) minrad;
or
SELECT radius
FROM (SELECT ST_Collect(geom) geom FROM a) tbla,
LATERAL ST_MinimumBoundingRadius(tbla.geom) minrad;

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.

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.