#!/usr/bin/python import argparse import configparser import csv import re import sqlite3 from datetime import timedelta from itertools import islice from typing import List, NamedTuple, Optional import isodate import jsonschema from osgeo import ogr from osgeo.ogr import Layer, DataSource, Geometry, wkbPoint, Feature from osgeo.osr import SpatialReference, CoordinateTransformation, OAMS_TRADITIONAL_GIS_ORDER from wrpylib.json_tools import order_json_keys, format_json from wrpylib.mwapi import WikiSite, page_json from wrpylib.vao import Vao class DistInfo(NamedTuple): city_name: str geoname_id: int duration_minutes: int dist_m: int co2_kg: float def vao_car_distance(vao: Vao, parking_lon: float, parking_lat: float, city: Feature) -> Optional[DistInfo]: geometry = city.GetGeometryRef() point = geometry.GetPoint(0) city_lon, city_lat, _ = point parameter = { 'originCoordLat': city_lat, 'originCoordLong': city_lon, 'destCoordLat': parking_lat, 'destCoordLong': parking_lon, 'groupFilter': 'API_CAR', 'totalCar': '1|evnt=0,aevnt=0,tsta=0,htsta=0,getInitEndTimes=0', } response = vao.trip(parameter).json() trip = response.get('Trip', []) if trip: leg_list = trip[0].get('LegList', {}).get('Leg', []) if leg_list: leg = leg_list[0] duration = isodate.parse_duration(leg['duration']) duration_minutes = int(round(duration / timedelta(minutes=1))) dist_m = leg['dist'] co2_kg = trip[0]['Eco']['co2'] return DistInfo(city['name'], city['geonameid'], duration_minutes, dist_m, co2_kg) def update_sledrun(vao: Vao, db_cities: Layer, site: WikiSite, title: str): sledrun_json_page = site.query_page(f'{title}/Rodelbahn.json') sledrun_json = page_json(sledrun_json_page) # for now... if 'car_distances' in sledrun_json: return sledrun_json_orig = sledrun_json.copy() car_parking = sledrun_json.get('car_parking') if not car_parking: print(' (no parking)') print('') return parking = car_parking[0].get('position', {}).get('position') if not parking: return parking_lon = parking['longitude'] parking_lat = parking['latitude'] spatial_reference_ll = SpatialReference() spatial_reference_ll.ImportFromEPSG(4326) spatial_reference_ll.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER) spatial_reference_m = SpatialReference() spatial_reference_m.ImportFromProj4(f'+proj=merc +lat_ts={parking_lat}') # spatial_reference_m.ImportFromProj4(f'+proj=merc') ll_to_m = CoordinateTransformation(spatial_reference_ll, spatial_reference_m) m_to_ll = CoordinateTransformation(spatial_reference_m, spatial_reference_ll) loc_ll = Geometry(wkbPoint) loc_ll.AddPoint(parking_lon, parking_lat) # print(loc_ll.ExportToWkt()) loc_m = loc_ll.Clone() loc_m.Transform(ll_to_m) # print(loc_m.ExportToWkt()) max_dist_m = 60000 bound_m = loc_m.Buffer(max_dist_m) # print(bound_m.ExportToWkt()) bound_ll = bound_m.Clone() # print(bound_ll.ExportToWkt()) bound_ll.Transform(m_to_ll) # print(bound_ll.ExportToWkt()) db_cities.SetSpatialFilter(bound_ll) db_cities.SetAttributeFilter('level<=2') dist_info_list = [] for city in db_cities: dist_info = vao_car_distance(vao, parking_lon, parking_lat, city) if dist_info is not None: dist_info_list.append(dist_info) dist_info_list = sorted(dist_info_list, key=lambda di: di.dist_m)[:3] car_distances = [{ 'km': round(di.dist_m / 1000, 1), 'route': di.city_name, 'minutes': di.duration_minutes, 'geonames_id': di.geoname_id, 'onward_co2_kg': round(di.co2_kg, 1), } for di in dist_info_list] sledrun_json['car_distances'] = car_distances if sledrun_json == sledrun_json_orig: return jsonschema.validate(instance=sledrun_json, schema=site.sledrun_schema()) sledrun_json_ordered = order_json_keys(sledrun_json, site.sledrun_schema()) assert sledrun_json_ordered == sledrun_json sledrun_json_str = format_json(sledrun_json_ordered) site( 'edit', pageid=sledrun_json_page['pageid'], text=sledrun_json_str, summary=f'Entfernungen zu {title} eingefügt (dank VAO).', # minor=1, bot=1, baserevid=sledrun_json_page['revisions'][0]['revid'], nocreate=1, token=site.token(), ) def update_car_distances(ini_files: List[str]): ogr.UseExceptions() config = configparser.ConfigParser() config.read(ini_files) host = config.get('mysql', 'host') dbname = config.get('mysql', 'dbname') user = config.get('mysql', 'user_name') passwd = config.get('mysql', 'user_pass') cities_wr_source = ogr.Open(f'MySQL:{dbname},"host={host}","user={user}","password={passwd}","tables=wrcity"') db_cities = cities_wr_source.GetLayerByIndex(0) site = WikiSite(ini_files) vao = Vao(config.get('vao', 'access_id')) for result in site.query(list='categorymembers', cmtitle='Kategorie:Rodelbahn', cmlimit='max'): for page in result['categorymembers']: print(page['title']) update_sledrun(vao, db_cities, site, page['title']) def main(): parser = argparse.ArgumentParser(description='Update car distance information in sledrun JSON files.') parser.add_argument('inifile', nargs='+', help='inifile.ini, see: https://www.winterrodeln.org/trac/wiki/ConfigIni') args = parser.parse_args() update_car_distances(args.inifile) if __name__ == '__main__': main()