def process(data, request): mode = request["mode"] if mode == "time": return {"time": [data]} elif mode == "meta": return {"meta": [None]} # load the geometry and transform it into the requested projection geometry = load_wkt(data["wkt"]) if data["projection"] != request["projection"]: geometry = utils.shapely_transform( geometry, data["projection"], request["projection"] ) # take a shortcut when the geometry does not intersect the bbox x1, y1, x2, y2 = request["bbox"] if (x1 == x2) and (y1 == y2): # Don't do box(x1, y1, x2, y2), this gives an invalid geometry. bbox_geom = Point(x1, y1) else: bbox_geom = box(x1, y1, x2, y2) if not geometry.intersects(bbox_geom): return { "values": np.full( (1, request["height"], request["width"]), False, dtype=bool ), "no_data_value": None, } return utils.rasterize_geoseries( geoseries=GeoSeries([geometry]) if not geometry.is_empty else None, bbox=request["bbox"], projection=request["projection"], height=request["height"], width=request["width"], )
def process(data, request): mode = request["mode"] if mode == "time": return {"time": [data]} elif mode == "meta": return {"meta": [None]} # load the geometry and transform it into the requested projection geometry = load_wkt(data["wkt"]) if data["projection"] != request["projection"]: geometry = utils.shapely_transform(geometry, data["projection"], request["projection"]) # take a shortcut when the geometry does not intersect the bbox if not geometry.intersects(box(*request["bbox"])): return { "values": np.full((1, request["height"], request["width"]), False, dtype=np.bool), "no_data_value": None, } return utils.rasterize_geoseries( geoseries=GeoSeries([geometry]) if not geometry.is_empty else None, bbox=request["bbox"], projection=request["projection"], height=request["height"], width=request["width"], )
def test_shapely_transform(self): src_srs = "EPSG:28992" dst_srs = "EPSG:4326" box28992 = geometry.box(100000, 400000, 200000, 500000) box4326 = utils.shapely_transform(box28992, src_srs=src_srs, dst_srs=dst_srs) assert_almost_equal((5.4, 52.0), list(box4326.centroid.coords)[0], decimal=1)
def test_place_reproject(source, center_epsg3857): target = 572050, 6812050 # EPSG3857 coords somewhere inside RD validity place = raster.Place(source, "EPSG:3857", center_epsg3857, [target]) x, y = shapely_transform(Point(*target), "EPSG:3857", "EPSG:28992").coords[0] values = place.get_data( mode="vals", bbox=(x - 40, y - 40, x + 40, y + 40), projection="EPSG:28992", width=8, height=8, )["values"] assert (values == 7).all()
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 process(data, request): mode = request["mode"] if mode not in ("extent", "intersects", "centroid"): raise ValueError("Unknown mode '{}'".format(mode)) # load the geometry and transform it into the requested projection geometry = load_wkt(data["wkt"]) if data["projection"] != request["projection"]: geometry = utils.shapely_transform(geometry, data["projection"], request["projection"]) f = gpd.GeoDataFrame(geometry=[geometry], crs=utils.get_crs(request["projection"])) # compute the bounds of each geometry and filter on min_size min_size = request.get("min_size") if min_size: minx, miny, maxx, maxy = geometry.bounds if (maxy - miny) < min_size or (maxx - minx) < min_size: return { "projection": request["projection"], "features": gpd.GeoDataFrame([]), } if mode == "intersects": if not geometry.intersects(request["geometry"]): return { "projection": request["projection"], "features": gpd.GeoDataFrame([]), } return {"features": f, "projection": request["projection"]} elif mode == "centroid": with warnings.catch_warnings(): # geopandas warns if in WGS84 warnings.simplefilter("ignore") centroid = geometry.centroid if not centroid.intersects(request["geometry"]): return { "projection": request["projection"], "features": gpd.GeoDataFrame([]), } return {"features": f, "projection": request["projection"]} elif mode == "extent": if not geometry.intersects(request["geometry"]): return {"projection": request["projection"], "extent": None} return { "extent": tuple(geometry.bounds), "projection": request["projection"], }
def test_place_attrs_reproject(source, center_epsg3857): # Place should adapt the spatial attributes (extent & geometry) place = raster.Place( source, "EPSG:3857", center_epsg3857, [(572050, 6812050), (570050, 6811050)] ) # The native projection != store projection: assert place.projection is None assert place.geo_transform is None extent_epsg3857 = 570000, 6811000, 572100, 6812100 extent_epsg4326 = shapely_transform( box(*extent_epsg3857), "EPSG:3857", "EPSG:4326" ).bounds x1, x2, y1, y2 = place.geometry.GetEnvelope() assert (x1, y1, x2, y2) == pytest.approx(extent_epsg3857, rel=1e-4) assert place.extent == pytest.approx(extent_epsg4326, rel=1e-4)
def test_rasterize_wkt_vals(vals_request, projection): # vals_request has width=4, height=6 and cell size of 0.5 # we place a rectangle of 2 x 3 with corner at x=1, y=2 view = raster.RasterizeWKT( shapely_transform(box(135000.5, 455998, 135001.5, 455999.5), "EPSG:28992", projection).wkt, projection, ) vals_request["start"] = vals_request["stop"] = None actual = view.get_data(**vals_request) assert actual["values"][0].astype(int).tolist() == [ [0, 0, 0, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 0, 0, 0], [0, 0, 0, 0], ]
def get_sources_and_requests(self, **request): mode = request["mode"] if mode == "extent": return [(self.source, request)] if mode != "centroid": raise NotImplementedError("Cannot process '{}' mode".format(mode)) # tile the requested geometry in boxes that have a maximum size req_geometry = request["geometry"] tile_srs = self.projection request_srs = request["projection"] # transform the requested geometry into the tile geometry geometry = utils.shapely_transform(req_geometry, request_srs, tile_srs) x1, y1, x2, y2 = geometry.bounds ncols = ceil((x2 - x1) / self.size) nrows = ceil((y2 - y1) / self.size) if ncols <= 1 and nrows <= 1: return [(self.source, request)] # shortcut # resize the tiles so that all tiles have similar dimensions size_x = (x2 - x1) / ncols size_y = (y2 - y1) / nrows tiles = [ box( x1 + i * size_x, y1 + j * size_y, x1 + (i + 1) * size_x, y1 + (j + 1) * size_y, ) for i, j in product(range(ncols), range(nrows)) ] # intersect the tiles with the requested geometry series = gpd.GeoSeries(tiles).intersection(geometry) # drop empty tiles series = series[~series.is_empty] source = self.source request["projection"] = tile_srs return [(source, {**request, "geometry": tile}) for tile in series]
def test_place_attrs(source, center): place = raster.Place(source, "EPSG:28992", center, [(50, 50)]) # Place should not adapt these attributes: assert place.period == source.period assert place.timedelta == source.timedelta assert place.dtype == source.dtype assert place.fillvalue == source.fillvalue # If the place projection equals the store projection: assert place.projection == source.projection assert place.geo_transform == source.geo_transform extent_epsg28992 = (0, 0, 100, 100) extent_epsg4326 = shapely_transform( box(*extent_epsg28992), "EPSG:28992", "EPSG:4326" ).bounds x1, x2, y1, y2 = place.geometry.GetEnvelope() assert (x1, y1, x2, y2) == extent_epsg28992 assert place.extent == pytest.approx(extent_epsg4326, rel=1e-4)
def extent(self): return tuple( utils.shapely_transform(load_wkt(self.wkt), self.projection, "EPSG:4326").bounds)
def test_shapely_transform_invalid(self): src_srs = "EPSG:4326" dst_srs = "EPSG:28992" box4326 = geometry.box(100000, 400000, 200000, 500000) with pytest.raises(utils.TransformException): utils.shapely_transform(box4326, src_srs=src_srs, dst_srs=dst_srs)
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 center_epsg3857(center): yield shapely_transform(Point(*center), "EPSG:28992", "EPSG:3857").coords[0]