The repeated passing of the same point is tricky, and you won't get any direct help from PostGIS.
However, you can
- create a sequenced set of minimal components (two-vertice segments) from your LineStrings
SELECT id,
seq,
geom
FROM (
SELECT ln.id,
dmp.path[1] AS seq,
ST_MakeLine(
dmp.geom,
LEAD(dmp.geom) OVER(PARTITION BY ln.id ORDER BY dmp.path)
) AS geom
FROM <line_table> AS ln,
LATERAL ST_DumpPoints(ln.geom) AS dmp
) q
WHERE geom IS NOT NULL
;
- find all geometries in range and
ORDER BY the sequenced segments and the fraction of line length at which those geometries (for Points) or derived Point geometries (for Polygons) project onto the line segment:
WITH
segs AS [MATERIALIZED] (
<above_query>
)
SELECT other.id
FROM segs
JOIN <other_geometries_table> AS other
ON ST_DWithin(segs.geom, other.geom, <threshold>)
ORDER BY
segs.id, segs.seq, ST_LineLocatePoint(segs.geom, other.geom)
;
The above is working on Points only. You will have to derive a Point geometry from any crossed Polygon somehow; some options are to use
- the
ST_Centroid of the Polygon, after determining if it is crossed
- the
ST_Centroid of the ST_Intersection between Polygon and LineString
Using ST_Centroid has the advantage that you can pass in Points, which may make streamlining the query on mixed geometry types easier, if you are indeed using a mixed geometry types table as other.
If not, you can UNION ALL queries on both tables; select all fields (including the ST_LineLocatePoint fraction), and ORDER BY in an outer query:
WITH
segs AS [MATERIALIZED] (
<above_query>
)
SELECT ROW_NUMBER() OVER() AS seq,
q.id
FROM (
SELECT point.id,
segs.id AS _id,
segs.seq AS _seq,
ST_LineLocatePoint(segs.geom, point.geom) AS _frac
FROM segs
JOIN <point_table> AS point
ON ST_DWithin(segs.geom, point.geom, <threshold>)
UNION ALL
SELECT polygon.id,
segs.id AS _id,
segs.seq AS _seq,
ST_LineLocatePoint(segs.geom, ST_Centroid(ST_Intersection(segs.geom, polygon.geom))) AS _frac
FROM segs
JOIN <polygon_table> AS polygon
ON ST_Crosses(segs.geom, polygon.geom)
) q
ORDER BY
_id, _seq, _frac
;
Note:
This will double-count point B in your example! And that may or may not be intended: does the line pass point B twice? This is a general question of context and algorithm theory that you will encounter with this task, no matter the methodology. E.g. to exclude point B from getting counted twice, you'd need to add a rule to remove duplicates in adjacent segments.
So basically you go and split up my input line as multiple lines each containing two coordinates? Then you go and match those multiple line-segments?!? Order them and therefore I am already able to find multiple hits.
I think I need to let this sink a bit and also make some research on what
MATERIALIZED,LATERAL,PARTITIONand so on are doing, but this is an excellent starting point!Thank you very much!
– Georg Mar 17 '21 at 14:58