Package sabx10 :: Package osm :: Module legend
[hide private]
[frames] | no frames]

Source Code for Module sabx10.osm.legend

  1  ############################################################################### 
  2  # 
  3  # sabx10 - an SABX file manipulation library 
  4  # Copyright (C) 2009, 2010 Jay Farrimond (jay@sabikerides.com) 
  5  # 
  6  # This file is part of sabx10. 
  7  # 
  8  # sabx10 is free software: you can redistribute it and/or modify it under the 
  9  # terms of the GNU General Public License as published by the Free Software 
 10  # Foundation, either version 3 of the License, or (at your option) any later 
 11  # version. 
 12  # 
 13  # sabx10 is distributed in the hope that it will be useful, but WITHOUT ANY 
 14  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 15  # A PARTICULAR PURPOSE.  See the GNU General Public License for more details. 
 16  # 
 17  # You should have received a copy of the GNU General Public License along with 
 18  # sabx10.  If not, see <http://www.gnu.org/licenses/>. 
 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   
32 -def _calc_box_lats(ride, legend_height):
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 # bottom 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 # mid 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 # top 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
68 -def _calc_box_lons(ride, legend_width):
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 # left 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 # mid 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 # right 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
104 -def _create_legend_boxes(ride, legend_width, legend_height):
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), # top 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), # middle 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), # bottom 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
141 -def _check_boxes_for_point(boxes, lat, lon):
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
157 -def _check_boxes(boxes, ride):
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
180 -def _find_legend_box(ride, legend_width, legend_height):
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
199 -class LegendNode(object):
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
230 -def _gen_legend_item(lat, lon, text, dist, node_id):
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
255 -def generate_legend(the_ride, title, node_id):
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 #the_ride.logo_node, lat, lon = \ 276 # _gen_legend_item(lat, lon, '', 277 # height_of_pix * 47 * PIX_SCALE_FACTOR, node_id) 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