Terra Draw

repository·main·Indexed 21 days ago

https://github.com/jameslmilner/terra-draw

A library that centralizes map drawing logic to provide a consistent API across multiple JavaScript mapping libraries via an adapter system. It supports Leaflet, Mapbox GL JS, OpenLayers, MapLibre GL JS, Google Maps JS API, and ArcGIS JavaScript SDK. Terra Draw uses a Store for GeoJSON state management, Adapters to bridge with map providers, and Modes to define drawing tools like rectangles and polygons.

Tokens
37.8K
Snippets
104
Records
142
Agent score
71%

What's inside Terra Draw

  1. Overview of styling in Terra Draw

    main

    Terra Draw offers multiple ways to style the drawing experience. Styling can be applied globally or to specific features across different operational modes. The available modes for styling include:

    • Drawing Modes: Styling the features currently being drawn by the user.
    • Selection Mode: Styling features that have been selected.
    • Render Mode: Styling features that are being rendered on the map but are not actively being drawn or selected.

    You can also apply styles to specific feature types (such as Points, LineStrings, or Polygons) if required.

  2. Overview of Terra Draw

    main
    Terra Draw is a library designed to provide frictionless map drawing capabilities across various JavaScript mapping libraries. It centralizes drawing logic and provides multiple out-of-the-box drawing modes. The library is extensible, allowing developers to implement and use their own custom drawing modes.
  3. Configure and use Undo/Redo history

    main

    Terra Draw supports two independent history scopes for undo/redo functionality. Both must be explicitly enabled in the TerraDraw constructor.

    History Scopes

    1. modeLevel (TerraDrawModeUndoRedo): Handles in-progress drawing history within a specific mode. For example, in TerraDrawPolygonMode, it allows undoing/redoing recently added coordinates before the polygon is finished.
    2. sessionLevel (TerraDrawSessionUndoRedo): Handles completed edit history across the entire session. It tracks operations like creating, updating, or deleting fully formed features.

    Implementation

    To use undo/redo, include the appropriate mode and optionally TerraDrawUndoRedoKeyboardShortcuts in your configuration. You can limit memory usage by setting maxStackSize.

    Listen to the history event to sync your UI (e.g., enabling/disabling buttons).

    import {
      TerraDraw,
      TerraDrawPolygonMode,
      TerraDrawModeUndoRedo,
      TerraDrawSessionUndoRedo,
      TerraDrawUndoRedoKeyboardShortcuts,
    } from "terra-draw";
    
    const draw = new TerraDraw({
      adapter,
      modes: [new TerraDrawPolygonMode()],
      undoRedo: {
        modeLevel: new TerraDrawModeUndoRedo({ maxStackSize: 100 }),
        sessionLevel: new TerraDrawSessionUndoRedo({ maxStackSize: 100 }),
        keyboardShortcuts: new TerraDrawUndoRedoKeyboardShortcuts(),
      },
    });
    
    draw.start();
    
    draw.on("history", ({ cause, stack, undoSize, redoSize }) => {
      // cause: "push" | "undo" | "redo"
      // stack: "mode" | "session"
      // undoSize / redoSize: current stack sizes
    
      undoButton.disabled = undoSize === 0;
      redoButton.disabled = redoSize === 0;
    });
  4. How the Store manages feature state

    main

    The Store is the central component responsible for managing the state of all Features added to the map. While the Store is not directly exposed, it is managed via the Terra Draw instance API.

    Key characteristics:

    • Data Format: All data is represented using GeoJSON.
    • Supported Geometries: Currently, the store only supports Point, LineString, and Polygon geometry types.
    • Feature Uniqueness: Every feature must have a unique ID within a single Terra Draw instance. Attempting to add a feature with an existing ID will throw an error.
  5. Understand and use Modes in Terra Draw

    main

    Modes in Terra Draw encapsulate the specific logic required for creating, selecting, and rendering Features on a map. They allow you to switch the behavior of the Terra Draw instance between different interaction states.

    Modes are categorized into three types:

    1. Drawing Modes: Used for creating new features.
    2. Selection Mode: Used for interacting with or selecting existing features.
    3. Render Mode: Used for controlling how features are rendered.

    To use a mode, it must first be added to the Terra Draw instance during instantiation. Once added, you can activate a specific mode by calling the setMode method on your Terra Draw instance and passing the corresponding mode name.

    // Example of switching a mode (assuming 'draw-polygon' was added during instantiation)
    terraDraw.setMode('draw-polygon');
  6. How Terra Draw works: Store, Adapters, and Modes

    main

    Terra Draw is built on three core abstractions that allow it to remain decoupled from specific mapping libraries:

    1. Store: The central state manager. It holds all Feature geometries added to the map. You interact with the Store by instantiating TerraDraw and retrieving data via getSnapshot().
    2. Adapters: Thin wrappers that bridge Terra Draw with a specific mapping library (e.g., Leaflet, Mapbox GL JS, OpenLayers). They handle the library-specific logic for rendering and updating layers.
    3. Modes: The drawing logic. Each mode represents a specific tool (e.g., TerraDrawRectangleMode for rectangles, TerraDrawPolygonMode for polygons). Modes define how user interactions translate into features in the Store.

    To use Terra Draw, you instantiate it with an adapter and an array of modes.

    // Create a Terra Draw instance with an adapter and modes
    const draw = new TerraDraw({ adapter, modes });
    
    // Retrieve all features from the Store
    const features = draw.getSnapshot();
  7. Configure and use Mode names in Terra Draw

    main

    Every mode extending TerraDrawBaseMode has a mode property used to identify it. By default, modes have built-in names (e.g., TerraDrawPolygonMode defaults to polygon).

    You can provide a custom modeName during instantiation to allow multiple configurations of the same mode class. The mode name is also automatically added to the properties object of the resulting GeoJSON Feature.

    When adding data to a mode via addFeatures, Terra Draw uses the mode property of the feature to determine which mode to add it to.

    const draw = new TerraDraw({
      adapter: new TerraDrawLeafletAdapter({ lib, map }),
      modes: [
        // Uses default name "polygon"
        new TerraDrawPolygonMode(),
    
        // Uses custom name "custom-polygon"
        new TerraDrawPolygonMode({
          modeName: "custom-polygon",
        }),
    
        // Render Modes require a custom name
        new TerraDrawRenderMode({
          modeName: "custom-name",
        }),
      ],
    });
    
    draw.start();
  8. Use TerraDrawRenderMode for uneditable features

    main

    TerraDrawRenderMode is used to render features that are not intended to be edited (e.g., contextual data). You can add multiple render modes to a single TerraDraw instance to apply different styles to different layers of data.

    Features are added to a render mode using draw.addFeatures([feature]).

    const draw = new TerraDraw({
      adapter: new TerraDrawMapboxGLAdapter({ map, lib }),
      modes: [
        new TerraDrawRenderMode({
          modeName: "contextual",
          styles: {
            pointColor: "#00FFFF",
            pointOutlineColor: "#00FF00",
          },
        }),
      ],
    });
    
    draw.addFeatures([point]);
  9. Follow Conventional Commits for Terra Draw

    main

    The project enforces Conventional Commits via precommit hooks. Commit messages must include a type and a scope (the package name).

    Standard Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert

    Examples:

    • feat(terra-draw): some commit message
    • feat(terra-draw-openlayers-adapter): some commit relating specifically to OpenLayers adapter package
    • feat(terra-draw)!: some breaking change commit message (The ! indicates a breaking change and triggers a major version bump).

    Version Bumping Logic:

    • feat triggers a minor release (e.g., v1.0.0 -> v1.1.0).
    • fix triggers a patch release (e.g., v1.0.0 -> v1.0.1).
    • Breaking changes (!) trigger a major release (e.g., v1.0.0 -> v2.0.0).
  10. Supported mapping libraries and adapters

    main

    Terra Draw uses 'adapters' to interface with different mapping libraries. Each adapter is distributed as a separate npm package. The following libraries are currently supported:

    LibraryVersion supportednpm package
    Leafletv1terra-draw-leaflet-adapter
    OpenLayersv10terra-draw-openlayers-adapter
    MapLibre GL JSv4/5terra-draw-maplibre-gl-adapter
    Google Maps JS APIv3terra-draw-google-maps-adapter
    Mapbox GL JSv3terra-draw-mapbox-gl-adapter
    ArcGIS JavaScript SDKv4terra-draw-arcgis-adapter
  11. What are Adapters in Terra Draw

    main

    Adapters serve as the bridge between the terra-draw core library and external mapping libraries (such as Leaflet, Google Maps, or MapLibre).

    An adapter performs two primary roles:

    1. Event Handling: It captures input events from the mapping library and passes them to the Terra Draw core to create and manage geometries in the store.
    2. Rendering: It takes the geometries from the Terra Draw store and renders them specifically for the target mapping library's API.
  12. Understand Terra Draw Mode Types

    main

    Modes are categorized into four types via the ModeTypes enum:

    • Drawing: For creating new geometries on the map.
    • Select: For selecting and manipulating existing geometries.
    • Static: An inert mode that simply renders geometries.
    • Render: A 'view only' mode for rendering geometries.

    Note: You may only have one select mode instantiated in any single TerraDraw instance.