Esempio n. 1
0
    def test__add_ignore(self):
        self.assertEqual(OsmData._add_ignore([]), "")

        self.assertEqual(
            OsmData._add_ignore([("Foo", "Bar"), ("Baz", "Tar")]),
            " and not (./tag[@k='Foo' and @v='Bar'] and ./tag[@k='Baz' and @v='Tar'])",
        )
Esempio n. 2
0
 def test_element_contains_tag(self):
     element = self.valid001.filter_tag([("rover:id", "-133507")])[0]
     self.assertTrue(
         OsmData.element_contains_tag(element, "rover:id", "-133507"))
     self.assertTrue(OsmData.element_contains_tag(element, "test", "1"))
     self.assertFalse(
         OsmData.element_contains_tag(element, "building", "yes"))
Esempio n. 3
0
def main_lint(args):
    if args.output is None:
        args.output = args.input
    osm = OsmData(args.input, args.output, strict=False)
    if args.all:
        dup_way, dup_node = osm.lint_unique_ids()
        if len(dup_way) > 0 or len(dup_node) > 0:
            print("error")
            exit(-1)
        osm.lint_check_obstacles()
        osm.lint_cleanup_unused_nodes(args.dry_run)

    osm.save()
Esempio n. 4
0
    def convert_to_utm_poly_object(
        self,
        data,
        tag_name_space=None,
        shift_nodes=True,
        base_point=None,
        remove_duplicates=True,
    ):
        polygons_in_utm = []
        # ways which already are 'polygons'
        for way in data:
            # Collect nodes that belong to the current building.
            utm_points = self.convert_way_to_utm(way)

            if self.use_osm_id:
                element = PolyObjectWidthId(way.get("id"), utm_points)
                element.template_data.update(
                    OsmData.tags_to_dict(way, tag_name_space))
                polygons_in_utm.append(element)
            else:
                element = PolyObjectWidthId(-1, utm_points)
                element.template_data.update(
                    OsmData.tags_to_dict(way, tag_name_space))
                polygons_in_utm.append(element)

        utm_topography_elements = polygons_in_utm
        if base_point is None:
            self.base_point_utm = OsmConverter.find_new_base_point(
                polygons_in_utm)
        else:
            self.base_point_utm = list(base_point)

        if shift_nodes:
            for b in utm_topography_elements:
                b.shift_points(self.base_point_utm)

        if remove_duplicates:
            ret = []
            id_set = set()
            for element in utm_topography_elements:
                if element.id not in id_set:
                    ret.append(element)
                    id_set.add(element.id)
            utm_topography_elements = ret

        return utm_topography_elements, self.base_point_utm, self.osm.utm_zone_string
Esempio n. 5
0
def main_walls(args):
    if args.output is None:
        args.output = args.input

    osm = OsmData(args.input, args.output)
    for way_id in args.way_list:
        utm_points = osm.nodes_to_utm(osm.way_node_refs(way_id))
        osm.way_create_from_polyline(utm_points, dist=args.dist)

    osm.save()
Esempio n. 6
0
    def __init__(self, osm_file, use_osm_id, wall_thickness=0.25):
        self.osm_file = osm_file
        self.osm = OsmData(self.osm_file)
        self.use_osm_id = use_osm_id
        self.wall_thickness = wall_thickness
        self.aoi = None

        # self.xml_tree = etree.parse(self.osm_file)
        # self.node_dict = OsmConverter.extract_latitude_and_longitude_for_each_xml_node(self.xml_tree)
        # self.simple_buildings = OsmConverter.filter_for_buildings(self.xml_tree)
        # self.complex_buildings = OsmConverter.filter_for_buildings_in_relations(self.xml_tree)

        self.base_point_lon_lat = self.osm.base_point
        self.base_point_utm = [0.0, 0.0]
        self.zone_map = {}
        self.obstacles: List[Element] = []
        self.sources: List[Element] = []
        self.targets: List[Element] = []
        self.measurement: List[Element] = []
Esempio n. 7
0
    def test_extract_latitude_and_longitude_for_each_xml_node(self):
        xml_tree = etree.parse(TEST_DATA_LON_LAT)
        osm_xml = OsmData(TEST_DATA_LON_LAT)
        nodes_dictionary_with_lat_and_lon = osm_xml.node_latlon_lookup

        self.assertTrue(nodes_dictionary_with_lat_and_lon.get("1")[0] == "1.1")
        self.assertTrue(nodes_dictionary_with_lat_and_lon.get("1")[1] == "1.2")
        self.assertTrue(nodes_dictionary_with_lat_and_lon.get("2")[0] == "2.1")
        self.assertTrue(nodes_dictionary_with_lat_and_lon.get("2")[1] == "2.2")
        self.assertTrue(nodes_dictionary_with_lat_and_lon.get("3")[0] == "3.1")
        self.assertTrue(nodes_dictionary_with_lat_and_lon.get("3")[1] == "3.2")
Esempio n. 8
0
 def test_tags_to_dict(self):
     element = self.valid001.filter_tag([("rover:id", "-133507")])[0]
     self.assertDictEqual(OsmData.tags_to_dict(element), {})
     self.assertDictEqual(
         OsmData.tags_to_dict(element, ""),
         {
             "test": "1",
             "rover:id": "-133507",
             "rover:obstacle": "yes",
             "rover:obstacle:type": "polygon",
         },
     )
     self.assertDictEqual(
         OsmData.tags_to_dict(element, "rover:"),
         {
             "id": "-133507",
             "obstacle": "yes",
             "obstacle:type": "polygon"
         },
     )
