Implement difference between urban and rural for events and road segments.
This commit is contained in:
parent
8728347695
commit
66dd84982c
25
api/migrations/versions/b22108ab2ffb_add_event_zone.py
Normal file
25
api/migrations/versions/b22108ab2ffb_add_event_zone.py
Normal file
|
@ -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")
|
|
@ -124,6 +124,7 @@ class OvertakingEvent(Base):
|
||||||
distance_stationary = Column(Float)
|
distance_stationary = Column(Float)
|
||||||
course = Column(Float)
|
course = Column(Float)
|
||||||
speed = Column(Float)
|
speed = Column(Float)
|
||||||
|
zone = Column(ZoneType)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
return f"<OvertakingEvent {self.id}>"
|
return f"<OvertakingEvent {self.id}>"
|
||||||
|
|
|
@ -27,7 +27,7 @@ from obs.face.filter import (
|
||||||
|
|
||||||
from obs.face.osm import DataSource, DatabaseTileSource, OverpassTileSource
|
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
|
from obs.api.app import app
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
@ -293,6 +293,7 @@ async def import_overtaking_events(session, track, overtaking_events):
|
||||||
distance_stationary=m["distance_stationary"],
|
distance_stationary=m["distance_stationary"],
|
||||||
course=m["course"],
|
course=m["course"],
|
||||||
speed=m["speed"],
|
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())
|
session.add_all(event_models.values())
|
||||||
|
|
|
@ -77,6 +77,7 @@ async def export_events(req):
|
||||||
writer.field("direction", "N", decimal=0)
|
writer.field("direction", "N", decimal=0)
|
||||||
writer.field("course", "N", decimal=4)
|
writer.field("course", "N", decimal=4)
|
||||||
writer.field("speed", "N", decimal=4)
|
writer.field("speed", "N", decimal=4)
|
||||||
|
writer.field("zone", "N", decimal=4)
|
||||||
|
|
||||||
async for event in events:
|
async for event in events:
|
||||||
writer.point(event.longitude, event.latitude)
|
writer.point(event.longitude, event.latitude)
|
||||||
|
@ -87,6 +88,7 @@ async def export_events(req):
|
||||||
way_id=event.way_id,
|
way_id=event.way_id,
|
||||||
course=event.course,
|
course=event.course,
|
||||||
speed=event.speed,
|
speed=event.speed,
|
||||||
|
zone=event.zone
|
||||||
# "time"=event.time,
|
# "time"=event.time,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -107,6 +109,7 @@ async def export_events(req):
|
||||||
"course": event.course,
|
"course": event.course,
|
||||||
"speed": event.speed,
|
"speed": event.speed,
|
||||||
"time": event.time,
|
"time": event.time,
|
||||||
|
"zone": event.zone
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
|
@ -112,6 +112,7 @@ async def mapdetails_road(req):
|
||||||
"histogram": {
|
"histogram": {
|
||||||
"bins": [None if math.isinf(b) else b for b in bins.tolist()],
|
"bins": [None if math.isinf(b) else b for b in bins.tolist()],
|
||||||
"counts": hist.tolist(),
|
"counts": hist.tolist(),
|
||||||
|
"zone": road.zone
|
||||||
},
|
},
|
||||||
"values": list(map(rounder, arr.tolist())),
|
"values": list(map(rounder, arr.tolist())),
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
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 [
|
return [
|
||||||
'case',
|
'case',
|
||||||
['!', ['to-boolean', ['get', attribute]]],
|
['!', ['to-boolean', ['get', attribute]]],
|
||||||
fallback,
|
fallback,
|
||||||
|
["match", ['get', 'zone'], "rural",
|
||||||
[
|
[
|
||||||
'step',
|
'step',
|
||||||
['get', attribute],
|
['get', attribute],
|
||||||
'rgba(150, 0, 0, 1)',
|
'rgba(150, 0, 0, 1)',
|
||||||
1.1,
|
steps['rural'][0],
|
||||||
'rgba(255, 0, 0, 1)',
|
'rgba(255, 0, 0, 1)',
|
||||||
1.3,
|
steps['rural'][1],
|
||||||
'rgba(255, 220, 0, 1)',
|
'rgba(255, 220, 0, 1)',
|
||||||
1.5,
|
steps['rural'][2],
|
||||||
'rgba(67, 200, 0, 1)',
|
'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)',
|
'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)',
|
||||||
|
]
|
||||||
|
]
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -98,7 +98,10 @@ function LayerSidebar({
|
||||||
) :
|
) :
|
||||||
(
|
(
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<DiscreteColorMapLegend map={colorByDistance('distance_overtaker')[3].slice(2)} />
|
<span style={{color: "olive"}}>Urban</span>
|
||||||
|
<DiscreteColorMapLegend map={colorByDistance('distance_overtaker')[3][5].slice(2)} />
|
||||||
|
<span style={{color: "brown"}}>Rural</span>
|
||||||
|
<DiscreteColorMapLegend map={colorByDistance('distance_overtaker')[3][3].slice(2)} />
|
||||||
</List.Item>
|
</List.Item>
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
@ -120,7 +123,10 @@ function LayerSidebar({
|
||||||
{showEvents && (
|
{showEvents && (
|
||||||
<>
|
<>
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<DiscreteColorMapLegend map={colorByDistance('distance_overtaker')[3].slice(2)} />
|
<span style={{color: "olive"}}>Urban</span>
|
||||||
|
<DiscreteColorMapLegend map={colorByDistance('distance_overtaker')[3][5].slice(2)} />
|
||||||
|
<span style={{color: "brown"}}>Rural</span>
|
||||||
|
<DiscreteColorMapLegend map={colorByDistance('distance_overtaker')[3][3].slice(2)} />
|
||||||
</List.Item>
|
</List.Item>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
@ -9,7 +9,7 @@ import {Chart} from 'components'
|
||||||
import {pairwise} from 'utils'
|
import {pairwise} from 'utils'
|
||||||
|
|
||||||
import api from 'api'
|
import api from 'api'
|
||||||
import {colorByDistance} from 'mapstyles'
|
import {colorByDistance, borderByZone} from 'mapstyles'
|
||||||
|
|
||||||
import styles from './styles.module.less'
|
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 diff = bins[1] - bins[0]
|
||||||
|
const colortype = zone=="rural" ? 3:5;
|
||||||
const data = _.zip(
|
const data = _.zip(
|
||||||
bins.slice(0, bins.length - 1).map((v) => v + diff / 2),
|
bins.slice(0, bins.length - 1).map((v) => v + diff / 2),
|
||||||
counts
|
counts
|
||||||
).map((value) => ({
|
).map((value) => ({
|
||||||
value,
|
value,
|
||||||
itemStyle: {color: selectFromColorMap(colorByDistance()[3].slice(2), value[0])},
|
itemStyle: {color: selectFromColorMap(colorByDistance()[3][colortype].slice(2), value[0]),},
|
||||||
}))
|
}))
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -6,7 +6,7 @@ import produce from 'immer'
|
||||||
|
|
||||||
import {Page, Map} from 'components'
|
import {Page, Map} from 'components'
|
||||||
import {useConfig} from 'config'
|
import {useConfig} from 'config'
|
||||||
import {colorByDistance, colorByCount, reds} from 'mapstyles'
|
import {colorByDistance, colorByCount, borderByZone, reds} from 'mapstyles'
|
||||||
import {useMapConfig} from 'reducers/mapConfig'
|
import {useMapConfig} from 'reducers/mapConfig'
|
||||||
|
|
||||||
import RoadInfo from './RoadInfo'
|
import RoadInfo from './RoadInfo'
|
||||||
|
@ -67,6 +67,8 @@ const getEventsLayer = () => ({
|
||||||
paint: {
|
paint: {
|
||||||
'circle-radius': ['interpolate', ['linear'], ['zoom'], 14, 3, 17, 8],
|
'circle-radius': ['interpolate', ['linear'], ['zoom'], 14, 3, 17, 8],
|
||||||
'circle-color': colorByDistance('distance_overtaker'),
|
'circle-color': colorByDistance('distance_overtaker'),
|
||||||
|
'circle-stroke-color': borderByZone(),
|
||||||
|
'circle-stroke-width':['interpolate', ['linear'], ['zoom'], 14, 1, 17, 4],
|
||||||
},
|
},
|
||||||
minzoom: 11,
|
minzoom: 11,
|
||||||
})
|
})
|
||||||
|
|
|
@ -163,6 +163,7 @@ module.exports = function (webpackEnv) {
|
||||||
'/config.json': apiUrl,
|
'/config.json': apiUrl,
|
||||||
'/api': apiUrl,
|
'/api': apiUrl,
|
||||||
'/login': apiUrl,
|
'/login': apiUrl,
|
||||||
|
'/tiles': apiUrl
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
module: {
|
module: {
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
CREATE OR REPLACE FUNCTION layer_obs_events(bbox geometry, zoom_level int)
|
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
|
SELECT
|
||||||
id::bigint as event_id,
|
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,
|
(case when direction_reversed then -1 else 1 end)::int as direction,
|
||||||
course,
|
course,
|
||||||
speed,
|
speed,
|
||||||
|
zone,
|
||||||
way_id::bigint as way_id
|
way_id::bigint as way_id
|
||||||
FROM overtaking_event
|
FROM overtaking_event
|
||||||
WHERE ST_Transform(overtaking_event.geometry, 3857) && bbox;
|
WHERE ST_Transform(overtaking_event.geometry, 3857) && bbox;
|
||||||
|
|
|
@ -16,6 +16,8 @@ layer:
|
||||||
Direction of travel, as reported by GPS, in degree from North.
|
Direction of travel, as reported by GPS, in degree from North.
|
||||||
speed: |
|
speed: |
|
||||||
Speed of travel, as reported by GPS, in meters per second (?).
|
Speed of travel, as reported by GPS, in meters per second (?).
|
||||||
|
zone: |
|
||||||
|
rural or urban
|
||||||
defaults:
|
defaults:
|
||||||
srs: EPSG:3785
|
srs: EPSG:3785
|
||||||
datasource:
|
datasource:
|
||||||
|
@ -23,7 +25,7 @@ layer:
|
||||||
geometry_field: geometry
|
geometry_field: geometry
|
||||||
key_field: event_id
|
key_field: event_id
|
||||||
key_field_as_attribute: no
|
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:
|
schema:
|
||||||
- ./layer.sql
|
- ./layer.sql
|
||||||
|
|
|
@ -12,6 +12,7 @@ RETURNS TABLE(
|
||||||
overtaking_event_count int,
|
overtaking_event_count int,
|
||||||
usage_count bigint,
|
usage_count bigint,
|
||||||
direction int,
|
direction int,
|
||||||
|
zone zone_type,
|
||||||
offset_direction int
|
offset_direction int
|
||||||
) AS $$
|
) AS $$
|
||||||
|
|
||||||
|
@ -27,12 +28,13 @@ RETURNS TABLE(
|
||||||
(select count(id) from road_usage where road_usage.way_id = road.way_id and
|
(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,
|
(road.directionality != 0 or road_usage.direction_reversed = r.rev)) as usage_count,
|
||||||
r.dir as direction,
|
r.dir as direction,
|
||||||
|
road.zone::zone_type as zone,
|
||||||
case when road.directionality = 0 then r.dir else 0 end as offset_direction
|
case when road.directionality = 0 then r.dir else 0 end as offset_direction
|
||||||
FROM road
|
FROM road
|
||||||
LEFT JOIN (VALUES (-1, TRUE), (1, FALSE), (0, FALSE)) AS r(dir, rev) ON (abs(r.dir) != road.directionality)
|
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))
|
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.name = 'Merzhauser Straße'
|
||||||
WHERE road.geometry && bbox
|
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;
|
$$ LANGUAGE SQL IMMUTABLE;
|
||||||
|
|
|
@ -22,6 +22,8 @@ layer:
|
||||||
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.
|
||||||
|
zone: |
|
||||||
|
ural or urban
|
||||||
offset_direction: |
|
offset_direction: |
|
||||||
Factor for offset to shift the line to the driving side. One of -1, 0, 1.
|
Factor for offset to shift the line to the driving side. One of -1, 0, 1.
|
||||||
defaults:
|
defaults:
|
||||||
|
@ -44,6 +46,7 @@ layer:
|
||||||
overtaking_event_count,
|
overtaking_event_count,
|
||||||
usage_count,
|
usage_count,
|
||||||
direction,
|
direction,
|
||||||
|
zone,
|
||||||
offset_direction
|
offset_direction
|
||||||
FROM layer_obs_roads(!bbox!, z(!scale_denominator!))
|
FROM layer_obs_roads(!bbox!, z(!scale_denominator!))
|
||||||
) AS t
|
) AS t
|
||||||
|
|
Loading…
Reference in a new issue