-- Ensure pgrouting is installed CREATE EXTENSION IF NOT EXISTS pgrouting; -- Closest points on road to the STM point SELECT r.osm_id, r.highway, ST_LineLocatePoint(r.way, np.way), ST_LineInterpolatePoint(r.way, ST_LineLocatePoint(r.way, np.way)), np.way <-> r.way AS distance_from_start, np.way, r.way FROM golden.natural_point np INNER JOIN golden.road_line r ON ST_DWithin(np.way, r.way, 50) WHERE np.natural = 'peak' AND np.name = 'South Table Mountain' ORDER BY np.way <-> r.way ; -- Create my_route table -- with start point for routing DROP TABLE IF EXISTS my_route; CREATE TABLE my_route AS SELECT np.way AS start_point FROM golden.natural_point np WHERE np.name = 'South Table Mountain' ; -- Update my_route to add end point column ALTER TABLE my_route ADD end_point GEOMETRY (POINT, 3857); -- Set end point UPDATE my_route r SET end_point = ST_Centroid(b.way) FROM golden.building_polygon b WHERE b.name = 'Pangea Coffee Roasters' ; -- Visualize start/end points SELECT * FROM my_route; -- Costs models for Routing: -- Time, Distance, Scenic -- Use helper table for speed costs and filtering SELECT ld.subclass, r.* FROM pgosm.routable r INNER JOIN pgosm.layer_detail ld ON ld.code = r.code ; /* * Roads need prep for routing */ DROP TABLE IF EXISTS pgosm.routing_roads; -- Create roads subset filtered to sub-region. --- Select basic attributes CREATE TABLE pgosm.routing_roads AS SELECT r.osm_id AS id, r.name, r.traffic, r.highway, r.ref, r.code, ST_Length(r.way)::DOUBLE PRECISION AS cost_length, r.way AS the_geom FROM ( -- Create single Bounding Box for a specific region SELECT ST_Envelope(ST_Union(way)) AS way FROM golden.boundary_polygon bp WHERE bp.name = 'Golden' ) b INNER JOIN golden.road_line r ON r.way && b.way INNER JOIN pgosm.routable rbl ON r.code = rbl.code -- Routes for non-motorized traffic AND rbl.route_foot AND NOT rbl.route_motor ; CREATE INDEX GIX_pgosm_routing_roads ON pgosm.routing_roads USING GIST (the_geom); -- Ensure pgrouting-generated tables don't already exist DROP TABLE IF EXISTS pgosm.routing_roads_noded; DROP TABLE IF EXISTS pgosm.routing_roads_noded_vertices_pgr; -- Run pgrouting steps on custom roads layer -- Creates _noded -- Expensive step! SELECT pgr_nodeNetwork('pgosm.routing_roads', 1); -- Creates
_noded_vertices_pgr -- Expensive step! SELECT pgr_createTopology('pgosm.routing_roads_noded', 0.1); -- Analyzes
_noded_vertices_pgr -- Populates `cnt` and `chk` -- chk > 0 indicates vertix might have problem SELECT pgr_analyzegraph('pgosm.routing_roads_noded', 0.1); -- Noded versions have more rows SELECT 'original' AS src, COUNT(*) FROM pgosm.routing_roads UNION SELECT 'noded' AS src, COUNT(*) FROM pgosm.routing_roads_noded ; -- old_id links to osm_id SELECT * FROM pgosm.routing_roads_noded WHERE old_id = 29276956; SELECT osm_id, highway, way FROM golden.road_line WHERE osm_id = 29276956; -- Update
_noded with extra attributes for routing ALTER TABLE pgosm.routing_roads_noded ADD cost_length DOUBLE PRECISION NULL; ALTER TABLE pgosm.routing_roads_noded ADD cost_minutes DOUBLE PRECISION NULL; ALTER TABLE pgosm.routing_roads_noded ADD code TEXT NULL; -- Update noded with costs and sub-class code UPDATE pgosm.routing_roads_noded rn SET code = r.code, cost_length = ST_Length(rn.the_geom)::DOUBLE PRECISION, -- Math to calculate cost (minutes) based on length (meters) -- and speed (MPH) for given road classification cost_minutes = ST_Length(rn.the_geom)::DOUBLE PRECISION / (rbl.max_speed / 60 * 1.609344 * 1000) FROM pgosm.routing_roads r INNER JOIN pgosm.routable rbl ON r.code = rbl.code -- Maps to original OSM data osm_id WHERE rn.old_id = r.id ; -- Clear out unroutable roads -- Check your data before deleting it! ;-) DELETE FROM pgosm.routing_roads_noded WHERE source IS NULL; DELETE FROM pgosm.routing_roads_noded WHERE cost_minutes IS NULL; /* * Roads are now routing-ready! */ -- Visualize options for Route End Point --
_noded_vertices_pgr has the points our -- road data can route to SELECT r.id, a.end_point, r.the_geom FROM my_route a INNER JOIN pgosm.routing_roads_noded_vertices_pgr r ON ST_DWithin(a.end_point, r.the_geom, 200) ORDER BY a.end_point <-> r.the_geom ASC LIMIT 15 ; -- Add columns for start/end nodes (noded vertices for roads) ALTER TABLE my_route ADD start_node_id INT; ALTER TABLE my_route ADD end_node_id INT; SELECT * FROM my_route; -- Update my_route with Start Node ID UPDATE my_route SET start_node_id = o.id FROM ( SELECT r.id FROM my_route a INNER JOIN pgosm.routing_roads_noded_vertices_pgr r ON ST_DWithin(a.start_point, r.the_geom, 200) ORDER BY a.start_point <-> r.the_geom ASC LIMIT 1 ) o ; -- Update my_route with End Node ID UPDATE my_route SET end_node_id = o.id FROM ( SELECT r.id FROM my_route a INNER JOIN pgosm.routing_roads_noded_vertices_pgr r ON ST_DWithin(a.end_point, r.the_geom, 200) ORDER BY a.end_point <-> r.the_geom ASC LIMIT 1 ) o ; -- Join my_route to routing start/end details SELECT a.*, b.the_geom AS route_start, c.the_geom AS route_end FROM my_route a INNER JOIN pgosm.routing_roads_noded_vertices_pgr b ON a.start_node_id = b.id INNER JOIN pgosm.routing_roads_noded_vertices_pgr c ON a.end_node_id = c.id ; -- pgr_dijkstra() to route -- Provides step-by-step route with costs SELECT p.start_point, p.end_point, p.start_node_id, p.end_node_id, ld.subclass, rbl.max_speed, c.cost, c.agg_cost, n.the_geom FROM my_route p INNER JOIN pgr_dijkstra('SELECT id, source, target, cost_minutes AS cost FROM pgosm.routing_roads_noded ', p.start_node_id, p.end_node_id, False) c ON True INNER JOIN pgosm.routing_roads_noded n ON c.edge = n.id INNER JOIN pgosm.layer_detail ld ON n.code = ld.code INNER JOIN pgosm.routable rbl ON ld.code = rbl.code ORDER BY path_seq ; ALTER TABLE my_route ADD route GEOMETRY(MULTILINESTRING, 3857); ALTER TABLE my_route ADD cost_minutes NUMERIC(12,2); ALTER TABLE my_route ADD cost_length NUMERIC(12,2); -- Wrap into CTE for UPDATE WITH a AS ( SELECT SUM(c.cost) AS agg_cost, ST_Collect(n.the_geom) AS way FROM my_route p INNER JOIN pgr_dijkstra('SELECT id, source, target, cost_minutes AS cost FROM pgosm.routing_roads_noded ', p.start_node_id, p.end_node_id, False) c ON True INNER JOIN pgosm.routing_roads_noded n ON c.edge = n.id ) UPDATE my_route SET cost_minutes = agg_cost, route = way, cost_length = ST_Length(way) FROM a ; SELECT * FROM my_route; SELECT * FROM public.layer_styles; -- Visualize in QGIS