From 1eb98db14800d7a948a322a2855af97d39c8e11a Mon Sep 17 00:00:00 2001 From: tariqksoliman Date: Tue, 11 Feb 2025 15:37:42 -0800 Subject: [PATCH] #624 Get IdentifierTool working for COG: and add bidx and resampling params for COG: and stac-col: --- configure/src/core/Maker.js | 6 +- .../src/metaconfigs/layer-tile-config.json | 43 ++++++++- src/essence/Basics/Layers_/Layers_.js | 25 ++++- src/essence/Basics/Map_/Map_.js | 18 +++- .../Tools/Identifier/IdentifierTool.js | 92 +++++++++++++++++-- src/essence/Tools/Layers/LayersTool.js | 6 +- 6 files changed, 171 insertions(+), 19 deletions(-) diff --git a/configure/src/core/Maker.js b/configure/src/core/Maker.js index e56f1843..784e9e7e 100644 --- a/configure/src/core/Maker.js +++ b/configure/src/core/Maker.js @@ -377,7 +377,11 @@ const getComponent = ( }} value={text_array_f} onChange={(e) => { - updateConfiguration(com.field, e.target.value.split(","), layer); + let newValue = null; + if (e.target.value == null || e.target.value == "") newValue = null; + else newValue = e.target.value.split(","); + + updateConfiguration(com.field, newValue, layer); }} /> ); diff --git a/configure/src/metaconfigs/layer-tile-config.json b/configure/src/metaconfigs/layer-tile-config.json index e99fffee..9a70e4c2 100644 --- a/configure/src/metaconfigs/layer-tile-config.json +++ b/configure/src/metaconfigs/layer-tile-config.json @@ -123,7 +123,7 @@ "name": "Use TiTiler", "description": "Uses the TiTiler service to serve tiles if URL is a Cloud-Optimized GeoTiff (COG). TiTiler must be configured via the .env.", "type": "switch", - "width": 4, + "width": 3, "defaultChecked": false }, { @@ -131,16 +131,53 @@ "name": "Tile Matrix Set", "description": "If using TiTiler or if using a 'stac-collection:{collection}' URL, specify the projection for which to serve the tiles. Defaults to 'WebMercatorQuad'.", "type": "dropdown", - "width": 4, + "width": 5, "options": "{{TILE_MATRIX_SETS}}" + }, + { + "field": "cogResampling", + "name": "Resampling Method", + "description": "The resampling method for generated tiles through TiTiler. Defaults to 'nearest'.", + "type": "dropdown", + "width": 4, + "options": [ + "nearest", + "bilinear", + "cubic", + "cubic_spline", + "lanczos", + "average", + "mode", + "gauss", + "rms" + ] + } + ] + }, + { + "components": [ + { + "field": "cogBands", + "name": "Tile Bands", + "description": "Which bands from the COG from which to generate tiles. Defaults to '1,2,3' as RGB or '1' if it's a Transformed 32-bit COG. Can be a single number or a comma-separated list of numbers. Order matters.", + "type": "textarray", + "width": 4 + }, + { + "field": "cogBandsQuery", + "name": "Query Bands", + "description": "Which bands from the COG upon which to perform queries. Defaults to value of 'Tile Bands'.", + "type": "textarray", + "width": 4 } ] }, { + "subname": "32-bit COGs", "components": [ { "field": "cogTransform", - "name": "Transform COG", + "name": "Transform 32-bit COG", "description": "Enable rescaling and coloring 32-bit COGs on the fly. Will use TiTiler.", "type": "switch", "width": 3, diff --git a/src/essence/Basics/Layers_/Layers_.js b/src/essence/Basics/Layers_/Layers_.js index 15d6941f..9b724828 100644 --- a/src/essence/Basics/Layers_/Layers_.js +++ b/src/essence/Basics/Layers_/Layers_.js @@ -243,16 +243,20 @@ const L_ = { delete L_._onSpecificLayerToggleSubscriptions[fid] }, getUrl: function (type, url, layerData) { + let wasCOG = false + let nextUrl = url - if (nextUrl != null && nextUrl.startsWith('COG:')) + if (nextUrl != null && nextUrl.startsWith('COG:')) { nextUrl = nextUrl.slice(4) + wasCOG = true + } if (!F_.isUrlAbsolute(nextUrl)) { nextUrl = L_.missionPath + nextUrl } if ( type === 'tile' && - layerData && - layerData.throughTileServer === true + ((layerData && layerData.throughTileServer === true) || + wasCOG == true) ) { if ( !F_.isUrlAbsolute(nextUrl) && @@ -262,7 +266,20 @@ const L_ = { } } if (layerData && layerData.throughTileServer === true) { - nextUrl = `${window.location.origin}/titiler/cog/tiles/WebMercatorQuad/{z}/{x}/{y}.webp?url=${nextUrl}` + let bandsParam = '' + if (layerData.cogBands) { + layerData.cogBands.forEach((band) => { + if (band != null) bandsParam += `&bidx=${band}` + }) + } + let resamplingParam = '' + if (layerData.cogResampling) { + resamplingParam = `&resampling=${layerData.cogResampling}` + } + + nextUrl = `${window.location.origin}/titiler/cog/tiles/${ + layerData.tileMatrixSet || 'WebMercatorQuad' + }/{z}/{x}/{y}.webp?url=${nextUrl}${bandsParam}${resamplingParam}` } return nextUrl }, diff --git a/src/essence/Basics/Map_/Map_.js b/src/essence/Basics/Map_/Map_.js index 5e653b44..33bc1152 100644 --- a/src/essence/Basics/Map_/Map_.js +++ b/src/essence/Basics/Map_/Map_.js @@ -1109,13 +1109,29 @@ async function makeTileLayer(layerObj) { case 'stac-collection': splitColonType = splitColonLayerUrl[0] const splitParams = splitColonLayerUrl[1].split('?') + + // Bands + let bandsParam = '' + let b = layerObj.cogBands + if (b != null) { + b.forEach((band) => { + if (band != null) bandsParam += `&bidx=${band}` + }) + } + + // Resampling + let resamplingParam = '' + if (layerObj.cogResampling) { + resamplingParam = `&resampling=${layerObj.cogResampling}` + } + layerUrl = `${window.location.origin}${( window.location.pathname || '' ).replace(/\/$/g, '')}/titilerpgstac/collections/${ splitParams[0] }/tiles/${ layerObj.tileMatrixSet || 'WebMercatorQuad' - }/{z}/{x}/{y}?assets=asset` + }/{z}/{x}/{y}?assets=asset${bandsParam}${resamplingParam}` layerObj.tileformat = 'wmts' break default: diff --git a/src/essence/Tools/Identifier/IdentifierTool.js b/src/essence/Tools/Identifier/IdentifierTool.js index 346bc035..8604d78b 100644 --- a/src/essence/Tools/Identifier/IdentifierTool.js +++ b/src/essence/Tools/Identifier/IdentifierTool.js @@ -168,7 +168,7 @@ var IdentifierTool = { e.latlng.lat, Map_.map.getZoom(), ]) - }, 5) + }, 500) }, idPixelGlobe: function (e) { if (Globe_.litho.mouse) @@ -194,12 +194,14 @@ var IdentifierTool = { IdentifierTool.activeLayerURLs = [] IdentifierTool.zoomLevels = [] IdentifierTool.tileFormats = [] + IdentifierTool.imageData = [] for (let n in L_.layers.on) { if (L_.layers.on[n] == true) { //We only want the tile layers if (L_.layers.data[n].type == 'tile') { let url = - L_.layers.data[n].url.indexOf('stac-collection:') === 0 + L_.layers.data[n].url.indexOf('stac-collection:') === + 0 || L_.layers.data[n].url.indexOf('COG:') === 0 ? L_.layers.data[n].url : L_.getUrl( L_.layers.data[n].type, @@ -223,7 +225,8 @@ var IdentifierTool = { if ( IdentifierTool.activeLayerURLs[i].indexOf( 'stac-collection:' - ) === 0 + ) === 0 || + IdentifierTool.activeLayerURLs[i].indexOf('COG:') === 0 ) { IdentifierTool.imageData[i] = false trueValue = true @@ -322,8 +325,15 @@ var IdentifierTool = { //Oh IdentifierTool is the same as X != undefined if ( - IdentifierTool.activeLayerURLs[i].startsWith('stac-collection:') + IdentifierTool.activeLayerURLs[i].startsWith( + 'stac-collection:' + ) || + IdentifierTool.activeLayerURLs[i].startsWith('COG:') ) { + IdentifierTool.vars.data[IdentifierTool.activeLayerNames[i]] = + IdentifierTool.vars.data[ + IdentifierTool.activeLayerNames[i] + ] || {} IdentifierTool.vars.data[ IdentifierTool.activeLayerNames[i] ].data = [ @@ -499,7 +509,7 @@ var IdentifierTool = { if (!trueValue && !selfish) { IdentifierTool.mousemoveTimeout = setTimeout(function () { IdentifierTool.idPixel(e, lnglatzoom, true, true) - }, 150) + }, 500) } function parseValue(v, sigfigs, scalefactor) { @@ -657,6 +667,15 @@ function queryDataValue(url, lng, lat, numBands, layerUUID, callback) { if (L_.layers.data[layerUUID].time?.enabled == true) timeParam = `&datetime=${L_.layers.data[layerUUID].time.start}/${L_.layers.data[layerUUID].time.end}` + // Bands + let bandsParam = '' + let b = L_.layers.data[layerUUID].cogBandsQuery + if (b != null) { + b.forEach((band) => { + if (band != null) bandsParam += `&bidx=${band}` + }) + } + fetch( `${ mmgisglobal.NODE_ENV === 'development' @@ -664,7 +683,62 @@ function queryDataValue(url, lng, lat, numBands, layerUUID, callback) { : '' }/titilerpgstac/collections/${ url.split('stac-collection:')[1] - }/point/${lng},${lat}?assets=asset&items_limit=10${timeParam}`, + }/point/${lng},${lat}?assets=asset&items_limit=10${timeParam}${bandsParam}`, + { + method: 'GET', + headers: { + accept: 'application/json', + }, + } + ) + .then((res) => { + if (res.status === 200) { + return res.json() + } + }) + .then((json) => { + if (json.values) { + const values = [] + json.values.forEach((val, idx) => { + val[2].forEach((val2, idx2) => { + values.push([ + `${val[0]} - ${val2}`, + [val[1][idx2]], + [val2], + ]) + }) + }) + if (typeof callback === 'function') callback(values) + } + }) + .catch((err) => {}) + return + } else if (url.startsWith('COG:')) { + // Time + let timeParam = '' + if (L_.layers.data[layerUUID].time?.enabled == true) + timeParam = `&datetime=${L_.layers.data[layerUUID].time.start}/${L_.layers.data[layerUUID].time.end}` + + // Bands + let bandsParam = '' + let b = + L_.layers.data[layerUUID].cogBandsQuery || + L_.layers.data[layerUUID].cogBands + if (b != null) { + b.forEach((band) => { + if (band != null) bandsParam += `&bidx=${band}` + }) + } + + fetch( + `${ + mmgisglobal.NODE_ENV === 'development' + ? 'http://localhost:8888' + : '' + }/titiler/cog/point/${lng},${lat}?assets=asset&url=${L_.getUrl( + 'tile', + url + )}${timeParam}${bandsParam}`, { method: 'GET', headers: { @@ -679,7 +753,11 @@ function queryDataValue(url, lng, lat, numBands, layerUUID, callback) { }) .then((json) => { if (json.values) { - if (typeof callback === 'function') callback(json.values) + const values = [] + json.values.forEach((val, idx) => { + values.push([json.band_names[idx], [val]]) + }) + if (typeof callback === 'function') callback(values) } }) .catch((err) => {}) diff --git a/src/essence/Tools/Layers/LayersTool.js b/src/essence/Tools/Layers/LayersTool.js index d4851558..115b2c76 100644 --- a/src/essence/Tools/Layers/LayersTool.js +++ b/src/essence/Tools/Layers/LayersTool.js @@ -925,7 +925,7 @@ function interfaceWithMMGIS(fromInit) { 'Unable to locate layer.', 4000, true, - { x: 385, y: 6 }, + { x: 395, y: 6 }, '#e9ff26', 'black' ) @@ -937,7 +937,7 @@ function interfaceWithMMGIS(fromInit) { 'Please turn the layer on before locating.', 4000, true, - { x: 385, y: 6 }, + { x: 395, y: 6 }, '#e9ff26', 'black' ) @@ -957,7 +957,7 @@ function interfaceWithMMGIS(fromInit) { 'Unable to locate layer.', 4000, true, - { x: 385, y: 6 }, + { x: 395, y: 6 }, '#e9ff26', 'black' )