Skip to content

About

A GeoJSON editor for MapLibre GL JS.

Topics

Resources

Stars

17 stars

Watchers

2 watching

Forks

Repository files navigation

Waymark JS

A GeoJSON editor for MapLibre GL JS.

Warning

v2 — Unstable.

Overview

Waymark JS renders GeoJSON layers on a MapLibre GL JS map. It is headless: the consumer creates and owns the map, container, style, camera, popups and cursors; Waymark mounts and manages its GeoJSON overlay on the injected map.

Waymark renders circle, line and fill geometry families with per-layer and typed paint (including icon name hooks), and keeps a serialisable document model that round-trips through toJSON(). It emits DOM CustomEvents for layer lifecycle, runtime state and feature interaction — presentation stays in the consumer's hands.

MapLibre GL JS is a peer dependency. Waymark ships no MapLibre code, worker or CSS: install MapLibre alongside Waymark and import MapLibre's stylesheet yourself.

Installation

npm install waymark-js maplibre-gl

Quick start

import { Map } from "maplibre-gl";
import "maplibre-gl/dist/maplibre-gl.css";
import { createInstance } from "waymark-js";

// The consumer creates and owns the map.
const map = new Map({
  container: "map",
  style: "https://tiles.openfreemap.org/styles/bright",
  center: [-128.0097, 50.6539],
  zoom: 13,
});

// Waymark mounts its GeoJSON overlay onto the injected map.
const instance = createInstance({
  map,
  document: {
    config: { id: "map" },
    data: {
      layers: [
        {
          data: {
            type: "FeatureCollection",
            features: [
              {
                type: "Feature",
                properties: { title: "The Scarlet Ibis" },
                geometry: { type: "Point", coordinates: [-128.0097, 50.6539] },
              },
            ],
          },
        },
      ],
    },
  },
});

// Presentation is consumer-owned: Waymark emits, you decide what to show.
instance.on("waymark:feature.click", (event) => {
  const { feature, lngLat } = event.detail;
  console.log(feature.properties, lngLat);
});

// Call destroy() when finished to remove Waymark-owned layers and listeners.
// The map itself stays yours to remove with map.remove().
// instance.destroy();

Events

Events are dispatched as CustomEvents on the injected map's container (map.getContainer()). Use the standard on/off/once API:

instance.on("waymark:feature.click", handler);
instance.once("waymark:instance.destroyed", handler);
Group Events
Lifecycle waymark:instance.created, waymark:instance.destroyed
Data layers waymark:data.layer.added, waymark:data.layer.mounted, waymark:data.layer.error
State waymark:state.changed, waymark:state.debug.changed, waymark:state.types.visibility.changed
Feature interaction waymark:feature.click, waymark:feature.hover, waymark:feature.leave

Popups, tooltips and cursor styling are consumer-owned. See the API reference for payload shapes.

Serialisation

instance.toJSON() returns the canonical InstanceDocument: a plain, serialisable object with config, state and data slices.

const document = instance.toJSON();

// The document is safe to persist and round-trip.
const restored = createInstance({ map: anotherMap, document });
  • config — authored/default intent, kept stable across serialisation.
  • state — runtime delta only; unchanged branches are omitted.
  • data.layers[] — canonical { type: "geojson", data } layers.

Runtime-enriched metadata (MapLibre source/layer IDs, lifecycle phase) is excluded, and map/camera/basemap state is never serialised — the consumer's map owns those.

Commands

npm run dev                # Start the dev playground
npm run build              # Build the library to dist/waymark.js
npm run check:dist         # Assert the dist output shape (single bundle, bare maplibre-gl import, no CSS/worker/Vue)
npm run format             # Format with Prettier
npm run fixtures:refresh   # Regenerate the vendored basemap fixtures (fetches live assets; deliberate act)
npm run docs:sync          # Regenerate llms-full.txt; check docs contract and API doc sync
npm run check:llms         # Fail if docs/llms-full.txt or the docs/public copies are stale
npm run docs:dev           # Serve the docs site locally (Nuxt dev server)
npm run docs:generate      # Generate the static docs site to docs/.output/public
npm test                   # Unit tests (Vitest)
npm run test:browser       # Functional browser tests (Playwright)
npm run screenshots        # Regenerate the committed browser screenshots
npm run screenshots:verify # Regenerate into test-results/ and byte-compare against the committed set
npm run test:ci            # Full CI gate: format → llms check → docs sync → build → dist check → docs generate → unit → browser tests

CI/CD

Four GitHub Actions workflows automate the release pipeline:

  • CI (.github/workflows/ci.yml) — validates every PR and push to master with the full test:ci gate (functional browser tests only; no committed pixels).
  • Release (.github/workflows/release.yml) — runs on every push to master; when the current package.json version has no existing tag, creates the tag and a GitHub Release with auto-generated release notes.
  • Docs (.github/workflows/docs.yml) — builds the Nuxt docs site on master and deploys it to GitHub Pages; published at https://www.ogis.org/waymark-js/.
  • Screenshots identity (.github/workflows/screenshots.yml) — regenerates the committed screenshots on macos-15-intel and byte-compares them against the committed set as an environment-drift canary; uploads the verify report on failure.

Waymark JS — development page

Further Reading

About

A GeoJSON editor for MapLibre GL JS.

Topics

Resources

Stars

17 stars

Watchers

2 watching

Forks

Releases

Sponsor this project

Used by

Contributors

Languages