Ich wurde jetzt zweimal danach gefragt, also entschuldige die Verzögerung. Es ist unwahrscheinlich, dass dies als knappe Lösung angesehen wird. Ich habe es geschrieben, als ich etwas weiter unten in der Lernkurve war als ich es derzeit bin. Alle Tipps sind willkommen, auch stilistische.
--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");