Creating GPS route maps from google maps

This is an expansion of the excellent work done by Mr. Davis at his Open Source GPS HOW TO page. He has a google maps hack here that allows one to make a GPX xml file by clicking on intersections. This is a very quick way to make a route map.

I wanted to expand on his work in two ways:

  1. I wanted to fill in waypoint names for items that were left blank, and
  2. I wanted to automatically check for waypoint name duplication, because duplicates cause data to be overwritten in Etrex GPS devices.

I wrote a python script that does both things. All one has to do now is:

  1. Create a map on his website
  2. Name major waypoints on the map (I like to name intersections using the name of the street I’ll be turning on to)
  3. Save the GPX file to disk
  4. Run gpxrecode.py [inputfile] [outputfile]

gpxrecode removes the leading number and hyphen from the waypoint name. If the waypoint is blank it gives it the same name as the last one. If the waypoint is already in a cache of waypoints, it adds a number to the end of the name. The cache is maintained between runs of the program, so multiple routes shouldn’t clobber each-other’s names.

Source after the break.

#!/usr/bin/python

#take a gpx xml file from http://www.marengo-ltd.com/map/ and recode it to fill out blanks
#and remove numbers from beginning.  Eventually use an sqlite db or something to check for dupes over all routes

import string
import pickle
import os,os.path,sys
import math

import xml.sax
from xml.sax.saxutils import XMLFilterBase,  XMLGenerator

waypoints = []

NAME=0
LAT=1
LON=2

class gpxfilter(XMLFilterBase):
   def __init__(self, In, Out):
      XMLFilterBase.__init__(self, In)
      self.element_stack = []
      self.Out = Out
      self.last_waypoint = None

   def startElement(self, name, attrs):
      self.Out.characters("\\n"+"   "*len(self.element_stack))
      self.Out.startElement(name, attrs)
      if not attrs.has_key('lat'):
         self.element_stack.append([name,None, None])
      else:
         self.element_stack.append([name,attrs['lat'],attrs['lon']])
      
   def characters(self, content):
      if len(content)==0:
         return
      white=True
      for c in content:
         if not isWhitespace(c):
            white=False
            break
      if white:
         return
         
      if self.element_stack[-1][NAME] == "name":
         name = content
         
         if name.isdigit():
            name = self.last_waypoint
         
         split = name.split('-')
         if len(split) == 2 and split[0].isdigit() and len(split[1])>0: #get rid of 03-BLAH
            name = split[1]
         
         if len(name)>6:
            name = name[0:6]

         self.last_waypoint = name
         basename = name
      
         match_index = -1
         
         #first try original name
         #if name is unique, add it to cache and break
         #else if coords are close, break (use dupe name)
         #else loop (try new name)

         found = False
         for i in range(0,9999):
            if i>0:
               name = basename[0:6-len(str(i))]+str(i)
            try:
               match_index = [n[NAME] for n in waypoints].index(name)
            except: #didn't find a match
               if self.element_stack[-2][LAT] is not None:
                  waypoints.append([name,self.element_stack[-2][LAT],self.element_stack[-2][LON]])
               found = True
               break
            
            diff_lat = abs(float(self.element_stack[-2][LAT]) - float(waypoints[match_index][LAT]))
            diff_lon = abs(float(self.element_stack[-2][LON]) - float(waypoints[match_index][LON]))
            if diff_lat < = .0003 and diff_lon <= .0003:
               found = True
               break
         if not found:
            print "ran out of tries??? (should never happen)"
         self.Out.characters(name)
   
   def endElement(self, name):
      if name != self.element_stack[-1][NAME]:
         print "ERROR expected "+str(self.element_stack[-1])[0]+" got "+str(name)
      self.element_stack.pop(-1)
      self.Out.endElement(name)
      self.Out.characters("\\n"+"   "*(len(self.element_stack)-1))
      
def isWhitespace(s):
   """Is 's' a single character and whitespace?"""
   if s in string.whitespace:
      return 1
   return 0
      
      
if __name__ == "__main__":
   if len(sys.argv) < 3:
      print "need input file and output file"
      sys.exit(1)
   try:
      #try to load waypoint cache
      f = open(os.getenv('HOME')+"/.gpswaypoints","r")
      contrib,waypoints = pickle.load(f)
   except:
      contrib = []
      waypoints = []
   try:
      out = open(sys.argv[2],"w")
   except:
      print "couldn't open output file"
   In = xml.sax.make_parser()
   Out = XMLGenerator(out)
   filter_handler = gpxfilter(In, Out)
   filter_handler.parse(sys.argv[1])
   f = open(os.getenv('HOME')+"/.gpswaypoints","w")
   #save the cache
   if os.path.split(sys.argv[1])[1] not in contrib:
      contrib = contrib+[os.path.split(sys.argv[1])[1]]
   pickle.dump([contrib,waypoints],f)

One thought on “Creating GPS route maps from google maps”

  1. Hi Owen, thanks for linking to my HOWTO, I don't mind you linking directly to the main site (at http://www.marengo-ltd.com/map), the discussion site is more for feedback if anyone's got anything.

    P.S, my name's Martyn Davis, the h.h.monro thing was an attempt to obfuscate the email address - obviously worked too well !!!

Comments are closed.