Mi è stato chiesto questo due volte, quindi mi dispiace per il ritardo. È improbabile che ciò sia considerato una soluzione concisa; L'ho scritto un po 'più in basso nella curva di apprendimento di quanto lo sia attualmente. Qualche consiglio benvenuto, anche stilistico.
--Inputs:
--walkingNetwork = Line features representing edges pedestrians can walk on
--stops = Bus stops
--NOTE: stops.geom is already constrained to be coincident with line features
--from walkingNetwork. They may be on a vertex or between two vertices.
--This series of queries returns a version of walkingNetwork, with edges split
--into separate features where they intersect stops.
CREATE TABLE tmp_lineswithstops AS (
WITH subq AS (
SELECT
ST_Line_Locate_Point(
roads.geom,
ST_ClosestPoint(roads.geom, stops.geom)
) AS LR,
rank() OVER (
PARTITION BY roads.gid
ORDER BY ST_Line_Locate_Point(
roads.geom,
ST_ClosestPoint(roads.geom, stops.geom)
)
) AS LRRank,
ST_ClosestPoint(roads.geom, stops.geom),
roads.*
FROM walkingNetwork AS roads
LEFT OUTER JOIN stops
ON ST_Distance(roads.geom, stops.geom) < 0.01
WHERE ST_Equals(ST_StartPoint(roads.geom), stops.geom) IS false
AND ST_Equals(ST_EndPoint(roads.geom), stops.geom) IS false
ORDER BY gid, LRRank
)
SELECT * FROM subq
);
-- Calculate the interior edges with a join
--If the match is null, calculate the line to the end
CREATE TABLE tmp_testsplit AS (
SELECT
l1.gid,
l1.geom,
l1.lr AS LR1,
l1.st_closestpoint AS LR1geom,
l1.lrrank AS lr1rank,
l2.lr AS LR2,
l2.st_closestpoint AS LR2geom,
l2.lrrank AS lr2rank,
CASE WHEN l2.lrrank IS NULL -- When the point is the last along the line
THEN ST_Line_Substring(l1.geom, l1.lr, 1) --get the substring line to the end
ELSE ST_Line_Substring(l1.geom, l1.lr, l2.lr) --get the substring between the two points
END AS sublinegeom
FROM tmp_lineswithstops AS l1
LEFT OUTER JOIN tmp_lineswithstops AS l2
ON l1.gid = l2.gid
AND l2.lrrank = (l1.lrrank + 1)
);
--Calculate the start to first stop edge
INSERT INTO tmp_testsplit (gid, geom, lr1, lr1geom, lr1rank, lr2, lr2geom, lr2rank, sublinegeom)
SELECT gid, geom,
0 as lr1,
ST_StartPoint(geom) as lr1geom,
0 as lr1rank,
lr AS lr2,
st_closestpoint AS lr2geom,
lrrank AS lr2rank,
ST_Line_Substring(l1.geom, 0, lr) AS sublinegeom --Start to point
FROM tmp_lineswithstops AS l1
WHERE l1.lrrank = 1;
--Now match back to the original road features, both modified and unmodified
CREATE TABLE walkingNetwork_split AS (
SELECT
roadssplit.sublinegeom,
roadssplit.gid AS sgid, --split-gid
roads.*
FROM tmp_testsplit AS roadssplit
JOIN walkingNetwork AS r
ON r.gid = roadssplit.gid
RIGHT OUTER JOIN walkingNetwork AS roads --Original edges with null if unchanged, original edges with split geom otherwise
ON roads.gid = roadssplit.gid
);
--Now update the necessary columns, and drop the temporary columns
--You'll probably need to work on your own length and cost functions
--Here I assume it's valid to just multiply the old cost by the fraction of
--the length the now-split line represents of the non-split line
UPDATE walkingNetwork_split
SET geom = sublinegeom,
lengthz = lengthz*(ST_Length(sublinegeom)/ST_Length(geom)),
walk_seconds_ft = walk_seconds_ft*(ST_Length(sublinegeom)/ST_Length(geom)),
walk_seconds_tf = walk_seconds_tf*(ST_Length(sublinegeom)/ST_Length(geom))
WHERE sublinegeom IS NOT NULL
AND ST_Length(sublinegeom) > 0;
ALTER TABLE walkingNetwork_split
DROP COLUMN sublinegeom,
DROP COLUMN sgid;
--Drop intermediate tables
--You probably could use actual temporary tables;
--I prefer to have a sanity check at each stage
DROP TABLE IF EXISTS tmp_testsplit;
DROP TABLE IF EXISTS tmp_lineswithstops;
--Assign the edges a new unique id, so we can use this as source/target columns in pgRouting
ALTER TABLE walkingNetwork_split
DROP COLUMN IF EXISTS fid;
ALTER TABLE walkingNetwork_split
ADD COLUMN fid INTEGER;
CREATE SEQUENCE roads_seq;
UPDATE walkingNetwork_split
SET fid = nextval('roads_seq');
ALTER TABLE walkingNetwork_split
ADD PRIMARY KEY ("fid");