예제 #1
0
 def test_extent(self):
     expected = (
         utils.Extent((136700, 455795, 136705, 455800), utils.get_sr("EPSG:28992"))
         .transformed(utils.get_sr("EPSG:4326"))
         .bbox
     )
     assert_allclose(self.source.extent, expected, atol=1e-10)
예제 #2
0
 def __init__(self, store, place_projection, anchor, coordinates, statistic="last"):
     if not isinstance(store, RasterBlock):
         raise TypeError("'{}' object is not allowed".format(type(store)))
     try:
         get_sr(place_projection)
     except RuntimeError:
         raise ValueError(
             "'{}' is not a valid projection string".format(place_projection)
         )
     anchor = list(anchor)
     if len(anchor) != 2:
         raise ValueError("Expected 2 numbers in the 'anchor' parameter")
     for x in anchor:
         if not isinstance(x, (int, float)):
             raise TypeError("'{}' object is not allowed".format(type(x)))
     if coordinates is None or len(coordinates) == 0:
         coordinates = []
     else:
         coordinates = np.asarray(coordinates, dtype=float)
         if coordinates.ndim != 2 or coordinates.shape[1] != 2:
             raise ValueError(
                 "Expected a list of lists of 2 numbers in the "
                 "'coordinates' parameter"
             )
         coordinates = coordinates.tolist()
     check_statistic(statistic)
     super().__init__(store, place_projection, anchor, coordinates, statistic)
예제 #3
0
    def projection(self):
        """The native projection of this block.

        Only returns something if the place projection equals the store
        projection"""
        store_projection = self.store.projection
        if store_projection is None:
            return
        if get_sr(self.place_projection).IsSame(get_sr(store_projection)):
            return store_projection
예제 #4
0
 def __init__(self, wkt, projection):
     if not isinstance(wkt, str):
         raise TypeError("'{}' object is not allowed".format(type(wkt)))
     if not isinstance(projection, str):
         raise TypeError("'{}' object is not allowed".format(type(projection)))
     try:
         load_wkt(wkt)
     except WKTReadingError:
         raise ValueError("The provided geometry is not a valid WKT")
     try:
         utils.get_sr(projection)
     except TypeError:
         raise ValueError("The provided projection is not a valid WKT")
     super().__init__(wkt, projection)
예제 #5
0
 def test_geometry(self):
     expected = (
         utils.Extent((136700, 455795, 136705, 455800), utils.get_sr("EPSG:28992"))
         .as_geometry()
         .ExportToWkt()
     )
     self.assertEqual(expected, self.source.geometry.ExportToWkt())
예제 #6
0
def expand_request_meters(request, radius_m=1):
    """
    Expand request by `radius_m` meters, rounded so that an integer number of
    pixels is added to all sides.

    Returns a tuple of:
     - new request with adapted bbox, width and height
     - the radius transformed to pixels as a (y, x) tuple of floats
     - the added margins as a (y, x) tuple of integers
    """
    sr = get_sr(request["projection"])
    bbox = request["bbox"]

    # throughout, variables in the projected unit ( = meters, mostly) are
    # suffixed by _m, in pixels by _px
    if sr.IsGeographic():
        # expand geographic bbox in EPSG3857
        extent_geom = Extent(bbox, sr)
        bbox = extent_geom.transformed(EPSG3857).bbox
    else:
        # most Projected projections are in meters, but to be sure:
        radius_m /= sr.GetLinearUnits()

    # compute the initial zoom factors: how much to expand the bbox to obtain
    # margins of exactly 'radius' (in meters)
    x1, y1, x2, y2 = bbox
    shape_m = y2 - y1, x2 - x1

    if shape_m[0] > 0 and shape_m[1] > 0:
        # Resolution in pixels per meter:
        resolution = request["height"] / shape_m[0], request["width"] / shape_m[1]
        # How many pixels to add:
        radius_px = [radius_m * res for res in resolution]
        # How many pixels to add, rounded to integers:
        margins_px = [int(round(r)) for r in radius_px]
        # How many meters to add (based on rounded pixels):
        margins_m = [m / res for m, res in zip(margins_px, resolution)]
    else:
        # There is no resolution. Add MARGIN_THRESHOLD pixels to the request.
        radius_px = margins_px = [Smooth.MARGIN_THRESHOLD] * 2
        # Expand the request with radius_m exactly.
        margins_m = [radius_m] * 2

    # assemble the request
    new_request = request.copy()
    new_request["bbox"] = (
        x1 - margins_m[1],
        y1 - margins_m[0],
        x2 + margins_m[1],
        y2 + margins_m[0],
    )
    if sr.IsGeographic():
        # transform back to original projection
        extent_proj = Extent(new_request["bbox"], EPSG3857)
        new_request["bbox"] = extent_proj.transformed(sr).bbox
    new_request["height"] += 2 * margins_px[0]
    new_request["width"] += 2 * margins_px[1]

    return new_request, radius_px
