예제 #1
0
    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)
예제 #2
0
    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)
예제 #3
0
def ProduceImage(layer_properties, user_log_rect, user_width, user_height):
    """High-level production of the image.

  Args:
      layer_properties: Object with details about the layer.
      user_log_rect: The user-requested projected, ie map coordinates,
        not lat/lon, limits of the desired region. Ie BBOX, pretty much.
      user_width: The user-requested width of the image.
      user_height: The user-requested height of the image.

  Returns:
      The image to be presented to the user.
  """
    proj = layer_properties.projection

    zoom_level = tilecalcs.CalcZoomLevel(
        user_log_rect.Extent(),
        proj.InternalLogOuterBounds().Extent(),
        geom.Pair(user_width, user_height))

    tilepixel_rect, rect_of_tiles = tilecalcs.CalcTileRects(
        proj, user_log_rect, zoom_level)

    logger.info("Done tile calcs")

    tiles_array = _FetchTiles(rect_of_tiles, zoom_level, layer_properties)

    im_user = StitchTiles(tiles_array, layer_properties, tilepixel_rect,
                          rect_of_tiles, user_width, user_height)

    return im_user
예제 #4
0
    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))
예제 #5
0
    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))
예제 #6
0
    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))
예제 #7
0
    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)
예제 #8
0
    def PhysPtToLog(self, phys_pt):
        """Transforms 'back', from a pixel to the map-projected space.

    Args:
      phys_pt: Physical point.
    Returns:
      viewport rect.
    """
        log_x = ((phys_pt.x - self.phys_rect.x0) / self._w_phys * self._w_log +
                 self.log_rect.x0)
        log_y = ((phys_pt.y - self.phys_rect.y0) / self._h_phys * self._h_log +
                 self.log_rect.y0)

        return geom.Pair(log_x, log_y)
예제 #9
0
    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)
예제 #10
0
def StitchTiles(tiles_array, layer_properties, tilepixel_rect, rect_of_tiles,
                user_width, user_height):
    """Stitch an array of image tiles creating an image that fits within
    a rectangle and same size as passed width and height.

  Args:
      tiles_array: ImageArray of the tiles to stitch together.
      layer_properties: Object with details about the layer.
      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.
      user_width: The user-requested width of the image.
      user_height: The user-requested height of the image.
  Returns:
     The output image of the tiles stitching, clipped and resized.
  """

    im_whole_tiles_extent = geom.Pair(
        rect_of_tiles.Width() * _TILE_PIXEL_SIZE,
        rect_of_tiles.Height() * _TILE_PIXEL_SIZE)

    # Process transparency for map.
    # Presently "image/png" is the only picture format
    # which supports transparency.
    # If the picture format is "image/jpeg", then send
    # the image as it is, without processing it
    # for any transparency requirements.

    set_pixel_to_bgcolor = (layer_properties.image_format == "image/png"
                            and layer_properties.is_transparent == "FALSE")

    bgcolor = (layer_properties.bgcolor
               if set_pixel_to_bgcolor else _NO_DATA_PIXELS)
    alpha = _OPAQUE_ALPHA if set_pixel_to_bgcolor else _TRANSPARENT_ALPHA

    # Alpha channel is not required for jpeg formats.
    if layer_properties.image_format == "image/jpeg":
        mode = "RGB"
        color = bgcolor
    else:
        mode = "RGBA"
        color = bgcolor + alpha

    # If TRANSPARENT = TRUE, image format is "image/png",
    # then create a transparent image (alpha = 0) with a black background.

    # If TRANSPARENT = FALSE, image format is "image/png",
    # then create an opaque image (alpha = 255) with bgcolor background.

    # Will have unwanted margin but we'll crop it off later.
    im_whole_tiles = Image.new(mode, im_whole_tiles_extent.AsTuple(), color)

    logger.debug("Tiles rect (in tiles): %s %s", str(rect_of_tiles),
                 str(rect_of_tiles.Extent()))
    logger.debug("im_whole_tiles pixel extent: %s", str(im_whole_tiles_extent))

    for row in range(rect_of_tiles.Height()):
        for column in range(rect_of_tiles.Width()):
            pos = (int(column * _TILE_PIXEL_SIZE), int(row * _TILE_PIXEL_SIZE),
                   int((column + 1) * _TILE_PIXEL_SIZE),
                   int((row + 1) * _TILE_PIXEL_SIZE))
            im_tile = tiles_array.ImageAt(column, row)

            if set_pixel_to_bgcolor:
                im_tile = _SetTransPixelToBgcolor(im_tile, bgcolor)

            # It may be None.
            if im_tile:
                _PasteTile(im_whole_tiles, im_tile, pos)

    logger.debug("tilepixel_rect: %s", str(tilepixel_rect))

    # Relative to / within im_whole_tiles.
    # Round down to nearest 256.
    offset_within_tiled_image = geom.Pair(tilepixel_rect.x0 % _TILE_PIXEL_SIZE,
                                          tilepixel_rect.y0 % _TILE_PIXEL_SIZE)

    logger.debug("Offset within: %s", str(offset_within_tiled_image))

    within_tiled_image = geom.Rect.FromLowerLeftAndExtent(
        offset_within_tiled_image, tilepixel_rect.Extent())

    logger.debug("Cropping to: %s", str(within_tiled_image.AsTuple()))

    im_true = im_whole_tiles.crop(within_tiled_image.AsTuple())

    logger.debug("Stretching to requested: %s", str((user_width, user_height)))

    # Stretch the final pixels to match the aspect ratio of WIDTH /
    # HEIGHT. This is per the spec; doing this lets the client compensate
    # for non-square pixels.
    im_user = im_true.resize((user_width, user_height), Image.ANTIALIAS)

    return im_user
