npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

maplibre-gl-enviroatlas

v0.1.1

Published

A MapLibre GL JS plugin for searching and adding EPA EnviroAtlas web services to a map

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.

npm version License: MIT

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/auto themes
  • 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-enviroatlas

Quick 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 dev
  • examples/basic - vanilla TypeScript
  • examples/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:geolibre

This creates geolibre-plugin/maplibre-gl-enviroatlas-0.1.0.zip containing:

plugin.json
dist/index.js
dist/style.css

Copy 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:geolibre

Development

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 site

Docker

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 helpers

Data 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