Skip to content

Commit 71fc72b

Browse files
authored
Ensure milestone zeros work correctly (#497)
Fixes #494 Followup of #481 Part of #392 Changes: - Match exact positions against non-exact positions. If exact positions do not have a corresponding non-exact position, they are used as is. - Use textual position (not exact position) for determining if a milestone is a zero. (https://openrailwaymap.app/#view=10.39/49.5818/11.4117&style=signals) Before: <img width="1418" height="1110" alt="image" src="https://github.com/user-attachments/assets/2f26bb69-58b7-4545-988c-8c4c82ac613b" /> After: <img width="1417" height="1113" alt="image" src="https://github.com/user-attachments/assets/23e64517-4986-406a-8bf9-db51c51a3b02" />
1 parent 552e3b6 commit 71fc72b

File tree

5 files changed

+74
-31
lines changed

5 files changed

+74
-31
lines changed

import/openrailwaymap.lua

Lines changed: 61 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,7 @@ local railway_positions = osm2pgsql.define_table({
393393
{ column = 'railway', type = 'text' },
394394
{ column = 'position_numeric', type = 'real' },
395395
{ column = 'position_text', type = 'text', not_null = true },
396+
{ column = 'position_exact', type = 'text' },
396397
{ column = 'type', type = 'text', not_null = true },
397398
{ column = 'zero', type = 'boolean' },
398399
{ column = 'name', type = 'text' },
@@ -698,37 +699,58 @@ function name_tags(tags)
698699
return found_name_tags
699700
end
700701

702+
function position_is_zero(position)
703+
return position:find('^%-?%d+$') or position:find('^%-?%d*[,/.]0*$')
704+
end
705+
701706
function parse_railway_position(position)
702707
if not position then
703708
return nil
704709
end
705710

706711
if position:find('^mi:') then
712+
local stripped_position = position:gsub('^mi: ?', '')
713+
local position_with_dot = stripped_position:gsub(',', '.')
714+
707715
return {
708-
text = position:gsub('^mi:', ''),
716+
text = stripped_position,
717+
numeric = tonumber(position_with_dot),
709718
type = 'mi',
719+
zero = position_is_zero(stripped_position),
720+
exact = nil,
710721
}
711722
elseif position:find('^pkm:') then
723+
local stripped_position = position:gsub('^pkm: ?', '')
724+
local position_with_dot = stripped_position:gsub(',', '.')
725+
712726
return {
713-
text = position:gsub('^pkm:', ''),
727+
text = stripped_position,
728+
numeric = tonumber(position_with_dot),
714729
type = 'pkm',
730+
zero = position_is_zero(stripped_position),
731+
exact = nil,
715732
}
716733
else
734+
local position_with_dot = position:gsub(',', '.')
735+
717736
return {
718737
text = position,
738+
numeric = tonumber(position_with_dot),
719739
type = 'km',
740+
zero = position_is_zero(position),
741+
exact = nil,
720742
}
721743
end
722744
end
723745

724746
function parse_railway_positions(position, position_exact)
725-
-- Parse one or more positions from a position or exact position
747+
-- Collect positions, from both normal and exact positions, eliminating duplicates
726748

727-
if position_exact then
728-
local parsed_positions = {}
729-
local found_positions = false
749+
local parsed_positions = {}
750+
local found_positions = false
730751

731-
for part in string.gmatch(position_exact, '[^;]+') do
752+
if position then
753+
for part in string.gmatch(position, '[^;]+') do
732754
local stripped_part = part:gsub('^ ', '')
733755
local position = parse_railway_position(stripped_part)
734756

@@ -737,38 +759,48 @@ function parse_railway_positions(position, position_exact)
737759
found_positions = true
738760
end
739761
end
740-
741-
if found_positions then
742-
return parsed_positions
743-
end
744762
end
745763

746-
-- Fall back to non-exact positions
747-
748-
if position then
749-
local parsed_positions = {}
750-
local found_positions = false
751-
752-
for part in string.gmatch(position, '[^;]+') do
764+
if position_exact then
765+
for part in string.gmatch(position_exact, '[^;]+') do
753766
local stripped_part = part:gsub('^ ', '')
754767
local position = parse_railway_position(stripped_part)
755768

756769
if position then
757-
table.insert(parsed_positions, position)
758-
found_positions = true
759-
end
760-
end
770+
local found_existing_position = false
771+
772+
if found_positions and position.numeric ~= nil then
773+
for _, existing_position in ipairs(parsed_positions) do
774+
-- Verify if the position is close to another position. Note that this matches slightly outside the first decimal's precision.
775+
if existing_position.numeric ~= nil and math.abs(existing_position.numeric - position.numeric) < 0.1 then
776+
existing_position.numeric = position.numeric
777+
existing_position.exact = position.text
778+
found_existing_position = true
779+
end
780+
end
781+
end
761782

762-
if found_positions then
763-
return parsed_positions
783+
if not found_existing_position then
784+
table.insert(parsed_positions, position)
785+
found_positions = true
786+
end
787+
end
764788
end
765789
end
766790

767-
return nil
791+
if found_positions then
792+
return parsed_positions
793+
else
794+
return nil
795+
end
768796
end
769797

770798
function format_railway_position(item)
771-
return item.text .. ' (' .. item.type .. ')'
799+
if item.exact then
800+
return item.text .. ' @ ' .. item.exact.. ' (' .. item.type .. ')'
801+
else
802+
return item.text .. ' (' .. item.type .. ')'
803+
end
772804
end
773805

774806
local railway_station_values = osm2pgsql.make_check_values_func({'station', 'halt', 'tram_stop', 'service_station', 'yard', 'junction', 'spur_junction', 'crossover', 'site'})
@@ -926,16 +958,14 @@ function osm2pgsql.process_node(object)
926958

927959
if railway_position_values(tags.railway) and (position or position_exact) then
928960
for _, position in ipairs(parse_railway_positions(position, position_exact)) do
929-
local position_with_dots, _ = position.text:gsub(',', '.')
930-
local position_numeric = tonumber(position_with_dots)
931-
932961
railway_positions:insert({
933962
way = object:as_point(),
934963
railway = tags.railway,
935-
position_numeric = position_numeric,
964+
position_numeric = position.numeric,
936965
position_text = position.text,
966+
position_exact = position.exact,
937967
type = position.type,
938-
zero = position_numeric ~= nil and ((math.abs(position_numeric) % 1) < 0.001),
968+
zero = position.zero,
939969
name = tags['name'],
940970
ref = tags['ref'],
941971
operator = tags['operator'],

import/sql/tile_views.sql

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ CREATE OR REPLACE VIEW railway_text_km AS
492492
way,
493493
railway,
494494
position_text as pos,
495+
position_exact as pos_exact,
495496
zero,
496497
round(position_numeric) as pos_int,
497498
type,

martin/configuration.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ postgres:
188188
osm_id: integer
189189
railway: string
190190
pos: string
191+
pos_exact: string
191192
pos_int: integer
192193
zero: boolean
193194
type: string

proxy/js/features.mjs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -513,6 +513,9 @@ const features = {
513513
pos: {
514514
name: 'Position',
515515
},
516+
pos_exact: {
517+
name: 'Exact position',
518+
},
516519
type: {
517520
name: 'Type',
518521
},

proxy/js/styles.mjs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4658,6 +4658,7 @@ const legendData = {
46584658
zero: true,
46594659
pos_int: '47',
46604660
pos: '47.0',
4661+
pos_exact: '47.012',
46614662
type: 'km',
46624663
},
46634664
},
@@ -5251,6 +5252,7 @@ const legendData = {
52515252
zero: true,
52525253
pos_int: '47',
52535254
pos: '47.0',
5255+
pos_exact: '47.012',
52545256
type: 'km',
52555257
},
52565258
},
@@ -5475,6 +5477,7 @@ const legendData = {
54755477
zero: true,
54765478
pos_int: '47',
54775479
pos: '47.0',
5480+
pos_exact: '47.012',
54785481
type: 'km',
54795482
},
54805483
},
@@ -5714,6 +5717,7 @@ const legendData = {
57145717
zero: true,
57155718
pos_int: '47',
57165719
pos: '47.0',
5720+
pos_exact: '47.012',
57175721
type: 'km',
57185722
},
57195723
},
@@ -6083,6 +6087,7 @@ const legendData = {
60836087
zero: true,
60846088
pos_int: '47',
60856089
pos: '47.0',
6090+
pos_exact: '47.012',
60866091
type: 'km',
60876092
},
60886093
},
@@ -6153,6 +6158,7 @@ const legendData = {
61536158
zero: true,
61546159
pos_int: '47',
61556160
pos: '47.0',
6161+
pos_exact: '47.012',
61566162
type: 'km',
61576163
},
61586164
},
@@ -6223,6 +6229,7 @@ const legendData = {
62236229
zero: true,
62246230
pos_int: '47',
62256231
pos: '47.0',
6232+
pos_exact: '47.012',
62266233
type: 'km',
62276234
},
62286235
},
@@ -6301,6 +6308,7 @@ const legendData = {
63016308
zero: true,
63026309
pos_int: '47',
63036310
pos: '47.0',
6311+
pos_exact: '47.012',
63046312
type: 'km',
63056313
},
63066314
},

0 commit comments

Comments
 (0)