Snap points to linestring in Postgis - postgresql

This code snap line to points. Any ideas how to modify the code to snap the points to line. Thanks.
DROP TABLE IF EXISTS test_gps_point2;
SELECT D.ordinality As sub_id, D.geom::geometry(LINESTRING,3414) AS
geom INTO test_table
FROM
gtd1304_line_2a AS L
LEFT JOIN LATERAL
( -- form a multipoint of all the nodes
-- close enough to line to be considered on the line
SELECT
ST_Union(N.geom ORDER BY L.matched_line <-> N.geom) AS geom
FROM gtd1304_point_2 AS N
WHERE ST_DWithin(L.matched_line, N.geom, 10000)
) AS MP ON TRUE
CROSS JOIN LATERAL
-- snap the LINE to the MP which forces nodes to be injected to the line
-- then split at these node location and dump multilinestring into individual lines
ST_Dump(
COALESCE(ST_Split(ST_Snap(MP.geom, L.matched_line, 10), matched_line), MP.geom)
) WITH ORDINALITY AS D;

As far as I know you cannot do that, at least not with ST_Snap. I think the reason is that since you snap something A onto something B and get back something of type A with B snapped on it, and you can't get back something of type point with something snapped on it since then it becomes a line.

Related

Get nearest neighbor point from a table closest to the point in a given row of another table

I have two tables, table_a has polygons and the centroids of those polygons. table_b has another set of points overlaping the geometries in table_a.
For each row of table_a I need to find the point from table_b closest to the centroid of that row.
INSERT INTO nearest_node (nearest_drive_node)
SELECT osmid FROM london_drive_nodes
ORDER BY london_drive_nodes.geom <-> nearest_node.lsoa_centroid
LIMIT 1;
This returns
SQL Error [42P01]: ERROR: invalid reference to FROM-clause entry for table "nearest_node"
Hint: There is an entry for table "nearest_node", but it cannot be referenced from
this part of the query.
I'm not sure exactly how to use the value from table_a as the point in the ORDER BY part of the query. The examples I've found are finding the nearest neighbor of a single point as a text string, rather than a column of points.
Inserting the closest node as a new row in the table, without any other attribute, seems wrong. You most certainly want to update the existing records.
You must compute the closest node for each row of the input table, which can be achieve with a sub query.
UPDATE nearest_node
SET nearest_drive_node = (
SELECT london_drive_nodes.osmid
FROM london_drive_nodes
ORDER BY nearest_node.geom <-> london_drive_nodes.geom
LIMIT 1
);
If you were to just select (and eventually to insert this information in another table), you would rely on a lateral join:
select a.osmid,closest_pt.osmid, closest_pt.dist
from tablea a
CROSS JOIN LATERAL
(SELECT
osmid ,
a.geom <-> b.geom as dist
FROM tableb b
ORDER BY a.geom <-> b.geom
LIMIT 1) AS closest_pt;
The problem seems to be that you reference nearest_node in the query, but not in the FROM clause, but in general, your query wouldn't work "for each row" anyway. Try combining st_distance and regular min with group by to get the minimum distance, then wrap it in a CTE or subquery to identify which node it actually is:
WITH distances AS (
SELECT nn.id, nn.lsoa_centroid, min(st_distance(ldn.geom, nn.lsoa_centroid)) as min_distance
FROM london_drive_nodes ldn, nearest_node nn
GROUP BY nn.id
)
INSERT INTO nearest_node (nearest_drive_node)
SELECT ldn.osmid
FROM distances
JOIN london_drive_nodes ldn ON distances.min_distance = st_distance(distances.wgs84_centroid, ldn.wgs84_coordinates)

pgRouting: route starting from closest node along edge

