### Full Usage Example with MapLibre GL
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/1.GETTING_STARTED.md
A complete example demonstrating how to import Terra Draw and its MapLibre GL adapter, initialize a MapLibre map, create a Terra Draw instance with the adapter and modes, and start drawing.
```javascript
// Import Terra Draw
import { TerraDraw, TerraDrawRectangleMode } from "terra-draw";
import { TerraDrawMapLibreGLAdapter } from 'terra-draw-maplibre-gl-adapter
// Import MapLibreGL
import { MapLibreGL as lib } from "maplibre-gl";
// Create MapLibre Map, targeting
const map = new MapLibreGL.Map({ container: "map" });
// Create Terra Draw
const draw = new TerraDraw({
// Using the MapLibre Adapter
adapter: new TerraDrawMapLibreGLAdapter({ map, lib }),
// Add the Rectangle Mode
modes: [new TerraDrawRectangleMode()],
});
// Start drawing
draw.start();
draw.setMode("rectangle");
```
--------------------------------
### Install Terra Draw and OpenLayers Adapter
Source: https://github.com/jameslmilner/terra-draw/blob/main/packages/terra-draw-openlayers-adapter/README.md
Install the core terra-draw package and the OpenLayers adapter using npm.
```shell
npm install terra-draw terra-draw-openlayers-adapter
```
--------------------------------
### Install Terra Draw and MapLibre GL Adapter
Source: https://github.com/jameslmilner/terra-draw/blob/main/packages/terra-draw-maplibre-gl-adapter/README.md
Install the core Terra Draw package and the MapLibre GL adapter using npm.
```shell
npm install terra-draw terra-draw-maplibre-gl-adapter
```
--------------------------------
### Build Terra Draw Storybook
Source: https://github.com/jameslmilner/terra-draw/blob/main/packages/storybook/README.md
Generate a static production build of the Storybook. This is useful for deployment or sharing examples.
```shell
npm run storybook:build
```
--------------------------------
### Run Terra Draw Storybook in Development
Source: https://github.com/jameslmilner/terra-draw/blob/main/packages/storybook/README.md
Use this command to start the Storybook development server. This allows for live updates as you make changes to components.
```shell
npm run storybook
```
--------------------------------
### Remove Features by ID or Clear Store
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Provides examples for removing specific features from the store using their IDs and for clearing all features from the store.
```javascript
// Remove Feature from the Store by ID
draw.removeFeatures(["id-1", "id-2", "id-3"]);
// Remove all Features from the Store
draw.clear();
```
--------------------------------
### Custom Marker Styling in Custom Modes
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Example of how to style marker icons within a custom mode's `styleFeature` method, allowing dynamic configuration of marker properties.
```typescript
styleFeature(feature: GeoJSONStoreFeatures): TerraDrawAdapterStyling {
const styles = { ...getDefaultStyling() };
if (
feature.type === "Feature" &&
feature.geometry.type === "Point" &&
feature.properties.mode === this.mode
) {
styles.zIndex = Z_INDEX.LAYER_THREE;
styles.markerHeight = this.getNumericStylingValue(
this.styles?.markerHeight,
30, // Whatever the default maker height is
feature,
);
styles.markerWidth = this.getNumericStylingValue(
this.styles?.markerWidth,
30, // Whatever the default maker width is
feature,
);
styles.markerUrl = this.getUrlStylingValue(
this.styles?.markerUrl,
"https://www.example.com/your-custom-marker.png"
feature,
);
}
return styles;
}
```
--------------------------------
### Styling TerraDrawPolygonMode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Example of how to style the TerraDrawPolygonMode by passing a styles object to its constructor. Colors are represented as 6-digit Hex colors.
```javascript
const draw = new TerraDraw({
// Create Adapter
adapter: new TerraDrawMapboxGLAdapter({ map, lib }),
// Create Modes
modes: [
// Polygon Mode
new TerraDrawPolygonMode({
// Pass styles to the constructor
styles: {
// Fill colour (a string containing a 6 digit Hex color)
fillColor: "#00FFFF",
// Fill opacity (0 - 1)
fillOpacity: 0.7,
// Outline colour (Hex color)
outlineColor: "#00FF00",
//Outline width (Integer)
outlineWidth: 2,
},
}),
],
});
```
--------------------------------
### Listen for Select Event
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Get selected features by listening to the 'select' event. This method provides the ID of the selected feature.
```typescript
draw.on('select', (id: string) => {
const snapshot = draw.getSnapshot()
// Search the snapshot for the selected polygon
const polygon = snapshot.find((feature) => feature.id === id)
})
```
--------------------------------
### Retrieve All Features from Store
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Get a deep copy of all features currently in the store using `getSnapshot`. Mutating the returned array will not affect the store's state.
```javascript
// Get an array of all features in the Store. These features will be copies.
const features = draw.getSnapshot();
```
--------------------------------
### Add Marker Feature with Properties
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Example of adding a feature programmatically that will render as a marker icon. The 'marker: true' property in the GeoJSON properties is crucial for this.
```typescript
draw.addFeatures([
{
type: "Feature",
geometry: {
type: "Point",
coordinates: [-0.09, 51.505]
},
properties: {
mode: "marker", // or your custom mode name
marker: true, // Required for marker icon rendering
}
}
]);
```
--------------------------------
### Render Mode Styling for Points
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Example of creating a Terra Draw instance with a Render Mode to style uneditable points. This includes setting a unique mode name and defining point styles.
```javascript
const draw = new TerraDraw({
adapter: new TerraDrawMapboxGLAdapter({ map, lib }),
modes: [
new TerraDrawRenderMode({
modeName: "contextual",
styles: {
pointColor: "#00FFFF",
pointOutlineColor: "#00FF00",
},
}),
],
});
draw.addFeatures([point]);
```
--------------------------------
### Enable Drawing Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Set the active drawing mode on the Terra Draw instance after initialization. This allows users to start drawing with the specified mode.
```javascript
// Once we have started Terra Draw
draw.start();
// Enable the TerraDrawPolygonMode
draw.setMode("polygon");
// Switch to the TerraDrawFreehandMode
draw.setMode("freehand");
```
--------------------------------
### Initialize Terra Draw with Undo/Redo and Handle History Events
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/6.EVENTS.md
Configure Terra Draw with undo/redo capabilities and listen for the 'history' event to update UI elements like undo/redo buttons based on stack sizes. This setup enables custom control over undo/redo actions.
```typescript
import {
TerraDraw,
TerraDrawPolygonMode,
TerraDrawModeUndoRedo,
TerraDrawSessionUndoRedo,
TerraDrawUndoRedoKeyboardShortcuts,
} from "terra-draw";
const draw = new TerraDraw({
adapter,
modes: [new TerraDrawPolygonMode()],
undoRedo: {
modeLevel: new TerraDrawModeUndoRedo({ maxStackSize: 100 }), // Optional
sessionLevel: new TerraDrawSessionUndoRedo({ maxStackSize: 100 }), // Optional
keyboardShortcuts: new TerraDrawUndoRedoKeyboardShortcuts(), // Optional
},
});
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;
});
```
--------------------------------
### GeoJSON Feature with Mode Property
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
An example of a GeoJSON Feature created by drawing a polygon, showing the 'mode' property set to 'polygon'.
```json
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"properties": {
"mode": "polygon"
},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-1.82645, 51.17888],
[-1.826208, 51.179025],
[-1.825859, 51.178867],
[-1.82609, 51.178682],
[-1.82645, 51.17888]
]
]
}
}
]
}
```
--------------------------------
### Custom Polygon Validation Example
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Implement custom validation for polygons to prevent self-intersection. This validation runs when the drawing is finished or committed.
```typescript
polygonMode = new TerraDrawPolygonMode({
validation: (feature, { updateType }) => {
if (updateType === "finish" || updateType === "commit") {
return ValidateNotSelfIntersecting(feature);
}
return { valid: true }
}
});
```
--------------------------------
### Get Features at Pointer Event
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/6.EVENTS.md
Retrieve features under the pointer, with options to control search radius, polygon inclusion, and ignored feature types. Defaults are `ignoreSelectFeatures: false` and `pointerDistance: 30`.
```typescript
document.addEventListener("mousemove", (event) => {
const featuresAtMouseEvent = draw.getFeaturesAtPointerEvent(event, {
// The number pixels to search around input point
pointerDistance: 40,
// By default polygons are only considered at the location if they are directly selected
// this option allows them to be returned if they are within the pointer distance
includePolygonsWithinPointerDistance: true,
// Ignore features that have been selected
ignoreSelectFeatures: true,
// ignore coordinates
ignoreCoordinatePoints: true,
// Ignore the current feature if one is being drawn
ignoreCurrentlyDrawing: true,
// Ignore closing points
ignoreClosingPoints: true,
// Ignore snapping points
ignoreSnappingPoints: true,
// Adds to feature's properties information about the closest coordinate to pointer event
addClosestCoordinateInfoToProperties: true
});
// Do something with the features...
console.log({ featuresAtMouseEvent });
});
```
--------------------------------
### Retrieve a Single Feature by ID
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Get a copy of a specific feature from the store using its ID with the `getSnapshotFeature` method. This is useful when you need to work with an individual feature's data.
```javascript
// Get a copy of a specific feature from the Store
const features = draw.getSnapshotFeature('f8e5a38d-ecfa-4294-8461-d9cff0e0d7f8');
```
--------------------------------
### Get Features at Longitude/Latitude
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/6.EVENTS.md
Retrieve features at a specific longitude and latitude. Options include search radius and ignoring selected features. Defaults are `ignoreSelectFeatures: false` and `pointerDistance: 30`.
```typescript
map.on("mousemove", (event) => {
const { lng, lat } = event.lngLat;
const featuresAtLngLat = draw.getFeaturesAtLngLat(
{ lng, lat },
{
// The number pixels to search around input point
pointerDistance: 40,
// Ignore features that have been selected
ignoreSelectFeatures: true,
},
);
console.log({ featuresAtLngLat });
});
```
--------------------------------
### Instantiate MapLibre Adapter
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/3.ADAPTERS.md
Demonstrates how to import MapLibre, create a map instance, and then instantiate the TerraDrawMapLibreGLAdapter with the map instance.
```javascript
import maplibregl from "maplibre-gl";
import { TerraDraw, TerraDrawRectangleMode } from "terra-draw";
import { TerraDrawMapLibreGLAdapter } from "terra-draw-maplibre-gl-adapter";
// Create Map Instance
const map = new maplibregl.Map({
container: id,
style: 'https://demotiles.maplibre.org/style.json',
center: [lng, lat],
zoom: zoom,
});
// Create Adapter
const adapter = new TerraDrawMapLibreGLAdapter({
// Pass in the map instance
map,
});
```
--------------------------------
### Run Headed E2E Tests
Source: https://github.com/jameslmilner/terra-draw/blob/main/packages/e2e/README.md
Execute E2E tests with a browser window opening, allowing visual inspection of the test execution.
```shell
npm run test:headed
```
--------------------------------
### Instantiate Render Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Create a new Render Mode instance. A unique `modeName` is required to identify this mode, allowing for custom styling and management.
```javascript
const renderMode = new TerraDrawRenderMode({
// Unique mode name used to identify the Mode (required)
modeName: "custom-name",
});
```
--------------------------------
### Instantiating Terra Draw with Custom Mode Names
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Demonstrates how to add different modes, including custom-named instances of the same mode class, during Terra Draw instantiation.
```javascript
const draw = new TerraDraw({
adapter: new TerraDrawLeafletAdapter({ lib, map }),
modes: [
// Polygon Mode has the built-in name "polygon"
new TerraDrawPolygonMode(),
// Polygon Mode has the custom name "custom-polygon"
new TerraDrawPolygonMode({
modeName: "custom-polygon",
}),
// Render Modes are given custom names
new TerraDrawRenderMode({
modeName: "custom-name",
}),
],
});
draw.start();
```
--------------------------------
### Styling Polygons with Random Colors
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Demonstrates how to style polygons with dynamically generated random colors using a styling function and a cache for feature IDs. This approach allows for unique styling of each polygon.
```typescript
function getRandomColor() {
const letters = "0123456789ABCDEF";
let color = "#";
for (let i = 0; i < 6; i++) {
color += letters[Math.floor(Math.random() * 16)];
}
return color;
}
const colorCache: Record = {};
const draw = new TerraDraw({
adapter: new TerraDrawMapboxGLAdapter({
map,
coordinatePrecision: 9,
}),
modes: [
new TerraDrawPolygonMode({
styles: {
fillColor: ({ id }) => {
colorCache[id] = colorCache[id] || getRandomColor();
return colorCache[id];
},
},
}),
],
});
draw.on("delete", (ids) => ids.forEach((id) => delete cache[id]));
```
--------------------------------
### Configure Circle with Both Interactions
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Instantiate a Circle mode to enable both click-move-click and click-and-drag interactions. This provides maximum flexibility for users.
```typescript
new TerraDrawCircleMode({ drawInteraction: "click-move-or-drag" })
```
--------------------------------
### Check Drawing State
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Retrieve the current state of the active drawing mode. This can be used to determine if the user is currently drawing or has started a drawing operation.
```typescript
// Once we have started Terra Draw
draw.start();
// Enable the TerraDrawPolygonMode
draw.setMode("polygon");
// Determine if the user is currently drawing or not - either 'started' or 'drawing'
const state = draw.getModeState();
```
--------------------------------
### Styling Lines
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Customize the appearance of selected lines using color, opacity, and width properties.
```markdown
| Property | Type | Example Value | Description |
| --------------------------- | ------------ | ------------- | ----------------------- |
| `selectedLineStringColor` | Hex Color | `#00FFFF` | The color of the line |
| `selectedLineStringOpacity` | Number (0-1) | `0.7` | The opacity of the line |
| `selectedLineStringWidth` | Integer | `3` | The width of the line |
```
--------------------------------
### Styling Points
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Customize the appearance of selected points using color, opacity, and width properties for both fill and outline.
```markdown
| Property | Type | Example Value | Description |
| ----------------------------- | ------------ | ------------- | -------------------------------- |
| `selectedPointColor` | Hex Color | `#00FFFF` | The fill color of the point |
| `selectedPointOpacity` | Number (0-1) | `0.7` | The fill opacity of the point |
| `selectedPointWidth` | Integer | `2` | The width of the point |
| `selectedPointOutlineColor` | Hex Color | `#00FFFF` | The outline color of the point |
| `selectedPointOutlineOpacity` | Number (0-1) | `0.7` | The outline opacity of the point |
| `selectedPointOutlineWidth` | Integer | `2` | The outline width of the point |
```
--------------------------------
### Run Terra Draw Tests with Coverage
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/7.DEVELOPMENT.md
Run tests and generate a code coverage report. The script fails if coverage drops below 80%.
```shell
npm run test:coverage
```
--------------------------------
### Listen for Styling Changes
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Shows how to listen for the 'styling' change event, which is emitted when styles are updated using `updateModeOptions`.
```typescript
draw.on('change', (ids, type) => {
if (type === 'styling') {
console.log('styling has changed');
}
})
```
--------------------------------
### Dynamically Set Feature Mode Before Adding
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Explains how to dynamically assign a drawing mode to features before adding them to the store. This ensures features are associated with the correct mode, especially when multiple modes of the same type exist.
```javascript
// Iterate over the points
points.forEach((point) => {
// Set the `mode` property to "point"
point.properties.mode = "point";
});
draw.addFeatures(points);
```
--------------------------------
### Run Headless E2E Tests
Source: https://github.com/jameslmilner/terra-draw/blob/main/packages/e2e/README.md
Execute E2E tests without opening a browser window. This is useful for CI/CD pipelines.
```shell
npm run test
```
--------------------------------
### Select and LineString Mode Snapping Configuration
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Configure snapping behavior for Select and LineString modes, allowing snapping to coordinates and line segments of existing features.
```typescript
new TerraDrawSelectMode({
projection: "web-mercator",
flags: {
polygon: {
feature: {
coordinates: {
snappable: {
toLine: true,
toCoordinate: true
},
}
}
},
linestring: {
feature: {
coordinates: {
snappable: {
toLine: true,
toCoordinate: true
},
}
}
}
}
})
```
--------------------------------
### Update Polygon Mode Styles Dynamically
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Demonstrates how to initialize a mode with styles and then update them dynamically using `updateModeOptions`. This method allows for partial updates to the styles object.
```typescript
const draw = new TerraDraw({
adapter: new TerraDrawMapboxGLAdapter({ map, lib }),
modes: [
new TerraDrawPolygonMode({
// Pass styles to the constructor
styles: {
fillColor: "#00FFFF",
fillOpacity: 0.7,
outlineColor: "#00FF00",
outlineWidth: 2,
},
}),
],
});
// Later on...
draw.updateModeOptions('polygon', {
// We can pass in a partial styles object and it will just update the fields passed
styles: {
fillColor: "#b3250a",
fillOpacity: 0.85
}
})
```
--------------------------------
### Styling Polygons
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Customize the appearance of selected polygons, including fill and outline colors, opacities, and widths.
```markdown
| Property | Type | Example Value | Description |
| ----------------------------- | ------------ | ------------- | ---------------------------------- |
| `selectedPolygonColor` | Hex Color | `#00FFFF` | The fill color of the polygon |
| `selectedPolygonFillOpacity` | Number (0-1) | `0.7` | The fill opacity of the polygon |
| `selectedPolygonOutlineColor` | Hex Color | `#00FFFF` | The outline color of the polygon |
| `selectedPolygonOutlineOpacity` | Number (0-1) | `0.7` | The outline opacity of the polygon |
| `selectedPolygonOutlineWidth` | Integer | `2` | The outline width of the polygon |
```
--------------------------------
### Environment Variables for Terra Draw Storybook
Source: https://github.com/jameslmilner/terra-draw/blob/main/packages/storybook/README.md
Configure API keys and access tokens for different mapping libraries using a .env file. This is necessary for Storybook to function correctly with Google Maps and Mapbox.
```shell
GOOGLE_API_KEY=your-google-key-here
MAPBOX_ACCESS_TOKEN=your-mapbox-key-here
```
--------------------------------
### Configure Rectangle with Click-and-Drag
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Instantiate a Rectangle mode to only allow the click-and-drag interaction. This is useful for touch-friendly interfaces.
```typescript
new TerraDrawRectangleMode({ drawInteraction: "click-drag" })
```
--------------------------------
### Store and Restore Terra Draw Data with localStorage
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Persist drawing data by taking a snapshot with `getSnapshot`, filtering out internal points, and stringifying it for `localStorage`. Restore data by parsing the string and using `addFeatures`.
```javascript
const features = draw.getSnapshot()
// We don't want any mid points or selection points so we filter them out
const filteredFeatures = features.filter((f) => !f.properties.midPoint && !f.properties.selectionPoint)
// localStorage can only store strings, so we stringify the features first
localStorage.setItem('terra-draw-data', JSON.stringify(filteredFeatures));
// Later on, perhaps after the user has refreshed.
const retrievedFeatures = localStorage.getItem('terra-draw-data');
if (retrievedFeatures) {
draw.addFeatures(JSON.parse(retrievedFeatures))
}
```
--------------------------------
### Switching Between Terra Draw Modes
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Shows how to switch the active mode of a Terra Draw instance using the `setMode` method.
```javascript
draw.setMode('polygon')
// Later on...
draw.setMode('point')
```
--------------------------------
### Dynamically Update Mode Options
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Update mode options after instantiation using the `updateModeOptions` method. This allows for on-the-fly changes to mode behavior, such as modifying snapping settings.
```typescript
draw.updateModeOptions('polygon', {
snapping: {
toLine: false,
toCoordinate: true
}
})
```
--------------------------------
### Configure Rectangle with Default Click-Move-Click
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Explicitly set the Rectangle mode to use the default click-move-click interaction. This is the standard GIS/CAD-style drawing workflow.
```typescript
new TerraDrawRectangleMode({ drawInteraction: "click-move" })
```
--------------------------------
### Enable Coordinate Points in LineString Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Activate the rendering of coordinate points for each vertex in the LineString mode. These points are managed internally by the mode and store.
```typescript
import { TerraDraw, TerraDrawLineStringMode } from "terra-draw";
const draw = new TerraDraw({
adapter,
modes: [
new TerraDrawLineStringMode({
showCoordinatePoints: true,
}),
],
});
draw.start();
draw.setMode("linestring");
```
--------------------------------
### Styling Selection Points
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Customize the appearance of selection points used for moving existing geometry points, including fill and outline styles.
```markdown
| Property | Type | Example Value | Description |
| ------------------------------ | ------------ | ------------- | ------------------------------------------ |
| `selectionPointColor` | Hex Color | `#00FFFF` | The fill color of the selection point |
| `selectionPointOpacity` | Number (0-1) | `0.7` | The fill opacity of the selection point |
| `selectionPointOutlineColor` | Hex Color | `#00FF00` | The outline color of the selection point |
| `selectionPointOutlineOpacity` | Number (0-1) | `0.7` | The outline opacity of the selection point |
| `selectionPointWidth` | Integer | `2` | The width of the selection point |
| `selectionPointOutlineWidth` | Integer | `3` | The width of the selection point |
```
--------------------------------
### Polygon Mode Snapping Configuration
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Configure snapping to existing feature coordinates and line segments for the Polygon mode. Includes a validation function to prevent self-intersection on finish or commit.
```typescript
new TerraDrawPolygonMode({
snapping: {
toLine: true,
toCoordinate: true,
},
validation: (feature, { updateType }) => {
if (updateType === "finish" || updateType === "commit") {
return ValidateNotSelfIntersecting(feature);
}
return { valid: true };
},
})
```
--------------------------------
### Run Smoke Tests on Terra Draw Storybook
Source: https://github.com/jameslmilner/terra-draw/blob/main/packages/storybook/README.md
Execute a series of automated smoke tests against the Storybook stories. This helps ensure that all stories load and function as expected.
```shell
npm run storybook:test
```
--------------------------------
### Run Terra Draw Tests without Type Checking and with Coverage
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/7.DEVELOPMENT.md
Combines skipping type checking with generating a code coverage report for efficient local testing.
```shell
npm run test:nocheck:coverage
```
--------------------------------
### Enable Coordinate Points in Polygon Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Activate the rendering of coordinate points for each vertex in the Polygon mode. These points are managed internally by the mode and store.
```typescript
import { TerraDraw, TerraDrawPolygonMode } from "terra-draw";
const draw = new TerraDraw({
adapter,
modes: [
new TerraDrawPolygonMode({
showCoordinatePoints: true,
}),
],
});
draw.start();
draw.setMode("polygon");
```
--------------------------------
### Generate and Add Feature with Custom ID
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Illustrates generating a feature ID using `getFeatureId` before adding a feature to the store. This is useful when you need to manage feature IDs externally.
```javascript
const id = draw.getFeatureId()
// Add a Point to the Store
draw.addFeatures([
{
id,
type: "Feature",
geometry: {
type: "Point",
coordinates: [-1.825859, 51.178867],
},
properties: {
mode: "point",
},
},
]);
```
--------------------------------
### Style Selected Polygon in Selection Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Illustrates how to pass styles to the `TerraDrawSelectMode` constructor to customize the appearance of selected polygons. Different geometry types can have distinct selection styles.
```javascript
const draw = new TerraDraw({
adapter: new TerraDrawMapboxGLAdapter({ map, lib }),
modes: [
new TerraDrawPolygonMode(),
new TerraDrawSelectMode({
styles: {
// Fill colour
selectedPolygonColor: "#00FFFF",
// Fill opacity
selectedPolygonFillOpacity: 0.7,
// Outline colour
selectedPolygonOutlineColor: "#00FF00",
//Outline width
selectedPolygonOutlineWidth: 2,
},
flags: {
polygon: {
feature: {
draggable: true,
},
},
},
}),
],
});
draw.start();
```
--------------------------------
### Polygon Mode Custom Snapping
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Implement custom snapping logic in the Polygon mode by providing a `toCustom` function. This allows snapping to arbitrary positions, such as external data or other features.
```typescript
new TerraDrawPolygonMode({
snapping: {
toCustom: () => {
// You can return whatever position you want here, determined in any way you see fit!
return [0, 0]
}
}
})
```
--------------------------------
### Styling Mid Points
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/5.STYLING.md
Customize the appearance of midpoints used for adding new points to geometries, including fill and outline styles.
```markdown
| Property | Type | Example Value | Description |
| ------------------------ | ------------ | ------------- | ------------------------------------ |
| `midPointColor` | Hex Color | `#00FFFF` | The fill color of the mid point |
| `midPointOpacity` | Number (0-1) | `0.7` | The fill opacity of the mid point |
| `midPointOutlineColor` | Hex Color | `#00FFFF` | The outline color of the mid point |
| `midPointOutlineOpacity` | Number (0-1) | `0.7` | The outline opacity of the mid point |
| `midPointWidth` | Integer | `2` | The width of the mid point |
| `midPointOutlineWidth` | Integer | `3` | The width of the mid point |
```
--------------------------------
### LineString Mode with Globe Projection for Geodesic Lines
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Draw geodesic lines on a web mercator map by setting the `projection` to 'globe' and using `insertCoordinates` with a specified strategy and value.
```typescript
new TerraDrawLineStringMode({
projection: 'globe',
insertCoordinates: {
strategy: 'amount',
value: 10
}
}),
```
--------------------------------
### Configure Pointer Distance for Interaction Accuracy
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Adjust the 'pointerDistance' option to define the buffer zone around coordinates for selection or closing events. This helps differentiate intended interactions from general map clicks.
```typescript
new TerraDrawPolygonMode({ pointerDistance: 25 }),
```
--------------------------------
### Toggle Coordinate Points in Polygon Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Demonstrates how to enable and disable coordinate points for the polygon mode after instantiation using `updateModeOptions`. This function also affects existing features within the specified mode.
```typescript
draw.updateModeOptions("polygon", {
showCoordinatePoints: true,
});
draw.updateModeOptions("polygon", {
showCoordinatePoints: false,
});
```
--------------------------------
### Configure Selection Mode Flags
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
This snippet shows the various flags available when instantiating TerraDrawSelectMode, controlling manual selection/deselection and editing capabilities for different feature types.
```javascript
new TerraDrawSelectMode({
// Allow manual deselection of features
allowManualDeselection: true, // this defaults to true - allows users to deselect by clicking on the map
// Allow manual selection of features by clicking on the map
allowManualSelection: true, // this defaults to true - set to false to require selection via draw.selectFeature(id)
// Enable editing tools by Feature
flags: {
// Point
point: {
feature: {
draggable: true,
},
},
// Polygon
polygon: {
feature: {
draggable: true,
coordinates: {
midpoints: true,
draggable: true,
deletable: true,
},
},
},
// Line
linestring: {
feature: {
draggable: true,
coordinates: {
midpoints: true,
draggable: true,
deletable: true,
},
},
},
// Freehand
freehand: {
feature: {
draggable: true,
coordinates: {
midpoints: true,
draggable: true,
deletable: true,
},
},
},
// Circle
circle: {
feature: {
draggable: true,
coordinates: {
midpoints: true,
draggable: true,
deletable: true,
},
},
},
// Rectangle
rectangle: {
feature: {
draggable: true,
coordinates: {
midpoints: true,
draggable: true,
deletable: true,
},
},
},
},
// Styles go here...
styles: {
// See Styling Guide for more information
},
});
```
--------------------------------
### Configure Polygon Feature Editing in Selection Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
This snippet demonstrates how to enable editing for polygon features within the Selection Mode, including making the entire feature draggable and configuring coordinate manipulation.
```javascript
const selectMode = new TerraDrawSelectMode({
flags: {
polygon: {
feature: {
// The entire Feature can be moved
draggable: true,
// Individual coordinates that make up the Feature...
coordinates: {
// Midpoint be added
midpoints: {
// Midpoint be dragged
draggable: true
},
// Can be moved
draggable: true,
// Can snap to other coordinates from geometries _of the same mode_
snappable: true,
// Allow resizing of the geometry from a given origin.
// center will allow resizing of the aspect ratio from the center
// and opposite allows resizing from the opposite corner of the
// bounding box of the geometry.
resizable: 'center', // can also be 'opposite', 'center-fixed', 'opposite-fixed'
// Can be deleted
deletable: true,
// Provide a custom validation that will run when we attempt to edit the geometry
validation: (feature, context) => {
// context has the methods project and unproject and be used to go from screen space
// to geographic space and vice versa
// ValidateMinAreaSquareMeters can be imported from Terra Draw
return ValidateMinAreaSquareMeters(feature.geometry, 1000);
}
},
},
},
},
});
```
--------------------------------
### Handle Invalid Features After Adding
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Shows how to filter and process features that failed validation after being added to the store. This allows for specific error handling, such as sending analytics for features with holes.
```typescript
// Create a list of features that did not pass validation
const invalidFeatures = result.filter(({ valid }) => !valid);
// Do something with the information
invalidFeatures.forEach(({ reason }) => {
// ValidationReasonFeatureHasHoles is exposed in the Terra Draw library under ValidationReasons
if (reason === ValidationReasonFeatureHasHoles) {
// sentAnalytics is a fake function that illustrates reacting to a invalid feature
sentAnalytics({
type: 'error',
info: 'Feature that is being added to Terra Draw has holes when it should not'
})
}
});
```
--------------------------------
### Handle Terra Draw Change Events
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/6.EVENTS.md
Listen for changes to features, including creation, update, deletion, and styling. The 'context' object provides details about the event's origin and target.
```typescript
draw.on("change", (ids, type, context) => {
//Done editing
if (type === 'change') {
if (context?.origin === 'api') {
console.log('this was changed via the API!')
} else if (context?.origin === undefined) {
console.log('this change did not originate from the API!')
}
}
});
```
```typescript
draw.on("change", (ids, type, context) => {
if (type === 'change') {
if (context?.target === 'properties') {
console.log('properties were changed')
} else if (context?.target === 'geometry') {
console.log('geometry was changed')
}
}
});
```
```typescript
draw.on("change", (ids, type) => {
//Done editing
if (type === "delete") {
// Get the Store snapshot
const snapshot = draw.getSnapshot();
// Do something
//...
}
});
```
```typescript
draw.on("change", (ids: string[], type: string) => {
// Possible type values:
// 'create'
// 'update'
// 'delete'
// 'styling'
// Do something
//...
});
```
--------------------------------
### Programmatically Add, Select, and Deselect Features
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Manage features programmatically using Terra Draw instance methods. This includes adding features, selecting them by ID, and deselecting them.
```typescript
const draw = new TerraDraw({
adapter,
modes: [
new TerraDrawPointMode(),
new TerraDrawSelectMode({
flags: {
point: { feature: { draggable: true } },
},
}),
],
});
draw.start();
// Add feature programmatically
const result = draw.addFeatures([
{
id: "f8e5a38d-ecfa-4294-8461-d9cff0e0d7f8",
type: "Feature",
geometry: {
type: "Point",
coordinates: [-25.431289673, 34.355907891],
},
properties: {
mode: "point",
},
},
]);
// Log out if the feature passed validation or not
if (!result[0].valid) {
throw new Error(result.reason ? result.reason : 'Feature f8e5a38d-ecfa-4294-8461-d9cff0e0d7f8 was invalid')
}
// Select a given feature
draw.selectFeature("f8e5a38d-ecfa-4294-8461-d9cff0e0d7f8");
// Deselect the given feature
draw.deselectFeature("f8e5a38d-ecfa-4294-8461-d9cff0e0d7f8");
```
--------------------------------
### Handle Terra Draw Finish Event
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/6.EVENTS.md
Respond to the completion of drawing or editing actions. The context object specifies the action type (e.g., 'draw', 'dragFeature') and the mode.
```typescript
draw.on("finish", (id: string, context: { action: string, mode: string }) => {
if (context.action === 'draw') {
// Do something for draw finish event
} else if (context.action === 'dragFeature') {
// Do something for a drag finish event
} else if (context.action === 'dragCoordinate') {
//
}else if (context.action === 'dragCoordinateResize') {
//
}
});
```
--------------------------------
### Add Modes to Terra Draw Instance
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Add custom drawing modes, such as Polygon and Freehand, to the Terra Draw instance during initialization. Built-in modes have predefined names.
```javascript
const draw = new TerraDraw({
adapter: new TerraDrawLeafletAdapter({ lib, map }),
modes: [
// Polygon Mode has the built-in name "polygon"
new TerraDrawPolygonMode(),
// Freehand Mode has the built-in name "freehand"
new TerraDrawFreehandMode(),
],
});
draw.start();
```
--------------------------------
### Run Terra Draw Tests without Type Checking
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/7.DEVELOPMENT.md
Execute tests while skipping TypeScript type checking for faster local development. This option only checks explicitly tested files.
```shell
npm run test:nocheck
```
--------------------------------
### Configure Marker Icons in TerraDrawMarkerMode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Set custom marker icons, width, and height for the Marker mode. Ensure your GeoJSON features have a 'marker: true' property in their properties to render as icons.
```typescript
new TerraDrawMarkerMode({
styles: {
markerUrl:
"https://leafletjs.com/examples/custom-icons/leaf-green.png",
markerWidth: 25,
markerHeight: 60,
},
})
```
--------------------------------
### Access Map Event with Selected Feature
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Access the specific mouse/map event alongside the selected geometry by registering a click event on the map. This allows for actions like positioning popups based on user interaction.
```typescript
// Register an on click event onto the map itself
map.on('click', (event) => {
const snapshot = draw.getSnapshot()
// Search the snapshot for the selected polygon
const polygon = snapshot.find((feature) => feature.properties.selected === true && feature.geometry.type === 'Polygon')
// If there is not a selected polygon, don't do anything
if (!polygon) {
return
}
// Else create or update the popup location to be the cursor position!
if (popup) {
popup.setLngLat([event.lngLat.lng, event.lngLat.lat])
} else {
popup = new maplibregl.Popup({ closeOnClick: false })
.setLngLat([event.lngLat.lng, event.lngLat.lat])
.setHTML('Example Popup
')
.addTo(map);
}
})
```
--------------------------------
### Handle Terra Draw Select Event
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/6.EVENTS.md
Trigger a callback when a feature is selected. This event provides the ID of the selected feature.
```typescript
draw.on("select", (id: string) => {
// Do something
//...
});
```
--------------------------------
### Terra Draw Base Mode Anatomy
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/7.DEVELOPMENT.md
Defines the core methods and event handlers for creating custom modes in Terra Draw. Extend `TerraDrawBaseMode` to implement drawing or editing features.
```typescript
/** @internal */
start() {
this.setStarted();
}
/** @internal */
stop() {
this.setStopped();
}
/** @internal */
onClick(event: TerraDrawMouseEvent) {}
/** @internal */
onMouseMove(event: TerraDrawMouseEvent) {}
/** @internal */
onKeyDown(event: TerraDrawKeyboardEvent) {}
/** @internal */
onKeyUp(event: TerraDrawKeyboardEvent) {}
/** @internal */
onDragStart(event: TerraDrawMouseEvent) {}
/** @internal */
onDrag(event: TerraDrawMouseEvent) {}
/** @internal */
onDragEnd(event: TerraDrawMouseEvent) {}
/** @internal */
styleFeature(feature: GeoJSONStoreFeatures): TerraDrawAdapterStyling {}
```
--------------------------------
### Enable Editable Features in Polygon Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/4.MODES.md
Configure Terra Draw modes to support editable geometries. This allows users to make light edits, such as dragging coordinates, without switching to a separate select mode.
```typescript
new TerraDrawPolygonMode({ editable: true }),
```
--------------------------------
### Filter Features by Mode
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Filter the features obtained from `getSnapshot` to retrieve specific types, such as only polygon features. This allows for targeted data manipulation or display.
```javascript
features.filter((feature) => feature.properties.mode === 'polygon')
```
--------------------------------
### Custom Integer ID Strategy for Terra Draw
Source: https://github.com/jameslmilner/terra-draw/blob/main/guides/2.STORE.md
Define a custom ID strategy to generate incrementing integer IDs for features. Ensure your strategy includes a `isValidId` function to validate IDs and a `getId` function to generate them.
```typescript
const draw = new TerraDraw({
adapter,
modes,
idStrategy: {
isValidId: (id) => typeof id === "number" && Number.isInteger(id),
getId: (function () {
let id = 0;
return function () {
return ++id;
};
})() // Returns a function that returns incrementing integer ids
},
});
```