@defra/interactive-map 0.0.31-alpha → 0.0.32-alpha

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/README.md +2 -2
  2. package/assets/images/bright-ofm-thumb.jpg +0 -0
  3. package/assets/images/dark-ofm-thumb.jpg +0 -0
  4. package/assets/images/road-ofm-thumb.jpg +0 -0
  5. package/assets/templates/add-polygons.njk +143 -0
  6. package/assets/templates/add-symbols.njk +119 -0
  7. package/assets/templates/{map.njk → basic-map.njk} +9 -17
  8. package/assets/templates/button-map.njk +56 -0
  9. package/assets/templates/draw-tools.njk +161 -0
  10. package/assets/templates/marker-panel.njk +91 -0
  11. package/assets/templates/place-marker.njk +104 -0
  12. package/assets/templates/search-control.njk +98 -0
  13. package/assets/templates/select-feature.njk +101 -0
  14. package/assets/templates/style-switcher.njk +87 -0
  15. package/assets/templates/toggle-marker-label.njk +74 -0
  16. package/dist/esm/im-core.js +1 -1
  17. package/dist/esm/im-shell.js +1 -1
  18. package/dist/umd/im-core.js +1 -1
  19. package/dist/umd/index.js +1 -1
  20. package/docs/api.md +55 -0
  21. package/docs/assets/css/docusaurus.scss +22 -6
  22. package/docs/assets/images/add-polygons.jpg +0 -0
  23. package/docs/assets/images/add-symbols.jpg +0 -0
  24. package/docs/assets/images/place-marker.jpg +0 -0
  25. package/docs/assets/images/screenshot.jpg +0 -0
  26. package/docs/examples/add-marker-with-panel.mdx +1 -1
  27. package/docs/examples/add-polygons.mdx +225 -0
  28. package/docs/examples/add-symbols.mdx +177 -0
  29. package/docs/examples/{draw.mdx → draw-tools.mdx} +2 -2
  30. package/docs/examples/index.mdx +38 -2
  31. package/docs/examples/place-marker.mdx +151 -0
  32. package/docs/plugins/datasets.md +144 -96
  33. package/docs/plugins/interact.md +1 -4
  34. package/docusaurus.config.cjs +5 -2
  35. package/govuk-prototype-kit.config.json +52 -2
  36. package/package.json +1 -1
  37. package/plugins/beta/datasets/dist/css/index.css +1 -2
  38. package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -1
  39. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  40. package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +1 -1
  41. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +1 -1
  42. package/plugins/beta/datasets/dist/umd/index.js +1 -1
  43. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +27 -9
  44. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +46 -4
  45. package/plugins/beta/datasets/src/api/setFeatureVisibility.js +10 -1
  46. package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +48 -6
  47. package/plugins/beta/datasets/src/components/LayersMenu/Layers.module.scss +12 -12
  48. package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +11 -6
  49. package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +49 -0
  50. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +8 -1
  51. package/plugins/beta/datasets/src/reducers/pluginState.js +1 -1
  52. package/plugins/beta/datasets/src/registry/dataset.js +7 -3
  53. package/plugins/beta/datasets/src/registry/dataset.test.js +14 -0
  54. package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +1 -1
  55. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  56. package/plugins/beta/draw-es/src/api/addFeature.js +12 -3
  57. package/plugins/beta/draw-es/src/events.js +8 -3
  58. package/plugins/beta/draw-es/src/events.test.js +2 -0
  59. package/plugins/beta/draw-es/src/graphic.js +26 -2
  60. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  61. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  62. package/plugins/beta/draw-ml/src/DrawInit.jsx +8 -0
  63. package/plugins/beta/draw-ml/src/api/newLine.js +1 -0
  64. package/plugins/beta/draw-ml/src/api/newPolygon.js +1 -0
  65. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +21 -12
  66. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +4 -0
  67. package/plugins/beta/draw-ol/dist/esm/EditMode.js +1 -1
  68. package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
  69. package/plugins/beta/draw-ol/src/edit/keyboardHandler.js +4 -2
  70. package/plugins/beta/map-styles/src/mapStyles.scss +41 -41
  71. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  72. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  73. package/plugins/interact/dist/umd/index.js +1 -1
  74. package/plugins/interact/src/InteractInit.jsx +1 -3
  75. package/plugins/interact/src/InteractInit.test.js +0 -2
  76. package/plugins/interact/src/events.test.js +1 -1
  77. package/plugins/interact/src/hooks/useCrossHairVisibility.js +4 -10
  78. package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +5 -10
  79. package/plugins/interact/src/hooks/useHighlightSync.js +1 -10
  80. package/plugins/interact/src/hooks/useHighlightSync.test.js +2 -21
  81. package/plugins/interact/src/hooks/useInteractionHandlers.js +5 -11
  82. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +5 -25
  83. package/plugins/interact/src/reducer.js +4 -22
  84. package/plugins/interact/src/reducer.test.js +11 -39
  85. package/plugins/search/dist/css/index.css +5 -2
  86. package/plugins/search/src/components/Form/Form.module.scss +5 -2
  87. package/plugins/search/src/search.scss +4 -4
  88. package/src/App/components/CrossHair/CrossHair.module.scss +8 -8
  89. package/src/App/hooks/useLayoutMeasurements.js +10 -2
  90. package/src/App/hooks/useResizeObserver.js +4 -2
  91. package/src/InteractiveMap/InteractiveMap.js +4 -4
  92. package/src/InteractiveMap/InteractiveMap.test.js +3 -3
  93. package/src/InteractiveMap/behaviourController.js +3 -1
  94. package/src/InteractiveMap/behaviourController.test.js +6 -3
  95. package/src/InteractiveMap/domStateManager.js +2 -0
  96. package/src/config/events.js +21 -3
  97. package/docs/assets/images/screens-blue.jpg +0 -0
  98. package/docs/assets/images/screens-white.jpg +0 -0
@@ -1,32 +1,74 @@
1
1
  import { setFeatureVisibility } from './setFeatureVisibility.js'
2
+ import { datasetRegistry } from '../registry/datasetRegistry.js'
3
+ import { logger } from '../../../../../src/services/logger.js'
4
+
5
+ jest.mock('../registry/datasetRegistry.js', () => ({
6
+ datasetRegistry: { getDataset: jest.fn() }
7
+ }))
8
+
9
+ jest.mock('../../../../../src/services/logger.js', () => ({
10
+ logger: { warn: jest.fn() }
11
+ }))
2
12
 
