From 192babd929f00d9b5332a1bc04b6564a24b71e05 Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 18 Mar 2025 06:37:36 -0500 Subject: [PATCH 1/7] add tilejson and map links to collection if titiler_endpoint is set --- runtimes/eoapi/stac/eoapi/stac/app.py | 1 + runtimes/eoapi/stac/eoapi/stac/client.py | 34 +++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/runtimes/eoapi/stac/eoapi/stac/app.py b/runtimes/eoapi/stac/eoapi/stac/app.py index 12070a7..3ba2c3b 100644 --- a/runtimes/eoapi/stac/eoapi/stac/app.py +++ b/runtimes/eoapi/stac/eoapi/stac/app.py @@ -212,6 +212,7 @@ async def lifespan(app: FastAPI): title=settings.stac_fastapi_title, description=settings.stac_fastapi_description, pgstac_search_model=search_post_model, + titiler_endpoint=settings.titiler_endpoint, ), item_get_request_model=item_get_model, items_get_request_model=items_get_model, diff --git a/runtimes/eoapi/stac/eoapi/stac/client.py b/runtimes/eoapi/stac/eoapi/stac/client.py index e492a1b..7ebe8e1 100644 --- a/runtimes/eoapi/stac/eoapi/stac/client.py +++ b/runtimes/eoapi/stac/eoapi/stac/client.py @@ -14,7 +14,7 @@ Type, get_args, ) -from urllib.parse import unquote_plus, urljoin +from urllib.parse import unquote_plus, urlencode, urljoin import attr import jinja2 @@ -243,9 +243,35 @@ async def get_queryables( return queryables +def add_render_links(collection: Collection, titiler_endpoint: str) -> Collection: + if renders := collection.get("renders"): + base_url = f"{titiler_endpoint}/collections/{collection['id']}/WebMercatorQuad" + for render, metadata in renders.items(): + query_params = urlencode(metadata, doseq=True) + collection["links"].append( + { + "rel": "map", + "title": f"{render} interactive map", + "type": MimeTypes.html.value, + "href": f"{base_url}/map?{query_params}", + } + ) + collection["links"].append( + { + "rel": "tilejson", + "title": f"{render} tilejson", + "type": MimeTypes.html.value, + "href": f"{base_url}/tilejson.json?{query_params}", + } + ) + + return collection + + @attr.s class PgSTACClient(CoreCrudClient): pgstac_search_model: Type[PgstacSearch] = attr.ib(default=PgstacSearch) + titiler_endpoint: Optional[str] = attr.ib(default=None) async def landing_page( self, @@ -383,6 +409,9 @@ async def all_collections( **kwargs, ) -> Collections: collections = await super().all_collections(request, *args, **kwargs) + if self.titiler_endpoint: + for collection in collections["collections"]: + collection = add_render_links(collection, self.titiler_endpoint) output_type: Optional[MimeTypes] if f: @@ -425,6 +454,9 @@ async def get_collection( collection_id, request, *args, **kwargs ) + if self.titiler_endpoint: + collection = add_render_links(collection, self.titiler_endpoint) + output_type: Optional[MimeTypes] if f: output_type = MimeTypes[f] From abb07f42317a63589f07c428d050cb103860bc71 Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 18 Mar 2025 06:38:03 -0500 Subject: [PATCH 2/7] add render layers to the html collection view --- .../stac/eoapi/stac/templates/collection.html | 51 ++++++++++++++++++- 1 file changed, 49 insertions(+), 2 deletions(-) diff --git a/runtimes/eoapi/stac/eoapi/stac/templates/collection.html b/runtimes/eoapi/stac/eoapi/stac/templates/collection.html index 9f9755f..ad69b71 100644 --- a/runtimes/eoapi/stac/eoapi/stac/templates/collection.html +++ b/runtimes/eoapi/stac/eoapi/stac/templates/collection.html @@ -76,12 +76,13 @@

Links

window.addEventListener("load", function() { const collection = {{ response|tojson }}; var map = L.map('map').setView([0, 0], 1); - map.addLayer(new L.TileLayer( + var osmLayer = new L.TileLayer( 'https://tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 18, attribution: 'Map data © OpenStreetMap contributors' } - )); + ); + map.addLayer(osmLayer); for (let i = 0, len = collection.extent.spatial.bbox.length; i < len; i++) { const options = i === 0 ? { @@ -107,6 +108,52 @@

Links

map.fitBounds(bbox_polygon.getBounds()); } } + // Find all TileJSON links + const tileJsonLinks = collection.links.filter(link => link.rel === "tilejson"); + + // Create an object to hold our overlay layers + const overlayLayers = {}; + + // Process each TileJSON link + if (tileJsonLinks.length > 0) { + tileJsonLinks.forEach((link, index) => { + // Fetch the TileJSON data + fetch(link.href) + .then(response => response.json()) + .then(tileJson => { + // Create a tile layer using the TileJSON data + const tileLayer = L.tileLayer(tileJson.tiles[0], { + attribution: tileJson.attribution || '', + minZoom: tileJson.minzoom || 0, + maxZoom: tileJson.maxzoom || 18 + }); + + // Add a title for the layer + const layerName = link.title || `TileJSON Layer ${index + 1}`; + overlayLayers[layerName] = tileLayer; + + // Add the first layer to the map by default + if (index === 0) { + tileLayer.addTo(map); + } + + // Add layer control after all layers are processed + if (index === tileJsonLinks.length - 1) { + // Define the base layers + const baseLayers = { + "OpenStreetMap": osmLayer + }; + + // Add the layer control to the map + L.control.layers(baseLayers, overlayLayers).addTo(map); + } + }) + .catch(error => { + console.error("Error loading TileJSON:", error); + }); + }); + } + }); {% endif %} From 4c306aacde8c713055ff10c1c36e37b2c68fe869 Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 18 Mar 2025 07:05:31 -0500 Subject: [PATCH 3/7] enable full screen map! --- runtimes/eoapi/stac/eoapi/stac/client.py | 2 +- .../stac/eoapi/stac/templates/collection.html | 43 +++++++++++++------ 2 files changed, 32 insertions(+), 13 deletions(-) diff --git a/runtimes/eoapi/stac/eoapi/stac/client.py b/runtimes/eoapi/stac/eoapi/stac/client.py index 7ebe8e1..346e684 100644 --- a/runtimes/eoapi/stac/eoapi/stac/client.py +++ b/runtimes/eoapi/stac/eoapi/stac/client.py @@ -260,7 +260,7 @@ def add_render_links(collection: Collection, titiler_endpoint: str) -> Collectio { "rel": "tilejson", "title": f"{render} tilejson", - "type": MimeTypes.html.value, + "type": MimeTypes.json.value, "href": f"{base_url}/tilejson.json?{query_params}", } ) diff --git a/runtimes/eoapi/stac/eoapi/stac/templates/collection.html b/runtimes/eoapi/stac/eoapi/stac/templates/collection.html index ad69b71..1dfdf6c 100644 --- a/runtimes/eoapi/stac/eoapi/stac/templates/collection.html +++ b/runtimes/eoapi/stac/eoapi/stac/templates/collection.html @@ -39,7 +39,7 @@

{% for keyword in response.keywords %}
  • {{ keyword }}
  • {% endfor %} - + {% endif %} @@ -72,10 +72,24 @@

    Links

    {% if response.extent and response.extent.spatial %} + + + + + {% endif %} From 2c984a6bb54997abc7c77453503ccc0d420950f2 Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 18 Mar 2025 07:06:08 -0500 Subject: [PATCH 4/7] use AWS_SESSION_TOKEN in docker network if available --- docker-compose.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/docker-compose.yml b/docker-compose.yml index 4c4c16c..f8a93f9 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -89,6 +89,7 @@ services: - MOSAIC_CONCURRENCY=1 - AWS_ACCESS_KEY_ID=${AWS_ACCESS_KEY_ID} - AWS_SECRET_ACCESS_KEY=${AWS_SECRET_ACCESS_KEY} + - AWS_SESSION_TOKEN=${AWS_SESSION_TOKEN} env_file: - path: .env required: false From e14443457ad8501a8aad0ce41ddcc0e8f7da3e37 Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 18 Mar 2025 07:11:01 -0500 Subject: [PATCH 5/7] fix layerName --- runtimes/eoapi/stac/eoapi/stac/templates/collection.html | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/runtimes/eoapi/stac/eoapi/stac/templates/collection.html b/runtimes/eoapi/stac/eoapi/stac/templates/collection.html index 1dfdf6c..715d074 100644 --- a/runtimes/eoapi/stac/eoapi/stac/templates/collection.html +++ b/runtimes/eoapi/stac/eoapi/stac/templates/collection.html @@ -139,7 +139,8 @@

    Links

    maxZoom: tileJson.maxzoom || 18 }); - overlayLayers[layerName] = link.title || `TileJSON Layer ${index + 1}`; + const layerName = link.title || `TileJSON Layer ${index + 1}`; + overlayLayers[layerName] = tileLayer; // Add the first layer to the map by default if (index === 0) { From 3019810f0aa21a05d513893a85583aaa630a233e Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 18 Mar 2025 09:30:07 -0500 Subject: [PATCH 6/7] fix link rel types, use request.app.state.settings --- runtimes/eoapi/stac/eoapi/stac/app.py | 1 - runtimes/eoapi/stac/eoapi/stac/client.py | 16 ++++++++-------- .../stac/eoapi/stac/templates/collection.html | 3 ++- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/runtimes/eoapi/stac/eoapi/stac/app.py b/runtimes/eoapi/stac/eoapi/stac/app.py index 3ba2c3b..12070a7 100644 --- a/runtimes/eoapi/stac/eoapi/stac/app.py +++ b/runtimes/eoapi/stac/eoapi/stac/app.py @@ -212,7 +212,6 @@ async def lifespan(app: FastAPI): title=settings.stac_fastapi_title, description=settings.stac_fastapi_description, pgstac_search_model=search_post_model, - titiler_endpoint=settings.titiler_endpoint, ), item_get_request_model=item_get_model, items_get_request_model=items_get_model, diff --git a/runtimes/eoapi/stac/eoapi/stac/client.py b/runtimes/eoapi/stac/eoapi/stac/client.py index 346e684..3814471 100644 --- a/runtimes/eoapi/stac/eoapi/stac/client.py +++ b/runtimes/eoapi/stac/eoapi/stac/client.py @@ -244,13 +244,14 @@ async def get_queryables( def add_render_links(collection: Collection, titiler_endpoint: str) -> Collection: + """Adds links to html preview and tilejson endpoints for a collection""" if renders := collection.get("renders"): base_url = f"{titiler_endpoint}/collections/{collection['id']}/WebMercatorQuad" for render, metadata in renders.items(): query_params = urlencode(metadata, doseq=True) collection["links"].append( { - "rel": "map", + "rel": Relations.preview.value, "title": f"{render} interactive map", "type": MimeTypes.html.value, "href": f"{base_url}/map?{query_params}", @@ -258,8 +259,8 @@ def add_render_links(collection: Collection, titiler_endpoint: str) -> Collectio ) collection["links"].append( { - "rel": "tilejson", - "title": f"{render} tilejson", + "rel": Relations.tiles.value, + "title": f"{render} tiles", "type": MimeTypes.json.value, "href": f"{base_url}/tilejson.json?{query_params}", } @@ -271,7 +272,6 @@ def add_render_links(collection: Collection, titiler_endpoint: str) -> Collectio @attr.s class PgSTACClient(CoreCrudClient): pgstac_search_model: Type[PgstacSearch] = attr.ib(default=PgstacSearch) - titiler_endpoint: Optional[str] = attr.ib(default=None) async def landing_page( self, @@ -409,9 +409,9 @@ async def all_collections( **kwargs, ) -> Collections: collections = await super().all_collections(request, *args, **kwargs) - if self.titiler_endpoint: + if titiler_endpoint := request.app.state.settings.titiler_endpoint: for collection in collections["collections"]: - collection = add_render_links(collection, self.titiler_endpoint) + collection = add_render_links(collection, titiler_endpoint) output_type: Optional[MimeTypes] if f: @@ -454,8 +454,8 @@ async def get_collection( collection_id, request, *args, **kwargs ) - if self.titiler_endpoint: - collection = add_render_links(collection, self.titiler_endpoint) + if titiler_endpoint := request.app.state.settings.titiler_endpoint: + collection = add_render_links(collection, titiler_endpoint) output_type: Optional[MimeTypes] if f: diff --git a/runtimes/eoapi/stac/eoapi/stac/templates/collection.html b/runtimes/eoapi/stac/eoapi/stac/templates/collection.html index 715d074..c193ebd 100644 --- a/runtimes/eoapi/stac/eoapi/stac/templates/collection.html +++ b/runtimes/eoapi/stac/eoapi/stac/templates/collection.html @@ -124,7 +124,7 @@

    Links

    } // Add any tilejson links as layers to the map - const tileJsonLinks = collection.links.filter(link => link.rel === "tilejson"); + const tileJsonLinks = collection.links.filter(link => link.rel === "tiles"); const overlayLayers = {}; @@ -133,6 +133,7 @@

    Links

    fetch(link.href) .then(response => response.json()) .then(tileJson => { + console.log(tileJson) const tileLayer = L.tileLayer(tileJson.tiles[0], { attribution: tileJson.attribution || '', minZoom: tileJson.minzoom || 0, From 51238e0c0bbd1f930e031a449e0736959065cd8b Mon Sep 17 00:00:00 2001 From: hrodmn Date: Tue, 18 Mar 2025 09:37:50 -0500 Subject: [PATCH 7/7] fix direct links to stac browser --- infrastructure/app.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/infrastructure/app.py b/infrastructure/app.py index 40fe0d9..27ce1d6 100644 --- a/infrastructure/app.py +++ b/infrastructure/app.py @@ -4,6 +4,7 @@ import yaml from aws_cdk import ( App, + Duration, RemovalPolicy, Stack, aws_certificatemanager, @@ -387,11 +388,18 @@ def __init__( ), default_root_object="index.html", error_responses=[ + aws_cloudfront.ErrorResponse( + http_status=403, + response_http_status=200, + response_page_path="/index.html", + ttl=Duration.seconds(0), + ), aws_cloudfront.ErrorResponse( http_status=404, response_http_status=200, response_page_path="/index.html", - ) + ttl=Duration.seconds(0), + ), ], certificate=aws_certificatemanager.Certificate.from_certificate_arn( self,