Esempio n. 9
0
    def test_tag_update_or_create(self):
        element = self.valid001.filter_tag([("rover:id", "-133507")])[0]
        self.assertEqual(element.xpath("./tag[@k='test']/@v")[0], "1")
        OsmData.tag_update_or_create(element, "test", "3")
        self.assertEqual(element.xpath("./tag[@k='test']/@v")[0], "3")

        OsmData.tag_update_or_create(element, "test5", 42)
        self.assertEqual(element.xpath("./tag[@k='test5']/@v")[0], "42")

        OsmData.tag_update_or_create(element, "test5", 69)
        self.assertEqual(element.xpath("./tag[@k='test5']/@v")[0], "69")

        self.assertEqual(len(element.xpath("./tag[@k='test2']")), 0)
        OsmData.tag_update_or_create(element, "test2", "Foo")
        self.assertEqual(element.xpath("./tag[@k='test2']/@v")[0], "Foo")
Esempio n. 10
0
def main_convex_hull(args):
    if args.output is None:
        args.output = args.input

    osm = OsmData(args.input, args.output)
    for way_ids in args.way_list:
        osm.create_convex_hull(way_ids)

    osm.save()
Esempio n. 11
0
    def test__tag_matcher(self):
        self.assertTupleEqual(
            OsmData._tag_matcher([("Foo", "Bar"), ("Bazz", )], [],
                                 truth_operator=("and", "and")),
            ("./tag[@k='Bazz'] and ./tag[@k='Foo' and @v='Bar']", ""),
        )
        self.assertTupleEqual(
            OsmData._tag_matcher([("Foo", "Bar"), ("Bazz", )], [],
                                 truth_operator=("or", "and")),
            ("./tag[@k='Bazz'] or ./tag[@k='Foo' and @v='Bar']", ""),
        )

        self.assertTupleEqual(
            OsmData._tag_matcher([], [("Foo", "Bar"), ("Bazz", )],
                                 truth_operator=("and", "and")),
            ("", "./tag[@k='Bazz'] and ./tag[@k='Foo' and @v='Bar']"),
        )
        self.assertTupleEqual(
            OsmData._tag_matcher([], [("Foo", "Bar"), ("Bazz", )],
                                 truth_operator=("and", "or")),
            ("", "./tag[@k='Bazz'] or ./tag[@k='Foo' and @v='Bar']"),
        )

        self.assertTupleEqual(
            OsmData._tag_matcher([], [], truth_operator=("and", "or")),
            ("", ""))

        self.assertTupleEqual(
            OsmData._tag_matcher(
                [("A", "A"), ("B", "B")],
                [("C", "C"), ("D", "D")],
                truth_operator=("or", "and"),
            ),
            (
                "./tag[@k='A' and @v='A'] or ./tag[@k='B' and @v='B']",
                "./tag[@k='C' and @v='C'] and ./tag[@k='D' and @v='D']",
            ),
        )
Esempio n. 12
0
class OsmConverter:
    """
    Create Vadere Topography elements based on open street map xml input files.
    """
    def __init__(self, osm_file, use_osm_id, wall_thickness=0.25):
        self.osm_file = osm_file
        self.osm = OsmData(self.osm_file)
        self.use_osm_id = use_osm_id
        self.wall_thickness = wall_thickness
        self.aoi = None

        # self.xml_tree = etree.parse(self.osm_file)
        # self.node_dict = OsmConverter.extract_latitude_and_longitude_for_each_xml_node(self.xml_tree)
        # self.simple_buildings = OsmConverter.filter_for_buildings(self.xml_tree)
        # self.complex_buildings = OsmConverter.filter_for_buildings_in_relations(self.xml_tree)

        self.base_point_lon_lat = self.osm.base_point
        self.base_point_utm = [0.0, 0.0]
        self.zone_map = {}
        self.obstacles: List[Element] = []
        self.sources: List[Element] = []
        self.targets: List[Element] = []
        self.measurement: List[Element] = []

    def filter(self):
        for f in self.osm.obstacle_selectors:
            self.obstacles.extend(f())

        for f in self.osm.target_selectors:
            self.targets.extend(f())

        for f in self.osm.source_selectors:
            self.sources.extend(f())

        for f in self.osm.measurement_selectors:
            self.measurement.extend(f())

    def filter_area_of_interest(self):
        aoi = self.osm.get_area_of_intrest()

        if aoi:
            self.aoi = aoi
            self.obstacles = [
                o for o in self.obstacles
                if self.osm.contained_in_area_of_intrest(aoi, o)
            ]
            self.targets = [
                o for o in self.targets
                if self.osm.contained_in_area_of_intrest(aoi, o)
            ]
            self.sources = [
                o for o in self.sources
                if self.osm.contained_in_area_of_intrest(aoi, o)
            ]
            self.measurement = [
                o for o in self.measurement
                if self.osm.contained_in_area_of_intrest(aoi, o)
            ]

    def get_base_point_from_aoi(self):
        assert self.aoi is not None
        lonlat = (min(self.aoi[0]), min(self.aoi[1]))
        return self.osm.lookup.convert_latlon_to_utm(lonlat)

    def find_width_height_from_aoi(self, base):
        assert self.aoi is not None

        max_point = self.osm.lookup.convert_latlon_to_utm(
            (max(self.aoi[0]), max(self.aoi[1])))

        shift_in_x = -base[0]
        shift_in_y = -base[1]
        max_point_shifted = (max_point[0] + shift_in_x,
                             max_point[1] + shift_in_y)
        return max_point_shifted  # width and height

    @classmethod
    def from_args(cls, arg):
        c = cls(arg.input, arg.use_osm_id)
        c.filter()
        if arg.use_aoi:
            c.filter_area_of_interest()
        return c

    @staticmethod
    def get_git_hash():
        """
        :return: name of file with commit hash of current version. If the file contains uncommitted changes a
        'dirty' commit hash is returned.
        """
        try:
            repo_base = "../../.."
            repo = Repo(repo_base)
            current_file = os.path.relpath(__file__, repo_base)
            osm_helper_file = os.path.relpath(osm_helper.__file__, repo_base)
            file_name = os.path.basename(current_file)
            if current_file in repo.untracked_files:
                print(
                    f"{__file__} is not tracked by git. This is not good! You will not be able to reproduce the output"
                    f" later on.")
                return "not-tracked"

            if repo.is_dirty(path=current_file) or repo.is_dirty(
                    path=osm_helper_file):
                print(
                    f"warning: Converted output is based on a not committed script. Reproducing the result might not"
                    f" work. Commit changes first and rerun.")
                return f"{file_name}-{repo.commit().hexsha}-dirty"
            else:
                return f"{file_name}-{repo.commit().hexsha}"

        except InvalidGitRepositoryError:
            print(
                f"cannot find git repository at {os.path.abspath('../../..')}")
            return "no-repo-found"

    # @staticmethod
    # def xpath_k_v_tags(key_value_list: List):
    #     xpath = [OsmConverter.xpath_k_v_tag(*i) for i in key_value_list]
    #     return f"({' and '.join(xpath)})"
    #
    # @staticmethod
    # def xpath_k_v_tag(k: str, v: str):
    #     return f"./tag[@k='{k}' and @v='{v}']"
    #
    # @staticmethod
    # def add_ignore(ignore: List):
    #     if ignore is None:
    #         return ""
    #     else:
    #         return f" and not({OsmConverter.xpath_k_v_tags(ignore)})"
    #
    # @staticmethod
    # def filter_tag(xml_tree, include: list, exclude: list = None) -> List[Element]:
    #     """
    #     creates xpath string which will return all elements contains tags with a specific key (e.g ('key2') without
    #     checking the value or in the case of ('key1', 'val2') the value will be checked. All elements in the include
    #     or
    #     exclude list are concatenated with an 'and' operator
    #     :param xml_tree:
    #     :param include: list of the form [('key1', 'val1'), ('key1', 'val2'), ('key2'), ...]
    #     :param exclude: list of the form [('key1', 'val1'), ('key1', 'val2'), ('key2'), ...]
    #     :return:
    #     """
    #     include_tag_key = [element[0] for element in include if len(element) == 1]
    #     inc_1 = [f"./tag[@k='{k}']" for k in include_tag_key]
    #     include_tag_key_value_pair = [element for element in include if len(element) == 2]
    #     inc_2 = [f"./tag[@k='{e[0]}' and @v='{e[1]}']" for e in include_tag_key_value_pair]
    #
    #     if exclude is not None:
    #         exclude_tag_key = [element for element in exclude if len(element) == 1]
    #         exc_1 = [f"./tag[@k='{k}']" for k in exclude_tag_key]
    #         exclude_tag_key_value_pair = [element for element in exclude if len(element) == 2]
    #         exc_2 = [f"./tag[@k='{e[0]}' and @v='{e[1]}']" for e in exclude_tag_key_value_pair]
    #         exclude_xpath = f"and not ({' and '.join(exc_1 +  exc_2)})"
    #     else:
    #         exclude_xpath = ""
    #
    #     xpath = f"/osm/way[({' and '.join(inc_1 + inc_2)}) {exclude_xpath} ]"
    #     print(xpath)
    #     return xml_tree.xpath(xpath)
    #
    # @staticmethod
    # def filter_for_buildings(xml_tree, ignore: list = None) -> List[Element]:
    #     xpath = f"/osm/way[./tag/@k='building' {OsmConverter.add_ignore(ignore)}]"
    #     return xml_tree.xpath(xpath)
    #
    # @staticmethod
    # def filter_for_barrier(xml_tree, ignore: list = None) -> List[Element]:
    #     xpath = f"/osm/way[./tag/@k='barrier' {OsmConverter.add_ignore(ignore)}]"
    #     return xml_tree.xpath(xpath)
    #
    # @staticmethod
    # def filter_for_buildings_in_relations(xml_tree, ignore: list = None):
    #     # Note: A relation describes a shape with "cutting holes".
    #
    #     # Select "relation" nodes with a child node "tag" annotated with attribute "k='building'".
    #     xpath = f"/osm/relation[./tag/@k='building' {OsmConverter.add_ignore(ignore)}]"
    #     buildings = xml_tree.xpath(xpath)
    #
    #     # We only want the shapes and only the outer parts. role='inner' is for "cutting holes" in the shape.
    #     members_in_the_relations = [building.xpath("./member[./@type='way' and ./@role='outer']") for building in
    #                                 buildings]
    #     way_ids = []
    #     for element in members_in_the_relations:
    #         for way in element:
    #             way_ids.append(way.get("ref"))
    #     ways = xml_tree.xpath("/osm/way")
    #     ways_as_dict_with_id_key = {way.get("id"): way for way in ways}
    #     buildings_from_relations = [ways_as_dict_with_id_key[way_id] for way_id in way_ids]
    #     return buildings_from_relations

    @staticmethod
    def find_new_base_point(buildings: List[PolyObjectWidthId]):
        """
        The base point will be the smallest coordinate taken for all buildings of the current map boundary.
        This point will most likely not correspond with the Base Point which is the lower left corner of the
        map bound chosen at export time of the open street map xml file.
        :param buildings:
        :return: smallest coordinate (in utm)
        """
        # "buildings_cartesian" is a list of lists. The inner list contains the (x,y) tuples.
        # search for the lowest x- and y-coordinates within the points
        all_points = [
            point for building in buildings for point in building.points
        ]

        tuple_with_min_x = min(all_points, key=lambda point: point[0])
        tuple_with_min_y = min(all_points, key=lambda point: point[1])

        return tuple_with_min_x[0], tuple_with_min_y[1]

    @staticmethod
    def find_width_and_height(buildings: List[PolyObjectWidthId]):
        """
        :param buildings:
        :return: utm coordinates used to bound all buildings
        """
        width = 0
        height = 0
        for cartesian_points in buildings:
            for point in cartesian_points.points:
                width = max(width, point[0])
                height = max(height, point[1])
        return math.ceil(width), math.ceil(height)

    @staticmethod
    def to_vadere_topography(
        width,
        height,
        translation,
        zone_string,
        obstacles=None,
        sources=None,
        targets=None,
        measurement_areas=None,
    ):
        """

        :param measurement_areas:
        :param targets:
        :param sources:
        :param obstacles: list of Vadere obstacles (json string)
        :param width: of the topography bound
        :param height: of the topography bound
        :param translation: offset used to translate the topography to (0,0). This is needed to reverse the translation
               if needed
        :param zone_string: epgs or UTM string encoding the coordinates system
        :return:
        """
        with open("templates/vadere_topography_template.txt", "r") as f:
            vadere_topography_input = f.read()  # .replace('\n', '')

        epsg_description = f"OpenStreetMap export {OsmConverter.get_git_hash()}"
        vadere_topography_output = Template(
            vadere_topography_input).substitute(
                width=width,
                height=height,
                obstacles=obstacles,
                translate_x=translation[0],
                translate_y=translation[1],
                epsg=zone_string,
                epsg_description=epsg_description,
                sources=sources,
                targets=targets,
                measurement_areas=measurement_areas,
            )
        return vadere_topography_output

    @staticmethod
    def apply_template(
        poly_object: PolyObjectWidthId,
        template_string,
        default_template_data=None,
        indent_level=3,
    ):
        """
        :param indent_level:
        :param default_template_data:
        :param template_string:
        :param poly_object:
        :return: Vadere json representation of an obstacle
        """
        vadere_point_string = f"{'    ' * indent_level}" + '  { "x" : $x, "y" : $y }'

        obstacle_string_template = Template(template_string)
        point_string_template = Template(vadere_point_string)

        points_as_string = [
            point_string_template.substitute(x=x, y=y)
            for x, y in poly_object.points
        ]
        points_as_string_concatenated = ",\n".join(points_as_string)

        template_data = {}
        if default_template_data is not None:
            template_data.update(default_template_data)
        template_data.update(poly_object.template_data)
        template_data.setdefault("points", points_as_string_concatenated)

        vadere_obstacle_as_string = obstacle_string_template.substitute(
            template_data)

        return vadere_obstacle_as_string

    @staticmethod
    def to_vadere_obstacles(buildings: List[PolyObjectWidthId]):
        list_of_vadere_obstacles_as_strings = []
        for building in buildings:
            vadere_obstacles_as_strings = OsmConverter.apply_template(
                building,
                template_string=vadere_simple_topography_element_string)
            list_of_vadere_obstacles_as_strings.append(
                vadere_obstacles_as_strings)
        return list_of_vadere_obstacles_as_strings

    @staticmethod
    def to_vadere_measurement_area(buildings: List[PolyObjectWidthId]):
        list_of_vadere_measurement_area_as_strings = []
        for building in buildings:
            vadere_measurement_area_as_strings = OsmConverter.apply_template(
                building,
                template_string=vadere_simple_topography_element_string)
            list_of_vadere_measurement_area_as_strings.append(
                vadere_measurement_area_as_strings)
        return list_of_vadere_measurement_area_as_strings

    @staticmethod
    def to_vadere_sources(sources: List[PolyObjectWidthId]):
        list_of_vadere_sources_as_strings = []
        with open("templates/vadere_source_template.txt", "r") as f:
            vadere_source_template = f.read()
        for source in sources:
            source_string = OsmConverter.apply_template(
                source,
                template_string=vadere_source_template,
                default_template_data=source_defaults,
            )
            list_of_vadere_sources_as_strings.append(source_string)
        return list_of_vadere_sources_as_strings

    @staticmethod
    def to_vadere_targets(targets: List[PolyObjectWidthId]):
        list_of_vadere_target_as_strings = []
        with open("templates/vadere_target_template.txt", "r") as f:
            vadere_target_template = f.read()
        for target in targets:
            target_string = OsmConverter.apply_template(
                target,
                template_string=vadere_target_template,
                default_template_data=target_defaults,
            )
            list_of_vadere_target_as_strings.append(target_string)
        return list_of_vadere_target_as_strings

    @staticmethod
    def print_output(output_file, output):
        if output_file is None:
            print(output)
        else:
            with open(output_file, "w") as text_file:
                print(output, file=text_file)

    def convert_way_to_utm(self, way: Element):
        way_id = way.get("id")
        node_ids = self.osm.way_node_refs(way_id)
        converted_way_points = self.osm.nodes_to_utm(node_ids)

        # if way is closed remove last element (it's the same as the first)
        if self.osm.way_is_closed(way_id):
            converted_way_points = converted_way_points[:-1]

        return converted_way_points

    def print_xml_parsing_statistics(self):
        print(f"File: {self.osm_file}")
        print(f"  Nodes: {len(self.osm.nodes)}")
        print(f"  Polygons: {len(self.obstacles)}")
        print(
            f"  Base point: {self.base_point_lon_lat} (not to be confused with utm based point which!"
        )

    def convert_to_utm_poly_object(
        self,
        data,
        tag_name_space=None,
        shift_nodes=True,
        base_point=None,
        remove_duplicates=True,
    ):
        polygons_in_utm = []
        # ways which already are 'polygons'
        for way in data:
            # Collect nodes that belong to the current building.
            utm_points = self.convert_way_to_utm(way)

            if self.use_osm_id:
                element = PolyObjectWidthId(way.get("id"), utm_points)
                element.template_data.update(
                    OsmData.tags_to_dict(way, tag_name_space))
                polygons_in_utm.append(element)
            else:
                element = PolyObjectWidthId(-1, utm_points)
                element.template_data.update(
                    OsmData.tags_to_dict(way, tag_name_space))
                polygons_in_utm.append(element)

        utm_topography_elements = polygons_in_utm
        if base_point is None:
            self.base_point_utm = OsmConverter.find_new_base_point(
                polygons_in_utm)
        else:
            self.base_point_utm = list(base_point)

        if shift_nodes:
            for b in utm_topography_elements:
                b.shift_points(self.base_point_utm)

        if remove_duplicates:
            ret = []
            id_set = set()
            for element in utm_topography_elements:
                if element.id not in id_set:
                    ret.append(element)
                    id_set.add(element.id)
            utm_topography_elements = ret

        return utm_topography_elements, self.base_point_utm, self.osm.utm_zone_string
Esempio n. 13
0
 def test__xpath_k_v_tag(self):
     self.assertEqual(OsmData._xpath_k_v_tag("Foo", "Bar"),
                      "./tag[@k='Foo' and @v='Bar']")
Esempio n. 14
0
 def test__xpath_k_v_tags(self):
     self.assertEqual(
         OsmData._xpath_k_v_tags([("Foo", "Bar"), ("Baz", "Tar")]),
         "(./tag[@k='Foo' and @v='Bar'] and ./tag[@k='Baz' and @v='Tar'])",
     )
Esempio n. 15
0
 def setUp(self):
     self.valid001 = OsmData(input_file=os.path.join(
         os.path.dirname(__file__), "./valid001.osm"))
     self.valid002 = OsmData(input_file=os.path.join(
         os.path.dirname(__file__), "./valid002.osm"))
     self.valid003 = OsmData(input_file=os.path.join(
         os.path.dirname(__file__), "./valid003.osm"))
     self.valid004 = OsmData(input_file=os.path.join(
         os.path.dirname(__file__), "./valid004.osm"))
     self.invalid001 = OsmData(input_file=os.path.join(
         os.path.dirname(__file__), "./invalid001.osm"))
     self.invalid002 = OsmData(
         input_file=os.path.join(os.path.dirname(__file__),
                                 "./invalid002.osm"),
         strict=False,
     )
     self.invalid003 = OsmData(
         input_file=os.path.join(os.path.dirname(__file__),
                                 "./invalid003.osm"),
         strict=False,
     )
     self.invalid004 = OsmData(
         input_file=os.path.join(os.path.dirname(__file__),
                                 "./invalid004.osm"),
         strict=False,
     )
     self.invalid005 = OsmData(
         input_file=os.path.join(os.path.dirname(__file__),
                                 "./invalid005.osm"),
         strict=False,
     )