예제 #7
0
 def test_get_sr(self):
     wkt = """GEOGCS["GCS_WGS_1984",
                  DATUM["D_WGS_1984",SPHEROID[
                      "WGS_1984",6378137,298.257223563]],
                  PRIMEM["Greenwich",0],
                  UNIT["Degree",
                      0.017453292519943295]]"""
     self.assertTrue(utils.get_sr(wkt).ExportToProj4())
예제 #8
0
def expand_request_meters(request, radius_m=1):
    """
    Expand request by `radius_m` meters, rounded so that an integer number of
    pixels is added to all sides.

    Returns a tuple of:
     - new request with adapted bbox, width and height
     - the radius transformed to pixels as a (y, x) tuple of floats
     - the added margins as a (y, x) tuple of integers
    """
    sr = get_sr(request["projection"])
    bbox = request["bbox"]

    # throughout, variables in the projected unit ( = meters, mostly) are
    # suffixed by _m, in pixels by _px
    if sr.IsGeographic():
        # expand geographic bbox in EPSG3857
        extent_geom = Extent(bbox, sr)
        bbox = extent_geom.transformed(EPSG3857).bbox
    else:
        # most Projected projections are in meters, but to be sure:
        radius_m /= sr.GetLinearUnits()

    # compute the initial zoom factors: how much to expand the bbox to obtain
    # margins of exactly 'radius' (in meters)
    x1, y1, x2, y2 = bbox
    shape_m = y2 - y1, x2 - x1

    # omit the +1 in zoom for efficiency: zoom=0.2 is a zoom factor of 1.2
    zoom = [2 * radius_m / s for s in shape_m]

    # compute the size in pixels, and compute the margins (rounded size)
    shape_px = request["height"], request["width"]
    radius_px = [z * s / 2 for (z, s) in zip(zoom, shape_px)]
    margins_px = [int(round(sz)) for sz in radius_px]

    # use these (int-valued) margins to compute the actual zoom and margins
    zoom = [2 * m / s for (s, m) in zip(shape_px, margins_px)]
    margins_m = [z * s / 2 for (z, s) in zip(zoom, shape_m)]

    # assemble the request
    new_request = request.copy()
    new_request["bbox"] = (
        x1 - margins_m[1],
        y1 - margins_m[0],
        x2 + margins_m[1],
        y2 + margins_m[0],
    )
    if sr.IsGeographic():
        # transform back to original projection
        extent_proj = Extent(new_request["bbox"], EPSG3857)
        new_request["bbox"] = extent_proj.transformed(sr).bbox
    new_request["height"] += 2 * margins_px[0]
    new_request["width"] += 2 * margins_px[1]

    return new_request, radius_px
예제 #9
0
def test_rasterize_wkt_attrs():
    geom = box(135004, 455995, 135004.5, 455996)
    view = raster.RasterizeWKT(geom.wkt, "EPSG:28992")
    assert view.projection == "EPSG:28992"
    assert_almost_equal(view.geometry.GetEnvelope(), [135004, 135004.5, 455995, 455996])
    assert view.geometry.GetSpatialReference().IsSame(get_sr("EPSG:28992"))
    assert view.dtype == bool
    assert view.fillvalue is None
    assert_almost_equal(
        view.extent, shapely_transform(geom, "EPSG:28992", "EPSG:4326").bounds
    )
    assert view.timedelta is None
    assert view.period == (datetime(1970, 1, 1), datetime(1970, 1, 1))
