From ccbbdf3c77ffddffd6268c8911707bd437607565 Mon Sep 17 00:00:00 2001 From: "Dr. Andrew Annex" Date: Thu, 3 Oct 2024 13:46:37 -0700 Subject: [PATCH 1/8] Add missing supported_crs to mosaicjson wmts endpoint also made the bounds_crs discovered from the tms's geographic crs currently I call '.srs' on the geographic crs to get the bounds crs but it may make sense to add a function to determine the more compact urn representation first, failing that allow the WKT representation to pass through. --- src/titiler/core/titiler/core/factory.py | 3 +++ src/titiler/core/titiler/core/templates/wmts.xml | 2 +- src/titiler/mosaic/titiler/mosaic/factory.py | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/titiler/core/titiler/core/factory.py b/src/titiler/core/titiler/core/factory.py index 1cb9dfe76..79f020e5a 100644 --- a/src/titiler/core/titiler/core/factory.py +++ b/src/titiler/core/titiler/core/factory.py @@ -862,6 +862,8 @@ def wmts( supported_crs = f"EPSG:{tms.crs.to_epsg()}" else: supported_crs = tms.crs.srs + + bounds_crs = tms.crs.geographic_crs.srs or "urn:ogc:def:crs:OGC:2:84" return self.templates.TemplateResponse( request, @@ -869,6 +871,7 @@ def wmts( context={ "tiles_endpoint": tiles_url, "bounds": bounds, + "bounds_crs": bounds_crs, "tileMatrix": tileMatrix, "tms": tms, "supported_crs": supported_crs, diff --git a/src/titiler/core/titiler/core/templates/wmts.xml b/src/titiler/core/titiler/core/templates/wmts.xml index ae36a247f..fca0307eb 100644 --- a/src/titiler/core/titiler/core/templates/wmts.xml +++ b/src/titiler/core/titiler/core/templates/wmts.xml @@ -37,7 +37,7 @@ {{ title }} {{ layer_name }} {{ title }} - + {{ bounds[0] }} {{ bounds[1] }} {{ bounds[2] }} {{ bounds[3] }} diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index 366e10546..01e6651ec 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -640,14 +640,20 @@ def wmts( """ tileMatrix.append(tm) + supported_crs = tms.crs.srs + + bounds_crs = tms.crs.geographic_crs.srs or "urn:ogc:def:crs:OGC:2:84" + return self.templates.TemplateResponse( request, name="wmts.xml", context={ "tiles_endpoint": tiles_url, "bounds": bounds, + "bounds_crs": bounds_crs, "tileMatrix": tileMatrix, "tms": tms, + "supported_crs": supported_crs, "title": src_path if isinstance(src_path, str) else "TiTiler Mosaic", From d741ada4dac756f3819b3fb2aa273b99777fa960 Mon Sep 17 00:00:00 2001 From: "Dr. Andrew Annex" Date: Fri, 4 Oct 2024 09:30:01 -0700 Subject: [PATCH 2/8] bounds crs is now converted to uri --- src/titiler/core/titiler/core/factory.py | 3 ++- src/titiler/mosaic/titiler/mosaic/factory.py | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/titiler/core/titiler/core/factory.py b/src/titiler/core/titiler/core/factory.py index 79f020e5a..3a4047e25 100644 --- a/src/titiler/core/titiler/core/factory.py +++ b/src/titiler/core/titiler/core/factory.py @@ -27,6 +27,7 @@ from morecantile import TileMatrixSet from morecantile import tms as morecantile_tms from morecantile.defaults import TileMatrixSets +from morecantile.models import CRS_to_uri from pydantic import Field from rio_tiler.colormap import ColorMaps from rio_tiler.colormap import cmap as default_cmap @@ -863,7 +864,7 @@ def wmts( else: supported_crs = tms.crs.srs - bounds_crs = tms.crs.geographic_crs.srs or "urn:ogc:def:crs:OGC:2:84" + bounds_crs = CRS_to_uri(tms.geographic_crs) or "urn:ogc:def:crs:OGC:2:84" return self.templates.TemplateResponse( request, diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index 01e6651ec..568d3f45c 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -14,6 +14,7 @@ from geojson_pydantic.geometries import Polygon from morecantile import tms as morecantile_tms from morecantile.defaults import TileMatrixSets +from morecantile.models import CRS_to_uri from pydantic import Field from rio_tiler.constants import MAX_THREADS, WGS84_CRS from rio_tiler.io import BaseReader, MultiBandReader, MultiBaseReader, Reader @@ -642,7 +643,7 @@ def wmts( supported_crs = tms.crs.srs - bounds_crs = tms.crs.geographic_crs.srs or "urn:ogc:def:crs:OGC:2:84" + bounds_crs = CRS_to_uri(tms.crs.geographic_crs) or "urn:ogc:def:crs:OGC:2:84" return self.templates.TemplateResponse( request, From 6d7fcd555c7d8168914073a350e56486b7c528f1 Mon Sep 17 00:00:00 2001 From: "Dr. Andrew Annex" Date: Fri, 4 Oct 2024 09:31:46 -0700 Subject: [PATCH 3/8] bounds crs is now converted to uri --- src/titiler/core/titiler/core/factory.py | 2 +- src/titiler/mosaic/titiler/mosaic/factory.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/titiler/core/titiler/core/factory.py b/src/titiler/core/titiler/core/factory.py index 3a4047e25..7210bc09a 100644 --- a/src/titiler/core/titiler/core/factory.py +++ b/src/titiler/core/titiler/core/factory.py @@ -864,7 +864,7 @@ def wmts( else: supported_crs = tms.crs.srs - bounds_crs = CRS_to_uri(tms.geographic_crs) or "urn:ogc:def:crs:OGC:2:84" + bounds_crs = CRS_to_uri(tms.geographic_crs) return self.templates.TemplateResponse( request, diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index 568d3f45c..6e8ecd181 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -643,7 +643,7 @@ def wmts( supported_crs = tms.crs.srs - bounds_crs = CRS_to_uri(tms.crs.geographic_crs) or "urn:ogc:def:crs:OGC:2:84" + bounds_crs = CRS_to_uri(tms.crs.geographic_crs) return self.templates.TemplateResponse( request, From 112c1d2a4778de47652293842b53ce6ffe2dce58 Mon Sep 17 00:00:00 2001 From: "Dr. Andrew Annex" Date: Fri, 4 Oct 2024 09:58:36 -0700 Subject: [PATCH 4/8] added switch for boundingbox type, currently test is failing in a way I don't understand --- .gitignore | 4 ++++ src/titiler/application/tests/routes/test_cog.py | 5 ++++- src/titiler/core/titiler/core/factory.py | 2 ++ src/titiler/core/titiler/core/templates/wmts.xml | 4 ++-- src/titiler/mosaic/titiler/mosaic/factory.py | 2 ++ 5 files changed, 14 insertions(+), 3 deletions(-) diff --git a/.gitignore b/.gitignore index 3cfc7f31b..e5cc417fe 100644 --- a/.gitignore +++ b/.gitignore @@ -92,6 +92,10 @@ celerybeat-schedule venv/ ENV/ +# VScode +.vscode +.vscode/ + # Spyder project settings .spyderproject .spyproject diff --git a/src/titiler/application/tests/routes/test_cog.py b/src/titiler/application/tests/routes/test_cog.py index 3b185787b..ca488dbac 100644 --- a/src/titiler/application/tests/routes/test_cog.py +++ b/src/titiler/application/tests/routes/test_cog.py @@ -53,7 +53,6 @@ def test_info(rio, app): def test_wmts(rio, app): """test wmts endpoints.""" rio.open = mock_rasterio_open - response = app.get( "/cog/WebMercatorQuad/WMTSCapabilities.xml?url=https://myurl.com/cog.tif" ) @@ -69,6 +68,10 @@ def test_wmts(rio, app): "http://testserver/cog/tiles/WebMercatorQuad/{TileMatrix}/{TileCol}/{TileRow}@1x.png?url=https" in response.content.decode() ) + assert ( + '' + not in response.content.decode() # I don't understand yet why this test fails. + ) assert ( "http://www.opengis.net/def/crs/EPSG/0/3857" in response.content.decode() diff --git a/src/titiler/core/titiler/core/factory.py b/src/titiler/core/titiler/core/factory.py index 7210bc09a..a7c94dc1b 100644 --- a/src/titiler/core/titiler/core/factory.py +++ b/src/titiler/core/titiler/core/factory.py @@ -865,12 +865,14 @@ def wmts( supported_crs = tms.crs.srs bounds_crs = CRS_to_uri(tms.geographic_crs) + bounds_type = 'WGS84BoundingBox' if tms.geographic_crs == WGS84_CRS else 'BoundingBox' return self.templates.TemplateResponse( request, name="wmts.xml", context={ "tiles_endpoint": tiles_url, + "bounds_type": bounds_type, "bounds": bounds, "bounds_crs": bounds_crs, "tileMatrix": tileMatrix, diff --git a/src/titiler/core/titiler/core/templates/wmts.xml b/src/titiler/core/titiler/core/templates/wmts.xml index fca0307eb..2c3a8940f 100644 --- a/src/titiler/core/titiler/core/templates/wmts.xml +++ b/src/titiler/core/titiler/core/templates/wmts.xml @@ -37,10 +37,10 @@ {{ title }} {{ layer_name }} {{ title }} - + {{ bounds[0] }} {{ bounds[1] }} {{ bounds[2] }} {{ bounds[3] }} - + diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index 6e8ecd181..827d202fa 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -644,12 +644,14 @@ def wmts( supported_crs = tms.crs.srs bounds_crs = CRS_to_uri(tms.crs.geographic_crs) + bounds_type = 'WGS84BoundingBox' if tms.geographic_crs == WGS84_CRS else 'BoundingBox' return self.templates.TemplateResponse( request, name="wmts.xml", context={ "tiles_endpoint": tiles_url, + "bounds_type": bounds_type, "bounds": bounds, "bounds_crs": bounds_crs, "tileMatrix": tileMatrix, From 32abf75e99ff2a90b6d1920798534165216d74da Mon Sep 17 00:00:00 2001 From: "Dr. Andrew Annex" Date: Fri, 4 Oct 2024 10:01:57 -0700 Subject: [PATCH 5/8] fixed .crs.geographic_crs --- src/titiler/mosaic/titiler/mosaic/factory.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index 827d202fa..26293a4a1 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -643,7 +643,7 @@ def wmts( supported_crs = tms.crs.srs - bounds_crs = CRS_to_uri(tms.crs.geographic_crs) + bounds_crs = CRS_to_uri(tms.geographic_crs) bounds_type = 'WGS84BoundingBox' if tms.geographic_crs == WGS84_CRS else 'BoundingBox' return self.templates.TemplateResponse( From 3f73c94b2de81c32c9b27411e61bbf607b6ca6e0 Mon Sep 17 00:00:00 2001 From: "Dr. Andrew Annex" Date: Fri, 4 Oct 2024 10:25:37 -0700 Subject: [PATCH 6/8] black reformat and simplify bounds_type code --- .../application/tests/routes/test_cog.py | 2 +- src/titiler/core/titiler/core/factory.py | 14 ++++++++----- src/titiler/mosaic/titiler/mosaic/factory.py | 20 +++++++++++-------- 3 files changed, 22 insertions(+), 14 deletions(-) diff --git a/src/titiler/application/tests/routes/test_cog.py b/src/titiler/application/tests/routes/test_cog.py index ca488dbac..3e7726105 100644 --- a/src/titiler/application/tests/routes/test_cog.py +++ b/src/titiler/application/tests/routes/test_cog.py @@ -70,7 +70,7 @@ def test_wmts(rio, app): ) assert ( '' - not in response.content.decode() # I don't understand yet why this test fails. + not in response.content.decode() # I don't understand yet why this test fails. ) assert ( "http://www.opengis.net/def/crs/EPSG/0/3857" diff --git a/src/titiler/core/titiler/core/factory.py b/src/titiler/core/titiler/core/factory.py index a7c94dc1b..fae24de3a 100644 --- a/src/titiler/core/titiler/core/factory.py +++ b/src/titiler/core/titiler/core/factory.py @@ -269,9 +269,9 @@ class TilerFactory(BaseFactory): img_part_dependency: Type[DefaultDependency] = PartFeatureParams # Post Processing Dependencies (algorithm) - process_dependency: Callable[ - ..., Optional[BaseAlgorithm] - ] = available_algorithms.dependency + process_dependency: Callable[..., Optional[BaseAlgorithm]] = ( + available_algorithms.dependency + ) # Image rendering Dependencies rescale_dependency: Callable[..., Optional[RescaleType]] = RescalingParams @@ -863,9 +863,13 @@ def wmts( supported_crs = f"EPSG:{tms.crs.to_epsg()}" else: supported_crs = tms.crs.srs - + bounds_crs = CRS_to_uri(tms.geographic_crs) - bounds_type = 'WGS84BoundingBox' if tms.geographic_crs == WGS84_CRS else 'BoundingBox' + + if tms.geographic_crs == WGS84_CRS: + bounds_type = "WGS84BoundingBox" + else: + bounds_type = "BoundingBox" return self.templates.TemplateResponse( request, diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index 26293a4a1..2d8ee4e5a 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -89,9 +89,9 @@ class MosaicTilerFactory(BaseFactory): tile_dependency: Type[DefaultDependency] = TileParams # Post Processing Dependencies (algorithm) - process_dependency: Callable[ - ..., Optional[BaseAlgorithm] - ] = available_algorithms.dependency + process_dependency: Callable[..., Optional[BaseAlgorithm]] = ( + available_algorithms.dependency + ) # Image rendering Dependencies rescale_dependency: Callable[..., Optional[RescaleType]] = RescalingParams @@ -642,9 +642,13 @@ def wmts( tileMatrix.append(tm) supported_crs = tms.crs.srs - + bounds_crs = CRS_to_uri(tms.geographic_crs) - bounds_type = 'WGS84BoundingBox' if tms.geographic_crs == WGS84_CRS else 'BoundingBox' + + if tms.geographic_crs == WGS84_CRS: + bounds_type = "WGS84BoundingBox" + else: + bounds_type = "BoundingBox" return self.templates.TemplateResponse( request, @@ -657,9 +661,9 @@ def wmts( "tileMatrix": tileMatrix, "tms": tms, "supported_crs": supported_crs, - "title": src_path - if isinstance(src_path, str) - else "TiTiler Mosaic", + "title": ( + src_path if isinstance(src_path, str) else "TiTiler Mosaic" + ), "layer_name": "Mosaic", "media_type": tile_format.mediatype, }, From cc661642fbe98f1db2c247828573b2dc872e3e70 Mon Sep 17 00:00:00 2001 From: "Dr. Andrew Annex" Date: Fri, 4 Oct 2024 11:35:45 -0700 Subject: [PATCH 7/8] made geographic_crs explicit in certain mosaic and core tiler factor read methods when a tms is specified --- src/titiler/core/titiler/core/factory.py | 15 ++++++++++++--- src/titiler/mosaic/titiler/mosaic/factory.py | 4 ++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/titiler/core/titiler/core/factory.py b/src/titiler/core/titiler/core/factory.py index fae24de3a..06ec2e2c3 100644 --- a/src/titiler/core/titiler/core/factory.py +++ b/src/titiler/core/titiler/core/factory.py @@ -574,7 +574,10 @@ def tile( tms = self.supported_tms.get(tileMatrixSetId) with rasterio.Env(**env): with self.reader( - src_path, tms=tms, **reader_params.as_dict() + src_path, + tms=tms, + geographic_crs=tms.geographic_crs, + **reader_params.as_dict(), ) as src_dst: image = src_dst.tile( x, @@ -684,7 +687,10 @@ def tilejson( tms = self.supported_tms.get(tileMatrixSetId) with rasterio.Env(**env): with self.reader( - src_path, tms=tms, **reader_params.as_dict() + src_path, + tms=tms, + geographic_crs=tms.geographic_crs, + **reader_params.as_dict(), ) as src_dst: return { "bounds": src_dst.geographic_bounds, @@ -838,7 +844,10 @@ def wmts( tms = self.supported_tms.get(tileMatrixSetId) with rasterio.Env(**env): with self.reader( - src_path, tms=tms, **reader_params.as_dict() + src_path, + tms=tms, + geographic_crs=tms.geographic_crs, + **reader_params.as_dict(), ) as src_dst: bounds = src_dst.geographic_bounds minzoom = minzoom if minzoom is not None else src_dst.minzoom diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index 2d8ee4e5a..e2e6c28a0 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -330,6 +330,7 @@ def tile( with self.backend( src_path, tms=tms, + geographic_crs=tms.geographic_crs, reader=self.dataset_reader, reader_options=reader_params.as_dict(), **backend_params.as_dict(), @@ -458,6 +459,7 @@ def tilejson( with self.backend( src_path, tms=tms, + geographic_crs=tms.geographic_crs, reader=self.dataset_reader, reader_options=reader_params.as_dict(), **backend_params.as_dict(), @@ -618,6 +620,7 @@ def wmts( with self.backend( src_path, tms=tms, + geographic_crs=tms.geographic_crs, reader=self.dataset_reader, reader_options=reader_params.as_dict(), **backend_params.as_dict(), @@ -829,6 +832,7 @@ def assets_for_tile( with self.backend( src_path, tms=tms, + geographic_crs=tms.geographic_crs, reader=self.dataset_reader, reader_options=reader_params.as_dict(), **backend_params.as_dict(), From 69ae06eb715c1fe9c8364a60979038046a28ffc4 Mon Sep 17 00:00:00 2001 From: "Dr. Andrew Annex" Date: Fri, 4 Oct 2024 11:42:11 -0700 Subject: [PATCH 8/8] forgot mosaic json is updated to discover geographic_crs already --- src/titiler/mosaic/titiler/mosaic/factory.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/titiler/mosaic/titiler/mosaic/factory.py b/src/titiler/mosaic/titiler/mosaic/factory.py index e2e6c28a0..2d8ee4e5a 100644 --- a/src/titiler/mosaic/titiler/mosaic/factory.py +++ b/src/titiler/mosaic/titiler/mosaic/factory.py @@ -330,7 +330,6 @@ def tile( with self.backend( src_path, tms=tms, - geographic_crs=tms.geographic_crs, reader=self.dataset_reader, reader_options=reader_params.as_dict(), **backend_params.as_dict(), @@ -459,7 +458,6 @@ def tilejson( with self.backend( src_path, tms=tms, - geographic_crs=tms.geographic_crs, reader=self.dataset_reader, reader_options=reader_params.as_dict(), **backend_params.as_dict(), @@ -620,7 +618,6 @@ def wmts( with self.backend( src_path, tms=tms, - geographic_crs=tms.geographic_crs, reader=self.dataset_reader, reader_options=reader_params.as_dict(), **backend_params.as_dict(), @@ -832,7 +829,6 @@ def assets_for_tile( with self.backend( src_path, tms=tms, - geographic_crs=tms.geographic_crs, reader=self.dataset_reader, reader_options=reader_params.as_dict(), **backend_params.as_dict(),