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

Source Code for Module sabx10.osm.bounds

  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  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   
30 -class Bounds(Box):
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
67 - def expand_to_good_size(self, perc):
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
90 - def set_pixel_size(self):
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
103 - def calc_sep_dist(self, pix_icon_width, pix_icon_height):
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
122 -class Cluster(Bounds, Point):
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 """
133 - def __init__(self, lat, lon):
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
147 - def expand_to_point(self, lat, lon):
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
162 - def distance_from(self, pt):
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
171 - def set_pixel_size(self):
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