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 30067d0..fbdad52 100644
--- a/frontend/src/mapstyles/index.js
+++ b/frontend/src/mapstyles/index.js
@@ -124,6 +124,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 7da55ce..87e30b2 100644
--- a/frontend/src/pages/MapPage/LayerSidebar.tsx
+++ b/frontend/src/pages/MapPage/LayerSidebar.tsx
@@ -50,6 +50,7 @@ function LayerSidebar({
baseMap: { style },
obsRoads: { show: showRoads, showUntagged, attribute, maxCount },
obsEvents: { show: showEvents },
+ obsRegions: {show: showRegions},
filters: {
currentUser: filtersCurrentUser,
dateMode,
@@ -57,7 +58,7 @@ function LayerSidebar({
endDate,
thresholdAfter,
},
- } = mapConfig;
+ } = mapConfig
return (
@@ -77,6 +78,28 @@ function LayerSidebar({
/>
+
+ setMapConfigFlag('obsRegions.show', !showRegions)}
+ />
+
+
+ {showRegions && (
+ <>
+
Color regions based on event count
+
+
+
+ >
+ )}
+
produce(untaggedRoadsLayer, (draft) => {
draft.id = "obs_roads_normal";
draft.filter = isValidAttribute(colorAttribute);
+ draft.minzoom = 10
draft.paint["line-width"][6] = 6; // scale bigger on zoom
draft.paint["line-color"] = colorAttribute.startsWith("distance_")
? colorByDistance(colorAttribute)
@@ -66,8 +61,8 @@ const getRoadsLayer = (colorAttribute, maxCount) =>
: colorAttribute.endsWith("zone")
? borderByZone()
: "#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 = () => ({
@@ -162,6 +157,9 @@ function MapPage({ login }) {
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 2ec5398..a597fbb 100644
--- a/frontend/src/reducers/mapConfig.ts
+++ b/frontend/src/reducers/mapConfig.ts
@@ -27,6 +27,9 @@ export type MapConfig = {
obsEvents: {
show: boolean;
};
+ obsRegions: {
+ show: boolean;
+ };
filters: {
currentUser: boolean;
dateMode: "none" | "range" | "threshold";
@@ -49,6 +52,9 @@ export const initialState: MapConfig = {
obsEvents: {
show: false,
},
+ obsRegions: {
+ show: true,
+ },
filters: {
currentUser: false,
dateMode: "none",
diff --git a/roads_import.lua b/roads_import.lua
index 2c1c45b..adeb663 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,13 @@ 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' },
+})
+
local minspeed_rural = 60
function osm2pgsql.process_way(object)
@@ -131,3 +141,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 574d67c..6c4c5cd 100644
--- a/tile-generator/layers/obs_events/layer.sql
+++ b/tile-generator/layers/obs_events/layer.sql
@@ -14,7 +14,9 @@ RETURNS TABLE(event_id bigint, geometry geometry, distance_overtaker float, dist
FROM overtaking_event
FULL OUTER JOIN road ON road.way_id = overtaking_event.way_id
JOIN track on track.id = overtaking_event.track_id
- WHERE ST_Transform(overtaking_event.geometry, 3857) && bbox
+ WHERE
+ zoom_level >= 10 AND
+ ST_Transform(overtaking_event.geometry, 3857) && bbox
AND (user_id is NULL OR user_id = track.author_id)
AND time BETWEEN COALESCE(min_time, '1900-01-01'::timestamp) AND COALESCE(max_time, '2100-01-01'::timestamp);
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 2073150..391bfee 100644
--- a/tile-generator/layers/obs_roads/layer.sql
+++ b/tile-generator/layers/obs_roads/layer.sql
@@ -66,7 +66,9 @@ RETURNS TABLE(
GROUP BY overtaking_event.way_id, overtaking_event.direction_reversed
) e on (e.way_id = road.way_id and (road.directionality != 0 or e.direction_reversed = r.rev))
- WHERE road.geometry && bbox
+ WHERE
+ zoom_level >= 10 AND
+ road.geometry && bbox
GROUP BY
road.name,
road.way_id,
diff --git a/tile-generator/openbikesensor.yaml b/tile-generator/openbikesensor.yaml
index d3a7a65..3aedd7e 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.7.0
id: openbikesensor
description: >