1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """
22 Pixel-size aware bounding box and its derivatives.
23 """
24 import math
25
26 from sabx10.oxm import Box, Point
27
28 from consts import HEIGHT, WIDTH, PPI
29
31 """
32 Bounding box that understands pixel sizes in addition to the basic lat/lon
33 sizes. You need this because a Mapnik map is rendered into a PNG file,
34 which has its size defined in terms of pixels, while the map data is
35 represented in terms of lat/lon co-ordinates.
36
37 @ivar pix_width: width of box in pixels
38 @type pix_width: C{float}
39 @ivar pix_height: height of box in pixels
40 @type pix_height: C{float}
41 """
42 - def __init__(self, min_lat=200.0, min_lon=200.0,
43 max_lat=-200.0, max_lon=-200.0, box=None):
44 """
45 Initialize the bounds, either from explicit lat/lon coordinates, or
46 based on the size of a given Box.
47
48 @param min_lat: minimum latitude of box
49 @type min_lat: C{float}
50 @param min_lon: minimum longitude of box
51 @type min_lon: C{float}
52 @param max_lat: maximum latitude of box
53 @type max_lat: C{float}
54 @param max_lon: maximum longitude of box
55 @type max_lon: C{float}
56 @param box: example box to base this one on
57 @type box: L{Box}
58 """
59 if box is not None:
60 Box.__init__(self, box.min_lat, box.min_lon,
61 box.max_lat, box.max_lon)
62 else:
63 Box.__init__(self, min_lat, min_lon, max_lat, max_lon)
64 self.pix_width = 0
65 self.pix_height = 0
66
68 """
69 Resize the bounding box so that the width and height have the correct
70 aspect ratio, and so that the box is a certain percent bigger than the
71 map indicates. This way any icons and street names drawn near the edge
72 of the map won't get clipped.
73
74 @param perc: percentage to expand map (0.xx format)
75 @type perc: C{float}
76 """
77 width = self.width()
78 height = self.height()
79
80 if (self.pix_height / self.pix_width) > (height / width):
81 height = (self.pix_height / self.pix_width) * width
82 elif (self.pix_width / self.pix_height) > (width / height):
83 width = (self.pix_width / self.pix_height) * height
84
85 height *= 1.0 + perc
86 width *= 1.0 + perc
87
88 self.resize(width, height)
89
91 """
92 Set the pixel width and height of this box to the desired width and
93 height based on whether the map is taller that it's wide, or wider than
94 it's tall.
95 """
96 if self.width() > self.height():
97 self.pix_width = HEIGHT * PPI
98 self.pix_height = WIDTH * PPI
99 else:
100 self.pix_width = WIDTH * PPI
101 self.pix_height = HEIGHT * PPI
102
104 """
105 Calculate how far apart two points in this bounding box need to be so
106 that their icons won't overlap.
107
108 @param pix_icon_width: pixel width of icon being considered
109 @type pix_icon_width: C{float}
110 @param pix_icon_height: pixel height of icon being considered
111 @type pix_icon_height: C{float}
112
113 @return: distance apart, in statute miles, the icons must be
114 @rtype: C{float}
115 """
116 buf_width = (self.width() / self.pix_width) * pix_icon_width
117 buf_height = (self.height() / self.pix_height) * pix_icon_height
118 buf_dist = math.hypot(buf_width, buf_height)
119
120 return buf_dist
121
123 """
124 Keeps track of a cluster of points and the bounding box surrounding them.
125
126 @ivar center: L{Point} denoting center of the box
127 @type center: L{Point}
128 @ivar point_count: how many points this box encompasses
129 @type point_count: C{int}
130 @ivar index: index of this Cluster
131 @type index: C{int}
132 """
134 """
135 The bounding box starts out as just a single point.
136
137 @param lat: latitude of starting point
138 @type lat: C{float}
139 @param lon: longitude of starting point
140 @type lon: C{float}
141 """
142 Bounds.__init__(self, lat, lon, lat, lon)
143 Point.__init__(self, lat, lon)
144 self.point_count = 1
145 self.index = 0
146
148 """
149 Expand the bounding box to include the given point, and include the new
150 point in the point count.
151
152 @param lat: latitude of new point
153 @type lat: C{float}
154 @param lon: longitude of new point
155 @type lon: C{float}
156 """
157 Box.expand_to_point(self, lat, lon)
158 self.lat = (self.min_lat + self.max_lat) / 2.0
159 self.lon = (self.min_lon + self.max_lon) / 2.0
160 self.point_count += 1
161
163 """
164 How far away from the center of this box is the given point?
165
166 @param pt: L{Point} to check against
167 @type pt: L{Point}
168 """
169 return self.calculate_distance(pt)
170
172 """
173 Adjust the size of the cluster's bounding box and update it's center
174 point to reflect this. A L{Cluster}'s bounding box size is simply half
175 the width and half the height of the overall desired width and height.
176 This is because clustered points are drawn as simple PNGs that should
177 fit four on a page in a grid, and half width and half height yield a
178 quarter of the area of the full page.
179 """
180 self.pix_width = int((WIDTH * PPI) / 2)
181 self.pix_height = int((HEIGHT * PPI) / 2)
182