maplibre-gl-nasa-earthdata 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Qiusheng Wu
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,341 @@
1
+ # maplibre-gl-nasa-earthdata
2
+
3
+ A [MapLibre GL JS](https://maplibre.org/) plugin for searching and adding [NASA GIBS](https://earthdata.nasa.gov/gibs) (Global Imagery Browse Services) Earthdata imagery layers to a map.
4
+
5
+ [![npm version](https://img.shields.io/npm/v/maplibre-gl-nasa-earthdata.svg)](https://www.npmjs.com/package/maplibre-gl-nasa-earthdata)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ The plugin adds a collapsible map control that fetches the GIBS WMTS capabilities document, lets users search the catalog of 1,100+ raster layers by name, and add imagery layers to the map with per-layer date and opacity controls.
9
+
10
+ ## Features
11
+
12
+ - **Layer Search** - Search 1,100+ NASA GIBS raster layers by title or identifier
13
+ - **Category Browser** - Layers grouped into collapsible categories by platform/instrument (MODIS, VIIRS, MERRA2, ...)
14
+ - **Layer Management** - Added-layers panel with visibility toggle, legend display, opacity slider, and removal
15
+ - **Insert Before** - Choose where new layers are inserted in the map's layer stack (e.g. below labels)
16
+ - **Time Dimension Support** - Date picker for time-enabled layers (daily/monthly imagery); add the same layer multiple times with different dates to compare
17
+ - **Collapsible Control** - Compact 29x29 button that expands into a floating panel
18
+ - **Resizable Panel** - Drag the panel edge to adjust its width in any corner
19
+ - **Dark and Light Mode** - Follows the OS preference, or force a theme via the `theme` option
20
+ - **Small Screen Friendly** - Panel fits the viewport with a vertical scrollbar
21
+ - **TypeScript Support** - Full type definitions for all public APIs
22
+ - **React Integration** - React wrapper component and custom hook
23
+ - **Programmatic API** - Search and add/remove layers from code
24
+ - **GeoLibre Bundle Output** - Builds a zip plugin bundle for GeoLibre Desktop
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install maplibre-gl-nasa-earthdata
30
+ ```
31
+
32
+ ## Quick Start
33
+
34
+ ### Vanilla JavaScript/TypeScript
35
+
36
+ ```typescript
37
+ import maplibregl from "maplibre-gl";
38
+ import { NasaEarthdataControl } from "maplibre-gl-nasa-earthdata";
39
+ import "maplibre-gl-nasa-earthdata/style.css";
40
+
41
+ const map = new maplibregl.Map({
42
+ container: "map",
43
+ style: "https://tiles.openfreemap.org/styles/liberty",
44
+ center: [0, 20],
45
+ zoom: 2,
46
+ });
47
+
48
+ map.on("load", () => {
49
+ const control = new NasaEarthdataControl({
50
+ title: "NASA Earthdata",
51
+ collapsed: false,
52
+ });
53
+
54
+ map.addControl(control, "top-right");
55
+
56
+ control.on("layeradd", (event) => {
57
+ console.log("Added layer:", event.layer?.id);
58
+ });
59
+ });
60
+ ```
61
+
62
+ ### Programmatic layer management
63
+
64
+ ```typescript
65
+ const control = new NasaEarthdataControl();
66
+ map.addControl(control, "top-right");
67
+
68
+ // Load the GIBS catalog, then add a layer by identifier
69
+ await control.getCapabilities();
70
+ control.addLayer("MODIS_Terra_CorrectedReflectance_TrueColor", {
71
+ date: "2024-06-01",
72
+ opacity: 0.8,
73
+ });
74
+
75
+ // Update or remove later
76
+ control.setLayerDate("MODIS_Terra_CorrectedReflectance_TrueColor", "2024-07-01");
77
+ control.setLayerOpacity("MODIS_Terra_CorrectedReflectance_TrueColor", 0.5);
78
+ control.removeLayer("MODIS_Terra_CorrectedReflectance_TrueColor");
79
+ ```
80
+
81
+ ### React
82
+
83
+ ```tsx
84
+ import { useEffect, useRef, useState } from "react";
85
+ import maplibregl, { Map } from "maplibre-gl";
86
+ import {
87
+ NasaEarthdataControlReact,
88
+ useNasaEarthdata,
89
+ } from "maplibre-gl-nasa-earthdata/react";
90
+ import "maplibre-gl-nasa-earthdata/style.css";
91
+
92
+ function App() {
93
+ const mapContainer = useRef<HTMLDivElement>(null);
94
+ const [map, setMap] = useState<Map | null>(null);
95
+ const { state, setState, toggle } = useNasaEarthdata();
96
+
97
+ useEffect(() => {
98
+ if (!mapContainer.current) return;
99
+
100
+ const mapInstance = new maplibregl.Map({
101
+ container: mapContainer.current,
102
+ style: "https://tiles.openfreemap.org/styles/liberty",
103
+ center: [0, 20],
104
+ zoom: 2,
105
+ });
106
+
107
+ mapInstance.on("load", () => setMap(mapInstance));
108
+
109
+ return () => mapInstance.remove();
110
+ }, []);
111
+
112
+ return (
113
+ <div style={{ width: "100%", height: "100vh" }}>
114
+ <div ref={mapContainer} style={{ width: "100%", height: "100%" }} />
115
+ {map && (
116
+ <NasaEarthdataControlReact
117
+ map={map}
118
+ collapsed={state.collapsed}
119
+ onStateChange={setState}
120
+ onLayerAdd={(layer) => console.log("Added:", layer.id)}
121
+ />
122
+ )}
123
+ </div>
124
+ );
125
+ }
126
+ ```
127
+
128
+ ## API
129
+
130
+ ### NasaEarthdataControl
131
+
132
+ The main control class implementing MapLibre's `IControl` interface.
133
+
134
+ #### Constructor Options
135
+
136
+ | Option | Type | Default | Description |
137
+ | ----------------- | ------------------------------- | ---------------------- | ------------------------------------------------------------------------- |
138
+ | `collapsed` | `boolean` | `true` | Whether the panel starts collapsed (showing only the 29x29 toggle button) |
139
+ | `position` | `string` | `'top-right'` | Control position on the map |
140
+ | `title` | `string` | `'NASA Earthdata'` | Title displayed in the header |
141
+ | `panelWidth` | `number` | `320` | Initial width of the dropdown panel in pixels (drag the edge to resize) |
142
+ | `className` | `string` | `''` | Custom CSS class name |
143
+ | `capabilitiesUrl` | `string` | GIBS EPSG:3857 best | URL of the WMTS capabilities document |
144
+ | `includeVector` | `boolean` | `false` | Include vector-tile (MVT) layers in search results |
145
+ | `showOpacity` | `boolean` | `true` | Show an opacity slider for added layers |
146
+ | `attribution` | `string` | NASA EOSDIS GIBS link | Attribution applied to added raster sources |
147
+ | `theme` | `'auto' \| 'light' \| 'dark'` | `'auto'` | Color theme; `'auto'` follows the OS preference |
148
+
149
+ #### Methods
150
+
151
+ - `getCapabilities(force?)` - Fetch and cache the GIBS layer catalog
152
+ - `search(query)` - Search loaded layers by title or identifier
153
+ - `addLayer(layerId, { date?, opacity?, visible?, before? })` - Add a GIBS layer to the map. Time-enabled layers can be added multiple times with different dates; each addition is an instance with a unique `key` (see `AddedLayerState`)
154
+ - `removeLayer(keyOrId)` - Remove an instance by key, or all instances of a layer by its GIBS identifier
155
+ - `setLayerDate(keyOrId, date)` - Change the date of a time-enabled layer instance
156
+ - `setLayerOpacity(keyOrId, opacity)` - Change an instance's opacity (0 to 1)
157
+ - `setLayerVisibility(keyOrId, visible)` - Show or hide an added instance
158
+ - `getAddedLayers()` - Get the state of all added layers
159
+ - `toggle()` / `expand()` / `collapse()` - Control the panel
160
+ - `getState()` / `setState(state)` - Read or reconcile the control state
161
+ - `on(event, handler)` / `off(event, handler)` - Manage event handlers
162
+ - `getMap()` / `getContainer()` - Access the map instance and container element
163
+
164
+ #### Events
165
+
166
+ - `collapse` / `expand` - Panel visibility changed
167
+ - `statechange` - State changed (query, added layers, etc.)
168
+ - `layeradd` / `layerremove` - A GIBS layer was added or removed (payload includes `layer`)
169
+ - `capabilitiesload` - The GIBS catalog finished loading
170
+ - `error` - An error occurred (payload includes `error`)
171
+
172
+ ### NasaEarthdataControlReact
173
+
174
+ React wrapper component for `NasaEarthdataControl`.
175
+
176
+ #### Props
177
+
178
+ All `NasaEarthdataControl` options plus:
179
+
180
+ | Prop | Type | Description |
181
+ | --------------- | ---------- | -------------------------------------- |
182
+ | `map` | `Map` | MapLibre GL map instance (required) |
183
+ | `onStateChange` | `function` | Callback fired when state changes |
184
+ | `onLayerAdd` | `function` | Callback fired when a layer is added |
185
+ | `onLayerRemove` | `function` | Callback fired when a layer is removed |
186
+
187
+ ### useNasaEarthdata
188
+
189
+ Custom React hook for managing control state.
190
+
191
+ ```typescript
192
+ const {
193
+ state, // Current state
194
+ setState, // Update entire state
195
+ setCollapsed, // Set collapsed state
196
+ setPanelWidth, // Set panel width
197
+ setQuery, // Set the search query
198
+ setAddedLayers, // Set the added layers list
199
+ reset, // Reset to initial state
200
+ toggle, // Toggle collapsed state
201
+ } = useNasaEarthdata(initialState);
202
+ ```
203
+
204
+ ### GIBS helpers
205
+
206
+ Lower-level building blocks are exported for advanced use:
207
+
208
+ - `GibsClient` - Fetches, parses, and caches the capabilities document
209
+ - `parseCapabilities(xml, options?)` - Parse a WMTS capabilities XML string
210
+ - `buildTileUrl(layer, time?)` - Build an XYZ tile URL template for a layer
211
+ - `searchLayers(layers, query)` - Filter layers by title or identifier
212
+ - `DEFAULT_CAPABILITIES_URL` - The default GIBS capabilities URL
213
+
214
+ ### Exported Types
215
+
216
+ `NasaEarthdataControlOptions`, `NasaEarthdataState`, `AddedLayerState`, `NasaEarthdataEvent`, `NasaEarthdataEventPayload`, `NasaEarthdataEventHandler`, `NasaEarthdataReactProps`, `GibsLayer`, `GibsLayerFormat`, `GibsTimeDimension`, `GibsCapabilities`, `ParseOptions`, `GibsClientOptions`
217
+
218
+ ## Theming
219
+
220
+ The control renders properly in both light and dark mode:
221
+
222
+ - By default (`theme: 'auto'`), colors follow the OS `prefers-color-scheme` setting.
223
+ - Pass `theme: 'dark'` or `theme: 'light'` to force a theme.
224
+ - All colors are driven by CSS custom properties (prefixed `--ne-`) on the
225
+ `.maplibre-gl-nasa-earthdata` class, so they can be overridden in your own CSS.
226
+
227
+ ## Build a GeoLibre plugin zip
228
+
229
+ GeoLibre Desktop loads external plugins from an app data `plugins/` directory. The zip must contain `plugin.json` at the root, plus a bundled ESM entry and optional CSS file.
230
+
231
+ ```bash
232
+ npm install
233
+ npm run package:geolibre
234
+ ```
235
+
236
+ This creates:
237
+
238
+ ```text
239
+ geolibre-plugin/maplibre-gl-nasa-earthdata-0.1.0.zip
240
+ ```
241
+
242
+ Copy the zip into GeoLibre Desktop's app data `plugins/` directory and restart GeoLibre. On Linux with the default app identifier, that directory is usually:
243
+
244
+ ```text
245
+ ~/.local/share/org.geolibre.desktop/plugins/
246
+ ```
247
+
248
+ For the GeoLibre web app, serve the unpacked plugin with CORS enabled:
249
+
250
+ ```bash
251
+ npm run package:geolibre
252
+ npm run serve:geolibre -- 8000
253
+ ```
254
+
255
+ Then add `http://localhost:8000/plugin.json` in GeoLibre Settings > Plugins.
256
+
257
+ ## Development
258
+
259
+ ### Setup
260
+
261
+ ```bash
262
+ # Clone the repository
263
+ git clone https://github.com/opengeos/maplibre-gl-nasa-earthdata.git
264
+ cd maplibre-gl-nasa-earthdata
265
+
266
+ # Install dependencies
267
+ npm install
268
+
269
+ # Start development server
270
+ npm run dev
271
+ ```
272
+
273
+ ### Scripts
274
+
275
+ | Script | Description |
276
+ | -------------------------- | ---------------------------------------- |
277
+ | `npm run dev` | Start development server |
278
+ | `npm run build` | Build the library and GeoLibre bundle |
279
+ | `npm run build:lib` | Build the standalone MapLibre library |
280
+ | `npm run build:geolibre` | Build the GeoLibre ESM and CSS bundle |
281
+ | `npm run package:geolibre` | Build and zip the GeoLibre plugin bundle |
282
+ | `npm run build:examples` | Build examples for deployment |
283
+ | `npm run test` | Run tests |
284
+ | `npm run test:ui` | Run tests with UI |
285
+ | `npm run test:coverage` | Run tests with coverage |
286
+ | `npm run lint` | Lint the code |
287
+ | `npm run format` | Format the code |
288
+
289
+ ### Project Structure
290
+
291
+ ```text
292
+ maplibre-gl-nasa-earthdata/
293
+ ├── geolibre-plugin/
294
+ │ └── plugin.json # GeoLibre external plugin manifest
295
+ ├── scripts/
296
+ │ └── package-geolibre-plugin.mjs
297
+ ├── src/
298
+ │ ├── index.ts # Main entry point
299
+ │ ├── geolibre.ts # GeoLibre plugin wrapper entry point
300
+ │ ├── react.ts # React entry point
301
+ │ ├── index.css # Root styles
302
+ │ └── lib/
303
+ │ ├── core/ # Control class, React wrapper, and types
304
+ │ ├── gibs/ # GIBS capabilities parsing and tile URLs
305
+ │ ├── hooks/ # React hooks
306
+ │ ├── utils/ # Utility functions
307
+ │ └── styles/ # Component styles
308
+ ├── tests/ # Test files
309
+ ├── examples/ # Example applications
310
+ │ ├── basic/ # Vanilla JS example
311
+ │ └── react/ # React example
312
+ └── .github/workflows/ # CI/CD workflows
313
+ ```
314
+
315
+ ## Docker
316
+
317
+ The examples can be run using Docker. The image is automatically built and published to GitHub Container Registry.
318
+
319
+ ```bash
320
+ # Pull the latest image
321
+ docker pull ghcr.io/opengeos/maplibre-gl-nasa-earthdata:latest
322
+
323
+ # Run the container
324
+ docker run -p 8080:80 ghcr.io/opengeos/maplibre-gl-nasa-earthdata:latest
325
+ ```
326
+
327
+ Then open http://localhost:8080/maplibre-gl-nasa-earthdata/ in your browser to view the examples.
328
+
329
+ ```bash
330
+ # Or build locally
331
+ docker build -t maplibre-gl-nasa-earthdata .
332
+ docker run -p 8080:80 maplibre-gl-nasa-earthdata
333
+ ```
334
+
335
+ ## Attribution
336
+
337
+ Imagery provided by services from NASA's [Global Imagery Browse Services (GIBS)](https://earthdata.nasa.gov/gibs), part of NASA's Earth Observing System Data and Information System (EOSDIS). The plugin applies a NASA EOSDIS GIBS attribution to added raster sources by default.
338
+
339
+ ## License
340
+
341
+ MIT License - see [LICENSE](LICENSE) for details.