Draw
Draw, select and edit points, lines and polygons, built on terra-draw.
Installation
#ng-map-draw lives in the optional ng-mapcn/draw entry point, so apps that do not draw never load terra-draw. Install it and its MapLibre adapter, then import from ng-mapcn/draw.
# terra-draw and its MapLibre adapter are optional peer dependencies
npm i terra-draw terra-draw-maplibre-gl-adapter
# Copy-paste distribution: copies the draw sources to <path>/draw and adds the dependencies
ng add ng-mapcn --drawBasic Example
# Pick a tool and click the map. Lines and polygons finish by clicking the last vertex again (or Enter); Escape cancels. In select mode drag a shape, drag its vertices, or press Delete to remove it.
import { Component, signal, viewChild } from '@angular/core';
import { MapComponent } from 'ng-mapcn';
import { MapDrawComponent, type MapDrawFeatureCollection, type MapDrawMode } from 'ng-mapcn/draw';
@Component({
selector: 'app-draw-map',
standalone: true,
imports: [MapComponent, MapDrawComponent],
template: `
<button (click)="mode.set('point')">Point</button>
<button (click)="mode.set('line')">Line</button>
<button (click)="mode.set('polygon')">Polygon</button>
<button (click)="mode.set('select')">Select</button>
<button (click)="mode.set('static')">Pan</button>
<button (click)="draw().clear()">Clear</button>
<p>{{ features().features.length }} features</p>
<div class="h-[420px]">
<ng-map [center]="[-46.6333, -23.5505]" [zoom]="11">
<ng-map-draw [(mode)]="mode" [(features)]="features" />
</ng-map>
</div>
`,
})
export class DrawMapComponent {
readonly draw = viewChild.required(MapDrawComponent);
readonly mode = signal<MapDrawMode>('static');
readonly features = signal<MapDrawFeatureCollection>({ type: 'FeatureCollection', features: [] });
}Preloaded features and events
#features is a two-way GeoJSON FeatureCollection. This geofence editor starts from one polygon in select mode; edits flow back into the model and out through featureEdit.
Last event: none (1 zones)
import { Component, signal } from '@angular/core';
import { MapComponent } from 'ng-mapcn';
import { MapDrawComponent, type MapDrawFeatureCollection, type MapDrawEvent } from 'ng-mapcn/draw';
@Component({
selector: 'app-geofence-map',
standalone: true,
imports: [MapComponent, MapDrawComponent],
template: `
<ng-map [center]="[-46.6333, -23.5505]" [zoom]="12">
<ng-map-draw
mode="select"
[modes]="['polygon']"
[(features)]="zones"
(drawEnd)="last.set($event)"
(featureEdit)="last.set($event)"
/>
</ng-map>
`,
})
export class GeofenceMapComponent {
readonly last = signal<MapDrawEvent | null>(null);
// Features without an id get one; assigning a new collection replaces what is drawn.
readonly zones = signal<MapDrawFeatureCollection>({
type: 'FeatureCollection',
features: [
{
type: 'Feature',
properties: { name: 'Depot' },
geometry: {
type: 'Polygon',
coordinates: [[[-46.66, -23.54], [-46.62, -23.54], [-46.62, -23.56], [-46.66, -23.56], [-46.66, -23.54]]],
},
},
],
});
}Features model
#features is a GeoJSON FeatureCollection of points, lines and polygons, so it plugs straight into ng-map-geojson or your backend.
readonly features = signal<MapDrawFeatureCollection>({ type: 'FeatureCollection', features: [] });
// <ng-map-draw [(features)]="features" />
//
// - It updates when a draw, edit or delete completes (not on every mouse move).
// - Assigning a collection diffs by feature id: new ids are added, missing ones removed,
// changed geometry/properties updated in place. Assigning the same data is a no-op.
// - Features without an id get one; unsupported geometries (MultiPolygon, ...) are dropped and
// reported through (mapError) on <ng-map>.
// - properties.mode is the shape ('point' | 'line' | 'polygon'); other properties are yours.Modes and editing
#modes lists the shapes the user can draw and mode is the active tool (two-way). With editable (the default) the select mode drags shapes, reshapes lines and polygons by their vertices, inserts vertices from midpoints and deletes vertices.
<!-- Only polygons can be drawn; features stay selectable and editable -->
<ng-map-draw [modes]="['polygon']" [(mode)]="mode" [(features)]="features" />
<!-- Display only: no select mode, no editing -->
<ng-map-draw [editable]="false" [(features)]="features" />
<!-- mode: 'point' | 'line' | 'polygon' | 'select' | 'static' (default). A mode that is not
enabled falls back to 'static'. -->Events
#// <ng-map-draw
// (drawEnd)="onDrawEnd($event)" // a new shape was completed
// (featureEdit)="onEdit($event)" // moved, reshaped, vertex added/removed
// (featureSelect)="onSelect($event)" // select mode: the Feature
// (featureDeselect)="onDeselect($event)" // select mode: the feature id
// />
onDrawEnd(event: MapDrawEvent) {
// { feature, mode: 'polygon', action: 'draw' }
}Theme and style changes
# The palette follows the resolved map theme. A theme switch replaces the map style, which removes terra-draw's layers, so the component stops terra-draw and starts it again on the new style with the same features and mode. The same restart happens when modes or editable change; a shape that is still being drawn is lost. Changing color restyles in place.
<!-- Accent color of the drawn shapes (#rrggbb); defaults to a theme-aware blue -->
<ng-map-draw color="#16a34a" [(features)]="features" />
import { buildDrawStyles, defaultDrawPalettes } from 'ng-mapcn/draw';
// buildDrawStyles('dark') -> the terra-draw styles used for each mode