Esempio n. 16
0
class TestOsmData(unittest.TestCase):
    def setUp(self):
        self.valid001 = OsmData(input_file=os.path.join(
            os.path.dirname(__file__), "./valid001.osm"))
        self.valid002 = OsmData(input_file=os.path.join(
            os.path.dirname(__file__), "./valid002.osm"))
        self.valid003 = OsmData(input_file=os.path.join(
            os.path.dirname(__file__), "./valid003.osm"))
        self.valid004 = OsmData(input_file=os.path.join(
            os.path.dirname(__file__), "./valid004.osm"))
        self.invalid001 = OsmData(input_file=os.path.join(
            os.path.dirname(__file__), "./invalid001.osm"))
        self.invalid002 = OsmData(
            input_file=os.path.join(os.path.dirname(__file__),
                                    "./invalid002.osm"),
            strict=False,
        )
        self.invalid003 = OsmData(
            input_file=os.path.join(os.path.dirname(__file__),
                                    "./invalid003.osm"),
            strict=False,
        )
        self.invalid004 = OsmData(
            input_file=os.path.join(os.path.dirname(__file__),
                                    "./invalid004.osm"),
            strict=False,
        )
        self.invalid005 = OsmData(
            input_file=os.path.join(os.path.dirname(__file__),
                                    "./invalid005.osm"),
            strict=False,
        )

    def test_base_point(self):
        self.assertTupleEqual(self.valid001.base_point, (48.16077, 11.58451))

    def test_nodes(self):
        nodes = self.valid001.nodes
        self.assertEqual(len(nodes), 6)
        self.assertEqual(nodes[0].get("id"), "-127405")
        self.assertEqual(nodes[1].get("id"), "-127407")
        self.assertEqual(nodes[2].get("id"), "-127425")
        self.assertEqual(nodes[3].get("id"), "1318876746")
        self.assertEqual(nodes[4].get("id"), "1324414637")
        self.assertEqual(nodes[5].get("id"), "1324414707")

    def test_utm_zone(self):
        self.assertTupleEqual(self.valid001.utm_zone, (32, "U"))

    def test_utm_zone_string(self):
        self.assertEqual(self.valid001.utm_zone_string, "UTM Zone 32U")

    def test__xpath_k_v_tags(self):
        self.assertEqual(
            OsmData._xpath_k_v_tags([("Foo", "Bar"), ("Baz", "Tar")]),
            "(./tag[@k='Foo' and @v='Bar'] and ./tag[@k='Baz' and @v='Tar'])",
        )

    def test__xpath_k_v_tag(self):
        self.assertEqual(OsmData._xpath_k_v_tag("Foo", "Bar"),
                         "./tag[@k='Foo' and @v='Bar']")

    def test__add_ignore(self):
        self.assertEqual(OsmData._add_ignore([]), "")

        self.assertEqual(
            OsmData._add_ignore([("Foo", "Bar"), ("Baz", "Tar")]),
            " and not (./tag[@k='Foo' and @v='Bar'] and ./tag[@k='Baz' and @v='Tar'])",
        )

    def test__tag_matcher(self):
        self.assertTupleEqual(
            OsmData._tag_matcher([("Foo", "Bar"), ("Bazz", )], [],
                                 truth_operator=("and", "and")),
            ("./tag[@k='Bazz'] and ./tag[@k='Foo' and @v='Bar']", ""),
        )
        self.assertTupleEqual(
            OsmData._tag_matcher([("Foo", "Bar"), ("Bazz", )], [],
                                 truth_operator=("or", "and")),
            ("./tag[@k='Bazz'] or ./tag[@k='Foo' and @v='Bar']", ""),
        )

        self.assertTupleEqual(
            OsmData._tag_matcher([], [("Foo", "Bar"), ("Bazz", )],
                                 truth_operator=("and", "and")),
            ("", "./tag[@k='Bazz'] and ./tag[@k='Foo' and @v='Bar']"),
        )
        self.assertTupleEqual(
            OsmData._tag_matcher([], [("Foo", "Bar"), ("Bazz", )],
                                 truth_operator=("and", "or")),
            ("", "./tag[@k='Bazz'] or ./tag[@k='Foo' and @v='Bar']"),
        )

        self.assertTupleEqual(
            OsmData._tag_matcher([], [], truth_operator=("and", "or")),
            ("", ""))

        self.assertTupleEqual(
            OsmData._tag_matcher(
                [("A", "A"), ("B", "B")],
                [("C", "C"), ("D", "D")],
                truth_operator=("or", "and"),
            ),
            (
                "./tag[@k='A' and @v='A'] or ./tag[@k='B' and @v='B']",
                "./tag[@k='C' and @v='C'] and ./tag[@k='D' and @v='D']",
            ),
        )

    def test_filter_tag(self):
        ret = self.valid001.filter_tag(include=[("Foo", "Bar")])
        self.assertEqual(len(ret), 0)

        ret = self.valid001.filter_tag(include=[("test", "1")])
        self.assertEqual(len(ret), 1)

        ret = self.valid001.filter_tag(include=[("test", )])
        self.assertEqual(len(ret), 2)

        ret = self.valid001.filter_tag(include=[("test", )],
                                       exclude=[("rover:id", "-133507")])
        self.assertEqual(len(ret), 1)

        ret = self.valid001.filter_tag(include=[("test", )],
                                       exclude=[("rover:id", ),
                                                ("building", "yes")])
        self.assertEqual(len(ret), 2)

        ret = self.valid001.filter_tag(
            include=[("test", )],
            exclude=[("rover:id", ), ("building", "yes")],
            truth_operator=("and", "or"),
        )
        self.assertEqual(len(ret), 0)

        ret = self.valid001.filter_tag(exclude=[("rover:id", ),
                                                ("building", "yes")],
                                       truth_operator=("and", "or"))
        self.assertEqual(len(ret), 0)

        ret = self.valid001.filter_tag(include=[("rover:id", ),
                                                ("building", "yes")],
                                       truth_operator=("and", "and"))
        self.assertEqual(len(ret), 0)

        ret = self.valid001.filter_tag(include=[("rover:id", ),
                                                ("building", "yes")],
                                       truth_operator=("or", "and"))
        self.assertEqual(len(ret), 2)

        ret = self.valid001.filter_tag(None, None)
        self.assertEqual(len(ret), 2)

    def test_filter_for_buildings(self):
        ret = self.valid001.filter_for_buildings()
        self.assertEqual(len(ret), 1)
        self.assertEqual(ret[0].get("id"), "173168628")

    def test_filter_for_barrier(self):
        pass

    def test_filter_for_buildings_in_relations(self):
        pass

    def test_tags_to_dict(self):
        element = self.valid001.filter_tag([("rover:id", "-133507")])[0]
        self.assertDictEqual(OsmData.tags_to_dict(element), {})
        self.assertDictEqual(
            OsmData.tags_to_dict(element, ""),
            {
                "test": "1",
                "rover:id": "-133507",
                "rover:obstacle": "yes",
                "rover:obstacle:type": "polygon",
            },
        )
        self.assertDictEqual(
            OsmData.tags_to_dict(element, "rover:"),
            {
                "id": "-133507",
                "obstacle": "yes",
                "obstacle:type": "polygon"
            },
        )

    def test_utm_lookup(self):
        self.assertNotEqual(self.valid001.utm_lookup(1318876746), ())
        self.assertNotEqual(self.valid001.utm_lookup(-127425), ())
        self.assertRaises(OsmLookupError, self.valid001.utm_lookup, 666)

    def test_latlon_lookup(self):
        self.assertEqual(self.valid001.latlon_lookup(1318876746),
                         (48.1608727, 11.5891088))
        self.assertEqual(self.valid001.latlon_lookup(-127425),
                         (48.16255438099, 11.58692204178))
        self.assertRaises(OsmLookupError, self.valid001.latlon_lookup, 666)

    def test_tag_update_or_create(self):
        element = self.valid001.filter_tag([("rover:id", "-133507")])[0]
        self.assertEqual(element.xpath("./tag[@k='test']/@v")[0], "1")
        OsmData.tag_update_or_create(element, "test", "3")
        self.assertEqual(element.xpath("./tag[@k='test']/@v")[0], "3")

        OsmData.tag_update_or_create(element, "test5", 42)
        self.assertEqual(element.xpath("./tag[@k='test5']/@v")[0], "42")

        OsmData.tag_update_or_create(element, "test5", 69)
        self.assertEqual(element.xpath("./tag[@k='test5']/@v")[0], "69")

        self.assertEqual(len(element.xpath("./tag[@k='test2']")), 0)
        OsmData.tag_update_or_create(element, "test2", "Foo")
        self.assertEqual(element.xpath("./tag[@k='test2']/@v")[0], "Foo")

    def test_element_contains_tag(self):
        element = self.valid001.filter_tag([("rover:id", "-133507")])[0]
        self.assertTrue(
            OsmData.element_contains_tag(element, "rover:id", "-133507"))
        self.assertTrue(OsmData.element_contains_tag(element, "test", "1"))
        self.assertFalse(
            OsmData.element_contains_tag(element, "building", "yes"))

    def test_element(self):
        element = self.valid001.element("/osm/bounds")
        self.assertEqual(element.get("origin"),
                         "CGImap 0.7.5 (31203 thorn-02.openstreetmap.org)")
        self.assertRaises(OsmLookupError, self.valid001.element, "/foo/bar")
        self.assertRaises(OsmLookupError, self.valid001.element, "/osm/way")

    def test_elements(self):
        elements = self.valid001.elements("/osm/way")
        self.assertEqual(len(elements), 2)
        elements = self.valid001.elements("/osm/bounds")
        self.assertEqual(len(elements), 1)
        self.assertRaises(OsmLookupError, self.valid001.elements, "/foo/bar")

    def test_node(self):
        node = self.valid001.node(1318876746)
        self.assertEqual(node.get("user"), "KonB")
        self.assertRaises(OsmLookupError,
                          self.valid001.node,
                          1318876746,
                          rover_id=True)

        self.assertRaises(OsmLookupError, self.valid001.node, 666)
        node = self.valid001.node(333, rover_id=True)
        self.assertEqual(node.get("id"), "-127405")

    def test_node_exists(self):
        self.assertTrue(self.valid001.node_exists(1318876746))
        self.assertFalse(self.valid001.node_exists(1318876746, rover_id=True))
        self.assertTrue(self.valid001.node_exists(-127407))
        self.assertFalse(self.valid001.node_exists(-666))
        self.assertTrue(self.valid001.node(333, rover_id=True))
        self.assertTrue(self.valid001.node(-127405, rover_id=False))

    def test_node_add(self):

        self.assertEqual(len(self.valid001.nodes), 6)
        self.assertEqual(
            self.valid001.node_add((48.16256981067, 11.5868239213)), -127405)
        self.assertEqual(len(self.valid001.nodes), 6)

        self.assertRaises(RuntimeError, self.valid001.node_add, (45.66, 11.66))
        self.assertEqual(len(self.valid001.nodes), 6)

        node_id = self.valid001.node_add((48.16, 11.59))
        self.assertEqual(self.valid001.latlon_lookup(node_id), (48.16, 11.59))
        self.assertEqual(
            self.valid001.lookup.latlon_to_node.get((48.16, 11.59)), node_id)
        self.assertEqual(len(self.valid001.nodes), 7)

    def test_node_remove(self):
        node_id = 1318876746
        self.assertTrue(self.valid001.node_exists(node_id))
        self.valid001.utm_lookup(node_id)
        self.valid001.latlon_lookup(node_id)

        self.valid001.node_remove(node_id)
        self.assertFalse(self.valid001.node_exists(node_id))
        self.assertRaises(OsmLookupError, self.valid001.utm_lookup, node_id)
        self.assertRaises(OsmLookupError, self.valid001.latlon_lookup, node_id)

    def test_nodes_find_with_tags(self):
        self.assertEqual(
            len(self.valid001.nodes_find_with_tags([("rover:id", )])), 1)
        self.assertEqual(
            len(self.valid001.nodes_find_with_tags(exc_tag=[("rover:id", )])),
            5)

    def test_node_add_tag(self):
        node_id = -127405
        node = self.valid001.node(node_id)
        self.assertListEqual(node.xpath("./tag[@k='foo']/@v"), [])
        self.assertListEqual(
            self.valid001.nodes_find_with_tags([("foo", "bar")]), [])
        self.valid001.node_add_tag(node_id, "foo", "bar")
        self.assertEqual(node.xpath("./tag[@k='foo']/@v"), ["bar"])

    def test_nodes_not_used(self):
        self.assertSetEqual(
            self.valid001.nodes_not_used([-127405, 1318876746, 1324414637]),
            set())
        self.assertSetEqual(
            self.invalid001.nodes_not_used(
                [-127433, -127405, 1318876746, 1324414637]),
            {-127433},
        )

    def test_nodes_to_latlon(self):
        self.assertListEqual(
            self.valid001.nodes_to_latlon([-127405, 1318876746]),
            [(48.16256981067, 11.5868239213), (48.1608727, 11.5891088)],
        )

    def test_nodes_to_utm(self):
        self.assertListEqual(
            self.valid001.nodes_to_utm([-127405, 1318876746]),
            [
                (692351.2680216449, 5337605.595388093),
                (692527.5088385276, 5337422.705304472),
            ],
        )

    def test_way(self):
        self.assertEqual(self.valid001.way(173168628).get("id"), "173168628")
        self.assertRaises(OsmLookupError, self.valid001.way, 555)

        self.assertRaises(OsmLookupError,
                          self.valid001.way,
                          555,
                          rover_id=True)
        self.assertEqual(
            self.valid001.way(-133507, rover_id=True).get("id"), "-999069167")

    def test_way_exists(self):
        self.assertTrue(self.valid001.way_exists(173168628))
        self.assertFalse(self.valid001.way_exists(173168628, rover_id=True))

        self.assertFalse(self.valid001.way_exists(-133507, rover_id=False))
        self.assertTrue(self.valid001.way_exists(-133507, rover_id=True))

    def test_way_add_1(self):
        way = Way.create_with_refs(3, [-127405, 1318876746, 1324414637])
        self.valid001.way_add(way)
        self.assertTrue(self.valid001.way_exists(3))

    def test_way_add_2(self):
        # duplicated way id
        way = Way.create_with_refs(173168628,
                                   [-127405, 1318876746, 1324414637])
        self.assertRaises(OsmLookupError, self.valid001.way_add, way)

    def test_way_add_3(self):
        # missing reference
        way = Way.create_with_refs(3, [6, 1318876746, 1324414637])
        self.assertRaises(OsmLookupError, self.valid001.way_add, way)

    def test_way_create_from_polyline(self):
        utm_coords = self.valid002.lookup.node_to_utm.values()
        line = self.valid002.way(-999069173)
        self.assertEqual(line.xpath("./tag[@k='rover:id']/@v"), ["-133513"])
        self.assertEqual(line.xpath("./tag[@k='rover:obstacle:type']/@v"),
                         ["line"])

        new_way, _, _ = self.valid002.way_create_from_polyline(utm_coords)
        self.assertTrue(self.valid002.way_exists(new_way.id))
        self.assertEqual(len(new_way.nds), 9)  # start == end

        self.assertEqual(len(self.valid002.nodes), 4 + 8)  # only

    def test_way_is_closed(self):
        self.assertTrue(self.valid001.way_is_closed(173168628))
        self.assertFalse(self.valid002.way_is_closed(-999069173))

    def test_way_node_refs(self):
        self.assertListEqual(
            self.valid001.way_node_refs(-999069167),
            [-127405, -127407, -127425, -127405],
        )
        self.assertRaises(OsmLookupError, self.valid001.way_node_refs, 666)

    def test_way_remove(self):
        self.assertEqual(len(self.valid001.nodes), 6)
        self.assertEqual(len(self.valid001.ways), 2)
        self.valid001.way_remove(-999069167)
        self.assertEqual(len(self.valid001.nodes), 3)
        self.assertEqual(len(self.valid001.ways), 1)

        self.assertFalse(self.valid001.way_exists(-999069167))
        self.assertRaises(OsmLookupError, self.valid001.utm_lookup, -127405)
        self.assertRaises(OsmLookupError, self.valid001.latlon_lookup, -127407)

    def test_way_add_tag(self):
        pass

    def test_ways_find_with_tags(self):
        pass

    def test_create_convex_hull(self):
        el = self.valid003.ways_find_with_tags(
            inc_tag=[("rover:obstacle:hull", ), ("rover:obstacle:ignore",
                                                 "yes")])
        self.assertEqual(len(el), 0)
        hull_id = self.valid003.create_convex_hull([39057410, 36942797])
        el = self.valid003.ways_find_with_tags(
            inc_tag=[("rover:obstacle:hull", ), ("rover:obstacle:ignore",
                                                 "yes")])
        self.assertEqual(len(el), 2)

        self.assertListEqual(
            self.valid003.way_node_refs(hull_id),
            [
                429583851,
                429583850,
                429583279,
                429583282,
                429583298,
                429583853,
                429583852,
                429583851,
            ],
        )
        self.assertEqual(
            len(
                self.valid003.ways_find_with_tags(inc_tag=[
                    ("rover:id", str(np.abs(hull_id))),
                    ("rover:obstacle", "convex-hull"),
                    ("rover:obstacle:hull", "-1"),
                ])),
            1,
        )

    def test_lint_add_ids(self):
        self.assertEqual(len(self.invalid002.xml.xpath("/osm/way[@id < 0]")),
                         1)
        self.assertEqual(
            len(
                self.invalid002.xml.xpath(
                    "/osm/way[@id < 0 and not (./tag[@k='rover:id'])]")),
            1,
        )

        self.assertEqual(len(self.invalid002.xml.xpath("/osm/node[@id < 0]")),
                         4)
        self.assertEqual(
            len(
                self.invalid002.xml.xpath(
                    "/osm/node[@id < 0 and not (./tag[@k='rover:id'])]")),
            3,
        )

        self.invalid002.lint_add_ids(dry_run=True)

        self.assertEqual(len(self.invalid002.xml.xpath("/osm/way[@id < 0]")),
                         1)
        self.assertEqual(
            len(
                self.invalid002.xml.xpath(
                    "/osm/way[@id < 0 and not (./tag[@k='rover:id'])]")),
            1,
        )

        self.assertEqual(len(self.invalid002.xml.xpath("/osm/node[@id < 0]")),
                         4)
        self.assertEqual(
            len(
                self.invalid002.xml.xpath(
                    "/osm/node[@id < 0 and not (./tag[@k='rover:id'])]")),
            3,
        )

        self.invalid002.lint_add_ids(dry_run=False)

        self.assertEqual(len(self.invalid002.xml.xpath("/osm/way[@id < 0]")),
                         1)
        self.assertEqual(
            len(
                self.invalid002.xml.xpath(
                    "/osm/way[@id < 0 and not (./tag[@k='rover:id'])]")),
            0,
        )

        self.assertEqual(len(self.invalid002.xml.xpath("/osm/node[@id < 0]")),
                         4)
        self.assertEqual(
            len(
                self.invalid002.xml.xpath(
                    "/osm/node[@id < 0 and not (./tag[@k='rover:id'])]")),
            0,
        )

    def test_lint_unique_ids(self):
        ways, nodes = self.invalid003.lint_unique_ids()
        self.assertSetEqual(ways, {"-999069167"})
        self.assertSetEqual(nodes, {"-127405"})

        ways, nodes = self.valid001.lint_unique_ids()
        self.assertSetEqual(ways, set())
        self.assertSetEqual(nodes, set())

    def test_lint_check_obstacles(self):
        invalid_polygons, multiple_used_nodes = self.invalid004.lint_check_obstacles(
        )
        self.assertEqual(len(invalid_polygons), 2)
        self.assertEqual(len(multiple_used_nodes), 2)

        invalid_polygons, multiple_used_nodes = self.valid004.lint_check_obstacles(
        )
        self.assertEqual(len(invalid_polygons), 0)
        self.assertEqual(len(multiple_used_nodes), 0)

    def test_lint_cleanup_unused_nodes(self):
        self.assertRaises(OsmLookupError,
                          self.invalid003.lint_cleanup_unused_nodes)

        self.assertEqual(len(self.invalid005.xml.xpath("/osm/node")), 7)
        self.invalid005.lint_cleanup_unused_nodes(dry_run=True)
        self.assertEqual(len(self.invalid005.xml.xpath("/osm/node")), 7)
        self.invalid005.lint_cleanup_unused_nodes(dry_run=False)
        self.assertEqual(len(self.invalid005.xml.xpath("/osm/node")), 6)