예제 #1
0
    def node(self, data, tags):
        err = []
        keys = tags.keys()
        keys = set(keys) & self.SimpleValuedTag
        for k in keys:
            if ';' in tags[k]:
                err.append({"class": 3070, "subclass": stablehash64(k), "text": T_("Concerns tag: `{0}`", '='.join([k, tags[k]])) })

        return err
예제 #2
0
    def check(self, data, tags, check_list_open):
        err = []
        keyss = tags.keys()

        keys = set(keyss) & check_list_open
        for k in keys:
            if not self.Values_open.match(tags[k]):
                if k in self.exceptions_open:
                    if tags[k] in self.exceptions_open[k]:
                        # no error if in exception list
                        continue
                err.append({"class": 3040, "subclass": stablehash64(k), "text": T_("Bad value for %(key)s=%(val)s", {"key": k, "val": tags[k]})})

        keys = set(keyss) & self.check_list_closed
        for k in keys:
            if tags[k] not in self.allow_closed[k]:
                err.append({"class": 3040, "subclass": stablehash64(k), "text": T_("Bad value for %(key)s=%(val)s", {"key": k, "val": tags[k]})})

        return err
예제 #3
0
    def node(self, data, tags):
        err = []
        keys = tags.keys()
        for k in keys:
            part = k.split(':', 1)
            if ":(" in k or k.startswith("def:") or part[0] in self.exceptions:
                # acess:([date])
                # key def: can contains sign =
                continue
            if k in self.exceptions_whole:
                continue

            if not self.KeyPart1.match(part[0]):
                if self.KeyPart1Less.match(part[0]):
                    err.append({
                        "class":
                        30502,
                        "subclass":
                        stablehash64(k),
                        "text":
                        T_("Concerns tag: `{0}`", '='.join([k, tags[k]]))
                    })
                else:
                    err.append({
                        "class":
                        3050,
                        "subclass":
                        stablehash64(k),
                        "text":
                        T_("Concerns tag: `{0}`", '='.join([k, tags[k]]))
                    })
            elif len(part) == 2 and not self.KeyPart2.match(part[1]):
                err.append({
                    "class":
                    30501,
                    "subclass":
                    stablehash64(k),
                    "text":
                    T_("Concerns tag: `{0}`", '='.join([k, tags[k]]))
                })

        return err
예제 #4
0
    def __init__(self, config, logger=None):
        Analyser_Osmosis.__init__(self, config, logger)
        self.FR = config.options and (
            "country" in config.options
            and config.options["country"].startswith("FR")
            or "test" in config.options)
        self.classs[100] = self.def_class(
            item=6070,
            level=3,
            tags=['boundary', 'geom', 'fix:chair'],
            title=T_('Survey point out of boundary'),
            fix=T_(
                '''Check the position of node admin_centre role and boundaries.'''
            ),
            trap=T_(
                '''The geodesic markers should not be moved. These are reference points.
Some geodesic markers are really out of boundary.'''))
        self.classs[2] = self.def_class(
            item=6060,
            level=1,
            tags=['boundary', 'geom', 'fix:chair'],
            title=T_('Boundary intersection'),
            detail=T_(
                '''An area is marked as belonging to several city at once.'''),
            fix=T_('''Check how this area should be owned.'''))
        self.classs_change[3] = self.def_class(
            item=6060,
            level=2,
            tags=['boundary', 'geom', 'fix:chair'],
            title=T_('Lone boundary fragment'),
            detail=T_(
                '''Unconnected boundary fragment, a way with a boundary tag not part of a
boundary relation.'''),
            fix=T_(
                '''Delete the way, remove boundary tag or add to a relation.'''
            ))

        self.callback20 = lambda res: {
            "class": 100,
            "data":
            [self.relation_full, self.relation_full, self.positionAsText]
        }
        self.callback40 = lambda res: {
            "class": 2,
            "subclass": stablehash64(res[2]),
            "data":
            [self.relation_full, self.relation_full, self.positionAsText]
        }
        self.callback50 = lambda res: {
            "class": 3,
            "data": [self.way_full, self.positionAsText]
        }
예제 #5
0
    def node(self, data, tags):
        err = []
        keys = tags.keys()
        for k in keys:
            part = k.split(':', 1)
            if ":(" in k or k.startswith("def:") or part[0] in self.exceptions:
                # acess:([date])
                # key def: can contains sign =
                continue
            if k in self.exceptions_whole:
                continue

            if not self.KeyPart1.match(part[0]):
                err.append({
                    "class":
                    3050,
                    "subclass":
                    stablehash64(k),
                    "text":
                    T_("Bad tag %(k)s=%(v)s", {
                        "k": k,
                        "v": tags[k]
                    })
                })
            elif len(part) == 2 and not self.KeyPart2.match(part[1]):
                err.append({
                    "class":
                    30501,
                    "subclass":
                    stablehash64(k),
                    "text":
                    T_("Bad tag suffix %(k)s=%(v)s", {
                        "k": k,
                        "v": tags[k]
                    })
                })

        return err
예제 #6
0
    def __init__(self, config, logger = None):
        Analyser_Osmosis.__init__(self, config, logger)
        self.classs_change[1] = self.def_class(item = 1170, level = 3, tags = ['relation', 'fix:chair', 'geom'],
            title = T_('Double inner polygon'),
            detail = T_(
'''The geometry of the inner of the multipolygon is duplicated. One in
relation without tag and another with tags not part of the relation.'''),
            fix = T_(
'''Remove the ring without tag. Add in the relation the one with the tags
as `inner` role.'''))
        self.classs_change[2] = self.def_class(item = 1170, level = 2, tags = ['relation', 'fix:chair', 'multipolygon'],
            title = T_('Inconsistant multipolygon nature with members nature'),
            detail = T_(
'''Multipolygon defines a nature that is different from that specified in
the outers roles.'''))
        self.classs_change[3] = self.def_class(item = 1170, level = 2, tags = ['relation', 'fix:chair', 'multipolygon'],
            title = T_('Inconsistant multipolygon member nature'),
            detail = T_(
'''Multipolygon does not define nature, several found on the outer role
members.'''))
        self.classs_change[4] = self.def_class(item = 1170, level = 1, tags = ['relation', 'fix:chair', 'geom'],
            title = T_('Should be polygon, part of multipolygon or not having area tag'),
            detail = T_(
'''The nature of the way indicates that it is a surface, the way would be
a polygon or a part of a multipolygon as outer role.'''),
            fix = T_(
'''Close the way to make a polygon or add to a multipolygon.'''))

        self.callback10 = lambda res: {"class":1, "data":[self.relation_full, self.way_full, self.way_full, self.positionAsText]}
        self.callback20 = lambda res: {"class":2, "subclass":stablehash64(res[11]), "data":[self.relation_full, self.way_full, self.positionAsText],
            "text": {"en": u", ".join(map(lambda k: "%s=(%s,%s)"%k, filter(lambda k: k[1], (("landuse",res[3],res[4]), ("natural",res[5],res[6]), ("waterway",res[7],res[8]), ("building",res[9],res[10])))))}
        }
        self.callback30 = lambda res: {"class":3, "subclass":1, "data":[self.relation_full, self.positionAsText],
            "text": {"en": u", ".join(map(lambda k: "%s=(%s)"%k, filter(lambda k: k[1], (("landuse",res[2]), ("natural",res[3]), ("waterway",res[4]), ("building",res[5])))))}
        }
        self.callback40 = lambda res: {"class":4, "subclass":stablehash64(res[9]), "data":[self.way_full, self.positionAsText],
            "text": {"en": u", ".join(map(lambda k: "%s=%s"%k, filter(lambda k: k[1], (("area",res[2]), ("landuse",res[3]), ("natural",res[4]), ("waterway",res[5]), ("leisure",res[6]), ("amenity",res[7]), ("building",res[8])))))}
        }
예제 #7
0
    def check(self, data, tags, check_list_open):
        err = []
        keyss = tags.keys()

        keys = set(keyss) & check_list_open
        for k in keys:
            if not self.Values_open.match(tags[k]):
                if k in self.exceptions_open:
                    if tags[k] in self.exceptions_open[k]:
                        # no error if in exception list
                        continue
                err.append({"class": 3040, "subclass": stablehash64(k), "text": T_("Concerns tag: `{0}`", '='.join([k, tags[k]])) })

        keys = set(keyss) & self.check_list_closed
        for k in keys:
            if tags[k] not in self.allow_closed[k]:
                err.append({"class": 3040, "subclass": stablehash64(k), "text": T_("Concerns tag: `{0}`", '='.join([k, tags[k]])) })

        for k in keyss:
            if tags[k] == "unknown":
                err.append({"class": 40613, "subclass": stablehash64(k), "text": T_("Concerns tag: `{0}`", '='.join([k, tags[k]])) })

        return err
예제 #8
0
  def checkAccessKeys(self, tags, suffixes):
    err = []
    accessTags = {}
    for accesskey in self.accessKeys:
      for suffix in suffixes:
        if accesskey + suffix in tags:
          accessTags[accesskey + suffix] = {"transportMode": accesskey, "value": tags[accesskey + suffix], "suffix": suffix}

    for tag in accessTags:
      values = accessTags[tag]["value"].split(";")
      isConditional = ":conditional" in tag
      for accessVal in values:
        accessValue = accessVal
        if isConditional:
          if "@" in accessVal:
            accessValue = accessValue.split("@")[0]
          else:
            continue # value was split on a ";" in the condition
        accessValue = accessValue.strip()
        transportMode = accessTags[tag]["transportMode"]
        if transportMode in self.accessValuesSpecial and accessValue in self.accessValuesSpecial[transportMode]:
          continue
        if not accessValue in self.accessValuesGeneral:
          if (accessValue in self.accessKeys or accessValue == "emergency") and accessValue != transportMode:
            propose = tag + " = ### + " + accessValue + accessTags[tag]["suffix"] + " = yes"
            if len(values) > 1 or isConditional:
              propose = propose.replace("###", "...") # i.e. access=bus;destination should become access=destination + bus=yes instead of access=no + bus=yes
            else:
              propose = propose.replace("###", "no") # assume 'no' holds for all other transport modes
            if isConditional:
              propose = propose + " @ (...)" # conditional may need to change
            err.append({"class": 30405, "subclass": 0 + stablehash64(tag + '|' + accessValue), "text": T_("Access value \"{0}\" for key \"{1}\" is a transport mode. Consider using \"{2}\" instead", accessValue, tag, propose)})
          else:
            err.append({"class": 30404, "subclass": 0 + stablehash64(tag + '|' + accessValue), "text": T_("Unknown access value \"{0}\" for key \"{1}\"", accessValue, tag)})

    if err != []:
      return err
