maplibre-gl-raster 0.13.0 → 0.14.1
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/dist/{RasterControl-BDDduhO9.js → RasterControl-DZyNgBTd.js} +225 -158
- package/dist/{RasterControl-BDDduhO9.js.map → RasterControl-DZyNgBTd.js.map} +1 -1
- package/dist/index.mjs +1 -1
- package/dist/react.mjs +1 -1
- package/dist/types/index.d.ts +27 -1
- package/dist/types/react.d.ts +8 -0
- package/package.json +1 -1
|
@@ -19065,6 +19065,149 @@ function classNames(classes) {
|
|
|
19065
19065
|
return Object.entries(classes).filter(([, value]) => value).map(([key]) => key).join(" ");
|
|
19066
19066
|
}
|
|
19067
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
|
|
19068
19211
|
//#region src/lib/state/CogTilerEngine.ts
|
|
19069
19212
|
/**
|
|
19070
19213
|
* The colormaps assumed renderable before the wasm module has loaded.
|
|
@@ -19199,11 +19342,10 @@ var CogTilerEngine = class {
|
|
|
19199
19342
|
_applied = /* @__PURE__ */ new Map();
|
|
19200
19343
|
/** Monotonic tile-URL version per layer, bumped to refetch on settings edits. */
|
|
19201
19344
|
_versions = /* @__PURE__ */ new Map();
|
|
19202
|
-
|
|
19345
|
+
_onStyleData = null;
|
|
19203
19346
|
/** Latches true once the style has loaded. After that, `isStyleLoaded()` may
|
|
19204
|
-
* dip false again while sources load - which must NOT re-gate later syncs
|
|
19205
|
-
*
|
|
19206
|
-
* would be lost. */
|
|
19347
|
+
* dip false again while sources load - which must NOT re-gate later syncs, or
|
|
19348
|
+
* stats-driven re-renders would be lost. */
|
|
19207
19349
|
_styleReady = false;
|
|
19208
19350
|
constructor(map, deps) {
|
|
19209
19351
|
this._map = map;
|
|
@@ -19242,9 +19384,9 @@ var CogTilerEngine = class {
|
|
|
19242
19384
|
/** Tears the engine down: removes its layers, the protocol, and all caches. */
|
|
19243
19385
|
destroy() {
|
|
19244
19386
|
this._destroyed = true;
|
|
19245
|
-
if (this.
|
|
19246
|
-
this._map.off("
|
|
19247
|
-
this.
|
|
19387
|
+
if (this._onStyleData) {
|
|
19388
|
+
this._map.off("styledata", this._onStyleData);
|
|
19389
|
+
this._onStyleData = null;
|
|
19248
19390
|
}
|
|
19249
19391
|
this.clear();
|
|
19250
19392
|
if (this._protocolRegistered) {
|
|
@@ -19263,13 +19405,15 @@ var CogTilerEngine = class {
|
|
|
19263
19405
|
if (this._destroyed) return;
|
|
19264
19406
|
if (!this._styleReady) if (this._map.isStyleLoaded()) this._styleReady = true;
|
|
19265
19407
|
else {
|
|
19266
|
-
if (!this.
|
|
19267
|
-
this.
|
|
19268
|
-
this.
|
|
19408
|
+
if (!this._onStyleData) {
|
|
19409
|
+
this._onStyleData = () => {
|
|
19410
|
+
if (this._destroyed || !this._map.isStyleLoaded()) return;
|
|
19411
|
+
this._map.off("styledata", this._onStyleData);
|
|
19412
|
+
this._onStyleData = null;
|
|
19269
19413
|
this._styleReady = true;
|
|
19270
19414
|
this._apply();
|
|
19271
19415
|
};
|
|
19272
|
-
this._map.
|
|
19416
|
+
this._map.on("styledata", this._onStyleData);
|
|
19273
19417
|
}
|
|
19274
19418
|
return;
|
|
19275
19419
|
}
|
|
@@ -19407,7 +19551,11 @@ var CogTilerEngine = class {
|
|
|
19407
19551
|
assets: layer.assets ?? null,
|
|
19408
19552
|
render
|
|
19409
19553
|
});
|
|
19410
|
-
if (this._map.getLayer(lyrId))
|
|
19554
|
+
if (this._map.getLayer(lyrId)) {
|
|
19555
|
+
this._map.setPaintProperty(lyrId, "raster-opacity", layer.state.opacity);
|
|
19556
|
+
const { minZoom, maxZoom } = resolveZoomRange(layer.state);
|
|
19557
|
+
this._map.setLayerZoomRange(lyrId, minZoom, maxZoom);
|
|
19558
|
+
}
|
|
19411
19559
|
}
|
|
19412
19560
|
_addMapLayer(layer, renderKey) {
|
|
19413
19561
|
const srcId = this._srcId(layer.id);
|
|
@@ -19581,7 +19729,7 @@ var TiTilerEngine = class {
|
|
|
19581
19729
|
_pending = /* @__PURE__ */ new Map();
|
|
19582
19730
|
/** Layers whose bounds were already reported (report once per source url). */
|
|
19583
19731
|
_boundsKey = /* @__PURE__ */ new Map();
|
|
19584
|
-
|
|
19732
|
+
_onStyleData = null;
|
|
19585
19733
|
/** Latches true once the style has loaded; see CogTilerEngine for the
|
|
19586
19734
|
* rationale (isStyleLoaded may dip false again while sources load). */
|
|
19587
19735
|
_styleReady = false;
|
|
@@ -19618,9 +19766,9 @@ var TiTilerEngine = class {
|
|
|
19618
19766
|
/** Tears the engine down: removes its layers and all caches. */
|
|
19619
19767
|
destroy() {
|
|
19620
19768
|
this._destroyed = true;
|
|
19621
|
-
if (this.
|
|
19622
|
-
this._map.off("
|
|
19623
|
-
this.
|
|
19769
|
+
if (this._onStyleData) {
|
|
19770
|
+
this._map.off("styledata", this._onStyleData);
|
|
19771
|
+
this._onStyleData = null;
|
|
19624
19772
|
}
|
|
19625
19773
|
if (this._onMapError) {
|
|
19626
19774
|
this._map.off?.("error", this._onMapError);
|
|
@@ -19650,13 +19798,15 @@ var TiTilerEngine = class {
|
|
|
19650
19798
|
if (this._destroyed) return;
|
|
19651
19799
|
if (!this._styleReady) if (this._map.isStyleLoaded()) this._styleReady = true;
|
|
19652
19800
|
else {
|
|
19653
|
-
if (!this.
|
|
19654
|
-
this.
|
|
19655
|
-
this.
|
|
19801
|
+
if (!this._onStyleData) {
|
|
19802
|
+
this._onStyleData = () => {
|
|
19803
|
+
if (this._destroyed || !this._map.isStyleLoaded()) return;
|
|
19804
|
+
this._map.off("styledata", this._onStyleData);
|
|
19805
|
+
this._onStyleData = null;
|
|
19656
19806
|
this._styleReady = true;
|
|
19657
19807
|
this._apply();
|
|
19658
19808
|
};
|
|
19659
|
-
this._map.
|
|
19809
|
+
this._map.on("styledata", this._onStyleData);
|
|
19660
19810
|
}
|
|
19661
19811
|
return;
|
|
19662
19812
|
}
|
|
@@ -19712,10 +19862,13 @@ var TiTilerEngine = class {
|
|
|
19712
19862
|
...tilejson.bounds ? { bounds: tilejson.bounds } : {},
|
|
19713
19863
|
...typeof tilejson.maxzoom === "number" ? { maxzoom: tilejson.maxzoom } : {}
|
|
19714
19864
|
});
|
|
19865
|
+
const { minZoom, maxZoom } = resolveZoomRange(layer.state);
|
|
19715
19866
|
this._map.addLayer({
|
|
19716
19867
|
id: lyrId,
|
|
19717
19868
|
type: "raster",
|
|
19718
19869
|
source: srcId,
|
|
19870
|
+
minzoom: minZoom,
|
|
19871
|
+
maxzoom: maxZoom,
|
|
19719
19872
|
paint: { "raster-opacity": layer.state.opacity }
|
|
19720
19873
|
}, this._beforeMapId(layer));
|
|
19721
19874
|
} catch (err) {
|
|
@@ -19747,7 +19900,11 @@ var TiTilerEngine = class {
|
|
|
19747
19900
|
}
|
|
19748
19901
|
_setOpacity(layer) {
|
|
19749
19902
|
const lyrId = this._lyrId(layer.id);
|
|
19750
|
-
if (this._map.getLayer(lyrId))
|
|
19903
|
+
if (this._map.getLayer(lyrId)) {
|
|
19904
|
+
this._map.setPaintProperty(lyrId, "raster-opacity", layer.state.opacity);
|
|
19905
|
+
const { minZoom, maxZoom } = resolveZoomRange(layer.state);
|
|
19906
|
+
this._map.setLayerZoomRange(lyrId, minZoom, maxZoom);
|
|
19907
|
+
}
|
|
19751
19908
|
}
|
|
19752
19909
|
/** Enforces the draw order (first = bottom), honoring each layer's style
|
|
19753
19910
|
* beforeId when present. */
|
|
@@ -20079,133 +20236,6 @@ async function loadMosaic(url, signal) {
|
|
|
20079
20236
|
return parseMosaic(await resp.json());
|
|
20080
20237
|
}
|
|
20081
20238
|
//#endregion
|
|
20082
|
-
//#region src/lib/state/RasterLayer.ts
|
|
20083
|
-
/** Default visualization state for a freshly added layer. Mode/bands are
|
|
20084
|
-
* re-picked automatically once the band count is known (unless the caller
|
|
20085
|
-
* supplied them explicitly); single-band rasters default to the image's
|
|
20086
|
-
* embedded color table when present ('palette') or grayscale otherwise. */
|
|
20087
|
-
var DEFAULT_LAYER_STATE = {
|
|
20088
|
-
mode: "rgb",
|
|
20089
|
-
bands: [
|
|
20090
|
-
1,
|
|
20091
|
-
2,
|
|
20092
|
-
3
|
|
20093
|
-
],
|
|
20094
|
-
rescale: null,
|
|
20095
|
-
colormap: "gray",
|
|
20096
|
-
reversed: false,
|
|
20097
|
-
nodata: "auto",
|
|
20098
|
-
opacity: 1,
|
|
20099
|
-
gamma: 1,
|
|
20100
|
-
stretch: "linear",
|
|
20101
|
-
visible: true
|
|
20102
|
-
};
|
|
20103
|
-
/**
|
|
20104
|
-
* Creates a complete layer state from optional overrides.
|
|
20105
|
-
*
|
|
20106
|
-
* @param overrides - Partial state merged over the defaults
|
|
20107
|
-
* @returns A fully populated RasterLayerState
|
|
20108
|
-
*/
|
|
20109
|
-
function createLayerState(overrides) {
|
|
20110
|
-
return {
|
|
20111
|
-
...DEFAULT_LAYER_STATE,
|
|
20112
|
-
...overrides
|
|
20113
|
-
};
|
|
20114
|
-
}
|
|
20115
|
-
/**
|
|
20116
|
-
* Derives the public snapshot for a layer.
|
|
20117
|
-
*
|
|
20118
|
-
* @param layer - Internal layer record
|
|
20119
|
-
* @returns A read-only info object safe to hand to consumers
|
|
20120
|
-
*/
|
|
20121
|
-
function toLayerInfo(layer) {
|
|
20122
|
-
return {
|
|
20123
|
-
id: layer.id,
|
|
20124
|
-
name: layer.name,
|
|
20125
|
-
source: layer.source,
|
|
20126
|
-
memberUrls: layer.members ? layer.members.map((m) => m.url) : null,
|
|
20127
|
-
bandCount: layer.bandCount,
|
|
20128
|
-
bandNames: layer.bandNames ? new Map(layer.bandNames) : null,
|
|
20129
|
-
beforeId: layer.beforeId,
|
|
20130
|
-
attribution: layer.attribution,
|
|
20131
|
-
bounds: layer.bounds ? { ...layer.bounds } : null,
|
|
20132
|
-
loading: layer.loading,
|
|
20133
|
-
error: layer.error,
|
|
20134
|
-
state: { ...layer.state }
|
|
20135
|
-
};
|
|
20136
|
-
}
|
|
20137
|
-
/** True when `lngLat` falls inside `bounds` (edges included). */
|
|
20138
|
-
function containsPoint(bounds, lngLat) {
|
|
20139
|
-
const [lng, lat] = lngLat;
|
|
20140
|
-
return lng >= bounds.west && lng <= bounds.east && lat >= bounds.south && lat <= bounds.north;
|
|
20141
|
-
}
|
|
20142
|
-
/**
|
|
20143
|
-
* The layer's images that could carry a sample at `lngLat`, topmost first.
|
|
20144
|
-
*
|
|
20145
|
-
* A plain layer has exactly one image. A mosaic VRT layer has one per member,
|
|
20146
|
-
* and only the members whose extent covers the point are worth reading — a
|
|
20147
|
-
* member whose bounds have not been reported yet stays a candidate rather than
|
|
20148
|
-
* being skipped, since bounds only arrive once a member renders.
|
|
20149
|
-
*
|
|
20150
|
-
* Members are returned in reverse document order because that is the order they
|
|
20151
|
-
* are drawn in: `LayerManager` builds one deck.gl layer per member in member
|
|
20152
|
-
* order, so later members paint over earlier ones — matching GDAL, where a
|
|
20153
|
-
* VRT's later sources overwrite earlier ones. Sources placed at their natural
|
|
20154
|
-
* position can still overlap (adjacent scenes commonly do), so where they do,
|
|
20155
|
-
* the last one is what the user sees and therefore what a reader should report.
|
|
20156
|
-
*
|
|
20157
|
-
* @param layer - The layer to read from
|
|
20158
|
-
* @param lngLat - The location, [lng, lat] in WGS84
|
|
20159
|
-
* @returns Candidate images, topmost first; empty when the layer has not loaded
|
|
20160
|
-
* or the point falls outside every member
|
|
20161
|
-
*/
|
|
20162
|
-
function imagesAt(layer, lngLat) {
|
|
20163
|
-
if (!layer.members) return layer.geotiff ? [layer.geotiff] : [];
|
|
20164
|
-
return layer.members.filter((m) => !m.bounds || containsPoint(m.bounds, lngLat)).reverse().map((m) => m.geotiff);
|
|
20165
|
-
}
|
|
20166
|
-
/**
|
|
20167
|
-
* The mosaic assets whose extent covers `lngLat`, in manifest order.
|
|
20168
|
-
*
|
|
20169
|
-
* A mosaic manifest layer (MosaicJSON or STAC) has no opened image of its own —
|
|
20170
|
-
* the deck.gl {@link import('@developmentseed/deck.gl-geotiff').MosaicLayer}
|
|
20171
|
-
* opens each asset lazily while it is in view — so a reader works from the
|
|
20172
|
-
* manifest's bboxes and opens what it needs. This is the mosaic counterpart to
|
|
20173
|
-
* {@link imagesAt}, returning URLs because the COGs may not be open yet.
|
|
20174
|
-
*
|
|
20175
|
-
* Unlike a VRT's members these are *not* reversed. A VRT has a defined paint
|
|
20176
|
-
* order (later sources overwrite earlier ones), but a mosaic's assets are drawn
|
|
20177
|
-
* in whatever order its spatial index returns them, so no candidate is reliably
|
|
20178
|
-
* "topmost". Manifest order is used instead: it matches MosaicJSON's convention
|
|
20179
|
-
* that the first asset listed for a location is the preferred one. Where assets
|
|
20180
|
-
* genuinely overlap the drawn pixel is ambiguous, and the first covering asset
|
|
20181
|
-
* is as defensible a report as any.
|
|
20182
|
-
*
|
|
20183
|
-
* @param layer - The layer to read from
|
|
20184
|
-
* @param lngLat - The location, [lng, lat] in WGS84
|
|
20185
|
-
* @returns Covering asset URLs in manifest order; empty when the layer is not a
|
|
20186
|
-
* mosaic manifest, has not parsed yet, or the point falls outside every asset
|
|
20187
|
-
*/
|
|
20188
|
-
function assetsAt(layer, lngLat) {
|
|
20189
|
-
if (!layer.mosaicAssets) return [];
|
|
20190
|
-
const [lng, lat] = lngLat;
|
|
20191
|
-
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);
|
|
20192
|
-
}
|
|
20193
|
-
/**
|
|
20194
|
-
* Derives a display name from a URL or file name: the last path segment
|
|
20195
|
-
* without query string.
|
|
20196
|
-
*
|
|
20197
|
-
* @param source - URL string or file name
|
|
20198
|
-
* @returns A short human-readable layer name
|
|
20199
|
-
*/
|
|
20200
|
-
function deriveLayerName(source) {
|
|
20201
|
-
try {
|
|
20202
|
-
const segment = (source.includes("://") ? new URL(source).pathname : source).split("/").filter(Boolean).pop();
|
|
20203
|
-
return segment ? decodeURIComponent(segment) : source;
|
|
20204
|
-
} catch {
|
|
20205
|
-
return source;
|
|
20206
|
-
}
|
|
20207
|
-
}
|
|
20208
|
-
//#endregion
|
|
20209
20239
|
//#region src/lib/state/LayerManager.ts
|
|
20210
20240
|
/** Default engine when none is configured: the deck.gl GPU pipeline. */
|
|
20211
20241
|
var DEFAULT_ENGINE = "maplibre-gl-raster";
|
|
@@ -20427,8 +20457,10 @@ var LayerManager = class {
|
|
|
20427
20457
|
_crsFailed = /* @__PURE__ */ new Set();
|
|
20428
20458
|
_attributions = new globalThis.Map();
|
|
20429
20459
|
_attribStyleReady = false;
|
|
20430
|
-
|
|
20460
|
+
_onAttribStyleData = null;
|
|
20431
20461
|
_destroyed = false;
|
|
20462
|
+
_onZoom = null;
|
|
20463
|
+
_zoomVisibleSig = "";
|
|
20432
20464
|
/**
|
|
20433
20465
|
* Creates a LayerManager bound to a map.
|
|
20434
20466
|
*
|
|
@@ -20445,6 +20477,8 @@ var LayerManager = class {
|
|
|
20445
20477
|
...DEFAULT_DEPS$1,
|
|
20446
20478
|
...deps
|
|
20447
20479
|
};
|
|
20480
|
+
this._onZoom = () => this._syncZoomVisibility();
|
|
20481
|
+
this._map.on("zoom", this._onZoom);
|
|
20448
20482
|
}
|
|
20449
20483
|
/** The id of the layer currently selected for editing, or null. */
|
|
20450
20484
|
get selectedId() {
|
|
@@ -20930,9 +20964,13 @@ var LayerManager = class {
|
|
|
20930
20964
|
this._layers = [];
|
|
20931
20965
|
this._selectedId = null;
|
|
20932
20966
|
for (const id of [...this._attributions.keys()]) this._removeAttribution(id);
|
|
20933
|
-
if (this.
|
|
20934
|
-
this._map.off("
|
|
20935
|
-
this.
|
|
20967
|
+
if (this._onAttribStyleData) {
|
|
20968
|
+
this._map.off("styledata", this._onAttribStyleData);
|
|
20969
|
+
this._onAttribStyleData = null;
|
|
20970
|
+
}
|
|
20971
|
+
if (this._onZoom) {
|
|
20972
|
+
this._map.off("zoom", this._onZoom);
|
|
20973
|
+
this._onZoom = null;
|
|
20936
20974
|
}
|
|
20937
20975
|
if (this._overlay) {
|
|
20938
20976
|
this._deps.removeOverlay(this._map, this._overlay);
|
|
@@ -21182,13 +21220,15 @@ var LayerManager = class {
|
|
|
21182
21220
|
_syncAttributions() {
|
|
21183
21221
|
if (!this._attribStyleReady) if (this._map.isStyleLoaded()) this._attribStyleReady = true;
|
|
21184
21222
|
else {
|
|
21185
|
-
if (!this.
|
|
21186
|
-
this.
|
|
21187
|
-
this.
|
|
21223
|
+
if (!this._onAttribStyleData) {
|
|
21224
|
+
this._onAttribStyleData = () => {
|
|
21225
|
+
if (!this._map.isStyleLoaded()) return;
|
|
21226
|
+
this._map.off("styledata", this._onAttribStyleData);
|
|
21227
|
+
this._onAttribStyleData = null;
|
|
21188
21228
|
this._attribStyleReady = true;
|
|
21189
21229
|
this._syncAttributions();
|
|
21190
21230
|
};
|
|
21191
|
-
this._map.
|
|
21231
|
+
this._map.on("styledata", this._onAttribStyleData);
|
|
21192
21232
|
}
|
|
21193
21233
|
return;
|
|
21194
21234
|
}
|
|
@@ -21241,10 +21281,37 @@ var LayerManager = class {
|
|
|
21241
21281
|
return;
|
|
21242
21282
|
}
|
|
21243
21283
|
if (!this._overlay) return;
|
|
21244
|
-
const
|
|
21284
|
+
const renderable = this._layers.filter((l) => (l.geotiff || l.mosaicAssets) && l.state.visible && !this._crsFailed.has(l.id) && this._withinZoomRange(l.state));
|
|
21285
|
+
this._zoomVisibleSig = renderable.map((l) => l.id).join("|");
|
|
21286
|
+
const layers = renderable.flatMap((l) => this._buildCogLayers(l));
|
|
21245
21287
|
this._overlay.setProps({ layers });
|
|
21246
21288
|
}
|
|
21247
21289
|
/**
|
|
21290
|
+
* Whether a layer draws at the map's current zoom, following MapLibre's
|
|
21291
|
+
* per-layer zoom semantics: visible while `minZoom <= zoom < maxZoom`, hidden
|
|
21292
|
+
* outside that. An unset bound falls back to the full [0, 24] range, so a
|
|
21293
|
+
* layer with no constraint always passes.
|
|
21294
|
+
*/
|
|
21295
|
+
_withinZoomRange(state) {
|
|
21296
|
+
const zoom = this._map.getZoom();
|
|
21297
|
+
const min = state.minZoom ?? 0;
|
|
21298
|
+
const max = state.maxZoom ?? 24;
|
|
21299
|
+
return zoom >= min && zoom < max;
|
|
21300
|
+
}
|
|
21301
|
+
/**
|
|
21302
|
+
* Re-renders the deck.gl overlay when the map zoom crosses a layer's min/max
|
|
21303
|
+
* boundary. Runs on every 'zoom' event, so it stays cheap: it rebuilds only
|
|
21304
|
+
* when the set of in-range layers actually changes, leaving pans and in-range
|
|
21305
|
+
* zooms untouched (a needless rebuild would remount the COGLayers and refetch
|
|
21306
|
+
* their tiles). The native engines carry their zoom range on the MapLibre
|
|
21307
|
+
* layer itself, so this is a no-op for them.
|
|
21308
|
+
*/
|
|
21309
|
+
_syncZoomVisibility() {
|
|
21310
|
+
if (this._engine !== "maplibre-gl-raster" || !this._overlay) return;
|
|
21311
|
+
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;
|
|
21312
|
+
this._rebuild();
|
|
21313
|
+
}
|
|
21314
|
+
/**
|
|
21248
21315
|
* The deck.gl layers that draw one managed layer: exactly one for a plain
|
|
21249
21316
|
* raster, and one per member for a mosaic VRT.
|
|
21250
21317
|
*
|
|
@@ -24137,4 +24204,4 @@ var RasterControl = class {
|
|
|
24137
24204
|
//#endregion
|
|
24138
24205
|
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 };
|
|
24139
24206
|
|
|
24140
|
-
//# sourceMappingURL=RasterControl-
|
|
24207
|
+
//# sourceMappingURL=RasterControl-DZyNgBTd.js.map
|