def __init__(self, x0, y0, x1, y1): utils.Assert(IsNumber(x0)) utils.Assert(IsNumber(y0)) utils.Assert(IsNumber(x1)) utils.Assert(IsNumber(y1)) self.x0 = x0 self.y0 = y0 self.x1 = x1 self.y1 = y1
def CalcZoomLevel(log_extent, total_log_extent, pixel_extent): """Calculates zoom level. We want a zoom level that has enough detail to match the user's request (i.e., we do our best not to give stretched-out pixels). A bigger zoom == more pixels + detail. But, it would be wasteful to use a higher zoom level than necessary. Args: log_extent: map-space width, height. total_log_extent: total defined map-space extent (size of projection bounds, in its output coordinates). pixel_extent: desired width, height of the final pixel image. Returns: zoom level with at least as much detail as required. """ utils.Assert(isinstance(log_extent, geom.Pair)) utils.Assert(isinstance(total_log_extent, geom.Pair)) utils.Assert(isinstance(pixel_extent, geom.Pair)) # Simple, component-wise division. fraction_of_total = log_extent / total_log_extent total_pixel_extent_needed = pixel_extent / fraction_of_total logger.debug("Pixel extent needed %s", str(total_pixel_extent_needed)) # We want to round the zoom level up; ie, have /at least/ 1 tile pixel # per requested pixel. # 256 x 2^zoom >= totalpixelextent => zoom = log_2(totalpixelextent / 256) x_zoom = int( math.log( math.ceil(total_pixel_extent_needed.x / float(_TILE_PIXEL_SIZE)), 2)) y_zoom = int( math.log( math.ceil(total_pixel_extent_needed.y / float(_TILE_PIXEL_SIZE)), 2)) logger.debug("Zoom:x,y %d, %d", x_zoom, y_zoom) zoom = max(x_zoom, y_zoom) if _MAX_ZOOM < zoom: logger.warning( "WHOA; wild zoom (%d - should be max %d), " "alert Google. Limiting to %d.", zoom, _MAX_ZOOM, _MAX_ZOOM) zoom = _MAX_ZOOM return zoom
def AddImage(self, col, row, image): """Add an image to this 2D array. Args: col: x [0..ncols) row: y [0..nrows) image: the 256x256 tile image to add. """ if not isinstance(image, Image.Image): logger.error("Image parameter is not of type Image.Image") if not (0 <= col and col < self.ncols): logger.error("Column %d out of bounds: 0 - %d", col, self.ncols) utils.Assert(False) if not (0 <= row and row < self.nrows): logger.error("Row %d out of bounds: 0 - %d", row, self.nrows) utils.Assert(False) self.images[row][col] = image
def CalcTileRects(proj, bbox, zoom_level): """Tile calculations. Args: proj: the projection to use. bbox: is BBOX, ie desired rectangle in map, aka logical coordinates. zoom_level: The zoom level the tiles must come from. For the server. Returns: tilepixel_rect: The desired tile pixel space rect of our image gotten from the server, at a given zoom level. Unscaled; still needs to be shrunk (most of the time) to fit the final image. rect_of_tiles: <tilepixel_rect> 'rounded out' to whole tiles. There's one tile address extra at both the right, and down, for cleaner loops. """ utils.Assert(isinstance(bbox, geom.Rect), 'bbox not Rect') total_log_rect = proj.internalLogOuterBounds() total_tilepixel_length = TotalTilepixelExtent(zoom_level) # Is exclusive total_tilepixel_rect = geom.Rect(0, 0, total_tilepixel_length, total_tilepixel_length) # flip log because the corners have to correspond, and tiles' 0,0 is # upper left, log's -max, -max is lower left. mapping = xform.WindowViewportMapping( total_log_rect, geom.Rect.fromLlAndExtent( total_tilepixel_rect.xy0, # We leave this exclusive, here, because that acts more like # an 'ideal', real-valued rectangle, as our logical rect is # pretty much 'ideal' at 4e7 pixels. It matters with # more-zoomed-out, -> smaller tilepixel spaces. total_tilepixel_rect.Extent()).flippedVertically()) utils.logger.debug('bbox: %s' % str(bbox)) tilepixel_rect = mapping.LogRectToPhys(bbox).asInts() # TODO: eliminate double flipping. if tilepixel_rect.isFlippedVertically(): tilepixel_rect = tilepixel_rect.flippedVertically() utils.logger.debug('tilepixel_rect: %s' % str(tilepixel_rect)) # A pixel will land somewhere within /some/ tile, and that's # the tile we want. rect_of_tiles = tilepixel_rect / 256 utils.logger.debug('rect_of_tiles %s' % str(rect_of_tiles)) # Tiles and pixels are all 'graphics space', ie origin is upper-left. # Make the rects exclusive on the lower right, for looping convenience. rect_of_tiles.x1 += 1 rect_of_tiles.y1 += 1 utils.logger.debug('high-exclusive rect_of_tiles %s' % str(rect_of_tiles)) return tilepixel_rect, rect_of_tiles
def LogXYFromlonLat(self, lonlat): """Projects the lon, lat point, in degrees, to logical. Args: lonlat: Pair of lon, lat to be projected. Returns: Logical point projected from lon, lat. """ utils.Assert(isinstance(lonlat, geom.Pair)) return geom.Pair(self.MercX(lonlat.x), self.MercY(lonlat.y))
def LonLatFromLogXY(self, log_xy): """Inverse projection; log_x/y are scaled to planet_radius. Args: log_xy: logical point to be 'inverted' (ie find lon, lat). Returns: Pair of lon, lat. """ utils.Assert(isinstance(log_xy, geom.Pair)) return geom.Pair(self._Lon(log_xy.x), self._Lat(log_xy.y))
def LonLatFromLogXY(self, log_xy): """Inverse projection from the Mercator map coordinates. Args: log_xy: Pair- point of logical space to be reverse-projected back into a lon,lat. Returns: A Pair of the resulting lon, lat. """ utils.Assert(isinstance(log_xy, geom.Pair)) return geom.Pair(self._Lon(log_xy.x), self._Lat(log_xy.y))
def TotalTilepixelExtent(zoom): """Number of pixels on a side, at a given zoom. Args: zoom: zoom level. Returns: Whole tilepixel space extent for this zoom level. Height == width so, just one number is returned. """ utils.Assert(geom.IsNumber(zoom)) total = 256 * (2**zoom) utils.logger.debug('total pixels:%s' % str(total)) return total
def TotalTilepixelExtent(zoom): """Number of pixels on a side, at a given zoom. Args: zoom: zoom level. Returns: Whole tilepixel space extent for this zoom level. Height == width so, just one number is returned. """ utils.Assert(geom.IsNumber(zoom)) total = _TILE_PIXEL_SIZE * (2**zoom) logger.debug("Total pixels:%d", total) return total
def LogPtToPhys(self, log_pt): """Find tilepixel space point, of <log_pt>. Args: log_pt: window point. Returns: Corresponding viewport, for us tilepixel space, point. """ utils.Assert(isinstance(log_pt, geom.Pair), "logpt is not a geom.Pair") phys_x = (log_pt.x * self._logical_to_physical_x_scale + self._physical_x_offset) phys_y = (log_pt.y * self._logical_to_physical_y_scale + self._physical_y_offset) return geom.Pair(phys_x, phys_y)