diff --git a/api/prepare-api.sh b/api/prepare-api.sh index 674be2e3..a838fc8b 100755 --- a/api/prepare-api.sh +++ b/api/prepare-api.sh @@ -53,5 +53,5 @@ echo 'Building API container' docker compose build api echo 'Cleaning up' -[ -n "$SKIP_CLEANUP" ] || rm -rf api/postgres-data -[ -n "$SKIP_CLEANUP" ] || rm -f api/api.sql +[ -n "${SKIP_CLEANUP:-}" ] || rm -rf api/postgres-data +[ -n "${SKIP_CLEANUP:-}" ] || rm -f api/api.sql diff --git a/api/prepare_facilities.sql b/api/prepare_facilities.sql index b9793dfa..152e03f8 100644 --- a/api/prepare_facilities.sql +++ b/api/prepare_facilities.sql @@ -16,7 +16,7 @@ CREATE TABLE openrailwaymap_ref AS WHERE (railway_ref IS NOT NULL OR uic_ref IS NOT NULL) AND ( - railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site', 'tram_stop') + railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site') -- TODO support other states as well ); @@ -65,7 +65,7 @@ CREATE TABLE openrailwaymap_facilities_for_search AS way AS geom FROM stations_with_route_counts WHERE - railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site', 'tram_stop') + railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site') -- TODO support other states as well ) AS organised ) AS duplicated; diff --git a/import/openrailwaymap.lua b/import/openrailwaymap.lua index d495857b..48145135 100644 --- a/import/openrailwaymap.lua +++ b/import/openrailwaymap.lua @@ -207,7 +207,7 @@ end -- TODO clean up unneeded tags -local railway_station_values = osm2pgsql.make_check_values_func({'station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site', 'tram_stop'}) +local railway_station_values = osm2pgsql.make_check_values_func({'station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site'}) local railway_poi_values = osm2pgsql.make_check_values_func({'crossing', 'level_crossing', 'phone', 'tram_stop', 'border', 'owner_change', 'radio', 'lubricator'}) local railway_signal_values = osm2pgsql.make_check_values_func({'signal', 'buffer_stop', 'derail', 'vacancy_detection'}) local railway_position_values = osm2pgsql.make_check_values_func({'milestone', 'level_crossing', 'crossing'}) diff --git a/import/sql/get_station_importance.sql b/import/sql/get_station_importance.sql index 92ef7bd6..bf59d92d 100644 --- a/import/sql/get_station_importance.sql +++ b/import/sql/get_station_importance.sql @@ -46,7 +46,7 @@ CREATE OR REPLACE VIEW station_nodes_stop_positions_rel_count AS FROM stations AS s LEFT OUTER JOIN stop_positions_and_their_routes_clustered AS sprc ON (sprc.stop_name = s.name AND ST_DWithin(s.way, sprc.geom, 400)) - WHERE s.railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site', 'tram_stop'); + WHERE s.railway IN ('station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site'); -- Join clustered platforms with station nodes CREATE OR REPLACE VIEW station_nodes_platforms_rel_count AS @@ -74,7 +74,7 @@ CREATE MATERIALIZED VIEW IF NOT EXISTS stations_with_route_counts AS UNION ALL SELECT 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', 'tram_stop') + 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; diff --git a/proxy/js/ui.js b/proxy/js/ui.js index 7f4c8498..0aff5669 100644 --- a/proxy/js/ui.js +++ b/proxy/js/ui.js @@ -13,11 +13,30 @@ const backgroundRasterUrlControl = document.getElementById('backgroundRasterUrl' const legend = document.getElementById('legend') const legendMapContainer = document.getElementById('legend-map') +const icons = { + railway: { + station: '', + halt: '', + tram_stop: '', + service_station: '', + yard: '', + junction: '', + spur_junction: '', + crossover: '', + site: '', + milestone: '', + level_crossing: '', + crossing: '', + }, + edit: '', +} + function registerLastSearchResults(results) { const data = { type: 'FeatureCollection', features: results.map(result => ({ type: 'Feature', + properties: result, geometry: { type: 'Point', coordinates: [result.latitude, result.longitude], @@ -50,9 +69,14 @@ function searchForFacilities(type, term) { const queryString = facilitySearchQuery(type, term) fetch(`${location.origin}/api/facility?${queryString}`) .then(result => result.json()) + .then(result => result.map(item => ({ + ...item, + label: item.name, + icon: icons.railway[item.railway] ?? null, + }))) .then(result => { console.info('facility search result', result) - showSearchResults(result, item => item.name) + showSearchResults(result) }) .catch(error => { hideSearchResults(); @@ -68,9 +92,14 @@ function searchForMilestones(ref, position) { } else { fetch(`${location.origin}/api/milestone?ref=${encodeURIComponent(ref)}&position=${encodeURIComponent(position)}`) .then(result => result.json()) + .then(result => result.map(item => ({ + ...item, + label: `Line ${item.ref} @ ${item.position}`, + icon: icons.railway[item.railway] ?? null, + }))) .then(result => { console.info('milestone search result', result) - showSearchResults(result, item => `Ref: ${item.ref}, KM: ${item.position}`) + showSearchResults(result) }) .catch(error => { hideSearchResults(); @@ -80,7 +109,7 @@ function searchForMilestones(ref, position) { } } -function showSearchResults(results, renderItem) { +function showSearchResults(results) { registerLastSearchResults(results); const bounds = results.length > 0 @@ -112,7 +141,8 @@ function showSearchResults(results, renderItem) {
${results.map(result => ` - ${renderItem(result)} + ${result.icon ? `${result.icon}` : ''} + ${result.label} ` ).join('')}
@@ -127,10 +157,10 @@ function hideSearchResults() { function showSearch() { searchBackdrop.style.display = 'block'; - if (searchFacilitiesForm.style.display === 'block') { + if (searchFacilitiesForm.style.display !== 'none') { searchFacilityTermField.focus(); searchFacilityTermField.select(); - } else if (searchMilestonesForm.style.display === 'block') { + } else if (searchMilestonesForm.style.display !== 'none') { searchMilestoneRefField.focus(); searchMilestoneRefField.select(); } @@ -267,6 +297,7 @@ let selectedStyle = determineStyleFromHash(window.location.hash) // Configuration // const localStorageKey = 'openrailwaymap-configuration'; + function readConfiguration(localStorage) { const rawConfiguration = localStorage.getItem(localStorageKey); if (rawConfiguration) { @@ -283,9 +314,11 @@ function readConfiguration(localStorage) { return {}; } } + function storeConfiguration(localStorage, configuration) { localStorage.setItem(localStorageKey, JSON.stringify(configuration)); } + function updateConfiguration(name, value) { configuration[name] = value; storeConfiguration(localStorage, configuration) @@ -548,3 +581,40 @@ const onStylesheetChange = styleSheet => { map.on('load', () => onMapZoom(map.getZoom())); map.on('zoomend', () => onMapZoom(map.getZoom())); + +// When a click event occurs on a feature in the places layer, open a popup at the +// location of the feature, with description HTML from its properties. +map.on('click', 'search', (e) => { + const feature = e.features[0]; + const coordinates = feature.geometry.coordinates.slice(); + const properties = feature.properties; + const content = ` +
+ ${properties.icon ? `${properties.icon}` : ''} + ${properties.label} + ${icons.edit} +
+
+ ${properties.railway_ref ? `reference: ${properties.railway_ref}` : ''} + ${properties.ref ? `reference: ${properties.ref}` : ''} + ${properties.uic_ref ? `UIC reference: ${properties.uic_ref}` : ''} + ${properties.position ? `position: ${properties.position}` : ''} + ${properties.operator ? `operator: ${properties.operator}` : ''} +
+ `; + + new maplibregl.Popup() + .setLngLat(coordinates) + .setHTML(content) + .addTo(map); +}); + +// Change the cursor to a pointer when the mouse is over the places layer. +map.on('mouseenter', 'search', () => { + map.getCanvas().style.cursor = 'pointer'; +}); + +// Change it back to a pointer when it leaves. +map.on('mouseleave', 'search', () => { + map.getCanvas().style.cursor = ''; +});