Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
1 change: 1 addition & 0 deletions runtimes/eoapi/stac/eoapi/stac/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
34 changes: 33 additions & 1 deletion runtimes/eoapi/stac/eoapi/stac/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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.json.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,
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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]
Expand Down
77 changes: 72 additions & 5 deletions runtimes/eoapi/stac/eoapi/stac/templates/collection.html
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ <h1 class="my-4">
{% for keyword in response.keywords %}
<li class="badge badge-secondary">{{ keyword }}</li>
{% endfor %}
</lul>
</ul>
</div>
{% endif %}

Expand Down Expand Up @@ -72,17 +72,33 @@ <h2>Links</h2>
</div>

{% if response.extent and response.extent.spatial %}

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.fullscreen/2.0.0/Control.FullScreen.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.fullscreen/2.0.0/Control.FullScreen.min.js"></script>

<script>
window.addEventListener("load", function() {
const collection = {{ response|tojson }};
var map = L.map('map').setView([0, 0], 1);
map.addLayer(new L.TileLayer(
var map = L.map('map', {
fullscreenControl: true,
fullscreenControlOptions: {
position: 'bottomright',
title: 'View Fullscreen',
titleCancel: 'Exit Fullscreen',
content: '<i class="fa fa-expand"></i>' // You can customize this icon
}
}).setView([0, 0], 1);

var osmLayer = new L.TileLayer(
'https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
maxZoom: 18,
attribution: 'Map data &copy; <a href="https://openstreetmap.org/copyright">OpenStreetMap contributors</a>'
}
));
);
map.addLayer(osmLayer);

// Draw the bounding boxes
for (let i = 0, len = collection.extent.spatial.bbox.length; i < len; i++) {
const options = i === 0 ? {
fill: false,
Expand All @@ -101,12 +117,63 @@ <h2>Links</h2>
[bbox[3], bbox[0]]
], options);


map.addLayer(bbox_polygon);
if (i === 0) {
map.fitBounds(bbox_polygon.getBounds());
}
}

// Add any tilejson links as layers to the map
const tileJsonLinks = collection.links.filter(link => link.rel === "tilejson");

const overlayLayers = {};

if (tileJsonLinks.length > 0) {
tileJsonLinks.forEach((link, index) => {
fetch(link.href)
.then(response => response.json())
.then(tileJson => {
const tileLayer = L.tileLayer(tileJson.tiles[0], {
attribution: tileJson.attribution || '',
minZoom: tileJson.minzoom || 0,
maxZoom: tileJson.maxzoom || 18
});

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);
});
});
}

// Handle fullscreen change event to resize map
map.on('fullscreenchange', function() {
if (map.isFullscreen()) {
console.log('Entered fullscreen');
} else {
console.log('Exited fullscreen');
}
// Make sure the map fills the container after fullscreen changes
map.invalidateSize();
});
});
</script>
{% endif %}
Expand Down