Skip to content

Commit 02acb7d

Browse files
authored
Fix/map point query mosaic (#1338)
* fix: make point query work for mosaic factory in map.html * fix: do not render asset name * fix: gracefully handle 204/nodata responses * chore: update changelog
1 parent d1d68c6 commit 02acb7d

3 files changed

Lines changed: 58 additions & 22 deletions

File tree

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
### titiler.mosaic
1414

1515
* fix: `pixel-selection` method initialization to avoid FastAPI caching (author @raster-blaster, https://github.com/developmentseed/titiler/pull/1334)
16+
* fix: make the point query feature in map.html work for MosaicTilerFactory (https://github.com/developmentseed/titiler/pull/1338)
1617

1718
## 2.0.0b2 (2026-02-22)
1819

src/titiler/core/titiler/core/templates/map.html

Lines changed: 36 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,7 @@
249249

250250
fetch(`{{ point_endpoint|safe }}`.replace('{lat}', lat).replace('{lon}', lng))
251251
.then(res => {
252+
if (res.status === 204) return null;
252253
if (res.ok) return res.json();
253254

254255
return res.json().then(errorData => {
@@ -259,31 +260,44 @@
259260
})
260261
})
261262
.then(data => {
262-
const formatPopupContent = (data) => {
263-
let html = '<div style="max-width: 250px;">';
264-
265-
html += `<p><strong>Coordinates:</strong> ${lat}, ${lng}</p>`;
263+
if (data === null) {
264+
popup.setContent(`<p>No data at ${lat}, ${lng}</p>`);
265+
return;
266+
}
266267

267-
// single band case - just show the value
268-
if (data.band_names.length === 1) {
269-
const value = data.values[0] !== null ? data.values[0] : 'No data';
270-
html += `<p><strong>Value:</strong> ${value}</p>`;
268+
const renderBandTable = (bandNames, values) => {
269+
if (bandNames.length === 1) {
270+
const value = values[0] !== null ? values[0] : 'No data';
271+
return `<p><strong>Value:</strong> ${value}</p>`;
271272
}
272-
// multiple bands case - show in a table
273-
else {
274-
html += '<table style="width: 100%; border-collapse: collapse;">';
275-
html += '<tr><th style="text-align: left; padding: 1px; border-bottom: 1px solid #ddd;">Band</th>' +
276-
'<th style="text-align: right; padding: 1px; border-bottom: 1px solid #ddd;">Value</th></tr>';
277-
278-
for (let i = 0; i < data.band_names.length; i++) {
279-
const value = data.values[i] !== null ? data.values[i] : 'No data';
280-
html += `<tr>
281-
<td style="text-align: left; padding: 1px;">${data.band_names[i]}</td>
282-
<td style="text-align: right; padding: 1px;">${value}</td>
283-
</tr>`;
284-
}
273+
let html = '<table style="width: 100%; border-collapse: collapse;">';
274+
html += '<tr>'
275+
+ '<th style="text-align: left; padding: 1px; border-bottom: 1px solid #ddd;">Band</th>'
276+
+ '<th style="text-align: right; padding: 1px; border-bottom: 1px solid #ddd;">Value</th>'
277+
+ '</tr>';
278+
for (let i = 0; i < bandNames.length; i++) {
279+
const value = values[i] !== null ? values[i] : 'No data';
280+
html += `<tr>
281+
<td style="text-align: left; padding: 1px;">${bandNames[i]}</td>
282+
<td style="text-align: right; padding: 1px;">${value}</td>
283+
</tr>`;
284+
}
285+
html += '</table>';
286+
return html;
287+
};
285288

286-
html += '</table>';
289+
const formatPopupContent = (data) => {
290+
let html = `<div style="max-width: 250px;">
291+
<p><strong>Coordinates:</strong> ${lat}, ${lng}</p>`;
292+
293+
if (data.values !== undefined) {
294+
// TilerFactory shape: flat band list
295+
html += renderBandTable(data.band_names, data.values);
296+
} else if (data.assets !== undefined) {
297+
// MosaicTilerFactory shape: one entry per asset
298+
for (const asset of data.assets) {
299+
html += renderBandTable(asset.band_names, asset.values);
300+
}
287301
}
288302

289303
html += '</div>';

src/titiler/mosaic/titiler/mosaic/factory.py

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -836,12 +836,33 @@ def map_viewer(
836836

837837
tilejson_url += f"?{urlencode(qs)}"
838838

839+
point_url = self.url_for(request, "point", lon="{lon}", lat="{lat}")
840+
if request.query_params._list:
841+
qs_key_to_remove = [
842+
"tilesize",
843+
"tile_format",
844+
"minzoom",
845+
"maxzoom",
846+
"buffer",
847+
"padding",
848+
"colormap",
849+
"colormap_name",
850+
]
851+
qs = [
852+
(key, value)
853+
for (key, value) in request.query_params._list
854+
if key.lower() not in qs_key_to_remove
855+
]
856+
if qs:
857+
point_url += f"?{urlencode(qs)}"
858+
839859
tms = self.supported_tms.get(tileMatrixSetId)
840860
return self.templates.TemplateResponse(
841861
request,
842862
name="map.html",
843863
context={
844864
"tilejson_endpoint": tilejson_url,
865+
"point_endpoint": point_url,
845866
"tms": tms,
846867
"resolutions": [matrix.cellSize for matrix in tms],
847868
},

0 commit comments

Comments
 (0)