Repository navigation
A click on a raster answers with its value, like a click on a vector - #571
Draft
BhattaraiSijan wants to merge 4 commits into
Draft
BhattaraiSijan wants to merge 4 commits into
BhattaraiSijan wants to merge 4 commits into
Conversation
…ike a vector pick; FeaturePopup says when it is reading and when there is none
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
A click on a raster layer now answers the way a click on a vector feature does. Wishlist item from the Air4US team: clicking the map on a raster should show the data value there, like the vector popup. Until now only vector layers answered a click; a raster in the browser is a stack of tiles, and the number behind the pixel is not there.
The flow is the vector flow. A click is resolved once, in core, by draw order: the topmost layer under the cursor that has something at that point answers. A vector layer answers with its picked feature, already in memory. A raster layer marked
pickableanswers with its value, fetched from the service that serves its tiles. Both go out as the samemap:featureClickbroadcast, so FeaturePopup renders a raster's value through the same heading template and property list an admin already writes for a vector, and its code does not change.How the point URL is found
The point URL is deduced from the tile URL, and the deduction is a TiTiler-family convention, not a standard. TiTiler's factories pair
/tiles/{tms}/{z}/{x}/{y}with/point/{lon},{lat}, both routes reading the same query parameters, and titiler, titiler-pgstac, titiler-multidim/xarray and titiler-cmr all build on them. So core swaps the tile path for the point path, keeps the query string as the tiles sent it (model run, lead,sel=,where=,datetime,assets,bidx), and drops the parameters that only shape a rendered tile (colormap_name,rescale,tilesizeand the like). A client-side COG, drawn from a bare.tif, is asked through the layer's TiTiler/cog/point.No OGC rule says any of this. WMS and WMTS answer a point by
GetFeatureInfoon a pixel, OGC API Tiles has no point route, and other tile servers have their own or none. A tile URL of any other shape gets no point URL and the layer cannot answer a click. This is stated in the same words ontoPointUrlinlayerValueAt.ts, the only place the rule lives.Verified live against every raster Air4US draws (NAQFC O₃ on titiler-multidim 0.7, TEMPO HCHO and NO₂ on titiler-multidim 0.10) and against a VEDA titiler-pgstac collection.
What's in the diff
Basics/Layers_/layerValueAt.ts(new, pure):toPointUrl(the deduction above),readPointValue(both answer shapes: a single dataset'svalues: [n], and a mosaic's[item, [values], [bands]]per item, newest first),fetchPointValue(never rejects; a failure, a stall past 10 s or an unreadable answer is null and one console warning),boundsContain.Basics/Map_/clickResolver.ts(new, pure): one rule for a click. The engine's pick is the floor; the pickable rasters drawn above it are asked top-down and the first with a value there takes the click, shaped as a point feature withvalue,location(the clicked point aslat, lng, four decimals),asOf,band,itemin its properties. Nothing there falls through to the pick. A click with no raster to ask resolves in place, so vector-only timing is unchanged. A click superseded by a newer one resolves to nothing, so a stale answer never goes out.Basics/Map_/Map_.js: themap:featureClickbridge resolves through the resolver.resolveTimedTileSourceis the first two steps ofmakeTileLayerlifted out so creation and the value lookup read the same URL;currentTileUrlForfinishes it the way the deck branch does.rasterValueAtgates on the coverage record and the layer's bounding box before asking.rasterCandidatesAbovereads draw order, visibility,pickableand the raster type.MapEngines/Adapters/DeckGLHelpers.ts: an empty pick now carries where it landed, which the resolver needs.rasterValueAtemitslayers:valueFetchStart{ layerName, latlng }before the request andlayers:valueFetchEnd{ layerName, latlng, value, failed }when it settles, once per request that actually goes out. Both are listed in the Event Bus API doc. A point query takes two to seven seconds on Air4US, so the deadline is 20 s.Tools/FeaturePopup/FeaturePopupTool.js: listens tolayers:valueFetchStartand, for a layer that opted in, shows a card reading "Reading value…" at the point so the click visibly landed. A value then arrives as a feature and replaces it. A null answer is said, not swallowed: onlayers:valueFetchEndthe card becomes "No data at this point." under the layer's heading, or "The value could not be read." when the request failed, and the empty click that follows leaves that card up; the next click elsewhere takes it down. One raster asked after another in the same click finds the card already up. The feature click handling is unchanged apart from that one keep.Tools/FeaturePopup/config.json: the description says a pickable tile layer answers withvalue,location,asOf,band,item, and shows the reading card while the value is fetched.Opt-in is
pickable: trueon a tile layer, a core field with a default of off, so no layer fires a request unasked. Core does not read the plugin'sfeaturePopupblock to decide.Testing
tests/unit/layerValueAt.spec.js(new): the rewrite on every shape in use (multidim, pgstac collection with@1x, pgstac item with.png,/cog/tileswith.webp, bare.tifwith and without a base), refusal of OSM, WMS and a non-matching path, parameter stripping, both answer shapes, null, theasOfread, a failed, unreadable, throwing and stalled fetch, bounds.tests/unit/clickResolver.spec.js(new): in-place result with nothing to ask, no position means no ask, raster above the pick takes it, top-down order and early stop, fall-through to the pick, superseded click.tests/unit/deckGLHelpers.spec.js: an empty pick carries its position.tests/unit/FeaturePopupTool.spec.js: the plugin renders a resolved raster answer with the configured label; the reading card shows for an opted-in raster, not for one that did not opt in, once for two rasters in one click, is replaced by the value, emits no action when replaced, and never shows on Leaflet; a null answer turns it into the no-data card, a failed one into the could-not-read card, the empty click of the same spot leaves it up and the next click elsewhere takes it down; a null answer for a spot no longer shown is ignored, and a second raster is read after the first said nothing.npx vitest run tests/unit: all green (see the run in the PR checks).npx tsc --noEmitclean./point/{lon},{lat}with the tiles'sel=and nocolormap_name. TEMPO NO₂ inside its window → value with the observed time; the threewhere=filters go percent-encoded. Both on → the topmost answers; reorder and the other does. Pacific → "No data at this point." under the layer's heading, staying up until the next click. Click an AQS station drawn above NAQFC → the station card. Two quick clicks → only the last card.Not in this PR
air4us-v48-pick-value.config.json): not committed, imported into the dashboard.🤖 Generated with Claude Code