Skip to content

Commit 7d6d339

Browse files
Merge pull request #425 from rustprooflabs/expand-road-helper-table
Routing: Improve traffic cost model, rework Documentation for Routing
2 parents ab5b7b9 + be611e1 commit 7d6d339

File tree

12 files changed

+662
-511
lines changed

12 files changed

+662
-511
lines changed

Dockerfile

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -58,10 +58,9 @@ RUN git clone --depth 1 --branch $OSM2PGSQL_BRANCH $OSM2PGSQL_REPO \
5858
RUN wget https://github.com/rustprooflabs/pgdd/releases/download/0.6.1/pgdd_0.6.1_postgis_pg18_amd64.deb \
5959
&& dpkg -i ./pgdd_0.6.1_postgis_pg18_amd64.deb \
6060
&& rm ./pgdd_0.6.1_postgis_pg18_amd64.deb \
61-
&& wget https://github.com/rustprooflabs/convert/releases/download/0.0.5/convert_0.0.5_postgis_pg18_amd64.deb \
62-
&& dpkg -i ./convert_0.0.5_postgis_pg18_amd64.deb \
63-
&& rm ./convert_0.0.5_postgis_pg18_amd64.deb
64-
61+
&& wget https://github.com/rustprooflabs/convert/releases/download/0.1.0/convert_0.1.0_postgis_pg18_amd64.deb \
62+
&& dpkg -i ./convert_0.1.0_postgis_pg18_amd64.deb \
63+
&& rm ./convert_0.1.0_postgis_pg18_amd64.deb
6564

6665

6766
WORKDIR /app

db/data/roads-us.sql

Lines changed: 32 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,36 @@
11
-- Road lookup details for generic default-
22
-- Region: United States
3-
INSERT INTO pgosm.road (region, osm_type, route_motor, route_foot, route_cycle, maxspeed)
4-
VALUES ('United States', 'motorway', True, False, False, 104.60736),
5-
('United States', 'motorway_link', True, False, False, 104.60736),
6-
('United States', 'trunk', True, False, True, 96.56064),
7-
('United States', 'trunk_link', True, False, True, 96.56064),
8-
('United States', 'primary', True, False, True, 96.56064),
9-
('United States', 'primary_link', True, False, True, 96.56064),
10-
('United States', 'secondary', True, False, True, 72.42048),
11-
('United States', 'secondary_link', True, False, True, 72.42048),
12-
('United States', 'tertiary', True, False, True, 72.42048),
13-
('United States', 'tertiary_link', True, False, True, 72.42048),
14-
('United States', 'residential', True, True, True, 40.2336),
15-
('United States', 'service', True, True, True, 40.2336),
16-
('United States', 'unclassified', True, True, True, 30),
17-
('United States', 'proposed', False, False, False, -1),
18-
('United States', 'planned', False, False, False, -1),
19-
('United States', 'path', False, True, True, 4),
20-
('United States', 'footway', False, True, False, 4),
21-
('United States', 'track', False, True, True, 2),
22-
('United States', 'pedestrian', False, True, False, 4),
23-
('United States', 'cycleway', False, True, True, 32),
24-
('United States', 'crossing', False, True, True, 2),
25-
('United States', 'platform', False, True, False, 2),
26-
('United States', 'social_path', False, True, False, 3),
27-
('United States', 'steps', False, True, False, 2),
28-
('United States', 'trailhead', False, True, True, 3)
3+
INSERT INTO pgosm.road (
4+
region, osm_type, route_motor, route_foot, route_cycle, maxspeed
5+
, traffic_penalty_normal
6+
)
7+
VALUES ('United States', 'motorway', True, False, False, 104.60736, 0.75),
8+
('United States', 'motorway_link', True, False, False, 104.60736, 0.72),
9+
('United States', 'trunk', True, False, True, 96.56064, 0.75),
10+
('United States', 'trunk_link', True, False, True, 96.56064, 0.72),
11+
('United States', 'primary', True, False, True, 96.56064, 0.6),
12+
('United States', 'primary_link', True, False, True, 96.56064, 0.6),
13+
('United States', 'secondary', True, False, True, 72.42048, 0.6),
14+
('United States', 'secondary_link', True, False, True, 72.42048, 0.6),
15+
('United States', 'tertiary', True, False, True, 72.42048, 0.6),
16+
('United States', 'tertiary_link', True, False, True, 72.42048, 0.6),
17+
('United States', 'residential', True, True, True, 40.2336, 0.95),
18+
('United States', 'service', True, True, True, 40.2336, 0.95),
19+
('United States', 'unclassified', True, True, True, 30, 0.95),
20+
('United States', 'proposed', False, False, False, -1, 1.0),
21+
('United States', 'planned', False, False, False, -1, 1.0),
22+
('United States', 'path', False, True, True, 4, 1.0),
23+
('United States', 'footway', False, True, False, 4, 1.0),
24+
('United States', 'track', False, True, True, 2, 1.0),
25+
('United States', 'pedestrian', False, True, False, 4, 1.0),
26+
('United States', 'cycleway', False, True, True, 32, 0.95),
27+
('United States', 'crossing', False, True, True, 2, 0.3),
28+
('United States', 'platform', False, True, False, 2, 0.3),
29+
('United States', 'social_path', False, True, False, 3, 0.7),
30+
('United States', 'steps', False, True, False, 2, 0.9),
31+
('United States', 'trailhead', False, True, True, 3, 0.9)
2932
-- Doing nothing allows users to safely customize this table
30-
ON CONFLICT DO NOTHING
33+
ON CONFLICT (region, osm_type) DO UPDATE
34+
SET maxspeed = EXCLUDED.maxspeed
35+
, traffic_penalty_normal = EXCLUDED.traffic_penalty_normal
3136
;

