@defra/interactive-map 0.0.30-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 (247) 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/css/index.css +1 -1
  17. package/dist/esm/im-core.js +1 -1
  18. package/dist/esm/im-shell.js +1 -1
  19. package/dist/umd/im-core.js +1 -1
  20. package/dist/umd/index.js +1 -1
  21. package/docs/api/context.md +4 -4
  22. package/docs/api.md +166 -37
  23. package/docs/assets/css/docusaurus.scss +36 -4
  24. package/docs/assets/images/add-polygons.jpg +0 -0
  25. package/docs/assets/images/add-symbols.jpg +0 -0
  26. package/docs/assets/images/basic-map.jpg +0 -0
  27. package/docs/assets/images/button-first.jpg +0 -0
  28. package/docs/assets/images/deuteranopia-ozs-map-thumb.jpg +0 -0
  29. package/docs/assets/images/draw.jpg +0 -0
  30. package/docs/assets/images/marker-panel.jpg +0 -0
  31. package/docs/assets/images/night-ozs-map-thumb.jpg +0 -0
  32. package/docs/assets/images/outdoor-ozs-map-thumb.jpg +0 -0
  33. package/docs/assets/images/place-marker.jpg +0 -0
  34. package/docs/assets/images/screenshot.jpg +0 -0
  35. package/docs/assets/images/search.jpg +0 -0
  36. package/docs/assets/images/select-feature.jpg +0 -0
  37. package/docs/assets/images/style-switcher.jpg +0 -0
  38. package/docs/assets/images/toggle-marker-label.jpg +0 -0
  39. package/docs/assets/images/tritanopia-ozs-map-thumb.jpg +0 -0
  40. package/docs/examples/add-marker-with-panel.mdx +3 -3
  41. package/docs/examples/add-polygons.mdx +225 -0
  42. package/docs/examples/add-symbols.mdx +177 -0
  43. package/docs/examples/basic-map.mdx +2 -2
  44. package/docs/examples/draw-tools.mdx +271 -0
  45. package/docs/examples/index.mdx +95 -11
  46. package/docs/examples/place-marker.mdx +151 -0
  47. package/docs/examples/search-control.mdx +71 -0
  48. package/docs/examples/select-feature.mdx +146 -0
  49. package/docs/examples/style-switcher.mdx +135 -0
  50. package/docs/examples/toggle-marker-label.mdx +3 -3
  51. package/docs/plugins/datasets.md +157 -122
  52. package/docs/plugins/interact.md +1 -4
  53. package/docusaurus.config.cjs +9 -2
  54. package/govuk-prototype-kit.config.json +52 -2
  55. package/package.json +1 -1
  56. package/plugins/beta/datasets/dist/css/index.css +5 -2
  57. package/plugins/beta/datasets/dist/esm/im-datasets-ml-adapter.js +1 -0
  58. package/plugins/beta/datasets/dist/esm/im-datasets-plugin.js +1 -1
  59. package/plugins/beta/datasets/dist/esm/index.js +1 -1
  60. package/plugins/beta/datasets/dist/umd/im-datasets-ml-adapter.js +2 -0
  61. package/plugins/beta/datasets/dist/umd/im-datasets-plugin.js +2 -0
  62. package/plugins/beta/datasets/dist/umd/index.js +2 -0
  63. package/plugins/beta/datasets/src/adapters/loadLayerAdapter.js +33 -0
  64. package/plugins/beta/datasets/src/adapters/maplibre/layerBuilders.js +44 -120
  65. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.js +145 -245
  66. package/plugins/beta/datasets/src/adapters/maplibre/maplibreLayerAdapter.test.js +516 -0
  67. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.js +209 -0
  68. package/plugins/beta/datasets/src/adapters/maplibre/registry/mapLibreDataset.test.js +483 -0
  69. package/plugins/beta/datasets/src/api/addDataset.js +2 -5
  70. package/plugins/beta/datasets/src/api/addDataset.test.js +16 -0
  71. package/plugins/beta/datasets/src/api/getOpacity.js +14 -14
  72. package/plugins/beta/datasets/src/api/getOpacity.test.js +72 -0
  73. package/plugins/beta/datasets/src/api/getStyle.js +8 -9
  74. package/plugins/beta/datasets/src/api/getStyle.test.js +42 -0
  75. package/plugins/beta/datasets/src/api/removeDataset.js +7 -7
  76. package/plugins/beta/datasets/src/api/removeDataset.test.js +45 -0
  77. package/plugins/beta/datasets/src/api/setData.js +9 -3
  78. package/plugins/beta/datasets/src/api/setData.test.js +62 -0
  79. package/plugins/beta/datasets/src/api/setDatasetVisibility.js +5 -30
  80. package/plugins/beta/datasets/src/api/setDatasetVisibility.test.js +31 -0
  81. package/plugins/beta/datasets/src/api/setFeatureVisibility.js +9 -18
  82. package/plugins/beta/datasets/src/api/setFeatureVisibility.test.js +74 -0
  83. package/plugins/beta/datasets/src/api/setGlobals.js +20 -0
  84. package/plugins/beta/datasets/src/api/setGlobals.test.js +63 -0
  85. package/plugins/beta/datasets/src/api/setOpacity.js +8 -27
  86. package/plugins/beta/datasets/src/api/setOpacity.test.js +31 -0
  87. package/plugins/beta/datasets/src/api/setStyle.js +5 -21
  88. package/plugins/beta/datasets/src/api/setStyle.test.js +53 -0
  89. package/plugins/beta/datasets/src/components/Key/Key.jsx +51 -0
  90. package/plugins/beta/datasets/src/components/Key/KeyGroupItem.jsx +18 -0
  91. package/plugins/beta/datasets/src/components/Key/KeyItem.jsx +20 -0
  92. package/plugins/beta/datasets/src/components/Key/KeySvg.jsx +22 -0
  93. package/plugins/beta/datasets/src/components/Key/KeySvg.test.jsx +87 -0
  94. package/plugins/beta/datasets/src/components/Key/KeySvgLine.jsx +19 -0
  95. package/plugins/beta/datasets/src/components/Key/KeySvgLine.test.jsx +71 -0
  96. package/plugins/beta/datasets/src/components/{KeySvgPattern.jsx → Key/KeySvgPattern.jsx} +3 -4
  97. package/plugins/beta/datasets/src/components/Key/KeySvgPattern.test.jsx +55 -0
  98. package/plugins/beta/datasets/src/components/Key/KeySvgRect.jsx +22 -0
  99. package/plugins/beta/datasets/src/components/Key/KeySvgRect.test.jsx +85 -0
  100. package/plugins/beta/datasets/src/components/{KeySvgSymbol.jsx → Key/KeySvgSymbol.jsx} +5 -5
  101. package/plugins/beta/datasets/src/components/Key/KeySvgSymbol.test.jsx +80 -0
  102. package/plugins/beta/datasets/src/components/Key/svgProperties.test.js +61 -0
  103. package/plugins/beta/datasets/src/{panels → components/LayersMenu}/Layers.module.scss +15 -11
  104. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenu.jsx +33 -0
  105. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuCheckbox.jsx +31 -0
  106. package/plugins/beta/datasets/src/components/LayersMenu/LayersMenuGroupWrapper.jsx +19 -0
  107. package/plugins/beta/datasets/src/datasets.scss +2 -2
  108. package/plugins/beta/datasets/src/fetch/createDynamicSource.js +8 -8
  109. package/plugins/beta/datasets/src/{DatasetsInit.jsx → initialise/DatasetsInit.jsx} +19 -21
  110. package/plugins/beta/datasets/src/{defaults.js → initialise/defaults.js} +17 -2
  111. package/plugins/beta/datasets/src/initialise/defaults.test.js +127 -0
  112. package/plugins/beta/datasets/src/{datasets.js → initialise/initialiseDatasets.js} +30 -26
  113. package/plugins/beta/datasets/src/initialise/initialiseDatasets.test.js +273 -0
  114. package/plugins/beta/datasets/src/manifest.js +8 -6
  115. package/plugins/beta/datasets/src/reducers/__data__/demoDatasets.js +253 -0
  116. package/plugins/beta/datasets/src/reducers/datasetsToMenu.js +83 -0
  117. package/plugins/beta/datasets/src/reducers/datasetsToMenu.test.js +84 -0
  118. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.js +41 -0
  119. package/plugins/beta/datasets/src/reducers/mappedDatasetsReducer.test.js +117 -0
  120. package/plugins/beta/datasets/src/reducers/pluginState.js +206 -0
  121. package/plugins/beta/datasets/src/reducers/pluginState.test.js +386 -0
  122. package/plugins/beta/datasets/src/registry/__mocks__/datasetRegistry.js +29 -0
  123. package/plugins/beta/datasets/src/registry/createDataset.js +5 -0
  124. package/plugins/beta/datasets/src/registry/dataset.js +176 -0
  125. package/plugins/beta/datasets/src/registry/dataset.test.js +497 -0
  126. package/plugins/beta/datasets/src/registry/datasetDefinitionCache.js +35 -0
  127. package/plugins/beta/datasets/src/registry/datasetRegistry.js +116 -0
  128. package/plugins/beta/datasets/src/registry/datasetRegistry.test.js +335 -0
  129. package/plugins/beta/datasets/src/registry/dynamicGeoJson.js +17 -0
  130. package/plugins/beta/datasets/src/registry/globalDataset.js +36 -0
  131. package/plugins/beta/draw-es/dist/esm/im-draw-es-plugin.js +1 -1
  132. package/plugins/beta/draw-es/src/api/addFeature.js +12 -3
  133. package/plugins/beta/draw-es/src/events.js +8 -3
  134. package/plugins/beta/draw-es/src/events.test.js +2 -0
  135. package/plugins/beta/draw-es/src/graphic.js +26 -2
  136. package/plugins/beta/draw-ml/dist/esm/im-draw-ml-plugin.js +1 -1
  137. package/plugins/beta/draw-ml/dist/umd/im-draw-ml-plugin.js +1 -1
  138. package/plugins/beta/draw-ml/src/DrawInit.jsx +8 -0
  139. package/plugins/beta/draw-ml/src/api/newLine.js +1 -0
  140. package/plugins/beta/draw-ml/src/api/newPolygon.js +1 -0
  141. package/plugins/beta/draw-ml/src/events.js +13 -1
  142. package/plugins/beta/draw-ml/src/index.js +1 -1
  143. package/plugins/beta/draw-ml/src/manifest.js +3 -0
  144. package/plugins/beta/draw-ml/src/modes/createDrawMode.js +21 -12
  145. package/plugins/beta/draw-ml/src/modes/editVertexMode.js +4 -0
  146. package/plugins/beta/draw-ol/dist/esm/EditMode.js +1 -1
  147. package/plugins/beta/draw-ol/dist/esm/im-draw-ol-plugin.js +1 -1
  148. package/plugins/beta/draw-ol/src/edit/keyboardHandler.js +4 -2
  149. package/plugins/beta/map-styles/src/mapStyles.scss +41 -41
  150. package/plugins/interact/dist/esm/im-interact-plugin.js +1 -1
  151. package/plugins/interact/dist/umd/im-interact-plugin.js +1 -1
  152. package/plugins/interact/dist/umd/index.js +1 -1
  153. package/plugins/interact/src/InteractInit.jsx +3 -5
  154. package/plugins/interact/src/InteractInit.test.js +1 -3
  155. package/plugins/interact/src/events.js +2 -29
  156. package/plugins/interact/src/events.test.js +4 -102
  157. package/plugins/interact/src/hooks/useAttachEvents.js +3 -4
  158. package/plugins/interact/src/hooks/useAttachEvents.test.js +1 -3
  159. package/plugins/interact/src/hooks/useCrossHairVisibility.js +4 -10
  160. package/plugins/interact/src/hooks/useCrossHairVisibility.test.js +5 -10
  161. package/plugins/interact/src/hooks/useHighlightSync.js +1 -10
  162. package/plugins/interact/src/hooks/useHighlightSync.test.js +2 -21
  163. package/plugins/interact/src/hooks/useInteractionHandlers.js +15 -12
  164. package/plugins/interact/src/hooks/useInteractionHandlers.test.js +28 -29
  165. package/plugins/interact/src/manifest.js +0 -35
  166. package/plugins/interact/src/manifest.test.js +3 -43
  167. package/plugins/interact/src/reducer.js +4 -22
  168. package/plugins/interact/src/reducer.test.js +11 -39
  169. package/plugins/search/dist/css/index.css +5 -2
  170. package/plugins/search/src/components/Form/Form.module.scss +5 -2
  171. package/plugins/search/src/search.scss +4 -4
  172. package/providers/beta/esri/dist/esm/im-esri-provider.js +1 -1
  173. package/providers/beta/esri/src/esriProvider.js +4 -0
  174. package/providers/beta/openlayers/dist/esm/im-openlayers-provider.js +1 -1
  175. package/providers/beta/openlayers/dist/umd/im-openlayers-provider.js +1 -1
  176. package/providers/beta/openlayers/src/openlayersProvider.js +4 -0
  177. package/providers/beta/openlayers/src/openlayersProvider.test.js +5 -0
  178. package/providers/maplibre/dist/esm/im-maplibre-provider.js +1 -1
  179. package/providers/maplibre/dist/umd/im-maplibre-provider.js +1 -1
  180. package/providers/maplibre/src/maplibreProvider.js +6 -4
  181. package/providers/maplibre/src/maplibreProvider.test.js +5 -0
  182. package/providers/maplibre/src/utils/highlightFeatures.js +74 -25
  183. package/providers/maplibre/src/utils/highlightFeatures.test.js +132 -0
  184. package/providers/maplibre/src/utils/patternImages.js +2 -4
  185. package/providers/maplibre/src/utils/symbolImages.js +1 -4
  186. package/rollup.esm.mjs +2 -8
  187. package/src/App/components/CrossHair/CrossHair.module.scss +8 -8
  188. package/src/App/components/MapButton/MapButton.jsx +16 -1
  189. package/src/App/components/MapButton/MapButton.module.scss +12 -1
  190. package/src/App/components/MapButton/MapButton.test.jsx +40 -1
  191. package/src/App/hooks/useContinueEnabledEvaluator.js +31 -0
  192. package/src/App/hooks/useContinueEnabledEvaluator.test.js +118 -0
  193. package/src/App/hooks/useEvaluateProp.js +15 -15
  194. package/src/App/hooks/useEvaluateProp.test.js +80 -76
  195. package/src/App/hooks/useLayoutMeasurements.js +11 -5
  196. package/src/App/hooks/useResizeObserver.js +15 -2
  197. package/src/App/hooks/useResizeObserver.test.js +1 -0
  198. package/src/App/renderer/PluginInits.jsx +2 -0
  199. package/src/App/renderer/mapButtons.js +21 -1
  200. package/src/App/renderer/mapButtons.test.js +46 -1
  201. package/src/App/store/appReducer.js +1 -1
  202. package/src/App/store/appReducer.test.js +37 -0
  203. package/src/InteractiveMap/InteractiveMap.js +14 -4
  204. package/src/InteractiveMap/InteractiveMap.test.js +11 -3
  205. package/src/InteractiveMap/behaviourController.js +3 -1
  206. package/src/InteractiveMap/behaviourController.test.js +6 -3
  207. package/src/InteractiveMap/domStateManager.js +2 -0
  208. package/src/config/appConfig.js +25 -0
  209. package/src/config/appConfig.test.js +50 -0
  210. package/src/config/defaults.js +1 -0
  211. package/src/config/events.js +21 -3
  212. package/src/scss/settings/_dimensions.scss +1 -0
  213. package/src/services/patternRegistry.js +8 -4
  214. package/src/types.js +7 -0
  215. package/src/utils/patternUtils.js +1 -2
  216. package/src/utils/patternUtils.test.js +9 -0
  217. package/webpack.dev.mjs +10 -0
  218. package/webpack.umd.mjs +1 -1
  219. package/docs/assets/images/screens-blue.jpg +0 -0
  220. package/docs/assets/images/screens-white.jpg +0 -0
  221. package/plugins/beta/datasets/dist/adapters/maplibre/esm/im-datasets-ml-adapter.js +0 -1
  222. package/plugins/beta/datasets/dist/adapters/maplibre/esm/index.js +0 -1
  223. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-maplibre-adapter.js +0 -2
  224. package/plugins/beta/datasets/dist/umd/maplibre/im-datasets-plugin.js +0 -2
  225. package/plugins/beta/datasets/dist/umd/maplibre/index.js +0 -2
  226. package/plugins/beta/datasets/src/adapters/maplibre/index.js +0 -18
  227. package/plugins/beta/datasets/src/adapters/maplibre/layerIds.js +0 -75
  228. package/plugins/beta/datasets/src/adapters/maplibre/patternImages.js +0 -27
  229. package/plugins/beta/datasets/src/adapters/maplibre/symbolImages.js +0 -31
  230. package/plugins/beta/datasets/src/components/KeySvg.jsx +0 -24
  231. package/plugins/beta/datasets/src/components/KeySvgLine.jsx +0 -19
  232. package/plugins/beta/datasets/src/components/KeySvgRect.jsx +0 -22
  233. package/plugins/beta/datasets/src/index.maplibre.umd.js +0 -23
  234. package/plugins/beta/datasets/src/panels/Key.jsx +0 -62
  235. package/plugins/beta/datasets/src/panels/Layers.jsx +0 -141
  236. package/plugins/beta/datasets/src/reducer.js +0 -219
  237. package/plugins/beta/datasets/src/reducers/keyReducer.js +0 -34
  238. package/plugins/beta/datasets/src/utils/filters.js +0 -34
  239. package/plugins/beta/datasets/src/utils/mergeSublayer.js +0 -86
  240. package/plugins/beta/draw-ml/dist/css/index.css +0 -21
  241. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-maplibre-adapter.js.LICENSE.txt → im-datasets-ml-adapter.js.LICENSE.txt} +0 -0
  242. /package/plugins/beta/datasets/dist/umd/{maplibre/im-datasets-plugin.js.LICENSE.txt → im-datasets-plugin.js.LICENSE.txt} +0 -0
  243. /package/plugins/beta/datasets/dist/umd/{maplibre/index.js.LICENSE.txt → index.js.LICENSE.txt} +0 -0
  244. /package/plugins/beta/datasets/src/components/{EmptyKey.jsx → Key/EmptyKey.jsx} +0 -0
  245. /package/plugins/beta/datasets/src/components/{EmptyKey.test.jsx → Key/EmptyKey.test.jsx} +0 -0
  246. /package/plugins/beta/datasets/src/{panels → components/Key}/Key.module.scss +0 -0
  247. /package/plugins/beta/datasets/src/components/{svgProperties.js → Key/svgProperties.js} +0 -0
