-
Notifications
You must be signed in to change notification settings - Fork 0
/
get-itineraries.py
176 lines (158 loc) · 5.76 KB
/
get-itineraries.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
import requests, multiprocessing, time, os, csv, sys
from datetime import datetime, timedelta
from random import shuffle
# where are the things?
output_dir = '/home/nate/dissdata/routing/17476/'
OD_file = 'data/ODs.csv'
OTP_server = 'http://166.48.61.19:8080/otp/routers/ttc-17476/plan'
# define the start time
start_time = datetime( year=2017, month=11, day=6, hour=6 )
print('from ',start_time)
# and go for how long?
end_time = start_time + timedelta(hours=16,minutes=30)
print('until',end_time)
# how many processors to use?
num_procs = 6
# difference from server time zone in seconds
TZdiff = 3600 * 0
# read in the OD location data
points = {}
with open(OD_file) as csvfile:
reader = csv.DictReader(csvfile, delimiter=',')
for r in reader:
points[int(r['uid'])] = {'lat':float(r['lat']),'lon':float(r['lon'])}
# read in the set of OD's to analyze
all_pairs = False
OD_pairs = []
if all_pairs:
for oid in points.keys():
for did in points.keys():
if oid == did: continue
if not os.path.exists(output_dir+str(oid)+'/'+str(did)+'.csv'):
OD_pairs.append( (oid,did) )
else:
with open('data/sampled-ODs/5k-weighted-sample.csv') as csvfile:
reader = csv.DictReader(csvfile, delimiter=',')
for r in reader:
# skip if the results already exist
if not os.path.exists(output_dir+r['o']+'/'+r['d']+'.csv'):
OD_pairs.append( (int(r['o']),int(r['d'])) )
print(len(OD_pairs),'pairs to work on')
shuffle(OD_pairs)
def calc_od(od_tuple):
"""calculate all shortest itineraries for this OD pair in the time
period of interest"""
function_start_time = time.time() # for timing the function
# get the OD geometries
oid,did = od_tuple
print('starting',oid,'->',did)
o,d = points[oid], points[did]
# define the static OTP routing parameters
options = {
'mode':'TRANSIT,WALK',
'maxWalkDistance':2000, # meters
'wheelchair':'false',
'arriveBy':'false', # meaning departAt
'maxTransfers':2,
'numItineraries':1,
#'transferPenalty':5*60, # five minutes
#'walkReluctance':1.2,
#'waitReluctance':2,
'fromPlace':( str(o['lat'])+','+str(o['lon']) ),
'toPlace':( str(d['lat'])+','+str(d['lon']) )
#'reverseOptimizeOnTheFly': 'true'
}
# start at the start time
t = start_time
# results dictionary, keyed by arrival times
trips = {}
while t <= end_time:
#print oid, did, t, len(trips)
# set the time of this query
options['date'] = t.strftime('%m-%d-%Y')
options['time'] = t.strftime('%I:%M%p')
# make the request
response = requests.get( OTP_server, params=options )
try:
result = response.json()
except:
print('json error at',t,response.text)
t += timedelta(minutes=5)
continue
# check for errors
if 'error' in result:
# print an error message
print(oid,'-->',did,' -- ',result['error']['message'],'at',t)
# then add a good chunk of time before trying again
t += timedelta(minutes=10)
continue
# now we have an actual result from the router
itinerary = result['plan']['itineraries'][0]
# get details of the route
legs = summarize_legs(itinerary)
# overwrite any previous trip that may have had the same arrival time
# time from milliseconds to seconds and clip to nearest second
departure = int( itinerary['startTime'] / 1000.0 ) + TZdiff
arrival = int( itinerary['endTime'] / 1000.0 ) + TZdiff
# in case we have this time already, we need to switch to the slower,
# more optimized method to save time on future requests
if arrival in trips and not 'reverseOptimizeOnTheFly' in options:
options['reverseOptimizeOnTheFly'] = 'true'
print('\toptimizing on-the-fly')
# and store the result
trips[arrival] = { 'departure':departure, 'itinerary':legs }
# in case this is a walk only result, increment by more than a minute
# or we'll be here all day
if len(itinerary['legs']) == 1 and itinerary['legs'][0]['mode'] == 'WALK':
t = datetime.fromtimestamp(departure) + timedelta(minutes=10)
else:
# set the next request for just after the last departure time given here
t = datetime.fromtimestamp(departure) + timedelta(minutes=1)
# save the output
write_output(trips,oid,did)
# let us know what's going on
print( oid,'->',did,'took',round(time.time()-function_start_time,2),'& found',len(trips) )
def summarize_legs(itinerary):
"""returns a short array summary of the trip to be saved in the DB"""
legs = []
for leg in itinerary['legs']:
# is it a walking leg?
if leg['mode'] == 'WALK':
# show that we're walking and how far
legs.append('w'+str(int(leg['distance'])))
# or is it a transit leg?
elif 'route' in leg:
# add both stops and the route number
orig_stop = 's'+ leg['from']['stopId'][2:] # remove the '1:'
dest_stop = 's'+ leg['to']['stopId'][2:] # remove the '1:'
if len(legs) == 0 or legs[-1] != orig_stop:
legs.append( orig_stop )
legs.append( 'r'+ leg['route'] )
legs.append( dest_stop )
else:
print(response.url)
return legs
def write_output(trips,oid,did):
"""after all the results are in, output a file, even an empty one to show
that the work is done. store results in folders named after origins,
files named after destinations"""
if not os.path.exists(output_dir+str(oid)):
os.makedirs(output_dir+str(oid))
with open(output_dir+str(oid)+'/'+str(did)+'.csv','w+') as outfile:
outfile.write('o,d,depart,arrive,itinerary')
# now we're going to sort by the keys (arrival times)
arrival_times = list(trips.keys())
arrival_times.sort()
for arrival in arrival_times:
outfile.write(
'\n'+str(oid)+','+str(did)+','+
# cast these to datetime
str(trips[arrival]['departure'])+','+str(arrival)+','+
# format as a postgresql array literal
'"{'+','.join(trips[arrival]['itinerary'])+'}"'
)
# create the process pool
pool = multiprocessing.Pool(num_procs)
# and get it started
pool.map(calc_od,OD_pairs,chunksize=1)
print('COMPLETED!')