maplibre-gl-raster 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/{RasterControl-Cv3yZ90Z.js → RasterControl-hvApJoZQ.js} +290 -140
- package/dist/RasterControl-hvApJoZQ.js.map +1 -0
- package/dist/index.mjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/types/index.d.ts +43 -0
- package/dist/types/react.d.ts +8 -0
- package/package.json +2 -2
- package/dist/RasterControl-Cv3yZ90Z.js.map +0 -1
package/README.md
CHANGED
|
@@ -164,7 +164,10 @@ layer:
|
|
|
164
164
|
([cog-tiler-wasm](https://github.com/opengeos/cog-tiler-wasm)) wired to a
|
|
165
165
|
MapLibre custom protocol. Tiles are rendered on the CPU and served as native
|
|
166
166
|
MapLibre raster layers. The panel's settings (bands, rescale, colormap,
|
|
167
|
-
curve, gamma, nodata, opacity) map directly onto its render parameters.
|
|
167
|
+
curve, gamma, nodata, opacity) map directly onto its render parameters. This
|
|
168
|
+
tiler ships a shorter colormap list than the deck.gl sprite, so while it is
|
|
169
|
+
active the panel's colormap picker narrows to the ramps it can actually draw
|
|
170
|
+
(a name it does not know renders black rather than falling back).
|
|
168
171
|
- **`titiler`** - a server-side dynamic tiler
|
|
169
172
|
([TiTiler](https://developmentseed.org/titiler/)). Tiles are rendered by a
|
|
170
173
|
remote TiTiler instance and drawn as native MapLibre raster layers, so
|
|
@@ -2753,13 +2753,23 @@ var ColormapPicker = class {
|
|
|
2753
2753
|
constructor(options) {
|
|
2754
2754
|
this._palette = options.palette ?? null;
|
|
2755
2755
|
this._stats = options.stats ?? null;
|
|
2756
|
-
const
|
|
2757
|
-
|
|
2758
|
-
label: "Image palette (default)"
|
|
2759
|
-
}] : [], ...COLORMAP_OPTIONS.map((o) => ({
|
|
2756
|
+
const allowed = options.allowed;
|
|
2757
|
+
const named = COLORMAP_OPTIONS.filter((o) => !allowed || allowed.has(o.name)).map((o) => ({
|
|
2760
2758
|
value: o.name,
|
|
2761
2759
|
label: o.label
|
|
2762
|
-
}))
|
|
2760
|
+
}));
|
|
2761
|
+
const unsupported = allowed && options.value && options.value !== "palette" && !allowed.has(options.value) ? [{
|
|
2762
|
+
value: options.value,
|
|
2763
|
+
label: `${colormapDisplayName(options.value)} (not supported by this engine)`
|
|
2764
|
+
}] : [];
|
|
2765
|
+
const selectOptions = [
|
|
2766
|
+
...this._palette ? [{
|
|
2767
|
+
value: PALETTE_COLORMAP,
|
|
2768
|
+
label: "Image palette (default)"
|
|
2769
|
+
}] : [],
|
|
2770
|
+
...unsupported,
|
|
2771
|
+
...named
|
|
2772
|
+
];
|
|
2763
2773
|
this._select = select(selectOptions, options.value, (next) => {
|
|
2764
2774
|
this._updatePreview(next);
|
|
2765
2775
|
options.onChange(next);
|
|
@@ -19055,7 +19065,180 @@ function classNames(classes) {
|
|
|
19055
19065
|
return Object.entries(classes).filter(([, value]) => value).map(([key]) => key).join(" ");
|
|
19056
19066
|
}
|
|
19057
19067
|
//#endregion
|
|
19068
|
+
//#region src/lib/state/RasterLayer.ts
|
|
19069
|
+
/** Default visualization state for a freshly added layer. Mode/bands are
|
|
19070
|
+
* re-picked automatically once the band count is known (unless the caller
|
|
19071
|
+
* supplied them explicitly); single-band rasters default to the image's
|
|
19072
|
+
* embedded color table when present ('palette') or grayscale otherwise. */
|
|
19073
|
+
var DEFAULT_LAYER_STATE = {
|
|
19074
|
+
mode: "rgb",
|
|
19075
|
+
bands: [
|
|
19076
|
+
1,
|
|
19077
|
+
2,
|
|
19078
|
+
3
|
|
19079
|
+
],
|
|
19080
|
+
rescale: null,
|
|
19081
|
+
colormap: "gray",
|
|
19082
|
+
reversed: false,
|
|
19083
|
+
nodata: "auto",
|
|
19084
|
+
opacity: 1,
|
|
19085
|
+
gamma: 1,
|
|
19086
|
+
stretch: "linear",
|
|
19087
|
+
visible: true,
|
|
19088
|
+
minZoom: 0,
|
|
19089
|
+
maxZoom: 24
|
|
19090
|
+
};
|
|
19091
|
+
/**
|
|
19092
|
+
* Creates a complete layer state from optional overrides.
|
|
19093
|
+
*
|
|
19094
|
+
* @param overrides - Partial state merged over the defaults
|
|
19095
|
+
* @returns A fully populated RasterLayerState
|
|
19096
|
+
*/
|
|
19097
|
+
function createLayerState(overrides) {
|
|
19098
|
+
return {
|
|
19099
|
+
...DEFAULT_LAYER_STATE,
|
|
19100
|
+
...overrides
|
|
19101
|
+
};
|
|
19102
|
+
}
|
|
19103
|
+
/**
|
|
19104
|
+
* Resolves a layer's min/max zoom bounds, filling in the full [0, 24] range for
|
|
19105
|
+
* any bound left unset. Shared by the render engines so the deck.gl overlay and
|
|
19106
|
+
* the native MapLibre raster layers apply an identical zoom range.
|
|
19107
|
+
*
|
|
19108
|
+
* @param state - The layer state (only its zoom bounds are read)
|
|
19109
|
+
* @returns The concrete `{ minZoom, maxZoom }` bounds
|
|
19110
|
+
*/
|
|
19111
|
+
function resolveZoomRange(state) {
|
|
19112
|
+
return {
|
|
19113
|
+
minZoom: state.minZoom ?? 0,
|
|
19114
|
+
maxZoom: state.maxZoom ?? 24
|
|
19115
|
+
};
|
|
19116
|
+
}
|
|
19117
|
+
/**
|
|
19118
|
+
* Derives the public snapshot for a layer.
|
|
19119
|
+
*
|
|
19120
|
+
* @param layer - Internal layer record
|
|
19121
|
+
* @returns A read-only info object safe to hand to consumers
|
|
19122
|
+
*/
|
|
19123
|
+
function toLayerInfo(layer) {
|
|
19124
|
+
return {
|
|
19125
|
+
id: layer.id,
|
|
19126
|
+
name: layer.name,
|
|
19127
|
+
source: layer.source,
|
|
19128
|
+
memberUrls: layer.members ? layer.members.map((m) => m.url) : null,
|
|
19129
|
+
bandCount: layer.bandCount,
|
|
19130
|
+
bandNames: layer.bandNames ? new Map(layer.bandNames) : null,
|
|
19131
|
+
beforeId: layer.beforeId,
|
|
19132
|
+
attribution: layer.attribution,
|
|
19133
|
+
bounds: layer.bounds ? { ...layer.bounds } : null,
|
|
19134
|
+
loading: layer.loading,
|
|
19135
|
+
error: layer.error,
|
|
19136
|
+
state: { ...layer.state }
|
|
19137
|
+
};
|
|
19138
|
+
}
|
|
19139
|
+
/** True when `lngLat` falls inside `bounds` (edges included). */
|
|
19140
|
+
function containsPoint(bounds, lngLat) {
|
|
19141
|
+
const [lng, lat] = lngLat;
|
|
19142
|
+
return lng >= bounds.west && lng <= bounds.east && lat >= bounds.south && lat <= bounds.north;
|
|
19143
|
+
}
|
|
19144
|
+
/**
|
|
19145
|
+
* The layer's images that could carry a sample at `lngLat`, topmost first.
|
|
19146
|
+
*
|
|
19147
|
+
* A plain layer has exactly one image. A mosaic VRT layer has one per member,
|
|
19148
|
+
* and only the members whose extent covers the point are worth reading — a
|
|
19149
|
+
* member whose bounds have not been reported yet stays a candidate rather than
|
|
19150
|
+
* being skipped, since bounds only arrive once a member renders.
|
|
19151
|
+
*
|
|
19152
|
+
* Members are returned in reverse document order because that is the order they
|
|
19153
|
+
* are drawn in: `LayerManager` builds one deck.gl layer per member in member
|
|
19154
|
+
* order, so later members paint over earlier ones — matching GDAL, where a
|
|
19155
|
+
* VRT's later sources overwrite earlier ones. Sources placed at their natural
|
|
19156
|
+
* position can still overlap (adjacent scenes commonly do), so where they do,
|
|
19157
|
+
* the last one is what the user sees and therefore what a reader should report.
|
|
19158
|
+
*
|
|
19159
|
+
* @param layer - The layer to read from
|
|
19160
|
+
* @param lngLat - The location, [lng, lat] in WGS84
|
|
19161
|
+
* @returns Candidate images, topmost first; empty when the layer has not loaded
|
|
19162
|
+
* or the point falls outside every member
|
|
19163
|
+
*/
|
|
19164
|
+
function imagesAt(layer, lngLat) {
|
|
19165
|
+
if (!layer.members) return layer.geotiff ? [layer.geotiff] : [];
|
|
19166
|
+
return layer.members.filter((m) => !m.bounds || containsPoint(m.bounds, lngLat)).reverse().map((m) => m.geotiff);
|
|
19167
|
+
}
|
|
19168
|
+
/**
|
|
19169
|
+
* The mosaic assets whose extent covers `lngLat`, in manifest order.
|
|
19170
|
+
*
|
|
19171
|
+
* A mosaic manifest layer (MosaicJSON or STAC) has no opened image of its own —
|
|
19172
|
+
* the deck.gl {@link import('@developmentseed/deck.gl-geotiff').MosaicLayer}
|
|
19173
|
+
* opens each asset lazily while it is in view — so a reader works from the
|
|
19174
|
+
* manifest's bboxes and opens what it needs. This is the mosaic counterpart to
|
|
19175
|
+
* {@link imagesAt}, returning URLs because the COGs may not be open yet.
|
|
19176
|
+
*
|
|
19177
|
+
* Unlike a VRT's members these are *not* reversed. A VRT has a defined paint
|
|
19178
|
+
* order (later sources overwrite earlier ones), but a mosaic's assets are drawn
|
|
19179
|
+
* in whatever order its spatial index returns them, so no candidate is reliably
|
|
19180
|
+
* "topmost". Manifest order is used instead: it matches MosaicJSON's convention
|
|
19181
|
+
* that the first asset listed for a location is the preferred one. Where assets
|
|
19182
|
+
* genuinely overlap the drawn pixel is ambiguous, and the first covering asset
|
|
19183
|
+
* is as defensible a report as any.
|
|
19184
|
+
*
|
|
19185
|
+
* @param layer - The layer to read from
|
|
19186
|
+
* @param lngLat - The location, [lng, lat] in WGS84
|
|
19187
|
+
* @returns Covering asset URLs in manifest order; empty when the layer is not a
|
|
19188
|
+
* mosaic manifest, has not parsed yet, or the point falls outside every asset
|
|
19189
|
+
*/
|
|
19190
|
+
function assetsAt(layer, lngLat) {
|
|
19191
|
+
if (!layer.mosaicAssets) return [];
|
|
19192
|
+
const [lng, lat] = lngLat;
|
|
19193
|
+
return layer.mosaicAssets.filter((a) => lng >= a.bbox[0] && lng <= a.bbox[2] && lat >= a.bbox[1] && lat <= a.bbox[3]).map((a) => a.url);
|
|
19194
|
+
}
|
|
19195
|
+
/**
|
|
19196
|
+
* Derives a display name from a URL or file name: the last path segment
|
|
19197
|
+
* without query string.
|
|
19198
|
+
*
|
|
19199
|
+
* @param source - URL string or file name
|
|
19200
|
+
* @returns A short human-readable layer name
|
|
19201
|
+
*/
|
|
19202
|
+
function deriveLayerName(source) {
|
|
19203
|
+
try {
|
|
19204
|
+
const segment = (source.includes("://") ? new URL(source).pathname : source).split("/").filter(Boolean).pop();
|
|
19205
|
+
return segment ? decodeURIComponent(segment) : source;
|
|
19206
|
+
} catch {
|
|
19207
|
+
return source;
|
|
19208
|
+
}
|
|
19209
|
+
}
|
|
19210
|
+
//#endregion
|
|
19058
19211
|
//#region src/lib/state/CogTilerEngine.ts
|
|
19212
|
+
/**
|
|
19213
|
+
* The colormaps assumed renderable before the wasm module has loaded.
|
|
19214
|
+
*
|
|
19215
|
+
* This is only a starting guess: once the module is in memory the engine
|
|
19216
|
+
* reports its real set from `colormaps()` (see
|
|
19217
|
+
* {@link CogTilerEngine.supportedColormaps}), so a package that ships more
|
|
19218
|
+
* ramps widens the picker on its own with no change here. The conservative
|
|
19219
|
+
* subset below is what every published version has always known, so the picker
|
|
19220
|
+
* never offers a ramp the tiler cannot draw during that first moment.
|
|
19221
|
+
*
|
|
19222
|
+
* Rendering an unknown name is a soft failure, not a hard one: the tiler falls
|
|
19223
|
+
* back to its `gray` ramp (asserted upstream by
|
|
19224
|
+
* `colormap::tests::unknown_name_falls_back_to_gray`), so a mismatch looks like
|
|
19225
|
+
* a greyscale image rather than a blank tile.
|
|
19226
|
+
*/
|
|
19227
|
+
var COG_TILER_COLORMAPS = [
|
|
19228
|
+
"viridis",
|
|
19229
|
+
"magma",
|
|
19230
|
+
"plasma",
|
|
19231
|
+
"inferno",
|
|
19232
|
+
"cividis",
|
|
19233
|
+
"turbo",
|
|
19234
|
+
"terrain",
|
|
19235
|
+
"blues",
|
|
19236
|
+
"greens",
|
|
19237
|
+
"reds",
|
|
19238
|
+
"rdylgn",
|
|
19239
|
+
"spectral",
|
|
19240
|
+
"gray"
|
|
19241
|
+
];
|
|
19059
19242
|
/** A blank tile: cog-tiler returns an empty buffer for tiles outside the COG. */
|
|
19060
19243
|
var EMPTY_TILE = new Uint8Array(0);
|
|
19061
19244
|
/** Upper bound on how many mosaic assets one tile will composite.
|
|
@@ -19169,6 +19352,23 @@ var CogTilerEngine = class {
|
|
|
19169
19352
|
this._map = map;
|
|
19170
19353
|
this._deps = deps;
|
|
19171
19354
|
}
|
|
19355
|
+
/**
|
|
19356
|
+
* The colormap names this build of `cog-tiler-wasm` can actually render.
|
|
19357
|
+
*
|
|
19358
|
+
* Read from the module's own `colormaps()` once it has loaded, so upgrading
|
|
19359
|
+
* the package widens the picker without a change here; until then the
|
|
19360
|
+
* conservative {@link COG_TILER_COLORMAPS} baseline stands in. A module too
|
|
19361
|
+
* old to expose `colormaps()` also falls back to the baseline.
|
|
19362
|
+
*/
|
|
19363
|
+
get supportedColormaps() {
|
|
19364
|
+
let names;
|
|
19365
|
+
try {
|
|
19366
|
+
names = this._module?.colormaps?.();
|
|
19367
|
+
} catch {
|
|
19368
|
+
names = void 0;
|
|
19369
|
+
}
|
|
19370
|
+
return new Set(names?.length ? names : COG_TILER_COLORMAPS);
|
|
19371
|
+
}
|
|
19172
19372
|
/** Renders the given layers (in draw order, first = bottom), adding, updating,
|
|
19173
19373
|
* reordering, and removing native MapLibre raster layers to match. */
|
|
19174
19374
|
sync(layers) {
|
|
@@ -19240,7 +19440,9 @@ var CogTilerEngine = class {
|
|
|
19240
19440
|
throw err;
|
|
19241
19441
|
});
|
|
19242
19442
|
this._modulePromise.then(() => {
|
|
19243
|
-
if (
|
|
19443
|
+
if (this._destroyed) return;
|
|
19444
|
+
this._apply();
|
|
19445
|
+
this._deps.onReady?.();
|
|
19244
19446
|
}, () => {});
|
|
19245
19447
|
}
|
|
19246
19448
|
return null;
|
|
@@ -19348,7 +19550,11 @@ var CogTilerEngine = class {
|
|
|
19348
19550
|
assets: layer.assets ?? null,
|
|
19349
19551
|
render
|
|
19350
19552
|
});
|
|
19351
|
-
if (this._map.getLayer(lyrId))
|
|
19553
|
+
if (this._map.getLayer(lyrId)) {
|
|
19554
|
+
this._map.setPaintProperty(lyrId, "raster-opacity", layer.state.opacity);
|
|
19555
|
+
const { minZoom, maxZoom } = resolveZoomRange(layer.state);
|
|
19556
|
+
this._map.setLayerZoomRange(lyrId, minZoom, maxZoom);
|
|
19557
|
+
}
|
|
19352
19558
|
}
|
|
19353
19559
|
_addMapLayer(layer, renderKey) {
|
|
19354
19560
|
const srcId = this._srcId(layer.id);
|
|
@@ -19653,10 +19859,13 @@ var TiTilerEngine = class {
|
|
|
19653
19859
|
...tilejson.bounds ? { bounds: tilejson.bounds } : {},
|
|
19654
19860
|
...typeof tilejson.maxzoom === "number" ? { maxzoom: tilejson.maxzoom } : {}
|
|
19655
19861
|
});
|
|
19862
|
+
const { minZoom, maxZoom } = resolveZoomRange(layer.state);
|
|
19656
19863
|
this._map.addLayer({
|
|
19657
19864
|
id: lyrId,
|
|
19658
19865
|
type: "raster",
|
|
19659
19866
|
source: srcId,
|
|
19867
|
+
minzoom: minZoom,
|
|
19868
|
+
maxzoom: maxZoom,
|
|
19660
19869
|
paint: { "raster-opacity": layer.state.opacity }
|
|
19661
19870
|
}, this._beforeMapId(layer));
|
|
19662
19871
|
} catch (err) {
|
|
@@ -19688,7 +19897,11 @@ var TiTilerEngine = class {
|
|
|
19688
19897
|
}
|
|
19689
19898
|
_setOpacity(layer) {
|
|
19690
19899
|
const lyrId = this._lyrId(layer.id);
|
|
19691
|
-
if (this._map.getLayer(lyrId))
|
|
19900
|
+
if (this._map.getLayer(lyrId)) {
|
|
19901
|
+
this._map.setPaintProperty(lyrId, "raster-opacity", layer.state.opacity);
|
|
19902
|
+
const { minZoom, maxZoom } = resolveZoomRange(layer.state);
|
|
19903
|
+
this._map.setLayerZoomRange(lyrId, minZoom, maxZoom);
|
|
19904
|
+
}
|
|
19692
19905
|
}
|
|
19693
19906
|
/** Enforces the draw order (first = bottom), honoring each layer's style
|
|
19694
19907
|
* beforeId when present. */
|
|
@@ -20020,133 +20233,6 @@ async function loadMosaic(url, signal) {
|
|
|
20020
20233
|
return parseMosaic(await resp.json());
|
|
20021
20234
|
}
|
|
20022
20235
|
//#endregion
|
|
20023
|
-
//#region src/lib/state/RasterLayer.ts
|
|
20024
|
-
/** Default visualization state for a freshly added layer. Mode/bands are
|
|
20025
|
-
* re-picked automatically once the band count is known (unless the caller
|
|
20026
|
-
* supplied them explicitly); single-band rasters default to the image's
|
|
20027
|
-
* embedded color table when present ('palette') or grayscale otherwise. */
|
|
20028
|
-
var DEFAULT_LAYER_STATE = {
|
|
20029
|
-
mode: "rgb",
|
|
20030
|
-
bands: [
|
|
20031
|
-
1,
|
|
20032
|
-
2,
|
|
20033
|
-
3
|
|
20034
|
-
],
|
|
20035
|
-
rescale: null,
|
|
20036
|
-
colormap: "gray",
|
|
20037
|
-
reversed: false,
|
|
20038
|
-
nodata: "auto",
|
|
20039
|
-
opacity: 1,
|
|
20040
|
-
gamma: 1,
|
|
20041
|
-
stretch: "linear",
|
|
20042
|
-
visible: true
|
|
20043
|
-
};
|
|
20044
|
-
/**
|
|
20045
|
-
* Creates a complete layer state from optional overrides.
|
|
20046
|
-
*
|
|
20047
|
-
* @param overrides - Partial state merged over the defaults
|
|
20048
|
-
* @returns A fully populated RasterLayerState
|
|
20049
|
-
*/
|
|
20050
|
-
function createLayerState(overrides) {
|
|
20051
|
-
return {
|
|
20052
|
-
...DEFAULT_LAYER_STATE,
|
|
20053
|
-
...overrides
|
|
20054
|
-
};
|
|
20055
|
-
}
|
|
20056
|
-
/**
|
|
20057
|
-
* Derives the public snapshot for a layer.
|
|
20058
|
-
*
|
|
20059
|
-
* @param layer - Internal layer record
|
|
20060
|
-
* @returns A read-only info object safe to hand to consumers
|
|
20061
|
-
*/
|
|
20062
|
-
function toLayerInfo(layer) {
|
|
20063
|
-
return {
|
|
20064
|
-
id: layer.id,
|
|
20065
|
-
name: layer.name,
|
|
20066
|
-
source: layer.source,
|
|
20067
|
-
memberUrls: layer.members ? layer.members.map((m) => m.url) : null,
|
|
20068
|
-
bandCount: layer.bandCount,
|
|
20069
|
-
bandNames: layer.bandNames ? new Map(layer.bandNames) : null,
|
|
20070
|
-
beforeId: layer.beforeId,
|
|
20071
|
-
attribution: layer.attribution,
|
|
20072
|
-
bounds: layer.bounds ? { ...layer.bounds } : null,
|
|
20073
|
-
loading: layer.loading,
|
|
20074
|
-
error: layer.error,
|
|
20075
|
-
state: { ...layer.state }
|
|
20076
|
-
};
|
|
20077
|
-
}
|
|
20078
|
-
/** True when `lngLat` falls inside `bounds` (edges included). */
|
|
20079
|
-
function containsPoint(bounds, lngLat) {
|
|
20080
|
-
const [lng, lat] = lngLat;
|
|
20081
|
-
return lng >= bounds.west && lng <= bounds.east && lat >= bounds.south && lat <= bounds.north;
|
|
20082
|
-
}
|
|
20083
|
-
/**
|
|
20084
|
-
* The layer's images that could carry a sample at `lngLat`, topmost first.
|
|
20085
|
-
*
|
|
20086
|
-
* A plain layer has exactly one image. A mosaic VRT layer has one per member,
|
|
20087
|
-
* and only the members whose extent covers the point are worth reading — a
|
|
20088
|
-
* member whose bounds have not been reported yet stays a candidate rather than
|
|
20089
|
-
* being skipped, since bounds only arrive once a member renders.
|
|
20090
|
-
*
|
|
20091
|
-
* Members are returned in reverse document order because that is the order they
|
|
20092
|
-
* are drawn in: `LayerManager` builds one deck.gl layer per member in member
|
|
20093
|
-
* order, so later members paint over earlier ones — matching GDAL, where a
|
|
20094
|
-
* VRT's later sources overwrite earlier ones. Sources placed at their natural
|
|
20095
|
-
* position can still overlap (adjacent scenes commonly do), so where they do,
|
|
20096
|
-
* the last one is what the user sees and therefore what a reader should report.
|
|
20097
|
-
*
|
|
20098
|
-
* @param layer - The layer to read from
|
|
20099
|
-
* @param lngLat - The location, [lng, lat] in WGS84
|
|
20100
|
-
* @returns Candidate images, topmost first; empty when the layer has not loaded
|
|
20101
|
-
* or the point falls outside every member
|
|
20102
|
-
*/
|
|
20103
|
-
function imagesAt(layer, lngLat) {
|
|
20104
|
-
if (!layer.members) return layer.geotiff ? [layer.geotiff] : [];
|
|
20105
|
-
return layer.members.filter((m) => !m.bounds || containsPoint(m.bounds, lngLat)).reverse().map((m) => m.geotiff);
|
|
20106
|
-
}
|
|
20107
|
-
/**
|
|
20108
|
-
* The mosaic assets whose extent covers `lngLat`, in manifest order.
|
|
20109
|
-
*
|
|
20110
|
-
* A mosaic manifest layer (MosaicJSON or STAC) has no opened image of its own —
|
|
20111
|
-
* the deck.gl {@link import('@developmentseed/deck.gl-geotiff').MosaicLayer}
|
|
20112
|
-
* opens each asset lazily while it is in view — so a reader works from the
|
|
20113
|
-
* manifest's bboxes and opens what it needs. This is the mosaic counterpart to
|
|
20114
|
-
* {@link imagesAt}, returning URLs because the COGs may not be open yet.
|
|
20115
|
-
*
|
|
20116
|
-
* Unlike a VRT's members these are *not* reversed. A VRT has a defined paint
|
|
20117
|
-
* order (later sources overwrite earlier ones), but a mosaic's assets are drawn
|
|
20118
|
-
* in whatever order its spatial index returns them, so no candidate is reliably
|
|
20119
|
-
* "topmost". Manifest order is used instead: it matches MosaicJSON's convention
|
|
20120
|
-
* that the first asset listed for a location is the preferred one. Where assets
|
|
20121
|
-
* genuinely overlap the drawn pixel is ambiguous, and the first covering asset
|
|
20122
|
-
* is as defensible a report as any.
|
|
20123
|
-
*
|
|
20124
|
-
* @param layer - The layer to read from
|
|
20125
|
-
* @param lngLat - The location, [lng, lat] in WGS84
|
|
20126
|
-
* @returns Covering asset URLs in manifest order; empty when the layer is not a
|
|
20127
|
-
* mosaic manifest, has not parsed yet, or the point falls outside every asset
|
|
20128
|
-
*/
|
|
20129
|
-
function assetsAt(layer, lngLat) {
|
|
20130
|
-
if (!layer.mosaicAssets) return [];
|
|
20131
|
-
const [lng, lat] = lngLat;
|
|
20132
|
-
return layer.mosaicAssets.filter((a) => lng >= a.bbox[0] && lng <= a.bbox[2] && lat >= a.bbox[1] && lat <= a.bbox[3]).map((a) => a.url);
|
|
20133
|
-
}
|
|
20134
|
-
/**
|
|
20135
|
-
* Derives a display name from a URL or file name: the last path segment
|
|
20136
|
-
* without query string.
|
|
20137
|
-
*
|
|
20138
|
-
* @param source - URL string or file name
|
|
20139
|
-
* @returns A short human-readable layer name
|
|
20140
|
-
*/
|
|
20141
|
-
function deriveLayerName(source) {
|
|
20142
|
-
try {
|
|
20143
|
-
const segment = (source.includes("://") ? new URL(source).pathname : source).split("/").filter(Boolean).pop();
|
|
20144
|
-
return segment ? decodeURIComponent(segment) : source;
|
|
20145
|
-
} catch {
|
|
20146
|
-
return source;
|
|
20147
|
-
}
|
|
20148
|
-
}
|
|
20149
|
-
//#endregion
|
|
20150
20236
|
//#region src/lib/state/LayerManager.ts
|
|
20151
20237
|
/** Default engine when none is configured: the deck.gl GPU pipeline. */
|
|
20152
20238
|
var DEFAULT_ENGINE = "maplibre-gl-raster";
|
|
@@ -20370,6 +20456,8 @@ var LayerManager = class {
|
|
|
20370
20456
|
_attribStyleReady = false;
|
|
20371
20457
|
_onAttribStyleLoad = null;
|
|
20372
20458
|
_destroyed = false;
|
|
20459
|
+
_onZoom = null;
|
|
20460
|
+
_zoomVisibleSig = "";
|
|
20373
20461
|
/**
|
|
20374
20462
|
* Creates a LayerManager bound to a map.
|
|
20375
20463
|
*
|
|
@@ -20386,6 +20474,8 @@ var LayerManager = class {
|
|
|
20386
20474
|
...DEFAULT_DEPS$1,
|
|
20387
20475
|
...deps
|
|
20388
20476
|
};
|
|
20477
|
+
this._onZoom = () => this._syncZoomVisibility();
|
|
20478
|
+
this._map.on("zoom", this._onZoom);
|
|
20389
20479
|
}
|
|
20390
20480
|
/** The id of the layer currently selected for editing, or null. */
|
|
20391
20481
|
get selectedId() {
|
|
@@ -20400,6 +20490,26 @@ var LayerManager = class {
|
|
|
20400
20490
|
return this._titilerEndpoint;
|
|
20401
20491
|
}
|
|
20402
20492
|
/**
|
|
20493
|
+
* The colormap names the active engine can actually render, or null when it
|
|
20494
|
+
* supports every colormap the panel offers.
|
|
20495
|
+
*
|
|
20496
|
+
* Only `cog-tiler-wasm` is limited: it knows a fixed set compiled into the
|
|
20497
|
+
* wasm, and renders an unknown name with its `gray` ramp rather than the
|
|
20498
|
+
* requested one — which reads as "the colormap was ignored". The panel
|
|
20499
|
+
* narrows its picker to this set so a user cannot pick a ramp that will not
|
|
20500
|
+
* draw as chosen. The deck.gl engine draws the full sprite, and TiTiler
|
|
20501
|
+
* resolves names server-side.
|
|
20502
|
+
*
|
|
20503
|
+
* The set comes from the loaded engine, not a hard-coded list, so a
|
|
20504
|
+
* `cog-tiler-wasm` release that adds ramps widens the picker with no change
|
|
20505
|
+
* here. Before the engine exists (or its module has loaded) the conservative
|
|
20506
|
+
* {@link COG_TILER_COLORMAPS} baseline applies.
|
|
20507
|
+
*/
|
|
20508
|
+
get supportedColormaps() {
|
|
20509
|
+
if (this._engine !== "cog-tiler-wasm") return null;
|
|
20510
|
+
return this._cogEngine?.supportedColormaps ?? new Set(COG_TILER_COLORMAPS);
|
|
20511
|
+
}
|
|
20512
|
+
/**
|
|
20403
20513
|
* Points the `titiler` engine at a different TiTiler instance. Empty input
|
|
20404
20514
|
* restores the default endpoint. When the `titiler` engine is active, tiles
|
|
20405
20515
|
* refetch from the new server immediately. A no-op when unchanged.
|
|
@@ -20855,6 +20965,10 @@ var LayerManager = class {
|
|
|
20855
20965
|
this._map.off("load", this._onAttribStyleLoad);
|
|
20856
20966
|
this._onAttribStyleLoad = null;
|
|
20857
20967
|
}
|
|
20968
|
+
if (this._onZoom) {
|
|
20969
|
+
this._map.off("zoom", this._onZoom);
|
|
20970
|
+
this._onZoom = null;
|
|
20971
|
+
}
|
|
20858
20972
|
if (this._overlay) {
|
|
20859
20973
|
this._deps.removeOverlay(this._map, this._overlay);
|
|
20860
20974
|
this._overlay = null;
|
|
@@ -20902,7 +21016,8 @@ var LayerManager = class {
|
|
|
20902
21016
|
if (!this._cogEngine) this._cogEngine = new CogTilerEngine(this._map, {
|
|
20903
21017
|
loadModule: this._deps.loadCogTiler,
|
|
20904
21018
|
onBounds: (id, bounds, zoomTo) => this._onCogBounds(id, bounds, zoomTo),
|
|
20905
|
-
onError: (id, error) => this._onCogError(id, error)
|
|
21019
|
+
onError: (id, error) => this._onCogError(id, error),
|
|
21020
|
+
onReady: () => this._emit({ type: "rasterchange" })
|
|
20906
21021
|
});
|
|
20907
21022
|
return this._cogEngine;
|
|
20908
21023
|
}
|
|
@@ -21161,10 +21276,37 @@ var LayerManager = class {
|
|
|
21161
21276
|
return;
|
|
21162
21277
|
}
|
|
21163
21278
|
if (!this._overlay) return;
|
|
21164
|
-
const
|
|
21279
|
+
const renderable = this._layers.filter((l) => (l.geotiff || l.mosaicAssets) && l.state.visible && !this._crsFailed.has(l.id) && this._withinZoomRange(l.state));
|
|
21280
|
+
this._zoomVisibleSig = renderable.map((l) => l.id).join("|");
|
|
21281
|
+
const layers = renderable.flatMap((l) => this._buildCogLayers(l));
|
|
21165
21282
|
this._overlay.setProps({ layers });
|
|
21166
21283
|
}
|
|
21167
21284
|
/**
|
|
21285
|
+
* Whether a layer draws at the map's current zoom, following MapLibre's
|
|
21286
|
+
* per-layer zoom semantics: visible while `minZoom <= zoom < maxZoom`, hidden
|
|
21287
|
+
* outside that. An unset bound falls back to the full [0, 24] range, so a
|
|
21288
|
+
* layer with no constraint always passes.
|
|
21289
|
+
*/
|
|
21290
|
+
_withinZoomRange(state) {
|
|
21291
|
+
const zoom = this._map.getZoom();
|
|
21292
|
+
const min = state.minZoom ?? 0;
|
|
21293
|
+
const max = state.maxZoom ?? 24;
|
|
21294
|
+
return zoom >= min && zoom < max;
|
|
21295
|
+
}
|
|
21296
|
+
/**
|
|
21297
|
+
* Re-renders the deck.gl overlay when the map zoom crosses a layer's min/max
|
|
21298
|
+
* boundary. Runs on every 'zoom' event, so it stays cheap: it rebuilds only
|
|
21299
|
+
* when the set of in-range layers actually changes, leaving pans and in-range
|
|
21300
|
+
* zooms untouched (a needless rebuild would remount the COGLayers and refetch
|
|
21301
|
+
* their tiles). The native engines carry their zoom range on the MapLibre
|
|
21302
|
+
* layer itself, so this is a no-op for them.
|
|
21303
|
+
*/
|
|
21304
|
+
_syncZoomVisibility() {
|
|
21305
|
+
if (this._engine !== "maplibre-gl-raster" || !this._overlay) return;
|
|
21306
|
+
if (this._layers.filter((l) => (l.geotiff || l.mosaicAssets) && l.state.visible && !this._crsFailed.has(l.id) && this._withinZoomRange(l.state)).map((l) => l.id).join("|") === this._zoomVisibleSig) return;
|
|
21307
|
+
this._rebuild();
|
|
21308
|
+
}
|
|
21309
|
+
/**
|
|
21168
21310
|
* The deck.gl layers that draw one managed layer: exactly one for a plain
|
|
21169
21311
|
* raster, and one per member for a mosaic VRT.
|
|
21170
21312
|
*
|
|
@@ -22472,15 +22614,22 @@ var SettingsSection = class {
|
|
|
22472
22614
|
/** Collapsed/expanded state of the Rescale section, preserved across the
|
|
22473
22615
|
* full re-renders this section performs on every structural change. */
|
|
22474
22616
|
_rescaleOpen = true;
|
|
22617
|
+
/** Colormaps the active engine can draw, or null for all. Re-read on every
|
|
22618
|
+
* render, so switching engines renarrows the picker. */
|
|
22619
|
+
_getAllowedColormaps;
|
|
22475
22620
|
/**
|
|
22476
22621
|
* Creates the section.
|
|
22477
22622
|
*
|
|
22478
22623
|
* @param getLayer - Returns the currently selected layer (or null)
|
|
22479
22624
|
* @param setState - Applies a state patch to the selected layer
|
|
22625
|
+
* @param inspect - Pixel-inspect hooks for the Inspect button
|
|
22626
|
+
* @param getAllowedColormaps - Returns the colormaps the active engine can
|
|
22627
|
+
* render, or null when every colormap is drawable
|
|
22480
22628
|
*/
|
|
22481
|
-
constructor(getLayer, setState, inspect) {
|
|
22629
|
+
constructor(getLayer, setState, inspect, getAllowedColormaps) {
|
|
22482
22630
|
this._getLayer = getLayer;
|
|
22483
22631
|
this._inspect = inspect ?? null;
|
|
22632
|
+
this._getAllowedColormaps = getAllowedColormaps ?? (() => null);
|
|
22484
22633
|
this._setState = (patch) => {
|
|
22485
22634
|
this._applying = true;
|
|
22486
22635
|
try {
|
|
@@ -22578,6 +22727,7 @@ var SettingsSection = class {
|
|
|
22578
22727
|
value: state.colormap,
|
|
22579
22728
|
palette: mode === "single" ? layer.palette : null,
|
|
22580
22729
|
stats: mode === "single" ? statsForBand(layer.autoStats, state.bands[0] ?? 1) : null,
|
|
22730
|
+
allowed: this._getAllowedColormaps(),
|
|
22581
22731
|
onChange: (name) => {
|
|
22582
22732
|
this._setState({ colormap: name });
|
|
22583
22733
|
this.render();
|
|
@@ -23083,7 +23233,7 @@ var PanelUI = class {
|
|
|
23083
23233
|
}, (patch) => {
|
|
23084
23234
|
const id = this._manager.selectedId;
|
|
23085
23235
|
if (id) this._manager.setState(id, patch);
|
|
23086
|
-
}, options?.inspect);
|
|
23236
|
+
}, options?.inspect, () => this._manager.supportedColormaps);
|
|
23087
23237
|
this._root = el("div", { className: "mlr-panel" }, engine.el, addData.el, this._layerList.el, this._settings.el);
|
|
23088
23238
|
container.appendChild(this._root);
|
|
23089
23239
|
const onListChange = () => this._renderList();
|
|
@@ -24049,4 +24199,4 @@ var RasterControl = class {
|
|
|
24049
24199
|
//#endregion
|
|
24050
24200
|
export { DEFAULT_TITILER_ENDPOINT as A, COLORMAP_ROW_COUNT as B, throttle as C, loadVrt as D, isVrtUrl as E, loadColormapSprite as F, statsForBand as G, colormaps_default as H, sampleColormapStops as I, mergeAutoStats as J, MAX_SAMPLE_TILES as K, COLORMAP_DISPLAY_NAMES as L, isMosaicJsonUrl as M, PALETTE_COLORMAP as N, parseVrt as O, isKnownColormap as P, createResilientEpsgResolver as Q, COLORMAP_NAMES as R, generateId as S, isVrtFile as T, DEFAULT_INDEX_RANGE as U, colormapDisplayName as V, autoRangeFor as W, percentileFromHistogram as X, mergeBandStats as Y, readBandNames as Z, parseMosaic as _, NORMALIZED_DIFFERENCE_INDICES as a, debounce as b, readPixelValues as c, MAX_MOSAIC_ASSETS as d, MosaicUnsupportedError as f, mosaicMinZoom as g, mosaicInitialView as h, CUSTOM_NORMALIZED_DIFFERENCE as i, TITILER_TMS as j, loadGeoTIFF as k, DEFAULT_ENGINE as l, loadMosaic as m, Colorbar as n, guessBandForRole as o, assetUrlToHttps as p, computeAutoStats as q, CUSTOM_INDEX_ID as r, indexById as s, RasterControl as t, LayerManager as u, clamp as v, VrtUnsupportedError as w, formatNumericValue as x, classNames as y, COLORMAP_OPTIONS as z };
|
|
24051
24201
|
|
|
24052
|
-
//# sourceMappingURL=RasterControl-
|
|
24202
|
+
//# sourceMappingURL=RasterControl-hvApJoZQ.js.map
|