I am trying to find a way to route from the closest point on a linestring to my current location (lat, long). So far I am able to get the shortest path but it starts from the very beginning of the linestring (aka source). I am using prg_trsp
http://docs.pgrouting.org/2.0/en/src/trsp/doc/index.html
because it has a feature to specify the starting position along the linestring. I am able to correctly calculate the distance along the linestring and pass the values to the function but cannot figure out how to use the results from the function (pgr_costResult[]) to specify where the route should start (partially along the closest linestring).
I have a feeling I am doing something wrong with the join when I go to join the results from the routing algorithm to my edge table to get the geometry because when I join it uses the edge table's full geometry and not segments. Although, looking at the documentation, I don't see where you get a returned segment from the routing function.
Below is a screenshot of what I am trying to do (red line) and what I have (blue line) the point is the current location. The red line comes from using the pgrouting plugin in qgis with the trsp(edge) selection.
See code below:
Any help would be much appreciated!
SELECT st_linemerge(edgeTable.geom_way) FROM pgr_trsp('SELECT id, source, target, cost FROM edgeTable',
(SELECT id FROM origin),
(SELECT * FROM sourcePos),
(SELECT id FROM destination),
(SELECT * FROM destPos),
false, false) AS shortestPath
JOIN edgeTable ON shortestPath.id2 = edgeTable.id;
origin is the id of the starting route
sourcePos is how far along the linestring to offset
destination is the id of the end linestring
destPos is the fraction of the end linestring
all as specified here: http://docs.pgrouting.org/2.0/en/src/trsp/doc/index.html
Its because pgr_trsp() function doesn't give the output your are excepting. Pg_routing Plugin in QGIS does the snapping of the route generated from pgr_trsp(). So you have the output of red line snapped close to your point. So just pgr_trsp() won't give you your desired output. What you are trying to do is a bit complicated but possible. Here is how I solved this problem
WITH
--Make a start point
start_pt as (
select st_setsrid(st_makepoint(204845.95, 2410097.47), 32643) as starting),
--Make a End Point
end_pt as (
select st_setsrid(st_makepoint(204937.15, 2409430.86), 32643) as ending),
--Select Closest source node and its geom for start point
source_code AS (
select source, geom from edgeTable order by st_distance(geom, (select starting from start_pt)) limit 1),
--Select closest target node and its geom for end point
target_code AS (
select target, geom from edgeTable order by st_distance(geom, (select ending from end_pt)) limit 1),
--Route Union from pgr_trsp()
route as (
SELECT ST_LineMerge(ST_union(geom)) as geom, round( CAST(float8 (st_length(ST_union(geom))/1000) as numeric), 2) as length from (
SELECT geom FROM pgr_trsp(
'SELECT feat_id as id, source, target, cost_len as cost, geom FROM edgeTable',
(select source from source_code), (select target from target_code), false, false
) as di JOIN edgeTable
ON di.id2 = edgeTable.id) as foo)
--Finaly snap the route to precisely matach our start and end point
select ST_Line_Substring(geom,
ST_LineLocatePoint(geom, (select starting from start_pt)),
ST_LineLocatePoint(geom, (select ending from end_pt)))
from route
The only issue I have is that I have to switch Starting and Ending Point in last select statement. This can be handled via writing a function. Here is my output
Hope this Help...

Is this a good candidate for plpgsql?

Several of the introductory tutorials I've read on using for loops in plpgsql have said unless I really need a for loop, I should find another way.
As a programming novice I can't figure out a better way than a for loop.
I would like to go through each parcel gid, calculate the nearest street intersection by bounding box, then assign the intersection id to the nearest_intersection column in parcels.
All the parts of this function work nicely. But I can't figure out how to put them together.
BEGIN
FOR gid IN SELECT gid FROM parcels
LOOP
UPDATE parcels SET nearest_intersection =
(select intersection.osm_id
from intersections
order by
intersections.geom <-> (select geom from parcels where gid = parcel_id)
limit 1;)
end loop;
end;
Thank you!
In your current code, the loop doesn't make sense indeed because the UPDATE alone already processes every row of the parcels table.
What you probably want is (without a loop):
UPDATE parcels R SET nearest_intersection =
(select intersection.osm_id
from intersections
order by intersections.geom <-> R.geom
limit 1);
which in procedural thinking would be the equivalent to:
for every row R of parcels, find the row in intersections
whose geom is the nearest to R.geom and copies its osm_id into
R.nearest_intersection
On the other hand, if it had to be done with a loop, it would look like this:
FOR var_gid IN SELECT gid FROM parcels
LOOP
UPDATE parcels SET nearest_intersection =
(select intersection.osm_id
from intersections
order by
intersections.geom <-> parcels.geom)
limit 1)
WHERE parcels.gid=var_gid;
end loop;
Don't be a hostage of SQL purism. Write functions with loops. When you are a postgres expert you'll change them to queries. Or not.
You probably missed WHERE clause for UPDATE.