3
13
  describe('setFeatureVisibility', () => {
4
14
  const dispatch = jest.fn()
5
15
  const featureIds = [1, 2, 3]
16
+ const datasetId = 'my-dataset'
17
+
18
+ beforeEach(() => {
19
+ jest.clearAllMocks()
20
+ datasetRegistry.getDataset.mockReturnValue({ idProperty: 'id', generateIds: false })
21
+ })
22
+
6
23
  it('dispatches SHOW_FEATURES when visible is true', () => {
7
- setFeatureVisibility({ pluginState: { dispatch } }, true, featureIds, { datasetId: 'my-dataset' })
24
+ setFeatureVisibility({ pluginState: { dispatch } }, true, featureIds, { datasetId })
8
25
 
9
26
  expect(dispatch).toHaveBeenCalledWith({
10
27
  type: 'SHOW_FEATURES',
11
- payload: { datasetId: 'my-dataset', featureIds }
28
+ payload: { datasetId, featureIds }
12
29
  })
13
30
  })
14
31
 
15
32
  it('dispatches HIDE_FEATURES when visible is false', () => {
16
- setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId: 'my-dataset' })
33
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId })
17
34
 
18
35
  expect(dispatch).toHaveBeenCalledWith({
19
36
  type: 'HIDE_FEATURES',
20
- payload: { datasetId: 'my-dataset', featureIds }
37
+ payload: { datasetId, featureIds }
21
38
  })
22
39
  })
23
40
 
24
- it('uses undefined datasetId when options are not provided', () => {
41
+ it('uses null datasetId when options are not provided', () => {
25
42
  setFeatureVisibility({ pluginState: { dispatch } }, true, featureIds)
26
43
 
27
44
  expect(dispatch).toHaveBeenCalledWith({
28
45
  type: 'SHOW_FEATURES',
29
- payload: { datasetId: undefined, featureIds }
46
+ payload: { datasetId: null, featureIds }
30
47
  })
31
48
  })
49
+
50
+ it('does not warn when dataset has idProperty set', () => {
51
+ datasetRegistry.getDataset.mockReturnValue({ idProperty: 'parcel_id', generateIds: false })
52
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId })
53
+ expect(logger.warn).not.toHaveBeenCalled()
54
+ })
55
+
56
+ it('does not warn when dataset has generateIds set', () => {
57
+ datasetRegistry.getDataset.mockReturnValue({ idProperty: null, generateIds: true })
58
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId })
59
+ expect(logger.warn).not.toHaveBeenCalled()
60
+ })
61
+
62
+ it('warns when dataset has neither idProperty nor generateIds', () => {
63
+ datasetRegistry.getDataset.mockReturnValue({ idProperty: null, generateIds: false })
64
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds, { datasetId })
65
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining(`"${datasetId}"`))
66
+ expect(logger.warn).toHaveBeenCalledWith(expect.stringContaining('idProperty'))
67
+ })
68
+
69
+ it('does not warn or look up registry when datasetId is not provided', () => {
70
+ setFeatureVisibility({ pluginState: { dispatch } }, false, featureIds)
71
+ expect(datasetRegistry.getDataset).not.toHaveBeenCalled()
72
+ expect(logger.warn).not.toHaveBeenCalled()
73
+ })
32
74
  })
@@ -35,19 +35,19 @@
35
35
  margin-bottom: 0;
36
36
  }
37
37
  }
38
- }
39
-
40
- .im-c-datasets-layers__item-label {
41
- width: 100%;
42
- max-width: none;
43
- align-items: start;
44
- padding-top: 12px;
45
- padding-bottom: 10px;
46
- align-self: auto;
47
- font-size: 1rem;
48
- line-height: 1.2;
49
38
 
50
- color: var(--foreground-color);
39
+ .im-c-datasets-layers__item-label {
40
+ width: 100%;
41
+ max-width: none;
42
+ align-items: start;
43
+ padding-top: 12px;
44
+ padding-bottom: 10px;
45
+ align-self: auto;
46
+ font-size: 1rem;
47
+ line-height: 1.2;
48
+
49
+ color: var(--foreground-color);
50
+ }
51
51
  }
52
52
 
53
53
  // GovUK style overide
