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 AdvertizedLogOuterBounds(self): """Outer mapping bounds of this projection, that the user sees. Returns: A Rect of the official, by-the-book logical space of the projection. """ xy0 = geom.Pair(self.FlatX(-Flat.MAX_LONGITUDE), self.FlatY(-Flat.MAX_LATITUDE)) xy1 = geom.Pair(self.FlatX(Flat.MAX_LONGITUDE), self.FlatY(Flat.MAX_LATITUDE)) return geom.Rect(xy0.x, xy0.y, xy1.x, xy1.y)
def _LogOuterBounds(self): """Outer bounds of this projection. As near as practical to the whole globe. Returns: The logical, map space over which the projection is defined. """ xy0 = geom.Pair(self.MercX(-Mercator.MAX_LONGITUDE), self.MercY(-Mercator.MAX_LATITUDE)) xy1 = geom.Pair(self.MercX(Mercator.MAX_LONGITUDE), self.MercY(Mercator.MAX_LATITUDE)) return geom.Rect(xy0.x, xy0.y, xy1.x, xy1.y)
def _CheckBbox(self, bbox_text): coords = bbox_text.split(',') if len(coords) != 4: self._serviceExceptionImpl(None, 'Expected 4 BBOX coordinates') user_log_rect = geom.Rect(float(coords[0]), float(coords[1]), float(coords[2]), float(coords[3])) # The requirement on relative sizes of the params in BBOX, even # the equality part, is per the spec. if user_log_rect.y1 <= user_log_rect.y0: self._serviceExceptionImpl(None, 'BBOX.ymax <= BBOX.ymin') if user_log_rect.x1 <= user_log_rect.x0: self._serviceExceptionImpl(None, 'BBOX.xmax <= BBOX.xmin')
def _DumpCenterInfo(total_tilepixel_length, user_width, user_height): # debugging - should be in the center of the tiles c_tilex = total_tilepixel_length / 2 c_tiley = total_tilepixel_length / 2 c_tilew = user_width / 2 c_tileh = user_height / 2 centered_tilepixel_rect = geom.Rect(c_tilex - c_tilew, c_tiley - c_tileh, c_tilex + c_tilew, c_tiley + c_tileh) utils.logger.debug('centered pixels would be: ~ %s' % str(centered_tilepixel_rect)) centered_rect = centered_tilepixel_rect / 256 utils.logger.debug('centered tile rect would be: ~ %s' % str(centered_rect)) centered_rect.x1 += 1 centered_rect.y1 += 1 utils.logger.debug('high-exclusive centered tile rect would be: ~ %s' % str(centered_rect))
def _ProcessCommon(self): """Processes the GetMapRequest parameters and prepares the GEE tile URL. Returns: Nothing, but might raise a ServiceException of the right version. """ utils.logger.debug('wms.GetMapRequest._processCommon') self._CheckParameters(self.parameters, self.required_param_names) server_layers_by_name = self.parameters['server-obj'].getLayers() utils.logger.debug('find the target layer') # We only handle a single layer at a time (jeffdonner is not sure # what handling multiple ones would mean - compositing them per # STYLES perhaps? Sending them in sequence, somehow?) # Here we don't balk or warn at a list, we just silently send the # first item. layer_name = WmsGetMapRequest._ExtractLayerName( utils.GetValue(self.parameters, 'layers')) # By this point we know it's there. self.requested_layer_obj = server_layers_by_name[layer_name] utils.logger.debug('target layer: ' + layer_name) bbox_text = utils.GetValue(self.parameters, 'bbox') coords = bbox_text.split(',') self.user_log_rect = geom.Rect(float(coords[0]), float(coords[1]), float(coords[2]), float(coords[3])) # We should by rights check that CRS/SRS matches the layer's, but # we don't because (at least in jeffdonner's experience with # 'uppermidwest.map') we have to let MapServer feed us ones we # don't support in order to work with it. Luckily MapServer # produces the right result despite this confusion. # TODO: nice-to-have: add format flexibility (raster -> # png, vectorrastermap -> jpg) since PIL can transform them. At # least, ImageMaps could be png. self.user_width = int(utils.GetValue(self.parameters, 'width')) self.user_height = int(utils.GetValue(self.parameters, 'height')) utils.logger.debug('done')
def InternalLogOuterBounds(self): """Outer mapping bounds of this projection that our transform sees. GE Map server 4.4 does not cover its entire tilespace with map data, instead it preserves the image aspect ratio (there's twice as much longitude space as latitude). The top and bottom 1/4 of the tilespace is empty / black. We correct for this by treating the tilespace as representing 360 x 360 instead of 360 x 180. Returns: Rect of this projection's outer mapping / logical bounds, possibly adapted to quirks of the underlying pixel space (as here with 4.4's flat mapping). """ xy0 = geom.Pair(self.FlatX(-Flat.MAX_LONGITUDE), self.FlatY(2 * -Flat.MAX_LATITUDE)) xy1 = geom.Pair(self.FlatX(Flat.MAX_LONGITUDE), self.FlatY(2 * Flat.MAX_LATITUDE)) return geom.Rect(xy0.x, xy0.y, xy1.x, xy1.y)
def _DumpCenterInfo(total_tilepixel_length, user_width, user_height): """Calculate the center info of the tile. Args: total_tilepixel_length: Total tile length. user_width: The user-requested width of the image. user_height: The user-requested height of the image. """ # debugging - should be in the center of the tiles c_tilex = total_tilepixel_length / 2 c_tiley = total_tilepixel_length / 2 c_tilew = user_width / 2 c_tileh = user_height / 2 centered_tilepixel_rect = geom.Rect(c_tilex - c_tilew, c_tiley - c_tileh, c_tilex + c_tilew, c_tiley + c_tileh) logger.debug("Centered pixels would be: ~ %s", str(centered_tilepixel_rect)) centered_rect = centered_tilepixel_rect / _TILE_PIXEL_SIZE logger.debug("Centered tile rect would be: ~ %s", str(centered_rect)) centered_rect.x1 += 1 centered_rect.y1 += 1 logger.debug("High-exclusive centered tile rect would be: ~ %s", str(centered_rect))