Skip to main content

alphaThis is a new frontend component. Help us improve it and give your feedback on Slack.

Draw tools

Draw, edit, select and delete polygons and lines using the draw and interact plugins. A single "Draw tools" menu button groups all drawing actions together. The menu items update their enabled state based on whether a drawn feature is selected.

NOTE

The draw plugin (draw-ml) is currently in beta.

The map requires JavaScript to be enabled.
import InteractiveMap from '@defra/interactive-map'
import maplibreProvider from '@defra/interactive-map/providers/maplibre'
import createInteractPlugin from '@defra/interactive-map/plugins/interact'
import createDrawPlugin from '@defra/interactive-map/plugins/draw-ml'
const DRAW_LAYERS = ['fill-inactive.cold', 'stroke-inactive.cold']
const interactPlugin = createInteractPlugin({
layers: [
{ layerId: 'fill-inactive.cold', idProperty: 'id' },
{ layerId: 'stroke-inactive.cold', idProperty: 'id' }
],
interactionModes: ['selectFeature'],
multiSelect: true,
deselectOnClickOutside: true
})
const drawPlugin = createDrawPlugin()
const interactiveMap = new InteractiveMap('my-map', {
behaviour: 'inline',
mapProvider: maplibreProvider(),
mapStyle: {
url: 'https://your-tile-url/style.json',
attribution: 'Your tile attribution'
},
center: [-0.1276, 51.5074],
zoom: 12,
containerHeight: '516px',
plugins: [interactPlugin, drawPlugin]
})
let selectedFeatureIds = []
interactiveMap.on('map:ready', () => {
interactPlugin.enable()
interactiveMap.addButton('drawTools', {
label: 'Draw tools',
mobile: { slot: 'bottom-right' },
tablet: { slot: 'top-middle' },
desktop: { slot: 'top-middle' },
menuItems: [
{
id: 'drawPolygon',
label: 'Draw polygon',
iconSvgContent: '<path d="M19.5 7v10M4.5 7v10M7 19.5h10M7 4.5h10"/><path d="M22 18v3a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zm0-15v3a1 1 0 0 1-1 1h-3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zM7 18v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1v-3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1zM7 3v3a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h3a1 1 0 0 1 1 1z"/>',
onClick: () => {
interactiveMap.toggleButtonState('drawTools', 'hidden', true)
drawPlugin.newPolygon(crypto.randomUUID())
}
},
{
id: 'drawLine',
label: 'Draw line',
iconSvgContent: '<path d="M5.706 16.294L16.294 5.706"/><path d="M21 2v3c0 .549-.451 1-1 1h-3c-.549 0-1-.451-1-1V2c0-.549.451-1 1-1h3c.549 0 1 .451 1 1zM6 17v3c0 .549-.451 1-1 1H2c-.549 0-1-.451-1-1v-3c0-.549.451-1 1-1h3c.549 0 1 .451 1 1z"/>',
onClick: () => {
interactiveMap.toggleButtonState('drawTools', 'hidden', true)
drawPlugin.newLine(crypto.randomUUID())
}
},
{
id: 'editFeature',
label: 'Edit feature',
iconSvgContent: '<path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z"/><path d="m15 5 4 4"/>',
isDisabled: true,
onClick: () => {
if (!drawPlugin.editFeature(selectedFeatureIds[0])) return
interactiveMap.toggleButtonState('drawTools', 'hidden', true)
interactPlugin.disable()
}
},
{
id: 'deleteFeature',
label: 'Delete feature',
iconSvgContent: '<path d="M10 11v6"/><path d="M14 11v6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6"/><path d="M3 6h18"/><path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/>',
isDisabled: true,
onClick: () => {
drawPlugin.deleteFeature(selectedFeatureIds)
interactPlugin.clear()
interactiveMap.toggleButtonState('drawTools', 'hidden', false)
interactiveMap.toggleButtonState('drawPolygon', 'disabled', false)
interactiveMap.toggleButtonState('drawLine', 'disabled', false)
interactiveMap.toggleButtonState('editFeature', 'disabled', true)
interactiveMap.toggleButtonState('deleteFeature', 'disabled', true)
}
}
]
})
})
interactiveMap.on('draw:started', () => {
interactPlugin.disable()
})
interactiveMap.on('draw:created', () => {
interactiveMap.toggleButtonState('drawTools', 'hidden', false)
interactPlugin.enable()
})
interactiveMap.on('draw:edited', () => {
interactiveMap.toggleButtonState('drawTools', 'hidden', false)
interactPlugin.enable()
})
interactiveMap.on('draw:cancelled', () => {
interactiveMap.toggleButtonState('drawTools', 'hidden', false)
interactPlugin.enable()
})
interactiveMap.on('interact:selectionchange', (e) => {
const singleFeature = e.selectedFeatures.length === 1
const anyFeature = e.selectedFeatures.length > 0
const isDrawFeature = singleFeature && DRAW_LAYERS.includes(e.selectedFeatures[0].layerId)
const allDrawFeatures = anyFeature && e.selectedFeatures.every(f => DRAW_LAYERS.includes(f.layerId))
selectedFeatureIds = e.selectedFeatures.map(f => f.featureId)
interactiveMap.toggleButtonState('drawPolygon', 'disabled', singleFeature)
interactiveMap.toggleButtonState('drawLine', 'disabled', singleFeature)
interactiveMap.toggleButtonState('editFeature', 'disabled', !isDrawFeature)
interactiveMap.toggleButtonState('deleteFeature', 'disabled', !allDrawFeatures)
})