@@ -0,0 +1,335 @@
1
+ import { Dataset } from './dataset.js'
2
+ import { datasetRegistry } from './datasetRegistry.js'
3
+ import { mappedDatasetsReducer } from '../reducers/mappedDatasetsReducer.js'
4
+ import { datasetsWithGroups } from '../reducers/__data__/demoDatasets.js'
5
+ // Use the mock datasetRegistry with the demo datasets attached before each test.
6
+ // Dataset must be imported before datasetRegistry so that dataset.js is cached before
7
+ // the mock initialization triggers requireActual, avoiding a circular re-entry that
8
+ // would leave datasetRegistry undefined inside the mock.
9
+ jest.mock('./datasetRegistry.js')
10
+
11
+ describe('datasetRegistry', () => {
12
+ beforeEach(() => {
13
+ jest.clearAllMocks()
14
+ datasetRegistry._invalidateCache()
15
+ })
16
+ describe('attach', () => {
17
+ it("doesn't throw when no datasets are attached", () => {
18
+ expect(() => datasetRegistry.attach(undefined)).not.toThrow()
19
+ })
20
+ })
21
+
22
+ describe('getDataset', () => {
23
+ it('returns a Dataset instance for a known id', () => {
24
+ const dataset = datasetRegistry.getDataset('land-covers')
25
+ expect(dataset).toBeInstanceOf(Dataset)
26
+ })
27
+
28
+ it('returns the correct id', () => {
29
+ const dataset = datasetRegistry.getDataset('existing-fields')
30
+ expect(dataset.id).toBe('existing-fields')
31
+ })
32
+
33
+ it('returns a Dataset instance for a sublayer id', () => {
34
+ const dataset = datasetRegistry.getDataset('land-covers-130-131')
35
+ expect(dataset).toBeInstanceOf(Dataset)
36
+ expect(dataset.id).toBe('land-covers-130-131')
37
+ })
38
+
39
+ it('returns undefined for an unknown id', () => {
40
+ expect(datasetRegistry.getDataset('non-existent')).toBeUndefined()
41
+ })
42
+ })
43
+
44
+ describe('forEach', () => {
45
+ it('calls the callback once per dataset in orderedDatasets order', () => {
46
+ const ids = []
47
+ datasetRegistry.forEach(dataset => ids.push(dataset.id))
48
+ expect(ids).toEqual(datasetRegistry._orderedDatasets)
49
+ })
50
+
51
+ it('passes Dataset instances to the callback', () => {
52
+ datasetRegistry.forEach(dataset => expect(dataset).toBeInstanceOf(Dataset))
53
+ })
54
+ })
55
+
56
+ describe('forEachDataset', () => {
57
+ it('calls the callback only for top-level datasets', () => {
58
+ const ids = []
59
+ datasetRegistry.forEachDataset(dataset => ids.push(dataset.id))
60
+ expect(ids).toEqual(['land-covers', 'existing-fields', 'historic-monuments', 'hedge-control'])
61
+ })
62
+
63
+ it('does not include sublayers', () => {
64
+ const ids = []
65
+ datasetRegistry.forEachDataset(dataset => ids.push(dataset.id))
66
+ expect(ids).not.toContain('land-covers-130-131')
67
+ })
68
+
69
+ it('passes Dataset instances to the callback', () => {
70
+ datasetRegistry.forEachDataset(dataset => expect(dataset).toBeInstanceOf(Dataset))
71
+ })
72
+ })
73
+
74
+ describe('topLevelDatasets', () => {
75
+ it('returns only top-level dataset ids', () => {
76
+ const ids = datasetRegistry.topLevelDatasets().map(d => d.id)
77
+ expect(ids).toEqual(['land-covers', 'existing-fields', 'historic-monuments', 'hedge-control'])
78
+ })
79
+
80
+ it('does not include sublayers', () => {
81
+ const ids = datasetRegistry.topLevelDatasets().map(d => d.id)
82
+ expect(ids).not.toContain('land-covers-130-131')
83
+ })
84
+
85
+ it('returns Dataset instances', () => {
86
+ datasetRegistry.topLevelDatasets().forEach(d => expect(d).toBeInstanceOf(Dataset))
87
+ })
88
+ })
89
+
90
+ describe('getPatternAndSymbolConfigs', () => {
91
+ it('returns an object with patternConfigs and symbolConfigs arrays', () => {
92
+ const result = datasetRegistry.getPatternAndSymbolConfigs()
93
+ expect(Array.isArray(result.patternConfigs)).toBe(true)
94
+ expect(Array.isArray(result.symbolConfigs)).toBe(true)
95
+ })
96
+
97
+ it('collects fill pattern configs from sublayers', () => {
98
+ const { patternConfigs } = datasetRegistry.getPatternAndSymbolConfigs()
99
+ // land-covers sublayers each have a fillPattern style
100
+ expect(patternConfigs.length).toBeGreaterThan(0)
101
+ })
102
+
103
+ it('collects symbol configs from sublayers', () => {
104
+ const { symbolConfigs } = datasetRegistry.getPatternAndSymbolConfigs()
105
+ // historic-monuments sublayers inherit symbol: 'square' from their parent
106
+ expect(symbolConfigs.length).toBeGreaterThan(0)
107
+ })
108
+
109
+ it('returns empty arrays when no datasets have patterns or symbols', () => {
110
+ datasetRegistry.attach({ simple: { id: 'simple', style: { stroke: '#ff0000' } } })
111
+ const { patternConfigs, symbolConfigs } = datasetRegistry.getPatternAndSymbolConfigs()
112
+ expect(patternConfigs).toHaveLength(0)
113
+ expect(symbolConfigs).toHaveLength(0)
114
+ })
115
+ })
116
+
117
+ describe('keyItems', () => {
118
+ it('returns an object with an items array and hasGroups boolean', () => {
119
+ const { items, hasGroups } = datasetRegistry.keyItems()
120
+ expect(Array.isArray(items)).toBe(true)
121
+ expect(typeof hasGroups).toBe('boolean')
122
+ })
123
+
124
+ it('includes a sublayers entry for each dataset that has sublayers', () => {
125
+ const { items } = datasetRegistry.keyItems()
126
+ const sublayerItems = items.filter(item => item.type === 'sublayers')
127
+ expect(sublayerItems).toHaveLength(2) // land-covers and historic-monuments
128
+ })
129
+
130
+ it('includes a flat entry for datasets without sublayers and no groupLabel', () => {
131
+ const { items } = datasetRegistry.keyItems()
132
+ const flatItems = items.filter(item => item.type === 'flat')
133
+ expect(flatItems).toHaveLength(1) // existing-fields
134
+ })
135
+
136
+ it('sets hasGroups to true when any item has sublayers', () => {
137
+ const { hasGroups } = datasetRegistry.keyItems()
138
+ expect(hasGroups).toBe(true)
139
+ })
140
+
141
+ it('sets hasGroups to false when all items are flat with no groupLabel', () => {
142
+ datasetRegistry.attach({ simple: { id: 'simple', showInKey: true, visible: true, style: {} } })
143
+ const { hasGroups } = datasetRegistry.keyItems()
144
+ expect(hasGroups).toBe(false)
145
+ })
146
+
147
+ it('sets hasGroups to false when a dataset has only one sublayer and it is not visible', () => {
148
+ const { mappedDatasets: singleHiddenSublayer } = mappedDatasetsReducer({
149
+ datasets: [{
150
+ id: 'single-sublayer-dataset',
151
+ showInKey: true,
152
+ visible: true,
153
+ style: {},
154
+ sublayers: [{ id: 'sub', visible: false, style: {} }]
155
+ }]
156
+ })
157
+ datasetRegistry.attach(singleHiddenSublayer)
158
+ const { hasGroups } = datasetRegistry.keyItems()
159
+ expect(hasGroups).toBe(false)
160
+ })
161
+
162
+ it('only includes visible sublayers in sublayers items', () => {
163
+ const { items } = datasetRegistry.keyItems()
164
+ const landCoversItem = items.find(item => item.type === 'sublayers' && item.dataset.id === 'land-covers')
165
+ const sublayerIds = landCoversItem.sublayers.map(s => s.id)
166
+ expect(sublayerIds).not.toContain('land-covers-379') // land-covers-379 has visible: false
167
+ })
168
+
169
+ it('includes a group entry for datasets sharing a groupLabel', () => {
170
+ const { mappedDatasets: grouped } = mappedDatasetsReducer({ datasets: datasetsWithGroups })
171
+ datasetRegistry.attach(grouped)
172
+ const { items } = datasetRegistry.keyItems()
173
+ const groupItems = items.filter(item => item.type === 'group')
174
+ expect(groupItems).toHaveLength(1)
175
+ expect(groupItems[0].groupLabel).toBe('Test group')
176
+ })
177
+
178
+ it('only adds one group entry per unique groupLabel', () => {
179
+ const { mappedDatasets: grouped } = mappedDatasetsReducer({ datasets: datasetsWithGroups })
180
+ datasetRegistry.attach(grouped)
181
+ const { items } = datasetRegistry.keyItems()
182
+ const groupLabels = items.filter(i => i.type === 'group').map(i => i.groupLabel)
183
+ expect(groupLabels.length).toBe(new Set(groupLabels).size)
184
+ })
185
+
186
+ it('populates group item datasets with matching showInKey datasets', () => {
187
+ const { mappedDatasets: grouped } = mappedDatasetsReducer({ datasets: datasetsWithGroups })
188
+ datasetRegistry.attach(grouped)
189
+ const { items } = datasetRegistry.keyItems()
190
+ const groupItem = items.find(item => item.type === 'group' && item.groupLabel === 'Test group')
191
+ const ids = groupItem.datasets.map(d => d.id)
192
+ expect(ids).toContain('existing-fields')
193
+ expect(ids).toContain('hedge-control')
194
+ })
195
+
196
+ it('caches the result when datasets ref has not changed', () => {
197
+ const result1 = datasetRegistry.keyItems()
198
+ const result2 = datasetRegistry.keyItems()
199
+ expect(result1).toBe(result2)
200
+ })
201
+
202
+ it('recomputes when the datasets ref changes', () => {
203
+ const result1 = datasetRegistry.keyItems()
204
+ datasetRegistry.attach({ simple: { id: 'simple', showInKey: true, visible: true, style: {} } })
205
+ const result2 = datasetRegistry.keyItems()
206
+ expect(result1).not.toBe(result2)
207
+ })
208
+ })
209
+
210
+ describe('attachCreateDataset', () => {
211
+ let originalCreateDataset
212
+
213
+ beforeEach(() => {
214
+ originalCreateDataset = datasetRegistry._createDataset
215
+ })
216
+
217
+ afterEach(() => {
218
+ datasetRegistry._createDataset = originalCreateDataset
219
+ })
220
+
221
+ it('overrides the factory used by getDataset', () => {
222
+ const customFactory = jest.fn((def) => new Dataset(def))
223
+ datasetRegistry.attachCreateDataset(customFactory)
224
+ datasetRegistry.getDataset('land-covers')
225
+ expect(customFactory).toHaveBeenCalled()
226
+ })
227
+
228
+ it('uses the return value of the custom factory', () => {
229
+ const sentinel = { id: 'sentinel' }
230
+ datasetRegistry.attachCreateDataset(() => sentinel)
231
+ const result = datasetRegistry.getDataset('land-covers')
232
+ expect(result).toBe(sentinel)
233
+ })
234
+ })
235
+
236
+ describe('avoid recreating Dataset instances', () => {
237
+ // Because the registryDataset instances are pure and immutable wrappers
238
+ // around the dataset definition, we can return the same registryDataset
239
+ // instances as long as the definitions are the same.
240
+ // Avoiding unnecessary re-calculation of registryDataset instance members
241
+ // like patternConfigs and symbolConfigs which are derived from the definition.
242
+ const createDatasetSpy = jest.spyOn(datasetRegistry, '_createDataset')
243
+
244
+ it("doesn't throw invalidating before caching anything", () => {
245
+ expect(() => {
246
+ datasetRegistry.attach({
247
+ ...datasetRegistry.datasets,
248
+ 'land-covers-130-131': { id: 'land-covers-130-131', style: { stroke: '#00ff00' } }
249
+ })
250
+ }).not.toThrow()
251
+ })
252
+
253
+ it("doesn't throw if a sublayer without a valid parent is attached", () => {
254
+ expect(() => {
255
+ datasetRegistry.attach({
256
+ ...datasetRegistry.datasets,
257
+ 'no-parent': { id: 'no-parent', parentId: 'no-exists', style: {} }
258
+
259
+ })
260
+ datasetRegistry.getDataset('no-parent')
261
+ datasetRegistry.attach({
262
+ ...datasetRegistry.datasets,
263
+ 'no-parent': { id: 'no-parent', parentId: 'no-exists', style: {} }
264
+ })
265
+ }).not.toThrow()
266
+ })
267
+
268
+ it('returns the same Dataset instance for the same dataset definition', () => {
269
+ expect(datasetRegistry.getDataset('land-covers'))
270
+ .toBe(datasetRegistry.getDataset('land-covers'))
271
+ expect(createDatasetSpy).toHaveBeenCalledTimes(1)
272
+ })
273
+
274
+ it('removes cached Dataset instances when new datasets definition are attached', () => {
275
+ const landCovers1 = datasetRegistry.getDataset('land-covers') // createDatasetSpy called once
276
+ const landCovers1Definition = landCovers1._datasetDefinition
277
+ // attach new definitions, with a different land-covers definition
278
+ datasetRegistry.attach({ ...datasetRegistry.datasets, 'land-covers': { id: 'land-covers', style: {} } })
279
+
280
+ expect(datasetRegistry.getDataset('land-covers')).not.toBe(landCovers1) // should be different as the definition has changed
281
+ expect(createDatasetSpy).toHaveBeenCalledTimes(2)
282
+ // ensure the old definition is no longer cached - which could cause a memory leak
283
+ expect(datasetRegistry._definitionCache.getByDefinition(landCovers1Definition)).toBeUndefined()
284
+ })
285
+
286
+ it('should invalidate the cache of sublayers(children) when a parent dataset definition is updated', () => {
287
+ const landCovers = datasetRegistry.getDataset('land-covers')
288
+ const landCoversSublayer = datasetRegistry.getDataset('land-covers-130-131')
289
+ expect(createDatasetSpy).toHaveBeenCalledTimes(2)
290
+ const landCoversDefinition = landCovers._datasetDefinition
291
+
292
+ // Update the parent dataset definition, which should invalidate both the parent and sublayer cache entries
293
+ const newLandCoversDefinition = { ...landCoversDefinition, style: { stroke: '#00ff00' } }
294
+ datasetRegistry.attach({ ...datasetRegistry.datasets, 'land-covers': newLandCoversDefinition })
295
+
296
+ expect(datasetRegistry.getDataset('land-covers')).not.toBe(landCovers)
297
+ expect(datasetRegistry.getDataset('land-covers-130-131')).not.toBe(landCoversSublayer)
298
+ expect(createDatasetSpy).toHaveBeenCalledTimes(4)
299
+ })
300
+
301
+ it('should invalidate the cache of the parent and siblings when a sublayers dataset definition is updated', () => {
302
+ const landCovers = datasetRegistry.getDataset('land-covers')
303
+ const landCoversSublayer130 = datasetRegistry.getDataset('land-covers-130-131')
304
+ const landCoversSublayer332 = datasetRegistry.getDataset('land-covers-332')
305
+ expect(createDatasetSpy).toHaveBeenCalledTimes(3)
306
+ const sublayerDefinition = landCoversSublayer130._datasetDefinition
307
+
308
+ // Update the parent dataset definition, which should invalidate both the parent and sublayer cache entries
309
+ const newSublayerDefinition = { ...sublayerDefinition, style: { stroke: '#00ff00' } }
310
+ datasetRegistry.attach({ ...datasetRegistry.datasets, 'land-covers-130-131': newSublayerDefinition })
311
+
312
+ expect(datasetRegistry.getDataset('land-covers')).not.toBe(landCovers)
313
+ expect(datasetRegistry.getDataset('land-covers-130-131')).not.toBe(landCoversSublayer130)
314
+ expect(datasetRegistry.getDataset('land-covers-332')).not.toBe(landCoversSublayer332)
315
+
316
+ expect(createDatasetSpy).toHaveBeenCalledTimes(6)
317
+ })
318
+
319
+ it('should not invalidate the cache of different datasets when a definition is updated', () => {
320
+ const landCovers = datasetRegistry.getDataset('land-covers')
321
+ const existingFields = datasetRegistry.getDataset('existing-fields')
322
+ expect(createDatasetSpy).toHaveBeenCalledTimes(2)
323
+ const existingFieldsDefinition = existingFields._datasetDefinition
324
+
325
+ // Update existing-fields, which should not invalidate land-covers
326
+ const newExistingFieldsDefinition = { id: 'existing-fields', style: {} }
327
+ datasetRegistry.attach({ ...datasetRegistry.datasets, 'existing-fields': newExistingFieldsDefinition })
328
+
329
+ expect(datasetRegistry.getDataset('land-covers')).toBe(landCovers)
330
+ expect(datasetRegistry.getDataset('existing-fields')).not.toBe(existingFields)
331
+ expect(createDatasetSpy).toHaveBeenCalledTimes(3) // should have been called one more time
332
+ expect(datasetRegistry._definitionCache.getByDefinition(existingFieldsDefinition)).toBeUndefined() // old definition should no longer be cached
333
+ })
334
+ })
335
+ })
@@ -0,0 +1,17 @@
1
+ export class DynamicGeoJson {
2
+ constructor (dynamicGeoJSONDefinition) {
3
+ this.id = dynamicGeoJSONDefinition.id
4
+ this.url = dynamicGeoJSONDefinition.url
5
+ this.transformRequest = dynamicGeoJSONDefinition.transformRequest
6
+ this.maxFeatures = dynamicGeoJSONDefinition.maxFeatures
7
+ this.minZoom = dynamicGeoJSONDefinition.minZoom
8
+ this.idProperty = dynamicGeoJSONDefinition.idProperty
9
+ this.hiddenFeaturesIdExpression = ['to-string', ['get', this.idProperty]]
10
+ this.sourceId = `geojson-dynamic-${this.id}`
11
+ this.source = {
12
+ type: 'geojson',
13
+ data: { type: 'FeatureCollection', features: [] },
14
+ ...(this.idProperty ? { promoteId: this.idProperty } : { generateId: true })
15
+ }
16
+ }
17
+ }
@@ -0,0 +1,36 @@
1
+ let globalState = {}
2
+
3
+ export const attachGlobalState = (globalStateRef) => {
4
+ globalState = globalStateRef
5
+ }
6
+
7
+ // TODO - remove this once testing finishes
8
+ window.getGlobalState = () => Object.freeze(globalState)
9
+
10
+ const calculateLocalOpacity = (opacityValue, parentOpacityValue) => {
11
+ if (opacityValue === undefined) {
12
+ if (parentOpacityValue === undefined) {
13
+ return globalState.opacity
14
+ }
15
+ return parentOpacityValue
16
+ }
17
+ return opacityValue
18
+ }
19
+
20
+ const multiplyOpacities = (opacityValue = 1, parentOpacityValue = 1) => {
21
+ return Math.round((opacityValue * parentOpacityValue * globalState.opacity + Number.EPSILON) * 100) / 100
22
+ }
23
+
24
+ export const calculateOpacity = (opacityValue, parentOpacityValue) => {
25
+ switch (globalState.opacityMode) {
26
+ case 'global':
27
+ return globalState.opacity
28
+ case 'multiply':
29
+ return multiplyOpacities(opacityValue, parentOpacityValue)
30
+ case 'dataset':
31
+ default:
32
+ return calculateLocalOpacity(opacityValue, parentOpacityValue)
33
+ }
34
+ }
35
+
36
+ export const getGlobalVisibility = () => globalState.visible
@@ -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) {