@jupytergis/base 0.15.0 → 0.16.0-alpha.0

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 (393) hide show
  1. package/lib/commands/BaseCommandIDs.d.ts +5 -0
  2. package/lib/commands/BaseCommandIDs.js +5 -0
  3. package/lib/commands/index.js +218 -47
  4. package/lib/commands/operationCommands.js +2 -2
  5. package/lib/constants.js +5 -1
  6. package/lib/{panelview/annotationPanel.js → features/annotations/AnnotationsPanel.js} +1 -1
  7. package/lib/{annotations → features/annotations}/index.d.ts +1 -0
  8. package/lib/{annotations → features/annotations}/index.js +1 -0
  9. package/lib/{console → features/console}/consoleview.js +1 -1
  10. package/lib/{panelview/filter-panel → features/filter}/Filter.js +4 -4
  11. package/lib/{panelview/filter-panel → features/filter}/FilterRow.d.ts +3 -2
  12. package/lib/{panelview/filter-panel → features/filter}/FilterRow.js +2 -1
  13. package/lib/{panelview/identify-panel → features/identify}/IdentifyPanel.d.ts +2 -0
  14. package/lib/features/identify/IdentifyPanel.js +102 -0
  15. package/lib/features/identify/components/FeatureCard.d.ts +17 -0
  16. package/lib/features/identify/components/FeatureCard.js +26 -0
  17. package/lib/features/identify/components/FeatureCardHeader.d.ts +11 -0
  18. package/lib/features/identify/components/FeatureCardHeader.js +30 -0
  19. package/lib/features/identify/components/FeatureFloater.d.ts +7 -0
  20. package/lib/features/identify/components/FeatureFloater.js +19 -0
  21. package/lib/features/identify/components/FeaturePropertyList.d.ts +11 -0
  22. package/lib/features/identify/components/FeaturePropertyList.js +18 -0
  23. package/lib/features/identify/components/FeatureRow.d.ts +13 -0
  24. package/lib/features/identify/components/FeatureRow.js +25 -0
  25. package/lib/features/identify/components/PropertyEditors.d.ts +44 -0
  26. package/lib/features/identify/components/PropertyEditors.js +56 -0
  27. package/lib/features/identify/hooks/useIdentifyPropertyEditor.d.ts +11 -0
  28. package/lib/features/identify/hooks/useIdentifyPropertyEditor.js +132 -0
  29. package/lib/features/identify/types/editorTypes.d.ts +21 -0
  30. package/lib/features/identify/utils/getFeatureIdentifier.d.ts +5 -0
  31. package/lib/features/identify/utils/getFeatureIdentifier.js +14 -0
  32. package/lib/features/identify/utils/highlightLayer.d.ts +11 -0
  33. package/lib/features/identify/utils/highlightLayer.js +57 -0
  34. package/lib/features/identify/utils/highlightStyle.d.ts +7 -0
  35. package/lib/features/identify/utils/highlightStyle.js +40 -0
  36. package/lib/{dialogs/layerBrowserDialog.js → features/layer-browser/index.js} +2 -2
  37. package/lib/features/layers/forms/layer/geoTiffLayerForm.d.ts +3 -0
  38. package/lib/{formbuilder/objectform/layer/webGlLayerForm.js → features/layers/forms/layer/geoTiffLayerForm.js} +5 -5
  39. package/lib/{formbuilder/objectform → features/layers/forms}/layer/heatmapLayerForm.js +4 -4
  40. package/lib/{formbuilder/objectform → features/layers/forms}/layer/hillshadeLayerForm.js +4 -4
  41. package/lib/{formbuilder/objectform → features/layers/forms}/layer/index.d.ts +1 -1
  42. package/lib/{formbuilder/objectform → features/layers/forms}/layer/index.js +1 -1
  43. package/lib/{formbuilder/objectform → features/layers/forms}/layer/layerform.d.ts +1 -1
  44. package/lib/{formbuilder/objectform → features/layers/forms}/layer/layerform.js +4 -4
  45. package/lib/features/layers/forms/layer/storySegmentLayerForm.js +150 -0
  46. package/lib/{formbuilder/objectform → features/layers/forms}/layer/vectorlayerform.js +4 -4
  47. package/lib/{formbuilder/objectform → features/layers/forms}/source/geojsonsource.js +5 -5
  48. package/lib/features/layers/forms/source/geopackagesource.d.ts +3 -0
  49. package/lib/features/layers/forms/source/geopackagesource.js +93 -0
  50. package/lib/{formbuilder/objectform → features/layers/forms}/source/geotiffsource.js +5 -5
  51. package/lib/{formbuilder/objectform → features/layers/forms}/source/index.d.ts +1 -0
  52. package/lib/{formbuilder/objectform → features/layers/forms}/source/index.js +1 -0
  53. package/lib/{formbuilder/objectform → features/layers/forms}/source/pathbasedsource.js +5 -5
  54. package/lib/{formbuilder/objectform → features/layers/forms}/source/sourceform.d.ts +1 -1
  55. package/lib/{formbuilder/objectform → features/layers/forms}/source/sourceform.js +4 -4
  56. package/lib/{formbuilder/objectform → features/layers/forms}/source/tilesourceform.js +4 -4
  57. package/lib/{formbuilder/objectform → features/layers/forms}/source/wmsTileSource.d.ts +1 -1
  58. package/lib/{formbuilder/objectform → features/layers/forms}/source/wmsTileSource.js +6 -6
  59. package/lib/{dialogs → features/layers}/layerCreationFormDialog.d.ts +1 -1
  60. package/lib/{dialogs → features/layers}/layerCreationFormDialog.js +1 -1
  61. package/lib/features/layers/symbology/Grammar.d.ts +11 -0
  62. package/lib/features/layers/symbology/Grammar.js +235 -0
  63. package/lib/{dialogs/symbology/vector_layer/types → features/layers/symbology}/Heatmap.d.ts +1 -1
  64. package/lib/{dialogs/symbology/vector_layer/types → features/layers/symbology}/Heatmap.js +30 -10
  65. package/lib/{dialogs → features/layers}/symbology/classificationModes.d.ts +6 -6
  66. package/lib/{dialogs → features/layers}/symbology/classificationModes.js +48 -44
  67. package/lib/{dialogs → features/layers}/symbology/colorRampUtils.d.ts +1 -0
  68. package/lib/{dialogs → features/layers}/symbology/colorRampUtils.js +12 -1
  69. package/lib/features/layers/symbology/components/MappingRow.d.ts +40 -0
  70. package/lib/features/layers/symbology/components/MappingRow.js +516 -0
  71. package/lib/features/layers/symbology/components/NumericInput.d.ts +20 -0
  72. package/lib/features/layers/symbology/components/NumericInput.js +44 -0
  73. package/lib/features/layers/symbology/components/ScaleEditor.d.ts +33 -0
  74. package/lib/features/layers/symbology/components/ScaleEditor.js +221 -0
  75. package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampControls.d.ts +1 -1
  76. package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampControls.js +3 -2
  77. package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampSelector.d.ts +2 -1
  78. package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampSelector.js +6 -1
  79. package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ModeSelectRow.d.ts +1 -1
  80. package/lib/{dialogs → features/layers}/symbology/components/color_ramp/RgbaColorPicker.js +39 -9
  81. package/lib/{dialogs → features/layers}/symbology/components/color_stops/StopRow.d.ts +1 -1
  82. package/lib/{dialogs → features/layers}/symbology/components/color_stops/StopRow.js +1 -1
  83. package/lib/features/layers/symbology/grammarToOLLayer.d.ts +27 -0
  84. package/lib/features/layers/symbology/grammarToOLLayer.js +145 -0
  85. package/lib/features/layers/symbology/grammarToOLStyle.d.ts +32 -0
  86. package/lib/features/layers/symbology/grammarToOLStyle.js +467 -0
  87. package/lib/{dialogs → features/layers}/symbology/hooks/useGetBandInfo.d.ts +1 -1
  88. package/lib/{dialogs → features/layers}/symbology/hooks/useGetBandInfo.js +1 -1
  89. package/lib/{dialogs → features/layers}/symbology/hooks/useGetProperties.js +1 -1
  90. package/lib/{dialogs → features/layers}/symbology/hooks/useGetSymbology.js +4 -2
  91. package/lib/features/layers/symbology/styleBuilder.d.ts +21 -0
  92. package/lib/features/layers/symbology/styleBuilder.js +145 -0
  93. package/lib/{dialogs → features/layers}/symbology/symbologyDialog.d.ts +1 -1
  94. package/lib/{dialogs → features/layers}/symbology/symbologyDialog.js +12 -12
  95. package/lib/{dialogs → features/layers}/symbology/symbologyUtils.d.ts +18 -10
  96. package/lib/{dialogs → features/layers}/symbology/symbologyUtils.js +0 -84
  97. package/lib/{panelview/objectproperties.d.ts → features/objectproperties/index.d.ts} +1 -1
  98. package/lib/{panelview/objectproperties.js → features/objectproperties/index.js} +5 -10
  99. package/lib/{dialogs → features/processing}/ProcessingFormDialog.d.ts +1 -1
  100. package/lib/{dialogs → features/processing}/ProcessingFormDialog.js +28 -14
  101. package/lib/features/processing/forms/MapExtentToggle.d.ts +13 -0
  102. package/lib/features/processing/forms/MapExtentToggle.js +20 -0
  103. package/lib/features/processing/forms/clipRasterByExtentForm.d.ts +10 -0
  104. package/lib/features/processing/forms/clipRasterByExtentForm.js +99 -0
  105. package/lib/{formbuilder/objectform/process → features/processing/forms}/dissolveProcessForm.js +5 -5
  106. package/lib/{formbuilder/objectform → features/processing/forms}/processingForm.d.ts +1 -1
  107. package/lib/{formbuilder/objectform → features/processing/forms}/processingForm.js +6 -6
  108. package/lib/features/processing/forms/rasterizeForm.d.ts +10 -0
  109. package/lib/features/processing/forms/rasterizeForm.js +75 -0
  110. package/lib/features/processing/forms/useMapExtent.d.ts +22 -0
  111. package/lib/features/processing/forms/useMapExtent.js +57 -0
  112. package/lib/{processing → features/processing}/index.d.ts +19 -2
  113. package/lib/features/processing/index.js +1246 -0
  114. package/lib/{processing → features/processing}/processingCommands.d.ts +1 -1
  115. package/lib/features/processing/processingCommands.js +168 -0
  116. package/lib/features/processing/serverProcessing.d.ts +51 -0
  117. package/lib/features/processing/serverProcessing.js +99 -0
  118. package/lib/{stacBrowser → features/stac-browser}/components/StacPanel.js +2 -2
  119. package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/QueryableComboBox.js +5 -5
  120. package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/QueryableRow.js +1 -1
  121. package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/StacFilterExtensionPanel.js +2 -2
  122. package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/StacQueryableFilters.js +1 -1
  123. package/lib/{stacBrowser → features/stac-browser}/components/geodes/StacFilterSection.js +3 -3
  124. package/lib/{stacBrowser → features/stac-browser}/components/shared/StacPanelResults.js +3 -3
  125. package/lib/{stacBrowser → features/stac-browser}/components/shared/StacSpatialExtent.js +1 -1
  126. package/lib/{stacBrowser → features/stac-browser}/components/shared/StacTemporalExtent.js +1 -1
  127. package/lib/{stacBrowser → features/stac-browser}/context/StacResultsContext.js +2 -2
  128. package/lib/{stacBrowser → features/stac-browser}/hooks/useGeodesSearch.js +2 -2
  129. package/lib/{stacBrowser → features/stac-browser}/hooks/useStacFilterExtension.js +3 -3
  130. package/lib/{stacBrowser → features/stac-browser}/hooks/useStacSearch.js +1 -1
  131. package/lib/features/stac-browser/types/types.js +1 -0
  132. package/lib/{panelview/story-maps → features/story}/SpectaPanel.d.ts +4 -1
  133. package/lib/{panelview/story-maps → features/story}/SpectaPanel.js +3 -4
  134. package/lib/{panelview/story-maps → features/story}/StoryEditorPanel.js +1 -1
  135. package/lib/{panelview/story-maps → features/story}/StoryViewerPanel.d.ts +12 -11
  136. package/lib/features/story/StoryViewerPanel.js +64 -0
  137. package/lib/features/story/__tests__/fixtures/listStoryTestItems.d.ts +9 -0
  138. package/lib/features/story/__tests__/fixtures/listStoryTestItems.js +21 -0
  139. package/lib/features/story/components/ListStoryMapOverlayPanel.d.ts +10 -0
  140. package/lib/features/story/components/ListStoryMapOverlayPanel.js +11 -0
  141. package/lib/features/story/components/ListStoryMarkdownMeasurePane.d.ts +11 -0
  142. package/lib/features/story/components/ListStoryMarkdownMeasurePane.js +55 -0
  143. package/lib/features/story/components/ListStoryOverlayMarkdown.d.ts +15 -0
  144. package/lib/features/story/components/ListStoryOverlayMarkdown.js +93 -0
  145. package/lib/features/story/components/ListStoryStageOverlay.d.ts +12 -0
  146. package/lib/features/story/components/ListStoryStageOverlay.js +132 -0
  147. package/lib/features/story/components/ListStoryVirtualScrollTrack.d.ts +6 -0
  148. package/lib/features/story/components/ListStoryVirtualScrollTrack.js +7 -0
  149. package/lib/{panelview/story-maps → features/story}/components/SpectaDesktopView.d.ts +4 -2
  150. package/lib/features/story/components/SpectaDesktopView.js +67 -0
  151. package/lib/{panelview/story-maps → features/story}/components/SpectaMobileView.d.ts +2 -4
  152. package/lib/{panelview/story-maps → features/story}/components/SpectaMobileView.js +3 -3
  153. package/lib/features/story/components/SpectaSingleModeContent.d.ts +18 -0
  154. package/lib/features/story/components/SpectaSingleModeContent.js +8 -0
  155. package/lib/features/story/context/ListStoryScrollTrackContext.d.ts +15 -0
  156. package/lib/features/story/context/ListStoryScrollTrackContext.js +142 -0
  157. package/lib/features/story/hooks/useCurrentSegmentIndex.d.ts +3 -0
  158. package/lib/features/story/hooks/useCurrentSegmentIndex.js +17 -0
  159. package/lib/features/story/hooks/useListStoryScroll.d.ts +15 -0
  160. package/lib/features/story/hooks/useListStoryScroll.js +107 -0
  161. package/lib/features/story/hooks/useQueuedMarkdownHeightMeasure.d.ts +19 -0
  162. package/lib/features/story/hooks/useQueuedMarkdownHeightMeasure.js +56 -0
  163. package/lib/features/story/hooks/useStoryImagePreload.d.ts +1 -0
  164. package/lib/features/story/hooks/useStoryImagePreload.js +24 -0
  165. package/lib/{panelview/story-maps → features/story}/hooks/useStoryMap.d.ts +2 -7
  166. package/lib/{panelview/story-maps → features/story}/hooks/useStoryMap.js +20 -44
  167. package/lib/features/story/hooks/useStoryScrollState.d.ts +21 -0
  168. package/lib/features/story/hooks/useStoryScrollState.js +39 -0
  169. package/lib/features/story/hooks/useStorySegmentSync.d.ts +8 -0
  170. package/lib/features/story/hooks/useStorySegmentSync.js +51 -0
  171. package/lib/features/story/types/types.d.ts +38 -0
  172. package/lib/features/story/types/types.js +1 -0
  173. package/lib/features/story/utils/computeListStoryScrollState.d.ts +12 -0
  174. package/lib/features/story/utils/computeListStoryScrollState.js +70 -0
  175. package/lib/features/story/utils/listStoryMeasureQueue.d.ts +11 -0
  176. package/lib/features/story/utils/listStoryMeasureQueue.js +14 -0
  177. package/lib/features/story/utils/listStoryScrollTrack.d.ts +17 -0
  178. package/lib/features/story/utils/listStoryScrollTrack.js +72 -0
  179. package/lib/features/story/utils/spectaPresentation.d.ts +9 -0
  180. package/lib/features/story/utils/spectaPresentation.js +37 -0
  181. package/lib/features/story/utils/storySegmentViewItems.d.ts +5 -0
  182. package/lib/features/story/utils/storySegmentViewItems.js +30 -0
  183. package/lib/index.d.ts +11 -9
  184. package/lib/index.js +11 -9
  185. package/lib/keybindings.json +5 -0
  186. package/lib/mainview/OpenEOTileLayer.d.ts +49 -0
  187. package/lib/mainview/OpenEOTileLayer.js +179 -0
  188. package/lib/mainview/TemporalSlider.js +11 -9
  189. package/lib/mainview/geoJsonFeaturePatch.d.ts +9 -0
  190. package/lib/mainview/geoJsonFeaturePatch.js +43 -0
  191. package/lib/mainview/mainView.d.ts +90 -7
  192. package/lib/mainview/mainView.js +1196 -586
  193. package/lib/mainview/mainviewwidget.d.ts +5 -1
  194. package/lib/mainview/mainviewwidget.js +4 -2
  195. package/lib/shared/components/Button.d.ts +1 -1
  196. package/lib/shared/components/DropdownMenu.d.ts +1 -0
  197. package/lib/shared/components/DropdownMenu.js +3 -2
  198. package/lib/shared/components/NativeSelect.d.ts +8 -0
  199. package/lib/shared/components/NativeSelect.js +29 -0
  200. package/lib/shared/components/Tabs.d.ts +3 -3
  201. package/lib/shared/components/Tabs.js +5 -5
  202. package/lib/{formbuilder → shared/formbuilder}/creationform.js +71 -4
  203. package/lib/{formbuilder → shared/formbuilder}/editform.js +1 -1
  204. package/lib/{formbuilder → shared/formbuilder}/formselectors.d.ts +2 -2
  205. package/lib/{formbuilder → shared/formbuilder}/formselectors.js +10 -4
  206. package/lib/shared/formbuilder/index.d.ts +4 -0
  207. package/lib/shared/formbuilder/index.js +4 -0
  208. package/lib/{formbuilder → shared/formbuilder}/objectform/SchemaForm.d.ts +1 -1
  209. package/lib/{formbuilder → shared/formbuilder}/objectform/StoryEditorForm.d.ts +1 -1
  210. package/lib/{formbuilder → shared/formbuilder}/objectform/StoryEditorForm.js +1 -1
  211. package/lib/{formbuilder → shared/formbuilder}/objectform/components/LayerSelect.js +1 -1
  212. package/lib/{formbuilder → shared/formbuilder}/objectform/components/SegmentFormSymbology.js +4 -4
  213. package/lib/{formbuilder → shared/formbuilder}/objectform/components/SourcePropertiesField.js +1 -1
  214. package/lib/{formbuilder → shared/formbuilder}/objectform/components/StorySegmentReset.js +1 -1
  215. package/lib/{formbuilder → shared/formbuilder}/objectform/components/WmsTileSourceUrlInput.js +4 -4
  216. package/lib/{formbuilder → shared/formbuilder}/objectform/fileselectorwidget.js +8 -1
  217. package/lib/{formbuilder → shared/formbuilder}/objectform/schemaUtils.d.ts +3 -1
  218. package/lib/{formbuilder → shared/formbuilder}/objectform/schemaUtils.js +11 -0
  219. package/lib/{formbuilder → shared/formbuilder}/objectform/useSchemaFormState.d.ts +1 -1
  220. package/lib/{formbuilder → shared/formbuilder}/objectform/useSchemaFormState.js +1 -1
  221. package/lib/{icons.d.ts → shared/icons.d.ts} +2 -0
  222. package/lib/{icons.js → shared/icons.js} +28 -18
  223. package/lib/tools.d.ts +2 -0
  224. package/lib/tools.js +138 -4
  225. package/lib/types.d.ts +6 -1
  226. package/lib/types.js +6 -1
  227. package/lib/{menus.js → workspace/menus.js} +10 -2
  228. package/lib/workspace/panels/components/TabbedPanel.d.ts +17 -0
  229. package/lib/workspace/panels/components/TabbedPanel.js +19 -0
  230. package/lib/{panelview → workspace/panels}/components/layers.js +63 -18
  231. package/lib/workspace/panels/components/legendItem.js +680 -0
  232. package/lib/workspace/panels/hooks/useLayerTree.d.ts +19 -0
  233. package/lib/workspace/panels/hooks/useLayerTree.js +103 -0
  234. package/lib/workspace/panels/hooks/useRightPanelOptions.d.ts +27 -0
  235. package/lib/workspace/panels/hooks/useRightPanelOptions.js +72 -0
  236. package/lib/workspace/panels/hooks/useUIState.d.ts +2 -0
  237. package/lib/workspace/panels/hooks/useUIState.js +25 -0
  238. package/lib/{panelview → workspace/panels}/index.d.ts +1 -1
  239. package/lib/{panelview → workspace/panels}/index.js +1 -1
  240. package/lib/{panelview → workspace/panels}/leftpanel.d.ts +1 -1
  241. package/lib/workspace/panels/leftpanel.js +70 -0
  242. package/lib/{panelview/rightpanel.d.ts → workspace/panels/mergedpanel.d.ts} +6 -5
  243. package/lib/workspace/panels/mergedpanel.js +166 -0
  244. package/lib/workspace/panels/rightpanel.d.ts +25 -0
  245. package/lib/{panelview → workspace/panels}/rightpanel.js +53 -63
  246. package/lib/{statusbar → workspace/statusbar}/StatusBar.js +5 -4
  247. package/lib/{toolbar → workspace/toolbar}/widget.d.ts +2 -0
  248. package/lib/{toolbar → workspace/toolbar}/widget.js +33 -5
  249. package/lib/{widget.d.ts → workspace/widget.d.ts} +7 -5
  250. package/lib/{widget.js → workspace/widget.js} +16 -7
  251. package/package.json +16 -4
  252. package/style/base.css +109 -1
  253. package/style/icons/geopackage.svg +95 -0
  254. package/style/icons/pencil_solid.svg +7 -0
  255. package/style/identify.css +95 -0
  256. package/style/layerBrowser.css +28 -0
  257. package/style/leftPanel.css +25 -0
  258. package/style/shared/button.css +12 -0
  259. package/style/shared/dropdownMenu.css +9 -0
  260. package/style/shared/nativeSelect.css +75 -0
  261. package/style/shared/tabs.css +1 -1
  262. package/style/spectaProgressBar.css +0 -1
  263. package/style/storyPanel.css +140 -9
  264. package/style/storySpectaArticleOverlay.css +129 -0
  265. package/style/symbologyDialog.css +285 -27
  266. package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +0 -4
  267. package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +0 -42
  268. package/lib/dialogs/symbology/tiff_layer/components/BandRow.d.ts +0 -23
  269. package/lib/dialogs/symbology/tiff_layer/components/BandRow.js +0 -59
  270. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +0 -4
  271. package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +0 -92
  272. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +0 -5
  273. package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +0 -313
  274. package/lib/dialogs/symbology/vector_layer/VectorRendering.d.ts +0 -4
  275. package/lib/dialogs/symbology/vector_layer/VectorRendering.js +0 -112
  276. package/lib/dialogs/symbology/vector_layer/components/ValueSelect.d.ts +0 -8
  277. package/lib/dialogs/symbology/vector_layer/components/ValueSelect.js +0 -9
  278. package/lib/dialogs/symbology/vector_layer/types/Canonical.d.ts +0 -4
  279. package/lib/dialogs/symbology/vector_layer/types/Canonical.js +0 -130
  280. package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +0 -4
  281. package/lib/dialogs/symbology/vector_layer/types/Categorized.js +0 -243
  282. package/lib/dialogs/symbology/vector_layer/types/Graduated.d.ts +0 -4
  283. package/lib/dialogs/symbology/vector_layer/types/Graduated.js +0 -362
  284. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.d.ts +0 -4
  285. package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +0 -120
  286. package/lib/formbuilder/index.d.ts +0 -4
  287. package/lib/formbuilder/index.js +0 -4
  288. package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +0 -95
  289. package/lib/formbuilder/objectform/layer/webGlLayerForm.d.ts +0 -3
  290. package/lib/formbuilder/objectform/process/index.d.ts +0 -1
  291. package/lib/formbuilder/objectform/process/index.js +0 -1
  292. package/lib/panelview/components/legendItem.js +0 -210
  293. package/lib/panelview/identify-panel/IdentifyPanel.js +0 -102
  294. package/lib/panelview/leftpanel.js +0 -139
  295. package/lib/panelview/story-maps/StoryViewerPanel.js +0 -116
  296. package/lib/panelview/story-maps/components/SpectaDesktopView.js +0 -49
  297. package/lib/processing/index.js +0 -200
  298. package/lib/processing/processingCommands.js +0 -67
  299. /package/lib/{panelview/annotationPanel.d.ts → features/annotations/AnnotationsPanel.d.ts} +0 -0
  300. /package/lib/{annotations → features/annotations}/components/Annotation.d.ts +0 -0
  301. /package/lib/{annotations → features/annotations}/components/Annotation.js +0 -0
  302. /package/lib/{annotations → features/annotations}/components/AnnotationFloater.d.ts +0 -0
  303. /package/lib/{annotations → features/annotations}/components/AnnotationFloater.js +0 -0
  304. /package/lib/{annotations → features/annotations}/components/Message.d.ts +0 -0
  305. /package/lib/{annotations → features/annotations}/components/Message.js +0 -0
  306. /package/lib/{annotations → features/annotations}/model.d.ts +0 -0
  307. /package/lib/{annotations → features/annotations}/model.js +0 -0
  308. /package/lib/{console → features/console}/consoleview.d.ts +0 -0
  309. /package/lib/{console → features/console}/index.d.ts +0 -0
  310. /package/lib/{console → features/console}/index.js +0 -0
  311. /package/lib/{panelview/filter-panel → features/filter}/Filter.d.ts +0 -0
  312. /package/lib/{stacBrowser/types/types.js → features/identify/types/editorTypes.js} +0 -0
  313. /package/lib/{dialogs/layerBrowserDialog.d.ts → features/layer-browser/index.d.ts} +0 -0
  314. /package/lib/{formbuilder/objectform → features/layers/forms}/layer/heatmapLayerForm.d.ts +0 -0
  315. /package/lib/{formbuilder/objectform → features/layers/forms}/layer/hillshadeLayerForm.d.ts +0 -0
  316. /package/lib/{formbuilder/objectform → features/layers/forms}/layer/storySegmentLayerForm.d.ts +0 -0
  317. /package/lib/{formbuilder/objectform → features/layers/forms}/layer/vectorlayerform.d.ts +0 -0
  318. /package/lib/{formbuilder/objectform → features/layers/forms}/source/geojsonsource.d.ts +0 -0
  319. /package/lib/{formbuilder/objectform → features/layers/forms}/source/geotiffsource.d.ts +0 -0
  320. /package/lib/{formbuilder/objectform → features/layers/forms}/source/pathbasedsource.d.ts +0 -0
  321. /package/lib/{formbuilder/objectform → features/layers/forms}/source/tilesourceform.d.ts +0 -0
  322. /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampSelectorEntry.d.ts +0 -0
  323. /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampSelectorEntry.js +0 -0
  324. /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ModeSelectRow.js +0 -0
  325. /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/RgbaColorPicker.d.ts +0 -0
  326. /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/cmocean.json +0 -0
  327. /package/lib/{dialogs → features/layers}/symbology/components/color_stops/StopContainer.d.ts +0 -0
  328. /package/lib/{dialogs → features/layers}/symbology/components/color_stops/StopContainer.js +0 -0
  329. /package/lib/{dialogs → features/layers}/symbology/hooks/useEffectiveSymbologyParams.d.ts +0 -0
  330. /package/lib/{dialogs → features/layers}/symbology/hooks/useEffectiveSymbologyParams.js +0 -0
  331. /package/lib/{dialogs → features/layers}/symbology/hooks/useGetProperties.d.ts +0 -0
  332. /package/lib/{dialogs → features/layers}/symbology/hooks/useGetSymbology.d.ts +0 -0
  333. /package/lib/{dialogs → features/layers}/symbology/hooks/useOkSignal.d.ts +0 -0
  334. /package/lib/{dialogs → features/layers}/symbology/hooks/useOkSignal.js +0 -0
  335. /package/lib/{formbuilder/objectform/process → features/processing/forms}/dissolveProcessForm.d.ts +0 -0
  336. /package/lib/{processing → features/processing}/processingFormToParam.d.ts +0 -0
  337. /package/lib/{processing → features/processing}/processingFormToParam.js +0 -0
  338. /package/lib/{stacBrowser → features/stac-browser}/components/StacPanel.d.ts +0 -0
  339. /package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/QueryableComboBox.d.ts +0 -0
  340. /package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/QueryableRow.d.ts +0 -0
  341. /package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/StacFilterExtensionPanel.d.ts +0 -0
  342. /package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/StacQueryableFilters.d.ts +0 -0
  343. /package/lib/{stacBrowser → features/stac-browser}/components/geodes/StacFilterSection.d.ts +0 -0
  344. /package/lib/{stacBrowser → features/stac-browser}/components/geodes/StacGeodesFilterPanel.d.ts +0 -0
  345. /package/lib/{stacBrowser → features/stac-browser}/components/geodes/StacGeodesFilterPanel.js +0 -0
  346. /package/lib/{stacBrowser → features/stac-browser}/components/shared/StacPanelResults.d.ts +0 -0
  347. /package/lib/{stacBrowser → features/stac-browser}/components/shared/StacSpatialExtent.d.ts +0 -0
  348. /package/lib/{stacBrowser → features/stac-browser}/components/shared/StacTemporalExtent.d.ts +0 -0
  349. /package/lib/{stacBrowser → features/stac-browser}/constants.d.ts +0 -0
  350. /package/lib/{stacBrowser → features/stac-browser}/constants.js +0 -0
  351. /package/lib/{stacBrowser → features/stac-browser}/context/StacResultsContext.d.ts +0 -0
  352. /package/lib/{stacBrowser → features/stac-browser}/hooks/useGeodesSearch.d.ts +0 -0
  353. /package/lib/{stacBrowser → features/stac-browser}/hooks/useStacFilterExtension.d.ts +0 -0
  354. /package/lib/{stacBrowser → features/stac-browser}/hooks/useStacSearch.d.ts +0 -0
  355. /package/lib/{stacBrowser → features/stac-browser}/index.d.ts +0 -0
  356. /package/lib/{stacBrowser → features/stac-browser}/index.js +0 -0
  357. /package/lib/{stacBrowser → features/stac-browser}/types/types.d.ts +0 -0
  358. /package/lib/{panelview/story-maps → features/story}/StoryEditorPanel.d.ts +0 -0
  359. /package/lib/{panelview/story-maps → features/story}/components/PreviewModeSwitch.d.ts +0 -0
  360. /package/lib/{panelview/story-maps → features/story}/components/PreviewModeSwitch.js +0 -0
  361. /package/lib/{panelview/story-maps → features/story}/components/StoryContentSection.d.ts +0 -0
  362. /package/lib/{panelview/story-maps → features/story}/components/StoryContentSection.js +0 -0
  363. /package/lib/{panelview/story-maps → features/story}/components/StoryImageSection.d.ts +0 -0
  364. /package/lib/{panelview/story-maps → features/story}/components/StoryImageSection.js +0 -0
  365. /package/lib/{panelview/story-maps → features/story}/components/StoryNavBar.d.ts +0 -0
  366. /package/lib/{panelview/story-maps → features/story}/components/StoryNavBar.js +0 -0
  367. /package/lib/{panelview/story-maps → features/story}/components/StorySubtitleSection.d.ts +0 -0
  368. /package/lib/{panelview/story-maps → features/story}/components/StorySubtitleSection.js +0 -0
  369. /package/lib/{panelview/story-maps → features/story}/components/StoryTitleSection.d.ts +0 -0
  370. /package/lib/{panelview/story-maps → features/story}/components/StoryTitleSection.js +0 -0
  371. /package/lib/{formbuilder → shared/formbuilder}/creationform.d.ts +0 -0
  372. /package/lib/{formbuilder → shared/formbuilder}/editform.d.ts +0 -0
  373. /package/lib/{formbuilder → shared/formbuilder}/objectform/SchemaForm.js +0 -0
  374. /package/lib/{formbuilder → shared/formbuilder}/objectform/components/LayerSelect.d.ts +0 -0
  375. /package/lib/{formbuilder → shared/formbuilder}/objectform/components/OpacitySlider.d.ts +0 -0
  376. /package/lib/{formbuilder → shared/formbuilder}/objectform/components/OpacitySlider.js +0 -0
  377. /package/lib/{formbuilder → shared/formbuilder}/objectform/components/SegmentFormSymbology.d.ts +0 -0
  378. /package/lib/{formbuilder → shared/formbuilder}/objectform/components/SourcePropertiesField.d.ts +0 -0
  379. /package/lib/{formbuilder → shared/formbuilder}/objectform/components/StorySegmentReset.d.ts +0 -0
  380. /package/lib/{formbuilder → shared/formbuilder}/objectform/components/WmsTileSourceUrlInput.d.ts +0 -0
  381. /package/lib/{formbuilder → shared/formbuilder}/objectform/fileselectorwidget.d.ts +0 -0
  382. /package/lib/{store.d.ts → shared/store.d.ts} +0 -0
  383. /package/lib/{store.js → shared/store.js} +0 -0
  384. /package/lib/{menus.d.ts → workspace/menus.d.ts} +0 -0
  385. /package/lib/{panelview → workspace/panels}/components/layers.d.ts +0 -0
  386. /package/lib/{panelview → workspace/panels}/components/legendItem.d.ts +0 -0
  387. /package/lib/{panelview → workspace/panels}/header.d.ts +0 -0
  388. /package/lib/{panelview → workspace/panels}/header.js +0 -0
  389. /package/lib/{statusbar → workspace/statusbar}/SpectaPresentationProgressBar.d.ts +0 -0
  390. /package/lib/{statusbar → workspace/statusbar}/SpectaPresentationProgressBar.js +0 -0
  391. /package/lib/{statusbar → workspace/statusbar}/StatusBar.d.ts +0 -0
  392. /package/lib/{toolbar → workspace/toolbar}/index.d.ts +0 -0
  393. /package/lib/{toolbar → workspace/toolbar}/index.js +0 -0
