maplibre-gl-components 0.9.0 → 0.10.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.
Files changed (47) hide show
  1. package/README.md +667 -440
  2. package/dist/{ControlGrid-BCMVmB72.js → ControlGrid-C4frbPj9.js} +455 -59
  3. package/dist/ControlGrid-C4frbPj9.js.map +1 -0
  4. package/dist/{ControlGrid-BQ1k9k7k.cjs → ControlGrid-EJTb0ceR.cjs} +422 -26
  5. package/dist/ControlGrid-EJTb0ceR.cjs.map +1 -0
  6. package/dist/index.cjs +2373 -53
  7. package/dist/index.cjs.map +1 -1
  8. package/dist/index.mjs +2407 -87
  9. package/dist/index.mjs.map +1 -1
  10. package/dist/maplibre-gl-components.css +1329 -139
  11. package/dist/react.cjs +112 -34
  12. package/dist/react.cjs.map +1 -1
  13. package/dist/react.mjs +112 -34
  14. package/dist/react.mjs.map +1 -1
  15. package/dist/types/index.d.ts +6 -2
  16. package/dist/types/index.d.ts.map +1 -1
  17. package/dist/types/lib/adapters/AddVectorAdapter.d.ts.map +1 -1
  18. package/dist/types/lib/adapters/PMTilesLayerAdapter.d.ts.map +1 -1
  19. package/dist/types/lib/core/AddVector.d.ts.map +1 -1
  20. package/dist/types/lib/core/BookmarkControl.d.ts +172 -0
  21. package/dist/types/lib/core/BookmarkControl.d.ts.map +1 -0
  22. package/dist/types/lib/core/CogLayer.d.ts.map +1 -1
  23. package/dist/types/lib/core/ControlGrid.d.ts.map +1 -1
  24. package/dist/types/lib/core/InspectControl.d.ts.map +1 -1
  25. package/dist/types/lib/core/MeasureControl.d.ts +203 -0
  26. package/dist/types/lib/core/MeasureControl.d.ts.map +1 -0
  27. package/dist/types/lib/core/MinimapControl.d.ts +77 -0
  28. package/dist/types/lib/core/MinimapControl.d.ts.map +1 -0
  29. package/dist/types/lib/core/MinimapControlReact.d.ts +32 -0
  30. package/dist/types/lib/core/MinimapControlReact.d.ts.map +1 -0
  31. package/dist/types/lib/core/PMTilesLayer.d.ts.map +1 -1
  32. package/dist/types/lib/core/PrintControl.d.ts +173 -0
  33. package/dist/types/lib/core/PrintControl.d.ts.map +1 -0
  34. package/dist/types/lib/core/StacLayer.d.ts.map +1 -1
  35. package/dist/types/lib/core/StacSearch.d.ts.map +1 -1
  36. package/dist/types/lib/core/ZarrLayer.d.ts.map +1 -1
  37. package/dist/types/lib/core/types.d.ts +426 -1
  38. package/dist/types/lib/core/types.d.ts.map +1 -1
  39. package/dist/types/lib/hooks/index.d.ts +1 -0
  40. package/dist/types/lib/hooks/index.d.ts.map +1 -1
  41. package/dist/types/lib/hooks/useMinimapControl.d.ts +35 -0
  42. package/dist/types/lib/hooks/useMinimapControl.d.ts.map +1 -0
  43. package/dist/types/react.d.ts +3 -2
  44. package/dist/types/react.d.ts.map +1 -1
  45. package/package.json +7 -2
  46. package/dist/ControlGrid-BCMVmB72.js.map +0 -1
  47. package/dist/ControlGrid-BQ1k9k7k.cjs.map +0 -1
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # maplibre-gl-components
2
2
 
3
- Legend, colorbar, basemap switcher, terrain toggle, search, vector data loader, feature inspector, and HTML control components for MapLibre GL JS maps.
3
+ Legend, colorbar, basemap switcher, terrain toggle, search, vector data loader, feature inspector, measurement tools, coordinate display, bookmarks, minimap, and more for MapLibre GL JS maps.
4
4
 
