maplibre-gl-enviroatlas
v0.1.1
Published
A MapLibre GL JS plugin for searching and adding EPA EnviroAtlas web services to a map
Maintainers
Readme
MapLibre GL EnviroAtlas
A MapLibre GL JS plugin for searching and adding EPA EnviroAtlas web services to a map. It ships a standalone MapLibre control, a React wrapper, and a GeoLibre Desktop plugin bundle.
Features
- Catalog Browsing - Browse EnviroAtlas folders, services, and sublayers in a collapsible tree
- Deep Search - Search both service names and individual sublayer names (e.g. "tree cover", "asthma")
- Layer Management - Visibility toggle, opacity slider, legend display, and removal for each added layer
- Resizable Panel - Drag the panel edge to resize it, in any control corner
- Layer Ordering - An "Insert before" dropdown places added layers anywhere in the map layer stack (e.g. below labels)
- MapServer and ImageServer - Adds dynamic ArcGIS services as MapLibre raster layers, reprojected to Web Mercator on the fly
- Fast Rendering - One export request per map view by default (like ArcGIS dynamic layers), instead of dozens of per-tile renders
- Auto Zoom - Zooms the map to each added layer's extent (disable with
fitBoundsOnAdd: false) - Extent-aware Requests - Requests are clamped to each service's data extent, avoiding slow out-of-extent server errors
- Dark and Light Mode - Follows the OS preference by default, with explicit
light/dark/autothemes - Small Screen Friendly - The panel caps its size to the map and scrolls vertically
- TypeScript Support - Full TypeScript support with exported type definitions
- React Integration - React wrapper component and custom hook
- GeoLibre Bundle Output - Builds a zip with root
plugin.json, bundled ESM, and CSS for GeoLibre Desktop
Installation
npm install maplibre-gl-enviroatlasQuick Start
Vanilla JavaScript / TypeScript
import maplibregl from 'maplibre-gl';
import { EnviroAtlasControl } from 'maplibre-gl-enviroatlas';
import 'maplibre-gl-enviroatlas/style.css';
import 'maplibre-gl/dist/maplibre-gl.css';
const map = new maplibregl.Map({
container: 'map',
style: 'https://tiles.openfreemap.org/styles/positron',
center: [-96, 38.5],
zoom: 4,
});
map.on('load', () => {
const control = new EnviroAtlasControl({
collapsed: true,
theme: 'auto',
});
map.addControl(control, 'top-right');
control.on('layeradd', (event) => {
console.log('Added layer:', event.layer?.label);
});
});React
import { EnviroAtlasControlReact, useEnviroAtlas } from 'maplibre-gl-enviroatlas/react';
import 'maplibre-gl-enviroatlas/style.css';
function MyMap({ map }) {
const { state, setState, toggle } = useEnviroAtlas({ collapsed: false });
return (
<>
<button onClick={toggle}>{state.collapsed ? 'Expand' : 'Collapse'}</button>
{map && (
<EnviroAtlasControlReact
map={map}
collapsed={state.collapsed}
theme="auto"
onStateChange={setState}
onLayerAdd={(layer) => console.log('Added', layer.label)}
/>
)}
</>
);
}API
EnviroAtlasControl
Implements MapLibre's IControl. The control renders as a 29x29 toggle button that expands into a floating panel.
Options (EnviroAtlasControlOptions)
| Option | Type | Default | Description |
| --- | --- | --- | --- |
| collapsed | boolean | true | Start with only the toggle button visible |
| position | 'top-left' \| 'top-right' \| 'bottom-left' \| 'bottom-right' | 'top-right' | Control position on the map |
| title | string | 'US EPA EnviroAtlas' | Panel header title |
| panelWidth | number | 360 | Panel width in pixels (capped to the viewport) |
| className | string | '' | Extra CSS class for the control container |
| theme | 'light' \| 'dark' \| 'auto' | 'auto' | Color theme; auto follows prefers-color-scheme |
| servicesUrl | string | EnviroAtlas REST root | ArcGIS REST services directory to browse |
| excludedFolders | string[] | ['test_services', 'Utilities', 'monitor'] | Folders hidden from browsing and search |
| defaultOpacity | number | 1 | Initial opacity for added layers (0 to 1) |
| renderMode | 'image' \| 'tiles' | 'image' | 'image' renders one export per map view (fast for dynamic ArcGIS services); 'tiles' requests one export per 256px tile |
| tileSize | number | 256 | Raster tile size in pixels (tiles mode) |
| imageFormat | string | 'png32' | ArcGIS export image format |
| attribution | string | 'U.S. EPA EnviroAtlas' | Attribution for added raster sources |
| beforeId | string | none | Existing map layer id to insert added layers before (e.g. a label layer); ignored when not found. Also selectable from the "Insert before" dropdown in the panel |
| searchDebounceMs | number | 250 | Debounce delay for the search input |
| fitBoundsOnAdd | boolean | true | Zoom the map to a layer's extent when it is added |
| quietTileErrors | boolean | true | Keep transient EnviroAtlas tile failures out of the console (surfaced via the error event instead) |
Methods
| Method | Description |
| --- | --- |
| addServiceLayer(service, sublayerId?, label?) | Adds a service or single MapServer sublayer to the map (async; resolves with the added layer) |
| restoreLayers(entries) | Re-registers host-persisted AddedLayer entries, reusing native sources/layers the host already recreated (no fitBounds, no notices). Deferred until onAdd when the control is not yet on a map |
| removeLayer(id) | Removes a layer added through the control |
| setLayerOpacity(id, opacity) | Sets the raster opacity of an added layer |
| setLayerVisibility(id, visible) | Shows or hides an added layer |
| setTheme(theme) | Switches the color theme |
| setPanelWidth(width) | Sets the panel width in pixels (also adjustable by dragging the panel edge) |
| refreshCatalog() | Clears caches and reloads the catalog tree |
| getState() / setState(partial) | Reads or merges the control state |
| toggle() / expand() / collapse() | Controls the panel visibility |
| on(event, handler) / off(event, handler) | Event subscription |
| getMap() / getContainer() | Accessors for the map and container |
Events
collapse, expand, statechange, layeradd, layerremove, layerchange, error. Handlers receive { type, state, layer?, error? }.
EnviroAtlasControlReact
React wrapper with all control options as props, plus:
| Prop | Type | Description |
| --- | --- | --- |
| map | maplibregl.Map | The map instance (required) |
| onStateChange | (state: EnviroAtlasState) => void | Fired on every state change |
| onLayerAdd | (layer: AddedLayer) => void | Fired when a layer is added |
| onLayerRemove | (layer: AddedLayer) => void | Fired when a layer is removed |
| onError | (error: Error) => void | Fired on catalog or network errors |
useEnviroAtlas(initialState?)
Hook returning { state, setState, setCollapsed, setPanelWidth, setTheme, setData, reset, toggle }.
Lower-level API
The catalog client and pure helpers are exported for advanced use:
import {
CatalogClient,
filterCatalog,
buildExportUrl,
buildExportImageUrl,
buildTileTemplate,
buildLegendUrl,
} from 'maplibre-gl-enviroatlas';buildTileTemplate(service, sublayerId?) returns a tile URL template (containing {bbox-epsg-3857}) suitable for a MapLibre raster source, using the ArcGIS dynamic export / exportImage endpoints with on-the-fly reprojection to EPSG:3857.
Theming
The stylesheet is driven by CSS custom properties (--ea-bg, --ea-fg, --ea-accent, ...). With theme: 'auto' the palette follows prefers-color-scheme; theme: 'light' or theme: 'dark' forces a palette via a data-theme attribute. You can override any variable in your own CSS:
.enviroatlas-control,
.enviroatlas-panel {
--ea-accent: #2e7d32;
}Examples
Run the dev server and open the examples:
npm install
npm run devexamples/basic- vanilla TypeScriptexamples/react- React wrapper and hook
Build a GeoLibre plugin zip
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.
npm install
npm run package:geolibreThis creates geolibre-plugin/maplibre-gl-enviroatlas-0.1.0.zip containing:
plugin.json
dist/index.js
dist/style.cssCopy the zip into GeoLibre Desktop's app data plugins/ directory and restart GeoLibre. For the GeoLibre web app, serve the unpacked plugin with CORS enabled:
npm run serve:geolibreDevelopment
npm install # install dependencies
npm run dev # dev server with the examples
npm test # run tests
npm run lint # lint
npm run build # build the library and the GeoLibre bundle
npm run build:examples # build the examples siteDocker
docker build -t maplibre-gl-enviroatlas .
docker run -p 8080:80 maplibre-gl-enviroatlas
# open http://localhost:8080/maplibre-gl-enviroatlas/Project Structure
src/
├── index.ts # Main entry (control + API + types)
├── react.ts # React entry (wrapper + hook)
├── geolibre.ts # GeoLibre Desktop plugin wrapper
└── lib/
├── api/ # Catalog client, URL builders, search
├── core/ # EnviroAtlasControl, map layer manager, types
├── ui/ # Panel, tree, results, added layers, legend views
├── hooks/ # useEnviroAtlas
├── styles/ # Themed stylesheet
└── utils/ # Generic helpersData Source
This plugin reads the public EnviroAtlas ArcGIS REST services directory at https://enviroatlas.epa.gov/arcgis/rest/services. EnviroAtlas data are produced by the U.S. Environmental Protection Agency. See the EnviroAtlas web services page for documentation and terms.
Note: in the default renderMode: 'image', MapLibre image sources cannot carry attribution text. If you want the EPA credit in the map's attribution bar, add it through your AttributionControl (customAttribution) or use renderMode: 'tiles', where the attribution option is attached to the raster source.
License
MIT