@@ -16,38 +16,72 @@ import { UUID } from '@lumino/coreutils';
16
16
  import { ContextMenu, Menu } from '@lumino/widgets';
17
17
  import { Collection, Map as OlMap, View, getUid, } from 'ol';
18
18
  import Feature from 'ol/Feature';
19
+ import TileState from 'ol/TileState';
19
20
  import { FullScreen, ScaleLine, Zoom } from 'ol/control';
20
21
  import { singleClick } from 'ol/events/condition';
21
- import { getCenter } from 'ol/extent';
22
+ import { getCenter, getSize } from 'ol/extent';
22
23
  import { GeoJSON, MVT } from 'ol/format';
23
24
  import { Point } from 'ol/geom';
24
25
  import { DragAndDrop, DragPan, DragRotate, DragZoom, KeyboardPan, KeyboardZoom, MouseWheelZoom, PinchRotate, PinchZoom, DoubleClickZoom, Select, } from 'ol/interaction';
25
- import { Heatmap as HeatmapLayer, Image as ImageLayer, Layer, Vector as VectorLayer, VectorTile as VectorTileLayer, WebGLTile as WebGlTileLayer, } from 'ol/layer';
26
+ import Draw from 'ol/interaction/Draw';
27
+ import Modify from 'ol/interaction/Modify';
28
+ import Snap from 'ol/interaction/Snap';
29
+ import { Heatmap as HeatmapLayer, Image as ImageLayer, Layer, VectorImage as VectorImageLayer, VectorTile as VectorTileLayer, WebGLTile as GeoTiffLayer, } from 'ol/layer';
30
+ import LayerGroup from 'ol/layer/Group';
26
31
  import TileLayer from 'ol/layer/Tile';
27
32
  import { fromLonLat, get as getProjection, toLonLat, transformExtent, } from 'ol/proj';
28
33
  import { register } from 'ol/proj/proj4.js';
29
34
  import RenderFeature, { toGeometry } from 'ol/render/Feature';
30
35
  import { GeoTIFF as GeoTIFFSource, ImageTile as ImageTileSource, TileWMS as TileWMSSource, Vector as VectorSource, VectorTile as VectorTileSource, XYZ as XYZSource, Tile as TileSource, } from 'ol/source';
31
36
  import Static from 'ol/source/ImageStatic';
32
- import { Circle, Fill, Icon, Stroke, Style } from 'ol/style';
37
+ import { Fill, Icon, Stroke, Style } from 'ol/style';
38
+ import CircleStyle from 'ol/style/Circle';
33
39
  //@ts-expect-error no types for ol-pmtiles
34
40
  import { PMTilesRasterSource, PMTilesVectorSource } from 'ol-pmtiles';
35
41
  import StacLayer from 'ol-stac';
36
42
  import proj4 from 'proj4';
37
43
  import proj4list from 'proj4-list';
38
44
  import * as React from 'react';
39
- import AnnotationFloater from "../annotations/components/AnnotationFloater";
40
45
  import { CommandIDs } from "../constants";
46
+ import AnnotationFloater from "../features/annotations/components/AnnotationFloater";
47
+ import FeatureFloater from "../features/identify/components/FeatureFloater";
48
+ import { getFeatureIdentifier } from "../features/identify/utils/getFeatureIdentifier";
41
49
  import { LoadingOverlay } from "../shared/components/loading";
42
50
  import useMediaQuery from "../shared/hooks/useMediaQuery";
