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)
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)
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
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)
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())
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
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())
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
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))
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)
def geometry(self): return ogr.CreateGeometryFromWkt(self.wkt, utils.get_sr(self.projection))
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))
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))
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
def geometry(self): if self.extent is None: return return Extent(self.extent, get_sr(self.projection)).as_geometry()
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))
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))
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}