예제 #11
0
def ProduceImage(layer_properties, user_log_rect, user_width, user_height):
    """High-level production of the image.

  Args:
      layer_properties: Object with details about the layer.
      user_log_rect: The user-requested projected, ie map coordinates,
        not lat/lon, limits of the desired region. Ie BBOX, pretty much.
      user_width: The user-requested width of the image.
      user_height: The user-requested height of the image.

  Returns:
      The image to be presented to the user.
  """
    proj = layer_properties.projection

    zoom_level = tilecalcs.CalcZoomLevel(
        user_log_rect.Extent(),
        proj.InternalLogOuterBounds().Extent(),
        geom.Pair(user_width, user_height))

    tilepixel_rect, rect_of_tiles = tilecalcs.CalcTileRects(
        proj, user_log_rect, zoom_level)

    logger.info("Done tile calcs")

    tiles_array = _FetchTiles(rect_of_tiles, zoom_level, layer_properties)

    im_whole_tiles_extent = geom.Pair(
        rect_of_tiles.Width() * _TILE_PIXEL_SIZE,
        rect_of_tiles.Height() * _TILE_PIXEL_SIZE)

    # Process transparency for map.
    # Presently "image/png" is the only picture format
    # which supports transparency.
    # If the picture format is "image/jpeg", then send
    # the image as it is, without processing it
    # for any transparency requirements.

    set_pixel_to_bgcolor = (layer_properties.image_format == "image/png"
                            and layer_properties.is_transparent == "FALSE")

    bgcolor = (layer_properties.bgcolor
               if set_pixel_to_bgcolor else _NO_DATA_PIXELS)
    alpha = _OPAQUE_ALPHA if set_pixel_to_bgcolor else _TRANSPARENT_ALPHA

    # Alpha channel is not required for jpeg formats.
    if layer_properties.image_format == "image/jpeg":
        mode = "RGB"
        color = bgcolor
    else:
        mode = "RGBA"
        color = bgcolor + alpha

    # If TRANSPARENT = TRUE, image format is "image/png",
    # then create a transparent image (alpha = 0) with a black background.

    # If TRANSPARENT = FALSE, image format is "image/png",
    # then create an opaque image (alpha = 255) with bgcolor background.

    # Will have unwanted margin but we'll crop it off later.
    im_whole_tiles = Image.new(mode, im_whole_tiles_extent.AsTuple(), color)

    logger.debug("Tiles rect (in tiles): %s %s", str(rect_of_tiles),
                 str(rect_of_tiles.Extent()))
    logger.debug("im_whole_tiles pixel extent: %s", str(im_whole_tiles_extent))

    for row in range(rect_of_tiles.Height()):
        for column in range(rect_of_tiles.Width()):
            pos = (int(column * _TILE_PIXEL_SIZE), int(row * _TILE_PIXEL_SIZE),
                   int((column + 1) * _TILE_PIXEL_SIZE),
                   int((row + 1) * _TILE_PIXEL_SIZE))
            im_tile = tiles_array.ImageAt(column, row)

            if set_pixel_to_bgcolor:
                im_tile = _SetTransPixelToBgcolor(im_tile, bgcolor)

            # It may be None.
            if im_tile:
                _PasteTile(im_whole_tiles, im_tile, pos)

    logger.debug("tilepixel_rect: %s", str(tilepixel_rect))

    # Relative to / within im_whole_tiles.
    # Round down to nearest 256.
    offset_within_tiled_image = geom.Pair(tilepixel_rect.x0 % _TILE_PIXEL_SIZE,
                                          tilepixel_rect.y0 % _TILE_PIXEL_SIZE)

    logger.debug("Offset within: %s", str(offset_within_tiled_image))

    within_tiled_image = geom.Rect.FromLowerLeftAndExtent(
        offset_within_tiled_image, tilepixel_rect.Extent())

    logger.debug("Cropping to: %s", str(within_tiled_image.AsTuple()))

    im_true = im_whole_tiles.crop(within_tiled_image.AsTuple())

    logger.debug("Stretching to requested: %s", str((user_width, user_height)))

    # Stretch the final pixels to match the aspect ratio of WIDTH /
    # HEIGHT. This is per the spec; doing this lets the client compensate
    # for non-square pixels.
    im_user = im_true.resize((user_width, user_height), Image.ANTIALIAS)

    return im_user