예제 #10
0
 def geometry(self):
     """Combined geometry in this block's native projection. """
     store_geometry = self.store.geometry
     if store_geometry is None:
         return
     sr = get_sr(self.place_projection)
     if not store_geometry.GetSpatialReference().IsSame(sr):
         store_geometry = store_geometry.Clone()
         store_geometry.TransformTo(sr)
     _x1, _x2, _y1, _y2 = store_geometry.GetEnvelope()
     p, q = self.anchor
     P, Q = zip(*self.coordinates)
     x1, x2 = _x1 + min(P) - p, _x2 + max(P) - p
     y1, y2 = _y1 + min(Q) - q, _y2 + max(Q) - q
     return ogr.CreateGeometryFromWkt(POLYGON.format(x1, y1, x2, y2), sr)
예제 #11
0
 def geometry(self):
     return ogr.CreateGeometryFromWkt(self.wkt,
                                      utils.get_sr(self.projection))
예제 #12
0
 def test_extent_has_repr(self):
     sr = utils.get_sr("EPSG:4326")
     extent = utils.Extent(sr=sr, bbox=(0, 0, 1, 1))
     self.assertTrue(repr(extent))
예제 #13
0
 def test_extent(self):
     sr = utils.get_sr("EPSG:4326")
     extent = utils.Extent(sr=sr, bbox=(0, 0, 1, 1))
     geometry = extent.as_geometry()
     self.assertEqual(str(geometry), "POLYGON ((0 0,1 0,1 1,0 1,0 0))")
     self.assertEqual(str(geometry.GetSpatialReference()), str(sr))
예제 #14
0
    def get_sources_and_requests(self, **request):
        if request["mode"] != "vals":
            return ({"mode": request["mode"]}, None), (self.store, request)

        # transform the anchor and coordinates into the requested projection
        anchor = shapely_transform(
            Point(self.anchor), self.place_projection, request["projection"]
        ).coords[0]
        coordinates = [
            shapely_transform(
                Point(coord), self.place_projection, request["projection"]
            ).coords[0]
            for coord in self.coordinates
        ]

        # transform the source's extent
        extent_geometry = self.store.geometry
        if extent_geometry is None:
            # no geometry means: no data
            return (({"mode": "null"}, None),)
        sr = get_sr(request["projection"])
        if not extent_geometry.GetSpatialReference().IsSame(sr):
            extent_geometry = extent_geometry.Clone()
            extent_geometry.TransformTo(sr)
        xmin, xmax, ymin, ymax = extent_geometry.GetEnvelope()

        # compute the requested cellsize
        x1, y1, x2, y2 = request["bbox"]
        size_x = (x2 - x1) / request["width"]
        size_y = (y2 - y1) / request["height"]

        # point requests: never request the full source extent
        if size_x > 0 and size_y > 0:
            # check what the full source extent would require
            full_height = math.ceil((ymax - ymin) / size_y)
            full_width = math.ceil((xmax - xmin) / size_x)
            if full_height * full_width <= request["width"] * request["height"]:
                _request = request.copy()
                _request["width"] = full_width
                _request["height"] = full_height
                _request["bbox"] = (
                    xmin,
                    ymin,
                    xmin + full_width * size_x,
                    ymin + full_height * size_y,
                )
                process_kwargs = {
                    "mode": "warp",
                    "anchor": anchor,
                    "coordinates": coordinates,
                    "src_bbox": _request["bbox"],
                    "dst_bbox": request["bbox"],
                    "cellsize": (size_x, size_y),
                    "statistic": self.statistic,
                }
                return [(process_kwargs, None), (self.store, _request)]

        # generate a new (backwards shifted) bbox for each coordinate
        sources_and_requests = []
        filtered_coordinates = []
        for _x, _y in coordinates:
            bbox = [
                x1 + anchor[0] - _x,
                y1 + anchor[1] - _y,
                x2 + anchor[0] - _x,
                y2 + anchor[1] - _y,
            ]
            # check the overlap with the source's extent
            # Note that raster cells are defined [xmin, xmax) and (ymin, ymax]
            # so points precisely at xmax or ymin certainly do not have data.
            if bbox[0] >= xmax or bbox[1] > ymax or bbox[2] < xmin or bbox[3] <= ymin:
                continue
            filtered_coordinates.append((_x, _y))
            _request = request.copy()
            _request["bbox"] = bbox
            sources_and_requests.append((self.store, _request))
        if len(sources_and_requests) == 0:
            # No coordinates inside: we still need to return an array
            # of the correct shape. Send a time request to get the depth.
            _request = request.copy()
            _request["mode"] = "time"
            process_kwargs = {
                "mode": "empty",
                "dtype": self.dtype,
                "fillvalue": self.fillvalue,
                "width": request["width"],
                "height": request["height"],
                "statistic": self.statistic,
            }
            return [(process_kwargs, None), (self.store, _request)]
        process_kwargs = {"mode": "group", "statistic": self.statistic}
        return [(process_kwargs, None)] + sources_and_requests
