-
-
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.
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
# | |
# 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