16This script transforms an XODR file by updating the geoReference and rotating the coordinates.
17It reads the input XODR file, extracts the original latitude and longitude from the geoReference tag,
18applies a transformation to the coordinates, and writes the modified data to a new XODR file.
19The script can be run from the command line with the following arguments:
20- input_file: The path to the input XODR file.
21- output_file: The path to the output XODR file.
23- pip install pyproj argparse lxml
25 python3 xodr_transform.py <input_file> <output_file>
29from pyproj import CRS, Transformer
34def extract_lat_lon_from_georeference(geo_text):
35 """Extracts lat_0 and lon_0 from the geoReference WKT string."""
36 lat_match = re.search(
r"\+lat_0=([-+]?[0-9]*\.?[0-9]+)", geo_text)
37 lon_match = re.search(
r"\+lon_0=([-+]?[0-9]*\.?[0-9]+)", geo_text)
38 if lat_match
and lon_match:
39 lat =
float(lat_match.group(1))
40 lon =
float(lon_match.group(1))
43 raise ValueError(
"geoReference does not contain +lat_0 and +lon_0")
47 """Updates the +lat_0 and +lon_0 values in a proj4 WKT string."""
48 new_text = re.sub(
r"\+lat_0=([-+]?[0-9]*\.?[0-9]+)", f
"+lat_0={new_lat}", old_text)
49 new_text = re.sub(
r"\+lon_0=([-+]?[0-9]*\.?[0-9]+)", f
"+lon_0={new_lon}", new_text)
54 angle_rad = math.radians(angle_deg)
55 x_rot = x * math.cos(angle_rad) - y * math.sin(angle_rad)
56 y_rot = x * math.sin(angle_rad) + y * math.cos(angle_rad)
60def transform_latlon(lat, lon, transformer_from_orig_to_utm, transformer_from_utm_to_new, angle_deg):
61 x, y = transformer_from_orig_to_utm.transform(lon, lat)
62 x_rot, y_rot =
rotate(x, y, angle_deg)
63 lon_new, lat_new = transformer_from_utm_to_new.transform(x_rot, y_rot, direction=
'INVERSE')
64 return lat_new, lon_new
68 return hdg + math.radians(angle_deg)
72 tree = etree.parse(input_path)
83 geo_ref_tag = root.find(
"header/geoReference")
84 if geo_ref_tag
is None or not geo_ref_tag.text:
85 raise ValueError(
"No geoReference tag found.")
89 if new_lat
is None or new_lon
is None:
97 crs_orig = CRS.from_proj4(f
"+proj=tmerc +lat_0={orig_lat} +lon_0={orig_lon} +ellps=WGS84 +datum=WGS84 +units=m +no_defs")
98 crs_new = CRS.from_proj4(f
"+proj=tmerc +lat_0={new_lat} +lon_0={new_lon} +ellps=WGS84 +datum=WGS84 +units=m +no_defs")
100 transformer_to_utm = Transformer.from_crs(
"EPSG:4326", crs_orig, always_xy=
True)
101 transformer_from_utm = Transformer.from_crs(
"EPSG:4326", crs_new, always_xy=
True)
103 for elem
in root.iter():
104 lat = elem.attrib.get(
"lat")
105 lon = elem.attrib.get(
"lon")
106 x = elem.attrib.get(
"x")
107 y = elem.attrib.get(
"y")
108 hdg = elem.attrib.get(
"hdg")
114 lat_new_val, lon_new_val =
transform_latlon(lat_f, lon_f, transformer_to_utm, transformer_from_utm, angle_deg)
115 elem.set(
"lat", f
"{lat_new_val:.8f}")
116 elem.set(
"lon", f
"{lon_new_val:.8f}")
122 x_rot, y_rot =
rotate(x_f, y_f, angle_deg)
123 elem.set(
"x", f
"{x_rot:.8f}")
124 elem.set(
"y", f
"{y_rot:.8f}")
130 elem.set(
"hdg", f
"{hdg_new:.8f}")
133 tree.write(output_path, encoding=
"UTF-8", xml_declaration=
True)
136if __name__ ==
"__main__":
139 parser = argparse.ArgumentParser(description=
"Transform XODR GPS-coordinates with a new geoReference and rotation.")
140 parser.add_argument(
"input_file", help=
"Path to input .xodr file")
141 parser.add_argument(
"output_file", help=
"Path to output .xodr file")
143 args = parser.parse_args()