@@ -1,20 +1,25 @@
1
1
  export const datasetsToMenu = (state) => {
2
2
  const { datasets = [] } = state
3
3
  const menu = []
4
- const datasetsForMenu = datasets.filter(dataset => dataset.showInMenu)
4
+ const datasetsForMenu = datasets.filter(dataset =>
5
+ dataset.showInMenu || dataset.sublayers?.some(s => s.showInMenu)
6
+ )
5
7
  const groups = new Map()
6
8
  datasetsForMenu.forEach(dataset => {
7
9
  if (dataset.sublayers?.length) {
10
+ const visibleSublayers = dataset.sublayers.filter(sublayer =>
11
+ dataset.showInMenu ? sublayer.showInMenu !== false : sublayer.showInMenu
12
+ )
13
+ if (!visibleSublayers.length) { return }
8
14
  menu.push({
9
15
  id: dataset.id,
10
16
  groupLabel: dataset.label,
11
17
  visibleWhen: true,
12
18
  type: 'checkbox',
13
- items: dataset.sublayers.filter(sublayer => sublayer.showInMenu)
14
- .map(sublayer => ({
15
- id: `${dataset.id}-${sublayer.id}`,
16
- label: sublayer.label
17
- }))
19
+ items: visibleSublayers.map(sublayer => ({
20
+ id: `${dataset.id}-${sublayer.id}`,
21
+ label: sublayer.label
22
+ }))
18
23
  })
19
24
  } else if (dataset.groupLabel) {
20
25
  // Check for existing group object for this groupLabel, or create it if it doesn't exist,
@@ -32,4 +32,53 @@ describe('datasetsToMenu', () => {
32
32
  .toEqual(expectedDatasetsMenuConfigWithGroups)
33
33
  })
34
34
  })
35
+
36
+ describe('showInMenu inheritance for sublayers', () => {
37
+ const sublayerDataset = (datasetShowInMenu, sublayerShowInMenu) => ({
38
+ id: 'test',
39
+ label: 'Test',
40
+ showInMenu: datasetShowInMenu,
41
+ sublayers: [
42
+ { id: 'a', label: 'A', ...(sublayerShowInMenu !== undefined && { showInMenu: sublayerShowInMenu }) },
43
+ { id: 'b', label: 'B', ...(sublayerShowInMenu !== undefined && { showInMenu: sublayerShowInMenu }) }
44
+ ]
45
+ })
46
+
47
+ it('sublayers inherit showInMenu: true from the dataset when not explicitly set', () => {
48
+ const result = datasetsToMenu({ datasets: [sublayerDataset(true, undefined)] })
49
+ expect(result[0].items).toHaveLength(2)
50
+ })
51
+
52
+ it('sublayer can opt out with showInMenu: false when dataset has showInMenu: true', () => {
53
+ const dataset = {
54
+ id: 'test',
55
+ label: 'Test',
56
+ showInMenu: true,
57
+ sublayers: [
58
+ { id: 'a', label: 'A', showInMenu: false },
59
+ { id: 'b', label: 'B' }
60
+ ]
61
+ }
62
+ const result = datasetsToMenu({ datasets: [dataset] })
63
+ expect(result[0].items).toEqual([{ id: 'test-b', label: 'B' }])
64
+ })
65
+
66
+ it('sublayers with showInMenu: true appear when dataset does not have showInMenu set', () => {
67
+ const dataset = {
68
+ id: 'test',
69
+ label: 'Test',
70
+ sublayers: [
71
+ { id: 'a', label: 'A', showInMenu: true },
72
+ { id: 'b', label: 'B' }
73
+ ]
74
+ }
75
+ const result = datasetsToMenu({ datasets: [dataset] })
76
+ expect(result[0].items).toEqual([{ id: 'test-a', label: 'A' }])
77
+ })
78
+
79
+ it('does not add a menu entry when dataset has showInMenu: true but all sublayers opt out', () => {
80
+ const result = datasetsToMenu({ datasets: [sublayerDataset(true, false)] })
81
+ expect(result).toHaveLength(0)
82
+ })
83
+ })
35
84
  })
