Created
January 15, 2020 11:55
-
-
Save cphyc/9ffd983416c722c66577df63edfaa19b to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import sys | |
import os | |
import argparse | |
from datetime import datetime | |
try: | |
import requests | |
from scipy.interpolate import interp1d | |
import pandas as pd | |
import numpy as np | |
import gnuplotlib as gp | |
except ModuleNotFoundError as e: | |
module = str(e).split("'")[1] | |
print(f'Module {module} is not installed. Install it, e.g. using pip: `pip install {module}`') | |
sys.exit(1) | |
MORNING_TIMES = (9, 11.5) | |
EVENING_TIMES = (17, 19.5) | |
TRAVEL_TIME = 25 / 60 # 25 minutes | |
LOCATION = 'Bermondsey' | |
def find_optimal(hourly, bounds, travel_time): | |
cor = interp1d(hourly.time.astype(int)/100, hourly.chanceofrain.astype(int), kind='quadratic', | |
bounds_error=False, fill_value='extrapolate') | |
dt = 5/60 # 5 minutes | |
ispace = int(np.round(travel_time / dt)) | |
t = np.arange(bounds[0], bounds[1]+travel_time, dt) | |
cor_cumsum = np.cumsum(np.abs(cor(t))) | |
mean = (cor_cumsum[ispace:] - cor_cumsum[:-ispace]) / ispace | |
imin = np.argmin(mean) | |
return dict(t_optimal=t[imin], chance_of_rain_optimal=mean[imin], | |
t_earliest=t[0], chance_of_rain_earliest=mean[0], | |
t_latest=t[-ispace], chance_of_rain_latest=mean[-1], | |
t=t[:-ispace], chance_of_rain=mean) | |
if __name__ == '__main__': | |
parser = argparse.ArgumentParser(description='Find optimal time to bike.') | |
group = parser.add_mutually_exclusive_group() | |
group.add_argument('--evening', action='store_true', | |
help='If set, do evening travel.') | |
group.add_argument('--morning', action='store_true', | |
help='If set, do morning travel.') | |
group.add_argument('--auto', action='store_true', | |
help='Automatically pick morning or evening trip.') | |
parser.add_argument('--bounds', nargs=2, type=float, default=(-1, -1), | |
help='Travel time boundaries') | |
parser.add_argument('--travel-time', type=float, default=TRAVEL_TIME, | |
help='Travel duration.') | |
parser.add_argument('--city', type=str, default=LOCATION) | |
args = parser.parse_args() | |
data = requests.get(f'https://wttr.in/{args.city}?format=j1').json() | |
# Get today's hourly weather | |
hourly = pd.DataFrame(data['weather'][0]['hourly'], dtype='float') | |
now = datetime.now() | |
now_hour = now.hour + now.minute / 60 | |
if args.auto: | |
if now < MORNING_TIMES[1] or now > EVENING_TIMES[1]: | |
morning = True | |
else: | |
morning = False | |
else: | |
morning = args.morning | |
if morning: | |
bounds = list(MORNING_TIMES) | |
else: | |
bounds = list(EVENING_TIMES) | |
bounds[0] = max(now_hour, bounds[0]) | |
if bounds[0] > bounds[1]: | |
raise Exception('Lower boundary is later then late boundary, %s > %s!' % (bounds[0], bounds[1])) | |
# Now finds the optimal time to go | |
data = find_optimal(hourly, bounds, args.travel_time) | |
def h_to_hmin(v): | |
hour = int(v) | |
minute = int((v-hour)*60) | |
return hour, minute | |
def print_info(pre, time, chance_of_rain): | |
h, m = h_to_hmin(time) | |
print(f'{pre} {h:02d}:{m:02d}, with {chance_of_rain:.0f}% chance of rain.') | |
print_info('Best time: ', data['t_optimal'], data['chance_of_rain_optimal']) | |
print_info('Earliest: ', data['t_earliest'], data['chance_of_rain_earliest']) | |
print_info('Latest: ', data['t_latest'], data['chance_of_rain_latest']) | |
rows, columns = (int(_) for _ in os.popen('stty size', 'r').read().split()) | |
xmin, xmax = int(np.floor(bounds[0])), int(np.ceil(bounds[1])) | |
gp.plot(data['t'], data['chance_of_rain'], _with = 'lines', terminal=f'dumb {columns}, {rows-5}', | |
_ymin=-5, _ymax=105, | |
unset=['grid'], set=[f'xtics out nomirror add {xmin},1,{xmax}', 'ytics out nomirror add 0,25,100'], | |
_xlabel='Departure time', _ylabel='Chance of rain') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment