#!/usr/bin/python
# -*- coding: utf-8 -*-
"""The Buxus Canvas: an API for drawing maps.
I don’t know much about maps, but I want some, so I thought I’d write
a thing.
See main() for an example.
"""
import sys
# line caps and joins
ROUND = object()
BUTT = object()
MITER = object()
def main():
canvas = create((80, 20, 81.2, 23)) # Set map projection in degrees.
canvas.newpath()
canvas.moveto(80.01, 21) # lat, lon — not x, y
canvas.lineto(81.0, 23)
canvas.setlinewidth(4)
canvas.setlinecap(ROUND)
canvas.stroke()
canvas.setlinewidth(2)
canvas.setcolor(rgb(128, 128, 128))
canvas.stroke()
canvas.write_svg(sys.stdout, 500, 500)
def create(bbox):
return Buxcanvas(bbox)
class Buxcanvas:
def __init__(self, bbox):
self.bbox = bbox
self.things = []
self._currentpath = None
self._linewidth = 1
self._linecap = ROUND
self._color = rgb(0, 0, 0)
def newpath(self):
self._currentpath = []
def moveto(self, lat, lon):
self._currentpath.append([])
self._currentpath[-1].append((lat, lon))
def lineto(self, lat, lon):
self._currentpath[-1].append((lat, lon))
def setlinewidth(self, n):
self._linewidth = n
def setlinecap(self, cap_type):
assert cap_type in [BUTT, ROUND]
self._linecap = cap_type
def setcolor(self, color):
self._color = color
def stroke(self):
self.things.append(Stroke(self._currentpath,
self._linewidth,
self._linecap,
self._color))
def write_svg(self, filehandle, width, height):
filehandle.write('\n')
def write_ppm(self, filehandle, width, height):
pass
def rgb(r, g, b):
return r, g, b
def svg_color((r, g, b)):
return '#%02x%02x%02x' % (r, g, b)
class Stroke:
def __init__(self, path, linewidth, linecap, color):
self.path = path
self.linewidth = linewidth
self.linecap = linecap
self.color = color
def write_svg(self, filehandle, projection):
filehandle.write("\n" % (self.svg_path(projection),
self.svg_linewidth(),
self.svg_linecap(),
self.svg_color()))
def svg_path(self, projection):
return ' d="%s"' % ' '.join("M %d %d" % projection.project(coords)
if i == 0 else
"L %d %d" % projection.project(coords)
for polygon in self.path
for i, coords in enumerate(polygon))
def svg_linewidth(self):
return ('' if self.linewidth == 1 else
' stroke-width="%dpx"' % self.linewidth
)
def svg_linecap(self):
return ('' if self.linecap == BUTT else
' stroke-linecap="round"'
)
def svg_color(self):
# no stroke is default for SVG?
return ' stroke="%s"' % svg_color(self.color)
class Projection:
def __init__(self, bbox, width, height):
self.lat_min, self.lon_min, self.lat_max, self.lon_max = bbox
self.width = width
self.height = height
def project(self, (lat, lon)):
# XXX this is bogus because projection.
return (self.width * ((float(lon) - self.lon_min)
/ (self.lon_max - self.lon_min)),
self.height * ((float(lat) - self.lat_min)
/ (self.lat_max - self.lat_min)))
if __name__ == '__main__':
main()