예제 #9
0
    def __init__(self, config, logger = None):
        Analyser_Osmosis.__init__(self, config, logger)
        self.classs[1] = self.def_class(item = 7040, level = 3, tags = ['power', 'fix:imagery'],
            title = T_('Lone power tower or pole'),
            fix = T_(
'''This tower should probably be connected to a power line.'''),
            trap = T_(
'''It's possible that disused power features could be disconnected from the network.
In which case make use of the `disused:` [lifecycle prefix](https://wiki.openstreetmap.org/wiki/Lifecycle_prefix).'''))
        self.classs[2] = self.def_class(item = 7040, level = 2, tags = ['power', 'fix:imagery'],
            title = T_('Unfinished power major line'),
            detail = T_(
'''The line ends in a vacuum, and should be connected to another line or
a transformer (`power=transformer`), a generator (`power=generator`)
or marked as transitioning into ground (`location:transition=yes`).'''),
            trap = T_(
'''It's possible that disused power features could be disconnected from the network.
In which case make use of the `disused:` [lifecycle prefix](https://wiki.openstreetmap.org/wiki/Lifecycle_prefix).'''))
        self.classs[6] = self.def_class(item = 7040, level = 3, tags = ['power', 'fix:imagery'],
            title = T_('Unfinished power minor line'),
            detail = T_(
'''The line ends in a vacuum, and should be connected to another line or
a transformer (`power=transformer`), a generator (`power=generator`)
or marked as transitioning into ground (`location:transition=yes`).'''),
            trap = T_(
'''It's possible that disused power features could be disconnected from the network.
In which case make use of the `disused:` [lifecycle prefix](https://wiki.openstreetmap.org/wiki/Lifecycle_prefix).'''))
        self.classs[3] = self.def_class(item = 7040, level = 3, tags = ['power', 'fix:chair'],
            title = T_('Connection between different voltages'),
            detail = T_('Two power lines meet at this point, but have inconsistent voltages (`voltage=*`).'))
        self.classs[4] = self.def_class(item = 7040, level = 3, tags = ['power', 'fix:imagery'],
            title = T_('Non power node on power way'),
            detail = T_(
'''Power lines can only form a straight line between supports and therefore shouldn't
have additional nodes that aren't tagged as a `power` feature.'''),
            fix = T_(
'''If this node is a tower or pole, use the tag `power=tower` or
`power=pole`. Otherwise remove it.'''))
        self.classs_change[5] = self.def_class(item = 7040, level = 3, tags = ['power', 'fix:imagery'],
            title = T_('Missing power tower or pole'),
            detail = T_(
'''Based on the statistical frequency of the poles on this power line,
there's likely an unmapped pole nearby.'''))
        self.classs[7] = self.def_class(item = 7040, level = 3, tags = ['power', 'fix:chair'],
            title = T_('Unmatched voltage of line on substation'))

        self.callback40 = lambda res: {"class":4, "data":[self.node_full, self.positionAsText], "fix":[{"+": {"power": "tower"}}, {"+": {"power": "pole"}}]}
        self.callback50 = lambda res: {"class":5, "subclass": stablehash64(res[1]), "data":[self.way_full, self.positionAsText]}
예제 #10
0
    def way(self, data, tags, nds):
        if not "highway" in tags:
            return
        err = []

        for tag in tags:
            if tag[-5:] == ":both":
                tag_default = tag[
                    0:-5]  # special handling because :both_ways also exists
            else:
                tag_default = tag.replace(":both:",
                                          ":").replace(":left", "").replace(
                                              ":right", "")
            if tag_default == tag:
                continue  # tag does not contain :both/:left/:right
            allowedAlternativeValues = []
            if tag_default in tags:
                # Some tags allow left/right/both as values, e.g. sidewalk=both equals sidewalk:both=yes
                if self.simplifyValue(tags[tag]) == "no":
                    allowedAlternativeValues = [
                        "yes", "left", "right", "separate", "opposite"
                    ]
                else:
                    allowedAlternativeValues = ["yes", "left", "right", "both"]
            else:
                tag_default = tag.replace(":left",
                                          ":both").replace(":right", ":both")

            if tag_default in tags:
                tt = self.simplifyValue(tags[tag])
                ttd = self.simplifyValue(tags[tag_default])
                if tag[0:5] == "name:" and tt in ttd:
                    continue  # 'name' probably equals "name:left" + "/" + name:right, handled by Name_Multiple
                if tt != ttd and not ttd in allowedAlternativeValues:
                    err.append({
                        "class":
                        33601,
                        "subclass":
                        1 + stablehash64(tag),
                        "text":
                        T_("Conflicting values of \"{0}\" and \"{1}\"",
                           tag_default, tag)
                    })

        if err != []:
            return err
예제 #11
0
    def __init__(self, config, logger = None):
        Analyser_Osmosis.__init__(self, config, logger)
        self.FR = config.options and ("country" in config.options and config.options["country"].startswith("FR") or "test" in config.options)
        detail = T_(
'''In France, probably a building imported from cadastre without the
[controls
needed](https://wiki.openstreetmap.org/wiki/France/Cadastre/Import_semi-automatique_des_b%C3%A2timents#Traitements_des_b.C3.A2timents_avec_JOSM_avant_envoi_vers_serveur_OSM).
Error is frequent on areas where lots of building are drawn manually,
such as the HOT activations.
''')
        self.classs_change[1] = self.def_class(item = 0, level = 3, tags = ['building', 'geom', 'fix:chair'],
            title = T_('Building intersection'),
            detail = self.merge_doc(detail, T_(
'''The intersection surface is probably due to inaccuracies in the
cadastre/import tools.''')))
        self.classs_change[2] = self.def_class(item = 0, level = 2, tags = ['building', 'geom', 'fix:chair'],
            title = T_('Large building intersection'),
            detail = self.merge_doc(detail, T_(
'''A large overlap. Requires a visual check (Bing, cadastre or
survey).''')))
        self.classs_change[3] = self.def_class(item = 0, level = 3, tags = ['building', 'geom', 'fix:chair'],
            title = T_('Building too small'),
            detail = self.merge_doc(detail, T_(
'''There is no intersection, but the surface is too small to be a
building.''')))
        self.classs_change[4] = self.def_class(item = 0, level = 3, tags = ['building', 'geom', 'fix:chair'],
            title = T_('Gap between buildings'),
            detail = self.merge_doc(detail, T_(
'''Space separation is probably due to inaccuracies in the
cadastre/import tools.''')))
        self.classs_change[5] = self.def_class(item = 0, level = 1, tags = ['building', 'fix:chair'],
            title = T_('Large building intersection cluster'),
            detail = self.merge_doc(detail, T_(
'''Group of important overlaps. Major problem like a double import.''')))
        if self.FR:
            self.classs_change[6] = self.def_class(item =  1, level = 3, tags = ['building', 'geom', 'fix:chair'],
                title = T_(u"Building in parts"),
                detail = detail)

        self.callback30 = lambda res: {"class":2 if res[3]>res[4] else 1, "data":[self.way, self.way, self.positionAsText]}
        self.callback40 = lambda res: {"class":3, "data":[self.way, self.positionAsText]}
        self.callback50 = lambda res: {"class":4, "data":[self.way, self.way, self.positionAsText]}
        self.callback60 = lambda res: {"class":5, "subclass": stablehash64(res[0]), "data":[self.positionAsText]}
        if self.FR:
            self.callback70 = lambda res: {"class":6, "data":[self.way, self.positionAsText]}
예제 #12
0
    def node(self, data, tags):
        err = []
        keys = tags.keys()
        keys = set(keys) & self.SimpleValuedTag
        for k in keys:
            if ';' in tags[k]:
                err.append({
                    "class":
                    3070,
                    "subclass":
                    stablehash64(k),
                    "text":
                    T_("Multiple values on tag: %(key)s=%(val)s", {
                        "key": k,
                        "val": tags[k]
                    })
                })

        return err
예제 #13
0
    def way(self, data, tags, nds):
        if not "highway" in tags:
            return

        err = []

        if (("parking:condition:right" in tags and not "parking:lane:right" in tags and not "parking:lane:both" in tags) or
            ("parking:condition:left" in tags and not "parking:lane:left" in tags and not "parking:lane:both" in tags) or
            ("parking:condition:both" in tags and not "parking:lane:both" in tags and not ("parking:lane:left" in tags and "parking:lane:right" in tags))):
            err.append({"class": 31616})

        sides = list(map(lambda tag: tag[len("parking:lane:"):].split(":")[0], filter(lambda tag: tag.startswith("parking:lane:"), tags)))
        if len(sides) == 0:
            return err
        sides = [i for i in sides if i not in ("left", "right", "both")]

        if len(sides) > 0:
            err.append({"class": 31611})

        if "parking:lane:both" in tags and (("parking:lane:right" in tags and tags["parking:lane:right"] == tags["parking:lane:both"]) or
                                            ("parking:lane:left" in tags and tags["parking:lane:left"] == tags["parking:lane:both"])):
            # Conflicting values are dealt with in Highway_Sides
            err.append({"class": 31614})

        for side in ("parking:lane:right", "parking:lane:left", "parking:lane:both"):
            if side in tags:
                if tags[side] not in self.parkingLaneValues:
                    # Unknown value of parking:lane:[side]
                    if tags[side] in self.parkingConditionValues:
                        err.append({"class": 31615, "subclass": stablehash64(side), "text": T_("`{0}` is a value for key `{1}`", tags[side], "parking:condition:[side]")})
                    elif tags[side] in self.parkingConditionReasonValues:
                        err.append({"class": 31615, "subclass": stablehash64(side), "text": T_("`{0}` is a value for key `{1}`", tags[side], "parking:condition:[side]:reason")})
                    else:
                        err.append({"class": 31615, "subclass": stablehash64(side)})
                condition = side.replace("lane", "condition")
                if not condition in tags:
                    condition = "parking:condition:both"
                if condition in tags:
                    if tags[side] == "no" and tags[condition][0:2] != "no":
                        # parking:lane:[side] = no together with parking:condition:[side]
                        err.append({"class": 31617, "subclass": stablehash64(side)})
                    elif tags[side] == "separate":
                        # parking:lane:[side] = separate together with parking:condition:[side]
                        err.append({"class": 31618, "subclass": stablehash64(side)})
                    elif tags[condition] in self.parkingLaneValues and tags[condition] not in self.parkingConditionValues:
                        # A value for parking:lane:[side] was used as condition
                        err.append({"class": 31619, "subclass": stablehash64(side), "text": T_("`{0}` is a value for key `{1}`", tags[condition], "parking:lane:[side]")})

        return err
예제 #14
0
    def way(self, data, tags, nds):
        if not "highway" in tags:
            return

        err = []

        sides = list(
            map(lambda tag: tag[len(self.parking_lane):].split(":")[0],
                filter(lambda tag: tag.startswith(self.parking_lane), tags)))
        n_sides = len(sides)
        sides = [i for i in sides if i not in ("left", "right", "both")]

        conditions = map(
            lambda tag: ":".join(tag.split(":")[0:3]).replace(
                ":condition:", ":lane:"),
            filter(lambda tag: tag.startswith(self.parking_condition), tags))
        for c in conditions:
            if c not in tags:
                err.append({"class": 31616})
                break

        if n_sides == 0:
            return err

        if len(sides) > 0:
            err.append({"class": 31611})

        if ("parking:lane:right" in tags or "parking:lane:left"
                in tags) and "parking:lane:both" in tags:
            err.append({"class": 31614})

        for side in ("parking:lane:right", "parking:lane:left",
                     "parking:lane:both"):
            if side in tags and tags[side] not in ("parallel", "diagonal",
                                                   "perpendicular", "marked",
                                                   "no_parking", "no_stopping",
                                                   "fire_lane"):
                err.append({"class": 31615, "subclass": stablehash64(side)})

        return err
예제 #15
0
    def node(self, data, tags):
        if u"name" not in tags:
            return
        if (u"highway" not in tags) and (u"waterway"
                                         not in tags) and (u"place"
                                                           not in tags):
            return
        words = []

        name = tags[u"name"]
        name_subst = self.apply_special_subst(name)
        split = self._split(name_subst)
        for i in range(0, len(split), 2):
            split[i] = self.remove_special_subst(split[i])
        splitfix = list(split)

        if split and split[0] and split[0][0] in self.minus:
            words.append(split[0])
            splitfix[0] = split[0].capitalize()
        for i in range(0, len(split), 2):
            word = split[i]
            if word in self.special:
                continue
            if word[0] in self.minus:
                words.append(word)
                splitfix[i] = split[i].capitalize()
        if words:
            return {
                "class":
                906,
                "subclass":
                stablehash64(','.join(words)),
                "text":
                T_("Missing capital letter for: {0}",
                   u", ".join(sorted(set(words)))),
                "fix": {
                    "name": "".join(splitfix)
                }
            }
        return
 def __init__(self, config, logger=None):
     Analyser_Osmosis.__init__(self, config, logger)
     self.FR = config.options and (
         "country" in config.options
         and config.options["country"].startswith("FR")
         or "test" in config.options)
     self.classs[100] = {
         "item": "6070",
         "level": 3,
         "tag": ["boundary", "geom", "fix:chair"],
         "desc": T_(u"Survey point out of boundary")
     }
     self.classs[2] = {
         "item": "6060",
         "level": 1,
         "tag": ["boundary", "geom", "fix:chair"],
         "desc": T_(u"Boundary intersection")
     }
     self.classs_change[3] = {
         "item": "6060",
         "level": 2,
         "tag": ["boundary", "geom", "fix:chair"],
         "desc": T_(u"Lone boundary fragment")
     }
     self.callback20 = lambda res: {
         "class": 100,
         "data":
         [self.relation_full, self.relation_full, self.positionAsText]
     }
     self.callback40 = lambda res: {
         "class": 2,
         "subclass": stablehash64(res[2]),
         "data":
         [self.relation_full, self.relation_full, self.positionAsText]
     }
     self.callback50 = lambda res: {
         "class": 3,
         "data": [self.way_full, self.positionAsText]
     }
예제 #17
0
    def node(self, data, tags):
        err = []
        for key, value in tags.items():
            m = self.non_printable.search(key)
            if m:
                err.append({"class": 50702, "subclass": 0 + stablehash64(key), "text": T_f(u"\"{0}\" unexpected non printable char ({1}, 0x{2:04x}) in key at position {3}", key, unicodedata.name(m.group(0), ''), ord(m.group(0)), m.start() + 1)})
                break

            m = self.non_printable.search(value)
            if m:
                err.append({"class": 50702, "subclass": 1 + stablehash64(key), "text": T_f(u"\"{0}\"=\"{1}\" unexpected non printable char ({2}, 0x{3:04x}) in value at position {4}", key, value, unicodedata.name(m.group(0), ''), ord(m.group(0)), m.start() + 1)})
                break

            m = self.other_symbol.search(key)
            if m:
                err.append({"class": 50703, "subclass": 0 + stablehash64(key), "text": T_f(u"\"{0}\" unexpected symbol char ({1}, 0x{2:04x}) in key at position {3}", key, unicodedata.name(m.group(0), ''), ord(m.group(0)), m.start() + 1)})
                break

            m = self.other_symbol.search(value)
            if m:
                err.append({"class": 50703, "subclass": 1 + stablehash64(key), "text": T_f(u"\"{0}\"=\"{1}\" unexpected symbol char ({2}, 0x{3:04x}) in value at position {4}", key, value, unicodedata.name(m.group(0), ''), ord(m.group(0)), m.start() + 1)})
                break

            err_size = len(err)
            # https://en.wikipedia.org/wiki/Bi-directional_text#Table_of_possible_BiDi-types
            for c in u"\u200E\u200F\u061C\u202A\u202D\u202B\u202E\u202C\u2066\u2067\u2068\u2069":
                m = key.find(c)
                if m > 0:
                    err.append({"class": 50702, "subclass": 2 + stablehash64(key), "text": T_f(u"\"{0}\" unexpected non printable char ({1}, 0x{2:04x}) in key at position {3}", key, unicodedata.name(c, ''), ord(c), m + 1)})
                    break

                m = value.find(c)
                if m > 0:
                    err.append({"class": 50702, "subclass": 3 + stablehash64(key), "text": T_f(u"\"{0}\"=\"{1}\" unexpected non printable char ({2}, 0x{3:04x}) in value at position {4}", key, value, unicodedata.name(c, ''), ord(c), m + 1)})
                    break

            if err_size != len(err):
                break

            if self.default:
                if key in self.names:
                    s = self.non_letter.sub(u" ", value)
                    s = self.alone_char.sub(u"", s)
                    s = self.roman_number.sub(u"", s)
                    s = self.default.sub(u"", s)
                    if len(s) > 0 and not(len(value) == 2 and len(s) == 1) and len(s) <= len(value) / 10 + 1:
                        if len(s) == 1:
                            c = s[0]
                            u = self.uniq_script and confusables.unconfuse(c, self.uniq_script)
                            if u:
                                err.append({"class": 50701, "subclass": 0 + stablehash64(key),
                                    "text": T_f(u"\"{0}\"=\"{1}\" unexpected char \"{2}\" ({3}, 0x{4:04x}). Means \"{5}\" ({6}, 0x{7:04x})?", key, value, s, unicodedata.name(c, ''), ord(c), u, unicodedata.name(u, ''), ord(u)),
                                    "fix": {key: value.replace(c, u)}
                                })
                            else:
                                err.append({"class": 50701, "subclass": 0 + stablehash64(key),
                                    "text": T_f(u"\"{0}\"=\"{1}\" unexpected char \"{2}\" ({3}, 0x{4:04x})", key, value, s, unicodedata.name(c, ''), ord(c))
                                })
                        else:
                            err.append({"class": 50701, "subclass": 0 + stablehash64(key), "text": T_f(u"\"{0}\"=\"{1}\" unexpected \"{2}\"", key, value, s)})
                        break

            l = key.split(':')
            if len(l) > 1 and l[0] in self.names and l[1] in self.lang:
                s = self.non_letter.sub(u" ", value)
                s = self.alone_char.sub(u"\\1", s)
                s = self.roman_number.sub(u"\\1", s)
                s = self.lang[l[1]].sub(u"", s)
                if len(s) > 0:
                    if len(s) == 1:
                        c = s[0]
                        u = self.uniq_scripts.get(l[1]) and confusables.unconfuse(c, self.uniq_scripts.get(l[1]))
                        if u:
                            err.append({"class": 50701, "subclass": 1 + stablehash64(key),
                                "text": T_f(u"\"{0}\"=\"{1}\" unexpected char \"{2}\" ({3}, 0x{4:04x}). Means \"{5}\" ({6}, 0x{7:04x})?", key, value, s, unicodedata.name(c, ''), ord(c), u, unicodedata.name(u, ''), ord(u)),
                                "fix": {key: value.replace(c, u)}
                            })
                        else:
                            err.append({"class": 50701, "subclass": 1 + stablehash64(key),
                                "text": T_f(u"\"{0}\"=\"{1}\" unexpected char \"{2}\" ({3}, 0x{4:04x})", key, value, s, unicodedata.name(c, ''), ord(c))
                            })
                    else:
                        err.append({"class": 50701, "subclass": 1 + stablehash64(key), "text": T_f(u"\"{0}\"=\"{1}\" unexpected \"{2}\"", key, value, s)})
                    break

        return err
