# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3
#     language: python
#     name: python3
# ---

import sys # for automation and parallelisation
manual, scenario = (True, 'base') if 'ipykernel' in sys.argv[0] else (False, sys.argv[1])
if manual:
    # %matplotlib inline

import os
import pandas as pd
import numpy as np
import geopandas as gpd
from tqdm import tqdm
from syspy.skims import skims
from quetzal.model import stepmodel
from quetzal.io import excel

# # Preparation of the LoS tables
# ## Corrects footpaths in the LoS table
# ## Needs PT LoS table and footpaths

input_path = '../input_static/spatial_'
output_path = '../output/'
model_path = '../model/'

# Load scenario parameters
params = excel.read_var(file='../input/parameters.xls', scenario=scenario)

sm = stepmodel.read_json(model_path + 'de_pt_los')
ae = stepmodel.read_json(model_path + 'de_pt_access_egress')

# The only kind of all-walk connections between centroids should be direct connections without intermediate stops at PT nodes because these are always connected to access/egress links which have non-footpath properties.
#
# Thus, footpaths already computed in prep2 will be parametrised with census based mean distances.

# Drop walking links that use access-egress links
sm.pt_los = sm.pt_los.loc[sm.pt_los['route_type']!='walk']

# Use saved non-motorised paths, if they exist from a previous run
import os.path
if os.path.exists(model_path + 'de_pt_los_walk/'):
    w = stepmodel.read_json(model_path + 'de_pt_los_walk')
    sm.pt_los = sm.pt_los.append(w.pt_los.loc[w.pt_los['route_type']=='walk']).reset_index(drop=True)
    print('Added walking paths from previous run')
    sm.to_json(model_path + 'de_pt_los',
               only_attributes=['pt_los'],
               encoding='utf-8')
    assert manual # break automation

sm.pt_los.sample()

# ## Compute mean distances
# Based on census data: mean population-weighted distance from origin centroid to every population cluster in the destination zone

# Load census data: Zensus 2011 - Einwohnerzahl je Hektar
# Copyright: © Statistisches Bundesamt, Wiesbaden 2015
# (im Auftrag der Herausgebergemeinschaft)
# Vervielfältigung und Verbreitung, auch auszugsweise, mit Quellenangabe gestattet
if os.path.isfile(input_path + 'Zensus_2011.geojson'):
    pop = gpd.read_file(input_path + 'Zensus_2011.geojson', driver='GeoJSON')
else:
    print('No file with geometries found')
    import shapely.speedups
    pop = pd.read_csv(input_path + 'Zensus_2011.csv', sep=';')
    # Restrict to entries that hold information
    pop = pop.loc[pop['Einwohner']!=-1]
    pop = gpd.GeoDataFrame(pop[['Einwohner']],
                          geometry=gpd.points_from_xy(pop['x_mp_100m'], pop['y_mp_100m']))
    pop.crs = 3035
    pop.to_crs(sm.epsg, inplace=True)
    pop['FID'] = np.nan
    z = stepmodel.read_zip(model_path + 'de_zones.zip')
    shapely.speedups.enable
    for _, zone in tqdm(z.zones.iterrows(), total=z.zones.shape[0]):
        pop.loc[pop['geometry'].within(zone['geometry']), 'FID'] = zone['FID']
    pop.to_file(input_path + 'Zensus_2011.geojson', driver='GeoJSON')

pop.sample()

# Get zone-connecting footpaths
foot = ae.footpaths.loc[(ae.footpaths['a'].apply(lambda s: len(s.split('_'))<=2)) &
                        (ae.footpaths['b'].apply(lambda s: len(s.split('_'))<=2))]
print(len(foot))
foot.sample()

try:
    speed = foot['speed'].mean() # in km/h
except KeyError:
    speed = params['pt_access']['speed_bicycle']
foot = foot[['a', 'b']].copy()
speed

# Build all columns of LoS table
foot['index'] = foot.index
foot['path'] = [(a,i,b) for a,i,b in zip(foot['a'], foot.index, foot['b'])]
foot.drop('index', axis=1, inplace=True)
foot = foot.rename(columns={'a': 'origin', 'b': 'destination'})
foot['link_path'] = [[] for _ in range(len(foot))]
for col in ['ntransfers', 'access_time', 'footpath_time',
            'in_vehicle_time', 'waiting_time',
            'boarding_time', 'time', 'length', 'price']:
    if col in sm.pt_los.columns:
        foot[col] = 0
foot['route_types'] = [('walk',) for _ in range(len(foot))]
foot['route_type'] = 'walk'

foot = foot.loc[~(~(foot['origin'].str.startswith('DE')) |
                  ~(foot['origin'].str.startswith('DE')) |
                  (foot['origin']==foot['destination']))]
foot.shape

foot.drop_duplicates(subset=['origin', 'destination'], inplace=True)
foot.reset_index(drop=True, inplace=True)

foot.sample()

# Calculate mean population-weighted distances
# from the origin centroid to all points in the destination zone
for ind, row in tqdm(foot.iterrows(), total=len(foot.index)):
    o = ae.centroids.loc[row['origin'], 'geometry']
    zone = pop.loc[pop['NUTS_ID']==row['destination']]
    weighted_dist = [skims.get_distance_from_lon_lat_in_m(
        o.coords[0][0], o.coords[0][1], p['geometry'].coords[0][0], p['geometry'].coords[0][1])
                     * p['Einwohner'] for _, p in zone.iterrows()]
    foot.loc[ind, 'length'] = sum(weighted_dist) / zone['Einwohner'].sum()

foot['length'].hist(bins=50)

# Compare to quickly approximated distances
ae.footpaths.loc[(ae.footpaths['a'].apply(lambda s: len(s.split('_'))<=2)) &
                 (ae.footpaths['b'].apply(lambda s: len(s.split('_'))<=2)),
                 'distance'].hist(bins=50)

# Replace NaN
foot.loc[foot['length'].isna(), 'length'] = foot.loc[foot['length'].notna(), 'length'].mean()

# Generate time in seconds
foot['time'] = foot['length'] / (speed / 3.6)
if 'footpath_time' in sm.pt_los.columns:
    foot['footpath_time'] = foot['time']

(foot['time']/3600).hist(bins=50) # in h

# Make DataFrame lighter
cols = ['length'] + [col for col in foot.columns if col[-4:]=='time']
foot[cols] = foot[cols].astype(int)

# Add to LoS table
sm.pt_los = sm.pt_los.append(foot).reset_index(drop=True)

sm.pt_los.loc[sm.pt_los['route_type']=='walk'].sample()

# ## Save

sm.to_json(model_path + 'de_pt_los',
           only_attributes=['pt_los'],
           encoding='utf-8')

StarcoderdataJupyterScriptsDedupFiltered