@@ -11,7 +11,14 @@ const flattenSublayer = (parentId, sublayer) => {
11
11
 
12
12
  const reduceDatasets = (acc, dataset) => {
13
13
  const { id } = dataset
14
- acc[id] = applyDatasetDefaultsWithoutFlattening(dataset)
14
+ const processed = applyDatasetDefaultsWithoutFlattening(dataset)
15
+ // For sublayers, don't let defaults override the absence of showInKey/showInMenu —
16
+ // the Dataset class inherits these from the parent when not explicitly set by the user
17
+ if (dataset.parentId) {
18
+ if (!('showInKey' in dataset)) { delete processed.showInKey }
19
+ if (!('showInMenu' in dataset)) { delete processed.showInMenu }
20
+ }
21
+ acc[id] = processed
15
22
  const { orderedDatasets } = acc
16
23
  orderedDatasets.push(id)
17
24
  const flattenedSublayers = dataset.sublayers?.map((sublayer) => flattenSublayer(id, sublayer))
@@ -116,7 +116,7 @@ const hideFeatures = (state, payload) => {
116
116
  }
117
117
  const mappedDataset = { ...state.mappedDatasets[datasetId] }
118
118
  const existingIds = mappedDataset.hiddenFeatures || []
119
- const newIds = [...new Set([...existingIds, ...featureIds])]
119
+ const newIds = Array.from(new Set([...existingIds, ...featureIds]))
120
120
  mappedDataset.hiddenFeatures = newIds
121
121
 
122
122
  return addAction('applyFeatureFilter', [datasetId], {
@@ -18,12 +18,16 @@ export class Dataset {
18
18
  get tiles () { return this._datasetDefinition.tiles }
19
19
  get geojson () { return this._datasetDefinition.geojson }
20
20
  get idProperty () { return this._datasetDefinition.idProperty }
21
- // TODO - handle transformRequest for non-dynamicGeoJSON as well (e.g. to add auth headers) --- IGNORE ---
22
- // get transformRequest () { return this._datasetDefinition.transformRequest }
21
+ get generateIds () { return this._datasetDefinition.generateIds }
23
22
  get parentId () { return this._datasetDefinition.parentId }
24
23
  get minZoom () { return this._datasetDefinition.minZoom || this.parent?.minZoom }
25
24
  get maxZoom () { return this._datasetDefinition.maxZoom || this.parent?.maxZoom }
26
- get showInKey () { return this._datasetDefinition.showInKey || this.parent?.showInKey || false }
25
+ get showInKey () {
26
+ const own = this._datasetDefinition.showInKey
27
+ if (own !== undefined) { return own }
28
+ return this.parent?.showInKey ?? false
29
+ }
30
+
27
31
  get groupLabel () { return this._datasetDefinition.groupLabel }
28
32
 
29
33
  get opacity () {
@@ -201,6 +201,20 @@ describe('Dataset class', () => {
201
201
  datasetRegistry.attach({ parent: parentDef, child: childDef })
202
202
  expect(new Dataset(childDef).showInKey).toBe(false)
203
203
  })
204
+
205
+ it('allows a sublayer to opt out with showInKey: false even when parent has showInKey: true', () => {
206
+ const parentDef = { id: 'parent', showInKey: true, style: {} }
207
+ const childDef = { id: 'child', parentId: 'parent', showInKey: false, style: {} }
208
+ datasetRegistry.attach({ parent: parentDef, child: childDef })
209
+ expect(new Dataset(childDef).showInKey).toBe(false)
210
+ })
211
+
212
+ it('allows a sublayer to opt in with showInKey: true even when parent has showInKey: false', () => {
213
+ const parentDef = { id: 'parent', showInKey: false, style: {} }
214
+ const childDef = { id: 'child', parentId: 'parent', showInKey: true, style: {} }
215
+ datasetRegistry.attach({ parent: parentDef, child: childDef })
216
+ expect(new Dataset(childDef).showInKey).toBe(true)
217
+ })
204
218
  })
205
219
 
206
220
  describe('tiles, geojson, idProperty, parentId', () => {
@@ -11,7 +11,7 @@ export class DynamicGeoJson {
11
11
  this.source = {
12
12
  type: 'geojson',
13
13
  data: { type: 'FeatureCollection', features: [] },
14
- generateId: true
14
+ ...(this.idProperty ? { promoteId: this.idProperty } : { generateId: true })
15
15
  }
16
16
  }
17
17
  }
@@ -1 +1 @@
1
- import e from"@babel/runtime/helpers/defineProperty";import{useEffect as t}from"preact/compat";import r from"@arcgis/core/widgets/Sketch/SketchViewModel.js";import a from"@arcgis/core/layers/GraphicsLayer.js";import o from"@babel/runtime/helpers/asyncToGenerator";import*as n from"@arcgis/core/geometry/operators/simplifyOperator.js";import i from"@arcgis/core/Graphic.js";function p(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function l(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?p(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):p(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}var c={APP_ADD_MARKER:"app:addmarker",APP_UPDATE_MARKER:"app:updatemarker",APP_REMOVE_MARKER:"app:removemarker",APP_SET_MODE:"app:setmode",APP_REVERT_MODE:"app:revertmode",APP_ADD_BUTTON:"app:addbutton",APP_TOGGLE_BUTTON_STATE:"app:togglebuttonstate",APP_ADD_PANEL:"app:addpanel",APP_REMOVE_PANEL:"app:removepanel",APP_SHOW_PANEL:"app:showpanel",APP_HIDE_PANEL:"app:hidepanel",APP_ADD_CONTROL:"app:addcontrol",APP_READY:"app:ready",APP_OPENED:"app:opened",APP_CLOSED:"app:closed",APP_PANEL_OPENED:"app:panelopened",APP_PANEL_CLOSED:"app:panelclosed",MAP_SET_STYLE:"map:setstyle",MAP_SET_SIZE:"map:setsize",MAP_SET_FEATURES:"map:setfeatures",MAP_SET_ACTIVE_FEATURE:"map:setactivefeature",MAP_SELECT_FEATURE:"map:selectfeature",MAP_SET_PIXEL_RATIO:"map:setpixelratio",MAP_FIT_TO_BOUNDS:"map:fittobounds",MAP_SET_VIEW:"map:setview",MAP_INIT_MAP_STYLES:"map:initmapstyles",MAP_STYLE_CHANGE:"map:stylechange",MAP_LOADED:"map:loaded",MAP_READY:"map:ready",MAP_SIZE_CHANGE:"map:sizechange",MAP_FIRST_IDLE:"map:firstidle",MAP_MOVE_START:"map:movestart",MAP_MOVE:"map:move",MAP_MOVE_END:"map:moveend",MAP_STATE_UPDATED:"map:stateupdated",MAP_DATA_CHANGE:"map:datachange",MAP_RENDER:"map:render",MAP_CLICK:"map:click",MAP_DESTROY:"map:destroy"};function s(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function d(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?s(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):s(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}function u(e){return{type:"simple-fill",color:[0,120,255,.2],outline:{color:"dark"===e?"#ffffff":"#d4351c",width:2}}}function m(e,t,r){return new i({geometry:{type:"polygon",rings:t,spatialReference:27700},attributes:{id:e},symbol:u(r)})}function y(e){if(null==e||!e.geometry)throw new Error("Invalid graphic");var{geometry:t,attributes:r={}}=e;switch(t.type){case"point":return{type:"Feature",geometry:{type:"Point",coordinates:[t.x,t.y]},properties:d({},r)};case"polyline":return{type:"Feature",geometry:{type:"LineString",coordinates:t.paths[0]},properties:d({},r)};case"polygon":return{type:"Feature",geometry:{type:"Polygon",coordinates:t.rings},properties:d({},r)};default:throw new Error("Unsupported geometry type: ".concat(t.type))}}function v(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function E(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?v(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):v(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}var f={mobile:{slot:"actions",showLabel:!0},tablet:{slot:"actions",showLabel:!0},desktop:{slot:"actions",showLabel:!0}},P={reducer:{initialState:{mode:null,feature:null,tempFeature:null},actions:{SET_MODE:(e,t)=>l(l({},e),{},{mode:t}),SET_FEATURE:(e,t)=>l(l({},e),{},{feature:void 0===t.feature?e.feature:t.feature,tempFeature:void 0===t.tempFeature?e.tempFeature:t.tempFeature})}},InitComponent:e=>{var i,p,l,s,{appState:d,mapState:v,pluginConfig:E,pluginState:f,services:P,mapProvider:h,buttonConfig:g}=e,{eventBus:_}=P,{mapColorScheme:O}=v.mapStyle||{},A=null===(i=null===(p=E.includeModes)||void 0===p?void 0:p.includes(d.mode))||void 0===i||i,S=null!==(l=null===(s=E.excludeModes)||void 0===s?void 0:s.includes(d.mode))&&void 0!==l&&l,b=v.isMapReady&&A&&!S;t(()=>{if(b&&!h.sketchViewModel){var{sketchViewModel:e,sketchLayer:t,emptySketchLayer:o}=(e=>{var{mapProvider:t}=e,{view:o}=t,n=new a({id:"sketchLayer"});o.map.add(n);var i=new a({id:"emptySketchLayer"});return o.map.add(i),{sketchViewModel:new r({view:o,layer:i,defaultUpdateOptions:{tool:"reshape",updateOnGraphicClick:!1,multipleSelectionEnabled:!1,toggleToolOnClick:!1,highlightOptions:{enabled:!1}}}),emptySketchLayer:i,sketchLayer:n}})({mapProvider:h});return h.sketchViewModel=e,h.sketchLayer=t,h.emptySketchLayer=o,_.emit("draw:ready"),()=>{h.sketchViewModel=null,h.sketchLayer=null,h.emptySketchLayer=null}}},[v.isMapReady,d.mode]),t(()=>{if(b&&h.sketchViewModel){var e=function(e){var{pluginState:t,mapProvider:r,events:a,eventBus:i,buttonConfig:p,mapColorScheme:l}=e,{view:c,sketchViewModel:s,sketchLayer:d,emptySketchLayer:v}=r;if(!s)return null;var{drawDone:E,drawCancel:f}=p,{dispatch:P,mode:h,feature:g}=t,_=function(){var e=o(function*(){var e,r=null===(e=t.feature)||void 0===e||null===(e=e.properties)||void 0===e?void 0:e.id,a=null,o="active"===s.state&&!r;if("active"===s.state&&r&&(s.cancel(),yield new Promise(e=>setTimeout(e,50))),s.polygonSymbol=u(l),null==d||d.graphics.items.forEach(e=>{var t=m(e.attributes.id,e.geometry.rings,l);e.symbol=t.symbol,r===e.attributes.id&&(a=e)}),a&&!o&&s.layer===d)try{yield s.update(a,{tool:"reshape",toggleToolOnClick:!1})}catch(e){"AbortError"!==e.name&&console.error("Error updating sketch:",e)}});return function(){return e.apply(this,arguments)}}(),O=()=>{var e,t,r=null!==(e=null===(t=d.graphics)||void 0===t||null===(t=t.items)||void 0===t?void 0:t[0])&&void 0!==e?e:null;r&&setTimeout(()=>s.update(r),0)},A=()=>_(),S=e=>{if(e){var t=y(e);i.emit("draw:updated",t),P({type:"SET_FEATURE",payload:{tempFeature:t}})}},b=function(){var e=o(function*(){h&&((s.updateGraphics||[]).length||O())});return function(){return e.apply(this,arguments)}}(),T=function(){var e=o(function*(e){var{toolEventInfo:t}=e,r=null==e?void 0:e.graphic;if(r&&"vertex-add"===(null==t?void 0:t.type)){var a,o,n=null===(a=r.geometry)||void 0===a?void 0:a.rings;(null==n?void 0:n.length)>1?setTimeout(()=>s.undo(),0):(null==n||null===(o=n[0])||void 0===o?void 0:o.length)>3&&S(r)}});return function(t){return e.apply(this,arguments)}}(),w=function(){var e=o(function*(e){var t,r=null==e||null===(t=e.graphics)||void 0===t?void 0:t[0];S(r)});return function(t){return e.apply(this,arguments)}}();i.on(a.MAP_STYLE_CHANGE,A);var M=s.on("update",e=>{var t,r=null===(t=e.toolEventInfo)||void 0===t?void 0:t.type,a=e.graphics[0];"move-start"===r&&(s.cancel(),O()),"reshape"===r&&(n.isSimple(a.geometry)||s.undo()),"reshape-stop"!==r&&"vertex-remove"!==r||S(a)}),k=c.on("click",b),D=s.on("create",T),L=s.on("undo",w),j=E.onClick,C=f.onClick;return E.onClick=()=>{s.cancel(),s.layer=v,P({type:"SET_MODE",payload:null}),P({type:"SET_FEATURE",payload:{feature:null,tempFeature:null}}),i.emit("draw:done",{newFeature:t.tempFeature})},f.onClick=()=>{if(s.cancel(),d.removeAll(),g){var e=m(g.id||g.properties.id,g.geometry.coordinates,l);d.add(e)}s.layer=v,P({type:"SET_MODE",payload:null}),i.emit("draw:cancelled")},()=>{i.off(a.MAP_STYLE_CHANGE,A),M.remove(),k.remove(),D.remove(),L.remove(),E.onClick=j,f.onClick=C}}({pluginState:f,mapProvider:h,events:c,eventBus:_,buttonConfig:g,mapColorScheme:O});return()=>{e()}}},[b,O,f])},buttons:[E({id:"drawDone",label:"Done",variant:"primary",hiddenWhen:e=>{var{pluginState:t}=e;return!t.mode},enableWhen:e=>{var{pluginState:t}=e;return!!t.tempFeature}},f),E({id:"drawCancel",label:"Cancel",variant:"tertiary",hiddenWhen:e=>{var{pluginState:t}=e;return!t.mode}},f)],api:{newPolygon:(e,t)=>{var{mapState:r,pluginState:a,mapProvider:o,services:n}=e,{dispatch:i}=a,{sketchViewModel:p,sketchLayer:l}=o,{eventBus:c}=n;p.layer=l;var s=p.on("create",e=>{if("complete"===e.state){e.graphic.attributes={id:t},requestAnimationFrame(()=>{p.update(e.graphic,{tool:"reshape",toggleToolOnClick:!1})});var r=y(e.graphic);c.emit("draw:created",r),i({type:"SET_FEATURE",payload:{tempFeature:r}}),s.remove()}});p.polygonSymbol=u(r.mapStyle.mapColorScheme),p.create("polygon"),i({type:"SET_MODE",payload:"new-polygon"})},editFeature:(e,t)=>{var{pluginState:r,mapProvider:a}=e,{dispatch:o}=r,{sketchViewModel:n,sketchLayer:i}=a,p=i.graphics.items.find(e=>e.attributes.id===t),l=p.geometry.extent,c=[l.xmin,l.ymin,l.xmax,l.ymax];a.fitToBounds(c),n.layer=i,n.update(p,{tool:"reshape",toggleToolOnClick:!1,enableRotation:!1,enableScaling:!1}),o({type:"SET_FEATURE",payload:{feature:y(p)}}),o({type:"SET_MODE",payload:"edit-feature"})},addFeature:(e,t)=>{var{pluginState:r,mapState:a,mapProvider:o,services:n}=e,{dispatch:i}=r,{mapStyle:p}=a,{sketchViewModel:l,sketchLayer:c,emptySketchLayer:s}=o,{eventBus:d}=n,u=m(t.id,t.geometry.coordinates,p.mapColorScheme);c.add(u),l.layer=s,i({type:"SET_FEATURE",payload:{feature:t}}),d.emit("draw:add",t)},deleteFeature:(e,t)=>{var{pluginState:r,mapProvider:a,services:o}=e,{dispatch:n}=r,{sketchViewModel:i,sketchLayer:p,emptySketchLayer:l}=a,{eventBus:c}=o,s=p.graphics.items.find(e=>e.attributes.id===t);i.cancel(),p.remove(s),i.layer=l,n({type:"SET_FEATURE",payload:{feature:null,tempFeature:null}}),c.emit("draw:delete",{featureId:t}),n({type:"SET_MODE",payload:null})}}};export{P as manifest};
1
+ import e from"@babel/runtime/helpers/defineProperty";import{useEffect as t}from"preact/compat";import r from"@arcgis/core/widgets/Sketch/SketchViewModel.js";import a from"@arcgis/core/layers/GraphicsLayer.js";import o from"@babel/runtime/helpers/asyncToGenerator";import*as n from"@arcgis/core/geometry/operators/simplifyOperator.js";import i from"@arcgis/core/Graphic.js";function p(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function l(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?p(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):p(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}var c={APP_ADD_MARKER:"app:addmarker",APP_UPDATE_MARKER:"app:updatemarker",APP_REMOVE_MARKER:"app:removemarker",APP_SET_MODE:"app:setmode",APP_REVERT_MODE:"app:revertmode",APP_ADD_BUTTON:"app:addbutton",APP_TOGGLE_BUTTON_STATE:"app:togglebuttonstate",APP_ADD_PANEL:"app:addpanel",APP_REMOVE_PANEL:"app:removepanel",APP_SHOW_PANEL:"app:showpanel",APP_HIDE_PANEL:"app:hidepanel",APP_ADD_CONTROL:"app:addcontrol",APP_READY:"app:ready",APP_OPENED:"app:opened",APP_CLOSED:"app:closed",APP_FULLSCREEN_CHANGE:"app:fullscreenchange",APP_PANEL_OPENED:"app:panelopened",APP_PANEL_CLOSED:"app:panelclosed",MAP_SET_STYLE:"map:setstyle",MAP_SET_SIZE:"map:setsize",MAP_SET_FEATURES:"map:setfeatures",MAP_SET_ACTIVE_FEATURE:"map:setactivefeature",MAP_SELECT_FEATURE:"map:selectfeature",MAP_SET_PIXEL_RATIO:"map:setpixelratio",MAP_FIT_TO_BOUNDS:"map:fittobounds",MAP_SET_VIEW:"map:setview",MAP_INIT_MAP_STYLES:"map:initmapstyles",MAP_STYLE_CHANGE:"map:stylechange",MAP_LOADED:"map:loaded",MAP_READY:"map:ready",MAP_SIZE_CHANGE:"map:sizechange",MAP_FIRST_IDLE:"map:firstidle",MAP_MOVE_START:"map:movestart",MAP_MOVE:"map:move",MAP_MOVE_END:"map:moveend",MAP_STATE_UPDATED:"map:stateupdated",MAP_DATA_CHANGE:"map:datachange",MAP_RENDER:"map:render",MAP_CLICK:"map:click",MAP_DESTROY:"map:destroy"},s="[interactive-map]",u=function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return console.warn(s,...t)},d=function(){for(var e=arguments.length,t=new Array(e),r=0;r<e;r++)t[r]=arguments[r];return console.error(s,...t)};function m(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function y(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?m(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):m(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}function v(e){return{type:"simple-fill",color:[0,120,255,.2],outline:{color:"dark"===e?"#ffffff":"#d4351c",width:2}}}function f(e,t,r){var a=arguments.length>3&&void 0!==arguments[3]?arguments[3]:{},{simplify:o=!0,allowHoles:p=!1}=a,l=new i({geometry:{type:"polygon",rings:t,spatialReference:27700},attributes:{id:e},symbol:v(r)});return o||!1===p?((e,t)=>{var{simplify:r=!0,allowHoles:a=!1}=t;return!r||n.isSimple(e.geometry)||(u("Feature geometry is not simple, attempting to simplify"),e.geometry=n.execute(e.geometry),n.isSimple(e.geometry))?!1===a&&(null==e?void 0:e.geometry.rings.length)>1?(d("Feature contains multiple polygons, which are not supported."),null):e:(d("Failed to simplify geometry, cannot add feature"),null)})(l,a):l}function E(e){if(null==e||!e.geometry)throw new Error("Invalid graphic");var{geometry:t,attributes:r={}}=e;switch(t.type){case"point":return{type:"Feature",geometry:{type:"Point",coordinates:[t.x,t.y]},properties:y({},r)};case"polyline":return{type:"Feature",geometry:{type:"LineString",coordinates:t.paths[0]},properties:y({},r)};case"polygon":return{type:"Feature",geometry:{type:"Polygon",coordinates:t.rings},properties:y({},r)};default:throw new Error("Unsupported geometry type: ".concat(t.type))}}function g(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var a=Object.getOwnPropertySymbols(e);t&&(a=a.filter(function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable})),r.push.apply(r,a)}return r}function h(t){for(var r=1;r<arguments.length;r++){var a=null!=arguments[r]?arguments[r]:{};r%2?g(Object(a),!0).forEach(function(r){e(t,r,a[r])}):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(a)):g(Object(a)).forEach(function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(a,e))})}return t}var P={mobile:{slot:"actions",showLabel:!0},tablet:{slot:"actions",showLabel:!0},desktop:{slot:"actions",showLabel:!0}},_={reducer:{initialState:{mode:null,feature:null,tempFeature:null},actions:{SET_MODE:(e,t)=>l(l({},e),{},{mode:t}),SET_FEATURE:(e,t)=>l(l({},e),{},{feature:void 0===t.feature?e.feature:t.feature,tempFeature:void 0===t.tempFeature?e.tempFeature:t.tempFeature})}},InitComponent:e=>{var i,p,l,s,{appState:u,mapState:d,pluginConfig:m,pluginState:y,services:g,mapProvider:h,buttonConfig:P}=e,{eventBus:_}=g,{mapColorScheme:A}=d.mapStyle||{},O=null===(i=null===(p=m.includeModes)||void 0===p?void 0:p.includes(u.mode))||void 0===i||i,S=null!==(l=null===(s=m.excludeModes)||void 0===s?void 0:s.includes(u.mode))&&void 0!==l&&l,b=d.isMapReady&&O&&!S;t(()=>{if(b&&!h.sketchViewModel){var{sketchViewModel:e,sketchLayer:t,emptySketchLayer:o}=(e=>{var{mapProvider:t}=e,{view:o}=t,n=new a({id:"sketchLayer"});o.map.add(n);var i=new a({id:"emptySketchLayer"});return o.map.add(i),{sketchViewModel:new r({view:o,layer:i,defaultUpdateOptions:{tool:"reshape",updateOnGraphicClick:!1,multipleSelectionEnabled:!1,toggleToolOnClick:!1,highlightOptions:{enabled:!1}}}),emptySketchLayer:i,sketchLayer:n}})({mapProvider:h});return h.sketchViewModel=e,h.sketchLayer=t,h.emptySketchLayer=o,_.emit("draw:ready"),()=>{h.sketchViewModel=null,h.sketchLayer=null,h.emptySketchLayer=null}}},[d.isMapReady,u.mode]),t(()=>{if(b&&h.sketchViewModel){var e=function(e){var{pluginState:t,mapProvider:r,events:a,eventBus:i,buttonConfig:p,mapColorScheme:l}=e,{view:c,sketchViewModel:s,sketchLayer:u,emptySketchLayer:d}=r;if(!s)return null;var{drawDone:m,drawCancel:y}=p,{dispatch:g,mode:h,feature:P}=t,_=function(){var e=o(function*(){var e,r=null===(e=t.feature)||void 0===e||null===(e=e.properties)||void 0===e?void 0:e.id,a=null,o="active"===s.state&&!r;if("active"===s.state&&r&&(s.cancel(),yield new Promise(e=>setTimeout(e,50))),s.polygonSymbol=v(l),null==u||u.graphics.items.forEach(e=>{var t=f(e.attributes.id,e.geometry.rings,l);t&&(e.symbol=t.symbol),r===e.attributes.id&&(a=e)}),a&&!o&&s.layer===u)try{yield s.update(a,{tool:"reshape",toggleToolOnClick:!1})}catch(e){"AbortError"!==e.name&&console.error("Error updating sketch:",e)}});return function(){return e.apply(this,arguments)}}(),A=()=>{var e,t,r=null!==(e=null===(t=u.graphics)||void 0===t||null===(t=t.items)||void 0===t?void 0:t[0])&&void 0!==e?e:null;r&&setTimeout(()=>s.update(r),0)},O=()=>_(),S=e=>{if(e){var t=E(e);i.emit("draw:updated",t),g({type:"SET_FEATURE",payload:{tempFeature:t}})}},b=function(){var e=o(function*(){h&&((s.updateGraphics||[]).length||A())});return function(){return e.apply(this,arguments)}}(),w=function(){var e=o(function*(e){var{toolEventInfo:t}=e,r=null==e?void 0:e.graphic;if(r&&"vertex-add"===(null==t?void 0:t.type)){var a,o,n=null===(a=r.geometry)||void 0===a?void 0:a.rings;(null==n?void 0:n.length)>1?setTimeout(()=>s.undo(),0):(null==n||null===(o=n[0])||void 0===o?void 0:o.length)>3&&S(r)}});return function(t){return e.apply(this,arguments)}}(),T=function(){var e=o(function*(e){var t,r=null==e||null===(t=e.graphics)||void 0===t?void 0:t[0];S(r)});return function(t){return e.apply(this,arguments)}}();i.on(a.MAP_STYLE_CHANGE,O);var M=s.on("update",e=>{var t,r=null===(t=e.toolEventInfo)||void 0===t?void 0:t.type,a=e.graphics[0];"move-start"===r&&(s.cancel(),A()),"reshape"===r&&(n.isSimple(a.geometry)&&1===a.geometry.rings.length||s.undo()),"reshape-stop"!==r&&"vertex-remove"!==r||S(a)}),k=c.on("click",b),D=s.on("create",w),L=s.on("undo",T),C=m.onClick,j=y.onClick;return m.onClick=()=>{s.cancel(),s.layer=d,g({type:"SET_MODE",payload:null}),g({type:"SET_FEATURE",payload:{feature:null,tempFeature:null}}),i.emit("draw:done",{newFeature:t.tempFeature})},y.onClick=()=>{if(s.cancel(),u.removeAll(),P){var e=f(P.id||P.properties.id,P.geometry.coordinates,l);e&&u.add(e)}s.layer=d,g({type:"SET_MODE",payload:null}),i.emit("draw:cancelled")},()=>{i.off(a.MAP_STYLE_CHANGE,O),M.remove(),k.remove(),D.remove(),L.remove(),m.onClick=C,y.onClick=j}}({pluginState:y,mapProvider:h,events:c,eventBus:_,buttonConfig:P,mapColorScheme:A});return()=>{e()}}},[b,A,y])},buttons:[h({id:"drawDone",label:"Done",variant:"primary",hiddenWhen:e=>{var{pluginState:t}=e;return!t.mode},enableWhen:e=>{var{pluginState:t}=e;return!!t.tempFeature}},P),h({id:"drawCancel",label:"Cancel",variant:"tertiary",hiddenWhen:e=>{var{pluginState:t}=e;return!t.mode}},P)],api:{newPolygon:(e,t)=>{var{mapState:r,pluginState:a,mapProvider:o,services:n}=e,{dispatch:i}=a,{sketchViewModel:p,sketchLayer:l}=o,{eventBus:c}=n;p.layer=l;var s=p.on("create",e=>{if("complete"===e.state){e.graphic.attributes={id:t},requestAnimationFrame(()=>{p.update(e.graphic,{tool:"reshape",toggleToolOnClick:!1})});var r=E(e.graphic);c.emit("draw:created",r),i({type:"SET_FEATURE",payload:{tempFeature:r}}),s.remove()}});p.polygonSymbol=v(r.mapStyle.mapColorScheme),p.create("polygon"),i({type:"SET_MODE",payload:"new-polygon"})},editFeature:(e,t)=>{var{pluginState:r,mapProvider:a}=e,{dispatch:o}=r,{sketchViewModel:n,sketchLayer:i}=a,p=i.graphics.items.find(e=>e.attributes.id===t),l=p.geometry.extent,c=[l.xmin,l.ymin,l.xmax,l.ymax];a.fitToBounds(c),n.layer=i,n.update(p,{tool:"reshape",toggleToolOnClick:!1,enableRotation:!1,enableScaling:!1}),o({type:"SET_FEATURE",payload:{feature:E(p)}}),o({type:"SET_MODE",payload:"edit-feature"})},addFeature:function(e,t){var{pluginState:r,mapState:a,mapProvider:o,services:n}=e,i=arguments.length>2&&void 0!==arguments[2]?arguments[2]:{},{dispatch:p}=r,{mapStyle:l}=a,{sketchViewModel:c,sketchLayer:s,emptySketchLayer:u}=o,{eventBus:d}=n,m=f(t.id,t.geometry.coordinates,l.mapColorScheme,i),y={success:!0};return m?(s.add(m),c.layer=u,p({type:"SET_FEATURE",payload:{feature:t}}),d.emit("draw:add",t),y):(y.success=!1,y)},deleteFeature:(e,t)=>{var{pluginState:r,mapProvider:a,services:o}=e,{dispatch:n}=r,{sketchViewModel:i,sketchLayer:p,emptySketchLayer:l}=a,{eventBus:c}=o,s=p.graphics.items.find(e=>e.attributes.id===t);i.cancel(),p.remove(s),i.layer=l,n({type:"SET_FEATURE",payload:{feature:null,tempFeature:null}}),c.emit("draw:delete",{featureId:t}),n({type:"SET_MODE",payload:null})}}};export{_ as manifest};
@@ -1,13 +1,21 @@
1
1
  import { createGraphic } from '../graphic.js'
2
2
 
3
- export const addFeature = ({ pluginState, mapState, mapProvider, services }, feature) => {
3
+ export const addFeature = ({ pluginState, mapState, mapProvider, services }, feature, simplifyOptions = {}) => {
4
4
  const { dispatch } = pluginState
5
5
  const { mapStyle } = mapState
6
6
  const { sketchViewModel, sketchLayer, emptySketchLayer } = mapProvider
7
7
  const { eventBus } = services
8
8
 
9
- const graphic = createGraphic(feature.id, feature.geometry.coordinates, mapStyle.mapColorScheme)
10
-
9
+ const graphic = createGraphic(feature.id, feature.geometry.coordinates, mapStyle.mapColorScheme, simplifyOptions)
10
+ const result = {
11
+ success: true
12
+ }
13
+ if (!graphic) {
14
+ result.success = false
15
+ // TODO - enhance the results with the specific reason the feature could not be added,
16
+ // e.g. invalid geometry, unsupported geometry type, etc.
17
+ return result
18
+ }
11
19
  // Add the graphic to the layer
12
20
  sketchLayer.add(graphic)
13
21
 
@@ -18,4 +26,5 @@ export const addFeature = ({ pluginState, mapState, mapProvider, services }, fea
18
26
  dispatch({ type: 'SET_FEATURE', payload: { feature } })
19
27
 
20
28
  eventBus.emit('draw:add', feature)
29
+ return result
21
30
  }
@@ -35,7 +35,9 @@ export function attachEvents ({ pluginState, mapProvider, events, eventBus, butt
35
35
  graphic.geometry.rings,
36
36
  mapColorScheme
37
37
  )
38
- graphic.symbol = newGraphic.symbol
38
+ if (newGraphic) {
39
+ graphic.symbol = newGraphic.symbol
40
+ }
39
41
 
40
42
  if (activeGraphicId === graphic.attributes.id) {
41
43
  activeGraphic = graphic
@@ -88,8 +90,9 @@ export function attachEvents ({ pluginState, mapProvider, events, eventBus, butt
88
90
  }
89
91
 
90
92
  // Prevent self-intersect
93
+ // TODO - refactor this when rings and/or multipolygons are supported
91
94
  if (toolInfoType === 'reshape') {
92
- const isSimple = simplifyOperator.isSimple(graphic.geometry)
95
+ const isSimple = simplifyOperator.isSimple(graphic.geometry) && graphic.geometry.rings.length === 1
93
96
  if (!isSimple) {
94
97
  sketchViewModel.undo()
95
98
  }
@@ -157,7 +160,9 @@ export function attachEvents ({ pluginState, mapProvider, events, eventBus, butt
157
160
  feature.geometry.coordinates,
158
161
  mapColorScheme
159
162
  )
160
- sketchLayer.add(graphic)
163
+ if (graphic) {
164
+ sketchLayer.add(graphic)
165
+ }
161
166
  }
162
167
 
163
168
  // Prevent selection
@@ -5,6 +5,8 @@ import * as graphicJs from './graphic.js'
5
5
  jest.mock('./graphic.js')
6
6
  const createGraphic = jest.spyOn(graphicJs, 'createGraphic')
7
7
  const createSymbol = jest.spyOn(graphicJs, 'createSymbol')
8
+ const simplifyGraphic = jest.spyOn(graphicJs, 'createGraphic')
9
+ simplifyGraphic.mockImplementation((graphic, options) => graphic)
8
10
  // const graphicToGeoJSON = jest.spyOn(graphicJs, 'graphicToGeoJSON')
9
11
 
10
12
  const dispatch = jest.fn()
@@ -1,4 +1,6 @@
1
1
  import Graphic from '@arcgis/core/Graphic.js'
2
+ import * as simplifyOperator from '@arcgis/core/geometry/operators/simplifyOperator.js'
3
+ import { logger } from '../../../../src/services/logger.js'
2
4
 
3
5
  function createSymbol (mapColorScheme) {
4
6
  return {
@@ -11,8 +13,26 @@ function createSymbol (mapColorScheme) {
11
13
  }
12
14
  }
13
15
 
14
- function createGraphic (id, coordinates, mapColorScheme) {
15
- return new Graphic({
16
+ // TODO - add support for multipolygons and polygons with holes
17
+ const simplifyGraphic = (graphic, { simplify = true, allowHoles = false }) => {
18
+ if (simplify && !simplifyOperator.isSimple(graphic.geometry)) {
19
+ logger.warn('Feature geometry is not simple, attempting to simplify')
20
+ graphic.geometry = simplifyOperator.execute(graphic.geometry)
21
+ if (!simplifyOperator.isSimple(graphic.geometry)) {
22
+ logger.error('Failed to simplify geometry, cannot add feature')
23
+ return null
24
+ }
25
+ }
26
+ if (allowHoles === false && graphic?.geometry.rings.length > 1) {
27
+ logger.error('Feature contains multiple polygons, which are not supported.')
28
+ return null
29
+ }
30
+ return graphic
31
+ }
32
+
33
+ function createGraphic (id, coordinates, mapColorScheme, simplifyOptions = {}) {
34
+ const { simplify = true, allowHoles = false } = simplifyOptions
35
+ const graphic = new Graphic({
16
36
  geometry: {
17
37
  type: 'polygon',
18
38
  rings: coordinates,
@@ -23,6 +43,10 @@ function createGraphic (id, coordinates, mapColorScheme) {
23
43
  },
24
44
  symbol: createSymbol(mapColorScheme)
25
45
  })
46
+ if (simplify || allowHoles === false) {
47
+ return simplifyGraphic(graphic, simplifyOptions)
48
+ }
49
+ return graphic
26
50
  }
27
51
 
28
52
  function graphicToGeoJSON (graphic) {