예제 #18
0
    def way(self, data, tags, nds):
        if not "highway" in tags:
            return

        lanes = False
        for tag in tags:
            if "lanes" in tag:
                lanes = True
                break

        if not lanes:
            return

        tags_lanes = {}
        for tag in tags:
            if "lanes" in tag and not "source" in tag and not "FIXME" in tag:
                tags_lanes[tag] = tags[tag]

        err = []

        # Check turn lanes values
        tl = "turn:lanes" in tags_lanes
        tlf = "turn:lanes:forward" in tags_lanes
        tlb = "turn:lanes:backward" in tags_lanes
        tl2 = "turn:lanes:both_ways" in tags_lanes
        if tl or tlf or tlb or tl2:
            for tl in ["turn:lanes", "turn:lanes:forward", "turn:lanes:backward", "turn:lanes:both_ways"]:
                if tl in tags_lanes:
                    ttt = tags_lanes[tl].split("|")
                    unknown_turn_lanes_value = False
                    bad_turn_lanes_value = False
                    i = 0
                    for tt in ttt:
                        settt = set(tt.split(";"))
                        for t in settt:
                            if t not in ["left", "slight_left", "sharp_left", "through", "right", "slight_right", "sharp_right", "reverse", "merge_to_left", "merge_to_right", "none", ""]:
                                unknown_turn_lanes_value = True
                                err.append({"class": 31606, "subclass": 0 + stablehash64(tl + '|' + t + '|' + str(i)), "text": T_("Unknown turn lanes value \"{0}\"", t)})

                        if ("merge_to_left" in settt and i == 0) or ("merge_to_right" in settt and i == len(ttt) - 1):
                            # A lane must exist in the merging direction
                            err.append({"class": 31600, "subclass": 1 + stablehash64(tl + '|' + tt + '|' + str(i))})
                            bad_turn_lanes_value = True

                        elif (not unknown_turn_lanes_value and len(settt) > 1 and ("none" in settt or "" in settt)):
                            # A single turn lane containing both `none` and `something`
                            if (not ("none" in settt and "" in settt and len(settt) == 2)):
                                err.append({"class": 316012, "subclass": 3 + stablehash64(tl + '|' + tt + '|' + str(i)), "text": T_("Combined none and indicated turn lane: \"{0}\"", tt)})
                                bad_turn_lanes_value = True

                        elif (not unknown_turn_lanes_value and len(settt) > 1 and
                              ((len(settt) > 2 and ("merge_to_right" in settt or "merge_to_left" in settt)) or
                               ("merge_to_right" in settt and not "merge_to_left" in settt) or
                               ("merge_to_left" in settt and not "merge_to_right" in settt))):
                            # A combination of merge_to_* with a turn (other than another merge_to_*)
                            err.append({"class": 316011, "subclass": 2 + stablehash64(tl + '|' + tt + '|' + str(i)), "text": T_("Merge together with another turn lane: \"{0}\"", tt)})

                        i += 1
                    if not unknown_turn_lanes_value and not bad_turn_lanes_value:
                        # Sequence (left-to-right) should be sharp_left (1)|left (2)|slight_left (3)|through (4)|slight_right (5)|right (6)|sharp_right (7)
                        # Reverse (depends on driving direction) and merge_to_* (can be anywhere except
                        #  outer lane in merging direction; already handled by error 31600) are ignored
                        # None is a special case: considered as 'through' except when outer lane
                        throughvalue = "4"
                        t = tags_lanes[tl] \
                            .replace("reverse", "-") \
                            .replace("merge_to_right", "-").replace("merge_to_left", "-") \
                            .replace("none", "N") \
                            .replace("sharp_left", "1").replace("slight_left", "3").replace("left", "2") \
                            .replace("through", throughvalue) \
                            .replace("slight_right", "5").replace("sharp_right", "7").replace("right", "6") \
                            .replace(";", "").split("|")

                        # Empty equals a 'none', otherwise sort values within a single lane (as left;right equals right;left)
                        t = ''.join(map(lambda e: "N" if len(e) == 0 else e[0] if len(e) == 1 else ''.join(sorted(e)), t))

                        t = t.replace('-', '') # Ignored values

                        # Ignore single none on the outside lanes: it could be a bus lane
                        # Treat all other nones as throughs (multiple bus lanes or a dedicated lane in between two turns is unlikely)
                        if t[0:1] == "N":
                            t = t[1:]
                        if t[-1:] == "N":
                            t = t[0:-1]
                        t = t.replace('N', throughvalue)

                        if t != ''.join(sorted(t)):
                            err.append({"class": 31607, "subclass": 1 + stablehash64(tl), "text": T_("Bad turn lanes order in \"{0}\"", tl)})

        # Check access lanes values

        # Count for non fullwidth lanes
        non_fullwidth_lanes_number = {}
        for direction in ['', ':forward', ':backward', ':both_ways']:
            o = tags_lanes.get('bicycle:lanes'+direction)
            if o:
                non_fullwidth_lanes_number[direction] = ilen(filter(lambda i: i == 'designated', o.split('|')))

        for access in ['hgv', 'bus', 'access', 'bicycle', 'psv', 'taxi', 'vehicle', 'motor_vehicle', 'hov', 'motorcycle', 'goods']:
            base = access+':lanes'
            for tag in tags_lanes:
                if tag.startswith(base):
                    try:
                        int(tags_lanes[tag])
                        err.append({"class": 31609, "subclass": 1 + stablehash64(tag), "text": {'en': '{0}={1}'.format(tag, tags_lanes[tag]) }})
                    except ValueError:
                        # Ok, should not be an integer
                        pass

        stars = []
        for tag in tags_lanes:
            if ":lanes" in tag:
                star = tag.split(':')[0]
                if star not in ('source', 'proposed', 'construction', 'note'):
                    stars.append(star)
        stars = list(set(stars))

        for star in stars:
            l = star + '' in tags_lanes
            lf = star + ':forward' in tags_lanes
            lb = star + ':backward' in tags_lanes
            l2 = star + ':both_ways' in tags_lanes
            if l and (lf or lb or l2):
                err.append({"class": 31603, "subclass": 0 + stablehash64(star), "text": {"en": star + ":*"}})

        if err != []:
            return err

        number = {'lanes': {}}
        for tag in tags_lanes:
            if tag == 'lanes' or tag.startswith('lanes:'):
                try:
                    n = int(tags_lanes[tag])
                    parts = tag.split(':')
                    direction = ''
                    if len(parts) == 1:
                        number['lanes'][''] = n
                    elif len(parts) == 2 and parts[1] in ['forward', 'backward', 'both_ways']:
                        number['lanes'][':'+parts[1]] = n
                except ValueError:
                    err.append({"class": 31601, "subclass": 0 + stablehash64(tag), "text": T_("lanes={0} is not an integer", tags_lanes[tag])})

        for star in stars:
            number[star] = {}
            for direction in ['', ':forward', ':backward', ':both_ways']:
                o = tags_lanes.get(star+':lanes'+direction)
                if o:
                    number[star][direction] = len(o.split('|'))

        n_lanes = {}
        for direction in ['', ':forward', ':backward', ':both_ways']:
            tag = None
            for star in sorted(number.keys()):
                non_fullwidth_lanes_number_star = ((non_fullwidth_lanes_number.get(direction) or 0) if star != 'lanes' else 0)
                non_fullwidth_lanes_number_tag = ((non_fullwidth_lanes_number.get(direction) or 0) if tag != 'lanes:lanes'+direction else 0)
                if n_lanes.get(direction) is not None and number[star].get(direction) is not None and \
                        number[star][direction] - non_fullwidth_lanes_number_star != \
                        n_lanes[direction] - non_fullwidth_lanes_number_tag:
                    err.append({"class": 31608, "subclass": 0 + stablehash64(direction + '|' + star), "text": {
                        "en": "(lanes({0})={1}) - (non fullwidth={2}) != (lanes({3})={4}) - (non fullwidth={5})".format(
                            star+":*"+direction, number[star][direction], non_fullwidth_lanes_number_star,
                            tag, n_lanes[direction], non_fullwidth_lanes_number_tag) }})
                elif n_lanes.get(direction) is None and number[star].get(direction) is not None:
                    # Fist loop, pick the star as tag and the number of lanes to compare to the others
                    n_lanes[direction] = number[star][direction]
                    tag = star+":lanes"+direction

        if err != []:
            return err

        if tags["highway"] == 'motorway':
            oneway = "oneway" not in tags or tags["oneway"] not in ["no", "false"]
        else:
            oneway = "oneway" in tags and tags["oneway"] not in ["no", "false"]

        if oneway:
            for tag in tags:
                if tag.startswith('oneway:') and tags[tag] in ["no", "false"]:
                    # Oneway for mainstream traffic, but not for an other one, so we are not really on a oneway
                    oneway = False

        if tags.get('junction') == 'roundabout':
            oneway = True

        nl = n_lanes.get('')
        nlf = n_lanes.get(':forward')
        nlb = n_lanes.get(':backward')
        nl2 = n_lanes.get(':both_ways')

        nfw_nl = non_fullwidth_lanes_number.get('') or 0
        nfw_nlf = non_fullwidth_lanes_number.get(':forward') or 0
        nfw_nlb = non_fullwidth_lanes_number.get(':backward') or 0
        nfw_nl2 = non_fullwidth_lanes_number.get(':both_ways') or 0

        if oneway:
            if nl is not None and nlf is not None and nl != nlf - nfw_nlf:
                err.append({"class": 31604, "subclass": 0, "text": T_("on oneway, (lanes={0}) != (lanes:forward={1}) - (non fullwidth forward={2})", nl, nlf, nfw_nlf)})
            elif nlb is not None or nl2 is not None:
                err.append({"class": 31605, "subclass": 0})
        else:
            if nl is not None and nlf is not None and nlb is not None and nl != nlf + nlb + (nl2 or 0) - nfw_nl - nfw_nlf - nfw_nlb - nfw_nl2:
                err.append({"class": 31604, "subclass": 0, "text": T_("on two way, (lanes={0}) != (lanes:forward={1}) + (lanes:backward={2}) + (lanes:both_ways={3}) - (non fullwidth={4}) - (non fullwidth forward={5}) - (non fullwidth backward={6}) - (non fullwidth both_ways={7})", nl, nlf, nlb, nl2, nfw_nl, nfw_nlf, nfw_nlb, nfw_nl2)})
            elif nl is not None and nlf is not None and nl <= nlf - nfw_nlf:
                err.append({"class": 31604, "subclass": 0, "text": T_("on two way, (lanes={0}) <= (lanes:forward={1}) - (non fullwidth forward={2})", nl, nlf, nfw_nlf)})
            elif nl is not None and nlb is not None and nl <= nlb - nfw_nlb:
                err.append({"class": 31604, "subclass": 0, "text": T_("on two way, (lanes={0}) <= (lanes:backward={1}) - (non fullwidth backward={2})", nl, nlb, nfw_nlb)})
            elif nl is not None and nl2 is not None and nl < nl2 - nfw_nl2:
                err.append({"class": 31604, "subclass": 0, "text": T_("on two way, (lanes={0}) < (lanes:both_ways={1}) - (non fullwidth both_ways={2})", nl, nl2, nfw_nl2)})

        if err != []:
            return err
예제 #19
0
 def analyser_osmosis_common(self):
     self.run(sql10, lambda res: {"class":1, "subclass":stablehash64(res[0]), "data":[self.positionAsText]} )
예제 #20
0
    def analyse(self, tags, wikipediaTag="wikipedia"):
        err = []
        if wikipediaTag in tags:
            m = self.wiki_regexp.match(tags[wikipediaTag])
            if (tags[wikipediaTag].startswith(u"http://")
                    or tags[wikipediaTag].startswith(u"https://")) and not m:
                # tag 'wikipedia' starts with 'http://' but it's not a wikipedia url
                return [{"class": 30310, "subclass": 0}]
            elif m:
                # tag 'wikipedia' seams to be an url
                return [{
                    "class": 30311,
                    "subclass": 1,
                    "text": T_("Use wikipedia={0}:*", m.group(2)),
                    "fix": {
                        wikipediaTag:
                        "{0}:{1}".format(m.group(2),
                                         self.human_readable(m.group(3)))
                    }
                }]

            if not self.lang_regexp.match(tags[wikipediaTag]):
                err.append({"class": 30312, "subclass": 2})
            else:
                prefix = tags[wikipediaTag].split(':', 1)[0]
                tag = wikipediaTag + ':' + prefix
                if tag in tags:
                    err.append({
                        "class": 30316,
                        "subclass": 6,
                        "fix": {
                            '-': [tag]
                        }
                    })
            if "%" in tags[wikipediaTag] or "_" in tags[wikipediaTag]:
                err.append({
                    "class": 30313,
                    "subclass": 3,
                    "fix": {
                        wikipediaTag: self.human_readable(tags[wikipediaTag])
                    }
                })

        interwiki = False
        missing_primary = []
        for tag in [t for t in tags if t.startswith(wikipediaTag + ":")]:
            suffix = tag[len(wikipediaTag) + 1:]
            if ":" in suffix:
                suffix = suffix.split(":")[0]

            if self.Country and self.Country.startswith(
                    "UA"
            ) and suffix == "ru":  # In Ukraine wikipedia=uk:X + wikipedia:ru=Y are allowed
                continue

            if wikipediaTag in tags:
                if interwiki is False:
                    try:
                        lang, title = tags[wikipediaTag].split(':')
                        json_str = urlread(
                            u"https://" + lang +
                            u".wikipedia.org/w/api.php?action=query&prop=langlinks&titles="
                            + title + u"&redirects=&lllimit=500&format=json",
                            30)
                        interwiki = json.loads(json_str)
                        interwiki = dict(
                            map(
                                lambda x: [x["lang"], x["*"]],
                                list(interwiki["query"]["pages"].values())[0]
                                ["langlinks"]))
                    except:
                        interwiki = None

                if interwiki and suffix in interwiki and interwiki[
                        suffix] == self.human_readable(tags[tag]):
                    err.append({
                        "class":
                        30317,
                        "subclass":
                        stablehash64(tag),
                        "fix": [{
                            '-': [tag]
                        }, {
                            '-': [tag],
                            '~': {
                                wikipediaTag: suffix + ':' + interwiki[suffix]
                            }
                        }]
                    })

            if suffix in tags:
                # wikipedia:xxxx only authorized if tag xxxx exist
                err.extend(self.analyse(tags, wikipediaTag + ":" + suffix))

            elif self.lang_restriction_regexp.match(suffix):
                if not wikipediaTag in tags:
                    m = self.wiki_regexp.match(tags[tag])
                    if m:
                        value = self.human_readable(m.group(3))
                    elif tags[tag].startswith(suffix + ":"):
                        value = tags[tag][len(suffix) + 1:]
                    else:
                        value = self.human_readable(tags[tag])
                    missing_primary.append({
                        '-': [tag],
                        '+': {
                            wikipediaTag: "{0}:{1}".format(suffix, value)
                        }
                    })
            else:
                err.append({
                    "class": 30315,
                    "subclass": stablehash64(tag),
                    "text": T_("Invalid wikipedia suffix '{0}'", suffix)
                })

        if missing_primary != []:
            if self.Language:
                missing_primary = sorted(
                    missing_primary,
                    key=lambda x: x['+'][wikipediaTag][0:2]
                    if x['+'][wikipediaTag][0:2] != self.Language.split('_')[
                        0] else '')
            err.append({"class": 30314, "subclass": 4, "fix": missing_primary})

        return err