43
- import StatusBar from "../statusbar/StatusBar";
44
- import { debounce, isLightTheme, loadFile, throttle } from "../tools";
51
+ import { markerIcon } from "../shared/icons";
52
+ import { debounce, INTERNAL_PROXY_BASE, isJupyterLite, isLightTheme, loadFile, throttle, } from "../tools";
53
+ import StatusBar from "../workspace/statusbar/StatusBar";
45
54
  import CollaboratorPointers from './CollaboratorPointers';
46
55
  import { FollowIndicator } from './FollowIndicator';
56
+ import { OpenEOTileLayer, OpenEOTileSource } from './OpenEOTileLayer';
47
57
  import TemporalSlider from './TemporalSlider';
48
- import { markerIcon } from '../icons';
49
- import { LeftPanel, RightPanel } from '../panelview';
50
- import { SpectaPanel } from '../panelview/story-maps/SpectaPanel';
58
+ import { createGeoJSONFeaturePatcher, } from './geoJsonFeaturePatch';
59
+ import { ensureHighlightLayer } from '../features/identify/utils/highlightLayer';
60
+ import { buildHighlightStyle } from '../features/identify/utils/highlightStyle';
61
+ import { grammarToOLLayer } from '../features/layers/symbology/grammarToOLLayer';
62
+ import { extractEncodingFieldValues, grammarToOLStyle, } from '../features/layers/symbology/grammarToOLStyle';
63
+ import { DEFAULT_FLAT_STYLE } from '../features/layers/symbology/styleBuilder';
64
+ import { SpectaPanel } from '../features/story/SpectaPanel';
65
+ import { ListStoryStageOverlay } from '../features/story/components/ListStoryStageOverlay';
66
+ import { ListStoryScrollTrackProvider } from '../features/story/context/ListStoryScrollTrackContext';
67
+ import { STORY_TYPE } from '../types';
68
+ import { LeftPanel, MergedPanel, RightPanel } from '../workspace/panels';
69
+ const DRAW_GEOMETRIES = ['Point', 'LineString', 'Polygon'];
70
+ const drawInteractionStyle = new Style({
71
+ fill: new Fill({
72
+ color: 'rgba(255, 255, 255, 0.2)',
73
+ }),
74
+ stroke: new Stroke({
75
+ color: '#ffcc33',
76
+ width: 2,
77
+ }),
78
+ image: new CircleStyle({
79
+ radius: 7,
80
+ fill: new Fill({
81
+ color: '#ffcc33',
82
+ }),
83
+ }),
84
+ });
51
85
  export class MainView extends React.Component {
52
86
  constructor(props) {
53
87
  super(props);
@@ -64,48 +98,8 @@ export class MainView extends React.Component {
64
98
  return transformExtent(extent, view.getProjection(), targetProjection);
65
99
  };
66
100
  this.createSelectInteraction = () => {
67
- const pointStyle = new Style({
68
- image: new Circle({
69
- radius: 5,
70
- fill: new Fill({
71
- color: '#C52707',
72
- }),
73
- stroke: new Stroke({
74
- color: '#171717',
75
- width: 2,
76
- }),
77
- }),
78
- });
79
- const lineStyle = new Style({
80
- stroke: new Stroke({
81
- color: '#171717',
82
- width: 2,
83
- }),
84
- });
85
- const polygonStyle = new Style({
86
- fill: new Fill({ color: '#C5270780' }),
87
- stroke: new Stroke({
88
- color: '#171717',
89
- width: 2,
90
- }),
91
- });
92
- const styleFunction = (feature) => {
93
- var _a;
94
- const geometryType = (_a = feature.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType();
95
- switch (geometryType) {
96
- case 'Point':
97
- case 'MultiPoint':
98
- return pointStyle;
99
- case 'LineString':
100
- case 'MultiLineString':
101
- return lineStyle;
102
- case 'Polygon':
103
- case 'MultiPolygon':
104
- return polygonStyle;
105
- }
106
- };
107
101
  const selectInteraction = new Select({
108
- hitTolerance: 5,
102
+ hitTolerance: 3,
109
103
  multi: true,
110
104
  layers: layer => {
111
105
  var _a, _b;
@@ -115,19 +109,84 @@ export class MainView extends React.Component {
115
109
  return false;
116
110
  }
117
111
  const selectedLayerId = Object.keys(selectedLayers)[0];
118
- return layer === this.getLayer(selectedLayerId);
112
+ const expected = this.getLayer(selectedLayerId);
113
+ if (layer === expected) {
114
+ return true;
115
+ }
116
+ // Grammar multi-layer symbology wraps sub-layers in a LayerGroup.
117
+ // OL Select flattens groups, so we receive leaf layers, not the group.
118
+ if (expected instanceof LayerGroup) {
119
+ return expected.getLayers().getArray().includes(layer);
120
+ }
121
+ return false;
119
122
  },
120
123
  condition: (event) => {
121
124
  return singleClick(event) && this._model.currentMode === 'identifying';
122
125
  },
123
- style: styleFunction,
126
+ // Use the layer's own style so selected features keep their original
127
+ // appearance. Visual highlight feedback comes from _highlightLayer.
128
+ style: null,
124
129
  });
125
130
  selectInteraction.on('select', event => {
131
+ var _a, _b, _c;
126
132
  const identifiedFeatures = [];
133
+ const highlightFeatures = [];
134
+ // Look up the selected layer's style function for adaptive highlights.
135
+ const localState = (_a = this._model) === null || _a === void 0 ? void 0 : _a.sharedModel.awareness.getLocalState();
136
+ const selectedLayers = (_b = localState === null || localState === void 0 ? void 0 : localState.selected) === null || _b === void 0 ? void 0 : _b.value;
137
+ const selectedLayerId = selectedLayers
138
+ ? Object.keys(selectedLayers)[0]
139
+ : undefined;
140
+ const mapLayer = selectedLayerId
141
+ ? this.getLayer(selectedLayerId)
142
+ : undefined;
143
+ // For LayerGroup (multi-layer grammar), collect style functions from
144
+ // all sub-layers so we can match the right one per feature.
145
+ const styleFnCandidates = [];
146
+ if (mapLayer instanceof LayerGroup) {
147
+ for (const sub of mapLayer.getLayers().getArray()) {
148
+ if ('getStyleFunction' in sub) {
149
+ styleFnCandidates.push(sub.getStyleFunction());
150
+ }
151
+ }
152
+ }
153
+ else if (mapLayer && 'getStyleFunction' in mapLayer) {
154
+ styleFnCandidates.push(mapLayer.getStyleFunction());
155
+ }
156
+ const resolution = (_c = this._Map.getView().getResolution()) !== null && _c !== void 0 ? _c : 1;
127
157
  selectInteraction.getFeatures().forEach(feature => {
128
- identifiedFeatures.push(feature.getProperties());
158
+ identifiedFeatures.push({
159
+ feature: feature.getProperties(),
160
+ floaterOpen: false,
161
+ });
162
+ const geom = feature.getGeometry();
163
+ if (geom) {
164
+ const hlFeature = new Feature({ geometry: geom });
165
+ // Try each style function candidate; use the first that resolves
166
+ // a non-empty style array (important for LayerGroup sub-layers
167
+ // where only one sub-layer's style applies to this feature).
168
+ for (const fn of styleFnCandidates) {
169
+ if (!fn) {
170
+ continue;
171
+ }
172
+ const resolved = fn(feature, resolution);
173
+ const styles = Array.isArray(resolved)
174
+ ? resolved
175
+ : resolved
176
+ ? [resolved]
177
+ : [];
178
+ if (styles.length > 0) {
179
+ const gType = geom.getType();
180
+ hlFeature.setStyle(styles.map(s => this._buildHighlightStyle(s, gType)));
181
+ break;
182
+ }
183
+ }
184
+ highlightFeatures.push(hlFeature);
185
+ }
129
186
  });
130
187
  this._model.syncIdentifiedFeatures(identifiedFeatures, this._mainViewModel.id);
188
+ // Sync _highlightLayer with the current selection (clears on deselect).
189
+ this._setHighlightFeatures(highlightFeatures);
131
190
  });
132
191
  this._Map.addInteraction(selectInteraction);
133
192
  };
@@ -210,25 +269,16 @@ export class MainView extends React.Component {
210
269
  rank: 2,
211
270
  });
212
271
  };
272
+ // Used by VectorTileLayer (which shares a flat-style API with Grammar output).
213
273
  this.vectorLayerStyleRuleBuilder = (layer) => {
214
274
  var _a, _b;
215
275
  const layerParams = layer.parameters;
216
- if (!layerParams) {
217
- return;
276
+ const ss = layerParams === null || layerParams === void 0 ? void 0 : layerParams.symbologyState;
277
+ if (!ss || Object.keys(ss).length === 0) {
278
+ return [{ style: DEFAULT_FLAT_STYLE }];
218
279
  }
219
- const defaultStyle = {
220
- 'fill-color': 'rgba(255,255,255,0.4)',
221
- 'stroke-color': '#3399CC',
222
- 'stroke-width': 1.25,
223
- 'circle-radius': 5,
224
- 'circle-fill-color': 'rgba(255,255,255,0.4)',
225
- 'circle-stroke-width': 1.25,
226
- 'circle-stroke-color': '#3399CC',
227
- };
228
- const defaultRules = {
229
- style: defaultStyle,
230
- };
231
- const layerStyle = Object.assign({}, defaultRules);
280
+ const flatStyle = grammarToOLStyle(layerParams.symbologyState, []);
281
+ const layerStyle = { style: flatStyle };
232
282
  if (((_a = layer.filters) === null || _a === void 0 ? void 0 : _a.logicalOp) && ((_b = layer.filters.appliedFilters) === null || _b === void 0 ? void 0 : _b.length) > 0) {
233
283
  const buildCondition = (filter) => {
234
284
  const base = [filter.operator, ['get', filter.feature]];
@@ -236,89 +286,13 @@ export class MainView extends React.Component {
236
286
  ? [...base, filter.betweenMin, filter.betweenMax]
237
287
  : [...base, filter.value];
238
288
  };
239
- let filterExpr;
240
- // 'Any' and 'All' operators require more than one argument
241
- // So if there's only one filter, skip that part to avoid error
242
- if (layer.filters.appliedFilters.length === 1) {
243
- filterExpr = buildCondition(layer.filters.appliedFilters[0]);
244
- }
245
- else {
246
- // Arguments for "Any" and 'All' need to be wrapped in brackets
247
- filterExpr = [
248
- layer.filters.logicalOp,
249
- ...layer.filters.appliedFilters.map(buildCondition),
250
- ];
251
- }
252
- layerStyle.filter = filterExpr;
253
- }
254
- if (!layerParams.color) {
255
- return [layerStyle];
256
- }
257
- const newStyle = Object.assign(Object.assign({}, defaultStyle), layerParams.color);
258
- layerStyle.style = newStyle;
259
- // When fallbackColor[3] === 0, add an OL filter so features that would be
260
- // drawn with a transparent color are excluded from rendering entirely.
261
- // alpha === 0 is the contract: the default TRANSPARENT [0,0,0,0] triggers
262
- // this automatically, and a future dedicated picker option can set it.
263
- const symbologyState = layerParams.symbologyState;
264
- if (Array.isArray(symbologyState === null || symbologyState === void 0 ? void 0 : symbologyState.fallbackColor) &&
265
- symbologyState.fallbackColor[3] === 0) {
266
- let matchFilter;
267
- if (symbologyState.renderType === 'Categorized') {
268
- const fillExpr = layerParams.color['fill-color'];
269
- if (Array.isArray(fillExpr) &&
270
- fillExpr[0] === 'case' &&
271
- fillExpr.length >= 4) {
272
- // Extract conditions from ['case', cond, val, cond, val, ..., fallback],
273
- // skipping any whose matched output color is also fully transparent.
274
- const conditions = [];
275
- for (let i = 1; i < fillExpr.length - 1; i += 2) {
276
- const output = fillExpr[i + 1];
277
- if (Array.isArray(output) && output[3] === 0) {
278
- continue;
279
- }
280
- conditions.push(fillExpr[i]);
281
- }
282
- // If every stop is transparent, use a never-true filter to hide all.
283
- matchFilter =
284
- conditions.length === 0
285
- ? ['==', 0, 1]
286
- : conditions.length === 1
287
- ? conditions[0]
288
- : ['any', ...conditions];
289
- }
290
- }
291
- else if (symbologyState.renderType === 'Graduated') {
292
- const fillExpr = layerParams.color['fill-color'];
293
- // Graduated fill is ['case', ['has', field], interpolateExpr, fallback].
294
- // Features missing the attribute fall through to the fallback, so filter
295
- // them out with the same ['has', field] condition.
296
- if (Array.isArray(fillExpr) &&
297
- fillExpr[0] === 'case' &&
298
- Array.isArray(fillExpr[1]) &&
299
- fillExpr[1][0] === 'has') {
300
- matchFilter = fillExpr[1];
301
- }
302
- }
303
- else if (symbologyState.renderType === 'Canonical') {
304
- const fillExpr = layerParams.color['fill-color'];
305
- // Canonical fill is ['coalesce', ['get', field], fallback].
306
- // Features missing the attribute fall through to the fallback, so filter
307
- // them out with ['has', field]. Note: features that have the field but
308
- // with a non-color value also get the fallback — that gap is accepted.
309
- if (Array.isArray(fillExpr) &&
310
- fillExpr[0] === 'coalesce' &&
311
- Array.isArray(fillExpr[1]) &&
312
- fillExpr[1][0] === 'get') {
313
- matchFilter = ['has', fillExpr[1][1]];
314
- }
315
- }
316
- if (matchFilter) {
317
- // Combine with any existing user-applied filter.
318
- layerStyle.filter = layerStyle.filter
319
- ? ['all', layerStyle.filter, matchFilter]
320
- : matchFilter;
321
- }
289
+ layerStyle.filter =
290
+ layer.filters.appliedFilters.length === 1
291
+ ? buildCondition(layer.filters.appliedFilters[0])
292
+ : [
293
+ layer.filters.logicalOp,
294
+ ...layer.filters.appliedFilters.map(buildCondition),
295
+ ];
322
296
  }
323
297
  return [layerStyle];
324
298
  };
@@ -413,96 +387,36 @@ export class MainView extends React.Component {
413
387
  delete this._originalFeatures[id];
414
388
  }
415
389
  };
416
- this._onClientSharedStateChanged = (sender, clients) => {
417
- var _a, _b, _c;
390
+ this._handleSelectedChanged = () => {
391
+ var _a;
418
392
  const localState = this._model.localState;
419
393
  if (!localState) {
420
394
  return;
421
395
  }
422
- const remoteUser = localState.remoteUser;
423
- // If we are in following mode, we update our position and selection
424
- if (remoteUser) {
425
- const remoteState = clients.get(remoteUser);
426
- if (!remoteState) {
427
- return;
428
- }
429
- if (((_a = remoteState.user) === null || _a === void 0 ? void 0 : _a.username) !== ((_b = this.state.remoteUser) === null || _b === void 0 ? void 0 : _b.username)) {
430
- this.setState(old => (Object.assign(Object.assign({}, old), { remoteUser: remoteState.user })));
431
- }
432
- const remoteViewport = remoteState.viewportState;
433
- if (remoteViewport.value) {
434
- const { x, y } = remoteViewport.value.coordinates;
435
- const zoom = remoteViewport.value.zoom;
436
- this._moveToPosition({ x, y }, zoom, 0);
437
- }
396
+ const selectedLayers = (_a = localState.selected) === null || _a === void 0 ? void 0 : _a.value;
397
+ if (!selectedLayers) {
398
+ return;
438
399
  }
439
- else {
440
- // If we are unfollowing a remote user, we reset our center and zoom to their previous values
441
- if (this.state.remoteUser !== null) {
442
- this.setState(old => (Object.assign(Object.assign({}, old), { remoteUser: null })));
443
- const viewportState = (_c = localState.viewportState) === null || _c === void 0 ? void 0 : _c.value;
444
- if (viewportState) {
445
- this._moveToPosition(viewportState.coordinates, viewportState.zoom);
446
- }
447
- }
400
+ const selectedLayerId = Object.keys(selectedLayers)[0];
401
+ const JGISLayer = this._model.getLayer(selectedLayerId);
402
+ if (!JGISLayer) {
403
+ return;
448
404
  }
449
- // cursors
450
- clients.forEach((client, clientId) => {
451
- var _a;
452
- if (!(client === null || client === void 0 ? void 0 : client.user)) {
453
- return;
454
- }
455
- const pointer = (_a = client.pointer) === null || _a === void 0 ? void 0 : _a.value;
456
- // We already display our own cursor on mouse move
457
- if (this._model.getClientId() === clientId) {
458
- return;
459
- }
460
- const clientPointers = Object.assign({}, this.state.clientPointers);
461
- let currentClientPointer = clientPointers[clientId];
462
- if (pointer) {
463
- const pixel = this._Map.getPixelFromCoordinate([
464
- pointer.coordinates.x,
465
- pointer.coordinates.y,
466
- ]);
467
- const lonLat = toLonLat([pointer.coordinates.x, pointer.coordinates.y]);
468
- if (!currentClientPointer) {
469
- currentClientPointer = {
470
- username: client.user.username,
471
- displayName: client.user.display_name,
472
- color: client.user.color,
473
- coordinates: {
474
- x: pixel[0],
475
- y: pixel[1],
476
- },
477
- lonLat: {
478
- longitude: lonLat[0],
479
- latitude: lonLat[1],
480
- },
481
- };
482
- }
483
- else {
484
- currentClientPointer = Object.assign(Object.assign({}, currentClientPointer), { coordinates: {
485
- x: pixel[0],
486
- y: pixel[1],
487
- }, lonLat: {
488
- longitude: lonLat[0],
489
- latitude: lonLat[1],
490
- } });
491
- }
492
- clientPointers[clientId] = currentClientPointer;
493
- }
494
- else {
495
- delete clientPointers[clientId];
496
- }
497
- this.setState(old => (Object.assign(Object.assign({}, old), { clientPointers })));
498
- });
499
- // Temporal controller bit
500
- // ? There's probably a better way to get changes in the model to trigger react rerenders
501
- const isTemporalControllerActive = localState.isTemporalControllerActive;
502
- if (isTemporalControllerActive !== this.state.displayTemporalController) {
503
- this.setState(old => (Object.assign(Object.assign({}, old), { displayTemporalController: isTemporalControllerActive })));
504
- this._mainViewModel.commands.notifyCommandChanged(CommandIDs.temporalController);
405
+ this._syncVectorDrawingFromSelection(JGISLayer, selectedLayerId);
406
+ };
407
+ this._syncVectorDrawingFromSelection = (layer, selectedLayerId) => {
408
+ const decision = this._getVectorDrawingSelectionDecision(layer, selectedLayerId);
409
+ if (decision.disableEditing) {
410
+ this._model.editingVectorLayer = false;
411
+ this._updateEditingVectorLayer();
412
+ return;
413
+ }
414
+ if (!decision.shouldRebind) {
415
+ return;
505
416
  }
417
+ this._previousDrawLayerID = selectedLayerId;
418
+ this._currentDrawLayerID = selectedLayerId;
419
+ this._editVectorLayer();
506
420
  };
507
421
  this._onSharedModelStateChange = (_, change) => {
508
422
  var _a;
@@ -521,6 +435,10 @@ export class MainView extends React.Component {
521
435
  }
522
436
  }
523
437
  };
438
+ this._handleIdentifiedFeaturesChanged = () => {
439
+ this.setState(old => (Object.assign(Object.assign({}, old), { identifyFeatureFloatersVersion: old.identifyFeatureFloatersVersion + 1 })));
440
+ this._clearHighlightWhenIdentifyDisabled();
441
+ };
524
442
  /**
525
443
  * Handler for when story maps change in the model.
526
444
  * Updates specta state and presentation colors when story data becomes available.
@@ -571,6 +489,7 @@ export class MainView extends React.Component {
571
489
  let accumulatedDeltaY = 0;
572
490
  let scrollContainer = (_b = (_a = this.storyViewerPanelRef.current) === null || _a === void 0 ? void 0 : _a.getScrollContainer()) !== null && _b !== void 0 ? _b : null;
573
491
  const processStoryScrollFrame = () => {
492
+ var _a;
574
493
  this._pendingStoryScrollRafId = null;
575
494
  const currentPanelHandle = this.storyViewerPanelRef.current;
576
495
  if (!currentPanelHandle || !scrollContainer) {
@@ -579,6 +498,12 @@ export class MainView extends React.Component {
579
498
  }
580
499
  const deltaY = accumulatedDeltaY;
581
500
  accumulatedDeltaY = 0;
501
+ const storyType = (_a = this._model.getSelectedStory().story) === null || _a === void 0 ? void 0 : _a.storyType;
502
+ // Don't want to handle next/prev logic in list mode
503
+ if (storyType === STORY_TYPE.verticalScroll) {
504
+ scrollContainer.scrollBy({ top: deltaY });
505
+ return;
506
+ }
582
507
  const isScrollingUp = deltaY < 0;
583
508
  const isScrollingDown = deltaY > 0;
584
509
  const isAtTop = currentPanelHandle.getAtTop();
@@ -652,7 +577,7 @@ export class MainView extends React.Component {
652
577
  jsonData = JSON.parse(data);
653
578
  }
654
579
  catch (e) {
655
- console.warn(`Failed to parse annotation data for ${key}:`, e);
580
+ this._log('warning', `Failed to parse annotation data for ${key}: ${e}`);
656
581
  return;
657
582
  }
658
583
  }
@@ -682,6 +607,9 @@ export class MainView extends React.Component {
682
607
  this._handleWindowResize = () => {
683
608
  // TODO SOMETHING
684
609
  };
610
+ this._handleSegmentTransitionChange = (payload) => {
611
+ this.setState({ segmentTransition: payload });
612
+ };
685
613
  this._handleSpectaTouchStart = (e) => {
686
614
  if (e.touches.length > 0) {
687
615
  this._spectaTouchStartX = e.touches[0].clientX;
@@ -708,6 +636,148 @@ export class MainView extends React.Component {
708
636
  this._model.setCurrentSegmentIndex(current + 1);
709
637
  }
710
638
  };
639
+ this._handleDrawGeometryTypeChange = (
640
+ /* handle with the change of geometry and instantiate new draw interaction and other ones accordingly*/
641
+ event) => {
642
+ const drawGeometryLabel = event.target.value;
643
+ this._currentDrawGeometry = drawGeometryLabel;
644
+ this._updateInteractions();
645
+ this._updateDrawSource();
646
+ this.setState(old => (Object.assign(Object.assign({}, old), { drawGeometryLabel })));
647
+ };
648
+ this._getVectorSourceFromLayerID = (layerID) => {
649
+ /* get the OpenLayers VectorSource corresponding to the JGIS currentDrawLayerID */
650
+ const layers = this._Map.getLayers();
651
+ const layerArray = layers.getArray();
652
+ const matchingLayer = layerArray.find(layer => layer.get('id') === layerID);
653
+ const source = matchingLayer === null || matchingLayer === void 0 ? void 0 : matchingLayer.get('source');
654
+ this._currentVectorSource = source;
655
+ return this._currentVectorSource;
656
+ };
657
+ this._getDrawSourceFromSelectedLayer = () => {
658
+ var _a, _b, _c, _d;
659
+ const selectedLayers = (_c = (_b = (_a = this._model) === null || _a === void 0 ? void 0 : _a.sharedModel.awareness.getLocalState()) === null || _b === void 0 ? void 0 : _b.selected) === null || _c === void 0 ? void 0 : _c.value;
660
+ if (!selectedLayers) {
661
+ return;
662
+ }
663
+ const selectedLayerID = Object.keys(selectedLayers)[0];
664
+ this._currentDrawLayerID = selectedLayerID;
665
+ const JGISLayer = this._model.getLayer(selectedLayerID);
666
+ this._currentDrawSourceID = (_d = JGISLayer === null || JGISLayer === void 0 ? void 0 : JGISLayer.parameters) === null || _d === void 0 ? void 0 : _d.source;
667
+ if (this._currentDrawSourceID) {
668
+ this._currentDrawSource = this._model.getSource(this._currentDrawSourceID);
669
+ }
670
+ };
671
+ this._onVectorSourceChange = () => {
672
+ if (!this._currentVectorSource ||
673
+ !this._currentDrawSource ||
674
+ !this._currentDrawSourceID) {
675
+ return;
676
+ }
677
+ const geojsonWriter = new GeoJSON({
678
+ featureProjection: this._Map.getView().getProjection(),
679
+ });
680
+ const features = this._currentVectorSource
681
+ .getFeatures()
682
+ .map(feature => geojsonWriter.writeFeatureObject(feature));
683
+ const updatedData = {
684
+ type: 'FeatureCollection',
685
+ features: features,
686
+ };
687
+ const updatedJGISLayerSource = {
688
+ name: this._currentDrawSource.name,
689
+ type: this._currentDrawSource.type,
690
+ parameters: {
691
+ data: updatedData,
692
+ },
693
+ };
694
+ this._currentDrawSource = updatedJGISLayerSource;
695
+ this._model.sharedModel.updateSource(this._currentDrawSourceID, updatedJGISLayerSource);
696
+ };
697
+ this._updateDrawSource = () => {
698
+ if (this._currentVectorSource) {
699
+ this._currentVectorSource.on('change', this._onVectorSourceChange);
700
+ }
701
+ };
702
+ this._updateInteractions = () => {
703
+ if (this._draw) {
704
+ this._removeDrawInteraction();
705
+ }
706
+ if (this._select) {
707
+ this._removeSelectInteraction();
708
+ }
709
+ if (this._modify) {
710
+ this._removeModifyInteraction();
711
+ }
712
+ if (this._snap) {
713
+ this._removeSnapInteraction();
714
+ }
715
+ this._draw = new Draw({
716
+ style: drawInteractionStyle,
717
+ type: this._currentDrawGeometry,
718
+ source: this._currentVectorSource,
719
+ });
720
+ this._draw.on('drawend', this._handleDrawEnd);
721
+ this._select = new Select();
722
+ this._modify = new Modify({
723
+ features: this._select.getFeatures(),
724
+ });
725
+ this._snap = new Snap({
726
+ source: this._currentVectorSource,
727
+ });
728
+ this._Map.addInteraction(this._draw);
729
+ this._Map.addInteraction(this._select);
730
+ this._Map.addInteraction(this._modify);
731
+ this._Map.addInteraction(this._snap);
732
+ this._draw.setActive(true);
733
+ this._select.setActive(false);
734
+ this._modify.setActive(false);
735
+ this._snap.setActive(true);
736
+ };
737
+ this._handleDrawEnd = (event) => {
738
+ const feature = event.feature;
739
+ feature.set('_id', UUID.uuid4());
740
+ feature.set('_createdAt', new Date().toISOString());
741
+ feature.set('_creatorClientId', this._model.getClientId().toString());
742
+ feature.set('_fromDrawTool', true);
743
+ feature.set('Label', 'New Label');
744
+ };
745
+ this._editVectorLayer = () => {
746
+ this._getDrawSourceFromSelectedLayer();
747
+ if (!this._currentDrawLayerID) {
748
+ return;
749
+ }
750
+ this._currentVectorSource = this._getVectorSourceFromLayerID(this._currentDrawLayerID);
751
+ if (!this._currentVectorSource || !this._currentDrawGeometry) {
752
+ return;
753
+ }
754
+ this._updateInteractions(); /* remove previous interactions and instantiate new ones */
755
+ this._updateDrawSource(); /*add new features, update source and get changes reported to the JGIS Document in geoJSON format */
756
+ };
757
+ this._removeDrawInteraction = () => {
758
+ this._draw.setActive(false);
759
+ this._Map.removeInteraction(this._draw);
760
+ };
761
+ this._removeSelectInteraction = () => {
762
+ this._select.setActive(false);
763
+ this._Map.removeInteraction(this._select);
764
+ };
765
+ this._removeSnapInteraction = () => {
766
+ this._snap.setActive(false);
767
+ this._Map.removeInteraction(this._snap);
768
+ };
769
+ this._removeModifyInteraction = () => {
770
+ this._modify.setActive(false);
771
+ this._Map.removeInteraction(this._modify);
772
+ };
773
+ /**
774
+ * Shared source update wrapper for child components that need to mutate a
775
+ * source and refresh corresponding map layers.
776
+ */
777
+ this.persistAndRefreshSource = async (id, source) => {
778
+ this._model.sharedModel.updateSource(id, source);
779
+ await this.updateSource(id, source);
780
+ };
711
781
  this._isPositionInitialized = false;
712
782
  this.divRef = React.createRef(); // Reference of render div
713
783
  this.mainViewRef = React.createRef();
@@ -717,6 +787,9 @@ export class MainView extends React.Component {
717
787
  this._ready = false;
718
788
  this._sourceToLayerMap = new Map();
719
789
  this._originalFeatures = {};
790
+ this._highlightLayerRef = { current: null };
791
+ this._addLayerForPanels = (id, layer, index) => this.addLayer(id, layer, index);
792
+ this._removeLayerForPanels = (id) => this.removeLayer(id);
720
793
  this._featurePropertyCache = new Map();
721
794
  this._isSpectaPresentationInitialized = false;
722
795
  this._storyScrollHandler = null;
@@ -725,6 +798,7 @@ export class MainView extends React.Component {
725
798
  this._state = props.state;
726
799
  this._formSchemaRegistry = props.formSchemaRegistry;
727
800
  this._annotationModel = props.annotationModel;
801
+ this._loggerRegistry = props.loggerRegistry;
728
802
  // Enforce the map to take the full available width in the case of Jupyter Notebook viewer
729
803
  const el = document.getElementById('main-panel');
730
804
  if (el) {
@@ -747,31 +821,39 @@ export class MainView extends React.Component {
747
821
  this._mainViewModel = this.props.viewModel;
748
822
  this._mainViewModel.viewSettingChanged.connect(this._onViewChanged, this);
749
823
  this._model = this._mainViewModel.jGISModel;
824
+ this._patchGeoJSONFeatureProperties = createGeoJSONFeaturePatcher({
825
+ model: this._model,
826
+ persistAndRefreshSource: this.persistAndRefreshSource,
827
+ });
750
828
  this._model.themeChanged.connect(this._handleThemeChange, this);
751
829
  this._model.sharedOptionsChanged.connect(this._onSharedOptionsChanged, this);
752
- this._model.clientStateChanged.connect(this._onClientSharedStateChanged, this);
830
+ this._model.temporalControllerActiveChanged.connect(this._handleTemporalControllerActiveChanged, this);
831
+ const remoteUserSignals = [
832
+ this._model.remoteUserChanged,
833
+ this._model.viewportStateChanged,
834
+ ];
835
+ remoteUserSignals.forEach(signal => signal.connect(this._handleRemoteUserChanged, this));
836
+ this._model.pointerChanged.connect(this._handlePointerChanged, this);
837
+ this._model.selectedChanged.connect(this._handleTemporalControllerActiveChanged, this);
838
+ this._model.selectedChanged.connect(this._handleSelectedChanged, this);
753
839
  this._model.sharedLayersChanged.connect(this._onLayersChanged, this);
754
840
  this._model.sharedLayerTreeChanged.connect(this._onLayerTreeChange, this);
755
841
  this._model.sharedSourcesChanged.connect(this._onSourcesChange, this);
756
842
  this._model.sharedModel.changed.connect(this._onSharedModelStateChange);
757
843
  this._model.sharedMetadataChanged.connect(this._onSharedMetadataChanged, this);
844
+ this._model.identifiedFeaturesChanged.connect(this._handleIdentifiedFeaturesChanged, this);
758
845
  this._model.zoomToPositionSignal.connect(this._onZoomToPosition, this);
759
846
  this._model.settingsChanged.connect(this._onSettingsChanged, this);
760
847
  this._model.updateLayerSignal.connect(this._triggerLayerUpdate, this);
761
848
  this._model.addFeatureAsMsSignal.connect(this._convertFeatureToMs, this);
762
849
  this._model.geolocationChanged.connect(this._handleGeolocationChanged, this);
850
+ // Keep draw editing UI/interactions in sync with the shared editing mode.
851
+ this._model.editingVectorLayerChanged.connect(this._updateEditingVectorLayer, this);
763
852
  this._model.flyToGeometrySignal.connect(this.flyToGeometry, this);
764
853
  this._model.highlightFeatureSignal.connect(this.highlightFeatureOnMap, this);
765
854
  Promise.resolve().then(() => {
766
855
  this._syncSettingsFromRegistry();
767
856
  });
768
- // Watch isIdentifying and clear the highlight when Identify Tool is turned off
769
- this._model.sharedModel.awareness.on('change', () => {
770
- var _a;
771
- if (this._model.currentMode !== 'identifying' && this._highlightLayer) {
772
- (_a = this._highlightLayer.getSource()) === null || _a === void 0 ? void 0 : _a.clear();
773
- }
774
- });
775
857
  this.state = {
776
858
  id: this._mainViewModel.id,
777
859
  lightTheme: isLightTheme(),
@@ -784,9 +866,13 @@ export class MainView extends React.Component {
784
866
  loadingErrors: [],
785
867
  displayTemporalController: false,
786
868
  filterStates: {},
869
+ editingVectorLayer: false,
870
+ drawGeometryLabel: '',
787
871
  jgisSettings: this._model.jgisSettings,
788
872
  isSpectaPresentation: this._model.isSpectaMode(),
789
873
  initialLayersReady: false,
874
+ identifyFeatureFloatersVersion: 0,
875
+ segmentTransition: null,
790
876
  };
791
877
  this._sources = [];
792
878
  this._loadingLayers = new Set();
@@ -798,6 +884,10 @@ export class MainView extends React.Component {
798
884
  }
799
885
  async componentDidMount() {
800
886
  var _a;
887
+ if (this._loggerRegistry) {
888
+ const logger = this._loggerRegistry.getLogger(this._model.filePath);
889
+ logger.level = 'debug';
890
+ }
801
891
  window.addEventListener('resize', this._handleWindowResize);
802
892
  const options = this._model.getOptions();
803
893
  const projection = (_a = options.projection) !== null && _a !== void 0 ? _a : DEFAULT_PROJECTION;
@@ -806,6 +896,10 @@ export class MainView extends React.Component {
806
896
  : [0, 0];
807
897
  const zoom = options.zoom !== undefined ? options.zoom : 1;
808
898
  await this.generateMap(center, zoom, projection);
899
+ this._handleRemoteUserChanged();
900
+ this._handlePointerChanged();
901
+ this._handleTemporalControllerActiveChanged();
902
+ this._handleSelectedChanged();
809
903
  this._mainViewModel.initSignal();
810
904
  if (window.jupytergisMaps !== undefined && this._documentPath) {
811
905
  window.jupytergisMaps[this._documentPath] = this._Map;
@@ -828,7 +922,16 @@ export class MainView extends React.Component {
828
922
  this._model.themeChanged.disconnect(this._handleThemeChange, this);
829
923
  this._model.settingsChanged.disconnect(this._onSettingsChanged, this);
830
924
  this._model.sharedOptionsChanged.disconnect(this._onSharedOptionsChanged, this);
831
- this._model.clientStateChanged.disconnect(this._onClientSharedStateChanged, this);
925
+ this._model.temporalControllerActiveChanged.disconnect(this._handleTemporalControllerActiveChanged, this);
926
+ const remoteUserSignals = [
927
+ this._model.remoteUserChanged,
928
+ this._model.viewportStateChanged,
929
+ ];
930
+ remoteUserSignals.forEach(signal => signal.disconnect(this._handleRemoteUserChanged, this));
931
+ this._model.pointerChanged.disconnect(this._handlePointerChanged, this);
932
+ this._model.selectedChanged.disconnect(this._handleTemporalControllerActiveChanged, this);
933
+ this._model.selectedChanged.disconnect(this._handleSelectedChanged, this);
934
+ this._model.identifiedFeaturesChanged.disconnect(this._handleIdentifiedFeaturesChanged, this);
832
935
  // Clean up story scroll listener
833
936
  this._cleanupStoryScrollListener();
834
937
  this._mainViewModel.dispose();
@@ -892,9 +995,7 @@ export class MainView extends React.Component {
892
995
  this._Map.addInteraction(dragAndDropInteraction);
893
996
  this.createSelectInteraction();
894
997
  const view = this._Map.getView();
895
- view.on('change:center', () => this._updateCenter());
896
- // TODO: Note for the future, will need to update listeners if view changes
897
- view.on('change:center', throttle(() => {
998
+ const syncViewportThrottled = throttle(() => {
898
999
  var _a;
899
1000
  // Not syncing center if following someone else
900
1001
  if ((_a = this._model.localState) === null || _a === void 0 ? void 0 : _a.remoteUser) {
@@ -906,18 +1007,30 @@ export class MainView extends React.Component {
906
1007
  if (!center || !zoom) {
907
1008
  return;
908
1009
  }
1010
+ const currentExtent = view.calculateExtent(this._Map.getSize());
909
1011
  this._model.syncViewport({
910
1012
  coordinates: {
911
1013
  x: center[0],
912
1014
  y: center[1],
913
1015
  },
914
1016
  zoom,
1017
+ extent: [
1018
+ currentExtent[0],
1019
+ currentExtent[1],
1020
+ currentExtent[2],
1021
+ currentExtent[3],
1022
+ ],
915
1023
  }, this._mainViewModel.id);
916
- }));
1024
+ }, 200);
1025
+ view.on('change:center', () => {
1026
+ this._updateCenter();
1027
+ syncViewportThrottled();
1028
+ });
917
1029
  this._Map.on('postrender', () => {
918
1030
  if (this.state.annotations) {
919
1031
  this._updateAnnotation();
920
1032
  }
1033
+ this._updateFeatureFloaters();
921
1034
  });
922
1035
  this._Map.on('moveend', () => {
923
1036
  var _a;
@@ -981,241 +1094,359 @@ export class MainView extends React.Component {
981
1094
  * @param source - the source object.
982
1095
  */
983
1096
  async addSource(id, source) {
984
- var _a, _b, _c;
1097
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j;
1098
+ this._log('info', `Loading source "${(_a = source.name) !== null && _a !== void 0 ? _a : id}" (${source.type})`);
985
1099
  let newSource;
986
- switch (source.type) {
987
- case 'RasterSource': {
988
- const sourceParameters = source.parameters;
989
- const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
990
- sourceParameters.url.endsWith('pmtiles.gz');
991
- const url = this.computeSourceUrl(source);
992
- if (!pmTiles) {
993
- newSource = new XYZSource({
1100
+ try {
1101
+ switch (source.type) {
1102
+ case 'RasterSource': {
1103
+ const sourceParameters = source.parameters;
1104
+ const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
1105
+ sourceParameters.url.endsWith('pmtiles.gz');
1106
+ const url = this.computeSourceUrl(source);
1107
+ if (!pmTiles) {
1108
+ newSource = new XYZSource({
1109
+ interpolate: sourceParameters.interpolate,
1110
+ attributions: sourceParameters.attribution,
1111
+ minZoom: sourceParameters.minZoom,
1112
+ maxZoom: sourceParameters.maxZoom,
1113
+ tileSize: 256,
1114
+ url: url,
1115
+ });
1116
+ }
1117
+ else {
1118
+ newSource = new PMTilesRasterSource({
1119
+ interpolate: sourceParameters.interpolate,
1120
+ attributions: sourceParameters.attribution,
1121
+ tileSize: 256,
1122
+ url: url,
1123
+ });
1124
+ }
1125
+ break;
1126
+ }
1127
+ case 'RasterDemSource': {
1128
+ const sourceParameters = source.parameters;
1129
+ newSource = new ImageTileSource({
994
1130
  interpolate: sourceParameters.interpolate,
1131
+ url: this.computeSourceUrl(source),
995
1132
  attributions: sourceParameters.attribution,
996
- minZoom: sourceParameters.minZoom,
997
- maxZoom: sourceParameters.maxZoom,
998
- tileSize: 256,
999
- url: url,
1000
1133
  });
1134
+ break;
1001
1135
  }
1002
- else {
1003
- newSource = new PMTilesRasterSource({
1004
- interpolate: sourceParameters.interpolate,
1005
- attributions: sourceParameters.attribution,
1006
- tileSize: 256,
1007
- url: url,
1136
+ case 'VectorTileSource': {
1137
+ const sourceParameters = source.parameters;
1138
+ const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
1139
+ sourceParameters.url.endsWith('pmtiles.gz');
1140
+ const url = this.computeSourceUrl(source);
1141
+ if (!pmTiles) {
1142
+ const vtSourceOptions = {
1143
+ attributions: sourceParameters.attribution,
1144
+ minZoom: sourceParameters.minZoom,
1145
+ maxZoom: sourceParameters.maxZoom,
1146
+ url: url,
1147
+ format: new MVT({
1148
+ featureClass: RenderFeature,
1149
+ }),
1150
+ };
1151
+ if (sourceParameters.useProxy) {
1152
+ const extraHeaders = (_b = sourceParameters.httpHeaders) !== null && _b !== void 0 ? _b : {};
1153
+ const headersParam = Object.keys(extraHeaders).length > 0
1154
+ ? `&headers=${encodeURIComponent(JSON.stringify(extraHeaders))}`
1155
+ : '';
1156
+ const proxyBase = isJupyterLite()
1157
+ ? `${this._model.jgisSettings.proxyUrl}/`
1158
+ : `${INTERNAL_PROXY_BASE}`;
1159
+ vtSourceOptions.tileLoadFunction = (tile, tileUrl) => {
1160
+ const vtTile = tile;
1161
+ const proxyUrl = `${proxyBase}?url=${encodeURIComponent(tileUrl)}${headersParam}`;
1162
+ vtTile.setLoader((extent, _resolution, projection) => {
1163
+ return fetch(proxyUrl)
1164
+ .then(response => {
1165
+ if (!response.ok) {
1166
+ throw new Error(`Tile proxy request failed: ${response.status} ${response.statusText}`);
1167
+ }
1168
+ return response.arrayBuffer();
1169
+ })
1170
+ .then(data => {
1171
+ const features = vtTile.getFormat().readFeatures(data, {
1172
+ extent,
1173
+ featureProjection: projection,
1174
+ });
1175
+ vtTile.setFeatures(features);
1176
+ this._log('debug', `Proxy tile loaded: ${tileUrl}`);
1177
+ return features;
1178
+ })
1179
+ .catch((err) => {
1180
+ this._log('error', `Proxy tile error for ${tileUrl}: ${err.message}`);
1181
+ tile.setState(TileState.ERROR);
1182
+ return [];
1183
+ });
1184
+ });
1185
+ };
1186
+ }
1187
+ newSource = new VectorTileSource(vtSourceOptions);
1188
+ }
1189
+ else {
1190
+ newSource = new PMTilesVectorSource({
1191
+ attributions: sourceParameters.attribution,
1192
+ url: url,
1193
+ });
1194
+ }
1195
+ newSource.on('tileloadend', (event) => {
1196
+ const tile = event.tile;
1197
+ const features = tile.getFeatures();
1198
+ if (features && features.length > 0) {
1199
+ this._model.syncTileFeatures({
1200
+ sourceId: id,
1201
+ features,
1202
+ });
1203
+ }
1008
1204
  });
1205
+ break;
1009
1206
  }
1010
- break;
1011
- }
1012
- case 'RasterDemSource': {
1013
- const sourceParameters = source.parameters;
1014
- newSource = new ImageTileSource({
1015
- interpolate: sourceParameters.interpolate,
1016
- url: this.computeSourceUrl(source),
1017
- attributions: sourceParameters.attribution,
1018
- });
1019
- break;
1020
- }
1021
- case 'VectorTileSource': {
1022
- const sourceParameters = source.parameters;
1023
- const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
1024
- sourceParameters.url.endsWith('pmtiles.gz');
1025
- const url = this.computeSourceUrl(source);
1026
- if (!pmTiles) {
1027
- newSource = new VectorTileSource({
1028
- attributions: sourceParameters.attribution,
1029
- minZoom: sourceParameters.minZoom,
1030
- maxZoom: sourceParameters.maxZoom,
1031
- url: url,
1032
- format: new MVT({
1033
- featureClass: RenderFeature,
1034
- }),
1207
+ case 'OpenEOTileSource': {
1208
+ const sourceParameters = source.parameters;
1209
+ newSource = new OpenEOTileSource({
1210
+ connectionInfo: {
1211
+ url: sourceParameters.serverUrl,
1212
+ authBearer: sourceParameters.authBearer,
1213
+ },
1214
+ processGraph: sourceParameters.processGraph,
1035
1215
  });
1216
+ break;
1036
1217
  }
1037
- else {
1038
- newSource = new PMTilesVectorSource({
1039
- attributions: sourceParameters.attribution,
1040
- url: url,
1218
+ case 'GeoJSONSource': {
1219
+ const data = ((_c = source.parameters) === null || _c === void 0 ? void 0 : _c.data) ||
1220
+ (await loadFile({
1221
+ filepath: (_d = source.parameters) === null || _d === void 0 ? void 0 : _d.path,
1222
+ type: 'GeoJSONSource',
1223
+ model: this._model,
1224
+ }));
1225
+ const format = new GeoJSON({
1226
+ featureProjection: this._Map.getView().getProjection(),
1227
+ });
1228
+ const featureArray = format.readFeatures(data, {
1229
+ featureProjection: this._Map.getView().getProjection(),
1230
+ });
1231
+ const featureCollection = new Collection(featureArray);
1232
+ featureCollection.forEach(feature => {
1233
+ feature.setId(getUid(feature));
1041
1234
  });
1235
+ newSource = new VectorSource({
1236
+ features: featureCollection,
1237
+ });
1238
+ break;
1042
1239
  }
1043
- newSource.on('tileloadend', (event) => {
1044
- const tile = event.tile;
1045
- const features = tile.getFeatures();
1046
- if (features && features.length > 0) {
1047
- this._model.syncTileFeatures({
1048
- sourceId: id,
1049
- features,
1050
- });
1051
- }
1052
- });
1053
- break;
1054
- }
1055
- case 'GeoJSONSource': {
1056
- const data = ((_a = source.parameters) === null || _a === void 0 ? void 0 : _a.data) ||
1057
- (await loadFile({
1058
- filepath: (_b = source.parameters) === null || _b === void 0 ? void 0 : _b.path,
1059
- type: 'GeoJSONSource',
1240
+ case 'ShapefileSource': {
1241
+ const parameters = source.parameters;
1242
+ const geojson = await loadFile({
1243
+ filepath: parameters.path,
1244
+ type: 'ShapefileSource',
1060
1245
  model: this._model,
1246
+ });
1247
+ const geojsonData = Array.isArray(geojson) ? geojson[0] : geojson;
1248
+ const format = new GeoJSON();
1249
+ newSource = new VectorSource({
1250
+ features: format.readFeatures(geojsonData, {
1251
+ dataProjection: 'EPSG:4326',
1252
+ featureProjection: this._Map.getView().getProjection(),
1253
+ }),
1254
+ });
1255
+ break;
1256
+ }
1257
+ case 'ImageSource': {
1258
+ const sourceParameters = source.parameters;
1259
+ // Convert lon/lat array to extent
1260
+ // Get lon/lat from source coordinates
1261
+ const leftSide = Math.min(...sourceParameters.coordinates.map(corner => corner[0]));
1262
+ const bottomSide = Math.min(...sourceParameters.coordinates.map(corner => corner[1]));
1263
+ const rightSide = Math.max(...sourceParameters.coordinates.map(corner => corner[0]));
1264
+ const topSide = Math.max(...sourceParameters.coordinates.map(corner => corner[1]));
1265
+ // Convert lon/lat to OpenLayer coordinates
1266
+ const topLeft = fromLonLat([leftSide, topSide]);
1267
+ const bottomRight = fromLonLat([rightSide, bottomSide]);
1268
+ // Get extent from coordinates
1269
+ const minX = topLeft[0];
1270
+ const maxY = topLeft[1];
1271
+ const maxX = bottomRight[0];
1272
+ const minY = bottomRight[1];
1273
+ const extent = [minX, minY, maxX, maxY];
1274
+ const imageUrl = await loadFile({
1275
+ filepath: sourceParameters.path,
1276
+ type: 'ImageSource',
1277
+ model: this._model,
1278
+ });
1279
+ newSource = new Static({
1280
+ interpolate: sourceParameters.interpolate,
1281
+ imageExtent: extent,
1282
+ url: imageUrl,
1283
+ crossOrigin: '',
1284
+ });
1285
+ break;
1286
+ }
1287
+ case 'VideoSource': {
1288
+ this._log('warning', 'Video Tiles not supported with Open Layers');
1289
+ break;
1290
+ }
1291
+ case 'GeoTiffSource': {
1292
+ const sourceParameters = source.parameters;
1293
+ const addNoData = (url) => {
1294
+ return Object.assign(Object.assign({}, url), { nodata: 0 });
1295
+ };
1296
+ const sources = await Promise.all(sourceParameters.urls.map(async (sourceInfo) => {
1297
+ var _a, _b, _c, _d;
1298
+ const isRemote = ((_a = sourceInfo.url) === null || _a === void 0 ? void 0 : _a.startsWith('http://')) ||
1299
+ ((_b = sourceInfo.url) === null || _b === void 0 ? void 0 : _b.startsWith('https://'));
1300
+ const isDataUrl = (_c = sourceInfo.url) === null || _c === void 0 ? void 0 : _c.startsWith('data:');
1301
+ if (isRemote) {
1302
+ return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, url: sourceInfo.url });
1303
+ }
1304
+ else if (isDataUrl) {
1305
+ // Inline base64 GeoTIFF embedded in the .jGIS doc.
1306
+ const blob = await (await fetch(sourceInfo.url)).blob();
1307
+ return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, url: URL.createObjectURL(blob) });
1308
+ }
1309
+ else {
1310
+ const geotiff = await loadFile({
1311
+ filepath: (_d = sourceInfo.url) !== null && _d !== void 0 ? _d : '',
1312
+ type: 'GeoTiffSource',
1313
+ model: this._model,
1314
+ });
1315
+ return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, geotiff, url: URL.createObjectURL(geotiff.file) });
1316
+ }
1061
1317
  }));
1062
- const format = new GeoJSON({
1063
- featureProjection: this._Map.getView().getProjection(),
1064
- });
1065
- const featureArray = format.readFeatures(data, {
1066
- featureProjection: this._Map.getView().getProjection(),
1067
- });
1068
- const featureCollection = new Collection(featureArray);
1069
- featureCollection.forEach(feature => {
1070
- feature.setId(getUid(feature));
1071
- });
1072
- newSource = new VectorSource({
1073
- features: featureCollection,
1074
- });
1075
- break;
1076
- }
1077
- case 'ShapefileSource': {
1078
- const parameters = source.parameters;
1079
- const geojson = await loadFile({
1080
- filepath: parameters.path,
1081
- type: 'ShapefileSource',
1082
- model: this._model,
1083
- });
1084
- const geojsonData = Array.isArray(geojson) ? geojson[0] : geojson;
1085
- const format = new GeoJSON();
1086
- newSource = new VectorSource({
1087
- features: format.readFeatures(geojsonData, {
1088
- dataProjection: 'EPSG:4326',
1089
- featureProjection: this._Map.getView().getProjection(),
1090
- }),
1091
- });
1092
- break;
1093
- }
1094
- case 'ImageSource': {
1095
- const sourceParameters = source.parameters;
1096
- // Convert lon/lat array to extent
1097
- // Get lon/lat from source coordinates
1098
- const leftSide = Math.min(...sourceParameters.coordinates.map(corner => corner[0]));
1099
- const bottomSide = Math.min(...sourceParameters.coordinates.map(corner => corner[1]));
1100
- const rightSide = Math.max(...sourceParameters.coordinates.map(corner => corner[0]));
1101
- const topSide = Math.max(...sourceParameters.coordinates.map(corner => corner[1]));
1102
- // Convert lon/lat to OpenLayer coordinates
1103
- const topLeft = fromLonLat([leftSide, topSide]);
1104
- const bottomRight = fromLonLat([rightSide, bottomSide]);
1105
- // Get extent from coordinates
1106
- const minX = topLeft[0];
1107
- const maxY = topLeft[1];
1108
- const maxX = bottomRight[0];
1109
- const minY = bottomRight[1];
1110
- const extent = [minX, minY, maxX, maxY];
1111
- const imageUrl = await loadFile({
1112
- filepath: sourceParameters.path,
1113
- type: 'ImageSource',
1114
- model: this._model,
1115
- });
1116
- newSource = new Static({
1117
- interpolate: sourceParameters.interpolate,
1118
- imageExtent: extent,
1119
- url: imageUrl,
1120
- crossOrigin: '',
1121
- });
1122
- break;
1123
- }
1124
- case 'VideoSource': {
1125
- console.warn('Video Tiles not supported with Open Layers');
1126
- break;
1127
- }
1128
- case 'GeoTiffSource': {
1129
- const sourceParameters = source.parameters;
1130
- const addNoData = (url) => {
1131
- return Object.assign(Object.assign({}, url), { nodata: 0 });
1132
- };
1133
- const sources = await Promise.all(sourceParameters.urls.map(async (sourceInfo) => {
1134
- var _a, _b, _c;
1135
- const isRemote = ((_a = sourceInfo.url) === null || _a === void 0 ? void 0 : _a.startsWith('http://')) ||
1136
- ((_b = sourceInfo.url) === null || _b === void 0 ? void 0 : _b.startsWith('https://'));
1137
- if (isRemote) {
1138
- return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, url: sourceInfo.url });
1318
+ newSource = new GeoTIFFSource({
1319
+ interpolate: sourceParameters.interpolate,
1320
+ sources,
1321
+ normalize: sourceParameters.normalize,
1322
+ wrapX: sourceParameters.wrapX,
1323
+ });
1324
+ break;
1325
+ }
1326
+ case 'GeoPackageVectorSource': {
1327
+ const sourceParameters = source.parameters;
1328
+ if (!sourceParameters) {
1329
+ throw new Error('GeoPackageSource has no parameters');
1139
1330
  }
1140
- else {
1141
- const geotiff = await loadFile({
1142
- filepath: (_c = sourceInfo.url) !== null && _c !== void 0 ? _c : '',
1143
- type: 'GeoTiffSource',
1144
- model: this._model,
1145
- });
1146
- return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, geotiff, url: URL.createObjectURL(geotiff.file) });
1331
+ const tableMap = await loadFile({
1332
+ filepath: sourceParameters.path,
1333
+ type: 'GeoPackageVectorSource',
1334
+ model: this._model,
1335
+ });
1336
+ const table = tableMap[sourceParameters.tables];
1337
+ const vectorSource = table.source;
1338
+ vectorSource['projection'] = getProjection(sourceParameters.projection);
1339
+ newSource = vectorSource;
1340
+ break;
1341
+ }
1342
+ case 'GeoPackageRasterSource': {
1343
+ const sourceParameters = source.parameters;
1344
+ if (!sourceParameters) {
1345
+ throw new Error('GeoPackageSource has no parameters');
1147
1346
  }
1148
- }));
1149
- newSource = new GeoTIFFSource({
1150
- interpolate: sourceParameters.interpolate,
1151
- sources,
1152
- normalize: sourceParameters.normalize,
1153
- wrapX: sourceParameters.wrapX,
1154
- });
1155
- break;
1156
- }
1157
- case 'GeoParquetSource': {
1158
- const parameters = source.parameters;
1159
- const geojson = await loadFile({
1160
- filepath: parameters.path,
1161
- type: 'GeoParquetSource',
1162
- model: this._model,
1163
- });
1164
- const geojsonData = Array.isArray(geojson) ? geojson[0] : geojson;
1165
- const format = new GeoJSON();
1166
- newSource = new VectorSource({
1167
- features: format.readFeatures(geojsonData, {
1168
- dataProjection: parameters.projection,
1169
- featureProjection: this._Map.getView().getProjection(),
1170
- }),
1171
- });
1172
- break;
1173
- }
1174
- case 'MarkerSource': {
1175
- const parameters = source.parameters;
1176
- const point = new Point(parameters.feature.coords);
1177
- const marker = new Feature({
1178
- type: 'icon',
1179
- geometry: point,
1180
- });
1181
- // Replace color placeholder in SVG with the parameter color
1182
- const markerColor = parameters.color || '#3463a0';
1183
- const svgString = markerIcon.svgstr
1184
- .replace('{{COLOR}}', markerColor)
1185
- .replace('<svg', '<svg width="128" height="128"');
1186
- const iconStyle = new Style({
1187
- image: new Icon({
1188
- src: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`,
1189
- scale: 0.25,
1190
- anchor: [0.5, 1],
1191
- anchorXUnits: 'fraction',
1192
- anchorYUnits: 'fraction',
1193
- }),
1194
- });
1195
- marker.setStyle(iconStyle);
1196
- newSource = new VectorSource({
1197
- features: [marker],
1198
- });
1199
- break;
1200
- }
1201
- case 'WmsTileSource': {
1202
- const sourceParameters = source.parameters;
1203
- const url = sourceParameters.url;
1204
- const selectedLayer = (_c = sourceParameters === null || sourceParameters === void 0 ? void 0 : sourceParameters.params) === null || _c === void 0 ? void 0 : _c.layers;
1205
- newSource = new TileWMSSource({
1206
- attributions: sourceParameters === null || sourceParameters === void 0 ? void 0 : sourceParameters.attribution,
1207
- url,
1208
- params: {
1209
- LAYERS: selectedLayer,
1210
- TILED: true,
1211
- },
1212
- });
1213
- break;
1347
+ const tableMap = await loadFile({
1348
+ filepath: sourceParameters.path,
1349
+ type: 'GeoPackageRasterSource',
1350
+ model: this._model,
1351
+ });
1352
+ const { gpr, tileDao } = tableMap[sourceParameters.tables];
1353
+ const rasterSource = new XYZSource({
1354
+ minZoom: (_e = sourceParameters.minZoom) !== null && _e !== void 0 ? _e : tileDao.minWebMapZoom,
1355
+ maxZoom: (_f = sourceParameters.maxZoom) !== null && _f !== void 0 ? _f : tileDao.maxWebMapZoom,
1356
+ interpolate: sourceParameters.interpolate,
1357
+ url: '{z},{x},{y}',
1358
+ tileLoadFunction(tile, src) {
1359
+ const [z, x, y] = src.split(',').map(Number);
1360
+ gpr
1361
+ .getTile(x, y, z)
1362
+ .then((dataUri) => (tile.getImage().src = dataUri));
1363
+ },
1364
+ attributions: sourceParameters.attribution,
1365
+ });
1366
+ newSource = rasterSource;
1367
+ break;
1368
+ }
1369
+ case 'GeoParquetSource': {
1370
+ const parameters = source.parameters;
1371
+ const geojson = await loadFile({
1372
+ filepath: parameters.path,
1373
+ type: 'GeoParquetSource',
1374
+ model: this._model,
1375
+ });
1376
+ const geojsonData = Array.isArray(geojson) ? geojson[0] : geojson;
1377
+ const format = new GeoJSON();
1378
+ newSource = new VectorSource({
1379
+ features: format.readFeatures(geojsonData, {
1380
+ dataProjection: parameters.projection,
1381
+ featureProjection: this._Map.getView().getProjection(),
1382
+ }),
1383
+ });
1384
+ break;
1385
+ }
1386
+ case 'MarkerSource': {
1387
+ const parameters = source.parameters;
1388
+ const point = new Point(parameters.feature.coords);
1389
+ const marker = new Feature({
1390
+ type: 'icon',
1391
+ geometry: point,
1392
+ });
1393
+ // Replace color placeholder in SVG with the parameter color
1394
+ const markerColor = parameters.color || '#3463a0';
1395
+ const svgString = markerIcon.svgstr
1396
+ .replace('{{COLOR}}', markerColor)
1397
+ .replace('<svg', '<svg width="128" height="128"');
1398
+ const iconStyle = new Style({
1399
+ image: new Icon({
1400
+ src: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`,
1401
+ scale: 0.25,
1402
+ anchor: [0.5, 1],
1403
+ anchorXUnits: 'fraction',
1404
+ anchorYUnits: 'fraction',
1405
+ }),
1406
+ });
1407
+ marker.setStyle(iconStyle);
1408
+ newSource = new VectorSource({
1409
+ features: [marker],
1410
+ });
1411
+ break;
1412
+ }
1413
+ case 'WmsTileSource': {
1414
+ const sourceParameters = source.parameters;
1415
+ const url = sourceParameters.url;
1416
+ const selectedLayer = (_g = sourceParameters === null || sourceParameters === void 0 ? void 0 : sourceParameters.params) === null || _g === void 0 ? void 0 : _g.layers;
1417
+ newSource = new TileWMSSource({
1418
+ attributions: sourceParameters === null || sourceParameters === void 0 ? void 0 : sourceParameters.attribution,
1419
+ url,
1420
+ params: {
1421
+ LAYERS: selectedLayer,
1422
+ TILED: true,
1423
+ },
1424
+ });
1425
+ break;
1426
+ }
1214
1427
  }
1215
1428
  }
1429
+ catch (err) {
1430
+ this._log('error', `Failed to load source "${(_h = source.name) !== null && _h !== void 0 ? _h : id}" (${source.type}): ${err.message}`);
1431
+ return;
1432
+ }
1433
+ this._log('info', `Source "${(_j = source.name) !== null && _j !== void 0 ? _j : id}" (${source.type}) loaded successfully`);
1216
1434
  newSource.set('id', id);
1435
+ // Forward OL tile/feature load errors to the JupyterLab log console.
1436
+ // These errors (CORS failures, network errors, etc.) are written directly
1437
+ // by the browser to DevTools and cannot be captured by console patching —
1438
+ // OL's own events are the only reliable interception point.
1439
+ newSource.on('tileloaderror', (evt) => {
1440
+ var _a, _b, _c;
1441
+ const url = (_c = (_b = (_a = evt === null || evt === void 0 ? void 0 : evt.tile) === null || _a === void 0 ? void 0 : _a.getKey) === null || _b === void 0 ? void 0 : _b.call(_a)) !== null && _c !== void 0 ? _c : '';
1442
+ this._log('error', `Tile load error for source "${id}"${url ? ': ' + url : ''}`);
1443
+ });
1444
+ newSource.on('featuresloaderror', () => {
1445
+ this._log('error', `Features load error for source "${id}"`);
1446
+ });
1217
1447
  // _sources is a list of OpenLayers sources
1218
1448
  this._sources[id] = newSource;
1449
+ this._trackSourceExtZoom(id, newSource);
1219
1450
  }
1220
1451
  computeSourceUrl(source) {
1221
1452
  const parameters = source.parameters;
@@ -1286,8 +1517,11 @@ export class MainView extends React.Component {
1286
1517
  for (let targetLayerPosition = 0; targetLayerPosition < layerIds.length; targetLayerPosition++) {
1287
1518
  const layerId = layerIds[targetLayerPosition];
1288
1519
  const layer = this._model.sharedModel.getLayer(layerId);
1520
+ if (this._loadingLayers.has(layerId)) {
1521
+ continue;
1522
+ }
1289
1523
  if (!layer) {
1290
- console.warn(`Layer with ID ${layerId} does not exist in the shared model.`);
1524
+ this._log('warning', `Layer with ID ${layerId} does not exist in the shared model.`);
1291
1525
  continue;
1292
1526
  }
1293
1527
  const mapLayer = this.getLayer(layerId);
@@ -1319,7 +1553,7 @@ export class MainView extends React.Component {
1319
1553
  * @returns - the map layer.
1320
1554
  */
1321
1555
  async _buildMapLayer(id, layer) {
1322
- var _a, _b, _c;
1556
+ var _a, _b, _c, _d, _e, _f, _g, _h;
1323
1557
  this.setState(old => (Object.assign(Object.assign({}, old), { loadingLayer: true })));
1324
1558
  this._loadingLayers.add(id);
1325
1559
  let newMapLayer;
@@ -1354,12 +1588,23 @@ export class MainView extends React.Component {
1354
1588
  }
1355
1589
  case 'VectorLayer': {
1356
1590
  layerParameters = layer.parameters;
1357
- newMapLayer = new VectorLayer({
1358
- opacity: layerParameters.opacity,
1359
- visible: layer.visible,
1360
- source: this._sources[layerParameters.source],
1361
- style: this.vectorLayerStyleRuleBuilder(layer),
1362
- });
1591
+ if (Array.isArray((_b = layerParameters.symbologyState) === null || _b === void 0 ? void 0 : _b.layers)) {
1592
+ const olSource = this._sources[layerParameters.source];
1593
+ const grammarState = layerParameters.symbologyState;
1594
+ const rows = olSource instanceof VectorSource
1595
+ ? olSource.getFeatures().map(f => f.getProperties())
1596
+ : [];
1597
+ const featureValues = extractEncodingFieldValues(grammarState, rows);
1598
+ newMapLayer = grammarToOLLayer(layerParameters.symbologyState, olSource, layerParameters.opacity, layer.visible, featureValues);
1599
+ }
1600
+ else {
1601
+ newMapLayer = new VectorImageLayer({
1602
+ opacity: layerParameters.opacity,
1603
+ visible: layer.visible,
1604
+ source: this._sources[layerParameters.source],
1605
+ style: this.vectorLayerStyleRuleBuilder(layer),
1606
+ });
1607
+ }
1363
1608
  break;
1364
1609
  }
1365
1610
  case 'VectorTileLayer': {
@@ -1374,7 +1619,7 @@ export class MainView extends React.Component {
1374
1619
  }
1375
1620
  case 'HillshadeLayer': {
1376
1621
  layerParameters = layer.parameters;
1377
- newMapLayer = new WebGlTileLayer({
1622
+ newMapLayer = new GeoTiffLayer({
1378
1623
  opacity: 0.3,
1379
1624
  visible: layer.visible,
1380
1625
  source: this._sources[layerParameters.source],
@@ -1393,20 +1638,35 @@ export class MainView extends React.Component {
1393
1638
  });
1394
1639
  break;
1395
1640
  }
1396
- case 'WebGlLayer': {
1641
+ case 'OpenEOTileLayer': {
1397
1642
  layerParameters = layer.parameters;
1398
- // This is to handle python sending a None for the color
1399
- const layerOptions = {
1643
+ newMapLayer = new OpenEOTileLayer({
1400
1644
  opacity: layerParameters.opacity,
1401
1645
  visible: layer.visible,
1402
1646
  source: this._sources[layerParameters.source],
1403
- };
1404
- if (layerParameters.color) {
1405
- layerOptions['style'] = {
1406
- color: layerParameters.color,
1647
+ });
1648
+ break;
1649
+ }
1650
+ case 'GeoTiffLayer': {
1651
+ layerParameters = layer.parameters;
1652
+ const geoTiffSource = this._sources[layerParameters.source];
1653
+ if (Array.isArray((_c = layerParameters.symbologyState) === null || _c === void 0 ? void 0 : _c.layers)) {
1654
+ newMapLayer = grammarToOLLayer(layerParameters.symbologyState, geoTiffSource, (_d = layerParameters.opacity) !== null && _d !== void 0 ? _d : 1, (_e = layer.visible) !== null && _e !== void 0 ? _e : true, [], true);
1655
+ }
1656
+ else {
1657
+ // This is to handle python sending a None for the color
1658
+ const layerOptions = {
1659
+ opacity: layerParameters.opacity,
1660
+ visible: layer.visible,
1661
+ source: geoTiffSource,
1407
1662
  };
1663
+ if (layerParameters.color) {
1664
+ layerOptions['style'] = {
1665
+ color: layerParameters.color,
1666
+ };
1667
+ }
1668
+ newMapLayer = new GeoTiffLayer(layerOptions);
1408
1669
  }
1409
- newMapLayer = new WebGlTileLayer(layerOptions);
1410
1670
  break;
1411
1671
  }
1412
1672
  case 'HeatmapLayer': {
@@ -1415,9 +1675,9 @@ export class MainView extends React.Component {
1415
1675
  opacity: layerParameters.opacity,
1416
1676
  visible: layer.visible,
1417
1677
  source: this._sources[layerParameters.source],
1418
- blur: (_b = layerParameters.blur) !== null && _b !== void 0 ? _b : 15,
1419
- radius: (_c = layerParameters.radius) !== null && _c !== void 0 ? _c : 8,
1420
- gradient: layerParameters.color,
1678
+ blur: (_f = layerParameters.blur) !== null && _f !== void 0 ? _f : 15,
1679
+ radius: (_g = layerParameters.radius) !== null && _g !== void 0 ? _g : 8,
1680
+ gradient: (_h = layerParameters.symbologyState) === null || _h === void 0 ? void 0 : _h.gradient,
1421
1681
  });
1422
1682
  break;
1423
1683
  }
@@ -1458,7 +1718,7 @@ export class MainView extends React.Component {
1458
1718
  var _a;
1459
1719
  const sourceProjection = (_a = newMapLayer.getSource()) === null || _a === void 0 ? void 0 : _a.getProjection();
1460
1720
  if (!sourceProjection) {
1461
- console.warn('Layer source projection is undefined or invalid');
1721
+ this._log('warning', 'Layer source projection is undefined or invalid');
1462
1722
  return;
1463
1723
  }
1464
1724
  const projectionCode = sourceProjection.getCode();
@@ -1466,7 +1726,7 @@ export class MainView extends React.Component {
1466
1726
  if (!isProjectionRegistered) {
1467
1727
  // Check if the projection exists in proj4list
1468
1728
  if (!proj4list[projectionCode]) {
1469
- console.warn(`Projection code '${projectionCode}' not found in proj4list`);
1729
+ this._log('warning', `Projection code '${projectionCode}' not found in proj4list`);
1470
1730
  return;
1471
1731
  }
1472
1732
  try {
@@ -1474,7 +1734,7 @@ export class MainView extends React.Component {
1474
1734
  register(proj4);
1475
1735
  }
1476
1736
  catch (error) {
1477
- console.warn(`Failed to register projection '${projectionCode}'. Error: ${error.message}`);
1737
+ this._log('warning', `Failed to register projection '${projectionCode}'. Error: ${error.message}`);
1478
1738
  return;
1479
1739
  }
1480
1740
  }
@@ -1499,12 +1759,14 @@ export class MainView extends React.Component {
1499
1759
  const numLayers = this._Map.getLayers().getLength();
1500
1760
  const safeIndex = Math.min(index, numLayers);
1501
1761
  this._Map.getLayers().insertAt(safeIndex, newMapLayer);
1762
+ this._trackLayerViewState(id, newMapLayer);
1502
1763
  // doing +1 instead of calling method again
1503
1764
  if (!this.state.initialLayersReady &&
1504
1765
  numLayers + 1 === this._initialLayersCount) {
1505
1766
  this.setState(old => (Object.assign(Object.assign({}, old), { initialLayersReady: true })));
1506
1767
  }
1507
1768
  }
1769
+ this._model.syncSelected({ [id]: { type: 'layer' } }, this._model.getClientId().toString());
1508
1770
  }
1509
1771
  catch (error) {
1510
1772
  if (this.state.loadingErrors.find(item => item.id === id && item.error === error.message)) {
@@ -1530,7 +1792,7 @@ export class MainView extends React.Component {
1530
1792
  * @param layer - the layer object.
1531
1793
  */
1532
1794
  async updateLayer(id, layer, mapLayer, oldLayer) {
1533
- var _a, _b, _c, _d, _e, _f, _g, _h;
1795
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
1534
1796
  layer.type !== 'StorySegmentLayer' && mapLayer.setVisible(layer.visible);
1535
1797
  switch (layer.type) {
1536
1798
  case 'RasterLayer': {
@@ -1539,6 +1801,11 @@ export class MainView extends React.Component {
1539
1801
  }
1540
1802
  case 'VectorLayer': {
1541
1803
  const layerParams = layer.parameters;
1804
+ if (Array.isArray((_b = layerParams.symbologyState) === null || _b === void 0 ? void 0 : _b.layers)) {
1805
+ // Grammar layers may change structure (e.g. KDE added/removed) — rebuild.
1806
+ this.replaceLayer(id, layer);
1807
+ break;
1808
+ }
1542
1809
  mapLayer.setOpacity(layerParams.opacity || 1);
1543
1810
  mapLayer.setStyle(this.vectorLayerStyleRuleBuilder(layer));
1544
1811
  break;
@@ -1556,33 +1823,50 @@ export class MainView extends React.Component {
1556
1823
  case 'ImageLayer': {
1557
1824
  break;
1558
1825
  }
1559
- case 'WebGlLayer': {
1560
- mapLayer.setOpacity((_b = layer.parameters) === null || _b === void 0 ? void 0 : _b.opacity);
1561
- if ((_c = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _c === void 0 ? void 0 : _c.color) {
1562
- mapLayer.setStyle({
1563
- color: layer.parameters.color,
1564
- });
1826
+ case 'GeoTiffLayer': {
1827
+ if (Array.isArray((_d = (_c = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _c === void 0 ? void 0 : _c.symbologyState) === null || _d === void 0 ? void 0 : _d.layers)) {
1828
+ this.replaceLayer(id, layer);
1829
+ }
1830
+ else {
1831
+ mapLayer.setOpacity((_e = layer.parameters) === null || _e === void 0 ? void 0 : _e.opacity);
1832
+ if ((_f = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _f === void 0 ? void 0 : _f.color) {
1833
+ mapLayer.setStyle({
1834
+ color: layer.parameters.color,
1835
+ });
1836
+ }
1565
1837
  }
1566
1838
  break;
1567
1839
  }
1840
+ case 'OpenEOTileLayer': {
1841
+ const layerParams = layer.parameters;
1842
+ const openeoLayer = mapLayer;
1843
+ openeoLayer.setOpacity((_g = layerParams.opacity) !== null && _g !== void 0 ? _g : 1);
1844
+ break;
1845
+ }
1568
1846
  case 'HeatmapLayer': {
1569
1847
  const layerParams = layer.parameters;
1570
1848
  const heatmap = mapLayer;
1571
- heatmap.setOpacity((_d = layerParams.opacity) !== null && _d !== void 0 ? _d : 1);
1572
- heatmap.setBlur((_e = layerParams.blur) !== null && _e !== void 0 ? _e : 15);
1573
- heatmap.setRadius((_f = layerParams.radius) !== null && _f !== void 0 ? _f : 8);
1574
- heatmap.setGradient((_g = layerParams.color) !== null && _g !== void 0 ? _g : ['#00f', '#0ff', '#0f0', '#ff0', '#f00']);
1849
+ heatmap.setOpacity((_h = layerParams.opacity) !== null && _h !== void 0 ? _h : 1);
1850
+ heatmap.setBlur((_j = layerParams.blur) !== null && _j !== void 0 ? _j : 15);
1851
+ heatmap.setRadius((_k = layerParams.radius) !== null && _k !== void 0 ? _k : 8);
1852
+ heatmap.setGradient((_m = (_l = layerParams.symbologyState) === null || _l === void 0 ? void 0 : _l.gradient) !== null && _m !== void 0 ? _m : [
1853
+ '#00f',
1854
+ '#0ff',
1855
+ '#0f0',
1856
+ '#ff0',
1857
+ '#f00',
1858
+ ]);
1575
1859
  this.handleTemporalController(id, layer);
1576
1860
  break;
1577
1861
  }
1578
1862
  case 'StacLayer':
1579
- mapLayer.setOpacity(((_h = layer.parameters) === null || _h === void 0 ? void 0 : _h.opacity) || 1);
1863
+ mapLayer.setOpacity(((_o = layer.parameters) === null || _o === void 0 ? void 0 : _o.opacity) || 1);
1580
1864
  break;
1581
1865
  }
1582
1866
  }
1583
1867
  flyToGeometry(sender, geometry) {
1584
1868
  if (!geometry || typeof geometry.getExtent !== 'function') {
1585
- console.warn('Invalid geometry for flyToGeometry:', geometry);
1869
+ this._log('warning', `Invalid geometry for flyToGeometry: ${geometry}`);
1586
1870
  return;
1587
1871
  }
1588
1872
  const view = this._Map.getView();
@@ -1594,11 +1878,12 @@ export class MainView extends React.Component {
1594
1878
  });
1595
1879
  }
1596
1880
  highlightFeatureOnMap(sender, featureOrGeometry) {
1881
+ var _a;
1597
1882
  const geometry = (featureOrGeometry === null || featureOrGeometry === void 0 ? void 0 : featureOrGeometry.geometry) ||
1598
1883
  (featureOrGeometry === null || featureOrGeometry === void 0 ? void 0 : featureOrGeometry._geometry) ||
1599
1884
  featureOrGeometry;
1600
1885
  if (!geometry) {
1601
- console.warn('No geometry found in feature:', featureOrGeometry);
1886
+ this._log('warning', `No geometry found in feature: ${featureOrGeometry}`);
1602
1887
  return;
1603
1888
  }
1604
1889
  const isOlGeometry = typeof geometry.getCoordinates === 'function';
@@ -1608,63 +1893,128 @@ export class MainView extends React.Component {
1608
1893
  featureProjection: this._Map.getView().getProjection(),
1609
1894
  });
1610
1895
  const olFeature = new Feature(Object.assign({ geometry: parsedGeometry }, (geometry !== featureOrGeometry ? featureOrGeometry : {})));
1611
- if (!this._highlightLayer) {
1612
- this._highlightLayer = new VectorLayer({
1613
- source: new VectorSource(),
1614
- style: feature => {
1615
- var _a;
1616
- const geomType = (_a = feature.getGeometry()) === null || _a === void 0 ? void 0 : _a.getType();
1617
- switch (geomType) {
1618
- case 'Point':
1619
- case 'MultiPoint':
1620
- return new Style({
1621
- image: new Circle({
1622
- radius: 6,
1623
- fill: new Fill({
1624
- color: 'rgba(255, 255, 0, 0.8)',
1625
- }),
1626
- stroke: new Stroke({
1627
- color: '#ff0',
1628
- width: 2,
1629
- }),
1630
- }),
1631
- });
1632
- case 'LineString':
1633
- case 'MultiLineString':
1634
- return new Style({
1635
- stroke: new Stroke({
1636
- color: 'rgba(255, 255, 0, 0.8)',
1637
- width: 3,
1638
- }),
1639
- });
1640
- case 'Polygon':
1641
- case 'MultiPolygon':
1642
- return new Style({
1643
- stroke: new Stroke({
1644
- color: '#f00',
1645
- width: 2,
1646
- }),
1647
- fill: new Fill({
1648
- color: 'rgba(255, 255, 0, 0.8)',
1649
- }),
1650
- });
1651
- default:
1652
- return new Style({
1653
- stroke: new Stroke({
1654
- color: '#000',
1655
- width: 2,
1656
- }),
1657
- });
1658
- }
1659
- },
1660
- zIndex: 999,
1661
- });
1662
- this._Map.addLayer(this._highlightLayer);
1663
- }
1664
- const source = this._highlightLayer.getSource();
1896
+ this._ensureHighlightLayer();
1897
+ const source = (_a = this._highlightLayerRef.current) === null || _a === void 0 ? void 0 : _a.getSource();
1665
1898
  source === null || source === void 0 ? void 0 : source.clear();
1666
1899
  source === null || source === void 0 ? void 0 : source.addFeature(olFeature);
1667
1900
  }
1901
+ _ensureHighlightLayer() {
1902
+ ensureHighlightLayer(this._Map, this._highlightLayerRef);
1903
+ }
1904
+ /**
1905
+ * Replace the highlight layer contents with the given geometries.
1906
+ * Clears the source first so that stale highlights are always removed,
1907
+ * including when the selection becomes empty (geometries = []).
1908
+ */
1909
+ _setHighlightGeometries(geometries) {
1910
+ var _a;
1911
+ this._ensureHighlightLayer();
1912
+ const source = (_a = this._highlightLayerRef.current) === null || _a === void 0 ? void 0 : _a.getSource();
1913
+ source === null || source === void 0 ? void 0 : source.clear();
1914
+ for (const geom of geometries) {
1915
+ source === null || source === void 0 ? void 0 : source.addFeature(new Feature({ geometry: geom }));
1916
+ }
1917
+ }
1918
+ /**
1919
+ * Replace the highlight layer contents with pre-styled features.
1920
+ * Each feature carries its own highlight style via feature.setStyle().
1921
+ */
1922
+ _setHighlightFeatures(features) {
1923
+ var _a;
1924
+ this._ensureHighlightLayer();
1925
+ const source = (_a = this._highlightLayerRef.current) === null || _a === void 0 ? void 0 : _a.getSource();
1926
+ source === null || source === void 0 ? void 0 : source.clear();
1927
+ for (const f of features) {
1928
+ source === null || source === void 0 ? void 0 : source.addFeature(f);
1929
+ }
1930
+ }
1931
+ _buildHighlightStyle(original, geomType) {
1932
+ return buildHighlightStyle(original, geomType);
1933
+ }
1934
+ /**
1935
+ * Compute extent for layer or source
1936
+ */
1937
+ _computeExtent(layer, source) {
1938
+ try {
1939
+ if (source instanceof VectorSource) {
1940
+ const extent = source.getExtent();
1941
+ if (extent) {
1942
+ return extent;
1943
+ }
1944
+ }
1945
+ if (source instanceof TileSource || source instanceof VectorTileSource) {
1946
+ const tileGrid = source.getTileGrid();
1947
+ const extent = tileGrid === null || tileGrid === void 0 ? void 0 : tileGrid.getExtent();
1948
+ if (extent) {
1949
+ return extent;
1950
+ }
1951
+ }
1952
+ if (layer instanceof StacLayer) {
1953
+ const extent = layer.getExtent();
1954
+ if (extent) {
1955
+ return extent;
1956
+ }
1957
+ }
1958
+ }
1959
+ catch (error) {
1960
+ this._log('warning', `Failed to compute extent: ${error}`);
1961
+ }
1962
+ return undefined;
1963
+ }
1964
+ _computeZoomFromExtent(extent) {
1965
+ var _a, _b;
1966
+ if (!this._Map) {
1967
+ return null;
1968
+ }
1969
+ const view = this._Map.getView();
1970
+ const size = (_a = this._Map.getSize()) !== null && _a !== void 0 ? _a : getSize(extent);
1971
+ const resolution = view.getResolutionForExtent(extent, size);
1972
+ const zoom = view.getZoomForResolution(resolution);
1973
+ return (_b = zoom !== null && zoom !== void 0 ? zoom : view.getZoom()) !== null && _b !== void 0 ? _b : 0;
1974
+ }
1975
+ /**
1976
+ * Track layer's extent and zoom in model's view state
1977
+ */
1978
+ _trackLayerViewState(layerId, olLayer) {
1979
+ var _a;
1980
+ const effectiveLayer = olLayer instanceof LayerGroup
1981
+ ? olLayer.getLayers().getArray()[0]
1982
+ : olLayer;
1983
+ if (!effectiveLayer) {
1984
+ return;
1985
+ }
1986
+ const source = effectiveLayer.getSource();
1987
+ const sourceId = (_a = source === null || source === void 0 ? void 0 : source.get) === null || _a === void 0 ? void 0 : _a.call(source, 'id');
1988
+ let extent = sourceId ? this._model.getExtent(sourceId) : undefined;
1989
+ if (!extent) {
1990
+ extent = this._computeExtent(effectiveLayer, source);
1991
+ }
1992
+ if (extent) {
1993
+ const zoom = this._computeZoomFromExtent(extent);
1994
+ if (zoom === null) {
1995
+ return;
1996
+ }
1997
+ const view = { extent, zoom };
1998
+ this._model.updateLayerViewState(layerId, view);
1999
+ }
2000
+ }
2001
+ /**
2002
+ * Track source's extent and zoom in model's view state
2003
+ */
2004
+ _trackSourceExtZoom(sourceId, olSource) {
2005
+ var _a, _b, _c;
2006
+ const extent = this._computeExtent(undefined, olSource);
2007
+ if (extent) {
2008
+ const projection = (_c = (_b = (_a = olSource === null || olSource === void 0 ? void 0 : olSource.getProjection) === null || _a === void 0 ? void 0 : _a.call(olSource)) === null || _b === void 0 ? void 0 : _b.getCode) === null || _c === void 0 ? void 0 : _c.call(_b);
2009
+ const zoom = this._computeZoomFromExtent(extent);
2010
+ if (zoom === null) {
2011
+ return;
2012
+ }
2013
+ const view = Object.assign({ extent,
2014
+ zoom }, (projection && { projection }));
2015
+ this._model.updateLayerViewState(sourceId, view);
2016
+ }
2017
+ }
1668
2018
  /**
1669
2019
  * Wait for all layers to be loaded.
1670
2020
  */
@@ -1716,6 +2066,133 @@ export class MainView extends React.Component {
1716
2066
  this._Map.removeLayer(mapLayer);
1717
2067
  }
1718
2068
  }
2069
+ /**
2070
+ * Decide how selection changes should affect vector drawing state.
2071
+ *
2072
+ * This helper only computes whether
2073
+ * draw mode must be disabled (non-draw layer selected) and whether draw
2074
+ * interactions should be rebound (draw mode enabled and selected draw layer
2075
+ * changed).
2076
+ */
2077
+ _getVectorDrawingSelectionDecision(layer, selectedLayerId) {
2078
+ const isDrawVectorLayer = this._model.checkIfIsADrawVectorLayer(layer);
2079
+ if (!isDrawVectorLayer) {
2080
+ return { disableEditing: true, shouldRebind: false };
2081
+ }
2082
+ if (!this._model.editingVectorLayer) {
2083
+ return { disableEditing: false, shouldRebind: false };
2084
+ }
2085
+ if (selectedLayerId === this._previousDrawLayerID) {
2086
+ return { disableEditing: false, shouldRebind: false };
2087
+ }
2088
+ return { disableEditing: false, shouldRebind: true };
2089
+ }
2090
+ _handleTemporalControllerActiveChanged() {
2091
+ var _a, _b, _c;
2092
+ const localState = this._model.localState;
2093
+ if (!localState) {
2094
+ return;
2095
+ }
2096
+ const isTemporalControllerActive = localState.isTemporalControllerActive === true;
2097
+ const selectedLayers = (_a = localState.selected) === null || _a === void 0 ? void 0 : _a.value;
2098
+ const selectedLayerId = selectedLayers
2099
+ ? ((_b = Object.keys(selectedLayers)[0]) !== null && _b !== void 0 ? _b : null)
2100
+ : null;
2101
+ const layerType = selectedLayerId
2102
+ ? (_c = this._model.getLayer(selectedLayerId)) === null || _c === void 0 ? void 0 : _c.type
2103
+ : null;
2104
+ const isSelectionValid = !!selectedLayers &&
2105
+ Object.keys(selectedLayers).length === 1 &&
2106
+ !this._model.getSource(selectedLayerId) &&
2107
+ ['VectorLayer', 'HeatmapLayer'].includes(layerType !== null && layerType !== void 0 ? layerType : '');
2108
+ const displayTemporalController = isTemporalControllerActive && isSelectionValid;
2109
+ if (displayTemporalController !== this.state.displayTemporalController) {
2110
+ this.setState(old => (Object.assign(Object.assign({}, old), { displayTemporalController })));
2111
+ this._mainViewModel.commands.notifyCommandChanged(CommandIDs.temporalController);
2112
+ }
2113
+ }
2114
+ _handleRemoteUserChanged() {
2115
+ var _a, _b, _c;
2116
+ const localState = this._model.localState;
2117
+ if (!localState) {
2118
+ return;
2119
+ }
2120
+ const remoteUser = localState.remoteUser;
2121
+ const clients = this._model.sharedModel.awareness.getStates();
2122
+ // If we are in following mode, update UI and viewport from the remote user.
2123
+ if (remoteUser) {
2124
+ const remoteState = clients.get(remoteUser);
2125
+ if (!remoteState) {
2126
+ return;
2127
+ }
2128
+ if (((_a = remoteState.user) === null || _a === void 0 ? void 0 : _a.username) !== ((_b = this.state.remoteUser) === null || _b === void 0 ? void 0 : _b.username)) {
2129
+ this.setState(old => (Object.assign(Object.assign({}, old), { remoteUser: remoteState.user })));
2130
+ }
2131
+ const remoteViewport = remoteState.viewportState;
2132
+ if (remoteViewport.value) {
2133
+ const { x, y } = remoteViewport.value.coordinates;
2134
+ const zoom = remoteViewport.value.zoom;
2135
+ this._moveToPosition({ x, y }, zoom, 0);
2136
+ }
2137
+ return;
2138
+ }
2139
+ // If we are unfollowing, reset to local viewport and clear follow UI.
2140
+ if (this.state.remoteUser !== null) {
2141
+ this.setState(old => (Object.assign(Object.assign({}, old), { remoteUser: null })));
2142
+ const viewportState = (_c = localState.viewportState) === null || _c === void 0 ? void 0 : _c.value;
2143
+ if (viewportState) {
2144
+ this._moveToPosition(viewportState.coordinates, viewportState.zoom);
2145
+ }
2146
+ }
2147
+ }
2148
+ _handlePointerChanged() {
2149
+ const clients = this._model.sharedModel.awareness.getStates();
2150
+ const clientPointers = Object.assign({}, this.state.clientPointers);
2151
+ clients.forEach((client, clientId) => {
2152
+ var _a;
2153
+ if (!(client === null || client === void 0 ? void 0 : client.user) || this._model.getClientId() === clientId) {
2154
+ return;
2155
+ }
2156
+ const pointer = (_a = client.pointer) === null || _a === void 0 ? void 0 : _a.value;
2157
+ let currentClientPointer = clientPointers[clientId];
2158
+ if (pointer) {
2159
+ const pixel = this._Map.getPixelFromCoordinate([
2160
+ pointer.coordinates.x,
2161
+ pointer.coordinates.y,
2162
+ ]);
2163
+ const lonLat = toLonLat([pointer.coordinates.x, pointer.coordinates.y]);
2164
+ if (!currentClientPointer) {
2165
+ currentClientPointer = {
2166
+ username: client.user.username,
2167
+ displayName: client.user.display_name,
2168
+ color: client.user.color,
2169
+ coordinates: {
2170
+ x: pixel[0],
2171
+ y: pixel[1],
2172
+ },
2173
+ lonLat: {
2174
+ longitude: lonLat[0],
2175
+ latitude: lonLat[1],
2176
+ },
2177
+ };
2178
+ }
2179
+ else {
2180
+ currentClientPointer = Object.assign(Object.assign({}, currentClientPointer), { coordinates: {
2181
+ x: pixel[0],
2182
+ y: pixel[1],
2183
+ }, lonLat: {
2184
+ longitude: lonLat[0],
2185
+ latitude: lonLat[1],
2186
+ } });
2187
+ }
2188
+ clientPointers[clientId] = currentClientPointer;
2189
+ }
2190
+ else {
2191
+ delete clientPointers[clientId];
2192
+ }
2193
+ });
2194
+ this.setState(old => (Object.assign(Object.assign({}, old), { clientPointers })));
2195
+ }
1719
2196
  _onSharedOptionsChanged() {
1720
2197
  if (!this._Map) {
1721
2198
  return;
@@ -1781,7 +2258,7 @@ export class MainView extends React.Component {
1781
2258
  view = new View({ projection: newProjection });
1782
2259
  }
1783
2260
  else {
1784
- console.warn(`Invalid projection: ${projection}`);
2261
+ this._log('warning', `Invalid projection: ${projection}`);
1785
2262
  return;
1786
2263
  }
1787
2264
  }
@@ -1876,6 +2353,11 @@ export class MainView extends React.Component {
1876
2353
  const { id, oldValue: oldLayer, newValue: newLayer } = change;
1877
2354
  if (!newLayer || Object.keys(newLayer).length === 0) {
1878
2355
  this.removeLayer(id);
2356
+ if (this._model.checkIfIsADrawVectorLayer(oldLayer)) {
2357
+ this._model.editingVectorLayer = false;
2358
+ this._updateEditingVectorLayer();
2359
+ this._mainViewModel.commands.notifyCommandChanged(CommandIDs.toggleDrawFeatures);
2360
+ }
1879
2361
  return;
1880
2362
  }
1881
2363
  if (oldLayer && oldLayer.type !== newLayer.type) {
@@ -1886,6 +2368,9 @@ export class MainView extends React.Component {
1886
2368
  const layerTree = JupyterGISModel.getOrderedLayerIds(this._model);
1887
2369
  if (layerTree.includes(id)) {
1888
2370
  this.updateLayer(id, newLayer, mapLayer, oldLayer);
2371
+ if (mapLayer) {
2372
+ this._trackLayerViewState(id, mapLayer);
2373
+ }
1889
2374
  }
1890
2375
  else {
1891
2376
  this.updateLayers(layerTree);
@@ -1914,6 +2399,14 @@ export class MainView extends React.Component {
1914
2399
  }
1915
2400
  }
1916
2401
  });
2402
+ this.setState(old => (Object.assign(Object.assign({}, old), { identifyFeatureFloatersVersion: old.identifyFeatureFloatersVersion + 1 })));
2403
+ }
2404
+ _clearHighlightWhenIdentifyDisabled() {
2405
+ var _a, _b;
2406
+ if (this._model.currentMode !== 'identifying' &&
2407
+ this._highlightLayerRef.current) {
2408
+ (_b = (_a = this._highlightLayerRef.current) === null || _a === void 0 ? void 0 : _a.getSource()) === null || _b === void 0 ? void 0 : _b.clear();
2409
+ }
1917
2410
  }
1918
2411
  _computeAnnotationPosition(annotation) {
1919
2412
  const { x, y } = annotation.position;
@@ -1938,6 +2431,58 @@ export class MainView extends React.Component {
1938
2431
  }
1939
2432
  });
1940
2433
  }
2434
+ _computeFeatureFloaterPosition(feature) {
2435
+ var _a;
2436
+ const geometry = (_a = feature === null || feature === void 0 ? void 0 : feature.geometry) !== null && _a !== void 0 ? _a : feature === null || feature === void 0 ? void 0 : feature._geometry;
2437
+ if (!geometry) {
2438
+ return undefined;
2439
+ }
2440
+ if (typeof geometry.getExtent === 'function') {
2441
+ const extent = geometry.getExtent();
2442
+ const center = getCenter(extent);
2443
+ const pixels = this._Map.getPixelFromCoordinate(center);
2444
+ if (pixels) {
2445
+ return { x: pixels[0], y: pixels[1] };
2446
+ }
2447
+ return undefined;
2448
+ }
2449
+ if (geometry.type === 'Point' && Array.isArray(geometry.coordinates)) {
2450
+ const pixels = this._Map.getPixelFromCoordinate(geometry.coordinates);
2451
+ if (pixels) {
2452
+ return { x: pixels[0], y: pixels[1] };
2453
+ }
2454
+ }
2455
+ return undefined;
2456
+ }
2457
+ _getVisibleDrawIdentifiedFeatures() {
2458
+ var _a, _b, _c;
2459
+ const identifiedFeatures = (_c = (_b = (_a = this._model.localState) === null || _a === void 0 ? void 0 : _a.identifiedFeatures) === null || _b === void 0 ? void 0 : _b.value) !== null && _c !== void 0 ? _c : [];
2460
+ const drawEntries = identifiedFeatures.filter(entry => entry.floaterOpen === true);
2461
+ const visibleFeatures = drawEntries
2462
+ .map(entry => {
2463
+ const featureId = getFeatureIdentifier(entry.feature);
2464
+ if (!featureId) {
2465
+ return undefined;
2466
+ }
2467
+ return [featureId, entry.feature];
2468
+ })
2469
+ .filter((entry) => !!entry);
2470
+ return visibleFeatures;
2471
+ }
2472
+ _updateFeatureFloaters() {
2473
+ this._getVisibleDrawIdentifiedFeatures().forEach(([floaterKey, feature]) => {
2474
+ const el = document.getElementById(`feature-floater-${floaterKey}`);
2475
+ if (!el) {
2476
+ return;
2477
+ }
2478
+ const screenPosition = this._computeFeatureFloaterPosition(feature);
2479
+ if (!screenPosition) {
2480
+ return;
2481
+ }
2482
+ el.style.left = `${Math.round(screenPosition.x)}px`;
2483
+ el.style.top = `${Math.round(screenPosition.y)}px`;
2484
+ });
2485
+ }
1941
2486
  // TODO this and flyToPosition need a rework
1942
2487
  _onZoomToPosition(_, id) {
1943
2488
  var _a, _b, _c;
@@ -1948,7 +2493,6 @@ export class MainView extends React.Component {
1948
2493
  return;
1949
2494
  }
1950
2495
  // The id is a layer
1951
- let extent;
1952
2496
  const layer = this.getLayer(id);
1953
2497
  const source = layer === null || layer === void 0 ? void 0 : layer.getSource();
1954
2498
  const jgisLayer = this._model.getLayer(id);
@@ -1998,19 +2542,13 @@ export class MainView extends React.Component {
1998
2542
  return;
1999
2543
  }
2000
2544
  }
2001
- if (source instanceof VectorSource) {
2002
- extent = source.getExtent();
2003
- }
2004
- if (source instanceof TileSource) {
2005
- // Tiled sources don't have getExtent() so we get it from the grid
2006
- const tileGrid = source.getTileGrid();
2007
- extent = tileGrid === null || tileGrid === void 0 ? void 0 : tileGrid.getExtent();
2008
- }
2009
- if (layer instanceof StacLayer) {
2010
- extent = layer.getExtent();
2011
- }
2545
+ const extent = this._computeExtent(layer, source);
2012
2546
  if (!extent) {
2013
- console.warn('Layer has no extent.');
2547
+ this._log('warning', 'Layer ${id} has no extent.');
2548
+ return;
2549
+ }
2550
+ if (!extent.every(value => Number.isFinite(value))) {
2551
+ this._log('warning', `Layer ${id} has an invalid extent: ${extent.join(', ')}`);
2014
2552
  return;
2015
2553
  }
2016
2554
  // Convert layer extent value to view projection if needed
@@ -2019,6 +2557,10 @@ export class MainView extends React.Component {
2019
2557
  const transformedExtent = sourceProjection && sourceProjection !== viewProjection
2020
2558
  ? transformExtent(extent, sourceProjection, viewProjection)
2021
2559
  : extent;
2560
+ if (!transformedExtent.every(value => Number.isFinite(value))) {
2561
+ this._log('warning', `Layer ${id} has an invalid transformed extent: ${transformedExtent.join(', ')}`);
2562
+ return;
2563
+ }
2022
2564
  this._Map.getView().fit(transformedExtent, {
2023
2565
  size: this._Map.getSize(),
2024
2566
  duration: 500,
@@ -2082,7 +2624,8 @@ export class MainView extends React.Component {
2082
2624
  this._syncPointer(coordinates);
2083
2625
  }
2084
2626
  async _addMarker(e) {
2085
- if (this._model.currentMode !== 'marking') {
2627
+ if (this.state.editingVectorLayer ||
2628
+ this._model.currentMode !== 'marking') {
2086
2629
  return;
2087
2630
  }
2088
2631
  const coordinate = this._Map.getCoordinateFromPixel(e.pixel);
@@ -2094,7 +2637,7 @@ export class MainView extends React.Component {
2094
2637
  const layerParams = {
2095
2638
  opacity: 1.0,
2096
2639
  source: sourceId,
2097
- symbologyState: { renderType: 'Single Symbol' },
2640
+ symbologyState: { layers: [] },
2098
2641
  };
2099
2642
  const sourceModel = {
2100
2643
  type: 'MarkerSource',
@@ -2114,25 +2657,29 @@ export class MainView extends React.Component {
2114
2657
  }
2115
2658
  _identifyFeature(e) {
2116
2659
  var _a, _b;
2117
- if (this._model.currentMode !== 'identifying') {
2660
+ if (this.state.editingVectorLayer ||
2661
+ this._model.currentMode !== 'identifying') {
2118
2662
  return;
2119
2663
  }
2120
2664
  const localState = (_a = this._model) === null || _a === void 0 ? void 0 : _a.sharedModel.awareness.getLocalState();
2121
2665
  const selectedLayer = (_b = localState === null || localState === void 0 ? void 0 : localState.selected) === null || _b === void 0 ? void 0 : _b.value;
2122
2666
  if (!selectedLayer) {
2123
- console.warn('Layer must be selected to use identify tool');
2667
+ this._log('warning', 'Layer must be selected to use identify tool');
2124
2668
  return;
2125
2669
  }
2126
2670
  const layerId = Object.keys(selectedLayer)[0];
2127
2671
  const jgisLayer = this._model.getLayer(layerId);
2128
2672
  switch (jgisLayer === null || jgisLayer === void 0 ? void 0 : jgisLayer.type) {
2673
+ case 'VectorLayer':
2674
+ // Handled by selectInteraction (createSelectInteraction).
2675
+ break;
2129
2676
  case 'VectorTileLayer': {
2130
2677
  const geometries = [];
2131
2678
  const features = [];
2132
- let foundAny = false;
2679
+ let foundAnyFeatures = false;
2133
2680
  this._Map.forEachFeatureAtPixel(e.pixel, (feature) => {
2134
2681
  var _a, _b;
2135
- foundAny = true;
2682
+ foundAnyFeatures = true;
2136
2683
  let geom;
2137
2684
  let props = {};
2138
2685
  if (feature instanceof RenderFeature) {
@@ -2158,15 +2705,18 @@ export class MainView extends React.Component {
2158
2705
  geometries.push(geom);
2159
2706
  }
2160
2707
  if (props && Object.keys(props).length > 0) {
2161
- features.push(props);
2708
+ features.push({
2709
+ feature: props,
2710
+ floaterOpen: false,
2711
+ });
2162
2712
  }
2163
2713
  return true;
2164
2714
  });
2165
2715
  if (features.length > 0) {
2166
- this._model.syncIdentifiedFeatures(features, this._mainViewModel.id);
2716
+ this._model.syncIdentifiedFeatures(features, this._model.getClientId().toString());
2167
2717
  }
2168
- else if (!foundAny) {
2169
- this._model.syncIdentifiedFeatures([], this._mainViewModel.id);
2718
+ else if (!foundAnyFeatures) {
2719
+ this._model.syncIdentifiedFeatures([], this._model.getClientId().toString());
2170
2720
  }
2171
2721
  if (geometries.length > 0) {
2172
2722
  for (const geom of geometries) {
@@ -2180,7 +2730,7 @@ export class MainView extends React.Component {
2180
2730
  }
2181
2731
  break;
2182
2732
  }
2183
- case 'WebGlLayer': {
2733
+ case 'GeoTiffLayer': {
2184
2734
  const layer = this.getLayer(layerId);
2185
2735
  const data = layer.getData(e.pixel);
2186
2736
  // TODO: Handle dataviews?
@@ -2194,7 +2744,7 @@ export class MainView extends React.Component {
2194
2744
  }
2195
2745
  // last element is alpha
2196
2746
  bandValues['Alpha'] = data[data.length - 1];
2197
- this._model.syncIdentifiedFeatures([bandValues], this._mainViewModel.id);
2747
+ this._model.syncIdentifiedFeatures([{ feature: bandValues, floaterOpen: false }], this._mainViewModel.id);
2198
2748
  const coordinate = this._Map.getCoordinateFromPixel(e.pixel);
2199
2749
  const point = new Point(coordinate);
2200
2750
  // trigger highlight via signal
@@ -2213,7 +2763,7 @@ export class MainView extends React.Component {
2213
2763
  this.updateSource(layerId, jgisLayer);
2214
2764
  }
2215
2765
  if (!jgisLayer || !olLayer) {
2216
- console.error('Failed to update layer -- layer not found');
2766
+ this._log('error', 'Failed to update layer -- layer not found');
2217
2767
  return;
2218
2768
  }
2219
2769
  this.updateLayer(layerId, jgisLayer, olLayer);
@@ -2223,6 +2773,9 @@ export class MainView extends React.Component {
2223
2773
  const { id: layerId, selectedFeature } = json;
2224
2774
  const olLayer = this.getLayer(layerId);
2225
2775
  const source = olLayer.getSource();
2776
+ if (typeof source.forEachFeature !== 'function') {
2777
+ return;
2778
+ }
2226
2779
  source.forEachFeature(feature => {
2227
2780
  const time = feature.get(selectedFeature);
2228
2781
  const parsedTime = typeof time === 'string' ? Date.parse(time) : time;
@@ -2239,7 +2792,19 @@ export class MainView extends React.Component {
2239
2792
  throw new Error('Could not move to geolocation, because current zoom is not defined.');
2240
2793
  }
2241
2794
  }
2795
+ _updateEditingVectorLayer() {
2796
+ const editingVectorLayer = this._model.editingVectorLayer;
2797
+ this.setState(old => (Object.assign(Object.assign({}, old), { editingVectorLayer })));
2798
+ if (editingVectorLayer === true) {
2799
+ this._editVectorLayer();
2800
+ }
2801
+ if (editingVectorLayer === false && this._draw) {
2802
+ this._removeDrawInteraction();
2803
+ this._currentDrawLayerID = undefined;
2804
+ }
2805
+ }
2242
2806
  render() {
2807
+ var _a, _b, _c;
2243
2808
  return (React.createElement(React.Fragment, null,
2244
2809
  Object.entries(this.state.annotations).map(([key, annotation]) => {
2245
2810
  if (!this._model.annotationModel) {
@@ -2252,6 +2817,21 @@ export class MainView extends React.Component {
2252
2817
  }, className: 'jGIS-Popup-Wrapper' },
2253
2818
  React.createElement(AnnotationFloater, { itemId: key, annotationModel: this._model.annotationModel }))));
2254
2819
  }),
2820
+ this._getVisibleDrawIdentifiedFeatures().map(([floaterKey, feature]) => {
2821
+ const screenPosition = this._computeFeatureFloaterPosition(feature);
2822
+ if (!screenPosition) {
2823
+ return null;
2824
+ }
2825
+ return (React.createElement("div", { key: `feature-floater-${floaterKey}`, id: `feature-floater-${floaterKey}`, style: {
2826
+ left: screenPosition.x,
2827
+ top: screenPosition.y,
2828
+ }, className: "jGIS-Popup-Wrapper jGIS-FeatureFloater-Wrapper" },
2829
+ React.createElement(FeatureFloater, { feature: feature })));
2830
+ }),
2831
+ this.state.editingVectorLayer && (React.createElement("div", { className: "jgis-geometry-type-selector-overlay" },
2832
+ React.createElement("select", { className: "geometry-type-selector", id: "geometry-type-selector", value: (_a = this.state.drawGeometryLabel) !== null && _a !== void 0 ? _a : '', onChange: this._handleDrawGeometryTypeChange },
2833
+ React.createElement("option", { value: "", disabled: true, hidden: true }, "Geometry type"),
2834
+ DRAW_GEOMETRIES.map(geometryType => (React.createElement("option", { key: geometryType, value: geometryType }, geometryType)))))),
2255
2835
  React.createElement("div", { className: "jGIS-Mainview-Container" },
2256
2836
  this.state.displayTemporalController && (React.createElement(TemporalSlider, { model: this._model, filterStates: this.state.filterStates })),
2257
2837
  React.createElement("div", { ref: this.mainViewRef, className: "jGIS-Mainview data-jgis-keybinding", tabIndex: 0, style: {
@@ -2266,16 +2846,46 @@ export class MainView extends React.Component {
2266
2846
  React.createElement(LoadingOverlay, { loading: this.state.loading }),
2267
2847
  React.createElement(FollowIndicator, { remoteUser: this.state.remoteUser }),
2268
2848
  React.createElement(CollaboratorPointers, { clients: this.state.clientPointers }),
2269
- React.createElement("div", { ref: this.divRef, style: {
2849
+ React.createElement("div", { ref: this.divRef, className: "jgis-mainview-stage", style: {
2270
2850
  width: '100%',
2271
2851
  height: '100%',
2852
+ position: 'relative',
2272
2853
  } },
2273
- React.createElement("div", { className: "jgis-panels-wrapper" }, !this.state.isSpectaPresentation ? (React.createElement(React.Fragment, null,
2274
- this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state, settings: this.state.jgisSettings })),
2275
- this._formSchemaRegistry && this._annotationModel && (React.createElement(RightPanel, { model: this._model, commands: this._mainViewModel.commands, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel, addLayer: this.addLayer.bind(this), removeLayer: this.removeLayer.bind(this), settings: this.state.jgisSettings })))) : (this.state.initialLayersReady && (React.createElement(SpectaPanel, { model: this._model, isSpecta: this.state.isSpectaPresentation, isMobile: this.props.isMobile, onSegmentTransitionEnd: () => this._clearStoryScrollGuard(), containerRef: this.spectaContainerRef, storyViewerPanelRef: this.storyViewerPanelRef, addLayer: this.addLayer.bind(this), removeLayer: this.removeLayer.bind(this) })))),
2276
- React.createElement("div", { ref: this.controlsToolbarRef, className: "jgis-controls-toolbar" }))),
2854
+ React.createElement(ListStoryScrollTrackProvider, { model: this._model, enabled: this.state.isSpectaPresentation &&
2855
+ ((_b = this._model.getSelectedStory().story) === null || _b === void 0 ? void 0 : _b.storyType) ===
2856
+ STORY_TYPE.verticalScroll },
2857
+ this.state.isSpectaPresentation &&
2858
+ ((_c = this._model.getSelectedStory().story) === null || _c === void 0 ? void 0 : _c.storyType) ===
2859
+ STORY_TYPE.verticalScroll ? (React.createElement(ListStoryStageOverlay, { model: this._model, segmentTransition: this.state.segmentTransition })) : null,
2860
+ React.createElement("div", { className: "jgis-panels-wrapper" }, !this.state.isSpectaPresentation ? (React.createElement(React.Fragment, null, this.props.isMobile &&
2861
+ this._state &&
2862
+ this._formSchemaRegistry &&
2863
+ this._annotationModel ? (React.createElement(MergedPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state, settings: this.state.jgisSettings, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel, addLayer: this.addLayer.bind(this), removeLayer: this.removeLayer.bind(this) })) : (React.createElement(React.Fragment, null,
2864
+ this._state && (React.createElement(LeftPanel, { model: this._model, commands: this._mainViewModel.commands, state: this._state, settings: this.state.jgisSettings })),
2865
+ this._formSchemaRegistry &&
2866
+ this._annotationModel && (React.createElement(RightPanel, { model: this._model, commands: this._mainViewModel.commands, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel, addLayer: this.addLayer.bind(this), removeLayer: this.removeLayer.bind(this), settings: this.state.jgisSettings, patchGeoJSONFeatureProperties: this._patchGeoJSONFeatureProperties })))))) : (this.state.initialLayersReady && (React.createElement(SpectaPanel, { model: this._model, isSpecta: this.state.isSpectaPresentation, isMobile: this.props.isMobile, onSegmentTransitionEnd: () => this._clearStoryScrollGuard(), containerRef: this.spectaContainerRef, storyViewerPanelRef: this.storyViewerPanelRef, addLayer: this._addLayerForPanels, removeLayer: this._removeLayerForPanels, onSegmentTransitionChange: this._handleSegmentTransitionChange })))),
2867
+ React.createElement("div", { ref: this.controlsToolbarRef, className: "jgis-controls-toolbar" })))),
2277
2868
  !this.state.isSpectaPresentation && (React.createElement(StatusBar, { jgisModel: this._model, loading: this.state.loadingLayer, projection: this.state.viewProjection, scale: this.state.scale })))));
2278
2869
  }
2870
+ _log(level, message) {
2871
+ var _a;
2872
+ // Always mirror to the browser console regardless of whether the JupyterLab
2873
+ // logger is available.
2874
+ if (level === 'error' || level === 'critical') {
2875
+ // eslint-disable-next-line no-console
2876
+ console.error(message);
2877
+ }
2878
+ else if (level === 'warning') {
2879
+ // eslint-disable-next-line no-console
2880
+ console.warn(message);
2881
+ }
2882
+ else {
2883
+ // eslint-disable-next-line no-console
2884
+ console.log(message);
2885
+ }
2886
+ // Forward to JupyterLab log console when available.
2887
+ (_a = this._loggerRegistry) === null || _a === void 0 ? void 0 : _a.getLogger(this._model.filePath).log({ type: 'text', level, data: message });
2888
+ }
2279
2889
  }
2280
2890
  // ! TODO make mainview a modern react component instead of a class
2281
2891
  /** Thin wrapper that injects isMobile from useMediaQuery so MainView can use it in JSX. */