Find cluster given node in PostgreSQL

I am representing a graph in Postgres 9.1 (happens to be bidirectional and cyclic):
CREATE TABLE nodes (
id SERIAL PRIMARY KEY,
name text
);
CREATE TABLE edges (
id SERIAL PRIMARY KEY,
node1_id int REFERENCES nodes(id),
node2_id int REFERENCES nodes(id)
);
Given a particular node ID, want to retrieve all other nodes in that cluster. I started with the "Paths from a single node" example here, and this is where I got:
WITH RECURSIVE search_graph(id, path) AS (
SELECT id, ARRAY[id]
FROM nodes
UNION
SELECT e.node2_id, sg.path || e.node2_id
FROM search_graph sg
JOIN edges e
ON e.node1_id = sg.id
)
-- find all nodes connected to node 3
SELECT DISTINCT id FROM search_graph WHERE path #> ARRAY[3];
I can't figure out a) if there is a simpler way to write this since I don't care about collecting the full path, and b) how to make it traverse in both directions (node1->node2 and node2->node1 for each edge). Shedding any light on a good approach would be appreciated. Thanks!
A couple points.
First, you really want to make sure your path traversal is not going to go into a loop. Secondly handling both sides is not too bad. Finally depending on what you are doing, you may want to push the where clause into the CTE somehow to reduce generating every possible graph network and then picking the one you want.
Traversing itself both directions is not too hard. I haven't tested this but it should be possible with something like:
WITH RECURSIVE search_graph(path, last_node1, last_node2) AS (
SELECT ARRAY[id], id, id
FROM nodes WHERE id = 3 -- start where we want to start!
UNION ALL
SELECT sg.path || e.node2_id || e.node1_id, e.node1_id, e.node2_id
FROM search_graph sg
JOIN edges e
ON (e.node1_id = sg.last_node2 AND NOT path #> array[e.node2_id])
OR (e.node2_id = sg.last_node1 AND NOT path #> array[e.node1_id])
)
-- Moved where clause to start of graph search
SELECT distinct unnest(path) FROM search_graph; -- duplicates possible
Hope this helps.

Updating multiple rows in one table based on multiple rows in a second

I have two tables, table1 and table2, both of which contain columns that store postgis geometries. What I want to do is see where the geometry stored in any row of table2 geometrically intersects with the geometry stored in any row of table1 and update a count column in table1 with the number of intersections. Therefore, if I have a geometry in row 1 of table1 that intersects with the geometries stored in 5 rows in table2, I want to store a count of 5 in a separate column in table one. The tricky part for me is that I want to do this for every row of column 1 at the same time.
I have the following:
UPDATE circles SET intersectCount = intersectCount + 1 FROM rectangles
WHERE ST_INTERSECTS(cirlces.geom, rectangles.geom);
...which doesn't seem to be working. I'm not too familiar with postgres (or sql in general) and I'm wondering if I can do this all in one statement or if I need a few. I have some ideas for how I would do this with multiple statements (or using for loop) but I'm really looking for a concise solution. Any help would be much appreciated.
Thanks!
something like:
update t1 set ctr=helper.ctr
from (
select t1.id, count(*) as cnt
from t1, t2
where st_intersects(t1.col, t2.col)
group by t1.id
) helper
where helper.id=t1.id
?
btw: Your version does not work, because a row can get updated only once in a single update statement.