db/deploy/pgosm_road.sql

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,14 +19,18 @@ CREATE TABLE IF NOT EXISTS pgosm.road
1919
CONSTRAINT uq_pgosm_routable_code UNIQUE (region, osm_type)
2020
);
2121

22+
ALTER TABLE pgosm.road
23+
ADD COLUMN IF NOT EXISTS traffic_penalty_normal NUMERIC(3,2) NOT NULL DEFAULT 1.0
24+
;
25+
2226

2327
COMMENT ON TABLE pgosm.road IS 'Provides lookup information for road layers, generally related to routing use cases.';
2428
COMMENT ON COLUMN pgosm.road.region IS 'Allows defining different definitions based on region. Can be custom defined.';
2529
COMMENT ON COLUMN pgosm.road.osm_type IS 'Value from highway tags.';
2630
COMMENT ON COLUMN pgosm.road.route_motor IS 'Used to filter for classifications that typically allow motorized traffic.';
2731
COMMENT ON COLUMN pgosm.road.route_foot IS 'Used to filter for classifications that typically allow foot traffic.';
2832
COMMENT ON COLUMN pgosm.road.route_cycle IS 'Used to filter for classifications that typically allow bicycle traffic.';
29-
COMMENT ON COLUMN pgosm.road.maxspeed IS 'Maxspeed in km/hr';
30-
COMMENT ON COLUMN pgosm.road.maxspeed_mph IS 'Maxspeed in mph';
33+
COMMENT ON COLUMN pgosm.road.maxspeed IS 'Max speed in km/hr';
34+
COMMENT ON COLUMN pgosm.road.maxspeed_mph IS 'Max speed in mph. Calculated from max speed in km/hr.';
3135

3236
COMMIT;

db/deploy/routing_functions.sql

Lines changed: 119 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
CREATE OR REPLACE PROCEDURE {schema_name}.pgrouting_version_check()
1+
CREATE OR REPLACE PROCEDURE {schema_name}.extension_version_check()
22
LANGUAGE plpgsql
33
AS $$
44
DECLARE pgr_ver TEXT;
5+
DECLARE convert_version TEXT;
56
BEGIN
67
-- Ensure pgRouting extension exists with at least pgRouting 4.0
78
IF NOT EXISTS (
@@ -21,10 +22,28 @@ BEGIN
2122
pgr_ver;
2223
END IF;
2324

25+
-- Ensure convert extension exists with at least convert 0.1.0
26+
IF NOT EXISTS (
27+
SELECT 1 FROM pg_extension WHERE extname = 'convert'
28+
) THEN
29+
RAISE EXCEPTION
30+
'The convert extension is not installed. Version >= 0.1.0 required.';
31+
END IF;
32+
33+
SELECT extversion INTO convert_version FROM pg_extension WHERE extname = 'convert'
34+
;
35+
36+
-- Enforce minimum version
37+
IF string_to_array(convert_version, '.')::INT[] < ARRAY[0,1,0] THEN
38+
RAISE EXCEPTION
39+
'Convert version % detected. Version >= 0.1.0 required.',
40+
convert_version;
41+
END IF;
42+
2443
END $$;
2544

2645

27-
COMMENT ON PROCEDURE {schema_name}.pgrouting_version_check IS 'Ensures appropriate pgRouting extension is installed with an appropriate version.';
46+
COMMENT ON PROCEDURE {schema_name}.extension_version_check IS 'Ensures pgRouting and convert extensions are installed with appropriate versions.';
2847

2948

3049

@@ -229,7 +248,7 @@ LANGUAGE plpgsql
229248
AS $$
230249
BEGIN
231250

232-
CALL {schema_name}.pgrouting_version_check();
251+
CALL {schema_name}.extension_version_check();
233252

234253
--Create edges table for input to routing_prepare_edge_network procedure
235254
DROP TABLE IF EXISTS route_edge_input;
@@ -263,68 +282,75 @@ BEGIN
263282
, tunnel TEXT
264283
, bridge TEXT
265284
, access TEXT
266-
, geom GEOMETRY(LINESTRING)
285+
, cost_length DOUBLE PRECISION
286+
, cost_length_forward DOUBLE PRECISION NULL
287+
, cost_length_reverse DOUBLE PRECISION NULL
288+
, cost_motor_forward_s DOUBLE PRECISION NULL
289+
, cost_motor_reverse_s DOUBLE PRECISION NULL
290+
, geom GEOMETRY(LINESTRING) NOT NULL
267291
--, UNIQUE (osm_id, sub_id) -- Currently not enforceable... dups exist...
268292
);
269293

270294
INSERT INTO {schema_name}.routing_road_edge (
271295
osm_id, sub_id, osm_type, name, ref, maxspeed, oneway, layer, tunnel, bridge, major
272296
, route_foot, route_cycle, route_motor, access
273-
, geom
297+
, cost_length
298+
, geom -- Through geom is in initial query
299+
-- Forward/reverse added next
300+
, cost_length_forward, cost_length_reverse
301+
-- Travel times added last.
302+
, cost_motor_forward_s, cost_motor_reverse_s
274303
)
304+
WITH add_cost AS (
275305
SELECT re.osm_id, re.sub_id
276306
, r.osm_type, r.name, r.ref, r.maxspeed
277307
, r.oneway, re.layer, r.tunnel, r.bridge, r.major
278308
, r.route_foot, r.route_cycle, r.route_motor, r.access
309+
, ST_Length(ST_Transform(re.geom, 4326)::GEOGRAPHY) AS cost_length
279310
, re.geom
280311
FROM route_edges_output re
281312
INNER JOIN {schema_name}.road_line r ON re.osm_id = r.osm_id
282-
ORDER BY re.geom
313+
), add_forward_reverse AS (
314+
SELECT a.*
315+
, CASE WHEN a.oneway IN (0, 1) OR a.oneway IS NULL
316+
THEN a.cost_length
317+
WHEN a.oneway = -1
318+
THEN -1 * a.cost_length
319+
END AS cost_length_forward
320+
, CASE WHEN a.oneway IN (0, -1) OR a.oneway IS NULL
321+
THEN a.cost_length
322+
WHEN a.oneway = 1
323+
THEN -1 * a.cost_length
324+
END AS cost_length_reverse
325+
FROM add_cost a
326+
)
327+
SELECT a.*
328+
, convert.ttt_meters_km_hr_to_seconds(
329+
a.cost_length_forward, COALESCE(a.maxspeed, r.maxspeed) * r.traffic_penalty_normal
330+
) AS cost_motor_forward_s
331+
, convert.ttt_meters_km_hr_to_seconds(
332+
a.cost_length_reverse, COALESCE(a.maxspeed, r.maxspeed) * r.traffic_penalty_normal
333+
) AS cost_motor_reverse_s
334+
FROM add_forward_reverse a
335+
INNER JOIN pgosm.road r ON a.osm_type = r.osm_type
336+
ORDER BY a.geom
283337
;
284338