예제 #21
0
    def analyser_osmosis_common(self):
        self.run(sql10.format(type="nodes"))
        self.run(sql20.format(type="nodes"))
        self.run(
            sql30.format(type="nodes",
                         as_text="geom",
                         table="nodes",
                         geo="geom"),
            lambda res: {
                "class":
                1,
                "subclass":
                stablehash64(res[1]),
                "data":
                [self.node_full, None, None, None, None, self.positionAsText],
                "text": {
                    "en":
                    "{0} -> {1}".format(res[1], res[1].replace(
                        res[3], res[4], 1))
                },
                "fix": {
                    "-": [res[1]],
                    "+": {
                        res[1].replace(res[3], res[4], 1): res[2]
                    }
                }
            })

        self.run(sql10.format(type="ways"))
        self.run(sql20.format(type="ways"))
        self.run(
            sql30.format(type="ways",
                         as_text="way_locate(linestring)",
                         table="ways",
                         geo="linestring"),
            lambda res: {
                "class":
                1,
                "subclass":
                stablehash64(res[1]),
                "data":
                [self.way_full, None, None, None, None, self.positionAsText],
                "text": {
                    "en":
                    "{0} -> {1}".format(res[1], res[1].replace(
                        res[3], res[4], 1))
                },
                "fix": {
                    "-": [res[1]],
                    "+": {
                        res[1].replace(res[3], res[4], 1): res[2]
                    }
                }
            })

        self.run(sql10.format(type="relations"))
        self.run(sql20.format(type="relations"))
        self.run(
            sql30.format(type="relations",
                         as_text="relation_locate(id)",
                         table="relations",
                         geo="user"), lambda res: {
                             "class":
                             1,
                             "subclass":
                             stablehash64(res[1]),
                             "data": [
                                 self.relation_full, None, None, None, None,
                                 self.positionAsText
                             ],
                             "text": {
                                 "en":
                                 "{0} -> {1}".format(
                                     res[1], res[1].replace(res[3], res[4], 1))
                             },
                             "fix": {
                                 "-": [res[1]],
                                 "+": {
                                     res[1].replace(res[3], res[4], 1): res[2]
                                 }
                             }
                         })
예제 #22
0
    def check(self, tags):
        err = []
        for tag in self.PHONE_TAGS:
            if tag not in tags:
                continue
            phone = tags[tag]
            if u';' in phone:
                continue  # Ignore multiple phone numbers

            if self.suffix_separators is not None:
                phone = phone.split(self.suffix_separators, 1)[0]

            if self.values_separators:
                p = phone
                for sep in self.values_separators:
                    if sep in phone:
                        phone = phone.replace(sep, '; ')
                if p != phone:
                    phone = phone.replace('  ', ' ')
                    err.append({
                        "class": 30926,
                        "subclass": stablehash64(tag),
                        "text": {
                            'en': u'='.join([tag, phone])
                        },
                        "fix": {
                            tag:
                            phone.replace(' / ', '; ').replace(' - ',
                                                               '; ').replace(
                                                                   ',', ';')
                        }
                    })
                    continue

            phone_test = phone
            for c in '+0123456789 -./()':
                phone_test = phone_test.replace(c, '')
            if len(phone_test) > 0:
                err.append({
                    "class":
                    30925,
                    "subclass":
                    stablehash64(tag),
                    "text":
                    T_f(
                        u"Not allowed char \"{0}\" in phone number tag \"{1}\"",
                        phone_test, tag)
                })
                continue

            # Before local prefix
            if self.InternationalPrefix:
                r = self.InternationalPrefix.match(phone)
                if r:
                    err.append({
                        "class": 30924,
                        "subclass": stablehash64(tag),
                        "text": {
                            'en': u'='.join([tag, phone])
                        },
                        "fix": {
                            tag: "+" + r.group(1)
                        }
                    })
                    continue

            if self.InternationalAndLocalPrefix:
                r = self.InternationalAndLocalPrefix.match(phone)
                if r:
                    err.append({
                        "class": 30921,
                        "subclass": stablehash64(tag),
                        "text": {
                            'en': u'='.join([tag, phone])
                        },
                        "fix": {
                            tag: "+" + self.code + " " + r.group(1)
                        }
                    })
                    continue

            if self.MissingInternationalPrefix:
                r = self.MissingInternationalPrefix.match(phone)
                if r:
                    err.append({
                        "class": 30923,
                        "subclass": stablehash64(tag),
                        "text": {
                            'en': u'='.join([tag, phone])
                        },
                        "fix": {
                            tag: "+" + self.code + " " + r.group(1)
                        }
                    })
                    continue

            if self.BadShort:
                r = self.BadShort.match(phone)
                if r:
                    err.append({
                        "class": 30922,
                        "subclass": stablehash64(tag),
                        "text": {
                            'en': u'='.join([tag, phone])
                        },
                        "fix": {
                            tag: r.group(1)
                        }
                    })
                    continue

            # Last
            if self.Format:
                r = self.Format.match(phone)
                if not r:
                    err.append({
                        "class": 30920,
                        "subclass": stablehash64(tag),
                        "text": {
                            'en': u'='.join([tag, phone])
                        }
                    })
                    continue

        return err
  def way(self, data, tags, nds):
    # Get the relevant tags with *:conditional
    tags_conditional = {}
    for tag in tags:
      if tag[-12:] == ":conditional":
        if "source:" in tag or "note:" in tag or "fixme:" in tag:
          continue
        tags_conditional[tag] = tags[tag]
    if len(tags_conditional) == 0:
      return

    err = []
    for tag in tags_conditional:
      tag_value = tags_conditional[tag]
      conditions = []
      parentheses = 0
      bad_tag = False

      if not "@" in tag_value:
        err.append({"class": 33501, "subclass": 0 + stablehash64(tag + '|' + tag_value), "text": T_("Missing `@` in \"{0}\"", tag)})
        continue

      # Conditionals are split by semicolons, i.e. value @ condition; value @ condition
      # Herein, condition can also contain semicolons, e.g. no @ (Mo 06:00-24:00; Tu-Fr 00:00-24:00)
      # In this case, the condition is wrapped in parentheses ( )
      # Additionally, there's the magic keyword 'AND' to combine conditions

      # Get the parts after the @ excluding parentheses and put them in the list conditions
      # Also validate the syntax of value @ (condition); value @ condition is obeyed
      tmp_str = ""
      condition_started = False
      for c in tag_value:
        if c == "@":
          if len(tmp_str.strip()) == 0:
            err.append({"class": 33501, "subclass": 1 + stablehash64(tag + '|' + tag_value), "text": T_("Missing value for the condition in \"{0}\"", tag)})
            bad_tag = True
            break
          tmp_str = ""
          condition_started = True
        elif c == "(":
          parentheses += 1
          if not condition_started:
            err.append({"class": 33501, "subclass": 0 + stablehash64(tag + '|' + tag_value), "text": T_("Missing `@` in \"{0}\"", tag)})
            bad_tag = True
            break
        elif c == ")":
          parentheses -= 1
          if parentheses == -1:
            err.append({"class": 33501, "subclass": 2 + stablehash64(tag + '|' + tag_value), "text": T_("Mismatch in the number of parentheses in \"{0}\"", tag)})
            bad_tag = True
            break
        elif c == ";" and parentheses == 0 and condition_started:
          tmp_str = tmp_str.strip()
          if len(tmp_str) == 0:
            err.append({"class": 33501, "subclass": 3 + stablehash64(tag + '|' + tag_value), "text": T_("Missing condition, `@` or parentheses in \"{0}\"", tag)})
            bad_tag = True
            break
          conditions.append(tmp_str)
          condition_started = False
          tmp_str = ""
        else:
          tmp_str += c

      if not bad_tag:
        if parentheses == 0:
          # Last condition wouldn't be added in the loop
          tmp_str = tmp_str.strip()
          if not condition_started or len(tmp_str) == 0:
            err.append({"class": 33501, "subclass": 3 + stablehash64(tag + '|' + tag_value), "text": T_("Missing condition, `@` or parentheses in \"{0}\"", tag)})
            continue
          conditions.append(tmp_str)
        else:
          err.append({"class": 33501, "subclass": 2 + stablehash64(tag + '|' + tag_value), "text": T_("Mismatch in the number of parentheses in \"{0}\"", tag)})
          continue

      # Check the position of AND is ok
      if not bad_tag:
        for condition in conditions:
          tmp_cond = " " + condition.replace(" ", "  ") + " "
          tmp_ANDsplitted = tmp_cond.upper().split(" AND ")
          for splittedANDpart in tmp_ANDsplitted:
            if len(splittedANDpart.strip()) == 0:
              err.append({"class": 33501, "subclass": 4 + stablehash64(tag + '|' + tag_value), "text": T_("Missing condition before or after AND combinator in \"{0}\"", tag)})
              bad_tag = True
              break

      if bad_tag:
        continue

      # Find outdated conditional restrictions, i.e. temporary road closures
      for condition in conditions:
        years_str = re.findall(self.ReYear, condition)
        if len(years_str) == 0:
          continue

        maxYear = int(max(years_str))
        if maxYear < self.currentYear:
          err.append({"class": 33503, "subclass": 0 + stablehash64(tag + '|' + tag_value + '|' + condition), "text": T_("Condition \"{0}\" in \"{1}\" was only valid until {2}", condition, tag, maxYear)})

      # No parentheses around conditions
      if tag_value.count("(") < len(conditions):
        if not (tag_value.count("(") == len(conditions) - 1 and not tag_value[-1] == ")" and re.search(self.ReSimpleCondition, conditions[-1])):
          # Accept no parentheses around the last one if the last condition was a simple one
          err.append({"class": 33502, "subclass": 0 + stablehash64(tag + '|' + tag_value), "text": T_("Add parentheses around the condition(s) in \"{0}\"", tag)})

    if err != []:
      return err
    def __init__(self, config, logger=None):
        Analyser_Osmosis.__init__(self, config, logger)
        self.classs[1] = self.def_class(
            item=7040,
            level=3,
            tags=['power', 'fix:imagery'],
            title=T_('Lone power tower or pole'),
            fix=T_(
                '''This tower should probably be connected to a power line.''')
        )
        self.classs[2] = self.def_class(
            item=7040,
            level=2,
            tags=['power', 'fix:imagery'],
            title=T_('Unfinished power major line'),
            detail=T_(
                '''The line ends in a vacuum, and should be connected to another line or
a transformer `power=substation` or a generator `power=generator`.'''))
        self.classs[6] = self.def_class(
            item=7040,
            level=3,
            tags=['power', 'fix:imagery'],
            title=T_('Unfinished power minor line'))
        self.classs[3] = self.def_class(
            item=7040,
            level=3,
            tags=['power', 'fix:chair'],
            title=T_('Connection between different voltages'),
            detail=T_(
                '''Two lines meet in one point, but inconsistent with voltages from the
tag `voltage=*`.'''))
        self.classs_change[4] = self.def_class(
            item=7040,
            level=3,
            tags=['power', 'fix:imagery'],
            title=T_('Non power node on power way'),
            fix=T_(
                '''If this node is a tower or pole, use the tag `power=tower` or
`power=pole` else remove it.'''))
        self.classs_change[5] = self.def_class(
            item=7040,
            level=3,
            tags=['power', 'fix:imagery'],
            title=T_('Missing power tower or pole'),
            detail=T_(
                '''According to the statistical frequency of the poles on the power line,
it should miss one pole nearby.'''))
        self.classs[7] = self.def_class(
            item=7040,
            level=3,
            tags=['power', 'fix:chair'],
            title=T_('Unmatched voltage of line on substation'))

        self.callback40 = lambda res: {
            "class": 4,
            "data": [self.node_full, self.positionAsText],
            "fix": [{
                "+": {
                    "power": "tower"
                }
            }, {
                "+": {
                    "power": "pole"
                }
            }]
        }
        self.callback50 = lambda res: {
            "class": 5,
            "subclass": stablehash64(res[1]),
            "data": [self.way_full, self.positionAsText]
        }
