diff --git a/api/prepare_facilities.sql b/api/prepare_facilities.sql index 152e03f8..1347d335 100644 --- a/api/prepare_facilities.sql +++ b/api/prepare_facilities.sql @@ -62,7 +62,7 @@ CREATE TABLE openrailwaymap_facilities_for_search AS railway_ref, name_tags, route_count, - way AS geom + ST_Centroid(way) AS geom FROM stations_with_route_counts WHERE railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site') diff --git a/docker-compose.yml b/docker-compose.yml index abf8fb05..8eb2e7e0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -57,7 +57,7 @@ services: [ "$$TILES" = "low-med" ] && $$MARTIN --min-zoom 7 --max-zoom 7 --source railway_line_med --output-file /tiles/railway_line_med.mbtiles && mbtiles summary /tiles/railway_line_med.mbtiles [ "$$TILES" = "low-med" ] && $$MARTIN --min-zoom 7 --max-zoom 7 --source standard_railway_text_stations_med --output-file /tiles/standard_railway_text_stations_med.mbtiles && mbtiles summary /tiles/standard_railway_text_stations_med.mbtiles [ "$$TILES" = "high" ] && $$MARTIN --min-zoom 8 --max-zoom "$$MAX_ZOOM" --source railway_line_high,railway_text_km --output-file /tiles/high.mbtiles && mbtiles summary /tiles/high.mbtiles - [ "$$TILES" = "standard" ] && $$MARTIN --min-zoom 8 --max-zoom "$$MAX_ZOOM" --source standard_railway_turntables,standard_railway_text_stations,standard_railway_symbols,standard_railway_switch_ref --output-file /tiles/standard.mbtiles && mbtiles summary /tiles/standard.mbtiles + [ "$$TILES" = "standard" ] && $$MARTIN --min-zoom 8 --max-zoom "$$MAX_ZOOM" --source standard_railway_turntables,standard_railway_text_stations,standard_railway_grouped_stations,standard_railway_symbols,standard_railway_switch_ref --output-file /tiles/standard.mbtiles && mbtiles summary /tiles/standard.mbtiles [ "$$TILES" = "speed" ] && $$MARTIN --min-zoom 8 --max-zoom "$$MAX_ZOOM" --source speed_railway_signals --output-file /tiles/speed.mbtiles && mbtiles summary /tiles/speed.mbtiles [ "$$TILES" = "signals" ] && $$MARTIN --min-zoom 8 --max-zoom "$$MAX_ZOOM" --source signals_railway_signals,signals_signal_boxes --output-file /tiles/signals.mbtiles && mbtiles summary /tiles/signals.mbtiles [ "$$TILES" = "electrification" ] && $$MARTIN --min-zoom 8 --max-zoom "$$MAX_ZOOM" --source electrification_signals --output-file /tiles/electrification.mbtiles && mbtiles summary /tiles/electrification.mbtiles diff --git a/import/sql/get_station_importance.sql b/import/sql/get_station_importance.sql index 9128b208..6047dcdb 100644 --- a/import/sql/get_station_importance.sql +++ b/import/sql/get_station_importance.sql @@ -60,24 +60,39 @@ CREATE OR REPLACE VIEW station_nodes_platforms_rel_count AS -- needs about 3 to 4 minutes for whole Germany -- or about 20 to 30 minutes for the whole planet CREATE MATERIALIZED VIEW IF NOT EXISTS stations_with_route_counts AS - SELECT DISTINCT ON (osm_id, name, station, railway_ref, railway) id, osm_id, name, station, railway_ref, railway, route_count, name_tags, way + SELECT + MIN(id) as id, + MIN(osm_id) as osm_id, + name, + station, + railway_ref, + railway, + MAX(route_count) as route_count, + hstore(string_agg(nullif(name_tags::text, ''), ',')) as name_tags, + ST_RemoveRepeatedPoints(ST_Collect(way)) as way + FROM ( + SELECT + *, + ST_ClusterDBSCAN(way, 400, 1) OVER (PARTITION BY name, station, railway_ref, railway) AS cluster_id FROM ( - SELECT id, osm_id, name, station, railway_ref, railway, ARRAY_LENGTH(ARRAY_AGG(DISTINCT route_id), 1) AS route_count, name_tags, way - FROM ( - SELECT id, osm_id, name, station, railway_ref, railway, UNNEST(route_ids) AS route_id, name_tags, way - FROM station_nodes_stop_positions_rel_count - UNION ALL - SELECT id, osm_id, name, station, railway_ref, railway, UNNEST(route_ids) AS route_id, name_tags, way - FROM station_nodes_platforms_rel_count - ) AS a - GROUP BY id, osm_id, name, station, railway_ref, railway, way, name_tags + SELECT MIN(id) as id, MIN(osm_id) as osm_id, name, station, railway_ref, railway, ARRAY_LENGTH(ARRAY_AGG(DISTINCT route_id), 1) AS route_count, name_tags, way + FROM ( + SELECT id, osm_id, name, station, railway_ref, railway, UNNEST(route_ids) AS route_id, name_tags, way + FROM station_nodes_stop_positions_rel_count + UNION ALL + SELECT id, osm_id, name, station, railway_ref, railway, UNNEST(route_ids) AS route_id, name_tags, way + FROM station_nodes_platforms_rel_count + ) AS a + GROUP BY name, station, railway_ref, railway, way, name_tags UNION ALL SELECT id, osm_id, name, station, railway_ref, railway, 0 AS route_count, name_tags, way - FROM stations - WHERE railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site') - ) AS facilities - -- ORDER BY is required to ensure that the larger route_count is used. - ORDER BY osm_id, name, station, railway_ref, railway, route_count DESC; + FROM stations + WHERE railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site') + ) AS grouped_facilities + ) AS facilities + GROUP BY name, station, railway_ref, railway, cluster_id + -- ORDER BY is required to ensure that the larger route_count is used. + ORDER BY name, station, railway_ref, railway, route_count DESC; CREATE INDEX IF NOT EXISTS stations_with_route_counts_geom_index ON stations_with_route_counts diff --git a/import/sql/tile_views.sql b/import/sql/tile_views.sql index e2d33ddf..ba3847a2 100644 --- a/import/sql/tile_views.sql +++ b/import/sql/tile_views.sql @@ -139,7 +139,7 @@ CREATE OR REPLACE VIEW standard_railway_text_stations_low AS SELECT id, osm_id, - way, + ST_Centroid(way) as way, railway_ref as label FROM stations_with_route_counts WHERE @@ -153,7 +153,7 @@ CREATE OR REPLACE VIEW standard_railway_text_stations_med AS SELECT id, osm_id, - way, + ST_Centroid(way) as way, railway_ref as label FROM stations_with_route_counts WHERE @@ -185,23 +185,47 @@ CREATE OR REPLACE VIEW standard_railway_text_stations AS WHEN railway = 'site' THEN 600 WHEN railway = 'crossover' THEN 700 ELSE 50 - END AS rank - FROM - (SELECT - id, - osm_id, - way, - railway, - route_count, - station, - railway_ref, - name - FROM stations_with_route_counts - WHERE railway IN ('station', 'halt', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site') - AND name IS NOT NULL - ) AS r + END AS rank, + count + FROM ( + SELECT + id, + osm_id, + ST_Centroid(way) as way, + railway, + route_count, + station, + railway_ref, + name, + ST_NumGeometries(way) as count + FROM stations_with_route_counts + WHERE railway IN ('station', 'halt', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site', 'tram_stop') + AND name IS NOT NULL + ) AS r ORDER by rank DESC NULLS LAST, route_count DESC NULLS LAST; +CREATE OR REPLACE VIEW standard_railway_grouped_stations AS + SELECT + id, + osm_id, + way, + railway, + station, + railway_ref as label, + name + FROM ( + SELECT + id, + osm_id, + ST_Buffer(ST_ConvexHull(way), 50) as way, + railway, + station, + railway_ref, + name + FROM stations_with_route_counts + WHERE railway IN ('station', 'halt', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site', 'tram_stop') + ) AS r; + CREATE OR REPLACE VIEW standard_railway_symbols AS SELECT id, diff --git a/martin/configuration.yml b/martin/configuration.yml index 2dd1e8e5..eee78fd3 100644 --- a/martin/configuration.yml +++ b/martin/configuration.yml @@ -201,6 +201,21 @@ postgres: station: string label: string name: string + count: integer + + standard_railway_grouped_stations: + schema: public + table: standard_railway_grouped_stations + srid: 3857 + geometry_column: way + geometry_type: POLYGON + properties: + id: integer + osm_id: integer + railway: string + station: string + label: string + name: string standard_railway_symbols: schema: public diff --git a/proxy/js/features.mjs b/proxy/js/features.mjs index 8f2491a4..1bec0a70 100644 --- a/proxy/js/features.mjs +++ b/proxy/js/features.mjs @@ -156,6 +156,7 @@ const features = { 'standard_railway_text_stations_low-standard_railway_text_stations_low': stationFeatures, 'standard_railway_text_stations_med-standard_railway_text_stations_med': stationFeatures, 'openrailwaymap_standard-standard_railway_text_stations': stationFeatures, + 'openrailwaymap_standard-standard_railway_grouped_stations': stationFeatures, 'openrailwaymap_standard-standard_railway_turntables': { features: { turntable: { diff --git a/proxy/js/styles.mjs b/proxy/js/styles.mjs index ac1583f3..65a5901f 100644 --- a/proxy/js/styles.mjs +++ b/proxy/js/styles.mjs @@ -1384,6 +1384,48 @@ const layers = Object.fromEntries(knownThemes.map(theme => [theme, { 'text-max-width': 5, }, }, + { + id: 'railway_grouped_stations', + type: 'fill', + minzoom: 8, + source: 'openrailwaymap_standard', + 'source-layer': 'standard_railway_grouped_stations', + filter: ['step', ['zoom'], + ['all', + ['==', ['get', 'railway'], 'station'], + ['!=', ['get', 'station'], 'light_rail'], + ['!=', ['get', 'station'], 'subway'], + ['!=', ['get', 'station'], 'funicular'], + ], + 9, + ['all', + ['any', + ['==', ['get', 'railway'], 'station'], + ['==', ['get', 'railway'], 'halt'], + ], + ['!=', ['get', 'station'], 'funicular'], + ], + 10, + ['all', + ['!=', ['get', 'railway'], 'tram_stop'], + ['!=', ['get', 'station'], 'funicular'], + ], + 13, + ['!=', ['get', 'station'], 'funicular'], + ], + paint: { + 'fill-color': ['case', + ['==', ['get', 'railway'], 'tram_stop'], colors[theme].styles.standard.tram, + ['==', ['get', 'station'], 'light_rail'], colors[theme].styles.standard.light_rail, + ['==', ['get', 'station'], 'subway'], colors[theme].styles.standard.subway, + colors[theme].styles.standard.main, + ], + 'fill-opacity': ['case', + ['boolean', ['feature-state', 'hover'], false], 0.3, + 0.2, + ], + }, + }, { id: 'railway_tunnel_casing', type: 'line', @@ -2170,6 +2212,7 @@ const layers = Object.fromEntries(knownThemes.map(theme => [theme, { ['==', ['get', 'railway'], 'station'], ['==', ['get', 'railway'], 'halt'], ], + ['!=', ['get', 'railway'], 'tram_stop'], ['!=', ['get', 'station'], 'funicular'], ], 10, @@ -2177,23 +2220,16 @@ const layers = Object.fromEntries(knownThemes.map(theme => [theme, { ['!=', ['get', 'railway'], 'tram_stop'], ['!=', ['get', 'station'], 'funicular'], ], - 13, - ['!=', ['get', 'station'], 'funicular'], ], paint: { 'text-color': ['case', ['==', ['get', 'railway'], 'yard'], colors[theme].styles.standard.yardText, - ['==', ['get', 'railway'], 'tram_stop'], colors[theme].styles.standard.tramStopText, ['==', ['get', 'railway'], 'station'], colors[theme].styles.standard.stationsText, ['==', ['get', 'railway'], 'halt'], colors[theme].styles.standard.stationsText, colors[theme].styles.standard.defaultText, ], 'text-halo-color': ['case', ['boolean', ['feature-state', 'hover'], false], colors[theme].hover.textHalo, - // ['==', ['get', 'railway'], 'yard'], colors[theme].halo, - // ['==', ['get', 'railway'], 'tram_stop'], colors[theme].halo, - // ['==', ['get', 'railway'], 'station'], colors[theme].halo, - // ['==', ['get', 'railway'], 'halt'], colors[theme].halo, colors[theme].halo, ], 'text-halo-width': 1.5, @@ -2204,17 +2240,51 @@ const layers = Object.fromEntries(knownThemes.map(theme => [theme, { ['get', 'label'], 10, ['get', 'name'], + 15, + ['case', + ['>', ['get', 'count'], 1], ['concat', ['get', 'name'], ' (', ['get', 'count'], ')'], + ['get', 'name'], + ], ], // TODO light rail / subway oblique font 'text-font': ['Noto Sans Bold'], - // TODO text-variable-anchor-offset + 'text-variable-anchor': ['center', 'top', 'bottom', 'left', 'right'], 'text-size': 11, 'text-padding': 10, 'text-max-width': 5, - 'text-offset': ['case', - ['==', ['get', 'railway'], 'tram_stop'], ['literal', [0, 1]], - ['literal', [0, 0]] + }, + }, + { + id: 'railway_tram_stations', + type: 'symbol', + minzoom: 13, + source: 'openrailwaymap_standard', + 'source-layer': 'standard_railway_text_stations', + filter: ['==', ['get', 'railway'], 'tram_stop'], + paint: { + 'text-color': colors[theme].styles.standard.tramStopText, + 'text-halo-color': ['case', + ['boolean', ['feature-state', 'hover'], false], colors[theme].hover.textHalo, + colors[theme].halo, ], + 'text-halo-width': 1.5, + }, + layout: { + 'symbol-z-order': 'source', + 'text-field': ['step', ['zoom'], + ['get', 'name'], + 15, + ['case', + ['>', ['get', 'count'], 1], ['concat', ['get', 'name'], ' (', ['get', 'count'], ')'], + ['get', 'name'], + ], + ], + // TODO light rail / subway oblique font + 'text-font': ['Noto Sans Bold'], + 'text-size': 11, + 'text-padding': 10, + 'text-max-width': 5, + 'text-variable-anchor': ['top', 'bottom'], }, }, searchResults, @@ -4052,6 +4122,7 @@ const legendData = { railway: feature.feature, }, })), + "openrailwaymap_standard-standard_railway_grouped_stations": [], "openrailwaymap_standard-standard_railway_turntables": [ { legend: 'Turntable', diff --git a/proxy/proxy.conf.template b/proxy/proxy.conf.template index 1af5a7f2..3a72d2a8 100644 --- a/proxy/proxy.conf.template +++ b/proxy/proxy.conf.template @@ -94,7 +94,7 @@ server { location / { if ($http_referer ~ ^http://localhost) { rewrite ^/high$ /railway_line_high,railway_text_km last; - rewrite ^/standard$ /standard_railway_turntables,standard_railway_text_stations,standard_railway_symbols,standard_railway_switch_ref last; + rewrite ^/standard$ /standard_railway_turntables,standard_railway_text_stations,standard_railway_grouped_stations,standard_railway_symbols,standard_railway_switch_ref last; rewrite ^/speed$ /speed_railway_signals last; rewrite ^/signals$ /signals_railway_signals,signals_signal_boxes last; rewrite ^/electrification$ /electrification_signals last;