diff --git a/api/obs/api/routes/mapdetails.py b/api/obs/api/routes/mapdetails.py
index 51aa7df..5b0eba3 100644
--- a/api/obs/api/routes/mapdetails.py
+++ b/api/obs/api/routes/mapdetails.py
@@ -112,6 +112,7 @@ async def mapdetails_road(req):
"histogram": {
"bins": [None if math.isinf(b) else b for b in bins.tolist()],
"counts": hist.tolist(),
+ "zone": road.zone
},
"values": list(map(rounder, arr.tolist())),
}
diff --git a/frontend/src/mapstyles/index.js b/frontend/src/mapstyles/index.js
index c3a62fb..30067d0 100644
--- a/frontend/src/mapstyles/index.js
+++ b/frontend/src/mapstyles/index.js
@@ -44,27 +44,74 @@ export const reds = [
]
export function colorByCount(attribute = 'event_count', maxCount, colormap = viridis) {
- return colormapToScale(colormap, ['case', ['to-boolean', ['get', attribute]], ['get', attribute], 0], 0, maxCount)
+ return colormapToScale(colormap, ['case', isValidAttribute(attribute), ['get', attribute], 0], 0, maxCount)
}
-export function colorByDistance(attribute = 'distance_overtaker_mean', fallback = '#ABC') {
+var steps = {'rural': [1.6,1.8,2.0,2.2],
+ 'urban': [1.1,1.3,1.5,1.7]}
+
+export function isValidAttribute(attribute) {
+ if (attribute.endsWith('zone')) {
+ return ['in', ['get', attribute], ['literal', ['rural', 'urban']]]
+ }
+ return ['to-boolean', ['get', attribute]]
+}
+
+export function borderByZone() {
+ return ["match", ['get', 'zone'],
+ "rural", "cyan",
+ "urban", "blue",
+ "purple"
+ ]
+}
+
+export function colorByDistance(attribute = 'distance_overtaker_mean', fallback = '#ABC', zone='urban') {
+
return [
'case',
- ['!', ['to-boolean', ['get', attribute]]],
+ ['!', isValidAttribute(attribute)],
fallback,
+ ["match", ['get', 'zone'], "rural",
[
'step',
['get', attribute],
'rgba(150, 0, 0, 1)',
- 1.1,
+ steps['rural'][0],
'rgba(255, 0, 0, 1)',
- 1.3,
+ steps['rural'][1],
'rgba(255, 220, 0, 1)',
- 1.5,
+ steps['rural'][2],
'rgba(67, 200, 0, 1)',
- 1.7,
+ steps['rural'][3],
+ 'rgba(67, 150, 0, 1)',
+ ], "urban",
+ [
+ 'step',
+ ['get', attribute],
+ 'rgba(150, 0, 0, 1)',
+ steps['urban'][0],
+ 'rgba(255, 0, 0, 1)',
+ steps['urban'][1],
+ 'rgba(255, 220, 0, 1)',
+ steps['urban'][2],
+ 'rgba(67, 200, 0, 1)',
+ steps['urban'][3],
'rgba(67, 150, 0, 1)',
],
+ [
+ 'step',
+ ['get', attribute],
+ 'rgba(150, 0, 0, 1)',
+ steps['urban'][0],
+ 'rgba(255, 0, 0, 1)',
+ steps['urban'][1],
+ 'rgba(255, 220, 0, 1)',
+ steps['urban'][2],
+ 'rgba(67, 200, 0, 1)',
+ steps['urban'][3],
+ 'rgba(67, 150, 0, 1)',
+ ]
+ ]
]
}
diff --git a/frontend/src/pages/MapPage/LayerSidebar.tsx b/frontend/src/pages/MapPage/LayerSidebar.tsx
index 9e38026..46167b2 100644
--- a/frontend/src/pages/MapPage/LayerSidebar.tsx
+++ b/frontend/src/pages/MapPage/LayerSidebar.tsx
@@ -1,7 +1,7 @@
import React from 'react'
import _ from 'lodash'
import {connect} from 'react-redux'
-import {List, Select, Input, Divider, Checkbox, Header} from 'semantic-ui-react'
+import {List, Select, Input, Divider, Label, Checkbox, Header} from 'semantic-ui-react'
import {
MapConfig,
@@ -23,6 +23,7 @@ const ROAD_ATTRIBUTE_OPTIONS = [
{value: 'distance_overtaker_median', key: 'distance_overtaker_median', text: 'Overtaker distance median'},
{value: 'overtaking_event_count', key: 'overtaking_event_count', text: 'Event count'},
{value: 'usage_count', key: 'usage_count', text: 'Usage count'},
+ {value: 'zone', key: 'zone', text: 'Overtaking distance zone'}
]
function LayerSidebar({
@@ -96,10 +97,24 @@ function LayerSidebar({
>
) :
- (
+ attribute.endsWith('zone') ? (
+ <>
-
-
+
+
+ >
+ ) :
+ (
+ <>
+
+ Urban
+
+
+
+ Rural
+
+
+ >
)}
>
)}
@@ -120,7 +135,12 @@ function LayerSidebar({
{showEvents && (
<>
-
+ Urban
+
+
+
+ Rural
+
>
)}
diff --git a/frontend/src/pages/MapPage/RoadInfo.tsx b/frontend/src/pages/MapPage/RoadInfo.tsx
index 5d2758e..871279a 100644
--- a/frontend/src/pages/MapPage/RoadInfo.tsx
+++ b/frontend/src/pages/MapPage/RoadInfo.tsx
@@ -9,7 +9,7 @@ import {Chart} from 'components'
import {pairwise} from 'utils'
import api from 'api'
-import {colorByDistance} from 'mapstyles'
+import {colorByDistance, borderByZone} from 'mapstyles'
import styles from './styles.module.less'
@@ -34,7 +34,7 @@ const LABELS = {
max: 'Maximum',
mean: 'Average',
}
-const ZONE_COLORS = {urban: 'olive', rural: 'brown', motorway: 'purple'}
+const ZONE_COLORS = {urban: 'blue', rural: 'cyan', motorway: 'purple'}
const CARDINAL_DIRECTIONS = ['north', 'north-east', 'east', 'south-east', 'south', 'south-west', 'west', 'north-west']
const getCardinalDirection = (bearing) =>
bearing == null
@@ -75,14 +75,15 @@ function RoadStatsTable({data}) {
)
}
-function HistogramChart({bins, counts}) {
+function HistogramChart({bins, counts, zone}) {
const diff = bins[1] - bins[0]
+ const colortype = zone=="rural" ? 3:5;
const data = _.zip(
bins.slice(0, bins.length - 1).map((v) => v + diff / 2),
counts
).map((value) => ({
value,
- itemStyle: {color: selectFromColorMap(colorByDistance()[3].slice(2), value[0])},
+ itemStyle: {color: selectFromColorMap(colorByDistance()[3][colortype].slice(2), value[0]),},
}))
return (
diff --git a/frontend/src/pages/MapPage/index.tsx b/frontend/src/pages/MapPage/index.tsx
index 9d30847..a3196dd 100644
--- a/frontend/src/pages/MapPage/index.tsx
+++ b/frontend/src/pages/MapPage/index.tsx
@@ -6,7 +6,7 @@ import produce from 'immer'
import {Page, Map} from 'components'
import {useConfig} from 'config'
-import {colorByDistance, colorByCount, reds} from 'mapstyles'
+import {colorByDistance, colorByCount, borderByZone, reds, isValidAttribute} from 'mapstyles'
import {useMapConfig} from 'reducers/mapConfig'
import RoadInfo from './RoadInfo'
@@ -40,20 +40,23 @@ const untaggedRoadsLayer = {
minzoom: 12,
}
+const getUntaggedRoadsLayer = (colorAttribute, maxCount) =>
+ produce(untaggedRoadsLayer, (draft) => {
+ draft.filter = ['!', isValidAttribute(colorAttribute)]
+ })
+
+
const getRoadsLayer = (colorAttribute, maxCount) =>
produce(untaggedRoadsLayer, (draft) => {
draft.id = 'obs_roads_normal'
- if (colorAttribute.endsWith('_count')) {
- // delete draft.filter
- draft.filter = ['to-boolean', ['get', colorAttribute]]
- } else {
- draft.filter = draft.filter[1] // remove '!'
- }
+ draft.filter = isValidAttribute(colorAttribute)
draft.paint['line-width'][6] = 6 // scale bigger on zoom
draft.paint['line-color'] = colorAttribute.startsWith('distance_')
? colorByDistance(colorAttribute)
: colorAttribute.endsWith('_count')
? colorByCount(colorAttribute, maxCount)
+ : colorAttribute.endsWith('zone')
+ ? borderByZone()
: '#DDD'
draft.paint['line-opacity'][3] = 12
draft.paint['line-opacity'][5] = 13
@@ -128,8 +131,9 @@ export default function MapPage() {
const layers = []
+ const untaggedRoadsLayerCustom = useMemo(() => getUntaggedRoadsLayer(attribute), [attribute])
if (mapConfig.obsRoads.show && mapConfig.obsRoads.showUntagged) {
- layers.push(untaggedRoadsLayer)
+ layers.push(untaggedRoadsLayerCustom)
}
const roadsLayer = useMemo(() => getRoadsLayer(attribute, maxCount), [attribute, maxCount])
diff --git a/frontend/src/reducers/mapConfig.ts b/frontend/src/reducers/mapConfig.ts
index f0140cb..e7c14cf 100644
--- a/frontend/src/reducers/mapConfig.ts
+++ b/frontend/src/reducers/mapConfig.ts
@@ -11,7 +11,8 @@ type RoadAttribute =
| "distance_overtaker_max"
| "distance_overtaker_median"
| "overtaking_event_count"
- | "usage_count";
+ | "usage_count"
+ | "zone";
export type MapConfig = {
baseMap: {
diff --git a/frontend/webpack.config.js b/frontend/webpack.config.js
index 0930c34..3206622 100644
--- a/frontend/webpack.config.js
+++ b/frontend/webpack.config.js
@@ -163,6 +163,7 @@ module.exports = function (webpackEnv) {
'/config.json': apiUrl,
'/api': apiUrl,
'/login': apiUrl,
+ '/tiles': apiUrl
},
},
module: {
diff --git a/roads_import.lua b/roads_import.lua
index c314d50..2c1c45b 100644
--- a/roads_import.lua
+++ b/roads_import.lua
@@ -61,6 +61,8 @@ local roads = osm2pgsql.define_way_table('road', {
{ column = 'oneway', type = 'bool' },
})
+local minspeed_rural = 60
+
function osm2pgsql.process_way(object)
if object.tags.highway and contains(HIGHWAY_TYPES, object.tags.highway) then
local tags = object.tags
@@ -81,13 +83,30 @@ function osm2pgsql.process_way(object)
zone = "urban"
elseif string.match(zone, "motorway") then
zone = "motorway"
- elseif contains(URBAN_TYPES, tags.highway) then
+ elseif string.match(zone, "30") then
+ zone = "urban"
+ else
+ zone = "urban"
+ end
+ end
+ if not tags["zone:traffic"] then
+ if contains(URBAN_TYPES, tags.highway) then
zone = "urban"
elseif contains(MOTORWAY_TYPES, tags.highway) then
zone = "motorway"
+ elseif (tags.maxspeed) and (tonumber(string.match(tags.maxspeed, '[%d]*'))) and tonumber(string.match(tags.maxspeed, '[%d]*')) > minspeed_rural then
+ zone = "rural"
+ elseif (tags["maxspeed:forward"]) and (tonumber(string.match(tags["maxspeed:forward"], '[%d]*'))) and tonumber(string.match(tags["maxspeed:forward"], '[%d]*')) > minspeed_rural then
+ zone = "rural"
+ elseif (tags["maxspeed:backward"]) and (tonumber(string.match(tags["maxspeed:backward"], '[%d]*'))) and tonumber(string.match(tags["maxspeed:backward"], '[%d]*')) > minspeed_rural then
+ zone = "rural"
+ elseif tags['source:maxspeed'] and string.match(tags['source:maxspeed'], "rural") then
+ zone = "rural"
+ elseif tags['source:maxspeed'] and string.match(tags['source:maxspeed'], "urban") then
+ zone = "urban"
else
-- we can't figure it out
- zone = nil
+ zone = "urban"
end
end
diff --git a/tile-generator/layers/obs_events/layer.sql b/tile-generator/layers/obs_events/layer.sql
index c57874a..7ae714f 100644
--- a/tile-generator/layers/obs_events/layer.sql
+++ b/tile-generator/layers/obs_events/layer.sql
@@ -1,5 +1,5 @@
CREATE OR REPLACE FUNCTION layer_obs_events(bbox geometry, zoom_level int)
-RETURNS TABLE(event_id bigint, geometry geometry, distance_overtaker float, distance_stationary float, direction int, course float, speed float, way_id bigint) AS $$
+RETURNS TABLE(event_id bigint, geometry geometry, distance_overtaker float, distance_stationary float, direction int, course float, speed float, zone zone_type, way_id bigint) AS $$
SELECT
id::bigint as event_id,
@@ -9,8 +9,10 @@ RETURNS TABLE(event_id bigint, geometry geometry, distance_overtaker float, dist
(case when direction_reversed then -1 else 1 end)::int as direction,
course,
speed,
- way_id::bigint as way_id
+ CASE WHEN road.zone IS NULL THEN 'urban' else road.zone END as zone,
+ overtaking_event.way_id::bigint as way_id
FROM overtaking_event
+ FULL OUTER JOIN road ON (road.way_id = overtaking_event.way_id)
WHERE ST_Transform(overtaking_event.geometry, 3857) && bbox;
$$ LANGUAGE SQL IMMUTABLE;
diff --git a/tile-generator/layers/obs_events/obs_events.yaml b/tile-generator/layers/obs_events/obs_events.yaml
index 4eb82d2..be11e7c 100644
--- a/tile-generator/layers/obs_events/obs_events.yaml
+++ b/tile-generator/layers/obs_events/obs_events.yaml
@@ -16,6 +16,8 @@ layer:
Direction of travel, as reported by GPS, in degree from North.
speed: |
Speed of travel, as reported by GPS, in meters per second (?).
+ zone: |
+ rural or urban
defaults:
srs: EPSG:3785
datasource:
@@ -23,7 +25,7 @@ layer:
geometry_field: geometry
key_field: event_id
key_field_as_attribute: no
- query: (SELECT event_id, geometry, distance_overtaker, distance_stationary, direction, course, speed, way_id FROM layer_obs_events(!bbox!, z(!scale_denominator!))) AS t
+ query: (SELECT event_id, geometry, distance_overtaker, distance_stationary, direction, course, speed, zone, way_id FROM layer_obs_events(!bbox!, z(!scale_denominator!))) AS t
schema:
- ./layer.sql
diff --git a/tile-generator/layers/obs_roads/layer.sql b/tile-generator/layers/obs_roads/layer.sql
index 92c4f2f..53d8dd3 100644
--- a/tile-generator/layers/obs_roads/layer.sql
+++ b/tile-generator/layers/obs_roads/layer.sql
@@ -12,6 +12,7 @@ RETURNS TABLE(
overtaking_event_count int,
usage_count bigint,
direction int,
+ zone zone_type,
offset_direction int
) AS $$
@@ -27,12 +28,13 @@ RETURNS TABLE(
(select count(id) from road_usage where road_usage.way_id = road.way_id and
(road.directionality != 0 or road_usage.direction_reversed = r.rev)) as usage_count,
r.dir as direction,
+ road.zone::zone_type as zone,
case when road.directionality = 0 then r.dir else 0 end as offset_direction
FROM road
LEFT JOIN (VALUES (-1, TRUE), (1, FALSE), (0, FALSE)) AS r(dir, rev) ON (abs(r.dir) != road.directionality)
FULL OUTER JOIN overtaking_event ON (road.way_id = overtaking_event.way_id and (road.directionality != 0 or overtaking_event.direction_reversed = r.rev))
-- WHERE road.name = 'Merzhauser Straße'
WHERE road.geometry && bbox
- GROUP BY road.name, road.way_id, road.geometry, road.directionality, r.dir, r.rev;
+ GROUP BY road.name, road.way_id, road.geometry, road.directionality, r.dir, r.rev, road.zone;
$$ LANGUAGE SQL IMMUTABLE;
diff --git a/tile-generator/layers/obs_roads/obs_roads.yaml b/tile-generator/layers/obs_roads/obs_roads.yaml
index b246eb4..3b0e918 100644
--- a/tile-generator/layers/obs_roads/obs_roads.yaml
+++ b/tile-generator/layers/obs_roads/obs_roads.yaml
@@ -22,6 +22,8 @@ layer:
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,
even if it is oneway.
+ zone: |
+ ural or urban
offset_direction: |
Factor for offset to shift the line to the driving side. One of -1, 0, 1.
defaults:
@@ -44,6 +46,7 @@ layer:
overtaking_event_count,
usage_count,
direction,
+ zone,
offset_direction
FROM layer_obs_roads(!bbox!, z(!scale_denominator!))
) AS t