Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion api/features.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -1340,10 +1340,13 @@ const features = {
operator: {
name: 'Operator',
},
conversion: {
name: 'Conversion',
},
voltage: {
name: 'Voltage',
format: {
template: '%s V',
template: '%d V',
},
},
frequency: {
Expand Down
17 changes: 14 additions & 3 deletions api/test/api.hurl
Original file line number Diff line number Diff line change
Expand Up @@ -490,6 +490,19 @@ jsonpath "$.position" count == 1
jsonpath "$.position[0]" == "11.6 (km)"
jsonpath "$.structure" == "lattice"

# Substation
GET {{base_url}}/feature/openrailwaymap_electrification/electrification_substation/62032242
HTTP 200
Content-Type: application/json
[Asserts]
jsonpath "$.note" == null
jsonpath "$.description" == null
jsonpath "$.osm_id" == 62032242
jsonpath "$.osm_type" == "W"
jsonpath "$.voltage" count == 2
jsonpath "$.voltage[0]" == 30000
jsonpath "$.voltage[1]" == 750

# Substation
GET {{base_url}}/feature/openrailwaymap_electrification/electrification_substation/320522867
HTTP 200
Expand All @@ -499,9 +512,7 @@ jsonpath "$.note" == null
jsonpath "$.description" == null
jsonpath "$.osm_id" == 320522867
jsonpath "$.osm_type" == "W"
jsonpath "$.voltage" count == 2
jsonpath "$.voltage[0]" == "30000"
jsonpath "$.voltage[1]" == "750"
jsonpath "$.conversion" == "30kV 50 Hz ⇒ 750V ="

# Railway crossing
GET {{base_url}}/feature/openrailwaymap_standard/standard_railway_switch_ref/2101295628
Expand Down
85 changes: 62 additions & 23 deletions import/openrailwaymap.lua
Original file line number Diff line number Diff line change
Expand Up @@ -650,8 +650,9 @@ local substation = osm2pgsql.define_table({
{ column = 'name', type = 'text' },
{ column = 'location', type = 'text' },
{ column = 'operator', type = 'text' },
{ column = 'voltage', sql_type = 'text[]' },
{ column = 'frequency', sql_type = 'text[]' },
{ column = 'voltage', sql_type = 'integer[]' },
{ column = 'frequency', sql_type = 'real[]' },
{ column = 'conversion', type = 'text' },
{ column = 'wikidata', type = 'text' },
{ column = 'wikimedia_commons', type = 'text' },
{ column = 'wikimedia_commons_file', type = 'text' },
Expand Down Expand Up @@ -765,8 +766,31 @@ function electrification_state(tags)
return nil, nil, nil, nil, nil
end

-- Split a value and trim the parts
function split_semicolon(value)
if not value then
return nil
end

local items = {}
local has_items = false
for part in string.gmatch(value, '[^;]+') do
local stripped_part = strip_prefix(part, ' ')
if stripped_part then
table.insert(items, stripped_part)
has_items = true
end
end

if has_items then
return items
else
return nil
end
end

-- Put the items in a table into a raw SQL array string (quoted and comma-delimited)
function to_sql_array(items)
-- Put the items in a table into a raw SQL array string (quoted and comma-delimited)
if not items then
return nil
end
Expand All @@ -778,31 +802,20 @@ function to_sql_array(items)
result = result .. ','
end

-- Raw SQL array syntax
result = result .. "\"" .. item:gsub("\\", "\\\\"):gsub("\"", "\\\"") .. "\""
if type(item) == "number" then
result = result .. tostring(item)
else
-- Raw SQL array syntax
result = result .. "\"" .. item:gsub("\\", "\\\\"):gsub("\"", "\\\"") .. "\""
end
end

return result .. '}'
end

-- Split a value and turn it into a raw SQL array (quoted and comma-delimited)
function split_semicolon_to_sql_array(value)
if not value then
return nil
end

local items = {}

if value then
for part in string.gmatch(value, '[^;]+') do
local stripped_part = strip_prefix(part, ' ')
if stripped_part then
table.insert(items, stripped_part)
end
end
end

return to_sql_array(items)
return to_sql_array(split_semicolon(value))
end

local railway_state_tags = {
Expand Down Expand Up @@ -1086,6 +1099,29 @@ function stop_position_type(tags)
end
end

function format_voltage_frequency(voltage, frequency)
local voltage_text = voltage < 1000.0 and string.format('%.0dV', voltage) or string.format('%.1dkV', voltage / 1000.0)

if frequency == 0 then
return string.format("%s =", voltage_text)
else
return string.format("%s %.2d Hz", voltage_text, frequency)
end
end

function substation_voltage_frequency(voltage, frequency)
local voltages = map(split_semicolon(voltage), function(it) return tonumber(it) end)
local frequencies = map(split_semicolon(frequency), function(it) return tonumber(it) end)

if voltages and frequencies and #voltages == 2 and #frequencies == 2 then
-- conversion between source and destination
local conversion = string.format('%s ⇒ %s', format_voltage_frequency(voltages[1], frequencies[1]), format_voltage_frequency(voltages[2], frequencies[2]))
return nil, nil, conversion
else
return voltages, frequencies, nil
end
end

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(tag_functions.poi_railway_values)
local railway_signal_values = osm2pgsql.make_check_values_func({'signal', 'buffer_stop', 'derail', 'vacancy_detection'})
Expand Down Expand Up @@ -1582,15 +1618,18 @@ function osm2pgsql.process_way(object)
end

if tags.power == 'substation' and tags.substation == 'traction' then
local voltages, frequencies, conversion = substation_voltage_frequency(tags.voltage, tags.frequency)

substation:insert({
way = object:as_polygon(),
feature = 'traction',
name = tags.name,
ref = tags.ref,
location = tags.location,
operator = tags.operator,
voltage = split_semicolon_to_sql_array(tags.voltage),
frequency = split_semicolon_to_sql_array(tags.frequency),
voltage = to_sql_array(voltages),
frequency = to_sql_array(frequencies),
conversion = conversion,
wikidata = tags.wikidata,
wikimedia_commons = wikimedia_commons,
wikimedia_commons_file = wikimedia_commons_file,
Expand Down
1 change: 1 addition & 0 deletions import/sql/tile_views.sql
Original file line number Diff line number Diff line change
Expand Up @@ -1616,6 +1616,7 @@ CREATE OR REPLACE VIEW electrification_substation_view AS
operator,
voltage,
frequency,
conversion,
wikidata,
wikimedia_commons,
wikimedia_commons_file,
Expand Down
19 changes: 18 additions & 1 deletion import/test/test_import_substation.lua
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,23 @@ osm2pgsql.process_way({
})
assert.eq(osm2pgsql.get_and_clear_imported_data(), {
substation = {
{ feature = 'traction', location = 'indoor', voltage = '{"400000","225000","63000"}', frequency = '{"50","0"}', name = 'name', ref = 'ref', operator = 'operator', way = way },
{ feature = 'traction', location = 'indoor', voltage = '{400000,225000,63000}', frequency = '{50,0}', name = 'name', ref = 'ref', operator = 'operator', way = way },
},
})

osm2pgsql.process_way({
tags = {
['power'] = 'substation',
['substation'] = 'traction',
['voltage'] = '400000;750',
['frequency'] = '50;0',
},
as_polygon = function ()
return way
end,
})
assert.eq(osm2pgsql.get_and_clear_imported_data(), {
substation = {
{ feature = 'traction', conversion = '400kV 50 Hz ⇒ 750V =', way = way },
},
})
2 changes: 1 addition & 1 deletion proxy/js/ui.js
Original file line number Diff line number Diff line change
Expand Up @@ -2398,7 +2398,7 @@ function popupContent(feature, abortController) {
if (!format) {
return stringValue;
} else if (format.template) {
return format.template.replace('%s', () => stringValue).replace(/%(\.(\d+))?d/, (_1, _2, decimals) => Number(value).toFixed(Number(decimals)));
return format.template.replace('%s', () => stringValue).replace(/%(\.(\d+))?d/, (_1, _2, decimals) => Number(stringValue).toFixed(Number(decimals)));
} else if (format.lookup) {
const lookupCatalog = features && features[format.lookup];
if (!lookupCatalog) {
Expand Down
Loading