diff --git a/api/migrations/versions/a049e5eb24dd_create_table_region.py b/api/migrations/versions/a049e5eb24dd_create_table_region.py
new file mode 100644
index 0000000..e5dcf7f
--- /dev/null
+++ b/api/migrations/versions/a049e5eb24dd_create_table_region.py
@@ -0,0 +1,36 @@
+"""create table region
+
+Revision ID: a049e5eb24dd
+Revises: a9627f63fbed
+Create Date: 2022-04-02 21:28:43.124521
+
+"""
+from alembic import op
+import sqlalchemy as sa
+
+from migrations.utils import dbtype
+
+
+# revision identifiers, used by Alembic.
+revision = "a049e5eb24dd"
+down_revision = "a9627f63fbed"
+branch_labels = None
+depends_on = None
+
+
+def upgrade():
+ op.create_table(
+ "region",
+ sa.Column(
+ "way_id", sa.BIGINT, autoincrement=True, primary_key=True, index=True
+ ),
+ sa.Column("zone", dbtype("zone_type")),
+ sa.Column("name", sa.String),
+ sa.Column("geometry", dbtype("GEOMETRY"), index=True),
+ sa.Column("directionality", sa.Integer),
+ sa.Column("oenway", sa.Boolean),
+ )
+
+
+def downgrade():
+ op.drop_table("region")
diff --git a/api/obs/api/routes/frontend.py b/api/obs/api/routes/frontend.py
index fb681a0..7ccaa70 100644
--- a/api/obs/api/routes/frontend.py
+++ b/api/obs/api/routes/frontend.py
@@ -26,7 +26,7 @@ if app.config.FRONTEND_CONFIG:
.replace("111", "{x}")
.replace("222", "{y}")
],
- "minzoom": 12,
+ "minzoom": 0,
"maxzoom": 14,
}
),
diff --git a/frontend/config.example.json b/frontend/config.example.json
index 6566c6e..2918934 100644
--- a/frontend/config.example.json
+++ b/frontend/config.example.json
@@ -12,7 +12,7 @@
"obsMapSource": {
"type": "vector",
"tiles": ["https://portal.example.com/tiles/{z}/{x}/{y}.pbf"],
- "minzoom": 12,
+ "minzoom": 0,
"maxzoom": 14
}
}
diff --git a/frontend/src/components/ColorMapLegend.tsx b/frontend/src/components/ColorMapLegend.tsx
index f0c4d48..ca09860 100644
--- a/frontend/src/components/ColorMapLegend.tsx
+++ b/frontend/src/components/ColorMapLegend.tsx
@@ -59,7 +59,7 @@ export function DiscreteColorMapLegend({map}: {map: ColorMap}) {
)
}
-export default function ColorMapLegend({map, twoTicks = false}: {map: ColorMap, twoTicks?: boolean}) {
+export default function ColorMapLegend({map, twoTicks = false, digits=2}: {map: ColorMap, twoTicks?: boolean, digits?: number}) {
const min = map[0][0]
const max = map[map.length - 1][0]
const normalizeValue = (v) => (v - min) / (max - min)
@@ -81,7 +81,7 @@ export default function ColorMapLegend({map, twoTicks = false}: {map: ColorMap,
{tickValues.map(([value]) => (
- {value.toFixed(2)}
+ {value.toFixed(digits)}
))}
diff --git a/frontend/src/mapstyles/index.js b/frontend/src/mapstyles/index.js
index c3a62fb..77fb1a5 100644
--- a/frontend/src/mapstyles/index.js
+++ b/frontend/src/mapstyles/index.js
@@ -77,6 +77,58 @@ export const trackLayer = {
},
}
+export const getRegionLayers = (adminLevel = 6, baseColor = "#00897B", maxValue = 5000) => [{
+ id: 'region',
+ "type": "fill",
+ "source": "obs",
+ "source-layer": "obs_regions",
+ "minzoom": 0,
+ "maxzoom": 10,
+ "filter": [
+ "all",
+ ["==", "admin_level", adminLevel]
+ ],
+ "paint": {
+ "fill-color": baseColor,
+ "fill-antialias": true,
+ "fill-opacity": [
+ "interpolate",
+ ["linear"],
+ [
+ "log10",
+ [
+ "get",
+ "overtaking_event_count"
+ ]
+ ],
+ 0,
+ 0,
+ Math.log10(maxValue),
+ 0.9
+ ]
+ },
+},
+{
+ id: 'region-border',
+ "type": "line",
+ "source": "obs",
+ "source-layer": "obs_regions",
+ "minzoom": 0,
+ "maxzoom": 10,
+ "filter": [
+ "all",
+ ["==", "admin_level", adminLevel]
+ ],
+ "paint": {
+ "line-width": 1,
+ "line-color": baseColor,
+ },
+ "layout": {
+ "line-join": "round",
+ "line-cap": "round"
+ }
+}]
+
export const trackLayerRaw = produce(trackLayer, draft => {
// draft.paint['line-color'] = '#81D4FA'
draft.paint['line-width'][4] = 1
diff --git a/frontend/src/pages/MapPage/LayerSidebar.tsx b/frontend/src/pages/MapPage/LayerSidebar.tsx
index 9e38026..00e8fab 100644
--- a/frontend/src/pages/MapPage/LayerSidebar.tsx
+++ b/frontend/src/pages/MapPage/LayerSidebar.tsx
@@ -36,6 +36,7 @@ function LayerSidebar({
baseMap: {style},
obsRoads: {show: showRoads, showUntagged, attribute, maxCount},
obsEvents: {show: showEvents},
+ obsRegions: {show: showRegions},
} = mapConfig
return (
@@ -50,6 +51,28 @@ function LayerSidebar({
/>
+
+ setMapConfigFlag('obsRegions.show', !showRegions)}
+ />
+
+
+ {showRegions && (
+ <>
+ Color regions based on event count
+
+
+
+ >
+ )}
+
} else {
draft.filter = draft.filter[1] // remove '!'
}
+ draft.minzoom = 10
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)
: '#DDD'
- draft.paint['line-opacity'][3] = 12
- draft.paint['line-opacity'][5] = 13
+ // draft.paint['line-opacity'][3] = 12
+ // draft.paint['line-opacity'][5] = 13
})
const getEventsLayer = () => ({
@@ -137,6 +139,9 @@ export default function MapPage() {
layers.push(roadsLayer)
}
+ const regionLayers = useMemo(() => getRegionLayers(), [])
+ layers.push(...regionLayers)
+
const eventsLayer = useMemo(() => getEventsLayer(), [])
const eventsTextLayer = useMemo(() => getEventsTextLayer(), [])
if (mapConfig.obsEvents.show) {
diff --git a/frontend/src/reducers/mapConfig.ts b/frontend/src/reducers/mapConfig.ts
index f0140cb..d4880f6 100644
--- a/frontend/src/reducers/mapConfig.ts
+++ b/frontend/src/reducers/mapConfig.ts
@@ -26,6 +26,9 @@ export type MapConfig = {
obsEvents: {
show: boolean;
};
+ obsRegions: {
+ show: boolean;
+ };
};
export const initialState: MapConfig = {
@@ -41,6 +44,9 @@ export const initialState: MapConfig = {
obsEvents: {
show: false,
},
+ obsRegions: {
+ show: true,
+ },
};
type MapConfigAction = {
diff --git a/roads_import.lua b/roads_import.lua
index c314d50..5df9530 100644
--- a/roads_import.lua
+++ b/roads_import.lua
@@ -50,6 +50,9 @@ local MOTORWAY_TYPES = {
"motorway_link",
}
+local ADMIN_LEVEL_MIN = 2
+local ADMIN_LEVEL_MAX = 8
+
local ONEWAY_YES = {"yes", "true", "1"}
local ONEWAY_REVERSE = {"reverse", "-1"}
@@ -61,6 +64,14 @@ local roads = osm2pgsql.define_way_table('road', {
{ column = 'oneway', type = 'bool' },
})
+local regions = osm2pgsql.define_relation_table('region', {
+ { column = 'name', type = 'text' },
+ { column = 'geometry', type = 'geometry' },
+ { column = 'admin_level', type = 'int' },
+ { column = 'tags', type = 'hstore' },
+})
+
+
function osm2pgsql.process_way(object)
if object.tags.highway and contains(HIGHWAY_TYPES, object.tags.highway) then
local tags = object.tags
@@ -112,3 +123,21 @@ function osm2pgsql.process_way(object)
})
end
end
+
+function osm2pgsql.process_relation(object)
+ local admin_level = tonumber(object.tags.admin_level)
+ if object.tags.boundary == "administrative" and admin_level and admin_level >= ADMIN_LEVEL_MIN and admin_level <= ADMIN_LEVEL_MAX then
+ regions:add_row({
+ geometry = { create = 'area' },
+ name = object.tags.name,
+ admin_level = admin_level,
+ tags = object.tags,
+ })
+ end
+end
+
+function osm2pgsql.select_relation_members(relation)
+ if relation.tags.type == 'route' then
+ return { ways = osm2pgsql.way_member_ids(relation) }
+ end
+end
diff --git a/tile-generator/layers/obs_events/layer.sql b/tile-generator/layers/obs_events/layer.sql
index c57874a..03d085b 100644
--- a/tile-generator/layers/obs_events/layer.sql
+++ b/tile-generator/layers/obs_events/layer.sql
@@ -11,6 +11,8 @@ RETURNS TABLE(event_id bigint, geometry geometry, distance_overtaker float, dist
speed,
way_id::bigint as way_id
FROM overtaking_event
- WHERE ST_Transform(overtaking_event.geometry, 3857) && bbox;
+ WHERE
+ zoom_level >= 10 AND
+ ST_Transform(overtaking_event.geometry, 3857) && bbox;
$$ LANGUAGE SQL IMMUTABLE;
diff --git a/tile-generator/layers/obs_regions/layer.sql b/tile-generator/layers/obs_regions/layer.sql
new file mode 100644
index 0000000..fda2a44
--- /dev/null
+++ b/tile-generator/layers/obs_regions/layer.sql
@@ -0,0 +1,26 @@
+DROP FUNCTION IF EXISTS layer_obs_regions(geometry, int);
+
+CREATE OR REPLACE FUNCTION layer_obs_regions(bbox geometry, zoom_level int)
+RETURNS TABLE(
+ region_id bigint,
+ geometry geometry,
+ name text,
+ admin_level int,
+ overtaking_event_count int
+) AS $$
+
+ SELECT
+ region.relation_id::bigint as region_id,
+ ST_SimplifyPreserveTopology(region.geometry, ZRes(zoom_level + 2)) as geometry,
+ region.name as name,
+ region.admin_level as admin_level,
+ count(overtaking_event.id)::int as overtaking_event_count
+ FROM region
+ LEFT JOIN overtaking_event on ST_Within(ST_Transform(overtaking_event.geometry, 3857), region.geometry)
+ WHERE
+ zoom_level >= 4 AND
+ zoom_level <= 12 AND
+ ST_Transform(region.geometry, 3857) && bbox
+ GROUP BY region.relation_id, region.name, region.geometry, region.admin_level
+
+$$ LANGUAGE SQL IMMUTABLE;
diff --git a/tile-generator/layers/obs_regions/obs_regions.yaml b/tile-generator/layers/obs_regions/obs_regions.yaml
new file mode 100644
index 0000000..477bf45
--- /dev/null
+++ b/tile-generator/layers/obs_regions/obs_regions.yaml
@@ -0,0 +1,23 @@
+layer:
+ id: "obs_regions"
+ description: |
+ Statistics on administrative boundary areas ("regions")
+ buffer_size: 4
+ fields:
+ overtaking_event_count: |
+ Number of overtaking events.
+ name: |
+ Name of the region
+ admin_level: |
+ Administrative level of the boundary, as tagged in OpenStreetMap
+ defaults:
+ srs: EPSG:3785
+ datasource:
+ srid: 3857
+ geometry_field: geometry
+ key_field: region_id
+ key_field_as_attribute: no
+ query: (SELECT region_id, geometry, name, admin_level, overtaking_event_count FROM layer_obs_regions(!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..ce34d70 100644
--- a/tile-generator/layers/obs_roads/layer.sql
+++ b/tile-generator/layers/obs_roads/layer.sql
@@ -32,7 +32,9 @@ RETURNS TABLE(
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
+ WHERE
+ zoom_level >= 10 AND
+ road.geometry && bbox
GROUP BY road.name, road.way_id, road.geometry, road.directionality, r.dir, r.rev;
$$ LANGUAGE SQL IMMUTABLE;
diff --git a/tile-generator/openbikesensor.yaml b/tile-generator/openbikesensor.yaml
index cf5fdd6..ecfc647 100644
--- a/tile-generator/openbikesensor.yaml
+++ b/tile-generator/openbikesensor.yaml
@@ -3,6 +3,7 @@ tileset:
layers:
- layers/obs_events/obs_events.yaml
- layers/obs_roads/obs_roads.yaml
+ - layers/obs_regions/obs_regions.yaml
version: 0.6.2
id: openbikesensor
description: >