Recientemente he trabajado en un problema similar a este.
Mi metodología fue la siguiente:
- Encontrar la línea más cercana al punto o puntos de entrada
- Obtenga una pequeña subsección de esta línea
- Rota la subsección 90 grados alrededor de un punto de la línea más cercana al punto de entrada (nos permite cortar y o crear punto en la intersección)
A esta metodología podemos añadir una forma de cortar obtener un punto cuando estas dos líneas se cruzan... Para ello podemos utilizar algo como este :
ST_CollectionExtract(ST_Intersection(a.geom, b.geom), 1)
Estaba trabajando con una tabla de muchos puntos de entrada, pero debería estar bien para un solo punto (necesitamos un geom y id en las tablas de redes y puntos).
Código completo aquí:
--1 Closest line to point(s)
CREATE TABLE schema.test_closestline AS
SELECT pid ,lid, distance_m, a.geom
FROM
(SELECT q.id pid, a.id lid, ROUND(ST_Distance(q.geom, a.geom)::NUMERIC, 2) as distance_m,
ROW_NUMBER() OVER (PARTITION BY q.id ORDER BY ST_DISTANCE(a.geom,q.geom)) as row_number,
a.geom
FROM schema.road_network a
INNER JOIN schema.input_points q ON
ST_DWITHIN(a.geom,q.geom,100) -- use a sensible distance to restrict the result set without losing records
) a
WHERE a.row_number = 1
DROP TABLE IF EXISTS schema.test_blade_rotate;
--2/3 get Small line segment at right 90 degress to line, crossing at closest point to input point
CREATE TABLE schema.test_blade_rotate AS
SELECT
line.id, ST_Rotate(line.geom, 1.5708, ST_Centroid(line.geom)) geom --1.5708 radians = 90 degrees
FROM
(
SELECT paired.pid id,
CASE
WHEN ( round((ST_LineLocatePoint(paired.lgeom,paired.pgeom))::numeric,2) + 1 = 1 ) THEN ST_LineSubstring(paired.lgeom, round((ST_LineLocatePoint(paired.lgeom,paired.pgeom))::numeric,2), round((ST_LineLocatePoint(paired.lgeom,paired.pgeom))::numeric,2)+ 0.01)
WHEN ( round((ST_LineLocatePoint(paired.lgeom,paired.pgeom))::numeric,2) + 1 = 2) THEN ST_LineSubstring(paired.lgeom, round((ST_LineLocatePoint(paired.lgeom,paired.pgeom))::numeric,2) - 0.01, round((ST_LineLocatePoint(paired.lgeom,paired.pgeom))::numeric,2))
ELSE ST_LineSubstring(paired.lgeom,(round((ST_LineLocatePoint(paired.lgeom,paired.pgeom))::numeric,2) - 0.01), (round((ST_LineLocatePoint(paired.lgeom,paired.pgeom))::numeric,2)+0.01))
END AS geom
FROM
(SELECT line.pid, (ST_DUMP(line.geom)).geom lgeom, (ST_DUMP(point.geom)).geom pgeom
FROM
schema.test_closestline as line,
schema.input_points as point
WHERE line.pid = point.id) as paired
) as line;
--4 POINT FROM INTERSECT OF TWO LINES:
CREATE TABLE schema.closest_point AS
SELECT
b.id, ST_CollectionExtract(ST_Intersection(a.geom, b.geom), 1) as geom
FROM schema.road_network a, schema.test_blade_rotate b