maplibre-gl-layer-control 0.6.0 → 0.6.2
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 +152 -0
- package/dist/index.cjs +3 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.mjs +3 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,6 +25,7 @@ A comprehensive layer control for MapLibre GL with advanced styling capabilities
|
|
|
25
25
|
- ✅ **Accessibility** - Full ARIA support and keyboard navigation
|
|
26
26
|
- ✅ **TypeScript** - Full type safety and IntelliSense support
|
|
27
27
|
- ✅ **React integration** - Optional React components and hooks
|
|
28
|
+
- ✅ **Custom layer adapters** - Integrate non-MapLibre layers (deck.gl, Zarr, etc.)
|
|
28
29
|
|
|
29
30
|
## Installation
|
|
30
31
|
|
|
@@ -159,6 +160,7 @@ function MapComponent() {
|
|
|
159
160
|
| `showOpacitySlider` | `boolean` | `true` | Show opacity slider for layers |
|
|
160
161
|
| `showLayerSymbol` | `boolean` | `true` | Show layer type symbols (colored icons) next to layer names |
|
|
161
162
|
| `excludeDrawnLayers` | `boolean` | `true` | Exclude layers from drawing libraries (Geoman, Mapbox GL Draw, etc.) |
|
|
163
|
+
| `customLayerAdapters` | `CustomLayerAdapter[]` | `undefined` | Adapters for non-MapLibre layers (deck.gl, Zarr, etc.) |
|
|
162
164
|
|
|
163
165
|
### LayerState
|
|
164
166
|
|
|
@@ -218,6 +220,156 @@ When using the `layers` option to specify specific layers, all other layers are
|
|
|
218
220
|
|
|
219
221
|
This allows fine-grained control over which basemap layers are visible while maintaining a simplified layer control interface.
|
|
220
222
|
|
|
223
|
+
### Custom Layer Adapters
|
|
224
|
+
|
|
225
|
+
The layer control supports non-MapLibre layers (such as deck.gl or Zarr layers) through the Custom Layer Adapter interface. This allows you to integrate any custom layer type with the layer control's visibility toggle, opacity slider, and layer list.
|
|
226
|
+
|
|
227
|
+
#### CustomLayerAdapter Interface
|
|
228
|
+
|
|
229
|
+
```typescript
|
|
230
|
+
interface CustomLayerAdapter {
|
|
231
|
+
/** Unique type identifier for this adapter (e.g., 'cog', 'zarr', 'deck') */
|
|
232
|
+
type: string;
|
|
233
|
+
|
|
234
|
+
/** Get all layer IDs managed by this adapter */
|
|
235
|
+
getLayerIds(): string[];
|
|
236
|
+
|
|
237
|
+
/** Get the current state of a layer */
|
|
238
|
+
getLayerState(layerId: string): LayerState | null;
|
|
239
|
+
|
|
240
|
+
/** Set layer visibility */
|
|
241
|
+
setVisibility(layerId: string, visible: boolean): void;
|
|
242
|
+
|
|
243
|
+
/** Set layer opacity (0-1) */
|
|
244
|
+
setOpacity(layerId: string, opacity: number): void;
|
|
245
|
+
|
|
246
|
+
/** Get display name for a layer */
|
|
247
|
+
getName(layerId: string): string;
|
|
248
|
+
|
|
249
|
+
/** Get layer symbol type for UI display (optional) */
|
|
250
|
+
getSymbolType?(layerId: string): string;
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Subscribe to layer changes (add/remove).
|
|
254
|
+
* Returns an unsubscribe function.
|
|
255
|
+
*/
|
|
256
|
+
onLayerChange?(callback: (event: 'add' | 'remove', layerId: string) => void): () => void;
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
#### Implementing a Custom Adapter
|
|
261
|
+
|
|
262
|
+
Here's an example of implementing an adapter for deck.gl layers:
|
|
263
|
+
|
|
264
|
+
```typescript
|
|
265
|
+
import type { CustomLayerAdapter, LayerState } from 'maplibre-gl-layer-control';
|
|
266
|
+
import type { MapboxOverlay } from '@deck.gl/mapbox';
|
|
267
|
+
|
|
268
|
+
class DeckLayerAdapter implements CustomLayerAdapter {
|
|
269
|
+
readonly type = 'deck';
|
|
270
|
+
|
|
271
|
+
private deckOverlay: MapboxOverlay;
|
|
272
|
+
private deckLayers: Map<string, any>;
|
|
273
|
+
private changeCallbacks: Array<(event: 'add' | 'remove', layerId: string) => void> = [];
|
|
274
|
+
|
|
275
|
+
constructor(deckOverlay: MapboxOverlay, deckLayers: Map<string, any>) {
|
|
276
|
+
this.deckOverlay = deckOverlay;
|
|
277
|
+
this.deckLayers = deckLayers;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
getLayerIds(): string[] {
|
|
281
|
+
return Array.from(this.deckLayers.keys());
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
getLayerState(layerId: string): LayerState | null {
|
|
285
|
+
const layer = this.deckLayers.get(layerId);
|
|
286
|
+
if (!layer?.props) return null;
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
visible: layer.props.visible !== false,
|
|
290
|
+
opacity: layer.props.opacity ?? 1,
|
|
291
|
+
name: this.getName(layerId),
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
setVisibility(layerId: string, visible: boolean): void {
|
|
296
|
+
const layer = this.deckLayers.get(layerId);
|
|
297
|
+
if (!layer?.clone) return;
|
|
298
|
+
|
|
299
|
+
// deck.gl layers are immutable; clone with new props
|
|
300
|
+
const updatedLayer = layer.clone({ visible });
|
|
301
|
+
this.deckLayers.set(layerId, updatedLayer);
|
|
302
|
+
this.updateOverlay();
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
setOpacity(layerId: string, opacity: number): void {
|
|
306
|
+
const layer = this.deckLayers.get(layerId);
|
|
307
|
+
if (!layer?.clone) return;
|
|
308
|
+
|
|
309
|
+
const updatedLayer = layer.clone({ opacity });
|
|
310
|
+
this.deckLayers.set(layerId, updatedLayer);
|
|
311
|
+
this.updateOverlay();
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
getName(layerId: string): string {
|
|
315
|
+
return layerId.replace(/[-_]/g, ' ').replace(/\b\w/g, c => c.toUpperCase());
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
getSymbolType(): string {
|
|
319
|
+
return 'raster'; // Use raster symbol for deck.gl layers
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
onLayerChange(callback: (event: 'add' | 'remove', layerId: string) => void): () => void {
|
|
323
|
+
this.changeCallbacks.push(callback);
|
|
324
|
+
return () => {
|
|
325
|
+
const idx = this.changeCallbacks.indexOf(callback);
|
|
326
|
+
if (idx >= 0) this.changeCallbacks.splice(idx, 1);
|
|
327
|
+
};
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// Call this when layers are added/removed
|
|
331
|
+
notifyLayerAdded(layerId: string): void {
|
|
332
|
+
this.changeCallbacks.forEach(cb => cb('add', layerId));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
notifyLayerRemoved(layerId: string): void {
|
|
336
|
+
this.changeCallbacks.forEach(cb => cb('remove', layerId));
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
private updateOverlay(): void {
|
|
340
|
+
this.deckOverlay.setProps({ layers: Array.from(this.deckLayers.values()) });
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
#### Using Custom Adapters
|
|
346
|
+
|
|
347
|
+
Pass your custom adapters to the `customLayerAdapters` option:
|
|
348
|
+
|
|
349
|
+
```typescript
|
|
350
|
+
import { LayerControl } from 'maplibre-gl-layer-control';
|
|
351
|
+
|
|
352
|
+
// Create your custom adapter
|
|
353
|
+
const deckAdapter = new DeckLayerAdapter(deckOverlay, deckLayers);
|
|
354
|
+
|
|
355
|
+
// Create the layer control with the adapter
|
|
356
|
+
const layerControl = new LayerControl({
|
|
357
|
+
collapsed: false,
|
|
358
|
+
customLayerAdapters: [deckAdapter]
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
map.addControl(layerControl, 'top-right');
|
|
362
|
+
|
|
363
|
+
// When you add a new deck.gl layer, notify the adapter
|
|
364
|
+
deckLayers.set('my-deck-layer', myDeckLayer);
|
|
365
|
+
deckAdapter.notifyLayerAdded('my-deck-layer');
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
#### Limitations
|
|
369
|
+
|
|
370
|
+
- **Style Editor**: The style editor (gear icon) is not available for custom layers since they don't use MapLibre's paint properties. Clicking the gear icon will show an info panel explaining this.
|
|
371
|
+
- **Opacity Support**: Some layer types (like deck.gl's COGLayer) may not support dynamic opacity changes due to underlying library limitations. In these cases, the opacity slider will have no effect.
|
|
372
|
+
|
|
221
373
|
## Development
|
|
222
374
|
|
|
223
375
|
```bash
|
package/dist/index.cjs
CHANGED
|
@@ -1736,7 +1736,7 @@ class LayerControl {
|
|
|
1736
1736
|
if (!itemEl) return;
|
|
1737
1737
|
const layerState = this.state.layerStates[layerId];
|
|
1738
1738
|
if (layerState == null ? void 0 : layerState.isCustomLayer) {
|
|
1739
|
-
const editor2 = this.createCustomLayerInfoPanel(layerId
|
|
1739
|
+
const editor2 = this.createCustomLayerInfoPanel(layerId);
|
|
1740
1740
|
itemEl.appendChild(editor2);
|
|
1741
1741
|
this.styleEditors.set(layerId, editor2);
|
|
1742
1742
|
this.state.activeStyleEditor = layerId;
|
|
@@ -1763,7 +1763,7 @@ class LayerControl {
|
|
|
1763
1763
|
/**
|
|
1764
1764
|
* Create info panel for custom layers (style editing not supported)
|
|
1765
1765
|
*/
|
|
1766
|
-
createCustomLayerInfoPanel(layerId
|
|
1766
|
+
createCustomLayerInfoPanel(layerId) {
|
|
1767
1767
|
const editor = document.createElement("div");
|
|
1768
1768
|
editor.className = "layer-control-style-editor layer-control-custom-info";
|
|
1769
1769
|
const header = document.createElement("div");
|
|
@@ -1785,8 +1785,7 @@ class LayerControl {
|
|
|
1785
1785
|
content.className = "style-editor-controls";
|
|
1786
1786
|
const infoText = document.createElement("p");
|
|
1787
1787
|
infoText.className = "layer-control-custom-info-text";
|
|
1788
|
-
|
|
1789
|
-
infoText.textContent = `This is a ${typeLabel} layer. Style editing is not available for this layer type. Use the visibility toggle and opacity slider to control the layer.`;
|
|
1788
|
+
infoText.textContent = `This is a custom layer. Style editing is not available for this layer type. Use the visibility toggle and opacity slider to control the layer.`;
|
|
1790
1789
|
content.appendChild(infoText);
|
|
1791
1790
|
const actions = document.createElement("div");
|
|
1792
1791
|
actions.className = "style-editor-actions";
|