diff --git a/api/migrations/versions/b22108ab2ffb_add_event_zone.py b/api/migrations/versions/b22108ab2ffb_add_event_zone.py new file mode 100644 index 0000000..1fd53e9 --- /dev/null +++ b/api/migrations/versions/b22108ab2ffb_add_event_zone.py @@ -0,0 +1,25 @@ +"""add event zone + +Revision ID: b22108ab2ffb +Revises: a9627f63fbed +Create Date: 2022-04-30 19:06:11.472579 + +""" +from alembic import op +import sqlalchemy as sa + +from migrations.utils import dbtype + +# revision identifiers, used by Alembic. +revision = 'b22108ab2ffb' +down_revision = 'a9627f63fbed' +branch_labels = None +depends_on = None + + +def upgrade(): + op.add_column("overtaking_event", sa.Column("zone", dbtype("zone_type")), ) + + +def downgrade(): + op.drop_column("overtaking_event", "zone") diff --git a/api/obs/api/db.py b/api/obs/api/db.py index 00f849d..40b4045 100644 --- a/api/obs/api/db.py +++ b/api/obs/api/db.py @@ -124,6 +124,7 @@ class OvertakingEvent(Base): distance_stationary = Column(Float) course = Column(Float) speed = Column(Float) + zone = Column(ZoneType) def __repr__(self): return f"" diff --git a/api/obs/api/process.py b/api/obs/api/process.py index 6fc2c5e..90266e2 100644 --- a/api/obs/api/process.py +++ b/api/obs/api/process.py @@ -27,7 +27,7 @@ from obs.face.filter import ( from obs.face.osm import DataSource, DatabaseTileSource, OverpassTileSource -from obs.api.db import OvertakingEvent, RoadUsage, Track, make_session +from obs.api.db import OvertakingEvent, RoadUsage, Track, make_session, ZoneType from obs.api.app import app log = logging.getLogger(__name__) @@ -293,6 +293,7 @@ async def import_overtaking_events(session, track, overtaking_events): distance_stationary=m["distance_stationary"], course=m["course"], speed=m["speed"], + zone=m["OSM_zone"] if ('OSM_zone' in m and m['OSM_zone'] is not None) else "urban" ) session.add_all(event_models.values()) diff --git a/api/obs/api/routes/exports.py b/api/obs/api/routes/exports.py index 90218fd..a7bf668 100644 --- a/api/obs/api/routes/exports.py +++ b/api/obs/api/routes/exports.py @@ -77,6 +77,7 @@ async def export_events(req): writer.field("direction", "N", decimal=0) writer.field("course", "N", decimal=4) writer.field("speed", "N", decimal=4) + writer.field("zone", "N", decimal=4) async for event in events: writer.point(event.longitude, event.latitude) @@ -87,6 +88,7 @@ async def export_events(req): way_id=event.way_id, course=event.course, speed=event.speed, + zone=event.zone # "time"=event.time, ) @@ -107,6 +109,7 @@ async def export_events(req): "course": event.course, "speed": event.speed, "time": event.time, + "zone": event.zone }, } ) 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 56a8449..5d90dc0 100644 --- a/frontend/src/mapstyles/index.js +++ b/frontend/src/mapstyles/index.js @@ -47,24 +47,64 @@ export function colorByCount(attribute = 'event_count', maxCount, colormap = vir return colormapToScale(colormap, ['case', ['to-boolean', ['get', 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 borderByZone() { + return ["match", ['get', 'zone'], + "rural", "brown", + "urban", "olive", + "purple" + ] +} + +export function colorByDistance(attribute = 'distance_overtaker_mean', fallback = '#ABC', zone='urban') { + return [ 'case', ['!', ['to-boolean', ['get', 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..d0f7a4d 100644 --- a/frontend/src/pages/MapPage/LayerSidebar.tsx +++ b/frontend/src/pages/MapPage/LayerSidebar.tsx @@ -98,7 +98,10 @@ function LayerSidebar({ ) : ( - + Urban + + Rural + )} @@ -120,7 +123,10 @@ function LayerSidebar({ {showEvents && ( <> - + Urban + + Rural + )} diff --git a/frontend/src/pages/MapPage/RoadInfo.tsx b/frontend/src/pages/MapPage/RoadInfo.tsx index 5d2758e..35fdc0e 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' @@ -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..3579afa 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} from 'mapstyles' import {useMapConfig} from 'reducers/mapConfig' import RoadInfo from './RoadInfo' @@ -67,6 +67,8 @@ const getEventsLayer = () => ({ paint: { 'circle-radius': ['interpolate', ['linear'], ['zoom'], 14, 3, 17, 8], 'circle-color': colorByDistance('distance_overtaker'), + 'circle-stroke-color': borderByZone(), + 'circle-stroke-width':['interpolate', ['linear'], ['zoom'], 14, 1, 17, 4], }, minzoom: 11, }) 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/tile-generator/layers/obs_events/layer.sql b/tile-generator/layers/obs_events/layer.sql index c57874a..94a9792 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,6 +9,7 @@ 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, + zone, way_id::bigint as way_id FROM overtaking_event WHERE ST_Transform(overtaking_event.geometry, 3857) && bbox; 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