예제 #15
0
 def geometry(self):
     if self.extent is None:
         return
     return Extent(self.extent, get_sr(self.projection)).as_geometry()
예제 #16
0
 def _get_extent(self):
     bbox = self.geo_transform.get_bbox(
         (0, 0), (self.gdal_dataset.RasterYSize, self.gdal_dataset.RasterXSize)
     )
     return utils.Extent(bbox, utils.get_sr(self.projection))
예제 #17
0
 def _get_extent(self):
     if not self.data.size:
         return
     bbox = self.geo_transform.get_bbox((0, 0), self.data.shape[1:])
     return utils.Extent(bbox, utils.get_sr(self.projection))
예제 #18
0
    def process(args, request):
        origin, timedelta, bands, value, src_projection = args
        if origin is None or timedelta is None or bands is None:
            return
        td_seconds = timedelta.total_seconds()
        lo = origin
        start = request.get("start", None)
        stop = request.get("stop", None)

        if start is None:
            # take the latest
            bands_lo = bands - 1
            bands_hi = bands
        elif stop is None:
            # take the nearest to start
            start_band = (request["start"] - lo).total_seconds() / td_seconds
            bands_lo = min(max(int(round(start_band)), 0), bands - 1)
            bands_hi = bands_lo + 1
        else:
            bands_lo = (request["start"] - lo).total_seconds() / td_seconds
            bands_hi = (request["stop"] - lo).total_seconds() / td_seconds
            bands_lo = max(int(math.ceil(bands_lo)), 0)
            bands_hi = min(int(math.floor(bands_hi)) + 1, bands)

        depth = bands_hi - bands_lo

        if depth <= 0:
            return

        if request["mode"] == "time":
            return {"time": [origin + i * timedelta for i in range(bands_lo, bands_hi)]}
        if request["mode"] == "meta":
            return {
                "meta": [
                    "Testmeta for band {}".format(i) for i in range(bands_lo, bands_hi)
                ]
            }
        if request["mode"] != "vals":
            raise ValueError('Invalid mode "{}"'.format(request["mode"]))

        height = request.get("height", 1)
        width = request.get("width", 1)
        shape = (depth, height, width)

        # simple mode: return a filled value with type uint8
        if not hasattr(value, "shape"):
            fillvalue = 255
            result = np.full(shape, value, dtype=np.uint8)
            return {"values": result, "no_data_value": fillvalue}

        # there is an actual data array
        fillvalue = get_dtype_max(value.dtype)
        bbox = request.get("bbox", (0, 0, width, height))
        projection = request.get("projection", "EPSG:3857")
        if projection != src_projection:
            extent = Extent(bbox, get_sr(projection))
            bbox = extent.transformed(get_sr(src_projection)).bbox
        x1, y1, x2, y2 = [int(round(x)) for x in bbox]

        if x1 == x2 or y1 == y2:  # point request
            if x1 < 0 or x1 >= value.shape[1] or y1 < 0 or y1 >= value.shape[0]:
                result = np.array([[255]], dtype=np.uint8)
            else:
                result = value[y1 : y1 + 1, x1 : x1 + 1]
        else:
            _x1 = max(x1, 0)
            _y1 = max(y1, 0)
            _x2 = min(x2, value.shape[1])
            _y2 = min(y2, value.shape[0])
            result = value[_y1:_y2, _x1:_x2]
            result = np.pad(
                result,
                ((_y1 - y1, y2 - _y2), (_x1 - x1, x2 - _x2)),
                mode=str("constant"),
                constant_values=fillvalue,
            )
            if result.shape != (height, width):
                zoom = (height / result.shape[0], width / result.shape[1])
                mask = ndimage.zoom((result == fillvalue).astype(np.float), zoom) > 0.5
                result[result == fillvalue] = 0
                result = ndimage.zoom(result, zoom)
                result[mask] = fillvalue
        result = np.repeat(result[np.newaxis], depth, axis=0)

        # fill nan values
        result[~np.isfinite(result)] = fillvalue
        return {"values": result, "no_data_value": fillvalue}