예제 #25
0
  def way(self, data, tags, nds):
    # Get the relevant tags with *:conditional
    tags_conditional = {}
    for tag in tags:
      if tag[-12:] == ":conditional":
        if "source:" in tag or "note:" in tag or "fixme:" in tag:
          continue
        tags_conditional[tag] = tags[tag]
    if len(tags_conditional) == 0:
      return

    err = []
    for tag in tags_conditional:
      tag_value = tags_conditional[tag]
      conditions = []
      parentheses = 0
      past_parentheses = False
      bad_tag = False

      if not "@" in tag_value:
        err.append({"class": 33501, "subclass": 0 + stablehash64(tag + '|' + tag_value), "text": T_("Missing `@` in \"{0}\"", tag)})
        continue

      # Conditionals are split by semicolons, i.e. value @ condition; value @ condition
      # Herein, condition can also contain semicolons, e.g. no @ (Mo 06:00-24:00; Tu-Fr 00:00-24:00)
      # In this case, the condition is wrapped in parentheses ( )
      # Additionally, there's the magic keyword 'AND' to combine conditions

      # Get the parts after the @ excluding parentheses and put them in the list conditions
      # Also validate the syntax of value @ (condition); value @ condition is obeyed
      tmp_str = ""
      condition_started = False
      for c in tag_value:
        if c == "@" and not past_parentheses:
          if len(tmp_str.strip()) == 0:
            err.append({"class": 33501, "subclass": 1 + stablehash64(tag + '|' + tag_value), "text": T_("Missing value for the condition in \"{0}\"", tag)})
            bad_tag = True
            break
          tmp_str = ""
          condition_started = True
        elif c == "(":
          parentheses += 1
          if not condition_started:
            err.append({"class": 33501, "subclass": 0 + stablehash64(tag + '|' + tag_value), "text": T_("Missing `@` in \"{0}\"", tag)})
            bad_tag = True
            break
          if parentheses == 1 and tmp_str.lstrip() != "":
            err.append({"class": 33501, "subclass": 7 + stablehash64(tag + '|' + tag_value), "text": T_("Unexpected \"{0}\" before or after parentheses in \"{1}\"", tmp_str.strip(), tag)})
            bad_tag = True
            break
        elif c == ")":
          parentheses -= 1
          if parentheses == -1:
            err.append({"class": 33501, "subclass": 2 + stablehash64(tag + '|' + tag_value), "text": T_("Mismatch in the number of parentheses in \"{0}\"", tag)})
            bad_tag = True
            break
          if parentheses == 0:
            past_parentheses = True
        elif c == ";" and parentheses == 0 and condition_started:
          tmp_str = tmp_str.strip()
          if len(tmp_str) == 0:
            err.append({"class": 33501, "subclass": 3 + stablehash64(tag + '|' + tag_value), "text": T_("Missing condition, `@` or parentheses in \"{0}\"", tag)})
            bad_tag = True
            break
          conditions.append(tmp_str)
          condition_started = False
          past_parentheses = False
          tmp_str = ""
        else:
          tmp_str += c
          if past_parentheses and c != " ": # tolerate spaces
            err.append({"class": 33501, "subclass": 7 + stablehash64(tag + '|' + tag_value), "text": T_("Unexpected \"{0}\" before or after parentheses in \"{1}\"", c, tag)})
            bad_tag = True
            break

      if not bad_tag:
        if parentheses == 0:
          # Last condition wouldn't be added in the loop
          tmp_str = tmp_str.strip()
          if not condition_started or len(tmp_str) == 0:
            err.append({"class": 33501, "subclass": 3 + stablehash64(tag + '|' + tag_value), "text": T_("Missing condition, `@` or parentheses in \"{0}\"", tag)})
            continue
          conditions.append(tmp_str)
        else:
          err.append({"class": 33501, "subclass": 2 + stablehash64(tag + '|' + tag_value), "text": T_("Mismatch in the number of parentheses in \"{0}\"", tag)})
          continue

      if not bad_tag:
        for condition in conditions:
          condition_ANDsplitted = list(map(str.strip, self.ReAND.split(condition)))
          # Check the position of AND is ok
          if "" in condition_ANDsplitted:
            err.append({"class": 33501, "subclass": 4 + stablehash64(tag + '|' + tag_value + '|' + condition), "text": T_("Missing condition before or after AND combinator in \"{0}\"", tag)})
            bad_tag = True
            continue

          if len(condition_ANDsplitted) != condition.count("AND") + 1:
            # Likely lower/mixed case 'AND' used. Might also be a opening_hours fallback rule
            # For simplicity: ignore.
            continue

          for c in condition_ANDsplitted:
            # Validate time-based conditionals
            if self.isLikelyOpeningHourSyntax(c):
              sanitized = self.sanitize_openinghours(self.kOpeningHours452236.sub(r"\1\2", c))
              if not sanitized['isValid']:
                if "fix" in sanitized:
                  err.append({"class": 33504, "subclass": 6 + stablehash64(tag + '|' + tag_value + '|' + c), "text": T_("Involves \"{0}\" in \"{1}\". Consider using \"{2}\"", c, tag, sanitized['fix'])})
                else:
                  err.append({"class": 33504, "subclass": 6 + stablehash64(tag + '|' + tag_value + '|' + c), "text": T_("Involves \"{0}\" in \"{1}\"", c, tag)})
                bad_tag = True
                break
            else:
              # Validate vehicle property comparisons
              if c[0] in self.comparisonOperatorChars or c[-1] in self.comparisonOperatorChars:
                err.append({"class": 33501, "subclass": 5 + stablehash64(tag + '|' + tag_value + '|' + c), "text": T_("Unexpected <, = or > in \"{0}\"", tag)})
                bad_tag = True
                break

      if bad_tag:
        continue

      # Find outdated conditional restrictions, i.e. temporary road closures
      for condition in conditions:
        years_str = re.findall(self.ReYear, condition)
        if len(years_str) == 0:
          continue

        maxYear = int(max(years_str))
        if maxYear < self.currentYear:
          err.append({"class": 33503, "subclass": 0 + stablehash64(tag + '|' + tag_value + '|' + condition), "text": T_("Condition \"{0}\" in \"{1}\" was only valid until {2}", condition, tag, maxYear)})

      # No parentheses around conditions
      if tag_value.count("(") < len(conditions):
        if not (tag_value.count("(") == len(conditions) - 1 and not tag_value[-1] == ")" and re.search(self.ReSimpleCondition, conditions[-1])):
          # Accept no parentheses around the last one if the last condition was a simple one
          err.append({"class": 33502, "subclass": 0 + stablehash64(tag + '|' + tag_value), "text": T_("Add parentheses around the condition(s) in \"{0}\"", tag)})

    if err != []:
      return err
예제 #26
0
 def __init__(self, config, logger=None):
     Analyser_Osmosis.__init__(self, config, logger)
     self.FR = config.options and (
         "country" in config.options
         and config.options["country"].startswith("FR")
         or "test" in config.options)
     self.classs_change[1] = {
         "item": "0",
         "level": 3,
         "tag": ["building", "geom", "fix:chair"],
         "desc": T_(u"Building intersection")
     }
     self.classs_change[2] = {
         "item": "0",
         "level": 2,
         "tag": ["building", "geom", "fix:chair"],
         "desc": T_(u"Large building intersection")
     }
     self.classs_change[3] = {
         "item": "0",
         "level": 3,
         "tag": ["building", "geom", "fix:chair"],
         "desc": T_(u"Building too small")
     }
     self.classs_change[4] = {
         "item": "0",
         "level": 3,
         "tag": ["building", "geom", "fix:chair"],
         "desc": T_(u"Gap between buildings")
     }
     self.classs_change[5] = {
         "item": "0",
         "level": 1,
         "tag": ["building", "fix:chair"],
         "desc": T_(u"Large building intersection cluster")
     }
     if self.FR:
         self.classs_change[6] = {
             "item": "1",
             "level": 3,
             "tag": ["building", "geom", "fix:chair"],
             "desc": T_(u"Building in parts")
         }
     self.callback30 = lambda res: {
         "class": 2 if res[3] > res[4] else 1,
         "data": [self.way, self.way, self.positionAsText]
     }
     self.callback40 = lambda res: {
         "class": 3,
         "data": [self.way, self.positionAsText]
     }
     self.callback50 = lambda res: {
         "class": 4,
         "data": [self.way, self.way, self.positionAsText]
     }
     self.callback60 = lambda res: {
         "class": 5,
         "subclass": stablehash64(res[0]),
         "data": [self.positionAsText]
     }
     if self.FR:
         self.callback70 = lambda res: {
             "class": 6,
             "data": [self.way, self.positionAsText]
         }
