From 2753554b0f4dce146e8e026ad1eae3338ff4d9c7 Mon Sep 17 00:00:00 2001 From: Bane Sullivan Date: Sun, 8 Oct 2023 21:40:25 -0700 Subject: [PATCH] Limit flask and bug fixes (#178) * Limit flask * Fix CRS serialization issue * Shamelessly remove ROI support for REST client * Shamelessly drop support for histograms --- localtileserver/client.py | 62 ---------------------------- localtileserver/tiler/utilities.py | 3 ++ localtileserver/web/rest.py | 33 --------------- localtileserver/web/urls.py | 5 --- requirements.txt | 2 +- setup.py | 4 +- tests/test_client.py | 6 --- tests/test_client_rest.py | 66 +----------------------------- 8 files changed, 7 insertions(+), 174 deletions(-) diff --git a/localtileserver/client.py b/localtileserver/client.py index 6a03d911..4dfe1b1b 100644 --- a/localtileserver/client.py +++ b/localtileserver/client.py @@ -410,10 +410,6 @@ def pixel(self, y: float, x: float, units: str = "pixels", projection: Optional[ """ raise NotImplementedError # pragma: no cover - def histogram(self, bins: int = 256, density: bool = False): - """Get a histoogram for each band.""" - raise NotImplementedError # pragma: no cover - @property def default_zoom(self): m = self.metadata_safe() @@ -671,10 +667,6 @@ def pixel(self, y: float, x: float, units: str = "pixels"): region = {"left": x, "top": y, "units": units} return self.tile_source.getPixel(region=region) - def histogram(self, bins: int = 256, density: bool = False): - result = self.tile_source.histogram(bins=bins, density=density) - return result["histogram"] - class BaseRestfulTileClient(BaseTileClientInterface): """Connect to a localtileserver instance. @@ -691,51 +683,6 @@ def get_tile(self, z: int, x: int, y: int, *args, output_path=None, **kwargs): return save_file_from_request(r, output_path) return ImageBytes(r.content, mimetype=r.headers["Content-Type"]) - def extract_roi( - self, - left: float, - right: float, - bottom: float, - top: float, - units: str = "EPSG:4326", - encoding: str = "TILED", - output_path: pathlib.Path = None, - return_bytes: bool = False, - return_path: bool = False, - ): - path = f"api/world/region.tif?units={units}&encoding={encoding}&left={left}&right={right}&bottom={bottom}&top={top}" - r = requests.get(self.create_url(path)) - r.raise_for_status() - if return_bytes: - return ImageBytes(r.content, mimetype=r.headers["Content-Type"]) - output_path = save_file_from_request(r, output_path) - if return_path: - return output_path - return TileClient(output_path) - - def extract_roi_pixel( - self, - left: int, - right: int, - bottom: int, - top: int, - encoding: str = "TILED", - output_path: pathlib.Path = None, - return_bytes: bool = False, - return_path: bool = False, - ): - path = f"/api/pixel/region.tif?encoding={encoding}&left={left}&right={right}&bottom={bottom}&top={top}" - r = requests.get(self.create_url(path)) - r.raise_for_status() - if return_bytes: - return ImageBytes(r.content, mimetype=r.headers["Content-Type"]) - output_path = save_file_from_request(r, output_path) - if return_path: - return output_path - return TileClient( - output_path, default_projection="EPSG:3857" if encoding == "TILED" else None - ) - def metadata(self, projection: Optional[str] = ""): if projection not in self._metadata: if projection == "": @@ -820,15 +767,6 @@ def pixel(self, y: float, x: float, units: str = "pixels", projection: Optional[ r.raise_for_status() return r.json() - def histogram(self, bins: int = 256, density: bool = False): - params = {} - params["density"] = density - params["bins"] = bins - url = add_query_parameters(self.create_url("api/histogram"), params) - r = requests.get(url) - r.raise_for_status() - return r.json() - class RemoteTileClient(BaseRestfulTileClient): """Connect to a remote localtileserver instance at a given host URL. diff --git a/localtileserver/tiler/utilities.py b/localtileserver/tiler/utilities.py index f9bda813..5aecb895 100644 --- a/localtileserver/tiler/utilities.py +++ b/localtileserver/tiler/utilities.py @@ -8,6 +8,7 @@ import large_image from large_image_source_rasterio import RasterioFileTileSource +from rasterio import CRS from localtileserver.tiler.data import clean_url, get_data_path, get_pine_gulch_url @@ -139,6 +140,8 @@ def get_meta_data(tile_source: RasterioFileTileSource): meta.update(tile_source.getInternalMetadata()) # Override bounds for EPSG:4326 meta["bounds"] = get_tile_bounds(tile_source) + if isinstance(meta["projection"], CRS): + meta["projection"] = meta["projection"].to_string() return meta diff --git a/localtileserver/web/rest.py b/localtileserver/web/rest.py index b6f5b8c3..9403e338 100644 --- a/localtileserver/web/rest.py +++ b/localtileserver/web/rest.py @@ -497,36 +497,3 @@ def get(self): pixel.pop("value", None) pixel.update(region) return pixel - - -@api.doc( - params={ - "bins": { - "type": "int", - "default": 256, - }, - "density": { - "type": "bool", - "default": False, - }, - } -) -class HistogramView(BasePixelOperation): - """Returns histogram.""" - - def get(self): - kwargs = dict( - bins=int(request.args.get("bins", 256)), - density=str_to_bool(request.args.get("density", "False")), - ) - tile_source = self.get_tile_source(projection=None) - result = tile_source.histogram(**kwargs) - result = result["histogram"] - for entry in result: - for key in {"bin_edges", "hist", "range"}: - if key in entry: - entry[key] = [float(val) for val in list(entry[key])] - for key in {"min", "max", "samples"}: - if key in entry: - entry[key] = float(entry[key]) - return result diff --git a/localtileserver/web/urls.py b/localtileserver/web/urls.py index 8e82f001..d7ca316b 100644 --- a/localtileserver/web/urls.py +++ b/localtileserver/web/urls.py @@ -53,11 +53,6 @@ "/pixel", endpoint="pixel", ) -rest.api.add_resource( - rest.HistogramView, - "/histogram", - endpoint="histogram", -) rest.api.add_resource( rest.ListTileSources, "/sources", diff --git a/requirements.txt b/requirements.txt index 7c9c56a8..d041ce4c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,5 @@ click -flask>=2.0.0 +flask>=2.0.0,<3 Flask-Caching flask-cors flask-restx>=0.5.0 diff --git a/setup.py b/setup.py index 7f017fcc..43abf300 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ long_description = "" # major, minor, patch -version_info = 0, 7, 1 +version_info = 0, 7, 2 # Nice string for the version __version__ = ".".join(map(str, version_info)) @@ -42,7 +42,7 @@ python_requires=">=3.7", install_requires=[ "click", - "flask>=2.0.0", + "flask>=2.0.0,<3", "Flask-Caching", "flask-cors", "flask-restx>=0.5.0", diff --git a/tests/test_client.py b/tests/test_client.py index 83f905a0..3fa387f5 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -228,12 +228,6 @@ def test_pixel(bahamas): assert "bands" in pix -@pytest.mark.skip -def test_histogram(bahamas): - hist = bahamas.histogram() - assert len(hist) - - @pytest.mark.parametrize("encoding", ["PNG", "JPEG", "JPG"]) def test_thumbnail_encodings(bahamas, encoding): # large-image's rasterio source cannot handle: "TIF", "TIFF" diff --git a/tests/test_client_rest.py b/tests/test_client_rest.py index 7da09c59..c7f32669 100644 --- a/tests/test_client_rest.py +++ b/tests/test_client_rest.py @@ -7,15 +7,9 @@ from server_thread import ServerDownError, ServerManager from localtileserver.client import RestTileClient -from localtileserver.tiler import get_clean_filename, get_tile_bounds, get_tile_source +from localtileserver.tiler import get_clean_filename from localtileserver.utilities import ImageBytes -skip_shapely = False -try: - from shapely.geometry import box -except ImportError: - skip_shapely = True - skip_mac_arm = pytest.mark.skipif( platform.system() == "Darwin" and platform.processor() == "arm", reason="MacOS Arm issues." ) @@ -97,57 +91,6 @@ def test_client_force_shutdown(bahamas_file): # assert get_content(thumb_url_a) != get_content(thumb_url_b) -def test_extract_roi_world(bahamas_file): - bahamas = RestTileClient(bahamas_file) - # -78.047, -77.381, 24.056, 24.691 - path = bahamas.extract_roi(-78.047, -77.381, 24.056, 24.691, return_path=True) - assert path.exists() - source = get_tile_source(path, projection="EPSG:3857") - assert source.getMetadata()["geospatial"] - e = get_tile_bounds(source, projection="EPSG:4326") - assert e["xmin"] == pytest.approx(-78.047, abs=TOLERANCE) - assert e["xmax"] == pytest.approx(-77.381, abs=TOLERANCE) - assert e["ymin"] == pytest.approx(24.056, abs=TOLERANCE) - assert e["ymax"] == pytest.approx(24.691, abs=TOLERANCE) - roi = bahamas.extract_roi(-78.047, -77.381, 24.056, 24.691, return_bytes=True) - assert isinstance(roi, ImageBytes) - assert roi.mimetype == "image/tiff" - roi = bahamas.extract_roi(-78.047, -77.381, 24.056, 24.691) - assert roi.metadata()["geospatial"] - - -@pytest.mark.skipif(skip_shapely, reason="shapely not installed") -def test_extract_roi_world_shape(bahamas_file): - bahamas = RestTileClient(bahamas_file) - poly = box(-78.047, 24.056, -77.381, 24.691) - path = bahamas.extract_roi_shape(poly, return_path=True) - assert path.exists() - source = get_tile_source(path, projection="EPSG:3857") - assert source.getMetadata()["geospatial"] - e = get_tile_bounds(source, projection="EPSG:4326") - assert e["xmin"] == pytest.approx(-78.047, abs=TOLERANCE) - assert e["xmax"] == pytest.approx(-77.381, abs=TOLERANCE) - assert e["ymin"] == pytest.approx(24.056, abs=TOLERANCE) - assert e["ymax"] == pytest.approx(24.691, abs=TOLERANCE) - path = bahamas.extract_roi_shape(poly.wkt, return_path=True) - assert path.exists() - - -@pytest.mark.skip -def test_extract_roi_pixel(bahamas_file): - bahamas = RestTileClient(bahamas_file) - path = bahamas.extract_roi_pixel(100, 500, 300, 600, return_path=True) - assert path.exists() - source = get_tile_source(path) - assert source.getMetadata()["geospatial"] - assert source.getMetadata()["sizeX"] == 400 - assert source.getMetadata()["sizeY"] == 300 - roi = bahamas.extract_roi_pixel(100, 500, 300, 600) - assert roi.metadata()["geospatial"] - roi = bahamas.extract_roi_pixel(100, 500, 300, 600, return_bytes=True) - assert isinstance(roi, ImageBytes) - - def test_caching_query_params(bahamas_file): bahamas = RestTileClient(bahamas_file) thumb_url_a = bahamas.create_url("api/thumbnail.png") @@ -215,13 +158,6 @@ def test_pixel(bahamas_file): assert "bands" in pix -@pytest.mark.skip -def test_histogram(bahamas_file): - bahamas = RestTileClient(bahamas_file) - hist = bahamas.histogram() - assert len(hist) - - @pytest.mark.parametrize("encoding", ["PNG", "JPEG", "JPG"]) def test_thumbnail_encodings(bahamas_file, encoding): bahamas = RestTileClient(bahamas_file)