#!/usr/bin/env python # GPX trimmer # Copyright (C) 2008 Steve Hill # # This program is free software: you can redistribute it and/or modify # it under the terms of version 3 of the GNU General Public License as # published by the Free Software Foundation. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . import sys import xml.sax import geolib class gpx_handler(xml.sax.handler.ContentHandler): def __init__(self, outfile, exclude_boxes): self.outfile = outfile self.exclude_boxes = exclude_boxes self.point = None self.stack = None def startElement(self, name, attributes): if (self.point == None): if (name == "trkseg"): self.segment = [] elif (name == "trkpt"): self.point = { "name": name, "attributes": attributes, "data": None, "nodes": [] } self.stack = [self.point] else: self.outfile.open(name, attributes) else: node = { "name": name, "attributes": attributes, "data": None, "nodes": [] } prevnode = self.stack[len(self.stack)-1] prevnode["nodes"].append(node) self.stack.append(node) def characters(self, data): if (self.point == None): return data = data.strip() if (data == ""): return node = self.stack[len(self.stack)-1] node["data"] = data def is_in_box(self,point,box): lat = float(point["attributes"]["lat"]) lon = float(point["attributes"]["lon"]) if (lat < box[0]): return False if (lon < box[1]): return False if (lat > box[2]): return False if (lon > box[3]): return False return True def trim_segment(self,points): outpoints = [] pointbuff = [] start_discard = 0 mid = 0 for p in points: in_box = False for b in exclude_boxes: if (self.is_in_box(p, b)): in_box = True break if (in_box): if (len(outpoints) <= 0): # Start of track - discard start_discard += 1 else: # Middle or end of track - buffer pointbuff.append(p) else: mid += len(pointbuff) outpoints.extend(pointbuff) pointbuff = [] outpoints.append(p) end_discard = len(pointbuff) if ((start_discard > 0) or (end_discard > 0) or (mid > 0)): print "Trimmed",start_discard,"points from start and",end_discard,"points from end. ",mid,"untrimmed points within the boxes." return outpoints def write_node(self,node): self.outfile.open(node["name"],node["attributes"]) for n in node["nodes"]: self.write_node(n) if (node["data"] != None): self.outfile.data(node["data"]) self.outfile.close(node["name"]) def write_segment(self,points): if (len(points) > 0): self.outfile.open("trkseg") for p in points: self.write_node(p) self.outfile.close("trkseg") print "Wrote segment with",len(points),"points" def endElement(self, name): if (self.point == None): if (name == "trkseg"): trimmed = self.trim_segment(self.segment) self.write_segment(trimmed) self.segment = None else: self.outfile.close(name) else: if (name == "trkpt"): self.segment.append(self.point) self.point = None self.stack = None else: self.stack.pop() args = sys.argv progname = args.pop(0) if (len(args) < 6): print "GPX trimmer" print "Removes the start and end of tracks if they are within a set of bounding boxes." print print "Usage:", progname, " [ ...]" sys.exit(1) infile = file(args.pop(0), "r") outfile = geolib.xmlindent(args.pop(0), False) exclude_boxes = [] while (len(args) >= 4): box = ( float(args.pop(0)), float(args.pop(0)), float(args.pop(0)), float(args.pop(0)) ) print "Removing track starts and ends within",(box[0],box[1]),"-",(box[2],box[3]) exclude_boxes.append(box) parser = xml.sax.make_parser() handler = gpx_handler(outfile, exclude_boxes) parser.setContentHandler(handler) parser.parse(infile) del infile del outfile