Correct directional road rendering, and include all roads with null data in obs layer

This commit is contained in:
Paul Bienkowski 2021-10-31 20:50:09 +01:00
parent f2fa806cab
commit e3ec5ce1f9
6 changed files with 1240 additions and 58 deletions

View file

@ -50,8 +50,12 @@ local MOTORWAY_TYPES = {
"motorway_link", "motorway_link",
} }
local ONEWAY_YES = {"yes", "true", "1"}
local ONEWAY_REVERSE = {"reverse", "-1"}
local roads = osm2pgsql.define_way_table('road', { local roads = osm2pgsql.define_way_table('road', {
{ column = 'zone', type = 'text', sql_type="zone_type" }, { column = 'zone', type = 'text', sql_type="zone_type" },
{ column = 'directionality', type = 'int' },
{ column = 'name', type = 'text' }, { column = 'name', type = 'text' },
{ column = 'geometry', type = 'linestring' }, { column = 'geometry', type = 'linestring' },
{ column = 'tags', type = 'hstore' }, { column = 'tags', type = 'hstore' },
@ -87,10 +91,18 @@ function osm2pgsql.process_way(object)
end end
end end
local directionality = 0
if contains(ONEWAY_YES, tags["oneway"]) then
directionality = 1
elseif contains(ONEWAY_REVERSE, tags["oneway"]) then
directionality = -1
end
roads:add_row({ roads:add_row({
geometry = { create = 'linear' }, geom = { create = 'linear' },
name = tags.name, name = tags.name,
zone = zone, zone = zone,
directionality = directionality,
tags = tags, tags = tags,
}) })
end end

@ -1 +1 @@
Subproject commit 767b7166105b2446c3b4e0334222052114b0ebae Subproject commit e67a2a98f7d8d53c687bc44d72774b5429b049c3

View file

@ -1,61 +1,140 @@
import _ from 'lodash' import _ from 'lodash'
import bright from './bright.json' import bright from './bright.json'
import positron from './positron.json'
function getRoadsStyle(sourceUrl = "http://localhost:3002/data/v3.json") { function addRoadsStyle(style, sourceUrl = "http://localhost:3002/data/v3.json") {
return { style.sources.obs = {"type": "vector", "url": sourceUrl}
"version": 8,
"name": "OBS Roads", // insert before "road_oneway" layer
"sources": { let idx = style.layers.findIndex(l => l.id === 'road_oneway')
"obs": {"type": "vector", "url": sourceUrl} if (idx === -1) {
idx = style.layers.length
}
style.layers.splice(idx, 0, {
"id": "obs",
"type": "line",
"source": "obs",
"source-layer": "obs_roads",
"layout": {
"line-cap": "round",
"line-join": "round"
}, },
"layers": [ "paint": {
{ "line-width": [
"id": "obs", "interpolate",
"type": "line", ["exponential", 1.5],
"source": "obs", ["zoom"],
"source-layer": "obs_roads", 12,
"layout": {"line-cap": "round", "line-join": "round"}, 1,
"paint": { 17,
"line-width": {"stops": [[14, 2], [17, 8]]}, [
"line-color": [ "case",
"interpolate-hcl", [
["linear"], "!",
["get", "distance_overtaker_mean"], [
1, "to-boolean",
"rgba(255, 0, 0, 1)", [
1.3, "get",
"rgba(255, 200, 0, 1)", "distance_overtaker_mean"
1.5, ]
"rgba(67, 200, 0, 1)", ]
1.7,
"rgba(67, 150, 0, 1)"
], ],
"line-opacity": 1, 2,
"line-offset": {"stops": [[14, 1], [17, 7]]} 6
} ]
} ],
], "line-color": [
"id": "obs-roads" "case",
} [
} "!",
[
function mergeStyles(baseStyle, ...extensions) { "to-boolean",
const style = _.cloneDeep(baseStyle) [
for (const extension of extensions) { "get",
for (const key of Object.keys(extension)) { "distance_overtaker_mean"
if (['sources', 'layers', 'id', 'name', 'version'].includes(key)) { ]
continue ]
} ],
"#ABC",
throw new Error(`cannot use style ${extension.id ?? extension.name} as extension style, it defines ${key}`) [
} "interpolate-hcl",
style.sources = {...style.sources, ...extension.sources} ["linear"],
style.layers = [...style.layers, ...extension.layers] [
} "get",
"distance_overtaker_mean"
],
1,
"rgba(255, 0, 0, 1)",
1.3,
"rgba(255, 200, 0, 1)",
1.5,
"rgba(67, 200, 0, 1)",
1.7,
"rgba(67, 150, 0, 1)"
]
],
"line-opacity": [
"interpolate",
["linear"],
["zoom"],
12,
0,
13,
[
"case",
[
"!",
[
"to-boolean",
[
"get",
"distance_overtaker_mean"
]
]
],
0,
1
],
14,
[
"case",
[
"!",
[
"to-boolean",
[
"get",
"distance_overtaker_mean"
]
]
],
0,
1
],
15,
1
],
"line-offset": [
"interpolate",
["exponential", 1.5],
["zoom"],
12,
["get", "offset_direction"],
19,
[
"*",
["get", "offset_direction"],
8
]
]
},
"minzoom": 12
})
return style return style
} }
export const basemap = bright export const basemap = bright
export const obsRoads = (sourceUrl) => mergeStyles(basemap, getRoadsStyle(sourceUrl)) export const obsRoads = (sourceUrl) => addRoadsStyle(_.cloneDeep(positron), sourceUrl)

File diff suppressed because it is too large Load diff

View file

@ -1,15 +1,33 @@
CREATE OR REPLACE FUNCTION layer_obs_roads(bbox geometry, zoom_level int) CREATE OR REPLACE FUNCTION layer_obs_roads(bbox geometry, zoom_level int)
RETURNS TABLE(way_id bigint, geometry geometry, distance_overtaker_mean float, direction int) AS $$ RETURNS TABLE(
way_id bigint,
geometry geometry,
distance_overtaker_mean float,
distance_overtaker_min float,
distance_overtaker_max float,
distance_overtaker_median float,
distance_overtaker_array float[],
overtaking_event_count int,
direction int,
offset_direction int
) AS $$
SELECT SELECT
road.way_id::bigint as way_id, road.way_id::bigint as way_id,
road.geometry as geometry, road.geometry as geometry,
avg(distance_overtaker) as distance_overtaker_mean, avg(distance_overtaker) as distance_overtaker_mean,
r.dir as direction min(distance_overtaker) as distance_overtaker_min,
max(distance_overtaker) as distance_overtaker_max,
PERCENTILE_CONT(0.5) WITHIN GROUP (ORDER BY distance_overtaker) as distance_overtaker_median,
array_agg(distance_overtaker) as distance_overtaker_array,
count(overtaking_event.id)::int as distance_overtaker_count,
r.dir as direction,
case when road.directionality = 0 then r.dir else 0 end as offset_direction
FROM road FROM road
JOIN overtaking_event on road.way_id = overtaking_event.way_id FULL OUTER JOIN (VALUES (1, TRUE), (-1, FALSE)) AS r(dir, rev) ON (road.directionality = 0 or road.directionality = r.dir)
JOIN (VALUES (1, TRUE), (-1, FALSE)) AS r(dir, rev) ON overtaking_event.direction_reversed = r.rev FULL OUTER JOIN overtaking_event ON (road.way_id = overtaking_event.way_id and overtaking_event.direction_reversed = r.rev)
-- WHERE road.name = 'Schlierbergstraße'
WHERE road.geometry && bbox WHERE road.geometry && bbox
GROUP BY road.way_id, road.geometry, direction; GROUP BY road.way_id, road.geometry, road.directionality, direction;
$$ LANGUAGE SQL IMMUTABLE; $$ LANGUAGE SQL IMMUTABLE;

View file

@ -4,12 +4,24 @@ layer:
Road segment statistics for OBS events Road segment statistics for OBS events
buffer_size: 4 buffer_size: 4
fields: fields:
distance_overtaker_min: |
Overtaker minimum distance in meters.
distance_overtaker_max: |
Overtaker maximum distance in meters.
distance_overtaker_mean: | distance_overtaker_mean: |
Overtaker mean distance in meters. Overtaker mean distance in meters.
distance_overtaker_median: |
Overtaker median distance in meters.
distance_overtaker_array: |
All overtaker distance values in meters.
overtaking_event_count: |
Number of overtaking events.
direction: | direction: |
Contains -1 for events while going along the way backwards, 1 for Contains -1 for events while going along the way backwards, 1 for
forwards. Each road is emitted twice, if it has data for both directions, forwards. Each road is emitted twice, if it has data for both directions,
even if it is oneway. even if it is oneway.
offset_direction: |
Factor for offset to shift the line to the driving side. One of -1, 0, 1.
defaults: defaults:
srs: EPSG:3785 srs: EPSG:3785
datasource: datasource:
@ -17,7 +29,21 @@ layer:
geometry_field: geometry geometry_field: geometry
key_field: way_id key_field: way_id
key_field_as_attribute: no key_field_as_attribute: no
query: (SELECT way_id, geometry, distance_overtaker_mean, direction FROM layer_obs_roads(!bbox!, z(!scale_denominator!))) AS t query: |
(
SELECT
way_id,
geometry,
distance_overtaker_mean,
distance_overtaker_min,
distance_overtaker_max,
distance_overtaker_median,
distance_overtaker_array,
overtaking_event_count,
direction,
offset_direction
FROM layer_obs_roads(!bbox!, z(!scale_denominator!))
) AS t
schema: schema:
- ./layer.sql - ./layer.sql