285-
CREATE INDEX gix_{schema_name}_routing_road_edge
339+
CREATE INDEX gix_{schema_name}_routing_road_edge_geom
286340
ON {schema_name}.routing_road_edge
287341
USING GIST (geom)
288342
;
289-
290-
RAISE NOTICE 'Created table {schema_name}.routing_road_edge';
291-
292-
293-
ALTER TABLE {schema_name}.routing_road_edge
294-
ADD cost_length DOUBLE PRECISION NULL;
295-
296-
UPDATE {schema_name}.routing_road_edge
297-
SET cost_length = ST_Length(ST_Transform(geom, 4326)::GEOGRAPHY)
343+
CREATE INDEX ix_{schema_name}_routing_road_edge_vertex_id_source
344+
ON {schema_name}.routing_road_edge (vertex_id_source)
345+
;
346+
CREATE INDEX ix_{schema_name}_routing_road_edge_vertex_id_target
347+
ON {schema_name}.routing_road_edge (vertex_id_target)
298348
;
299-
300-
COMMENT ON COLUMN {schema_name}.routing_road_edge.cost_length IS 'Length based cost calculated using GEOGRAPHY for accurate length.';
301349

302350

303-
-- Add forward cost column, enforcing oneway restrictions
304-
ALTER TABLE {schema_name}.routing_road_edge
305-
ADD cost_length_forward NUMERIC
306-
GENERATED ALWAYS AS (
307-
CASE WHEN oneway IN (0, 1) OR oneway IS NULL
308-
THEN cost_length
309-
WHEN oneway = -1
310-
THEN -1 * cost_length
311-
END
312-
)
313-
STORED
314-
;
351+
RAISE NOTICE 'Created table {schema_name}.routing_road_edge';
315352

316-
-- Add reverse cost column, enforcing oneway restrictions
317-
ALTER TABLE {schema_name}.routing_road_edge
318-
ADD cost_length_reverse NUMERIC
319-
GENERATED ALWAYS AS (
320-
CASE WHEN oneway IN (0, -1) OR oneway IS NULL
321-
THEN cost_length
322-
WHEN oneway = 1
323-
THEN -1 * cost_length
324-
END
325-
)
326-
STORED
327-
;
353+
COMMENT ON COLUMN {schema_name}.routing_road_edge.cost_length IS 'Length based cost calculated using GEOGRAPHY for accurate length.';
328354

329355
COMMENT ON COLUMN {schema_name}.routing_road_edge.cost_length_forward IS 'Length based cost for forward travel with directed routing. Based on cost_length value.';
330356
COMMENT ON COLUMN {schema_name}.routing_road_edge.cost_length_reverse IS 'Length based cost for reverse travel with directed routing. Based on cost_length value.';
@@ -392,7 +418,7 @@ LANGUAGE plpgsql
392418
AS $$
393419
BEGIN
394420

395-
CALL {schema_name}.pgrouting_version_check();
421+
CALL {schema_name}.extension_version_check();
396422

397423
--Create edges table for input to routing_prepare_edge_network procedure
398424
DROP TABLE IF EXISTS route_edge_input;
@@ -520,3 +546,53 @@ END $$;
520546

521547

