source: subversion/applications/rendering/party/render.py @ 4990

Last change on this file since 4990 was 4990, checked in by ojw, 12 years ago

Auto-generate colour palette

File size: 4.9 KB
Line 
1# GPX Party renderer
2# Takes a directory structure full of GPX file tracklogs,
3# and renders them all to a single image
4#
5# Copyright 2007, Oliver White
6# Licensed as GNU GPL version3 or at your option any later version
7#
8# Usage: python render.py
9import cairo
10import math
11import sys
12import getopt
13import colorsys
14from xml.sax import saxutils
15from UserDict import UserDict
16from xml.sax import make_parser
17import os
18from os.path import join, getsize
19deg2rad = 0.0174532925
20M_PI = 3.1415926535
21
22class Palette:
23  def __init__(self, size):
24    size = max(size,1.0)
25    self.h = 0.0
26    self.dh = 1.0/(size + 1.0)
27    self.s = 1.0
28    self.v = 0.8
29  def get(self):
30    colour = colorsys.hsv_to_rgb(self.h, self.s, self.v)
31    self.h = self.h + self.dh
32    if(self.h > 1.0):
33      self.h = 0.0
34    return(colour)
35   
36class TracklogInfo(saxutils.DefaultHandler):
37  def __init__(self):
38    self.count = 0
39    self.countPoints = 0
40    self.points = {}
41    self.currentFile = ''
42  def walkDir(self, directory):
43    for root, dirs, files in os.walk(directory):
44      for file in files:
45        if(file.endswith(".gpx")):
46          print file
47          self.currentFile = join(root, file)
48          self.points[self.currentFile] = []
49          parser = make_parser()
50          parser.setContentHandler(self)
51          parser.parse(self.currentFile)
52          self.count = self.count + 1
53    self.currentFile = ''
54    if(self.countPoints == 0):
55      print "No GPX files found"
56      sys.exit()
57    print "Read %d points in %d files" % (self.countPoints,self.count)
58  def startElement(self, name, attrs):
59    if(name == 'trkpt'):
60      lat = float(attrs.get('lat', None))
61      lon = float(attrs.get('lon', None))
62      self.points[self.currentFile].append((lat,lon));
63      self.countPoints = self.countPoints + 1
64  def valid(self):
65    if(self.ratio == 0.0):
66      return(0)
67    if(self.dLat <= 0.0 or self.dLon <= 0):
68      return(0)
69    return(1)
70  def calculate(self, radius):
71    # Calculate mean and standard deviation
72    self.calculateCentre()
73    self.calculateExtents(radius)
74    # then use that to calculate extents
75    self.N = self.lat + self.sdLat
76    self.E = self.lon + self.sdLon
77    self.S = self.lat - self.sdLat
78    self.W = self.lon - self.sdLon
79    self.dLat = self.N - self.S
80    self.dLon = self.E - self.W
81    self.ratio = self.dLon / self.dLat
82    self.debug()
83  def calculateCentre(self):
84    sumLat = 0
85    sumLon = 0
86    for x in self.points.values():
87      for y in x:
88        sumLat = sumLat + y[0]
89        sumLon = sumLon + y[1]
90    self.lat = sumLat / self.countPoints
91    self.lon = sumLon / self.countPoints
92   
93  def calculateExtents(self,radius):
94    c = 40000.0 # circumference of earth, km
95    self.sdLat = (radius / (c / M_PI)) / deg2rad
96    self.sdLon = self.sdLat / math.cos(self.lat * deg2rad)
97    pass
98  def debug(self):
99    print "%d points" % self.countPoints
100    print "Pos: %f, %f +- %f, %f" % (self.lat,self.lon, self.sdLat,self.sdLon)
101    print "Lat %f to %f, Long %f to %f" % (self.S,self.N,self.W,self.E)
102    print "Ratio: %f" % self.ratio
103  def createImage(self,width,height,surface):
104    self.width = width
105    self.height = height
106    self.surface = surface
107    self.drawBorder()
108  def drawBorder(self):
109    border=5
110    ctx = cairo.Context(surface)
111    ctx.set_source_rgb(0,0,0)
112    ctx.rectangle(border,border,self.width-2*border, self.height-2*border)
113    ctx.stroke()
114  def drawTracklogs(self, pointsize):
115    self.palette = Palette(self.count)
116    ctx = cairo.Context(surface)
117    for a in self.points.values():
118      colour = self.palette.get()
119      ctx.set_source_rgb(colour[0],colour[1],colour[2])
120      for b in a:
121        x = self.xpos(b[1])
122        y = self.ypos(b[0])
123        ctx.arc(x, y, pointsize, 0, 2*M_PI)
124        ctx.fill()
125  def xpos(self,lon):
126    return(self.width * (lon - self.W) / self.dLon)
127  def ypos(self,lat):
128    return(self.height * (1 - (lat - self.S) / self.dLat))
129
130Thingy = TracklogInfo()
131
132# Handle command-line options
133opts, args = getopt.getopt(sys.argv[1:], "hs:d:r:p:", ["help=", "size=", "dir=", "radius=","pointsize="])
134# Defauts:
135directory = "./"
136size = 600
137radius = 10 # km
138pointsize = 1 # mm
139# Options:
140for o, a in opts:
141  if o in ("-h", "--help"):
142    print "Usage: render.py -d [directory] -s [size,pixel] -r [radius,km]"
143    sys.exit()
144  if o in ("-d", "--dir"):
145    directory = a
146  if o in ("-s", "--size"):
147    size = int(a)
148  if o in ("-r", "--radius"):
149    radius = float(a)
150  if o in ("-p", "--pointsize"):
151    pointsize = float(a)
152
153print "Loading data"
154Thingy.walkDir(directory)
155print "Calculating extents"
156Thingy.calculate(radius)
157if(not Thingy.valid):
158  print "Couldn't calculate extents"
159  sys.exit()
160width = size
161height = int(width / Thingy.ratio)
162print "Creating image %d x %d" % (width,height)
163
164surface = cairo.ImageSurface(cairo.FORMAT_ARGB32, width, height)
165Thingy.createImage(width, height, surface)
166Thingy.drawTracklogs(pointsize)
167
168surface.write_to_png("output.png")
169
Note: See TracBrowser for help on using the repository browser.