5
5
  [![npm version](https://badge.fury.io/js/maplibre-gl-components.svg)](https://badge.fury.io/js/maplibre-gl-components)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
@@ -23,6 +23,10 @@ Legend, colorbar, basemap switcher, terrain toggle, search, vector data loader,
23
23
  - **ZarrLayerControl** - Load and visualize multi-dimensional Zarr arrays with colormaps
24
24
  - **StacLayerControl** - Load COG layers from STAC (SpatioTemporal Asset Catalog) items
25
25
  - **StacSearchControl** - Search and visualize STAC items from public catalogs (Earth Search, Planetary Computer)
26
+ - **MeasureControl** - Measure distances and areas on the map with multiple unit options
27
+ - **BookmarkControl** - Save and restore map views with localStorage persistence
28
+ - **PrintControl** - Export the map as PNG, JPEG, or PDF with optional title overlay
29
+ - **MinimapControl** - Inset overview map showing the current viewport extent with optional click-to-navigate
26
30
  - **Zoom-based Visibility** - Show/hide components at specific zoom levels with `minzoom`/`maxzoom`
27
31
  - **React Support** - First-class React components and hooks
28
32
  - **TypeScript** - Full type definitions included
@@ -39,13 +43,27 @@ npm install maplibre-gl-components
39
43
  ### Vanilla JavaScript/TypeScript
40
44
 
41
45
  ```typescript
42
- import maplibregl from 'maplibre-gl';
43
- import { Colorbar, Legend, HtmlControl, BasemapControl, TerrainControl, SearchControl, VectorDatasetControl, AddVectorControl, ViewStateControl, CogLayerControl, ZarrLayerControl, StacLayerControl } from 'maplibre-gl-components';
44
- import 'maplibre-gl-components/style.css';
46
+ import maplibregl from "maplibre-gl";
47
+ import {
48
+ Colorbar,
49
+ Legend,
50
+ HtmlControl,
51
+ BasemapControl,
52
+ TerrainControl,
53
+ SearchControl,
54
+ VectorDatasetControl,
55
+ AddVectorControl,
56
+ ViewStateControl,
57
+ CogLayerControl,
58
+ ZarrLayerControl,
59
+ StacLayerControl,
60
+ MinimapControl,
61
+ } from "maplibre-gl-components";
62
+ import "maplibre-gl-components/style.css";
45
63
 
46
64
  const map = new maplibregl.Map({
47
- container: 'map',
48
- style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
65
+ container: "map",
66
+ style: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
49
67
  center: [-98, 38.5],
50
68
  zoom: 4,
51
69
  });
@@ -55,24 +73,24 @@ const terrainControl = new TerrainControl({
55
73
  exaggeration: 1.5,
56
74
  hillshade: true,
57
75
  });
58
- map.addControl(terrainControl, 'top-right');
76
+ map.addControl(terrainControl, "top-right");
59
77
 
60
78
  // Add a search control
61
79
  const searchControl = new SearchControl({
62
- placeholder: 'Search for a place...',
80
+ placeholder: "Search for a place...",
63
81
  flyToZoom: 14,
64
82
  showMarker: true,
65
83
  });
66
- map.addControl(searchControl, 'top-right');
84
+ map.addControl(searchControl, "top-right");
67
85
 
68
86
  // Add a vector dataset loader (file upload and drag-drop)
69
87
  const vectorControl = new VectorDatasetControl({
70
88
  fitBounds: true,
71
89
  });
72
- map.addControl(vectorControl, 'top-left');
90
+ map.addControl(vectorControl, "top-left");
73
91
 
74
- vectorControl.on('load', (event) => {
75
- console.log('Loaded:', event.dataset?.filename);
92
+ vectorControl.on("load", (event) => {
93
+ console.log("Loaded:", event.dataset?.filename);
76
94
  });
77
95
 
78
96
  // Add a view state control
@@ -81,130 +99,139 @@ const viewStateControl = new ViewStateControl({
81
99
  enableBBox: true,
82
100
  precision: 4,
83
101
  });
84
- map.addControl(viewStateControl, 'bottom-left');
102
+ map.addControl(viewStateControl, "bottom-left");
85
103
 
86
- viewStateControl.on('bboxdraw', (event) => {
87
- console.log('Drawn bbox:', event.bbox);
104
+ viewStateControl.on("bboxdraw", (event) => {
105
+ console.log("Drawn bbox:", event.bbox);
88
106
  });
89
107
 
90
108
  // Add a basemap switcher
91
109
  const basemapControl = new BasemapControl({
92
- defaultBasemap: 'OpenStreetMap.Mapnik',
110
+ defaultBasemap: "OpenStreetMap.Mapnik",
93
111
  showSearch: true,
94
- filterGroups: ['OpenStreetMap', 'CartoDB', 'Stadia', 'Esri'],
112
+ filterGroups: ["OpenStreetMap", "CartoDB", "Stadia", "Esri"],
95
113
  });
96
- map.addControl(basemapControl, 'top-left');
114
+ map.addControl(basemapControl, "top-left");
97
115
 
98
116
  // Add a colorbar
99
117
  const colorbar = new Colorbar({
100
- colormap: 'viridis',
118
+ colormap: "viridis",
101
119
  vmin: 0,
102
120
  vmax: 100,
103
- label: 'Temperature',
104
- units: '°C',
105
- orientation: 'vertical',
121
+ label: "Temperature",
122
+ units: "°C",
123
+ orientation: "vertical",
106
124
  });
107
- map.addControl(colorbar, 'bottom-right');
125
+ map.addControl(colorbar, "bottom-right");
108
126
 
109
127
  // Add a legend
110
128
  const legend = new Legend({
111
- title: 'Land Cover',
129
+ title: "Land Cover",
112
130
  items: [
113
- { label: 'Forest', color: '#228B22' },
114
- { label: 'Water', color: '#4169E1' },
115
- { label: 'Urban', color: '#808080' },
131
+ { label: "Forest", color: "#228B22" },
132
+ { label: "Water", color: "#4169E1" },
133
+ { label: "Urban", color: "#808080" },
116
134
  ],
117
135
  collapsible: true,
118
136
  });
119
- map.addControl(legend, 'bottom-left');
137
+ map.addControl(legend, "bottom-left");
120
138
 
121
139
  // Add an HTML control
122
140
  const htmlControl = new HtmlControl({
123
- html: '<div><strong>Stats:</strong> 1,234 features</div>',
141
+ html: "<div><strong>Stats:</strong> 1,234 features</div>",
124
142
  });
125
- map.addControl(htmlControl, 'top-left');
143
+ map.addControl(htmlControl, "top-left");
126
144
 
127
145
  // Update HTML dynamically
128
- htmlControl.setHtml('<div><strong>Stats:</strong> 5,678 features</div>');
146
+ htmlControl.setHtml("<div><strong>Stats:</strong> 5,678 features</div>");
129
147
 
130
148
  // Add a COG layer control (auto-loads the layer)
131
149
  const cogControl = new CogLayerControl({
132
- defaultUrl: 'https://example.com/dem.tif',
133
- defaultColormap: 'terrain',
150
+ defaultUrl: "https://example.com/dem.tif",
151
+ defaultColormap: "terrain",
134
152
  defaultRescaleMin: 0,
135
153
  defaultRescaleMax: 4000,
136
- loadDefaultUrl: true, // Auto-load the layer when control is added
154
+ loadDefaultUrl: true, // Auto-load the layer when control is added
137
155
  });
138
- map.addControl(cogControl, 'top-right');
156
+ map.addControl(cogControl, "top-right");
139
157
 
140
- cogControl.on('layeradd', (event) => {
141
- console.log('COG layer added:', event.url);
158
+ cogControl.on("layeradd", (event) => {
159
+ console.log("COG layer added:", event.url);
142
160
  });
143
161
 
144
162
  // Add a Zarr layer control (auto-loads the layer)
145
163
  const zarrControl = new ZarrLayerControl({
146
- defaultUrl: 'https://example.com/climate.zarr',
147
- defaultVariable: 'temperature',
148
- defaultColormap: ['#440154', '#21918c', '#fde725'],
164
+ defaultUrl: "https://example.com/climate.zarr",
165
+ defaultVariable: "temperature",
166
+ defaultColormap: ["#440154", "#21918c", "#fde725"],
149
167
  defaultClim: [0, 30],
150
- loadDefaultUrl: true, // Auto-load the layer when control is added
168
+ loadDefaultUrl: true, // Auto-load the layer when control is added
151
169
  });
152
- map.addControl(zarrControl, 'top-right');
170
+ map.addControl(zarrControl, "top-right");
153
171
 
154
- zarrControl.on('layeradd', (event) => {
155
- console.log('Zarr layer added:', event.url);
172
+ zarrControl.on("layeradd", (event) => {
173
+ console.log("Zarr layer added:", event.url);
156
174
  });
157
175
 
158
176
  // Add a STAC layer control (loads COG from STAC items)
159
177
  const stacControl = new StacLayerControl({
160
- defaultUrl: 'https://example.com/stac-item.json',
178
+ defaultUrl: "https://example.com/stac-item.json",
161
179
  loadDefaultUrl: true,
162
- defaultColormap: 'viridis',
180
+ defaultColormap: "viridis",
163
181
  defaultRescaleMin: 0,
164
182
  defaultRescaleMax: 255,
165
183
  });
166
- map.addControl(stacControl, 'top-right');
184
+ map.addControl(stacControl, "top-right");
167
185
 
168
- stacControl.on('stacload', (event) => {
169
- console.log('STAC item loaded:', event.url);
186
+ stacControl.on("stacload", (event) => {
187
+ console.log("STAC item loaded:", event.url);
170
188
  });
171
189
 
172
- stacControl.on('layeradd', (event) => {
173
- console.log('STAC layer added:', event.assetKey);
190
+ stacControl.on("layeradd", (event) => {
191
+ console.log("STAC layer added:", event.assetKey);
174
192
  });
175
193
  ```
176
194
 
177
195
  ### React
178
196
 
179
197
  ```tsx
180
- import { useState, useEffect, useRef } from 'react';
181
- import maplibregl from 'maplibre-gl';
182
- import { ColorbarReact, LegendReact, HtmlControlReact, BasemapReact, TerrainReact, SearchControlReact, VectorDatasetReact, ViewStateControlReact } from 'maplibre-gl-components/react';
183
- import 'maplibre-gl-components/style.css';
198
+ import { useState, useEffect, useRef } from "react";
199
+ import maplibregl from "maplibre-gl";
200
+ import {
201
+ ColorbarReact,
202
+ LegendReact,
203
+ HtmlControlReact,
204
+ BasemapReact,
205
+ TerrainReact,
206
+ SearchControlReact,
207
+ VectorDatasetReact,
208
+ ViewStateControlReact,
209
+ } from "maplibre-gl-components/react";
210
+ import "maplibre-gl-components/style.css";
184
211
 
185
212
  function MyMap() {
186
213
  const mapContainer = useRef<HTMLDivElement>(null);
187
214
  const [map, setMap] = useState<maplibregl.Map | null>(null);
188
- const [stats, setStats] = useState('Loading...');
215
+ const [stats, setStats] = useState("Loading...");
189
216
 
190
217
  useEffect(() => {
191
218
  if (!mapContainer.current) return;
192
219
 
193
220
  const mapInstance = new maplibregl.Map({
194
221
  container: mapContainer.current,
195
- style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json',
222
+ style: "https://basemaps.cartocdn.com/gl/positron-gl-style/style.json",
196
223
  center: [-98, 38.5],
197
224
  zoom: 4,
198
225
  });
199
226
 
200
- mapInstance.on('load', () => setMap(mapInstance));
227
+ mapInstance.on("load", () => setMap(mapInstance));
201
228
 
202
229
  return () => mapInstance.remove();
203
230
  }, []);
204
231
 
205
232
  return (
206
- <div style={{ width: '100%', height: '100vh' }}>
207
- <div ref={mapContainer} style={{ width: '100%', height: '100%' }} />
233
+ <div style={{ width: "100%", height: "100vh" }}>
234
+ <div ref={mapContainer} style={{ width: "100%", height: "100%" }} />
208
235
 
209
236
  {map && (
210
237
  <>
@@ -212,7 +239,9 @@ function MyMap() {
212
239
  map={map}
213
240
  fitBounds
214
241
  position="top-left"
215
- onDatasetLoad={(dataset) => console.log('Loaded:', dataset.filename)}
242
+ onDatasetLoad={(dataset) =>
243
+ console.log("Loaded:", dataset.filename)
244
+ }
216
245
  />
217
246
 
218
247
  <ViewStateControlReact
@@ -221,7 +250,7 @@ function MyMap() {
221
250
  enableBBox
222
251
  precision={4}
223
252
  position="bottom-left"
224
- onBBoxDraw={(bbox) => console.log('Drawn bbox:', bbox)}
253
+ onBBoxDraw={(bbox) => console.log("Drawn bbox:", bbox)}
225
254
  />
226
255
 
227
256
  <SearchControlReact
@@ -230,7 +259,7 @@ function MyMap() {
230
259
  flyToZoom={14}
231
260
  showMarker
232
261
  position="top-right"
233
- onResultSelect={(result) => console.log('Selected:', result.name)}
262
+ onResultSelect={(result) => console.log("Selected:", result.name)}
234
263
  />
235
264
 
236
265
  <TerrainReact
@@ -238,14 +267,14 @@ function MyMap() {
238
267
  exaggeration={1.5}
239
268
  hillshade
240
269
  position="top-right"
241
- onTerrainChange={(enabled) => console.log('Terrain:', enabled)}
270
+ onTerrainChange={(enabled) => console.log("Terrain:", enabled)}
242
271
  />
243
272
 
244
273
  <BasemapReact
245
274
  map={map}
246
275
  defaultBasemap="OpenStreetMap.Mapnik"
247
276
  showSearch
248
- filterGroups={['OpenStreetMap', 'CartoDB', 'Stadia']}
277
+ filterGroups={["OpenStreetMap", "CartoDB", "Stadia"]}
249
278
  position="top-left"
250
279
  />
251
280
 
@@ -263,8 +292,8 @@ function MyMap() {
263
292
  map={map}
264
293
  title="Categories"
265
294
  items={[
266
- { label: 'Low', color: '#2166ac' },
267
- { label: 'High', color: '#b2182b' },
295
+ { label: "Low", color: "#2166ac" },
296
+ { label: "High", color: "#b2182b" },
268
297
  ]}
269
298
  position="bottom-left"
270
299
  collapsible
@@ -290,33 +319,33 @@ A continuous gradient colorbar control.
290
319
 
291
320
  ```typescript
292
321
  interface ColorbarOptions {
293
- colormap?: ColormapName | string[]; // Colormap name or custom colors
294
- colorStops?: ColorStop[]; // Fine-grained color control
295
- vmin?: number; // Minimum value (default: 0)
296
- vmax?: number; // Maximum value (default: 1)
297
- label?: string; // Title/label
298
- units?: string; // Units suffix
299
- orientation?: 'horizontal' | 'vertical';
300
- position?: 'top-left' | 'top-right' | 'bottom-left' | 'bottom-right';
301
- barThickness?: number; // Bar width/height in pixels
302
- barLength?: number; // Bar length in pixels
322
+ colormap?: ColormapName | string[]; // Colormap name or custom colors
323
+ colorStops?: ColorStop[]; // Fine-grained color control
324
+ vmin?: number; // Minimum value (default: 0)
325
+ vmax?: number; // Maximum value (default: 1)
326
+ label?: string; // Title/label
327
+ units?: string; // Units suffix
328
+ orientation?: "horizontal" | "vertical";
329
+ position?: "top-left" | "top-right" | "bottom-left" | "bottom-right";
330
+ barThickness?: number; // Bar width/height in pixels
331
+ barLength?: number; // Bar length in pixels
303
332
  ticks?: { count?: number; values?: number[]; format?: (v: number) => string };
304
333
  visible?: boolean;
305
334
  backgroundColor?: string;
306
335
  opacity?: number;
307
336
  fontSize?: number;
308
337
  fontColor?: string;
309
- minzoom?: number; // Min zoom level to show (default: 0)
310
- maxzoom?: number; // Max zoom level to show (default: 24)
338
+ minzoom?: number; // Min zoom level to show (default: 0)
339
+ maxzoom?: number; // Max zoom level to show (default: 24)
311
340
  }
312
341
 
313
342
  // Methods
314
- colorbar.show()
315
- colorbar.hide()
316
- colorbar.update(options)
317
- colorbar.getState()
318
- colorbar.on(event, handler)
319
- colorbar.off(event, handler)
343
+ colorbar.show();
344
+ colorbar.hide();
345
+ colorbar.update(options);
346
+ colorbar.getState();
347
+ colorbar.on(event, handler);
348
+ colorbar.off(event, handler);
320
349
  ```
321
350
 
322
351
  ### Legend
@@ -326,7 +355,7 @@ A categorical legend control.
326
355
  ```typescript
327
356
  interface LegendOptions {
328
357
  title?: string;
329
- items?: LegendItem[]; // { label, color, shape?, icon? }
358
+ items?: LegendItem[]; // { label, color, shape?, icon? }
330
359
  position?: ControlPosition;
331
360
  visible?: boolean;
332
361
  collapsible?: boolean;
@@ -338,29 +367,29 @@ interface LegendOptions {
338
367
  opacity?: number;
339
368
  fontSize?: number;
340
369
  fontColor?: string;
341
- minzoom?: number; // Min zoom level to show (default: 0)
342
- maxzoom?: number; // Max zoom level to show (default: 24)
370
+ minzoom?: number; // Min zoom level to show (default: 0)
371
+ maxzoom?: number; // Max zoom level to show (default: 24)
343
372
  }
344
373
 
345
374
  interface LegendItem {
346
375
  label: string;
347
376
  color: string;
348
- shape?: 'square' | 'circle' | 'line';
377
+ shape?: "square" | "circle" | "line";
349
378
  strokeColor?: string;
350
- icon?: string; // URL to icon image
379
+ icon?: string; // URL to icon image
351
380
  }
352
381
 
353
382
  // Methods
354
- legend.show()
355
- legend.hide()
356
- legend.expand()
357
- legend.collapse()
358
- legend.toggle()
359
- legend.setItems(items)
360
- legend.addItem(item)
361
- legend.removeItem(label)
362
- legend.update(options)
363
- legend.getState()
383
+ legend.show();
384
+ legend.hide();
385
+ legend.expand();
386
+ legend.collapse();
387
+ legend.toggle();
388
+ legend.setItems(items);
389
+ legend.addItem(item);
390
+ legend.removeItem(label);
391
+ legend.update(options);
392
+ legend.getState();
364
393
  ```
365
394
 
366
395
  ### HtmlControl
@@ -369,8 +398,8 @@ A flexible HTML content control.
369
398
 
370
399
  ```typescript
371
400
  interface HtmlControlOptions {
372
- html?: string; // HTML content
373
- element?: HTMLElement; // Or provide a DOM element
401
+ html?: string; // HTML content
402
+ element?: HTMLElement; // Or provide a DOM element
374
403
  position?: ControlPosition;
375
404
  visible?: boolean;
376
405
  backgroundColor?: string;
@@ -379,18 +408,18 @@ interface HtmlControlOptions {
379
408
  opacity?: number;
380
409
  maxWidth?: number;
381
410
  maxHeight?: number;
382
- minzoom?: number; // Min zoom level to show (default: 0)
383
- maxzoom?: number; // Max zoom level to show (default: 24)
411
+ minzoom?: number; // Min zoom level to show (default: 0)
412
+ maxzoom?: number; // Max zoom level to show (default: 24)
384
413
  }
385
414
 
386
415
  // Methods
387
- htmlControl.show()
388
- htmlControl.hide()
389
- htmlControl.setHtml(html) // Update HTML content
390
- htmlControl.setElement(element) // Set DOM element
391
- htmlControl.getElement() // Get content container
392
- htmlControl.update(options)
393
- htmlControl.getState()
416
+ htmlControl.show();
417
+ htmlControl.hide();
418
+ htmlControl.setHtml(html); // Update HTML content
419
+ htmlControl.setElement(element); // Set DOM element
420
+ htmlControl.getElement(); // Get content container
421
+ htmlControl.update(options);
422
+ htmlControl.getState();
394
423
  ```
395
424
 
396
425
  ### BasemapControl
@@ -399,18 +428,18 @@ An interactive basemap switcher that loads providers from [xyzservices](https://
399
428
 
400
429
  ```typescript
401
430
  interface BasemapControlOptions {
402
- basemaps?: BasemapItem[]; // Custom basemaps array
403
- providersUrl?: string; // URL to fetch providers.json (defaults to xyzservices)
404
- defaultBasemap?: string; // Initial basemap ID (e.g., 'OpenStreetMap.Mapnik')
431
+ basemaps?: BasemapItem[]; // Custom basemaps array
432
+ providersUrl?: string; // URL to fetch providers.json (defaults to xyzservices)
433
+ defaultBasemap?: string; // Initial basemap ID (e.g., 'OpenStreetMap.Mapnik')
405
434
  position?: ControlPosition;
406
435
  visible?: boolean;
407
- collapsible?: boolean; // Whether control is collapsible (default: true)
408
- collapsed?: boolean; // Whether control starts collapsed (default: true)
409
- displayMode?: 'dropdown' | 'gallery' | 'list'; // UI mode (default: 'dropdown')
410
- showSearch?: boolean; // Show search input (default: true)
411
- filterGroups?: string[]; // Only include these provider groups
412
- excludeGroups?: string[]; // Exclude these provider groups
413
- excludeBroken?: boolean; // Exclude broken providers (default: true)
436
+ collapsible?: boolean; // Whether control is collapsible (default: true)
437
+ collapsed?: boolean; // Whether control starts collapsed (default: true)
438
+ displayMode?: "dropdown" | "gallery" | "list"; // UI mode (default: 'dropdown')
439
+ showSearch?: boolean; // Show search input (default: true)
440
+ filterGroups?: string[]; // Only include these provider groups
441
+ excludeGroups?: string[]; // Exclude these provider groups
442
+ excludeBroken?: boolean; // Exclude broken providers (default: true)
414
443
  backgroundColor?: string;
415
444
  maxWidth?: number;
416
445
  maxHeight?: number;
@@ -421,13 +450,13 @@ interface BasemapControlOptions {
421
450
  }
422
451
 
423
452
  interface BasemapItem {
424
- id: string; // Unique identifier
425
- name: string; // Display name
426
- group?: string; // Provider group (e.g., 'OpenStreetMap')
427
- url?: string; // XYZ tile URL template
428
- style?: string; // MapLibre style URL
453
+ id: string; // Unique identifier
454
+ name: string; // Display name
455
+ group?: string; // Provider group (e.g., 'OpenStreetMap')
456
+ url?: string; // XYZ tile URL template
457
+ style?: string; // MapLibre style URL
429
458
  attribution?: string;
430
- thumbnail?: string; // Preview image URL
459
+ thumbnail?: string; // Preview image URL
431
460
  maxZoom?: number;
432
461
  minZoom?: number;
433
462
  requiresApiKey?: boolean;
@@ -435,23 +464,24 @@ interface BasemapItem {
435
464
  }
436
465
 
437
466
  // Methods
438
- basemapControl.show()
439
- basemapControl.hide()
440
- basemapControl.expand()
441
- basemapControl.collapse()
442
- basemapControl.toggle()
443
- basemapControl.setBasemap(basemapId) // Switch to a basemap
444
- basemapControl.getBasemaps() // Get available basemaps
445
- basemapControl.addBasemap(basemap) // Add a custom basemap
446
- basemapControl.removeBasemap(id) // Remove a basemap
447
- basemapControl.setApiKey(id, key) // Set API key for a basemap
448
- basemapControl.getSelectedBasemap() // Get currently selected basemap
449
- basemapControl.update(options)
450
- basemapControl.getState()
451
- basemapControl.on('basemapchange', handler) // Listen for basemap changes
467
+ basemapControl.show();
468
+ basemapControl.hide();
469
+ basemapControl.expand();
470
+ basemapControl.collapse();
471
+ basemapControl.toggle();
472
+ basemapControl.setBasemap(basemapId); // Switch to a basemap
473
+ basemapControl.getBasemaps(); // Get available basemaps
474
+ basemapControl.addBasemap(basemap); // Add a custom basemap
475
+ basemapControl.removeBasemap(id); // Remove a basemap
476
+ basemapControl.setApiKey(id, key); // Set API key for a basemap
477
+ basemapControl.getSelectedBasemap(); // Get currently selected basemap
478
+ basemapControl.update(options);
479
+ basemapControl.getState();
480
+ basemapControl.on("basemapchange", handler); // Listen for basemap changes
452
481
  ```
453
482
 
454
483
  **Available Provider Groups:**
484
+
455
485
  - OpenStreetMap, CartoDB, Stadia, Esri, OpenTopoMap
456
486
  - Thunderforest, MapBox, MapTiler (require API keys)
457
487
  - NASAGIBS, OpenSeaMap, and 20+ more
@@ -463,22 +493,22 @@ A collapsible place search control with geocoding support.
463
493
  ```typescript
464
494
  interface SearchControlOptions {
465
495
  position?: ControlPosition;
466
- visible?: boolean; // Default: true
467
- collapsed?: boolean; // Start collapsed (icon only). Default: true
468
- placeholder?: string; // Search input placeholder. Default: 'Search places...'
469
- geocoderUrl?: string; // Geocoding API URL. Default: Nominatim
470
- maxResults?: number; // Max results to show. Default: 5
471
- debounceMs?: number; // Debounce delay in ms. Default: 300
472
- flyToZoom?: number; // Zoom level when selecting result. Default: 14
473
- showMarker?: boolean; // Show marker at selected location. Default: true
474
- markerColor?: string; // Marker color. Default: '#4264fb'
475
- collapseOnSelect?: boolean; // Collapse after selecting. Default: true
476
- clearOnSelect?: boolean; // Clear results after selecting. Default: true
477
- geocoder?: (query: string) => Promise<SearchResult[]>; // Custom geocoder function
496
+ visible?: boolean; // Default: true
497
+ collapsed?: boolean; // Start collapsed (icon only). Default: true
498
+ placeholder?: string; // Search input placeholder. Default: 'Search places...'
499
+ geocoderUrl?: string; // Geocoding API URL. Default: Nominatim
500
+ maxResults?: number; // Max results to show. Default: 5
501
+ debounceMs?: number; // Debounce delay in ms. Default: 300
502
+ flyToZoom?: number; // Zoom level when selecting result. Default: 14
503
+ showMarker?: boolean; // Show marker at selected location. Default: true
504
+ markerColor?: string; // Marker color. Default: '#4264fb'
505
+ collapseOnSelect?: boolean; // Collapse after selecting. Default: true
506
+ clearOnSelect?: boolean; // Clear results after selecting. Default: true
507
+ geocoder?: (query: string) => Promise<SearchResult[]>; // Custom geocoder function
478
508
  backgroundColor?: string;
479
509
  borderRadius?: number;
480
510
  opacity?: number;
481
- width?: number; // Expanded width in pixels. Default: 280
511
+ width?: number; // Expanded width in pixels. Default: 280
482
512
  fontSize?: number;
483
513
  fontColor?: string;
484
514
  minzoom?: number;
@@ -486,30 +516,30 @@ interface SearchControlOptions {
486
516
  }
487
517
 
488
518
  interface SearchResult {
489
- id: string; // Unique identifier
490
- name: string; // Place name
491
- displayName: string; // Full display name with address
492
- lng: number; // Longitude
493
- lat: number; // Latitude
494
- bbox?: [number, number, number, number]; // Bounding box [west, south, east, north]
495
- type?: string; // Place type (city, street, etc.)
496
- importance?: number; // Relevance score
519
+ id: string; // Unique identifier
520
+ name: string; // Place name
521
+ displayName: string; // Full display name with address
522
+ lng: number; // Longitude
523
+ lat: number; // Latitude
524
+ bbox?: [number, number, number, number]; // Bounding box [west, south, east, north]
525
+ type?: string; // Place type (city, street, etc.)
526
+ importance?: number; // Relevance score
497
527
  }
498
528
 
499
529
  // Methods
500
- searchControl.show()
501
- searchControl.hide()
502
- searchControl.expand() // Expand to show input
503
- searchControl.collapse() // Collapse to icon only
504
- searchControl.toggle() // Toggle expanded/collapsed
505
- searchControl.search(query) // Perform a search
506
- searchControl.selectResult(result) // Select a result and fly to it
507
- searchControl.clear() // Clear search and marker
508
- searchControl.update(options)
509
- searchControl.getState()
510
- searchControl.on('resultselect', handler) // Listen for result selection
511
- searchControl.on('search', handler) // Listen for search completion
512
- searchControl.on('clear', handler) // Listen for clear events
530
+ searchControl.show();
531
+ searchControl.hide();
532
+ searchControl.expand(); // Expand to show input
533
+ searchControl.collapse(); // Collapse to icon only
534
+ searchControl.toggle(); // Toggle expanded/collapsed
535
+ searchControl.search(query); // Perform a search
536
+ searchControl.selectResult(result); // Select a result and fly to it
537
+ searchControl.clear(); // Clear search and marker
538
+ searchControl.update(options);
539
+ searchControl.getState();
540
+ searchControl.on("resultselect", handler); // Listen for result selection
541
+ searchControl.on("search", handler); // Listen for search completion
542
+ searchControl.on("clear", handler); // Listen for clear events
513
543
  ```
514
544
 
515
545
  **Geocoding:**
@@ -521,7 +551,7 @@ const searchControl = new SearchControl({
521
551
  geocoder: async (query) => {
522
552
  const response = await fetch(`https://my-geocoder.com/search?q=${query}`);
523
553
  const data = await response.json();
524
- return data.map(item => ({
554
+ return data.map((item) => ({
525
555
  id: item.id,
526
556
  name: item.name,
527
557
  displayName: item.address,
@@ -539,14 +569,14 @@ A control for loading GeoJSON files via file upload button or drag-and-drop.
539
569
  ```typescript
540
570
  interface VectorDatasetControlOptions {
541
571
  position?: ControlPosition;
542
- visible?: boolean; // Default: true
543
- showDropZone?: boolean; // Show overlay when dragging. Default: true
544
- acceptedExtensions?: string[]; // File extensions. Default: ['.geojson', '.json']
545
- multiple?: boolean; // Allow multiple files. Default: true
546
- defaultStyle?: VectorLayerStyle; // Default styling for loaded layers
547
- fitBounds?: boolean; // Fit map to loaded data. Default: true
548
- fitBoundsPadding?: number; // Padding for fitBounds. Default: 50
549
- maxFileSize?: number; // Max file size in bytes. Default: 50MB
572
+ visible?: boolean; // Default: true
573
+ showDropZone?: boolean; // Show overlay when dragging. Default: true
574
+ acceptedExtensions?: string[]; // File extensions. Default: ['.geojson', '.json']
575
+ multiple?: boolean; // Allow multiple files. Default: true
576
+ defaultStyle?: VectorLayerStyle; // Default styling for loaded layers
577
+ fitBounds?: boolean; // Fit map to loaded data. Default: true
578
+ fitBoundsPadding?: number; // Padding for fitBounds. Default: 50
579
+ maxFileSize?: number; // Max file size in bytes. Default: 50MB
550
580
  backgroundColor?: string;
551
581
  borderRadius?: number;
552
582
  opacity?: number;
@@ -555,45 +585,47 @@ interface VectorDatasetControlOptions {
555
585
  }
556
586
 
557
587
  interface VectorLayerStyle {
558
- fillColor?: string; // Polygon fill. Default: '#3388ff'
559
- fillOpacity?: number; // Polygon fill opacity. Default: 0.3
560
- strokeColor?: string; // Line/outline color. Default: '#3388ff'
561
- strokeWidth?: number; // Line width. Default: 2
562
- strokeOpacity?: number; // Line opacity. Default: 1
563
- circleRadius?: number; // Point radius. Default: 6
564
- circleColor?: string; // Point color. Default: '#3388ff'
565
- circleStrokeColor?: string; // Point outline. Default: '#ffffff'
566
- circleStrokeWidth?: number; // Point outline width. Default: 2
588
+ fillColor?: string; // Polygon fill. Default: '#3388ff'
589
+ fillOpacity?: number; // Polygon fill opacity. Default: 0.3
590
+ strokeColor?: string; // Line/outline color. Default: '#3388ff'
591
+ strokeWidth?: number; // Line width. Default: 2
592
+ strokeOpacity?: number; // Line opacity. Default: 1
593
+ circleRadius?: number; // Point radius. Default: 6
594
+ circleColor?: string; // Point color. Default: '#3388ff'
595
+ circleStrokeColor?: string; // Point outline. Default: '#ffffff'
596
+ circleStrokeWidth?: number; // Point outline width. Default: 2
567
597
  }
568
598
 
569
599
  interface LoadedDataset {
570
- id: string; // Unique ID
571
- filename: string; // Original filename
572
- sourceId: string; // MapLibre source ID
573
- layerIds: string[]; // MapLibre layer IDs
574
- featureCount: number; // Number of features
575
- geometryTypes: string[]; // Geometry types present
576
- loadedAt: Date; // When loaded
600
+ id: string; // Unique ID
601
+ filename: string; // Original filename
602
+ sourceId: string; // MapLibre source ID
603
+ layerIds: string[]; // MapLibre layer IDs
604
+ featureCount: number; // Number of features
605
+ geometryTypes: string[]; // Geometry types present
606
+ loadedAt: Date; // When loaded
577
607
  }
578
608
 
579
609
  // Methods
580
- vectorControl.show()
581
- vectorControl.hide()
582
- vectorControl.getLoadedDatasets() // Get all loaded datasets
583
- vectorControl.removeDataset(id) // Remove a dataset by ID
584
- vectorControl.removeAllDatasets() // Remove all datasets
585
- vectorControl.loadGeoJSON(geojson, filename) // Programmatically load GeoJSON
586
- vectorControl.update(options)
587
- vectorControl.getState()
588
- vectorControl.on('load', handler) // Fired when a dataset is loaded
589
- vectorControl.on('error', handler) // Fired when an error occurs
610
+ vectorControl.show();
611
+ vectorControl.hide();
612
+ vectorControl.getLoadedDatasets(); // Get all loaded datasets
613
+ vectorControl.removeDataset(id); // Remove a dataset by ID
614
+ vectorControl.removeAllDatasets(); // Remove all datasets
615
+ vectorControl.loadGeoJSON(geojson, filename); // Programmatically load GeoJSON
616
+ vectorControl.update(options);
617
+ vectorControl.getState();
618
+ vectorControl.on("load", handler); // Fired when a dataset is loaded
619
+ vectorControl.on("error", handler); // Fired when an error occurs
590
620
  ```
591
621
 
592
622
  **Loading Methods:**
623
+
593
624
  - Click the upload button to open a file picker
594
625
  - Drag and drop GeoJSON files directly onto the map
595
626
 
596
627
  **Supported Formats:**
628
+
597
629
  - GeoJSON (.geojson, .json)
598
630
  - FeatureCollection, Feature, or raw Geometry objects
599
631
 
@@ -603,24 +635,24 @@ A control for loading vector data from URLs with support for multiple formats an
603
635
 
604
636
  ```typescript
605
637
  interface AddVectorControlOptions {
606
- position?: ControlPosition; // Control position (default: 'top-right')
607
- className?: string; // Custom CSS class
608
- visible?: boolean; // Initial visibility (default: true)
609
- collapsed?: boolean; // Start collapsed (default: true)
610
- beforeId?: string; // Layer ID to insert before
611
- defaultUrl?: string; // Pre-filled URL
612
- defaultLayerName?: string; // Pre-filled layer name
613
- loadDefaultUrl?: boolean; // Auto-load defaultUrl on init (default: false)
614
- defaultFormat?: 'auto' | 'geojson' | 'geoparquet' | 'flatgeobuf'; // Default format (default: 'auto')
615
- defaultOpacity?: number; // Default opacity 0-1 (default: 1)
616
- defaultFillColor?: string; // Default fill color (default: '#3388ff')
617
- defaultStrokeColor?: string; // Default stroke color (default: '#3388ff')
618
- defaultCircleColor?: string; // Default point color (default: '#3388ff')
619
- defaultPickable?: boolean; // Enable click popups (default: true)
620
- corsProxy?: string; // CORS proxy URL for cross-origin files
621
- fitBounds?: boolean; // Fit to data bounds (default: true)
622
- fitBoundsPadding?: number; // Padding for fitBounds (default: 50)
623
- panelWidth?: number; // Panel width in pixels (default: 300)
638
+ position?: ControlPosition; // Control position (default: 'top-right')
639
+ className?: string; // Custom CSS class
640
+ visible?: boolean; // Initial visibility (default: true)
641
+ collapsed?: boolean; // Start collapsed (default: true)
642
+ beforeId?: string; // Layer ID to insert before
643
+ defaultUrl?: string; // Pre-filled URL
644
+ defaultLayerName?: string; // Pre-filled layer name
645
+ loadDefaultUrl?: boolean; // Auto-load defaultUrl on init (default: false)
646
+ defaultFormat?: "auto" | "geojson" | "geoparquet" | "flatgeobuf"; // Default format (default: 'auto')
647
+ defaultOpacity?: number; // Default opacity 0-1 (default: 1)
648
+ defaultFillColor?: string; // Default fill color (default: '#3388ff')
649
+ defaultStrokeColor?: string; // Default stroke color (default: '#3388ff')
650
+ defaultCircleColor?: string; // Default point color (default: '#3388ff')
651
+ defaultPickable?: boolean; // Enable click popups (default: true)
652
+ corsProxy?: string; // CORS proxy URL for cross-origin files
653
+ fitBounds?: boolean; // Fit to data bounds (default: true)
654
+ fitBoundsPadding?: number; // Padding for fitBounds (default: 50)
655
+ panelWidth?: number; // Panel width in pixels (default: 300)
624
656
  backgroundColor?: string;
625
657
  borderRadius?: number;
626
658
  opacity?: number;
@@ -634,36 +666,36 @@ interface AddVectorControlOptions {
634
666
  **Usage:**
635
667
 
636
668
  ```typescript
637
- import { AddVectorControl } from 'maplibre-gl-components';
669
+ import { AddVectorControl } from "maplibre-gl-components";
638
670
 
639
671
  // Basic usage
640
672
  const addVectorControl = new AddVectorControl({
641
- position: 'top-right',
673
+ position: "top-right",
642
674
  collapsed: false,
643
675
  });
644
676
  map.addControl(addVectorControl);
645
677
 
646
678
  // With pre-filled URL that auto-loads
647
679
  const addVectorControl = new AddVectorControl({
648
- defaultUrl: 'https://example.com/data.geojson',
680
+ defaultUrl: "https://example.com/data.geojson",
649
681
  loadDefaultUrl: true,
650
682
  defaultOpacity: 0.8,
651
- defaultFillColor: '#ff6600',
683
+ defaultFillColor: "#ff6600",
652
684
  fitBounds: true,
653
685
  });
654
686
  map.addControl(addVectorControl);
655
687
 
656
688
  // Listen for events
657
- addVectorControl.on('layeradd', (event) => {
658
- console.log('Layer added:', event.layerId, event.url);
689
+ addVectorControl.on("layeradd", (event) => {
690
+ console.log("Layer added:", event.layerId, event.url);
659
691
  });
660
692
 
661
- addVectorControl.on('layerremove', (event) => {
662
- console.log('Layer removed:', event.layerId);
693
+ addVectorControl.on("layerremove", (event) => {
694
+ console.log("Layer removed:", event.layerId);
663
695
  });
664
696
 
665
- addVectorControl.on('error', (event) => {
666
- console.error('Error:', event.error);
697
+ addVectorControl.on("error", (event) => {
698
+ console.error("Error:", event.error);
667
699
  });
668
700
  ```
669
701
 
@@ -687,11 +719,13 @@ addVectorControl.removeAllLayers() // Remove all layers
687
719
  ```
688
720
 
689
721
  **Supported Formats:**
722
+
690
723
  - GeoJSON (.geojson, .json)
691
724
  - GeoParquet (.parquet, .geoparquet)
692
725
  - FlatGeobuf (.fgb)
693
726
 
694
727
  **Features:**
728
+
695
729
  - Auto-detect format from URL extension
696
730
  - Customizable fill, stroke, and point colors
697
731
  - Opacity slider with real-time updates
@@ -705,40 +739,41 @@ A toggle control for 3D terrain rendering using free AWS Terrarium elevation til
705
739
 
706
740
  ```typescript
707
741
  interface TerrainControlOptions {
708
- sourceUrl?: string; // Terrain tile URL (default: AWS Terrarium)
709
- encoding?: 'terrarium' | 'mapbox'; // Terrain encoding (default: 'terrarium')
710
- exaggeration?: number; // Vertical scale factor (default: 1.0)
711
- enabled?: boolean; // Initial terrain state (default: false)
712
- hillshade?: boolean; // Add hillshade layer (default: true)
713
- hillshadeExaggeration?: number; // Hillshade intensity (default: 0.5)
742
+ sourceUrl?: string; // Terrain tile URL (default: AWS Terrarium)
743
+ encoding?: "terrarium" | "mapbox"; // Terrain encoding (default: 'terrarium')
744
+ exaggeration?: number; // Vertical scale factor (default: 1.0)
745
+ enabled?: boolean; // Initial terrain state (default: false)
746
+ hillshade?: boolean; // Add hillshade layer (default: true)
747
+ hillshadeExaggeration?: number; // Hillshade intensity (default: 0.5)
714
748
  position?: ControlPosition;
715
749
  visible?: boolean;
716
750
  backgroundColor?: string;
717
751
  borderRadius?: number;
718
752
  opacity?: number;
719
- minzoom?: number; // Min zoom level to show (default: 0)
720
- maxzoom?: number; // Max zoom level to show (default: 24)
753
+ minzoom?: number; // Min zoom level to show (default: 0)
754
+ maxzoom?: number; // Max zoom level to show (default: 24)
721
755
  }
722
756
 
723
757
  // Methods
724
- terrainControl.show()
725
- terrainControl.hide()
726
- terrainControl.enable() // Enable terrain
727
- terrainControl.disable() // Disable terrain
728
- terrainControl.toggle() // Toggle terrain on/off
729
- terrainControl.isEnabled() // Check if terrain is enabled
730
- terrainControl.setExaggeration(value) // Set vertical exaggeration (0.1 - 10.0)
731
- terrainControl.getExaggeration() // Get current exaggeration
732
- terrainControl.enableHillshade() // Enable hillshade layer
733
- terrainControl.disableHillshade() // Disable hillshade layer
734
- terrainControl.toggleHillshade() // Toggle hillshade layer
735
- terrainControl.update(options)
736
- terrainControl.getState()
737
- terrainControl.on('terrainchange', handler) // Listen for terrain toggle
758
+ terrainControl.show();
759
+ terrainControl.hide();
760
+ terrainControl.enable(); // Enable terrain
761
+ terrainControl.disable(); // Disable terrain
762
+ terrainControl.toggle(); // Toggle terrain on/off
763
+ terrainControl.isEnabled(); // Check if terrain is enabled
764
+ terrainControl.setExaggeration(value); // Set vertical exaggeration (0.1 - 10.0)
765
+ terrainControl.getExaggeration(); // Get current exaggeration
766
+ terrainControl.enableHillshade(); // Enable hillshade layer
767
+ terrainControl.disableHillshade(); // Disable hillshade layer
768
+ terrainControl.toggleHillshade(); // Toggle hillshade layer
769
+ terrainControl.update(options);
770
+ terrainControl.getState();
771
+ terrainControl.on("terrainchange", handler); // Listen for terrain toggle
738
772
  ```
739
773
 
740
774
  **Terrain Source:**
741
775
  The control uses free terrain tiles from AWS:
776
+
742
777
  - URL: `https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png`
743
778
  - Encoding: Terrarium RGB-encoded elevation data
744
779
  - No API key required
@@ -750,17 +785,17 @@ A control for inspecting vector features on the map. Click on features to view t
750
785
  ```typescript
751
786
  interface InspectControlOptions {
752
787
  position?: ControlPosition;
753
- visible?: boolean; // Default: true
754
- enabled?: boolean; // Start with inspect mode on. Default: false
755
- maxFeatures?: number; // Max features at click point. Default: 10
756
- includeLayers?: string[]; // Only inspect these layers
757
- excludeLayers?: string[]; // Skip these layers
788
+ visible?: boolean; // Default: true
789
+ enabled?: boolean; // Start with inspect mode on. Default: false
790
+ maxFeatures?: number; // Max features at click point. Default: 10
791
+ includeLayers?: string[]; // Only inspect these layers
792
+ excludeLayers?: string[]; // Skip these layers
758
793
  highlightStyle?: InspectHighlightStyle; // Style for selected feature
759
- excludeProperties?: string[]; // Properties to hide (e.g., internal IDs)
760
- showGeometryType?: boolean; // Show geometry type badge. Default: true
761
- showLayerName?: boolean; // Show layer name. Default: true
762
- maxWidth?: number; // Popup max width. Default: 320
763
- maxHeight?: number; // Popup content max height. Default: 300
794
+ excludeProperties?: string[]; // Properties to hide (e.g., internal IDs)
795
+ showGeometryType?: boolean; // Show geometry type badge. Default: true
796
+ showLayerName?: boolean; // Show layer name. Default: true
797
+ maxWidth?: number; // Popup max width. Default: 320
798
+ maxHeight?: number; // Popup content max height. Default: 300
764
799
  backgroundColor?: string;
765
800
  borderRadius?: number;
766
801
  opacity?: number;
@@ -771,44 +806,45 @@ interface InspectControlOptions {
771
806
  }
772
807
 
773
808
  interface InspectHighlightStyle {
774
- fillColor?: string; // Polygon fill. Default: '#ffff00'
775
- fillOpacity?: number; // Polygon fill opacity. Default: 0.3
776
- strokeColor?: string; // Line/outline color. Default: '#ffff00'
777
- strokeWidth?: number; // Line width. Default: 3
778
- circleRadius?: number; // Point radius. Default: 10
779
- circleStrokeWidth?: number; // Point outline. Default: 3
809
+ fillColor?: string; // Polygon fill. Default: '#ffff00'
810
+ fillOpacity?: number; // Polygon fill opacity. Default: 0.3
811
+ strokeColor?: string; // Line/outline color. Default: '#ffff00'
812
+ strokeWidth?: number; // Line width. Default: 3
813
+ circleRadius?: number; // Point radius. Default: 10
814
+ circleStrokeWidth?: number; // Point outline. Default: 3
780
815
  }
781
816
 
782
817
  interface InspectedFeature {
783
- id: string; // Unique inspection ID
784
- feature: GeoJSON.Feature; // The GeoJSON feature
785
- layerId: string; // MapLibre layer ID
786
- sourceId: string; // MapLibre source ID
787
- lngLat: [number, number]; // Click coordinates
818
+ id: string; // Unique inspection ID
819
+ feature: GeoJSON.Feature; // The GeoJSON feature
820
+ layerId: string; // MapLibre layer ID
821
+ sourceId: string; // MapLibre source ID
822
+ lngLat: [number, number]; // Click coordinates
788
823
  }
789
824
 
790
825
  // Methods
791
- inspectControl.show()
792
- inspectControl.hide()
793
- inspectControl.enable() // Enable inspect mode
794
- inspectControl.disable() // Disable inspect mode
795
- inspectControl.toggle() // Toggle inspect mode on/off
796
- inspectControl.isEnabled() // Check if inspect mode is enabled
797
- inspectControl.clear() // Clear current inspection
798
- inspectControl.getInspectedFeatures() // Get all features at click point
799
- inspectControl.getSelectedFeature() // Get currently selected feature
800
- inspectControl.selectFeature(index) // Select feature by index
801
- inspectControl.nextFeature() // Navigate to next feature
802
- inspectControl.previousFeature() // Navigate to previous feature
803
- inspectControl.update(options)
804
- inspectControl.getState()
805
- inspectControl.on('enable', handler) // Fired when inspect mode is enabled
806
- inspectControl.on('disable', handler) // Fired when inspect mode is disabled
807
- inspectControl.on('featureselect', handler) // Fired when a feature is selected
808
- inspectControl.on('clear', handler) // Fired when inspection is cleared
826
+ inspectControl.show();
827
+ inspectControl.hide();
828
+ inspectControl.enable(); // Enable inspect mode
829
+ inspectControl.disable(); // Disable inspect mode
830
+ inspectControl.toggle(); // Toggle inspect mode on/off
831
+ inspectControl.isEnabled(); // Check if inspect mode is enabled
832
+ inspectControl.clear(); // Clear current inspection
833
+ inspectControl.getInspectedFeatures(); // Get all features at click point
834
+ inspectControl.getSelectedFeature(); // Get currently selected feature
835
+ inspectControl.selectFeature(index); // Select feature by index
836
+ inspectControl.nextFeature(); // Navigate to next feature
837
+ inspectControl.previousFeature(); // Navigate to previous feature
838
+ inspectControl.update(options);
839
+ inspectControl.getState();
840
+ inspectControl.on("enable", handler); // Fired when inspect mode is enabled
841
+ inspectControl.on("disable", handler); // Fired when inspect mode is disabled
842
+ inspectControl.on("featureselect", handler); // Fired when a feature is selected
843
+ inspectControl.on("clear", handler); // Fired when inspection is cleared
809
844
  ```
810
845
 
811
846
  **Usage:**
847
+
812
848
  1. Click the info button to enable inspect mode
813
849
  2. Click on any vector feature on the map
814
850
  3. View properties in the popup
@@ -822,20 +858,20 @@ A control that displays live map view state (center, bounds, zoom, pitch, bearin
822
858
  ```typescript
823
859
  interface ViewStateControlOptions {
824
860
  position?: ControlPosition;
825
- className?: string; // Custom CSS class
826
- visible?: boolean; // Default: true
827
- collapsed?: boolean; // Start collapsed (button only). Default: true
828
- precision?: number; // Decimal precision for coordinates. Default: 4
829
- showCenter?: boolean; // Show center coordinates. Default: true
830
- showBounds?: boolean; // Show map bounds. Default: true
831
- showZoom?: boolean; // Show zoom level. Default: true
832
- showPitch?: boolean; // Show pitch value. Default: true
833
- showBearing?: boolean; // Show bearing value. Default: true
834
- enableBBox?: boolean; // Enable bounding box drawing. Default: false
835
- bboxFillColor?: string; // BBox fill color. Default: 'rgba(0, 120, 215, 0.1)'
836
- bboxStrokeColor?: string; // BBox stroke color. Default: '#0078d7'
837
- bboxStrokeWidth?: number; // BBox stroke width. Default: 2
838
- panelWidth?: number; // Panel width in pixels. Default: 280
861
+ className?: string; // Custom CSS class
862
+ visible?: boolean; // Default: true
863
+ collapsed?: boolean; // Start collapsed (button only). Default: true
864
+ precision?: number; // Decimal precision for coordinates. Default: 4
865
+ showCenter?: boolean; // Show center coordinates. Default: true
866
+ showBounds?: boolean; // Show map bounds. Default: true
867
+ showZoom?: boolean; // Show zoom level. Default: true
868
+ showPitch?: boolean; // Show pitch value. Default: true
869
+ showBearing?: boolean; // Show bearing value. Default: true
870
+ enableBBox?: boolean; // Enable bounding box drawing. Default: false
871
+ bboxFillColor?: string; // BBox fill color. Default: 'rgba(0, 120, 215, 0.1)'
872
+ bboxStrokeColor?: string; // BBox stroke color. Default: '#0078d7'
873
+ bboxStrokeWidth?: number; // BBox stroke width. Default: 2
874
+ panelWidth?: number; // Panel width in pixels. Default: 280
839
875
  backgroundColor?: string;
840
876
  borderRadius?: number;
841
877
  opacity?: number;
@@ -848,35 +884,36 @@ interface ViewStateControlOptions {
848
884
  interface ViewStateControlState {
849
885
  visible: boolean;
850
886
  collapsed: boolean;
851
- center: [number, number]; // [lng, lat]
852
- bounds: [number, number, number, number]; // [west, south, east, north]
887
+ center: [number, number]; // [lng, lat]
888
+ bounds: [number, number, number, number]; // [west, south, east, north]
853
889
  zoom: number;
854
- pitch: number; // Degrees
855
- bearing: number; // Degrees
856
- drawingBBox: boolean; // Whether bbox drawing is active
857
- drawnBBox: [number, number, number, number] | null; // Drawn bbox or null
890
+ pitch: number; // Degrees
891
+ bearing: number; // Degrees
892
+ drawingBBox: boolean; // Whether bbox drawing is active
893
+ drawnBBox: [number, number, number, number] | null; // Drawn bbox or null
858
894
  }
859
895
 
860
896
  // Methods
861
- viewStateControl.show()
862
- viewStateControl.hide()
863
- viewStateControl.expand() // Expand the panel
864
- viewStateControl.collapse() // Collapse to button only
865
- viewStateControl.toggle() // Toggle expanded/collapsed
866
- viewStateControl.isCollapsed() // Check if collapsed
867
- viewStateControl.startBBoxDraw() // Start bounding box drawing mode
868
- viewStateControl.stopBBoxDraw() // Stop bounding box drawing mode
869
- viewStateControl.clearBBox() // Clear the drawn bounding box
870
- viewStateControl.update(options)
871
- viewStateControl.getState()
872
- viewStateControl.on('viewchange', handler) // Fired when map view changes
873
- viewStateControl.on('bboxdraw', handler) // Fired when bbox is drawn
874
- viewStateControl.on('bboxclear', handler) // Fired when bbox is cleared
875
- viewStateControl.on('drawstart', handler) // Fired when drawing mode starts
876
- viewStateControl.on('drawend', handler) // Fired when drawing mode ends
897
+ viewStateControl.show();
898
+ viewStateControl.hide();
899
+ viewStateControl.expand(); // Expand the panel
900
+ viewStateControl.collapse(); // Collapse to button only
901
+ viewStateControl.toggle(); // Toggle expanded/collapsed
902
+ viewStateControl.isCollapsed(); // Check if collapsed
903
+ viewStateControl.startBBoxDraw(); // Start bounding box drawing mode
904
+ viewStateControl.stopBBoxDraw(); // Stop bounding box drawing mode
905
+ viewStateControl.clearBBox(); // Clear the drawn bounding box
906
+ viewStateControl.update(options);
907
+ viewStateControl.getState();
908
+ viewStateControl.on("viewchange", handler); // Fired when map view changes
909
+ viewStateControl.on("bboxdraw", handler); // Fired when bbox is drawn
910
+ viewStateControl.on("bboxclear", handler); // Fired when bbox is cleared
911
+ viewStateControl.on("drawstart", handler); // Fired when drawing mode starts
912
+ viewStateControl.on("drawend", handler); // Fired when drawing mode ends
877
913
  ```
878
914
 
879
915
  **Usage:**
916
+
880
917
  1. Click the target button to expand/collapse the panel
881
918
  2. Pan, zoom, or tilt the map to see live updates
882
919
  3. Click "Draw BBox" to enter drawing mode
@@ -934,6 +971,7 @@ cogControl.on('error', handler) // Fired on error
934
971
  ```
935
972
 
936
973
  **Features:**
974
+
937
975
  - Supports single-band and multi-band GeoTIFFs
938
976
  - Automatic float/integer data type detection
939
977
  - 21 built-in colormaps with live preview
@@ -994,6 +1032,7 @@ zarrControl.on('error', handler) // Fired on error
994
1032
  ```
995
1033
 
996
1034
  **Features:**
1035
+
997
1036
  - Multi-dimensional array support (time, band, etc.)
998
1037
  - Automatic variable discovery from Zarr metadata
999
1038
  - 21 built-in colormaps with live preview
@@ -1004,13 +1043,14 @@ zarrControl.on('error', handler) // Fired on error
1004
1043
  - Multiple Zarr layers support
1005
1044
 
1006
1045
  **Example with dimension selector:**
1046
+
1007
1047
  ```typescript
1008
1048
  const zarrControl = new ZarrLayerControl({
1009
- defaultUrl: 'https://example.com/climate.zarr',
1010
- defaultVariable: 'temperature',
1011
- defaultColormap: ['#0000ff', '#ffffff', '#ff0000'], // Custom blue-white-red
1049
+ defaultUrl: "https://example.com/climate.zarr",
1050
+ defaultVariable: "temperature",
1051
+ defaultColormap: ["#0000ff", "#ffffff", "#ff0000"], // Custom blue-white-red
1012
1052
  defaultClim: [-20, 40],
1013
- defaultSelector: { time: 0, month: 'January' }, // Select first time step, January
1053
+ defaultSelector: { time: 0, month: "January" }, // Select first time step, January
1014
1054
  });
1015
1055
  ```
1016
1056
 
@@ -1021,18 +1061,18 @@ A control for loading Cloud Optimized GeoTIFF (COG) layers from STAC (SpatioTemp
1021
1061
  ```typescript
1022
1062
  interface StacLayerControlOptions {
1023
1063
  position?: ControlPosition;
1024
- className?: string; // Custom CSS class
1025
- visible?: boolean; // Default: true
1026
- collapsed?: boolean; // Start collapsed. Default: true
1027
- beforeId?: string; // Layer ID to insert before (for ordering)
1028
- defaultUrl?: string; // Initial STAC item URL
1029
- loadDefaultUrl?: boolean; // Auto-load defaultUrl on add. Default: false
1030
- defaultColormap?: ColormapName | 'none'; // Colormap name. Default: 'none'
1031
- defaultRescaleMin?: number; // Min value for rescaling. Default: 0
1032
- defaultRescaleMax?: number; // Max value for rescaling. Default: 255
1033
- defaultOpacity?: number; // Layer opacity. Default: 1
1034
- defaultPickable?: boolean; // Enable click popups. Default: true
1035
- panelWidth?: number; // Panel width in pixels. Default: 320
1064
+ className?: string; // Custom CSS class
1065
+ visible?: boolean; // Default: true
1066
+ collapsed?: boolean; // Start collapsed. Default: true
1067
+ beforeId?: string; // Layer ID to insert before (for ordering)
1068
+ defaultUrl?: string; // Initial STAC item URL
1069
+ loadDefaultUrl?: boolean; // Auto-load defaultUrl on add. Default: false
1070
+ defaultColormap?: ColormapName | "none"; // Colormap name. Default: 'none'
1071
+ defaultRescaleMin?: number; // Min value for rescaling. Default: 0
1072
+ defaultRescaleMax?: number; // Max value for rescaling. Default: 255
1073
+ defaultOpacity?: number; // Layer opacity. Default: 1
1074
+ defaultPickable?: boolean; // Enable click popups. Default: true
1075
+ panelWidth?: number; // Panel width in pixels. Default: 320
1036
1076
  backgroundColor?: string;
1037
1077
  borderRadius?: number;
1038
1078
  opacity?: number;
@@ -1043,47 +1083,49 @@ interface StacLayerControlOptions {
1043
1083
  }
1044
1084
 
1045
1085
  // Methods
1046
- stacControl.show()
1047
- stacControl.hide()
1048
- stacControl.expand()
1049
- stacControl.collapse()
1050
- stacControl.toggle()
1051
- stacControl.getState()
1052
- stacControl.update(options)
1053
- stacControl.loadStacUrl(url) // Fetch and parse a STAC item
1054
- stacControl.on('stacload', handler) // Fired when STAC item is loaded
1055
- stacControl.on('layeradd', handler) // Fired when layer is added
1056
- stacControl.on('layerremove', handler) // Fired when layer is removed
1057
- stacControl.on('error', handler) // Fired on error
1086
+ stacControl.show();
1087
+ stacControl.hide();
1088
+ stacControl.expand();
1089
+ stacControl.collapse();
1090
+ stacControl.toggle();
1091
+ stacControl.getState();
1092
+ stacControl.update(options);
1093
+ stacControl.loadStacUrl(url); // Fetch and parse a STAC item
1094
+ stacControl.on("stacload", handler); // Fired when STAC item is loaded
1095
+ stacControl.on("layeradd", handler); // Fired when layer is added
1096
+ stacControl.on("layerremove", handler); // Fired when layer is removed
1097
+ stacControl.on("error", handler); // Fired on error
1058
1098
  ```
1059
1099
 
1060
1100
  **Usage:**
1061
1101
 
1062
1102
  ```typescript
1063
- import { StacLayerControl } from 'maplibre-gl-components';
1103
+ import { StacLayerControl } from "maplibre-gl-components";
1064
1104
 
1065
1105
  // Basic usage with STAC item URL
1066
1106
  const stacControl = new StacLayerControl({
1067
- defaultUrl: 'https://canada-spot-ortho.s3.amazonaws.com/.../S5_11055_6057_20070622.json',
1107
+ defaultUrl:
1108
+ "https://canada-spot-ortho.s3.amazonaws.com/.../S5_11055_6057_20070622.json",
1068
1109
  loadDefaultUrl: true,
1069
- defaultColormap: 'viridis',
1110
+ defaultColormap: "viridis",
1070
1111
  defaultRescaleMin: 0,
1071
1112
  defaultRescaleMax: 255,
1072
1113
  });
1073
- map.addControl(stacControl, 'top-right');
1114
+ map.addControl(stacControl, "top-right");
1074
1115
 
1075
1116
  // Listen for events
1076
- stacControl.on('stacload', (event) => {
1077
- console.log('STAC loaded:', event.url);
1078
- console.log('Available assets:', event.state.assets);
1117
+ stacControl.on("stacload", (event) => {
1118
+ console.log("STAC loaded:", event.url);
1119
+ console.log("Available assets:", event.state.assets);
1079
1120
  });
1080
1121
 
1081
- stacControl.on('layeradd', (event) => {
1082
- console.log('Layer added:', event.assetKey, event.layerId);
1122
+ stacControl.on("layeradd", (event) => {
1123
+ console.log("Layer added:", event.assetKey, event.layerId);
1083
1124
  });
1084
1125
  ```
1085
1126
 
1086
1127
  **Features:**
1128
+
1087
1129
  - Fetches and parses STAC item JSON
1088
1130
  - Lists available COG assets (GeoTIFF files)
1089
1131
  - Asset selector dropdown
@@ -1095,6 +1137,7 @@ stacControl.on('layeradd', (event) => {
1095
1137
  - Multiple layer support
1096
1138
 
1097
1139
  **Workflow:**
1140
+
1098
1141
  1. Enter a STAC item URL and click "Fetch STAC"
1099
1142
  2. Select a COG asset from the dropdown
1100
1143
  3. Configure colormap, rescale range, and opacity
@@ -1108,18 +1151,18 @@ A control for searching and visualizing STAC items from public STAC API catalogs
1108
1151
  ```typescript
1109
1152
  interface StacSearchControlOptions {
1110
1153
  position?: ControlPosition;
1111
- className?: string; // Custom CSS class
1112
- visible?: boolean; // Default: true
1113
- collapsed?: boolean; // Start collapsed. Default: true
1114
- panelWidth?: number; // Panel width in pixels. Default: 360
1115
- maxHeight?: number; // Max panel height in pixels. Default: none
1116
- catalogs?: StacCatalog[]; // Predefined STAC catalogs
1117
- maxItems?: number; // Max search results. Default: 20
1118
- defaultRescaleMin?: number; // Min rescale value. Default: 0
1119
- defaultRescaleMax?: number; // Max rescale value. Default: 10000
1120
- defaultColormap?: string; // Colormap for single band. Default: 'viridis'
1121
- defaultRgbMode?: boolean; // Start in RGB mode. Default: true
1122
- showFootprints?: boolean; // Show item footprints on map. Default: true
1154
+ className?: string; // Custom CSS class
1155
+ visible?: boolean; // Default: true
1156
+ collapsed?: boolean; // Start collapsed. Default: true
1157
+ panelWidth?: number; // Panel width in pixels. Default: 360
1158
+ maxHeight?: number; // Max panel height in pixels. Default: none
1159
+ catalogs?: StacCatalog[]; // Predefined STAC catalogs
1160
+ maxItems?: number; // Max search results. Default: 20
1161
+ defaultRescaleMin?: number; // Min rescale value. Default: 0
1162
+ defaultRescaleMax?: number; // Max rescale value. Default: 10000
1163
+ defaultColormap?: string; // Colormap for single band. Default: 'viridis'
1164
+ defaultRgbMode?: boolean; // Start in RGB mode. Default: true
1165
+ showFootprints?: boolean; // Show item footprints on map. Default: true
1123
1166
  backgroundColor?: string;
1124
1167
  borderRadius?: number;
1125
1168
  opacity?: number;
@@ -1130,15 +1173,15 @@ interface StacSearchControlOptions {
1130
1173
  }
1131
1174
 
1132
1175
  interface StacCatalog {
1133
- name: string; // Display name
1134
- url: string; // STAC API URL
1176
+ name: string; // Display name
1177
+ url: string; // STAC API URL
1135
1178
  }
1136
1179
  ```
1137
1180
 
1138
1181
  **Usage:**
1139
1182
 
1140
1183
  ```typescript
1141
- import { StacSearchControl } from 'maplibre-gl-components';
1184
+ import { StacSearchControl } from "maplibre-gl-components";
1142
1185
 
1143
1186
  // Basic usage with default catalogs
1144
1187
  const stacSearch = new StacSearchControl({
@@ -1146,73 +1189,81 @@ const stacSearch = new StacSearchControl({
1146
1189
  maxItems: 20,
1147
1190
  showFootprints: true,
1148
1191
  });
1149
- map.addControl(stacSearch, 'top-right');
1192
+ map.addControl(stacSearch, "top-right");
1150
1193
 
1151
1194
  // With custom catalogs
1152
1195
  const stacSearch = new StacSearchControl({
1153
1196
  catalogs: [
1154
- { name: "Element84 Earth Search", url: "https://earth-search.aws.element84.com/v1" },
1155
- { name: "Microsoft Planetary Computer", url: "https://planetarycomputer.microsoft.com/api/stac/v1" },
1197
+ {
1198
+ name: "Element84 Earth Search",
1199
+ url: "https://earth-search.aws.element84.com/v1",
1200
+ },
1201
+ {
1202
+ name: "Microsoft Planetary Computer",
1203
+ url: "https://planetarycomputer.microsoft.com/api/stac/v1",
1204
+ },
1156
1205
  ],
1157
1206
  defaultRgbMode: true,
1158
1207
  defaultRescaleMin: 0,
1159
1208
  defaultRescaleMax: 3000,
1160
1209
  maxHeight: 500,
1161
1210
  });
1162
- map.addControl(stacSearch, 'top-right');
1211
+ map.addControl(stacSearch, "top-right");
1163
1212
 
1164
1213
  // Listen for events
1165
- stacSearch.on('catalogselect', (event) => {
1166
- console.log('Catalog selected:', event.catalog?.name);
1214
+ stacSearch.on("catalogselect", (event) => {
1215
+ console.log("Catalog selected:", event.catalog?.name);
1167
1216
  });
1168
1217
 
1169
- stacSearch.on('collectionsload', (event) => {
1170
- console.log('Collections loaded:', event.state.collections.length);
1218
+ stacSearch.on("collectionsload", (event) => {
1219
+ console.log("Collections loaded:", event.state.collections.length);
1171
1220
  });
1172
1221
 
1173
- stacSearch.on('collectionselect', (event) => {
1174
- console.log('Collection selected:', event.collection?.id);
1222
+ stacSearch.on("collectionselect", (event) => {
1223
+ console.log("Collection selected:", event.collection?.id);
1175
1224
  });
1176
1225
 
1177
- stacSearch.on('search', (event) => {
1178
- console.log('Search completed:', event.state.items.length, 'items found');
1226
+ stacSearch.on("search", (event) => {
1227
+ console.log("Search completed:", event.state.items.length, "items found");
1179
1228
  });
1180
1229
 
1181
- stacSearch.on('itemselect', (event) => {
1182
- console.log('Item selected:', event.item?.id);
1230
+ stacSearch.on("itemselect", (event) => {
1231
+ console.log("Item selected:", event.item?.id);
1183
1232
  });
1184
1233
 
1185
- stacSearch.on('display', (event) => {
1186
- console.log('Item displayed:', event.item?.id);
1234
+ stacSearch.on("display", (event) => {
1235
+ console.log("Item displayed:", event.item?.id);
1187
1236
  });
1188
1237
 
1189
- stacSearch.on('error', (event) => {
1190
- console.error('Error:', event.error);
1238
+ stacSearch.on("error", (event) => {
1239
+ console.error("Error:", event.error);
1191
1240
  });
1192
1241
  ```
1193
1242
 
1194
1243
  **Methods:**
1195
1244
 
1196
1245
  ```typescript
1197
- stacSearch.show()
1198
- stacSearch.hide()
1199
- stacSearch.expand()
1200
- stacSearch.collapse()
1201
- stacSearch.toggle()
1202
- stacSearch.getState()
1203
- stacSearch.update(options)
1204
- stacSearch.getSelectedCatalog() // Get current catalog
1205
- stacSearch.getSelectedCollection() // Get current collection
1206
- stacSearch.getSelectedItem() // Get current item
1207
- stacSearch.on(event, handler) // Subscribe to events
1208
- stacSearch.off(event, handler) // Unsubscribe from events
1246
+ stacSearch.show();
1247
+ stacSearch.hide();
1248
+ stacSearch.expand();
1249
+ stacSearch.collapse();
1250
+ stacSearch.toggle();
1251
+ stacSearch.getState();
1252
+ stacSearch.update(options);
1253
+ stacSearch.getSelectedCatalog(); // Get current catalog
1254
+ stacSearch.getSelectedCollection(); // Get current collection
1255
+ stacSearch.getSelectedItem(); // Get current item
1256
+ stacSearch.on(event, handler); // Subscribe to events
1257
+ stacSearch.off(event, handler); // Unsubscribe from events
1209
1258
  ```
1210
1259
 
1211
1260
  **Default Catalogs:**
1261
+
1212
1262
  - **Element84 Earth Search** - Sentinel-2, Landsat, NAIP, Copernicus DEM
1213
1263
  - **Microsoft Planetary Computer** - Extensive collection with tile server
1214
1264
 
1215
1265
  **Features:**
1266
+
1216
1267
  - Search STAC items within current map viewport
1217
1268
  - Date range filtering
1218
1269
  - Query filter support (e.g., cloud cover)
@@ -1226,6 +1277,7 @@ stacSearch.off(event, handler) // Unsubscribe from events
1226
1277
  - Custom catalog URL support
1227
1278
 
1228
1279
  **Workflow:**
1280
+
1229
1281
  1. Select a catalog (or enter custom URL)
1230
1282
  2. Click "Collections" to load available collections
1231
1283
  3. Select a collection (e.g., sentinel-2-l2a)
@@ -1236,13 +1288,182 @@ stacSearch.off(event, handler) // Unsubscribe from events
1236
1288
  8. Select bands and adjust rescale range
1237
1289
  9. Click "Display Item" to visualize
1238
1290
 
1291
+ ### MeasureControl
1292
+
1293
+ A control for measuring distances and areas on the map.
1294
+
1295
+ See the [measure-control example](./examples/measure-control/) for a complete working example.
1296
+
1297
+ ### BookmarkControl
1298
+
1299
+ A control for saving and restoring map views with localStorage persistence.
1300
+
1301
+ See the [bookmark-control example](./examples/bookmark-control/) for a complete working example.
1302
+
1303
+ ### PrintControl
1304
+
1305
+ A control for exporting the current map view as PNG, JPEG, or PDF. Supports optional title overlays, custom filenames, quality settings, and custom export sizes. PDF export requires the optional `jspdf` peer dependency.
1306
+
1307
+ ```typescript
1308
+ interface PrintControlOptions {
1309
+ position?: ControlPosition; // Control position (default: 'top-right')
1310
+ className?: string; // Custom CSS class
1311
+ visible?: boolean; // Initial visibility (default: true)
1312
+ collapsed?: boolean; // Start collapsed (default: true)
1313
+ format?: "png" | "jpeg" | "pdf"; // Default format (default: 'png')
1314
+ quality?: number; // JPEG quality 0-1 (default: 0.92)
1315
+ filename?: string; // Default filename without extension (default: 'map-export')
1316
+ title?: string; // Optional title rendered on the image
1317
+ titleFontSize?: number; // Title font size in pixels (default: 24)
1318
+ titleFontColor?: string; // Title font color (default: '#333333')
1319
+ titleBackground?: string; // Title background (default: 'rgba(255,255,255,0.8)')
1320
+ showSizeOptions?: boolean; // Show current/custom size options (default: false)
1321
+ width?: number; // Width override in pixels
1322
+ height?: number; // Height override in pixels
1323
+ panelWidth?: number; // Panel width in pixels (default: 280)
1324
+ backgroundColor?: string;
1325
+ borderRadius?: number;
1326
+ opacity?: number;
1327
+ fontSize?: number;
1328
+ fontColor?: string;
1329
+ minzoom?: number;
1330
+ maxzoom?: number;
1331
+ }
1332
+
1333
+ // Methods
1334
+ printControl.show()
1335
+ printControl.hide()
1336
+ printControl.getState()
1337
+ printControl.setFormat(format) // Set format: 'png', 'jpeg', or 'pdf'
1338
+ printControl.setQuality(quality) // Set JPEG quality (0.1 - 1)
1339
+ printControl.setTitle(title) // Set title text
1340
+ printControl.exportMap(options?) // Programmatic export, returns data URL (empty string for PDF)
1341
+ printControl.on('export', handler) // Fired after successful export
1342
+ printControl.on('copy', handler) // Fired after clipboard copy
1343
+ printControl.on('error', handler) // Fired on error
1344
+ ```
1345
+
1346
+ **Usage:**
1347
+
1348
+ ```typescript
1349
+ import { PrintControl } from "maplibre-gl-components";
1350
+
1351
+ const printControl = new PrintControl({
1352
+ filename: "my-map",
1353
+ format: "png",
1354
+ title: "My Map Title",
1355
+ });
1356
+ map.addControl(printControl, "top-right");
1357
+
1358
+ // Listen for export events
1359
+ printControl.on("export", (event) => {
1360
+ console.log("Exported:", event.state.filename);
1361
+ });
1362
+
1363
+ // Programmatic export
1364
+ const dataUrl = await printControl.exportMap({
1365
+ format: "jpeg",
1366
+ quality: 0.95,
1367
+ title: "Custom Title",
1368
+ });
1369
+
1370
+ // Export as PDF (requires jspdf)
1371
+ await printControl.exportMap({ format: "pdf" });
1372
+ ```
1373
+
1374
+ **Features:**
1375
+
1376
+ - Export as PNG, JPEG, or PDF
1377
+ - Optional title overlay rendered on the exported image
1378
+ - Customizable filename, quality, and export size
1379
+ - Copy to clipboard (PNG/JPEG only)
1380
+ - PDF export with auto landscape/portrait detection, fitted to A4 page
1381
+ - Programmatic export API
1382
+
1383
+ **PDF Export:**
1384
+
1385
+ PDF export requires the optional [`jspdf`](https://www.npmjs.com/package/jspdf) package:
1386
+
1387
+ ```bash
1388
+ npm install jspdf
1389
+ ```
1390
+
1391
+ The library is dynamically imported only when PDF format is selected, keeping the main bundle lean.
1392
+
1393
+ ### MinimapControl
1394
+
1395
+ An inset overview map that shows the current viewport extent on a smaller map. Supports click-to-navigate and customizable styling.
1396
+
1397
+ ```typescript
1398
+ interface MinimapControlOptions {
1399
+ position?: ControlPosition; // Control position (default: 'bottom-left')
1400
+ className?: string; // Custom CSS class
1401
+ visible?: boolean; // Initial visibility (default: true)
1402
+ collapsed?: boolean; // Start collapsed (default: false)
1403
+ width?: number; // Minimap width in pixels (default: 250)
1404
+ height?: number; // Minimap height in pixels (default: 180)
1405
+ zoomOffset?: number; // Zoom offset from main map (default: -5)
1406
+ style?: string | object; // Map style URL or object
1407
+ viewportRectColor?: string; // Viewport rectangle color (default: '#0078d7')
1408
+ viewportRectOpacity?: number; // Viewport rectangle fill opacity (default: 0.2)
1409
+ toggleable?: boolean; // Whether minimap can be toggled (default: true)
1410
+ interactive?: boolean; // Click minimap to navigate main map (default: false)
1411
+ minzoom?: number;
1412
+ maxzoom?: number;
1413
+ }
1414
+
1415
+ // Methods
1416
+ minimapControl.show()
1417
+ minimapControl.hide()
1418
+ minimapControl.expand() // Show the minimap panel
1419
+ minimapControl.collapse() // Hide the minimap panel
1420
+ minimapControl.toggle() // Toggle panel visibility
1421
+ minimapControl.getState()
1422
+ minimapControl.on(event, handler) // 'show' | 'hide' | 'expand' | 'collapse'
1423
+ minimapControl.off(event, handler)
1424
+ ```
1425
+
1426
+ **Usage:**
1427
+
1428
+ ```typescript
1429
+ import { MinimapControl } from "maplibre-gl-components";
1430
+
1431
+ const minimapControl = new MinimapControl({
1432
+ width: 250,
1433
+ height: 180,
1434
+ zoomOffset: -5,
1435
+ interactive: true,
1436
+ });
1437
+ map.addControl(minimapControl, "bottom-left");
1438
+
1439
+ minimapControl.on("expand", (event) => {
1440
+ console.log("Minimap expanded:", event.state);
1441
+ });
1442
+ ```
1443
+
1444
+ **Features:**
1445
+
1446
+ - Syncs center and zoom with the main map
1447
+ - Viewport rectangle overlay showing the visible area
1448
+ - Click anywhere on the minimap to fly the main map to that location
1449
+ - Drag on the minimap to pan the main map in real time
1450
+ - Toggleable panel with button
1451
+ - Customizable size, zoom offset, and style
1452
+
1453
+ See the [minimap-control example](./examples/minimap-control/) for a complete working example.
1454
+
1239
1455
  ### Layer Control Adapters
1240
1456
 
1241
1457
  To integrate COG and Zarr layers with [maplibre-gl-layer-control](https://github.com/AJPNorthwest/maplibre-gl-layer-control), use the included adapters:
1242
1458
 
1243
1459
  ```typescript
1244
- import { LayerControl, CustomLayerAdapter } from 'maplibre-gl-layer-control';
1245
- import { CogLayerControl, ZarrLayerControl, CogLayerAdapter, ZarrLayerAdapter } from 'maplibre-gl-components';
1460
+ import { LayerControl, CustomLayerAdapter } from "maplibre-gl-layer-control";
1461
+ import {
1462
+ CogLayerControl,
1463
+ ZarrLayerControl,
1464
+ CogLayerAdapter,
1465
+ ZarrLayerAdapter,
1466
+ } from "maplibre-gl-components";
1246
1467
 
1247
1468
  // Create layer controls
1248
1469
  const cogControl = new CogLayerControl({ collapsed: true });
@@ -1252,23 +1473,24 @@ map.addControl(cogControl);
1252
1473
  map.addControl(zarrControl);
1253
1474
 
1254
1475
  // Add layers
1255
- await cogControl.addLayer('https://example.com/dem.tif');
1256
- await zarrControl.addLayer('https://example.com/data.zarr', 'temperature');
1476
+ await cogControl.addLayer("https://example.com/dem.tif");
1477
+ await zarrControl.addLayer("https://example.com/data.zarr", "temperature");
1257
1478
 
1258
1479
  // Create layer control with adapters
1259
1480
  const layerControl = new LayerControl({
1260
1481
  customLayers: [
1261
- new CogLayerAdapter(cogControl, { name: 'Elevation DEM' }),
1262
- new ZarrLayerAdapter(zarrControl, { name: 'Temperature Data' }),
1482
+ new CogLayerAdapter(cogControl, { name: "Elevation DEM" }),
1483
+ new ZarrLayerAdapter(zarrControl, { name: "Temperature Data" }),
1263
1484
  ],
1264
1485
  });
1265
1486
  map.addControl(layerControl);
1266
1487
  ```
1267
1488
 
1268
1489
  **Adapter Options:**
1490
+
1269
1491
  ```typescript
1270
1492
  interface AdapterOptions {
1271
- name?: string; // Display name in layer control
1493
+ name?: string; // Display name in layer control
1272
1494
  defaultOpacity?: number; // Opacity when toggled on (default: 1)
1273
1495
  }
1274
1496
  ```
@@ -1276,6 +1498,7 @@ interface AdapterOptions {
1276
1498
  ## Built-in Colormaps
1277
1499
 
1278
1500
  ### Sequential
1501
+
1279
1502
  - `viridis` - Perceptually uniform, colorblind-friendly
1280
1503
  - `plasma` - Perceptually uniform
1281
1504
  - `inferno` - Perceptually uniform
@@ -1283,6 +1506,7 @@ interface AdapterOptions {
1283
1506
  - `cividis` - Colorblind-friendly
1284
1507
 
1285
1508
  ### Diverging
1509
+
1286
1510
  - `coolwarm` - Blue to red through white
1287
1511
  - `bwr` - Blue-white-red
1288
1512
  - `seismic` - Blue to red
@@ -1292,6 +1516,7 @@ interface AdapterOptions {
1292
1516
  - `spectral` - Rainbow-like diverging
1293
1517
 
1294
1518
  ### Miscellaneous
1519
+
1295
1520
  - `jet` - Classic rainbow
1296
1521
  - `rainbow` - Full spectrum
1297
1522
  - `turbo` - Improved rainbow
@@ -1307,7 +1532,7 @@ interface AdapterOptions {
1307
1532
  ```typescript
1308
1533
  // Use an array of colors
1309
1534
  const colorbar = new Colorbar({
1310
- colormap: ['#0000ff', '#00ff00', '#ffff00', '#ff0000'],
1535
+ colormap: ["#0000ff", "#00ff00", "#ffff00", "#ff0000"],
1311
1536
  vmin: 0,
1312
1537
  vmax: 100,
1313
1538
  });
@@ -1315,10 +1540,10 @@ const colorbar = new Colorbar({
1315
1540
  // Or use color stops for precise control
1316
1541
  const colorbar = new Colorbar({
1317
1542
  colorStops: [
1318
- { position: 0, color: '#0000ff' },
1319
- { position: 0.3, color: '#00ff00' },
1320
- { position: 0.7, color: '#ffff00' },
1321
- { position: 1, color: '#ff0000' },
1543
+ { position: 0, color: "#0000ff" },
1544
+ { position: 0.3, color: "#00ff00" },
1545
+ { position: 0.7, color: "#ffff00" },
1546
+ { position: 1, color: "#ff0000" },
1322
1547
  ],
1323
1548
  vmin: 0,
1324
1549
  vmax: 100,
@@ -1361,11 +1586,11 @@ const colorbar = new Colorbar({
1361
1586
  map={map}
1362
1587
  title="Lidar Point Cloud"
1363
1588
  items={[
1364
- { label: 'QL0 (Approx. <= 0.35m NPS)', color: '#003300' },
1365
- { label: 'QL1 (Approx. 0.35m NPS)', color: '#006600' },
1366
- { label: 'QL2 (Approx. 0.7m NPS)', color: '#00cc00' },
1367
- { label: 'QL3 (Approx. 1.4m NPS)', color: '#ccff00' },
1368
- { label: 'Other', color: '#99ccff' },
1589
+ { label: "QL0 (Approx. <= 0.35m NPS)", color: "#003300" },
1590
+ { label: "QL1 (Approx. 0.35m NPS)", color: "#006600" },
1591
+ { label: "QL2 (Approx. 0.7m NPS)", color: "#00cc00" },
1592
+ { label: "QL3 (Approx. 1.4m NPS)", color: "#ccff00" },
1593
+ { label: "Other", color: "#99ccff" },
1369
1594
  ]}
1370
1595
  minzoom={8}
1371
1596
  maxzoom={18}
@@ -1514,6 +1739,9 @@ See the [examples](./examples/) directory for complete working examples:
1514
1739
  - **Zarr Layer Example** - Multi-dimensional Zarr data visualization
1515
1740
  - **STAC Layer Example** - Load COG layers from STAC catalog items
1516
1741
  - **STAC Search Example** - Search and visualize STAC items from public catalogs
1742
+ - **Print Control Example** - Export map as PNG, JPEG, or PDF
1743
+ - **Minimap Control Example** - Inset overview map with viewport rectangle
1744
+
1517
1745
 
1518
1746
  ## Development
1519
1747
 
@@ -1562,12 +1790,11 @@ docker run -p 8080:80 maplibre-gl-components
1562
1790
 
1563
1791
  ### Available Tags
1564
1792
 
1565
- | Tag | Description |
1566
- |-----|-------------|
1567
- | `latest` | Latest release |
1568
- | `x.y.z` | Specific version (e.g., `1.0.0`) |
1569
- | `x.y` | Minor version (e.g., `1.0`) |
1570
-
1793
+ | Tag | Description |
1794
+ | -------- | -------------------------------- |
1795
+ | `latest` | Latest release |
1796
+ | `x.y.z` | Specific version (e.g., `1.0.0`) |
1797
+ | `x.y` | Minor version (e.g., `1.0`) |
1571
1798
 
1572
1799
  ## License
1573
1800