예제 #27
0
    def analyser_osmosis_common(self):
        self.run(
            "SET search_path TO %s" % (self.config.db_schema_path or ','.join(
                [self.config.db_user, self.config.db_schema, 'public']), ))
        table = self.load.run(self, self.parser, self.mapping,
                              self.config.db_user,
                              self.__class__.__name__.lower()[15:],
                              self.analyser_version())
        if not table:
            self.logger.log(u"Empty bbox, abort")
            return

        # Extract OSM objects
        if self.load.srid:
            typeSelect = {
                'N': 'geom',
                'W': 'linestring',
                'R': 'relation_locate(id)'
            }
            typeGeom = {
                'N': 'geom',
                'W': 'way_locate(linestring)',
                'R': 'relation_locate(id)'
            }
            if self.mapping.osmRef == "NULL" or self.possible_merge:
                typeShape = {
                    'N': 'geom',
                    'W': 'ST_Envelope(linestring)',
                    'R': 'relation_shape(id)'
                }
            else:
                typeShape = {'N': 'NULL', 'W': 'NULL', 'R': 'NULL'}
        else:
            typeSelect = {'N': 'NULL', 'W': 'NULL', 'R': 'NULL'}
            typeGeom = {'N': 'NULL', 'W': 'NULL', 'R': 'NULL'}
            typeShape = {'N': 'NULL', 'W': 'NULL', 'R': 'NULL'}
        self.logger.log(u"Retrive OSM item")
        where = "((" + (") OR (".join(
            map(lambda x: self.where(x), self.mapping.select.tags))) + "))"
        self.run("CREATE TEMP TABLE osm_item AS " + ("UNION ALL".join(
            map(
                lambda type: ("""(
                    SELECT
                        '%(type)s'::char(1) AS type,
                        id,
                        trim(both from ref) AS ref,
                        %(geom)s::geography AS geom,
                        %(shape)s::geography AS shape,
                        tags
                    FROM
                        %(from)s
                        LEFT JOIN LATERAL regexp_split_to_table(tags->'%(ref)s', ';') a(ref) ON true
                    WHERE""" + ("""
                        %(geomSelect)s IS NOT NULL AND""" if self.load.srid
                                else "") + ("""
                        ST_SetSRID(ST_Expand(ST_GeomFromText('%(bbox)s'), %(distance)s), 4326) && %(geomSelect)s AND"""
                                            if self.load.bbox and self.load.
                                            srid else "") + """
                        tags != ''::hstore AND
                        %(where)s)""") %
                {
                    "type": type[0].upper(),
                    "ref": self.mapping.osmRef,
                    "geomSelect": typeSelect[type[0].upper()],
                    "geom": typeGeom[type[0].upper()],
                    "shape": typeShape[type[0].upper()],
                    "from": type,
                    "bbox": self.load.bbox,
                    "distance": self.mapping.conflationDistance or 0,
                    "where": where
                }, self.mapping.select.types))))
        if self.mapping.osmRef != "NULL":
            self.run("CREATE INDEX osm_item_index_ref ON osm_item(ref)")
        self.run(
            "CREATE INDEX osm_item_index_shape ON osm_item USING GIST(shape)")

        joinClause = []
        if self.mapping.osmRef != "NULL":
            joinClause.append("official.ref = osm_item.ref")
        elif self.load.srid:
            joinClause.append("ST_DWithin(official.geom, osm_item.shape, %s)" %
                              self.mapping.conflationDistance)
        if self.mapping.extraJoin:
            joinClause.append(
                "official.tags->'%(tag)s' = osm_item.tags->'%(tag)s'" %
                {"tag": self.mapping.extraJoin})
        joinClause = " AND\n".join(joinClause) + "\n"

        # Missing official
        self.run(sql10 % {"official": table, "joinClause": joinClause})
        self.run(sql11)
        if self.missing_official:
            self.run(
                sql12, lambda res: {
                    "class":
                    self.missing_official["class"],
                    "subclass":
                    str(stablehash64("%s%s%s" % (res[0], res[1], res[3]))),
                    "self":
                    lambda r: [0] + r[1:],
                    "data": [self.node_new, self.positionAsText],
                    "text":
                    self.mapping.generate.text(
                        defaultdict(lambda: None, res[2]),
                        defaultdict(lambda: None, res[3])),
                    "fix":
                    self.passTags(res[2]) if self.mapping.generate.
                    missing_official_fix and res[2] != {} else None,
                })

        if self.mapping.osmRef != "NULL":
            self.run(sql20 % {"official": table, "joinClause": joinClause})
            self.run(sql21)
            if self.missing_osm:
                # Missing OSM
                self.run(
                    sql22, lambda res: {
                        "class":
                        self.missing_osm["class"],
                        "data":
                        [self.typeMapping[res[1]], None, self.positionAsText]
                    })
                # Invalid OSM
                self.run(
                    sql23 % {
                        "official": table,
                        "joinClause": joinClause
                    }, lambda res: {
                        "class":
                        self.missing_osm["class"],
                        "subclass":
                        str(stablehash64(res[5]))
                        if self.mapping.osmRef != "NULL" else None,
                        "data":
                        [self.typeMapping[res[1]], None, self.positionAsText]
                    })

            # Possible merge
            if self.possible_merge:
                possible_merge_joinClause = []
                possible_merge_orderBy = ""
                if self.load.srid:
                    possible_merge_joinClause.append(
                        "ST_DWithin(missing_official.geom, missing_osm.shape, %s)"
                        % self.mapping.conflationDistance)
                    possible_merge_orderBy = ", ST_Distance(missing_official.geom, missing_osm.shape) ASC"
                if self.mapping.extraJoin:
                    possible_merge_joinClause.append(
                        "missing_official.tags->'%(tag)s' = missing_osm.tags->'%(tag)s'"
                        % {"tag": self.mapping.extraJoin})
                possible_merge_joinClause = " AND\n".join(
                    possible_merge_joinClause) + "\n"
                self.run(
                    sql30 % {
                        "joinClause": possible_merge_joinClause,
                        "orderBy": possible_merge_orderBy
                    }, lambda res: {
                        "class":
                        self.possible_merge["class"],
                        "subclass":
                        str(stablehash64("%s%s" % (res[0], str(res[3])))),
                        "data":
                        [self.typeMapping[res[1]], None, self.positionAsText],
                        "text":
                        self.mapping.generate.text(
                            defaultdict(lambda: None, res[3]),
                            defaultdict(lambda: None, res[4])),
                        "fix":
                        self.mergeTags(
                            res[5], res[3], self.mapping.osmRef, self.mapping.
                            generate.tag_keep_multiple_values),
                    })

            self.dumpCSV(
                "SELECT ST_X(geom::geometry) AS lon, ST_Y(geom::geometry) AS lat, tags FROM %s"
                % table, "", ["lon", "lat"], lambda r, cc: list(
                    (r['lon'], r['lat'])) + cc)

            self.run(sql40 % {"official": table, "joinClause": joinClause})
            self.dumpCSV(
                sql41, ".byOSM", ["osm_id", "osm_type", "lon", "lat"],
                lambda r, cc: list(
                    (r['osm_id'], r['osm_type'], r['lon'], r['lat'])) + cc)

            file = io.open("%s/%s.metainfo.csv" %
                           (self.config.dst_dir, self.name),
                           "w",
                           encoding="utf8")
            file.write(
                u"file,origin,osm_date,official_non_merged,osm_non_merged,merged\n"
            )
            if self.missing_official:
                self.giscurs.execute("SELECT COUNT(*) FROM missing_official;")
                official_non_merged = self.giscurs.fetchone()[0]
            else:
                official_non_merged = 0
            self.giscurs.execute("SELECT COUNT(*) FROM missing_osm;")
            osm_non_merged = self.giscurs.fetchone()[0]
            self.giscurs.execute("SELECT COUNT(*) FROM match;")
            merged = self.giscurs.fetchone()[0]
            file.write(u"\"%s\",\"%s\",FIXME,%s,%s,%s\n" %
                       (self.name, self.parser.source.fileUrl or self.url,
                        official_non_merged, osm_non_merged, merged))
            file.close()

        # Moved official
        if self.moved_official:
            self.run(
                sql50 % {
                    "official": table,
                    "joinClause": joinClause
                }, lambda res: {
                    "class": self.moved_official["class"],
                    "data": [self.node_full, self.positionAsText],
                })

        # Update official
        if self.update_official:
            self.run(
                sql60 % {
                    "official": table,
                    "joinClause": joinClause
                }, lambda res: {
                    "class":
                    self.update_official["class"],
                    "subclass":
                    str(stablehash64("%s%s" % (res[0], str(res[5])))),
                    "data":
                    [self.typeMapping[res[1]], None, self.positionAsText],
                    "text":
                    self.mapping.generate.text(
                        defaultdict(lambda: None, res[3]),
                        defaultdict(lambda: None, res[5])),
                    "fix":
                    self.mergeTags(
                        res[4], res[3], self.mapping.osmRef, self.mapping.
                        generate.tag_keep_multiple_values),
                })
