Skip to content

Instantly share code, notes, and snippets.

@key
Forked from basuke/drive.py
Last active December 31, 2021 07:59
Show Gist options
  • Save key/b0157b4e51dcc6db86ab15bb4f981dca to your computer and use it in GitHub Desktop.
Save key/b0157b4e51dcc6db86ab15bb4f981dca to your computer and use it in GitHub Desktop.
Python3 support! Create the GPX file from origin and destination using the GoogleMap Direction API. You can use this output for simulate the location apps in Xcode.
#
# python drive.py 'origin' ['waypoint' ... ] 'destination' --apikey=GOOGLE_MAPS_APIKEY
#
# i.e. python drive.py 'Union Square, San Francisco' 'Ferry Building, San Francisco' 'Bay Bridge' SFO
import hashlib
import json
import sys
import urllib
import os
from urllib.parse import quote_plus
from math import radians, sin, cos, atan2, pow, sqrt
from xml.sax.saxutils import escape
from optparse import OptionParser
parser = OptionParser('Usage: %prog [options] origin [waypoint ...] destination')
parser.add_option(
'--cache',
dest='cache',
help='cache the result of query for Google into DIR',
metavar='DIR')
parser.add_option(
'--apikey',
dest='apikey',
help='Api key for Google Maps Direction API',
)
(options, args) = parser.parse_args()
if len(args) < 2:
parser.error('incorrect number of arguments')
def urlForDirection(args):
origin = args[0]
destination = args[-1]
url = 'https://maps.googleapis.com/maps/api/directions/json?origin=%s&destination=%s&sensor=false&key=%s'
url = url % (quote_plus(origin), quote_plus(destination), options.apikey)
if len(args) > 2:
url += '&waypoints=' + quote_plus('|'.join(args[1:-1]))
return url
def cachePath(url):
return options.cache + '/' + hashlib.md5(url).hexdigest() + '.drive'
def fetchDirection(url):
data = None
cache_path = None
fetched = False
if options.cache:
cache_path = cachePath(url)
if os.path.isfile(cache_path):
with open(cache_path, 'r') as fp:
data = json.load(fp)
if not data:
fp = urllib.request.urlopen(url)
data = json.load(fp)
fp.close()
fetched = True
if options.cache and fetched:
fp = open(cache_path, 'w')
json.dump(data, fp)
fp.close()
if data.get('status') != 'OK':
sys.stderr.write('error_message: {}\n'.format(data.get('error_message')))
sys.stderr.write('Bad data')
sys.exit(1)
route = data['routes'][0]
return route['legs']
def namedWaypoint(coordinate, name):
print('\t<wpt lat="%(lat).6f" lon="%(lng).6f">' % coordinate)
print('\t\t<name>%s</name>' % escape(name))
print('\t</wpt>')
def waypoint(coordinate):
print('\t<wpt lat="%(lat).6f" lon="%(lng).6f"/>' % coordinate)
def decodePoly(pts):
step1 = [ord(x) - 63 for x in pts]
step2, vals = [], []
for val in step1:
vals.insert(0, val & 0x1f)
if val < 0x20:
val = 0
for n in vals:
val = (val << 5) + n
if val % 2: val = ~(val - 1)
val /= 2.0
val /= 100000.0
step2.append(val)
vals = []
step3 = []
for lat, lng in zip(step2[0::2], step2[1::2]):
if len(step3) > 0:
lat += step3[-1]['lat']
lng += step3[-1]['lng']
step3.append({'lat': lat, 'lng': lng})
return step3
def distanceBetweenPoints(a, b):
lat1 = radians(a['lat'])
lng1 = radians(a['lng'])
lat2 = radians(b['lat'])
lng2 = radians(b['lng'])
R = 6371.0 * 1000 # radius of earth by km
a = pow(sin((lat1 - lat2) / 2.0), 2) + pow(sin((lng1 - lng2) / 2.0), 2) * cos(lat1) * cos(lat2)
c = 2.0 * atan2(sqrt(a), sqrt(1.0 - a))
return R * c # / 1.6 * 5280.0 # to mile, then to feet
def complementPoints(points, duration, distance):
result = []
for a, b in zip(points[0:-1], points[1:]):
dd = distanceBetweenPoints(a, b)
dt = duration * (dd / distance)
t = 0.0
lat = b['lat'] - a['lat']
lng = b['lng'] - a['lng']
while t < dt:
r = t / dt
pt = {'lat': a['lat'] + lat * r, 'lng': a['lng'] + lng * r}
result.append(pt)
t += 5
result.append(points[-1])
return result
def printGPX(legs):
print('<?xml version="1.0"?>')
print('<gpx version="1.1" creator="drive.py coded by basuke">')
namedWaypoint(legs[0]['start_location'], legs[0]['start_address'])
for leg in legs:
for step in leg['steps']:
start = step['start_location']
end = step['end_location']
duration = step['duration']['value']
distance = step['distance']['value']
decodePoly(step['polyline']['points'])
points = decodePoly(step['polyline']['points'])
points.insert(0, start)
points.append(end)
points = complementPoints(points, duration, distance)
for pt in points:
waypoint(pt)
namedWaypoint(leg['end_location'], leg['end_address'])
print('</gpx>')
url = urlForDirection(args)
legs = fetchDirection(url)
printGPX(legs)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment