5 from typing import List, Optional, Tuple
6 from xml.etree.ElementTree import ElementTree, Element
9 from geojson import Feature
10 from shapely.geometry import shape
11 from termcolor import cprint
13 from wrpylib.cli_tools import unified_diff, input_yes_no_quit, Choice
14 from wrpylib.json_tools import format_json
15 from wrpylib.mwapi import WikiSite
16 from wrpylib.wrgeojson import join_wrgeojson_ways, WrLineStringFeatureType, get_geometry_center, \
17 LanToToMetricTransformer, DEFAULT_MAX_DIST_ERROR_M
18 from wrpylib.wrosm import find_sledrun_relations, convert_osm_to_geojson, DeRefError, tags
21 def filter_way(feature: dict, way_type: str) -> bool:
22 return feature['geometry']['type'] == 'LineString' and feature.get('properties', {}).get('type') == way_type
25 def feature_distance(a: Feature, b: Feature) -> float:
26 return shape(a['geometry']).hausdorff_distance(shape(b['geometry']))
29 def geojson_way_sort_key(feature: Feature, reference_feature: Feature) -> Tuple[str, float]:
30 return feature.get('properties', {}).get('type', ''), feature_distance(feature, reference_feature)
33 def update_sledrun_wrgeojson_to_highres(wrgeojson_title: str, osm_tree: ElementTree, osm_sledrun_relation: Element,
34 max_dist_m: float, site: WikiSite):
36 osm_wrgeojson = convert_osm_to_geojson(osm_tree, osm_sledrun_relation)
38 print('Error: Incomplete XML - please load larger region.')
40 join_wrgeojson_ways(osm_wrgeojson)
42 wrgeojson_page = site.query_page(wrgeojson_title)
43 wrgeojson_page_content = wrgeojson_page['revisions'][0]['slots']['main']['content']
44 wr_wrgeojson = geojson.loads(wrgeojson_page_content)
46 center = get_geometry_center(wr_wrgeojson)
47 transformer = LanToToMetricTransformer(center.coordinates[1])
49 for way_type in WrLineStringFeatureType:
50 wr_ways = [f for f in wr_wrgeojson['features'] if filter_way(f, way_type.value)]
51 osm_ways = [f for f in osm_wrgeojson['features'] if filter_way(f, way_type.value)]
52 if len(wr_ways) != len(osm_ways):
53 raise ValueError(f'{wrgeojson_title} does not have the same number of {way_type.value} features')
55 for reference_feature in wr_wrgeojson['features']:
56 reference_feature_type: str = reference_feature.get('properties', {}).get('type', '')
57 if reference_feature_type not in [v.value for v in WrLineStringFeatureType]:
60 osm_features: List[Feature] = [f for f in osm_wrgeojson['features'] if filter_way(f, reference_feature_type)]
61 osm_features = sorted(osm_features, key=lambda f: geojson_way_sort_key(f, reference_feature))
62 osm_feature = osm_features[0]
63 osm_feature_m = transformer.geojson_lon_lat_to_metric(osm_feature)
64 reference_feature_m = transformer.geojson_lon_lat_to_metric(reference_feature)
66 dist_m = feature_distance(osm_feature_m, reference_feature_m)
67 if dist_m > max_dist_m:
68 raise ValueError(f'Distance of ways "{reference_feature_type}" too big ({dist_m} m > {max_dist_m}).')
70 reference_feature['geometry'] = osm_feature['geometry']
72 wr_wrgeojson_old = json.loads(wrgeojson_page_content)
73 if wr_wrgeojson_old == wr_wrgeojson:
76 wr_wrgeojson['wr_properties']['simplified'] = False
78 wr_wrgeojson_old_str = format_json(wr_wrgeojson_old)
79 wr_wrgeojson_str = format_json(wr_wrgeojson)
81 cprint(wrgeojson_title, 'green')
82 unified_diff(wr_wrgeojson_old_str, wr_wrgeojson_str)
83 choice = input_yes_no_quit('Do you accept the changes [yes, no, quit]? ', None)
84 if choice == Choice.no:
86 elif choice == Choice.quit:
91 pageid=wrgeojson_page['pageid'],
92 text=wr_wrgeojson_str,
93 summary='Höhere Auflösung der Wege erstellt.',
96 baserevid=wrgeojson_page['revisions'][0]['revid'],
102 def update_wrgeojson_to_highres(wrgeojson_title: str, osm_file: str, osm_index: Optional[int], ini_files: List[str]):
103 osm_tree = ElementTree()
104 osm_tree.parse(osm_file)
106 site = WikiSite(ini_files)
108 # Search all sledrun relations
109 osm_sledrun_list = list(find_sledrun_relations(osm_tree))
111 # Select one sledrun to work with
112 if len(osm_sledrun_list) == 0:
113 print('No sledruns in OSM file.')
115 elif len(osm_sledrun_list) > 1 and osm_index is None:
116 print(f'{len(osm_sledrun_list)} sledruns in the OSM. Please specify --osm_index:')
117 for i, osm_sledrun in enumerate(osm_sledrun_list):
118 t = tags(osm_sledrun)
119 print(f'{i}: {t.get("name", t)}')
121 osm_sledrun = osm_sledrun_list[0 if osm_index is None else osm_index]
123 max_dist = DEFAULT_MAX_DIST_ERROR_M * 1.5
124 update_sledrun_wrgeojson_to_highres(wrgeojson_title, osm_tree, osm_sledrun, max_dist, site)
128 parser = argparse.ArgumentParser(
129 description='Updates sledrun wrgeojson maps with higher resolution ways from OSM files.')
130 parser.add_argument('wrgeojson_title', help='Title of an existing wrgeojson page.')
131 parser.add_argument('osm_file', help='OSM file containing high resolution ways.')
132 parser.add_argument('--osm_index', type=int, help='Index of the sledrun relation within the OSM file.')
133 parser.add_argument('ini_file', nargs='+',
134 help='inifile.ini, see: https://www.winterrodeln.org/trac/wiki/ConfigIni')
135 args = parser.parse_args()
136 update_wrgeojson_to_highres(args.wrgeojson_title, args.osm_file, args.osm_index, args.ini_file)
139 if __name__ == '__main__':