예제 #28
0
    def way(self, data, tags, nds):
        if not "highway" in tags:
            return

        lanes = False
        for tag in tags:
            if "lanes" in tag:
                lanes = True
                break

        if not lanes:
            return

        tags_lanes = {}
        for tag in tags:
            if "lanes" in tag and not "source" in tag and not "FIXME" in tag:
                tags_lanes[tag] = tags[tag]

        err = []

        # Check trun lanes values
        tl = "turn:lanes" in tags_lanes
        tlf = "turn:lanes:forward" in tags_lanes
        tlb = "turn:lanes:backward" in tags_lanes
        tl2 = "turn:lanes:both_ways" in tags_lanes
        if tl or tlf or tlb or tl2:
            for tl in [
                    "turn:lanes", "turn:lanes:forward", "turn:lanes:backward",
                    "turn:lanes:both_ways"
            ]:
                if tl in tags_lanes:
                    ttt = tags_lanes[tl].split("|")
                    unknown = False
                    i = 0
                    for tt in ttt:
                        for t in set(tt.split(";")):
                            if t not in [
                                    "left", "slight_left", "sharp_left",
                                    "through", "right", "slight_right",
                                    "sharp_right", "reverse", "merge_to_left",
                                    "merge_to_right", "none", ""
                            ]:
                                unknown = True
                                err.append({
                                    "class":
                                    31606,
                                    "subclass":
                                    0 +
                                    stablehash64(tl + '|' + t + '|' + str(i)),
                                    "text":
                                    T_("Unknown turn lanes value \"{0}\"", t)
                                })
                            if (t == "merge_to_left"
                                    and i == 0) or (t == "merge_to_right"
                                                    and i == len(ttt) - 1):
                                err.append({
                                    "class":
                                    31600,
                                    "subclass":
                                    1 +
                                    stablehash64(tl + '|' + t + '|' + str(i))
                                })
                        i += 1
                    if not unknown:
                        # merge_to_left is a on the right and vice versa
                        t = tags_lanes[tl] \
                            .replace("left", "l").replace("slight_left", "l").replace("sharp_left", "l") \
                            .replace("through", " ") \
                            .replace("right", "r").replace("slight_right", "r").replace("sharp_right", "r") \
                            .replace("reverse", "U") \
                            .replace("merge_to_left", "r").replace("merge_to_right", "l") \
                            .replace("none", " ").replace(";", "").split("|")
                        t = ''.join(
                            map(
                                lambda e: " "
                                if len(e) == 0 or e[0] != e[-1] else e[0],
                                map(sorted, t)))
                        t = t.replace('U', '')  # Ignore reverse
                        last_left = self.rindex_(t, "l")
                        first_space = self.index_(t, " ")
                        last_space = self.rindex_(t, " ")
                        first_right = self.index_(t, "r")
                        # Check right is on the right and left is on the left...
                        if not ((last_left is None or first_space is None
                                 or last_left < first_space) and
                                (first_space is None or last_space is None
                                 or first_space <= last_space) and
                                (last_space is None or first_right is None
                                 or last_space < first_right) and
                                (last_left is None or first_right is None
                                 or last_left < first_right)):
                            err.append({
                                "class": 31607,
                                "subclass": 1 + stablehash64(tl)
                            })

        # Check access lanes values

        # Count for non fullwidth lanes
        non_fullwidth_lanes_number = {}
        for direction in ['', ':forward', ':backward', ':both_ways']:
            o = tags_lanes.get('bicycle:lanes' + direction)
            if o:
                non_fullwidth_lanes_number[direction] = ilen(
                    filter(lambda i: i == 'designated', o.split('|')))

        for access in [
                'hgv', 'bus', 'access', 'bicycle', 'psv', 'taxi', 'vehicle',
                'motor_vehicle', 'hov', 'motorcycle', 'goods'
        ]:
            base = access + ':lanes'
            for tag in tags_lanes:
                if tag.startswith(base):
                    try:
                        int(tags_lanes[tag])
                        err.append({
                            "class": 31609,
                            "subclass": 1 + stablehash64(tag),
                            "text": {
                                'en': '{0}={1}'.format(tag, tags_lanes[tag])
                            }
                        })
                    except ValueError:
                        # Ok, should not be an integer
                        pass

        stars = []
        for tag in tags_lanes:
            if ":lanes" in tag:
                star = tag.split(':')[0]
                if star not in ('source', 'proposed', 'construction', 'note'):
                    stars.append(star)
        stars = list(set(stars))

        for star in stars:
            l = star + '' in tags_lanes
            lf = star + ':forward' in tags_lanes
            lb = star + ':backward' in tags_lanes
            l2 = star + ':both_ways' in tags_lanes
            if l and (lf or lb or l2):
                err.append({
                    "class": 31603,
                    "subclass": 0 + stablehash64(star),
                    "text": {
                        "en": star + ":*"
                    }
                })

        if err != []:
            return err

        number = {'lanes': {}}
        for tag in tags_lanes:
            if tag == 'lanes' or tag.startswith('lanes:'):
                try:
                    n = int(tags_lanes[tag])
                    parts = tag.split(':')
                    direction = ''
                    if len(parts) == 1:
                        number['lanes'][''] = n
                    elif len(parts) == 2 and parts[1] in [
                            'forward', 'backward', 'both_ways'
                    ]:
                        number['lanes'][':' + parts[1]] = n
                except ValueError:
                    err.append({
                        "class":
                        31601,
                        "subclass":
                        0 + stablehash64(tag),
                        "text":
                        T_("lanes={0} is not an integer", tags_lanes[tag])
                    })

        for star in stars:
            number[star] = {}
            for direction in ['', ':forward', ':backward', ':both_ways']:
                o = tags_lanes.get(star + ':lanes' + direction)
                if o:
                    number[star][direction] = len(o.split('|'))

        n_lanes = {}
        for direction in ['', ':forward', ':backward', ':both_ways']:
            tag = None
            for star in sorted(number.keys()):
                non_fullwidth_lanes_number_star = ((
                    non_fullwidth_lanes_number.get(direction) or 0)
                                                   if star != 'lanes' else 0)
                non_fullwidth_lanes_number_tag = (
                    (non_fullwidth_lanes_number.get(direction) or 0)
                    if tag != 'lanes:lanes' + direction else 0)
                if n_lanes.get(direction) is not None and number[star].get(direction) is not None and \
                        number[star][direction] - non_fullwidth_lanes_number_star != \
                        n_lanes[direction] - non_fullwidth_lanes_number_tag:
                    err.append({
                        "class":
                        31608,
                        "subclass":
                        0 + stablehash64(direction + '|' + star),
                        "text": {
                            "en":
                            "(lanes({0})={1}) - (non fullwidth={2}) != (lanes({3})={4}) - (non fullwidth={5})"
                            .format(star + ":*" + direction,
                                    number[star][direction],
                                    non_fullwidth_lanes_number_star, tag,
                                    n_lanes[direction],
                                    non_fullwidth_lanes_number_tag)
                        }
                    })
                elif n_lanes.get(direction) is None and number[star].get(
                        direction) is not None:
                    # Fist loop, pick the star as tag and the number of lanes to compare to the others
                    n_lanes[direction] = number[star][direction]
                    tag = star + ":lanes" + direction

        if err != []:
            return err

        if tags["highway"] == 'motorway':
            oneway = "oneway" not in tags or tags["oneway"] not in [
                "no", "false"
            ]
        else:
            oneway = "oneway" in tags and tags["oneway"] not in ["no", "false"]

        if oneway:
            for tag in tags:
                if tag.startswith('oneway:') and tags[tag] in ["no", "false"]:
                    # Oneway for mainstream traffic, but not for an other one, so we are not really on a oneway
                    oneway = False

        if tags.get('junction') == 'roundabout':
            oneway = True

        nl = n_lanes.get('')
        nlf = n_lanes.get(':forward')
        nlb = n_lanes.get(':backward')
        nl2 = n_lanes.get(':both_ways')

        nfw_nl = non_fullwidth_lanes_number.get('') or 0
        nfw_nlf = non_fullwidth_lanes_number.get(':forward') or 0
        nfw_nlb = non_fullwidth_lanes_number.get(':backward') or 0
        nfw_nl2 = non_fullwidth_lanes_number.get(':both_ways') or 0

        if oneway:
            if nl is not None and nlf is not None and nl != nlf - nfw_nlf:
                err.append({
                    "class":
                    31604,
                    "subclass":
                    0,
                    "text":
                    T_(
                        "on oneway, (lanes={0}) != (lanes:forward={1}) - (non fullwidth forward={2})",
                        nl, nlf, nfw_nlf)
                })
            elif nlb is not None or nl2 is not None:
                err.append({"class": 31605, "subclass": 0})
        else:
            if nl is not None and nlf is not None and nlb is not None and nl != nlf + nlb + (
                    nl2 or 0) - nfw_nl - nfw_nlf - nfw_nlb - nfw_nl2:
                err.append({
                    "class":
                    31604,
                    "subclass":
                    0,
                    "text":
                    T_(
                        "on two way, (lanes={0}) != (lanes:forward={1}) + (lanes:backward={2}) + (lanes:both_ways={3}) - (non fullwidth={4}) - (non fullwidth forward={5}) - (non fullwidth backward={6}) - (non fullwidth both_ways={7})",
                        nl, nlf, nlb, nl2, nfw_nl, nfw_nlf, nfw_nlb, nfw_nl2)
                })
            elif nl is not None and nlf is not None and nl <= nlf - nfw_nlf:
                err.append({
                    "class":
                    31604,
                    "subclass":
                    0,
                    "text":
                    T_(
                        "on two way, (lanes={0}) <= (lanes:forward={1}) - (non fullwidth forward={2})",
                        nl, nlf, nfw_nlf)
                })
            elif nl is not None and nlb is not None and nl <= nlb - nfw_nlb:
                err.append({
                    "class":
                    31604,
                    "subclass":
                    0,
                    "text":
                    T_(
                        "on two way, (lanes={0}) <= (lanes:backward={1}) - (non fullwidth backward={2})",
                        nl, nlb, nfw_nlb)
                })
            elif nl is not None and nl2 is not None and nl < nl2 - nfw_nl2:
                err.append({
                    "class":
                    31604,
                    "subclass":
                    0,
                    "text":
                    T_(
                        "on two way, (lanes={0}) < (lanes:both_ways={1}) - (non fullwidth both_ways={2})",
                        nl, nl2, nfw_nl2)
                })

        if err != []:
            return err
예제 #29
0
    def __init__(self, config, logger=None):
        Analyser_Osmosis.__init__(self, config, logger)
        self.FR = config.options and (
            "country" in config.options
            and config.options["country"].startswith("FR")
            or "test" in config.options)
        fix = T_(
            '''Fix geometry so that buildings don't overlap, but share nodes if physically joined.
If geometry is correct and there's some vertical difference then make use of the `layer` tag to indicate this.'''
        )

        self.classs_change[1] = self.def_class(
            item=0,
            level=3,
            tags=['building', 'geom', 'fix:chair'],
            title=T_('Building intersection'),
            fix=fix)
        self.classs_change[2] = self.def_class(
            item=0,
            level=2,
            tags=['building', 'geom', 'fix:chair'],
            title=T_('Large building intersection'),
            fix=self.merge_doc(
                fix,
                T_('''Large intersections may also be a duplicated mapping - in which case delete the less accurate elements.'''
                   )))
        self.classs_change[3] = self.def_class(
            item=0,
            level=3,
            tags=['building', 'geom', 'fix:chair'],
            title=T_('Building too small'),
            detail=T_(
                'The area of this feature is too small to possibly be a building.'
            ),
            fix=T_('''- Correct the gometry if inaccurately mapped.
- Correct the tagging if this isn't a building.
- Delete the feature if it's invalid.'''))
        self.classs_change[4] = self.def_class(
            item=0,
            level=3,
            tags=['building', 'geom', 'fix:chair'],
            title=T_('Gap between buildings'),
            detail=T_(
                '''It looks like these buildings should be physically joined, but they don't share nodes to indicate this.'''
            ),
            fix=T_(
                'Connect the buildings by joining nodes where appropriate.'))
        self.classs_change[5] = self.def_class(
            item=0,
            level=1,
            tags=['building', 'fix:chair'],
            title=T_('Large building intersection cluster'),
            fix=self.merge_doc(
                fix,
                T_('''Large intersections may also be a duplicated mapping - in which case delete the less accurate elements.'''
                   )))
        if self.FR:
            self.classs_change[6] = self.def_class(
                item=1,
                level=3,
                tags=['building', 'geom', 'fix:chair'],
                title=T_("Building in parts"),
                fix=T_('Merge the building parts together as appropriate.'))

        self.callback30 = lambda res: {
            "class": 2 if res[3] > res[4] else 1,
            "data": [self.way, self.way, self.positionAsText]
        }
        self.callback40 = lambda res: {
            "class": 3,
            "data": [self.way, self.positionAsText]
        }
        self.callback50 = lambda res: {
            "class": 4,
            "data": [self.way, self.way, self.positionAsText]
        }
        self.callback60 = lambda res: {
            "class": 5,
            "subclass": stablehash64(res[0]),
            "data": [self.positionAsText]
        }
        if self.FR:
            self.callback70 = lambda res: {
                "class": 6,
                "data": [self.way, self.positionAsText]
            }
예제 #30
0
    def node(self, data, tags):
        err = []
        for k in tags:
            if k in self._update_ks:
                err.append({
                    "class":
                    self._update_ks[k][1],
                    "subclass":
                    stablehash64(u"%s|%s" % (self._update_ks, k)),
                    "text":
                    T_f(u"tag key: {0} => {1} (rule ks)", k,
                        self._update_ks[k][0])
                })
            if k in self._update_ks_vs and tags[k] in self._update_ks_vs[k]:
                err.append({
                    "class":
                    self._update_ks_vs[k][tags[k]][1],
                    "subclass":
                    stablehash64(u"%s|%s" % (self._update_ks, k)),
                    "text":
                    T_f(u"tag value: {0}={1} => {2} (rule ks_vs)", k, tags[k],
                        self._update_ks_vs[k][tags[k]][0])
                })
            if k in self._update_ks_vr:
                for v in self._update_ks_vr[k]:
                    if v.match(tags[k]):
                        err.append({
                            "class":
                            self._update_ks_vr[k][v][1],
                            "subclass":
                            stablehash64(u"%s|%s" % (v, k)),
                            "text":
                            T_f(u"tag value: {0}={1} => {2} (rule ks_vr)", k,
                                tags[k], self._update_ks_vr[k][v][0])
                        })

        for kk in tags:
            for k in self._update_kr:
                if k.match(kk):
                    err.append({
                        "class":
                        self._update_kr[k][1],
                        "subclass":
                        stablehash64(u"%s|%s" % (kk, k)),
                        "text":
                        T_f(u"tag key: {0} => {1} (rule kr)", kk,
                            self._update_kr[k][0])
                    })
            for k in self._update_kr_vs:
                if k.match(kk):
                    if tags[kk] in self._update_kr_vs[k]:
                        err.append({
                            "class":
                            self._update_kr_vs[k][tags[kk]][1],
                            "subclass":
                            stablehash64(u"%s|%s" % (kk, k)),
                            "text":
                            T_f(u"tag value: {0}={1} => {2} (rule kr_vs)", kk,
                                tags[kk], self._update_kr_vs[k][tags[kk]][0])
                        })
            for k in self._update_kr_vr:
                if k.match(kk):
                    for v in self._update_kr_vr[k]:
                        if v.match(tags[kk]):
                            err.append({
                                "class":
                                self._update_kr_vr[k][v][1],
                                "subclass":
                                stablehash64(u"%s|%s" % (kk, k)),
                                "text":
                                T_f(u"tag value: {0}={1} => {2} (rule kr_vr)",
                                    kk, tags[kk], self._update_kr_vr[k][v][0])
                            })
        return err