11.08., 9:00 - 11:00: Due to updates GitLab will be unavailable for some minutes between 09:00 and 11:00.

Commit b7e845e4 authored by Benedikt Kleinmeier's avatar Benedikt Kleinmeier

Merge branch 'master' into psychology

parents 7335797d 314b32e2
Pipeline #267886 passed with stages
in 140 minutes and 49 seconds
"""
Convert an OpenStreetMap XML file exported from https://www.openstreetmap.org/
to a Vadere topology (in Cartesian coordinates).
converter defined in osm_converter.py
helper and osm structures in osm_helper.py
"""
import argparse
import logging
import os
from random import sample
from typing import List
from osm_converter import OsmConverter
from osm_helper import OsmArg, OsmConfig, OsmData, PolyObjectWidthId
class HullAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if not hasattr(namespace, self.dest):
raise argparse.ArgumentError(f"namespace should contain {self.dest}")
attr = getattr(namespace, self.dest)
attr.append(values)
def random_source_target_match(
sources: List[PolyObjectWidthId], targets: List[PolyObjectWidthId]
):
target_ids = [t.template_data.get("id") for t in targets]
for source in sources:
if "targetIds" not in source.template_data:
t_ids = sample(target_ids, 2)
if source.id in t_ids:
t_ids.remove(source.id)
source.template_data.setdefault("targetIds", f"[ {t_ids[0]} ]")
def str2bool(v):
# see https://stackoverflow.com/a/43357954
if isinstance(v, bool):
return v
if v.lower() in ("yes", "true", "t", "y", "1"):
return True
elif v.lower() in ("no", "false", "f", "n", "0"):
return False
else:
raise argparse.ArgumentTypeError("Boolean value expected.")
def parse_command_line_arguments(manual_args=None):
main = argparse.ArgumentParser(
prog="OpenStreetMap (OSM) Util for (Vadere, OMNeT++)",
description="Collection of small commands to manipulate an OSM xml file to "
"preparer it for conversion to Vadere or OMNeT++ structures",
)
parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument(
"-i", "--input", dest="input", nargs="?", required=True, help="OSM input file"
)
parent_parser.add_argument(
"-o",
"--output",
dest="output",
nargs="?",
required=False,
help="OSM output. If not set the name derived from input file",
)
subparsers = main.add_subparsers(title="Commands")
#
# COMMAND 1
#
convert_parser = subparsers.add_parser(
"convert",
parents=[parent_parser],
description="Convert and OpenStreetMap file to a Vadere Topography or update an existing scenario file.",
)
convert_parser.add_argument(
"--use-osm-id",
dest="use_osm_id",
type=str2bool,
const=True,
nargs="?",
default=True,
help="Set to use osm ids for obstacles",
)
convert_parser.add_argument(
"--use-aoi",
dest="use_aoi",
type=str2bool,
const=True,
nargs="?",
default=False,
help="Set to reduce export to elements within an area of interest. "
"(way taged with vadere:area-of-intrest) ",
)
convert_parser.set_defaults(main_func=main_convert)
#
# COMMAND 2
#
hull_parser = subparsers.add_parser(
"convex-hull",
parents=[parent_parser],
description="Create a convex hull around each list of given way ids.",
)
hull_parser.set_defaults(main_func=main_convex_hull)
hull_parser.add_argument(
"-w",
"--ways",
dest="way_list",
default=[],
action=HullAction,
nargs="+",
help="list of way ids which span the convex hull",
)
#
# COMMAND 3
#
wall_parser = subparsers.add_parser(
"wall",
parents=[parent_parser],
description="Create obstacles around a line segment list.",
)
wall_parser.set_defaults(main_func=main_walls)
wall_parser.add_argument(
"-w",
"--ways",
dest="way_list",
default=[],
nargs="+",
help="list of way ids which define a line segment list",
)
wall_parser.add_argument(
"-d",
"--dist",
dest="dist",
default=0.25,
nargs="?",
help="The perpendicular distance between the line defined by the way element and the "
"parallel line used to build the polygon. The width of the polygon will thus be "
"2*dist",
)
#
# COMMAND 4
#
lint_parser = subparsers.add_parser(
"lint",
parents=[parent_parser],
description="Check for unique ids, add id-tag if missing, check for non "
"normalized obstacles",
)
lint_parser.set_defaults(main_func=main_lint)
lint_parser.add_argument(
"-a", "--all", dest="all", action="store_true", help="execute all test"
)
lint_parser.add_argument(
"--dry-run",
dest="dry_run",
action="store_true",
help="only print what would be done but do not change anything",
)
lint_parser.add_argument(
"--add-ids",
dest="add_ids",
action="store_true",
help="Ensure all manually added elements contain an id tag",
)
lint_parser.add_argument(
"--unique-ids",
dest="unique_ids",
action="store_true",
help="Check for unique ids",
)
lint_parser.add_argument(
"--check-obstacles",
dest="check_obstacles",
action="store_true",
help="check if file contains any touching obstacles",
)
#
# COMMAND 5
#
config_parser = subparsers.add_parser("use-config")
config_parser.add_argument(
"-c",
"--config",
dest="config",
nargs="?",
help="Execute commands in configuration file.",
)
config_parser.add_argument(
"-n",
"--new-config-file",
dest="new_config",
default="osm.config",
nargs="?",
help="create a default config file including all possible commands. If -c is used this"
"option is ignored",
)
config_parser.set_defaults(main_func=main_apply_config)
#
# Parse and return Namespace
#
if manual_args is not None:
cmd_args = main.parse_args(manual_args)
else:
cmd_args = main.parse_args()
return cmd_args
#
# Entry point functions.
#
def main_convert(cmd_args):
"""
Entry point
osm2vadere.pu mf.osm convert -h // for sub command specific help
osm2vadere.py mf.osm convert --output map.json
"""
if cmd_args.output is None:
dirname, basename = os.path.split(cmd_args.input)
cmd_args.output = os.path.join(dirname, f"{basename.split('.')[0]}.txt")
print(cmd_args)
converter = OsmConverter.from_args(cmd_args)
if converter.aoi:
base = converter.get_base_point_from_aoi()
(
obstacles_as_utm,
base_point_utm,
zone_string,
) = converter.convert_to_utm_poly_object(
data=converter.obstacles, base_point=base
)
else:
(
obstacles_as_utm,
base_point_utm,
zone_string,
) = converter.convert_to_utm_poly_object(data=converter.obstacles)
sources_as_utm, _, _ = converter.convert_to_utm_poly_object(
data=converter.sources,
base_point=base_point_utm,
tag_name_space="rover:source:",
)
targets_as_utm, _, _ = converter.convert_to_utm_poly_object(
data=converter.targets,
base_point=base_point_utm,
tag_name_space="rover:target:",
)
measurement_as_utm, _, _ = converter.convert_to_utm_poly_object(
data=converter.measurement,
base_point=base_point_utm,
tag_name_space="rover:measurementArea:",
)
# random preset for targetIds list for sources
# random_source_target_match(sources_as_utm, targets_as_utm)
# make sure everything lies within the topography
if converter.aoi:
width_topography, height_topography = converter.find_width_height_from_aoi(
base_point_utm
)
else:
width_topography, height_topography = OsmConverter.find_width_and_height(
obstacles_as_utm
)
list_of_vadere_obstacles_as_strings = OsmConverter.to_vadere_obstacles(
obstacles_as_utm
)
list_of_vadere_sources_as_strings = OsmConverter.to_vadere_sources(sources_as_utm)
list_of_vadere_target_as_strings = OsmConverter.to_vadere_targets(targets_as_utm)
list_of_vadere_measurement_as_strings = OsmConverter.to_vadere_measurement_area(
measurement_as_utm
)
obstacles_joined = ",\n".join(list_of_vadere_obstacles_as_strings)
sources_joined = ",\n".join(list_of_vadere_sources_as_strings)
targets_joined = ",\n".join(list_of_vadere_target_as_strings)
measurement_joined = ",\n".join(list_of_vadere_measurement_as_strings)
vadere_topography_output = OsmConverter.to_vadere_topography(
width_topography,
height_topography,
base_point_utm,
zone_string,
obstacles=obstacles_joined,
sources=sources_joined,
targets=targets_joined,
measurement_areas=measurement_joined,
)
OsmConverter.print_output(cmd_args.output, vadere_topography_output)
# converter.osm.save()
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()
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()
def main_apply_config(args):
cfg = OsmConfig(args.config)
default_args = {"input": cfg["Config"]["input"], "output": cfg["Config"]["output"]}
for cmd in cfg["Config"]["command-order"]:
if f"{cmd}:options" not in cfg:
logging.warning(
f"no option specified for command '{cmd}' will use default if possible"
)
cmd_args = {}
cmd_args.update(default_args)
cmd_args.update(cfg[f"{cmd}:options"])
if f"{cmd}:way-list" in cfg:
cmd_args.update({"way-list": cfg.get_way_list(cmd)})
cmd_args = OsmArg(cmd_args)
main_func = cmd_entry[cmd]
print(
"################################################################################"
)
print(f"### executing command {cmd}")
print(
"################################################################################"
)
main_func(cmd_args)
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()
cmd_entry = {
"lint": main_lint,
"wall": main_walls,
"convex-hull": main_convex_hull,
"convert": main_convert,
}
if __name__ == "__main__":
path = "/home/sts/repos/vadere/Scenarios/Demos/roVer/scenarios/test.config"
args = parse_command_line_arguments(["use-config", "-c", path])
args.main_func(args)
descartes
PyYAML
scipy
lxml
utm
numpy
gitpython
\ No newline at end of file
This source diff could not be displayed because it is too large. You can view the blob instead.
<osm>
<node id="1" visible="true" version="11" changeset="10291545" timestamp="2012-01-04T14:39:37Z" user="mawi42" uid="42393" lat="1.1" lon="1.2"/>
<node id="2" visible="true" version="9" changeset="2270948" timestamp="2009-08-26T20:02:36Z" user="pfui_osm_korinthenkacker" uid="36809" lat="2.1" lon="2.2"/>
<node id="3" visible="true" version="10" changeset="6745590" timestamp="2010-12-23T14:00:32Z" user="mawi42" uid="42393" lat="3.1" lon="3.2"/>
</osm>
\ No newline at end of file
"""
Convert an OpenStreetMap XML file exported from https://www.openstreetmap.org/
to a Vadere topology (in Cartesian coordinates).
# Convert an OpenStreetMap XML file exported from https://www.openstreetmap.org/
# to a Vadere topology (in Cartesian coordinates).
#
# Steps to run this script:
#
# 1. Go to https://www.openstreetmap.org/.
# 2. Click "Export" and adjust region of intereset and zoom level.
# 3. Call script and pass exported file from (2):
#
# python3 <script> <exported_file>
#
# 4. Insert output into "topography" tab of Vadere.
#
# Note: currently, the scripts converts only buildings to Vadere obstacles.
#
# Watch out: before running this script, install its dependencies using pip:
#
# pip install -r requirements.txt
#
# An OpenStreetMap XML file has following structure:
#
# 1. The boundary box of the exported tile.
# 2. List of all nodes in the tile containing latitude and longitude.
# 3. Paths are formed using references to (2).
#
# Example OSM file:
#
# <?xml version="1.0" encoding="UTF-8"?>
# <osm version="0.6" ...>
# <bounds minlat="47.8480100" minlon="11.8207100" maxlat="47.8495600" maxlon="11.8249200"/>
# <node id="31413334" ... lat="47.8563764" lon="11.8186396"/>
# ...
# <way id="192686406" ...>
# <nd ref="31413334"/>
# <nd ref="3578052113"/>
# <nd ref="394769402"/>
# <nd ref="3828186058"/>
# <tag k="highway" v="unclassified"/>
# </way>
# ...
#
# A Vadere obstacle looks like this:
#
# {
# "shape" : {
# "type" : "POLYGON",
# "points" : [ { "x" : 43.7, "y" : 3.4 }, ... ]
# },
# "id" : -1
# }
converter defined in osm_converter.py
helper and osm structures in osm_helper.py
from lxml import etree
from string import Template
"""
import argparse
import logging
import os
from random import sample
from typing import List
import utm
import math
from osm_converter import OsmConverter
from osm_helper import OsmArg, OsmConfig, OsmData, PolyObjectWidthId
def parse_command_line_arguments():
parser = argparse.ArgumentParser(description="Convert and OpenStreetMap file to a Vadere topology description.")
parser.add_argument("filename", type=str, nargs="?",
help="An OSM map in XML format.",
default="maps/paris_eiffel_tower.osm",
)
parser.add_argument("-o", "--output", type=str, nargs="?",
help="Specify filename if you want the output in a file.")
args = parser.parse_args()
class HullAction(argparse.Action):
def __call__(self, parser, namespace, values, option_string=None):
if not hasattr(namespace, self.dest):
raise argparse.ArgumentError(f"namespace should contain {self.dest}")
return args
attr = getattr(namespace, self.dest)
attr.append(values)
def extract_latitude_and_longitude_for_each_xml_node(xml_tree):
# Select all nodes (not only buildings).
nodes = xml_tree.xpath("/osm/node")
def random_source_target_match(
sources: List[PolyObjectWidthId], targets: List[PolyObjectWidthId]
):
target_ids = [t.template_data.get("id") for t in targets]
nodes_dictionary_with_lat_and_lon = {node.get("id"): (node.get("lat"), node.get("lon")) for node in nodes}
for source in sources:
if "targetIds" not in source.template_data:
t_ids = sample(target_ids, 2)
if source.id in t_ids:
t_ids.remove(source.id)
source.template_data.setdefault("targetIds", f"[ {t_ids[0]} ]")
return nodes_dictionary_with_lat_and_lon
def str2bool(v):
# see https://stackoverflow.com/a/43357954
if isinstance(v, bool):
return v
if v.lower() in ("yes", "true", "t", "y", "1"):
return True
elif v.lower() in ("no", "false", "f", "n", "0"):
return False
else:
raise argparse.ArgumentTypeError("Boolean value expected.")
def parse_command_line_arguments(manual_args=None):
main = argparse.ArgumentParser(
prog="OpenStreetMap (OSM) Util for (Vadere, OMNeT++)",
description="Collection of small commands to manipulate an OSM xml file to "
"preparer it for conversion to Vadere or OMNeT++ structures",
)
parent_parser = argparse.ArgumentParser(add_help=False)
parent_parser.add_argument(