Update points geometry while snapping them to nearest line
just put the select qry in a cte and add the id column and join to the update clause
with a as(SELECT ST_ClosestPoint(lines.geom, points.geom) as snapped_point,points.id id
FROM lines
INNER JOIN points on ST_Dwithin(lines.geom, points.geom, 5)
ORDER BY points.id)
UPDATE points
SET geom = snapped_point from a where points.id=a.id
An UPDATE
requires you to explicitly define the one, single return value to be put in place of each specified column value. To get only those, one usually wants to use a reference to the current row...and that's no problem, since the current row is available as record values to all statements in the UPDATE
query.
Your sub-query, however, fetches the full table join (all records of each possible pair in the JOIN
) and picks one at random (or likely the first row in the set).
Using the points.id
as in @ziggys answer is indeed one way to refer to the updated row; however, in cases where more than one line is in the given proximity, there will be as many candidates in the result with a matching id.
Instead, better make sure you will always snap the point to the closest line in the given proximity (if proximity is actually needed at all?); running
UPDATE points
SET geom = (
SELECT ST_ClosestPoint(lines.geom, points.geom)
FROM lines
-- WHERE ST_DWithin(points.geom, lines.geom, 5)
ORDER BY lines.geom <-> points.geom
LIMIT 1
)
;
will order the lines.geom
in proximity by distance to the updated points.geom
, and limits the set to the closest one. This is highly efficient only with a spatial index in place on lines.geom
!
This is commonly referred to as (spatial) (K) Nearest Neighbor search; more on the concept can be found e.g. here
- How to UPDATE with LATERAL Nearest-Neighbour query?
- Efficient way to find nearest feature between huge postgres tables