Skip to content

Commit 3b43ec0

Browse files
authored
Plugin/TagFix_Maxspeed_AT (#2506)
1 parent 02d8328 commit 3b43ec0

File tree

1 file changed

+252
-0
lines changed

1 file changed

+252
-0
lines changed

plugins/TagFix_Maxspeed_AT.py

Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
# -*- coding: utf-8 -*-
2+
3+
###########################################################################
4+
## ##
5+
## Copyrights Wolfgang Schreiter 2025 ##
6+
## ##
7+
## This program is free software: you can redistribute it and/or modify ##
8+
## it under the terms of the GNU General Public License as published by ##
9+
## the Free Software Foundation, either version 3 of the License, or ##
10+
## (at your option) any later version. ##
11+
## ##
12+
## This program is distributed in the hope that it will be useful, ##
13+
## but WITHOUT ANY WARRANTY; without even the implied warranty of ##
14+
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ##
15+
## GNU General Public License for more details. ##
16+
## ##
17+
## You should have received a copy of the GNU General Public License ##
18+
## along with this program. If not, see <http://www.gnu.org/licenses/>. ##
19+
## ##
20+
###########################################################################
21+
22+
from modules.OsmoseTranslation import T_
23+
from plugins.Plugin import Plugin
24+
25+
26+
class TagFix_Maxspeed_AT(Plugin):
27+
"""
28+
Checks for Austrian highway maxspeed tagging.
29+
"""
30+
31+
only_for = ["AT"]
32+
33+
valid_maxspeed_types = {
34+
# Agreed
35+
'sign': '',
36+
'AT:motorway': '130', 'AT:trunk': '100', 'AT:rural': '100', 'AT:urban': '50',
37+
'AT:city_limit30': '30', 'AT:city_limit40': '40',
38+
'AT:zone15': '15', 'AT:zone20': '20', 'AT:zone30': '30', 'AT:zone40': '40', 'AT:zone50': '50', 'AT:zone70': '70',
39+
'AT:shared_zone20': '20', 'AT:shared_zone30': '30',
40+
'AT:bicycle_road': '30',
41+
# Alternatives
42+
'AT:zone:20': '20', 'AT:zone:30': '30', 'AT:zone:40': '40', 'AT:zone:50': '50',
43+
'AT:zone': ''
44+
}
45+
46+
def init(self, logger):
47+
Plugin.init(self, logger)
48+
49+
self.errors[303220] = self.def_class(item=3032, level=2, tags=['maxspeed'],
50+
title=T_('Speed limit type without speed limit'),
51+
detail=T_(
52+
'''A speed limit type is given in `maxspeed:type` or `source:maxspeed`, but no speed limit is set in `maxspeed`.'''),
53+
fix=T_(
54+
'''Set `maxspeed` and either `maxspeed:type` or `source:maxspeed` as appropriate. For a list of values,
55+
see [Implicit maxspeed values](https://wiki.openstreetmap.org/wiki/Key:maxspeed#Implicit_maxspeed_values).'''),
56+
trap=T_(
57+
'''Do not just add a `maxspeed` value suitable for the type. The type may be incorrect too!
58+
Always check `highway`, all other tags related to speed and verify on the ground.'''),
59+
resource='https://wiki.openstreetmap.org/wiki/Key:maxspeed')
60+
61+
self.errors[309110] = self.def_class(item=3091, level=1, tags=['value', 'maxspeed'],
62+
title=T_('Invalid speed limit value'),
63+
detail=T_(
64+
'''The speed limit in `maxspeed` must be either numeric or `walk`. Do not specify a unit, km/h is the default.'''),
65+
fix=T_(
66+
'''Set `maxspeed` as appropriate and set speed limit type in either `maxspeed:type` or `source:maxspeed`. For a list of values,
67+
see [Implicit maxspeed values](https://wiki.openstreetmap.org/wiki/Key:maxspeed#Implicit_maxspeed_values).'''),
68+
trap=T_(
69+
'''If a speed limit type (e.g. `AT:*`) is set in `maxspeed`, do not assume it is correct!
70+
Always check `highway`, all other tags related to speed and verify on the ground.'''),
71+
resource='https://wiki.openstreetmap.org/wiki/Key:maxspeed')
72+
73+
self.errors[309111] = self.def_class(item=3091, level=2, tags=['maxspeed'],
74+
title=T_('Low speed limit value'),
75+
detail=T_(
76+
'''The speed limit in `maxspeed` is very low and no type is given in `maxspeed:type` or `source:maxspeed`.'''),
77+
fix=T_(
78+
'''For pedestrian areas and living streets (except shared zones), walking speed is the default and no
79+
speed limit or type should be set. If walking speed is signposted, set `maxspeed=walk`, `maxspeed:type=sign`
80+
and `traffic_sign=AT:54[text]` or `traffic_sign=AT:..,54[text]`. If a low speed is signposted,
81+
set `maxspeed` to the speed, `maxspeed:type=sign`.'''),
82+
trap=T_(
83+
'''Do not assume any of the data present is correct!
84+
Always check `highway`, all other tags related to speed and verify on the ground.'''),
85+
resource='https://wiki.openstreetmap.org/wiki/DE:Verkehrszeichen_in_Österreich')
86+
87+
self.errors[309112] = self.def_class(item=3091, level=2, tags=['value', 'maxspeed'],
88+
title=T_('Invalid speed limit type'),
89+
detail=T_(
90+
'''The speed limit type in `maxspeed:type` or `source:maxspeed` is not valid.'''),
91+
fix=T_(
92+
'''Set the appropriate speed limit type. For a list of values,
93+
see [Implicit maxspeed values](https://wiki.openstreetmap.org/wiki/Key:maxspeed#Implicit_maxspeed_values).'''),
94+
trap=T_(
95+
'''In case the speed limit type is `zone`, do not just change it to `AT:zone`, but to a more specific value.
96+
Do not assume any of the data present is correct!
97+
Always check `highway`, all other tags related to speed and verify on the ground.'''),
98+
resource='https://wiki.openstreetmap.org/wiki/DE:Key:maxspeed:type')
99+
100+
self.errors[303221] = self.def_class(item=3032, level=2, tags=['maxspeed'],
101+
title=T_('Multiple speed limit types'),
102+
detail=T_(
103+
'''`maxspeed:type` and `source:maxspeed` are both set. This may confuse mappers and data consumers
104+
if the values are different.'''),
105+
fix=T_(
106+
'''Set either `maxspeed:type` or `source:maxspeed`. For a list of values,
107+
see [Implicit maxspeed values](https://wiki.openstreetmap.org/wiki/Key:maxspeed#Implicit_maxspeed_values).'''),
108+
trap=T_(
109+
'''Do not assume any of the data present is correct!
110+
Always check `highway`, all other tags related to speed and verify on the ground.'''),
111+
resource='https://wiki.openstreetmap.org/wiki/DE:Key:maxspeed:type')
112+
113+
self.errors[303222] = self.def_class(item=3032, level=1, tags=['maxspeed'],
114+
title=T_('Speed limit and type mismatch'),
115+
detail=T_(
116+
'''The speed limit in `maxspeed` is not consistent with the speed limit type in `maxspeed:type` or `source:maxspeed`.'''),
117+
fix=T_(
118+
'''Adjust `maxspeed`, `maxspeed:type` or `source:maxspeed` as appropriate. For a list of values,
119+
see [Implicit maxspeed values](https://wiki.openstreetmap.org/wiki/Key:maxspeed#Implicit_maxspeed_values).'''),
120+
trap=T_(
121+
'''Do not assume any of the data present is correct!
122+
Always check `highway`, all other tags related to speed and verify on the ground.'''),
123+
resource='https://wiki.openstreetmap.org/wiki/DE:Key:maxspeed:type')
124+
125+
126+
def way(self, data, tags, nds):
127+
err = []
128+
129+
if tags.get('highway') is None:
130+
return err
131+
132+
# Checks apply only to these tags
133+
maxspeed = tags.get('maxspeed')
134+
maxspeed_type = tags.get('maxspeed:type')
135+
source_maxspeed = tags.get('source:maxspeed')
136+
137+
# Error: maxspeed type without maxspeed
138+
if not maxspeed:
139+
if maxspeed_type or source_maxspeed:
140+
err.append({'class': 303220, 'text': T_('`source:maxspeed` or `maxspeed:type` = `{0}` without maxspeed',
141+
maxspeed_type if maxspeed_type else source_maxspeed)})
142+
return err
143+
144+
# Error: maxspeed not numeric or 'walk'
145+
if maxspeed.endswith(' km/h'):
146+
maxspeed = maxspeed[:-5]
147+
if not maxspeed.isdigit() and maxspeed != 'walk':
148+
return {'class': 309110, 'text': T_('Invalid maxspeed: `{0}`', maxspeed)}
149+
150+
# Error: maxspeed suspiciously low, probably 'walk'; needs verification
151+
# except for speeds < 5 (covered in Number.py) and if signposted
152+
if maxspeed.isdigit():
153+
maxspeed_num = int(maxspeed)
154+
if (maxspeed_num > 4) and (maxspeed_num < 15) and (maxspeed_type != 'sign') and (source_maxspeed != 'sign'):
155+
return {'class': 309111, 'text': T_('Low maxspeed: `{0}`', maxspeed)}
156+
157+
valid_type = None
158+
if maxspeed_type:
159+
# Error: maxspeed:type is invalid
160+
if maxspeed_type in self.valid_maxspeed_types.keys():
161+
valid_type = maxspeed_type
162+
else:
163+
err.append({'class': 309112,
164+
'text': T_('Invalid maxspeed:type: `{0}`', maxspeed_type)})
165+
if source_maxspeed:
166+
# Error: source:maxspeed equal to maxspeed:type
167+
# Disabled for now to avoid excessive warnings; perform bulk cleanup first
168+
if maxspeed_type == source_maxspeed:
169+
# err.append({'class': 303221,
170+
# 'text': T_('Duplicate speed limit type: `{0}`', maxspeed_type)})
171+
pass
172+
# Error: source:maxspeed contains different maxspeed type
173+
elif source_maxspeed.startswith('AT:') or source_maxspeed in {'zone', 'sign', 'walk'}:
174+
err.append({'class': 303221,
175+
'text': T_('Conflicting speed limit types: `{0}`<>`{1}`', maxspeed_type, source_maxspeed)})
176+
elif source_maxspeed:
177+
# Error: source:maxspeed is invalid
178+
if source_maxspeed in self.valid_maxspeed_types.keys():
179+
valid_type = source_maxspeed
180+
elif source_maxspeed.startswith('AT:') or source_maxspeed in {'zone', 'walk'}:
181+
err.append({'class': 309112,
182+
'text': T_('Invalid source:maxspeed: `{0}`', source_maxspeed)})
183+
184+
# Error: maxspeed type doesn't match maxspeed
185+
# except for types covered in TagFix_Maxspeed plugin and types without specific speed
186+
if valid_type and valid_type not in {'AT:motorway', 'AT:trunk', 'AT:rural', 'AT:urban'}:
187+
if self.valid_maxspeed_types.get(valid_type) and (self.valid_maxspeed_types.get(valid_type) != maxspeed):
188+
err.append({'class': 303222,
189+
'text': T_('maxspeed and type mismatch: `{0}`<>`{1}`', maxspeed, valid_type)})
190+
191+
return err
192+
193+
###########################################################################
194+
from plugins.Plugin import TestPluginCommon
195+
196+
197+
class Test(TestPluginCommon):
198+
def test(self):
199+
plugin = TagFix_Maxspeed_AT(None)
200+
plugin.init(None)
201+
202+
# No error if not a highway
203+
assert not plugin.way(None, {'maxspeed_type': 'dont know', 'source:maxspeed': 'unknown'}, None)
204+
205+
# No error if valid
206+
assert not plugin.way(None, {'highway': 'primary'}, None)
207+
208+
assert not plugin.way(None, {'highway': 'primary', 'maxspeed': '100 km/h'}, None)
209+
210+
assert not plugin.way(None, {'highway': 'living_street', 'maxspeed': 'walk'}, None)
211+
212+
assert not plugin.way(None, {'highway': 'residential', 'maxspeed': '5', 'source:maxspeed': 'sign'}, None)
213+
214+
assert not plugin.way(None, {'highway': 'secondary', 'maxspeed': '70', 'maxspeed:type': 'sign'}, None)
215+
216+
assert not plugin.way(None, {'highway': 'tertiary', 'maxspeed': '50', 'source:maxspeed': 'AT:urban'}, None)
217+
218+
assert not plugin.way(None, {'highway': 'unclassified', 'maxspeed': '100', 'maxspeed:type': 'AT:rural',
219+
'source:maxspeed': 'read it in the news'}, None)
220+
221+
# Error when maxspeed type without maxspeed
222+
self.check_err(plugin.way(None, {'highway': 'secondary', 'maxspeed:type': 'sign'}, None))
223+
224+
self.check_err(plugin.way(None, {'highway': 'secondary', 'source:maxspeed': 'sign'}, None))
225+
226+
# Error when maxspeed not numeric or walk
227+
self.check_err(plugin.way(None, {'highway': 'tertiary', 'maxspeed': 'fast'}, None))
228+
229+
self.check_err(plugin.way(None, {'highway': 'tertiary', 'maxspeed': '70 mph'}, None))
230+
231+
# Error when maxspeed too low
232+
self.check_err(plugin.way(None, {'highway': 'residential', 'maxspeed': '5'}, None))
233+
234+
# Error when invalid speed limit type
235+
self.check_err(plugin.way(None, {'highway': 'residential', 'maxspeed': '50',
236+
'source:maxspeed': 'AT:city'}, None))
237+
238+
self.check_err(plugin.way(None, {'highway': 'secondary', 'maxspeed': '70', 'maxspeed:type': 'yes'}, None))
239+
240+
# Error when speed limit type duplication
241+
self.check_err(plugin.way(None, {'highway': 'unclassified', 'maxspeed': '100',
242+
'maxspeed:type': 'AT:zone40', 'source:maxspeed': 'AT:rural'}, None))
243+
244+
self.check_err(plugin.way(None, {'highway': 'unclassified', 'maxspeed': '100',
245+
'maxspeed:type': 'AT:rural', 'source:maxspeed': 'AT:urban'}, None))
246+
247+
# Error when speed and type mismatch
248+
self.check_err(plugin.way(None, {'highway': 'secondary', 'maxspeed': '70',
249+
'maxspeed:type': 'AT:city_limit30'}, None))
250+
251+
self.check_err(plugin.way(None, {'highway': 'tertiary', 'maxspeed': '50',
252+
'source:maxspeed': 'AT:city_limit30'}, None))

0 commit comments

Comments
 (0)