|
| 1 | +#-*- coding: utf-8 -*- |
| 2 | + |
| 3 | +########################################################################### |
| 4 | +## ## |
| 5 | +## Copyrights Osmose project 2024 ## |
| 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 | +from modules.downloader import urlread |
| 25 | + |
| 26 | + |
| 27 | +class TagFix_Tree(Plugin): |
| 28 | + |
| 29 | + def _read_leaf_properties_table(self): |
| 30 | + # The documented values, excluding: |
| 31 | + # - mixed leaf types/cycles: not compatible with `species` |
| 32 | + # - leaf_cycle = semi_*: might be climate dependent and unclear difference between semi_evergreen and semi_deciduous, see #2224 first comment |
| 33 | + allowed_leaf_type = ("broadleaved", "needleleaved", "leafless") |
| 34 | + allowed_leaf_cycle = ("evergreen", "deciduous") |
| 35 | + |
| 36 | + data = urlread(u"https://wiki.openstreetmap.org/w/index.php?title=Tag:natural%3Dtree/List_of_Species&action=raw", 1) |
| 37 | + data = list(map(lambda x: list(filter(lambda z: len(z) > 0, map(lambda y: y.strip(), x.split("|")))), data.split("|-")[1:-1])) |
| 38 | + species_map = {} |
| 39 | + for row in data: # data: list of [species, species:wikidata, leaf_cycle, leaf_type] |
| 40 | + this_species = {} |
| 41 | + if row[2] in allowed_leaf_cycle: |
| 42 | + this_species['leaf_cycle'] = row[2] |
| 43 | + if row[3] in allowed_leaf_type: |
| 44 | + this_species['leaf_type'] = row[3] |
| 45 | + if len(this_species) > 0: |
| 46 | + if len(row[1]) > 2 and row[1][0] == "Q": |
| 47 | + this_species['species:wikidata'] = row[1] |
| 48 | + species_map[row[0]] = this_species |
| 49 | + return species_map |
| 50 | + |
| 51 | + def init(self, logger): |
| 52 | + Plugin.init(self, logger) |
| 53 | + |
| 54 | + self.errors[31201] = self.def_class(item = 3120, level = 3, tags = ['tree', 'natural', 'fix:chair'], |
| 55 | + title = T_('Conflicting tree properties'), |
| 56 | + detail = T_( |
| 57 | +'''The leaf type and/or leaf cycle does not match with the species.'''), |
| 58 | + fix = T_( |
| 59 | +'''Verify that the species is correct, before adding the leaf properties.'''), |
| 60 | + resource = 'https://wiki.openstreetmap.org/wiki/Tag:natural%3Dtree/List_of_Species') |
| 61 | + |
| 62 | + # Read the wiki |
| 63 | + self.species_map = self._read_leaf_properties_table() |
| 64 | + |
| 65 | + def _check_leaf_properties(self, tags): |
| 66 | + err = [] |
| 67 | + |
| 68 | + if "species" in tags and tags["species"] in self.species_map: |
| 69 | + expected_tags = self.species_map[tags["species"]] |
| 70 | + |
| 71 | + if "leaf_cycle" in tags and tags["leaf_cycle"].startswith("semi_"): |
| 72 | + # Ignore leaf_cycle if it already has a value `semi_*`. This might be climate dependent, |
| 73 | + # and unclear difference between semi_evergreen and semi_deciduous, see #2224 first comment |
| 74 | + expected_tags = {x: expected_tags[x] for x in filter(lambda x: x != "leaf_cycle", expected_tags)} |
| 75 | + |
| 76 | + # The tags do not match with the data on the wiki. Don't check for wikidata (handled in item 3031) |
| 77 | + mismatches = set(filter(lambda t: t in tags and expected_tags[t] != tags[t] and t != "species:wikidata", expected_tags.keys())) |
| 78 | + if len(mismatches) > 0: |
| 79 | + err.append({ |
| 80 | + "class": 31201, |
| 81 | + "text": T_("Conflict between `{0}` and `{1}`", "`, `".join(mismatches), "species"), |
| 82 | + "fix": [ |
| 83 | + {"~": {x: expected_tags[x] for x in mismatches}, "+": {x: expected_tags[x] for x in list(filter(lambda t: t not in tags, expected_tags.keys()))}}, |
| 84 | + {"-": ["species"]} |
| 85 | + ]}) |
| 86 | + |
| 87 | + return err |
| 88 | + |
| 89 | + def node(self, data, tags): |
| 90 | + if "natural" in tags and tags["natural"] == "tree": |
| 91 | + return self._check_leaf_properties(tags) |
| 92 | + |
| 93 | + def way(self, data, tags, nds): |
| 94 | + if "natural" in tags and tags["natural"] == "tree_row": |
| 95 | + return self._check_leaf_properties(tags) |
| 96 | + |
| 97 | + |
| 98 | + |
| 99 | +########################################################################### |
| 100 | +from plugins.Plugin import TestPluginCommon |
| 101 | + |
| 102 | +class Test(TestPluginCommon): |
| 103 | + def test(self): |
| 104 | + a = TagFix_Tree(None) |
| 105 | + a.init(None) |
| 106 | + |
| 107 | + for t in [{"natural": "tree"}, |
| 108 | + {"natural": "tree", "leaf_type": "broadleaved"}, |
| 109 | + {"natural": "tree", "leaf_cycle": "deciduous"}, |
| 110 | + {"natural": "tree", "leaf_cycle": "deciduous", "leaf_type": "broadleaved"}, |
| 111 | + {"natural": "tree", "leaf_cycle": "deciduous", "leaf_type": "broadleaved", "species": "Acer buergerianum"}, |
| 112 | + {"natural": "tree", "leaf_cycle": "semi_deciduous", "leaf_type": "broadleaved", "species": "Acer buergerianum"}, # Ignore semi_*, see #2224 first comment |
| 113 | + {"natural": "tree", "leaf_cycle": "deciduous", "leaf_type": "broadleaved", "species": "Acer buergerianum", "species:wikidata": "Q941891"}, |
| 114 | + {"natural": "tree", "leaf_cycle": "deciduous", "leaf_type": "broadleaved", "species": "Acer buergerianum", "species:wikidata": "Q123456789"}, # Bad wikidata is handled by item 3031 |
| 115 | + {"natural": "tree", "leaf_cycle": "deciduous", "leaf_type": "broadleaved", "species": "Unknown species"}, |
| 116 | + {"natural": "tree", "species": "Unknown species"}, |
| 117 | + ]: |
| 118 | + assert not a.node(None, t), a.node(None, t) |
| 119 | + |
| 120 | + # Mismatching properties |
| 121 | + for t in [{"natural": "tree", "leaf_cycle": "deciduous", "leaf_type": "needleleaved", "species": "Acer buergerianum"}, |
| 122 | + {"natural": "tree", "leaf_cycle": "evergreen", "leaf_type": "broadleaved", "species": "Acer buergerianum"}, |
| 123 | + {"natural": "tree", "leaf_cycle": "evergreen", "leaf_type": "broadleaved", "species": "Acer buergerianum", "species:wikidata": "Q941891"}, |
| 124 | + {"natural": "tree", "leaf_cycle": "evergreen", "leaf_type": "needleleaved", "species": "Acer buergerianum"}, |
| 125 | + {"natural": "tree", "leaf_cycle": "evergreen", "species": "Acer buergerianum"}, |
| 126 | + ]: |
| 127 | + assert a.node(None, t), a.node(None, t) |
0 commit comments