From fcaada79b7fcdecaef1d2b3915231b5b1fed8064 Mon Sep 17 00:00:00 2001 From: Famlam Date: Sat, 14 Sep 2024 01:01:31 +0200 Subject: [PATCH] Add check for name being part of alt_name JOSMs validator rules already check if the value of name is equal to alt_name. This rule instead checks if the value of name is part of alt_name when alt_name contains multiple values --- plugins/TagFix_MultipleTag2.py | 63 ++++++++++++++++++++ plugins/TagFix_MultipleTag2.validator.mapcss | 13 ++++ 2 files changed, 76 insertions(+) diff --git a/plugins/TagFix_MultipleTag2.py b/plugins/TagFix_MultipleTag2.py index 2a9d25ce5..93a76224d 100644 --- a/plugins/TagFix_MultipleTag2.py +++ b/plugins/TagFix_MultipleTag2.py @@ -26,6 +26,7 @@ def init(self, logger): self.errors[40201] = self.def_class(item = 4020, level = 1, tags = mapcss.list_('tag') + mapcss.list_('fix:chair', 'highway', 'roundabout'), title = mapcss.tr('Roundabout as area')) self.errors[40303] = self.def_class(item = 4030, level = 1, tags = mapcss.list_('tag') + mapcss.list_('fix:chair'), title = mapcss.tr('Tag conflict'), trap = mapcss.tr('Sometimes the object needs both tags.'), detail = mapcss.tr('The object contains two incompatible tags.')) self.errors[40401] = self.def_class(item = 4040, level = 2, tags = mapcss.list_('tag') + mapcss.list_('fix:chair', 'name', 'tourism'), title = mapcss.tr('{0} with {1}, likely this is a single pitch instead', mapcss._tag_uncapture(capture_tags, '{0.tag}'), mapcss._tag_uncapture(capture_tags, '{1.tag}')), detail = mapcss.tr('The camping site has a numeric name. Numeric identifiers are much more common for a single pitch (`tourism=camp_pitch`) within a camping site. Possibly the two were interchanged?')) + self.errors[50802] = self.def_class(item = 5080, level = 3, tags = mapcss.list_('tag') + mapcss.list_('name', 'fix:chair'), title = mapcss.tr('{0} contains the value of {1}', mapcss._tag_uncapture(capture_tags, '{0.key}'), mapcss._tag_uncapture(capture_tags, '{1.key}')), trap = mapcss.tr('Possibly a different `alt_name` that is very similar to `name` was meant. Alternative names are often similar (but never equal) to the name.')) self.errors[71301] = self.def_class(item = 7130, level = 3, tags = mapcss.list_('tag') + mapcss.list_('highway', 'maxheight', 'fix:survey'), title = mapcss.tr('Missing maxheight tag'), detail = mapcss.tr('Missing `maxheight=*` or `maxheight:physical=*` for a tunnel or a way under a bridge.')) self.errors[303210] = self.def_class(item = 3032, level = 3, tags = mapcss.list_('tag'), title = mapcss.tr('Fence with {0} tag, also add {1}', mapcss._tag_uncapture(capture_tags, '{1.key}'), mapcss._tag_uncapture(capture_tags, '{2.key}'))) self.errors[303211] = self.def_class(item = 3032, level = 3, tags = mapcss.list_('tag'), title = mapcss.tr('suspicious tag combination')) @@ -215,6 +216,28 @@ def node(self, data, tags): 'name']) }}) + # *[alt_name][name][alt_name~=*name][alt_name!=*name] + if ('alt_name' in keys and 'name' in keys): + match = False + if not match: + capture_tags = {} + try: match = ((mapcss._tag_capture(capture_tags, 0, tags, 'alt_name')) and (mapcss._tag_capture(capture_tags, 1, tags, 'name')) and (mapcss.list_contains(mapcss._tag_capture(capture_tags, 2, tags, 'alt_name'), mapcss._value_capture(capture_tags, 2, mapcss.tag(tags, 'name')))) and (mapcss._tag_capture(capture_tags, 3, tags, 'alt_name') != mapcss._value_capture(capture_tags, 3, mapcss.tag(tags, 'name')))) + except mapcss.RuleAbort: pass + if match: + # -osmoseTags:list("name","fix:chair") + # -osmoseTrap:tr("Possibly a different `alt_name` that is very similar to `name` was meant. Alternative names are often similar (but never equal) to the name.") + # -osmoseItemClassLevel:"5080/50802/3" + # throwWarning:tr("{0} contains the value of {1}","{0.key}","{1.key}") + # fixAdd:concat("{0.key}=",join_list(";",trim_list(split(";",replace(concat(";",join_list(";",trim_list(split(";",tag("alt_name")))),";"),concat(";",tag("name"),";"),";"))))) + # assertMatch:"node name=y alt_name=\"x; y; z\"" + # assertMatch:"node name=y alt_name=x;y;z" + # assertNoMatch:"node name=y alt_name=xyz" + # assertNoMatch:"node name=y alt_name=y" + err.append({'class': 50802, 'subclass': 0, 'text': mapcss.tr('{0} contains the value of {1}', mapcss._tag_uncapture(capture_tags, '{0.key}'), mapcss._tag_uncapture(capture_tags, '{1.key}')), 'allow_fix_override': True, 'fix': { + '+': dict([ + (mapcss.concat(mapcss._tag_uncapture(capture_tags, '{0.key}='), mapcss.join_list(';', mapcss.trim_list(mapcss.split(';', mapcss.replace(mapcss.concat(';', mapcss.join_list(';', mapcss.trim_list(mapcss.split(';', mapcss.tag(tags, 'alt_name')))), ';'), mapcss.concat(';', mapcss.tag(tags, 'name'), ';'), ';')))))).split('=', 1)]) + }}) + # node[tunnel][!highway][!area:highway][!railway][!waterway][!piste:type][type!=tunnel][public_transport!=platform][route!=ferry][man_made!=pipeline][man_made!=goods_conveyor][man_made!=wildlife_crossing][man_made!=tunnel][power!=cable] if ('tunnel' in keys): match = False @@ -507,6 +530,24 @@ def way(self, data, tags, nds): 'name']) }}) + # *[alt_name][name][alt_name~=*name][alt_name!=*name] + if ('alt_name' in keys and 'name' in keys): + match = False + if not match: + capture_tags = {} + try: match = ((mapcss._tag_capture(capture_tags, 0, tags, 'alt_name')) and (mapcss._tag_capture(capture_tags, 1, tags, 'name')) and (mapcss.list_contains(mapcss._tag_capture(capture_tags, 2, tags, 'alt_name'), mapcss._value_capture(capture_tags, 2, mapcss.tag(tags, 'name')))) and (mapcss._tag_capture(capture_tags, 3, tags, 'alt_name') != mapcss._value_capture(capture_tags, 3, mapcss.tag(tags, 'name')))) + except mapcss.RuleAbort: pass + if match: + # -osmoseTags:list("name","fix:chair") + # -osmoseTrap:tr("Possibly a different `alt_name` that is very similar to `name` was meant. Alternative names are often similar (but never equal) to the name.") + # -osmoseItemClassLevel:"5080/50802/3" + # throwWarning:tr("{0} contains the value of {1}","{0.key}","{1.key}") + # fixAdd:concat("{0.key}=",join_list(";",trim_list(split(";",replace(concat(";",join_list(";",trim_list(split(";",tag("alt_name")))),";"),concat(";",tag("name"),";"),";"))))) + err.append({'class': 50802, 'subclass': 0, 'text': mapcss.tr('{0} contains the value of {1}', mapcss._tag_uncapture(capture_tags, '{0.key}'), mapcss._tag_uncapture(capture_tags, '{1.key}')), 'allow_fix_override': True, 'fix': { + '+': dict([ + (mapcss.concat(mapcss._tag_uncapture(capture_tags, '{0.key}='), mapcss.join_list(';', mapcss.trim_list(mapcss.split(';', mapcss.replace(mapcss.concat(';', mapcss.join_list(';', mapcss.trim_list(mapcss.split(';', mapcss.tag(tags, 'alt_name')))), ';'), mapcss.concat(';', mapcss.tag(tags, 'name'), ';'), ';')))))).split('=', 1)]) + }}) + return err def relation(self, data, tags, members): @@ -660,6 +701,24 @@ def relation(self, data, tags, members): 'name']) }}) + # *[alt_name][name][alt_name~=*name][alt_name!=*name] + if ('alt_name' in keys and 'name' in keys): + match = False + if not match: + capture_tags = {} + try: match = ((mapcss._tag_capture(capture_tags, 0, tags, 'alt_name')) and (mapcss._tag_capture(capture_tags, 1, tags, 'name')) and (mapcss.list_contains(mapcss._tag_capture(capture_tags, 2, tags, 'alt_name'), mapcss._value_capture(capture_tags, 2, mapcss.tag(tags, 'name')))) and (mapcss._tag_capture(capture_tags, 3, tags, 'alt_name') != mapcss._value_capture(capture_tags, 3, mapcss.tag(tags, 'name')))) + except mapcss.RuleAbort: pass + if match: + # -osmoseTags:list("name","fix:chair") + # -osmoseTrap:tr("Possibly a different `alt_name` that is very similar to `name` was meant. Alternative names are often similar (but never equal) to the name.") + # -osmoseItemClassLevel:"5080/50802/3" + # throwWarning:tr("{0} contains the value of {1}","{0.key}","{1.key}") + # fixAdd:concat("{0.key}=",join_list(";",trim_list(split(";",replace(concat(";",join_list(";",trim_list(split(";",tag("alt_name")))),";"),concat(";",tag("name"),";"),";"))))) + err.append({'class': 50802, 'subclass': 0, 'text': mapcss.tr('{0} contains the value of {1}', mapcss._tag_uncapture(capture_tags, '{0.key}'), mapcss._tag_uncapture(capture_tags, '{1.key}')), 'allow_fix_override': True, 'fix': { + '+': dict([ + (mapcss.concat(mapcss._tag_uncapture(capture_tags, '{0.key}='), mapcss.join_list(';', mapcss.trim_list(mapcss.split(';', mapcss.replace(mapcss.concat(';', mapcss.join_list(';', mapcss.trim_list(mapcss.split(';', mapcss.tag(tags, 'alt_name')))), ';'), mapcss.concat(';', mapcss.tag(tags, 'name'), ';'), ';')))))).split('=', 1)]) + }}) + return err @@ -688,6 +747,10 @@ class father: self.check_err(n.node(data, {'barrier': 'fence', 'material': 'wood'}), expected={'class': 303210, 'subclass': 0}) self.check_err(n.node(data, {'name': '24', 'tourism': 'camp_site'}), expected={'class': 40401, 'subclass': 0}) self.check_not_err(n.node(data, {'name': '24tents', 'tourism': 'camp_site'}), expected={'class': 40401, 'subclass': 0}) + self.check_err(n.node(data, {'alt_name': 'x; y; z', 'name': 'y'}), expected={'class': 50802, 'subclass': 0}) + self.check_err(n.node(data, {'alt_name': 'x;y;z', 'name': 'y'}), expected={'class': 50802, 'subclass': 0}) + self.check_not_err(n.node(data, {'alt_name': 'xyz', 'name': 'y'}), expected={'class': 50802, 'subclass': 0}) + self.check_not_err(n.node(data, {'alt_name': 'y', 'name': 'y'}), expected={'class': 50802, 'subclass': 0}) self.check_err(n.way(data, {'amenity': 'fuel', 'building': 'roof'}, [0]), expected={'class': 30322, 'subclass': 0}) self.check_not_err(n.way(data, {'amenity': 'parking', 'building': 'roof', 'parking': 'rooftop'}, [0]), expected={'class': 30322, 'subclass': 0}) self.check_err(n.way(data, {'fee': 'yes', 'highway': 'primary'}, [0]), expected={'class': 30320, 'subclass': 1000}) diff --git a/plugins/TagFix_MultipleTag2.validator.mapcss b/plugins/TagFix_MultipleTag2.validator.mapcss index cbb9bb440..09900da47 100644 --- a/plugins/TagFix_MultipleTag2.validator.mapcss +++ b/plugins/TagFix_MultipleTag2.validator.mapcss @@ -222,6 +222,19 @@ area[tourism=caravan_site][name][name=~/^[0-9]+$/][!ref][!building] { } +*[alt_name][name][alt_name~=*name][alt_name!=*name] { + throwWarning: tr("{0} contains the value of {1}", "{0.key}", "{1.key}"); + assertMatch: "node name=y alt_name=x;y;z"; + assertMatch: "node name=y alt_name=\"x; y; z\""; + assertNoMatch: "node name=y alt_name=y"; /* already checked by mapcss validator rules of JOSM */ + assertNoMatch: "node name=y alt_name=xyz"; + fixAdd: concat("{0.key}=", join_list(";", trim_list(split(";", replace(concat(";", join_list(";", trim_list(split(";", tag("alt_name")))), ";"), concat(";", tag("name"), ";"), ";"))))); + -osmoseItemClassLevel: "5080/50802/3"; + -osmoseTags: list("name", "fix:chair"); + -osmoseTrap: tr("Possibly a different `alt_name` that is very similar to `name` was meant. Alternative names are often similar (but never equal) to the name."); +} + + /* Workaround for issue #1766 */ node[tunnel][!highway][!area:highway][!railway][!waterway][!piste:type][type!=tunnel][public_transport!=platform][route!=ferry][man_made!=pipeline][man_made!=goods_conveyor][man_made!=wildlife_crossing][man_made!=tunnel][power!=cable] { throwWarning: tr("{0} on suspicious object", "{0.key}");