522548
COMMENT ON PROCEDURE {schema_name}.routing_prepare_water_network IS 'Creates the {schema_name}.routing_water_edge and {schema_name}.routing_water_vertex from the {schema_name}.water_line input data';
549+
550+
551+
552+
553+
CREATE OR REPLACE FUNCTION {schema_name}.route_motor_travel_time(
554+
route_vertex_id_start BIGINT, route_vertex_id_end BIGINT
555+
)
556+
RETURNS TABLE (segments BIGINT, vertex_ids BIGINT[], edge_ids BIGINT[]
557+
, total_cost_seconds DOUBLE PRECISION, geom GEOMETRY
558+
)
559+
LANGUAGE plpgsql
560+
ROWS 5
561+
AS $function$
562+
563+
BEGIN
564+
565+
RETURN QUERY
566+
WITH route_steps AS (
567+
SELECT d.node AS vertex_id
568+
, d.edge AS edge_id
569+
, d.cost
570+
, n.geom AS node_geom, e.geom AS edge_geom
571+
FROM pgr_dijkstra(
572+
'SELECT e.edge_id AS id
573+
, e.vertex_id_source AS source
574+
, e.vertex_id_target AS target
575+
, e.cost_motor_forward_s AS cost
576+
, e.cost_motor_reverse_s AS reverse_cost
577+
, e.geom
578+
FROM {schema_name}.routing_road_edge e
579+
WHERE e.route_motor
580+
',
581+
route_vertex_id_start, route_vertex_id_end, directed := True
582+
) d
583+
INNER JOIN {schema_name}.routing_road_vertex n ON d.node = n.id
584+
LEFT JOIN {schema_name}.routing_road_edge e ON d.edge = e.edge_id
585+
)
586+
SELECT COUNT(*) AS segments
587+
, ARRAY_AGG(vertex_id) AS vertex_ids
588+
, ARRAY_AGG(edge_id) AS edge_ids
589+
, SUM(cost) AS total_cost_seconds
590+
, ST_Collect(edge_geom) AS geom
591+
FROM route_steps
592+
;
593+
END
594+
$function$
595+
;
596+
597+
COMMENT ON FUNCTION {schema_name}.route_motor_travel_time IS 'Computes best route using ideal travel time costs. Does not account for traffic.';
598+

docs/src/SUMMARY.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,11 @@
1616
- [Data Files](./data-files.md)
1717
- [Query examples](./query.md)
1818
- [Routing](./routing.md)
19-
- [pgRouting 3](./routing-3.md)
20-
- [pgRouting 4](./routing-4.md)
19+
- [Routing Roads](./routing-road.md)
20+
- [Routing Water](./routing-water.md)
21+
- [Routing Process](./routing-process.md)
22+
- [Routing Roads: Legacy (pgRouting 3)](./routing-3.md)
23+
2124
- [Processing Time](./performance.md)
2225

2326
# Production Use

docs/src/readme.md

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -40,20 +40,16 @@ passed along to osm2pgsql, with post-processing steps creating indexes,
4040
constraints and comments.
4141

4242

43-
## Versions Supported
43+
## Minimum Versions Supported
4444

45-
Minimum versions supported:
46-
47-
* Postgres 12
48-
* PostGIS 3.0
49-
50-
This project will attempt, but not guarantee, to support PostgreSQL 12 until it
51-
reaches it EOL support.
45+
This project will attempt, but not guarantee, to support each major PostgreSQL version
46+
until it reaches it EOL support.
5247

5348
The Docker image is pinned to osm2pgsql's `master` branch. Users of the Docker image
5449
naturally use the latest version of osm2pgsql at the time the Docker image was created.
5550

56-
This project runs entirely in Docker, optionally connecting to an external Postgres instance.
51+
This project runs entirely in Docker, optionally connecting to an external
52+
Postgres instance at runtime.
5753
It should work on any typical OS able to run Docker.
5854

5955

docs/src/routing-3.md

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,22 @@
1-
# Routing with pgRouting 3
1+
# Routing Roads: Legacy (pgRouting 3)
22

3-
If you are using a pgRouting 4.0 or later see [Routing with pgRouting 4](./routing-4.md).
3+
## Plan your Upgrade!
44

5-
New development in PgOSM Flex will focus support on pgRouting 4.0 support
6-
per the Versions Supported section [in the About page](./readme.md).
7-
[PgOSM Flex 1.1.2](https://github.com/rustprooflabs/pgosm-flex/releases/tag/1.1.2)
8-
simplified and improved performance in the routing data preparation.
5+
This page is a legacy documentation page for versions of pgRouting
6+
older than 4.0. This process requires more manual effort to setup and
7+
results in lower-quality routing networks compared to the
8+
[latest procedures](./routing-road.md).
99

10+
It is recommended to use pgRouting 4.0 or later, see [the latest Routing Roads](./routing-road.md)
11+
documentation.
1012

11-
> ⚠️ This page will remain in the PgOSM documentation through at least 2026 to ensure
13+
> ⚠️ This page is no longer maintained.
14+
>
15+
> This page will remain in the PgOSM documentation for the foreseeable future to ensure
1216
> continuity for a transition to pgRouting 4.0.
13-
> There will not be improvements made to these legacy instructions.
17+
18+
19+
## Getting Started
1420

1521
Create the `pgRouting` extension.
1622

0 commit comments

Comments
 (0)