Created
December 31, 2011 04:01
-
-
Save mattwigway/1542816 to your computer and use it in GitHub Desktop.
OTP to Everywhere
This file contains hidden or 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/python | |
# otpeverywhere - make otp maps like | |
# http://www.flickr.com/photos/walkingsf/6536396399/ | |
# Copyright 2011 Matt Conway | |
# Licensed under the Apache License, Version 2.0 (the "License"); | |
# you may not use this file except in compliance with the License. | |
# You may obtain a copy of the License at | |
# http://www.apache.org/licenses/LICENSE-2.0 | |
# Unless required by applicable law or agreed to in writing, software | |
# distributed under the License is distributed on an "AS IS" BASIS, | |
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
# See the License for the specific language governing permissions and | |
# limitations under the License. | |
# Use the new, Python 2.7/3 argument parser | |
import argparse | |
from urllib2 import Request, urlopen | |
from psycopg2 import connect | |
from time import time | |
import json | |
parser = argparse.ArgumentParser(description="OTP Everywhere Maps") | |
parser.add_argument('-o', '--out-table', dest='outtable', type=str, | |
help='the table to store data in', action='store', | |
metavar='TABLE', default='otpeverywhere') | |
parser.add_argument('-i', '--in-table', dest='intable', type=str, action='store', | |
help='the table to retrieve destinations from', | |
metavar='TABLE', default='intersections') | |
parser.add_argument('-d', '--dsn', dest='dsn', type=str, | |
help='database dsn, as documented at http://initd.org/psycopg/docs/module.html#psycopg2.connect', | |
metavar='DSN', default='dbname=otp') | |
parser.add_argument('--otp-url', '-u', action='store', dest='otpurl', | |
help='your OTP base URL, default: http://localhost:8080/opentripplanner-api-webapp', | |
default='http://localhost:8080/opentripplanner-api-webapp') | |
parser.add_argument('--start', '-s', action='store', | |
help='the start lat,lon (WGS84)', | |
default='37.7847,-122.40766') # Market and Powell, downtown San Francisco, CA | |
parser.add_argument('--date', '-a', action='store', | |
help='date, format mm/dd/yyyy', | |
default='12/29/2011') | |
parser.add_argument('--time', '-t', action='store', | |
help='start time', | |
default='9:00 am') | |
parser.add_argument('--mode', '-m', action='store', | |
help='mode, e.g. TRANSIT,BICYCLE', | |
default='TRANSIT,WALK') | |
opts = parser.parse_args() | |
# Need two connections b/c named cursor is invalidated on commit | |
conn = connect(opts.dsn) | |
inconn = connect(opts.dsn) | |
incursor = inconn.cursor(name='input') | |
outcursor = conn.cursor() | |
# From http://seewah.blogspot.com/2009/11/gpolyline-decoding-in-python.html | |
def decode_line(encoded): | |
"""Decodes a polyline that was encoded using the Google Maps method. | |
See http://code.google.com/apis/maps/documentation/polylinealgorithm.html | |
This is a straightforward Python port of Mark McClure's JavaScript polyline decoder | |
(http://facstaff.unca.edu/mcmcclur/GoogleMaps/EncodePolyline/decode.js) | |
and Peter Chng's PHP polyline decode | |
(http://unitstep.net/blog/2008/08/02/decoding-google-maps-encoded-polylines-using-php/) | |
""" | |
encoded_len = len(encoded) | |
index = 0 | |
array = [] | |
lat = 0 | |
lng = 0 | |
while index < encoded_len: | |
b = 0 | |
shift = 0 | |
result = 0 | |
while True: | |
b = ord(encoded[index]) - 63 | |
index = index + 1 | |
result |= (b & 0x1f) << shift | |
shift += 5 | |
if b < 0x20: | |
break | |
dlat = ~(result >> 1) if result & 1 else result >> 1 | |
lat += dlat | |
shift = 0 | |
result = 0 | |
while True: | |
b = ord(encoded[index]) - 63 | |
index = index + 1 | |
result |= (b & 0x1f) << shift | |
shift += 5 | |
if b < 0x20: | |
break | |
dlng = ~(result >> 1) if result & 1 else result >> 1 | |
lng += dlng | |
array.append((lat * 1e-5, lng * 1e-5)) | |
return array | |
# I know, SQL injection, but you can't have psycopg escape a table name properly | |
# I think the source is safe, unless you install the script suid postgres or something | |
incursor.execute('SELECT X(transform(the_geom, 4326)) as lon, Y(transform(the_geom, 4326)) as lat FROM ' + opts.intable) | |
start_time = time() | |
i = 0 | |
for geom in incursor: | |
# build the url | |
url = '%s/ws/plan' % opts.otpurl | |
url += '?fromPlace=%s' % opts.start | |
url += '&toPlace=%s,%s' % (geom[1], geom[0]) | |
url += '&date=%s' % opts.date | |
url += '&time=%s' % opts.time.replace(' ', '%20') | |
url += '&arriveBy=false' | |
url += '&mode=%s' % opts.mode | |
url += '&numItineraries=1' | |
# Request JSON format | |
headers=dict(Accept='application/json') | |
# the user doesn't review the request, so it's technically unverifiable | |
r = Request(url, headers=headers, unverifiable=True) | |
# Parse the response | |
d = json.loads(urlopen(r).read()) | |
# Build an array of lat,lon | |
points = [] | |
# Were there errors? If so, d['plan'] == None | |
if d['plan']: | |
# parse the geometries. also add the times. | |
for leg in d['plan']['itineraries'][0]['legs']: | |
legPoints = decode_line(leg['legGeometry']['points']) | |
assert len(legPoints) == leg['legGeometry']['length'] | |
points += legPoints | |
wkt = 'SRID=4326;LINESTRING(%s)' % ', '.join(['%s %s' % (point[1], point[0]) for point in points]) | |
# ms -> s | |
totalTime = (d['plan']['itineraries'][0]['endTime'] - d['plan']['itineraries'][0]['startTime'])/1000 | |
outcursor.execute('INSERT INTO ' + opts.outtable + ' (the_geom, time) VALUES (ST_GeomFromEWKT(%s), %s)', (wkt, totalTime)) | |
else: | |
print 'Location %s, %s is unreachable, skipping' % (geom[1], geom[0]) | |
print d['error']['msg'] | |
i += 1 | |
if i % 100 == 0: | |
conn.commit() | |
newtime = time() | |
print '100 requests, %s seconds, average %s secs/request' % (newtime - start_time, (newtime - start_time)/100) | |
start_time = newtime | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment