1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 """
22 Generate and place the map legend. Look for a space in the map that doesn't
23 have any route items on it. Cheat by splitting the map into 9 squares (3x3)
24 and check each square for ride items intersecting it. Choose the square with
25 the least intersections, or an arbitrary one if there is a tie.
26 """
27
28 from sabx10.oxm import Box, pt_dist_from_pt
29
30 from consts import PIX_SCALE_FACTOR
31
33 """
34 Split the map into three equal pieces going top to bottom.
35
36 @param ride: ride representing map to split
37 @type ride: L{Ride}
38 @param legend_height: height of legend
39 @type legend_height: C{float}
40
41 @return: bottom min, bottom max, middle min, middle max, top min, top max
42 @rtype: C{float},C{float},C{float},C{float},C{float},C{float}
43 """
44
45 bottom_lat_min = ride.bounds.min_lat
46 bottom_lat_max, lon = pt_dist_from_pt(ride.bounds.min_lat,
47 ride.bounds.min_lon,
48 legend_height, 0)
49
50
51 mid_lat = (ride.bounds.min_lat + ride.bounds.max_lat) / 2.0
52 mid_dist = legend_height / 2.0
53 mid_lat_min, lon = pt_dist_from_pt(mid_lat, ride.bounds.min_lon,
54 mid_dist, 180)
55 mid_lat_max, lon = pt_dist_from_pt(mid_lat, ride.bounds.min_lon,
56 mid_dist, 0)
57
58
59 top_lat_min, lon = pt_dist_from_pt(ride.bounds.max_lat,
60 ride.bounds.max_lon,
61 legend_height, 180)
62 top_lat_max = ride.bounds.max_lat
63
64 return bottom_lat_min, bottom_lat_max, \
65 mid_lat_min, mid_lat_max, \
66 top_lat_min, top_lat_max
67
69 """
70 Split the map into three equal pieces going left to right.
71
72 @param ride: ride representing map to split
73 @type ride: L{Ride}
74 @param legend_width: width of legend
75 @type legend_width: C{float}
76
77 @return: left min, left max, middle min, middle max, right min, right max
78 @rtype: C{float},C{float},C{float},C{float},C{float},C{float}
79 """
80
81 left_lon_min = ride.bounds.min_lon
82 lat, left_lon_max = pt_dist_from_pt(ride.bounds.min_lat,
83 ride.bounds.min_lon,
84 legend_width, 90)
85
86
87 mid_lon = (float(ride.bounds.min_lon) + float(ride.bounds.max_lon)) / 2.0
88 mid_dist = legend_width / 2.0
89 lat, mid_lon_min = pt_dist_from_pt(ride.bounds.min_lat, mid_lon,
90 mid_dist, 270)
91 lat, mid_lon_max = pt_dist_from_pt(ride.bounds.min_lat, mid_lon,
92 mid_dist, 90)
93
94
95 lat, right_lon_min = pt_dist_from_pt(ride.bounds.max_lat,
96 ride.bounds.max_lon,
97 legend_width, 270)
98 right_lon_max = ride.bounds.max_lon
99
100 return left_lon_min, left_lon_max, \
101 mid_lon_min, mid_lon_max, \
102 right_lon_min, right_lon_max
103
105 """
106 Create the boxes to check against. This is a 3x3 grid splitting the map
107 into equal spaces.
108
109 @param ride: ride representing map to split
110 @type ride: L{Ride}
111 @param legend_width: width of legend
112 @type legend_width: C{float}
113 @param legend_height: height of legend
114 @type legend_height: C{float}
115
116 @return: C{list} of boxes
117 @rtype: C{list} of C{Box}
118 """
119 bottom_lat_min, bottom_lat_max, mid_lat_min, mid_lat_max, \
120 top_lat_min, top_lat_max = _calc_box_lats(ride, legend_height)
121
122 left_lon_min, left_lon_max, mid_lon_min, mid_lon_max, \
123 right_lon_min, right_lon_max = _calc_box_lons(ride, legend_width)
124
125 boxes = [Box(top_lat_min, left_lon_min, top_lat_max, left_lon_max),
126 Box(top_lat_min, mid_lon_min, top_lat_max, mid_lon_max),
127 Box(top_lat_min, right_lon_min, top_lat_max, right_lon_max),
128
129 Box(mid_lat_min, left_lon_min, mid_lat_max, left_lon_max),
130 Box(mid_lat_min, mid_lon_min, mid_lat_max, mid_lon_max),
131 Box(mid_lat_min, right_lon_min, mid_lat_max, right_lon_max),
132
133 Box(bottom_lat_min, left_lon_min, bottom_lat_max, left_lon_max),
134 Box(bottom_lat_min, mid_lon_min, bottom_lat_max, mid_lon_max),
135 Box(bottom_lat_min, right_lon_min, bottom_lat_max, right_lon_max)]
136 for the_box in boxes:
137 the_box.count = 0
138
139 return boxes
140
142 """
143 Check the list of boxes to see which ones the point is in. It really
144 should only be in one box since we divided the map evenly.
145
146 @param boxes: C{list} of boxes to check
147 @type boxes: C{list} of L{Box}
148 @param lat: latitude of point to check
149 @type lat: C{float}
150 @param lon: longitude of point to check
151 @type lon: C{float}
152 """
153 for the_box in boxes:
154 if the_box.is_pt_in_box(lat, lon):
155 the_box.count += 1
156
158 """
159 Check the list of boxes against all the point items in the ride. This
160 includes segment waypoints, stops, pois, and parking.
161
162 @param boxes: C{list} of boxes to check
163 @type boxes: C{list} of L{Box}
164 @param ride: ride representing map to check
165 @type ride: L{Ride}
166 """
167 _check_boxes_for_point(boxes,
168 ride.parking.lat, ride.parking.lon)
169
170 for seg in ride.segs:
171 for pt in seg.waypoints:
172 _check_boxes_for_point(boxes, pt.lat, pt.lon)
173
174 for stop in ride.stops:
175 _check_boxes_for_point(boxes, stop.lat, stop.lon)
176
177 for poi in ride.pois:
178 _check_boxes_for_point(boxes, poi.lat, poi.lon)
179
181 """
182 Check the map for a space to place the legend.
183
184 @param ride: ride representing map to check
185 @type ride: L{Ride}
186 @param legend_width: width of legend
187 @type legend_width: C{float}
188 @param legend_height: height of legend
189 @type legend_height: C{float}
190
191 @return: C{list} of boxes in order of goodness
192 @rtype: C{list} of L{Box}
193 """
194 boxes = _create_legend_boxes(ride, legend_width, legend_height)
195 _check_boxes(boxes, ride)
196 boxes.sort(key=lambda x: x.count)
197 return boxes[0]
198
200 """
201 Holds one item in the legend, one item per legend line.
202
203 @ivar id: id of item
204 @type id: C{int}
205 @ivar text: text of item
206 @type text: C{string}
207 @ivar lat: latitude of text top
208 @type lat: C{float}
209 @ivar lon: longitude of text left
210 @type lon: C{float}
211 """
212 - def __init__(self, id, text, lat, lon):
213 """
214 Save the passed-in values.
215
216 @param id: id of item
217 @type id: C{int}
218 @param text: text of item
219 @type text: C{string}
220 @param lat: latitude of text top
221 @type lat: C{float}
222 @param lon: longitude of text left
223 @type lon: C{float}
224 """
225 self.id = id
226 self.text = text
227 self.lat = lat
228 self.lon = lon
229
231 """
232 Generate a L{LegendNode}, and calculate a point below it. This allows us
233 to generate the items and "walk" them down the page in the process. We
234 end-up with legend items on successive lines on the map page.
235
236 @param lat: latitude of legend item
237 @type lat: C{float}
238 @param lon: longitude of legend item
239 @type lon: C{float}
240 @param text: text of legend item
241 @type text: C{string}
242 @param dist: distance down page for next item
243 @type dist: C{float}
244 @param node_id: L{NodeId} to generate id from
245 @type node_id: L{NodeId}
246
247 @return: L{LegendNode}, lat of next legend item, lon of next legend item
248 @rtype: L{LegendNode},C{float},C{float}
249 """
250 node = LegendNode(node_id.next(), text, lat, lon)
251 lat, lon = pt_dist_from_pt(lat, lon, dist, 180)
252
253 return node, lat, lon
254
256 """
257 Generate the legend items for the map for the given ride.
258
259 @param the_ride: L{Ride} to generate the legend for
260 @type the_ride: L{Ride}
261 @param title: title for the map
262 @type title: C{string}
263 @param node_id: L{NodeId} to generate legend node ids from
264 @type node_id: L{NodeId}
265 """
266 width_of_pix = the_ride.bounds.width() / the_ride.bounds.pix_width
267 height_of_pix = the_ride.bounds.height() / the_ride.bounds.pix_height
268
269 legend_width = 200.0 * width_of_pix * PIX_SCALE_FACTOR
270 legend_height = 200.0 * height_of_pix * PIX_SCALE_FACTOR
271 box = _find_legend_box(the_ride, legend_width, legend_height)
272
273 lat = box.max_lat
274 lon = (box.min_lon + box.max_lon) / 2.0
275
276
277
278 the_ride.title_node, lat, lon = \
279 _gen_legend_item(lat, lon, title,
280 height_of_pix * 20 * PIX_SCALE_FACTOR, node_id)
281 the_ride.dist_node, lat, lon = \
282 _gen_legend_item(lat, lon, "%.1f Mile Option" % the_ride.distance,
283 height_of_pix * 18 * PIX_SCALE_FACTOR, node_id)
284 the_ride.url_node, lat, lon = \
285 _gen_legend_item(lat, lon, 'www.sabikerides.com',
286 height_of_pix * 17 * PIX_SCALE_FACTOR, node_id)
287 the_ride.park_node, lat, lon = \
288 _gen_legend_item(lat, lon, 'parking in red',
289 height_of_pix * 17 * PIX_SCALE_FACTOR, node_id)
290 the_ride.turn_node, lat, lon = \
291 _gen_legend_item(lat, lon, 'numbered turns in green',
292 height_of_pix * 17 * PIX_SCALE_FACTOR, node_id)
293 the_ride.stop_node, lat, lon = \
294 _gen_legend_item(lat, lon, 'numbered stops in blue',
295 height_of_pix * 17 * PIX_SCALE_FACTOR, node_id)
296 the_ride.poi_node, lat, lon = \
297 _gen_legend_item(lat, lon, 'numbered points of interest in yellow',
298 height_of_pix * 17 * PIX_SCALE_FACTOR, node_id)
299