@jupytergis/base 0.15.0 → 0.16.0-alpha.1
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.
- package/lib/commands/BaseCommandIDs.d.ts +5 -0
- package/lib/commands/BaseCommandIDs.js +5 -0
- package/lib/commands/index.js +218 -48
- package/lib/commands/operationCommands.js +2 -2
- package/lib/constants.js +9 -1
- package/lib/{panelview/annotationPanel.js → features/annotations/AnnotationsPanel.js} +1 -1
- package/lib/{annotations → features/annotations}/index.d.ts +1 -0
- package/lib/{annotations → features/annotations}/index.js +1 -0
- package/lib/{console → features/console}/consoleview.js +1 -1
- package/lib/{panelview/filter-panel → features/filter}/Filter.js +4 -4
- package/lib/{panelview/filter-panel → features/filter}/FilterRow.d.ts +3 -2
- package/lib/{panelview/filter-panel → features/filter}/FilterRow.js +2 -1
- package/lib/{panelview/identify-panel → features/identify}/IdentifyPanel.d.ts +2 -0
- package/lib/features/identify/IdentifyPanel.js +102 -0
- package/lib/features/identify/components/FeatureCard.d.ts +17 -0
- package/lib/features/identify/components/FeatureCard.js +26 -0
- package/lib/features/identify/components/FeatureCardHeader.d.ts +11 -0
- package/lib/features/identify/components/FeatureCardHeader.js +30 -0
- package/lib/features/identify/components/FeatureFloater.d.ts +7 -0
- package/lib/features/identify/components/FeatureFloater.js +19 -0
- package/lib/features/identify/components/FeaturePropertyList.d.ts +11 -0
- package/lib/features/identify/components/FeaturePropertyList.js +18 -0
- package/lib/features/identify/components/FeatureRow.d.ts +13 -0
- package/lib/features/identify/components/FeatureRow.js +25 -0
- package/lib/features/identify/components/PropertyEditors.d.ts +44 -0
- package/lib/features/identify/components/PropertyEditors.js +56 -0
- package/lib/features/identify/hooks/useIdentifyPropertyEditor.d.ts +11 -0
- package/lib/features/identify/hooks/useIdentifyPropertyEditor.js +132 -0
- package/lib/features/identify/types/editorTypes.d.ts +21 -0
- package/lib/features/identify/utils/getFeatureIdentifier.d.ts +5 -0
- package/lib/features/identify/utils/getFeatureIdentifier.js +14 -0
- package/lib/features/identify/utils/highlightLayer.d.ts +11 -0
- package/lib/features/identify/utils/highlightLayer.js +57 -0
- package/lib/features/identify/utils/highlightStyle.d.ts +7 -0
- package/lib/features/identify/utils/highlightStyle.js +40 -0
- package/lib/{dialogs/layerBrowserDialog.js → features/layer-browser/index.js} +2 -2
- package/lib/{formbuilder/objectform/layer/heatmapLayerForm.d.ts → features/layers/forms/layer/geoTiffLayerForm.d.ts} +1 -1
- package/lib/{formbuilder/objectform/layer/webGlLayerForm.js → features/layers/forms/layer/geoTiffLayerForm.js} +5 -5
- package/lib/{formbuilder/objectform → features/layers/forms}/layer/hillshadeLayerForm.js +4 -4
- package/lib/{formbuilder/objectform → features/layers/forms}/layer/index.d.ts +1 -2
- package/lib/{formbuilder/objectform → features/layers/forms}/layer/index.js +1 -2
- package/lib/{formbuilder/objectform → features/layers/forms}/layer/layerform.d.ts +1 -1
- package/lib/{formbuilder/objectform → features/layers/forms}/layer/layerform.js +4 -4
- package/lib/features/layers/forms/layer/storySegmentLayerForm.js +150 -0
- package/lib/{formbuilder/objectform → features/layers/forms}/layer/vectorlayerform.js +4 -4
- package/lib/{formbuilder/objectform → features/layers/forms}/source/geojsonsource.js +5 -5
- package/lib/features/layers/forms/source/geopackagesource.d.ts +3 -0
- package/lib/features/layers/forms/source/geopackagesource.js +93 -0
- package/lib/{formbuilder/objectform → features/layers/forms}/source/geotiffsource.js +5 -5
- package/lib/{formbuilder/objectform → features/layers/forms}/source/index.d.ts +1 -0
- package/lib/{formbuilder/objectform → features/layers/forms}/source/index.js +1 -0
- package/lib/{formbuilder/objectform → features/layers/forms}/source/pathbasedsource.js +5 -5
- package/lib/{formbuilder/objectform → features/layers/forms}/source/sourceform.d.ts +1 -1
- package/lib/{formbuilder/objectform → features/layers/forms}/source/sourceform.js +4 -4
- package/lib/{formbuilder/objectform → features/layers/forms}/source/tilesourceform.js +4 -4
- package/lib/{formbuilder/objectform → features/layers/forms}/source/wmsTileSource.d.ts +1 -1
- package/lib/{formbuilder/objectform → features/layers/forms}/source/wmsTileSource.js +6 -6
- package/lib/{dialogs → features/layers}/layerCreationFormDialog.d.ts +1 -1
- package/lib/{dialogs → features/layers}/layerCreationFormDialog.js +1 -1
- package/lib/features/layers/symbology/Grammar.d.ts +11 -0
- package/lib/features/layers/symbology/Grammar.js +316 -0
- package/lib/{dialogs → features/layers}/symbology/classificationModes.d.ts +6 -6
- package/lib/{dialogs → features/layers}/symbology/classificationModes.js +48 -44
- package/lib/{dialogs → features/layers}/symbology/colorRampUtils.d.ts +1 -0
- package/lib/{dialogs → features/layers}/symbology/colorRampUtils.js +12 -1
- package/lib/features/layers/symbology/components/MappingRow.d.ts +40 -0
- package/lib/features/layers/symbology/components/MappingRow.js +520 -0
- package/lib/features/layers/symbology/components/NumericInput.d.ts +20 -0
- package/lib/features/layers/symbology/components/NumericInput.js +44 -0
- package/lib/features/layers/symbology/components/ScaleEditor.d.ts +33 -0
- package/lib/features/layers/symbology/components/ScaleEditor.js +221 -0
- package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampControls.d.ts +1 -1
- package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampControls.js +4 -3
- package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampSelector.d.ts +2 -1
- package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampSelector.js +6 -1
- package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ModeSelectRow.d.ts +1 -1
- package/lib/{dialogs → features/layers}/symbology/components/color_ramp/RgbaColorPicker.js +39 -9
- package/lib/{dialogs → features/layers}/symbology/components/color_stops/StopContainer.js +1 -1
- package/lib/{dialogs → features/layers}/symbology/components/color_stops/StopRow.d.ts +1 -1
- package/lib/{dialogs → features/layers}/symbology/components/color_stops/StopRow.js +14 -2
- package/lib/features/layers/symbology/grammarToOLLayer.d.ts +27 -0
- package/lib/features/layers/symbology/grammarToOLLayer.js +145 -0
- package/lib/features/layers/symbology/grammarToOLStyle.d.ts +32 -0
- package/lib/features/layers/symbology/grammarToOLStyle.js +487 -0
- package/lib/{dialogs → features/layers}/symbology/hooks/useGetBandInfo.d.ts +1 -1
- package/lib/{dialogs → features/layers}/symbology/hooks/useGetBandInfo.js +1 -1
- package/lib/{dialogs → features/layers}/symbology/hooks/useGetProperties.js +1 -1
- package/lib/{dialogs → features/layers}/symbology/hooks/useGetSymbology.js +4 -2
- package/lib/features/layers/symbology/styleBuilder.d.ts +24 -0
- package/lib/features/layers/symbology/styleBuilder.js +158 -0
- package/lib/{dialogs → features/layers}/symbology/symbologyDialog.d.ts +1 -1
- package/lib/{dialogs → features/layers}/symbology/symbologyDialog.js +11 -13
- package/lib/{dialogs → features/layers}/symbology/symbologyUtils.d.ts +18 -10
- package/lib/{dialogs → features/layers}/symbology/symbologyUtils.js +0 -84
- package/lib/{panelview/objectproperties.d.ts → features/objectproperties/index.d.ts} +1 -1
- package/lib/{panelview/objectproperties.js → features/objectproperties/index.js} +5 -10
- package/lib/{dialogs → features/processing}/ProcessingFormDialog.d.ts +1 -1
- package/lib/{dialogs → features/processing}/ProcessingFormDialog.js +28 -14
- package/lib/features/processing/forms/MapExtentToggle.d.ts +13 -0
- package/lib/features/processing/forms/MapExtentToggle.js +20 -0
- package/lib/features/processing/forms/clipRasterByExtentForm.d.ts +10 -0
- package/lib/features/processing/forms/clipRasterByExtentForm.js +99 -0
- package/lib/{formbuilder/objectform/process → features/processing/forms}/dissolveProcessForm.js +5 -5
- package/lib/{formbuilder/objectform → features/processing/forms}/processingForm.d.ts +1 -1
- package/lib/{formbuilder/objectform → features/processing/forms}/processingForm.js +6 -6
- package/lib/features/processing/forms/rasterizeForm.d.ts +10 -0
- package/lib/features/processing/forms/rasterizeForm.js +75 -0
- package/lib/features/processing/forms/useMapExtent.d.ts +22 -0
- package/lib/features/processing/forms/useMapExtent.js +57 -0
- package/lib/{processing → features/processing}/index.d.ts +19 -2
- package/lib/features/processing/index.js +1246 -0
- package/lib/{processing → features/processing}/processingCommands.d.ts +1 -1
- package/lib/features/processing/processingCommands.js +168 -0
- package/lib/features/processing/serverProcessing.d.ts +51 -0
- package/lib/features/processing/serverProcessing.js +99 -0
- package/lib/{stacBrowser → features/stac-browser}/components/StacPanel.js +2 -2
- package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/QueryableComboBox.js +5 -5
- package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/QueryableRow.js +1 -1
- package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/StacFilterExtensionPanel.js +2 -2
- package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/StacQueryableFilters.js +1 -1
- package/lib/{stacBrowser → features/stac-browser}/components/geodes/StacFilterSection.js +3 -3
- package/lib/{stacBrowser → features/stac-browser}/components/shared/StacPanelResults.js +3 -3
- package/lib/{stacBrowser → features/stac-browser}/components/shared/StacSpatialExtent.js +1 -1
- package/lib/{stacBrowser → features/stac-browser}/components/shared/StacTemporalExtent.js +1 -1
- package/lib/{stacBrowser → features/stac-browser}/context/StacResultsContext.js +2 -2
- package/lib/{stacBrowser → features/stac-browser}/hooks/useGeodesSearch.js +2 -2
- package/lib/{stacBrowser → features/stac-browser}/hooks/useStacFilterExtension.js +3 -3
- package/lib/{stacBrowser → features/stac-browser}/hooks/useStacSearch.js +1 -1
- package/lib/features/stac-browser/types/types.js +1 -0
- package/lib/{panelview/story-maps → features/story}/SpectaPanel.d.ts +4 -1
- package/lib/{panelview/story-maps → features/story}/SpectaPanel.js +3 -4
- package/lib/{panelview/story-maps → features/story}/StoryEditorPanel.js +1 -1
- package/lib/{panelview/story-maps → features/story}/StoryViewerPanel.d.ts +12 -11
- package/lib/features/story/StoryViewerPanel.js +64 -0
- package/lib/features/story/__tests__/fixtures/listStoryTestItems.d.ts +9 -0
- package/lib/features/story/__tests__/fixtures/listStoryTestItems.js +21 -0
- package/lib/features/story/components/ListStoryMapOverlayPanel.d.ts +10 -0
- package/lib/features/story/components/ListStoryMapOverlayPanel.js +11 -0
- package/lib/features/story/components/ListStoryMarkdownMeasurePane.d.ts +11 -0
- package/lib/features/story/components/ListStoryMarkdownMeasurePane.js +55 -0
- package/lib/features/story/components/ListStoryOverlayMarkdown.d.ts +15 -0
- package/lib/features/story/components/ListStoryOverlayMarkdown.js +93 -0
- package/lib/features/story/components/ListStoryStageOverlay.d.ts +11 -0
- package/lib/features/story/components/ListStoryStageOverlay.js +150 -0
- package/lib/features/story/components/ListStoryTitleBar.d.ts +7 -0
- package/lib/features/story/components/ListStoryTitleBar.js +20 -0
- package/lib/features/story/components/ListStoryTitleBarDesktop.d.ts +2 -0
- package/lib/features/story/components/ListStoryTitleBarDesktop.js +55 -0
- package/lib/features/story/components/ListStoryTitleBarMobile.d.ts +2 -0
- package/lib/features/story/components/ListStoryTitleBarMobile.js +41 -0
- package/lib/features/story/components/ListStoryVirtualScrollTrack.d.ts +6 -0
- package/lib/features/story/components/ListStoryVirtualScrollTrack.js +7 -0
- package/lib/{panelview/story-maps → features/story}/components/SpectaDesktopView.d.ts +4 -2
- package/lib/features/story/components/SpectaDesktopView.js +67 -0
- package/lib/features/story/components/SpectaMobileListModeContent.d.ts +13 -0
- package/lib/features/story/components/SpectaMobileListModeContent.js +36 -0
- package/lib/features/story/components/SpectaMobileSingleModeContent.d.ts +14 -0
- package/lib/{panelview/story-maps/components/SpectaMobileView.js → features/story/components/SpectaMobileSingleModeContent.js} +4 -6
- package/lib/{panelview/story-maps → features/story}/components/SpectaMobileView.d.ts +5 -3
- package/lib/features/story/components/SpectaMobileView.js +12 -0
- package/lib/features/story/components/SpectaSingleModeContent.d.ts +18 -0
- package/lib/features/story/components/SpectaSingleModeContent.js +8 -0
- package/lib/features/story/context/ListStoryScrollTrackContext.d.ts +19 -0
- package/lib/features/story/context/ListStoryScrollTrackContext.js +186 -0
- package/lib/features/story/hooks/useCurrentSegmentIndex.d.ts +3 -0
- package/lib/features/story/hooks/useCurrentSegmentIndex.js +17 -0
- package/lib/features/story/hooks/useListStoryScroll.d.ts +15 -0
- package/lib/features/story/hooks/useListStoryScroll.js +107 -0
- package/lib/features/story/hooks/useQueuedMarkdownHeightMeasure.d.ts +19 -0
- package/lib/features/story/hooks/useQueuedMarkdownHeightMeasure.js +56 -0
- package/lib/features/story/hooks/useStoryImagePreload.d.ts +1 -0
- package/lib/features/story/hooks/useStoryImagePreload.js +24 -0
- package/lib/{panelview/story-maps → features/story}/hooks/useStoryMap.d.ts +2 -7
- package/lib/{panelview/story-maps → features/story}/hooks/useStoryMap.js +21 -60
- package/lib/features/story/hooks/useStoryScrollState.d.ts +21 -0
- package/lib/features/story/hooks/useStoryScrollState.js +39 -0
- package/lib/features/story/hooks/useStorySegmentSync.d.ts +8 -0
- package/lib/features/story/hooks/useStorySegmentSync.js +51 -0
- package/lib/features/story/types/types.d.ts +43 -0
- package/lib/features/story/types/types.js +1 -0
- package/lib/features/story/utils/computeListStoryScrollState.d.ts +14 -0
- package/lib/features/story/utils/computeListStoryScrollState.js +79 -0
- package/lib/features/story/utils/listStoryMeasureQueue.d.ts +11 -0
- package/lib/features/story/utils/listStoryMeasureQueue.js +14 -0
- package/lib/features/story/utils/listStoryScrollTrack.d.ts +17 -0
- package/lib/features/story/utils/listStoryScrollTrack.js +72 -0
- package/lib/features/story/utils/spectaPresentation.d.ts +9 -0
- package/lib/features/story/utils/spectaPresentation.js +37 -0
- package/lib/features/story/utils/storySegmentViewItems.d.ts +5 -0
- package/lib/features/story/utils/storySegmentViewItems.js +30 -0
- package/lib/index.d.ts +11 -9
- package/lib/index.js +11 -9
- package/lib/keybindings.json +5 -0
- package/lib/mainview/OpenEOTileLayer.d.ts +49 -0
- package/lib/mainview/OpenEOTileLayer.js +179 -0
- package/lib/mainview/TemporalSlider.js +11 -9
- package/lib/mainview/components/MainViewMapSurface.d.ts +15 -0
- package/lib/mainview/components/MainViewMapSurface.js +13 -0
- package/lib/mainview/components/MainViewOverlayLayer.d.ts +9 -0
- package/lib/mainview/components/MainViewOverlayLayer.js +11 -0
- package/lib/mainview/components/MainViewSidePanels.d.ts +17 -0
- package/lib/mainview/components/MainViewSidePanels.js +10 -0
- package/lib/mainview/components/MainViewSpectaPanel.d.ts +17 -0
- package/lib/mainview/components/MainViewSpectaPanel.js +8 -0
- package/lib/mainview/components/MainViewStoryStage.d.ts +13 -0
- package/lib/mainview/components/MainViewStoryStage.js +17 -0
- package/lib/mainview/components/PositionedFloater.d.ts +10 -0
- package/lib/mainview/components/PositionedFloater.js +7 -0
- package/lib/mainview/geoJsonFeaturePatch.d.ts +9 -0
- package/lib/mainview/geoJsonFeaturePatch.js +43 -0
- package/lib/mainview/mainView.d.ts +93 -14
- package/lib/mainview/mainView.js +1216 -686
- package/lib/mainview/mainviewwidget.d.ts +5 -1
- package/lib/mainview/mainviewwidget.js +4 -2
- package/lib/shared/components/Button.d.ts +1 -1
- package/lib/shared/components/DropdownMenu.d.ts +1 -0
- package/lib/shared/components/DropdownMenu.js +3 -2
- package/lib/shared/components/NativeSelect.d.ts +8 -0
- package/lib/shared/components/NativeSelect.js +29 -0
- package/lib/shared/components/Tabs.d.ts +3 -3
- package/lib/shared/components/Tabs.js +5 -5
- package/lib/{formbuilder → shared/formbuilder}/creationform.js +71 -4
- package/lib/{formbuilder → shared/formbuilder}/editform.js +1 -1
- package/lib/{formbuilder → shared/formbuilder}/formselectors.d.ts +2 -2
- package/lib/{formbuilder → shared/formbuilder}/formselectors.js +10 -7
- package/lib/shared/formbuilder/index.d.ts +4 -0
- package/lib/shared/formbuilder/index.js +4 -0
- package/lib/{formbuilder → shared/formbuilder}/objectform/SchemaForm.d.ts +1 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/StoryEditorForm.d.ts +1 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/StoryEditorForm.js +1 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/components/LayerSelect.js +1 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/components/SegmentFormSymbology.js +4 -4
- package/lib/{formbuilder → shared/formbuilder}/objectform/components/SourcePropertiesField.js +1 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/components/StorySegmentReset.js +1 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/components/WmsTileSourceUrlInput.js +4 -4
- package/lib/{formbuilder → shared/formbuilder}/objectform/fileselectorwidget.js +8 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/schemaUtils.d.ts +3 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/schemaUtils.js +11 -0
- package/lib/{formbuilder → shared/formbuilder}/objectform/useSchemaFormState.d.ts +1 -1
- package/lib/{formbuilder → shared/formbuilder}/objectform/useSchemaFormState.js +1 -1
- package/lib/{icons.d.ts → shared/icons.d.ts} +2 -0
- package/lib/{icons.js → shared/icons.js} +28 -18
- package/lib/tools.d.ts +2 -0
- package/lib/tools.js +138 -4
- package/lib/types.d.ts +6 -1
- package/lib/types.js +6 -2
- package/lib/{menus.js → workspace/menus.js} +10 -2
- package/lib/workspace/panels/components/TabbedPanel.d.ts +17 -0
- package/lib/workspace/panels/components/TabbedPanel.js +19 -0
- package/lib/{panelview → workspace/panels}/components/layers.js +63 -18
- package/lib/workspace/panels/components/legendItem.js +680 -0
- package/lib/workspace/panels/hooks/useLayerTree.d.ts +19 -0
- package/lib/workspace/panels/hooks/useLayerTree.js +103 -0
- package/lib/workspace/panels/hooks/useRightPanelOptions.d.ts +27 -0
- package/lib/workspace/panels/hooks/useRightPanelOptions.js +72 -0
- package/lib/workspace/panels/hooks/useUIState.d.ts +2 -0
- package/lib/workspace/panels/hooks/useUIState.js +25 -0
- package/lib/{panelview → workspace/panels}/index.d.ts +1 -1
- package/lib/{panelview → workspace/panels}/index.js +1 -1
- package/lib/{panelview → workspace/panels}/leftpanel.d.ts +1 -1
- package/lib/workspace/panels/leftpanel.js +70 -0
- package/lib/{panelview/rightpanel.d.ts → workspace/panels/mergedpanel.d.ts} +6 -5
- package/lib/workspace/panels/mergedpanel.js +166 -0
- package/lib/workspace/panels/rightpanel.d.ts +25 -0
- package/lib/{panelview → workspace/panels}/rightpanel.js +53 -63
- package/lib/{statusbar → workspace/statusbar}/StatusBar.js +5 -4
- package/lib/{toolbar → workspace/toolbar}/widget.d.ts +2 -0
- package/lib/{toolbar → workspace/toolbar}/widget.js +33 -5
- package/lib/{widget.d.ts → workspace/widget.d.ts} +7 -5
- package/lib/{widget.js → workspace/widget.js} +16 -7
- package/package.json +16 -4
- package/style/base.css +124 -2
- package/style/icons/geopackage.svg +95 -0
- package/style/icons/pencil_solid.svg +7 -0
- package/style/identify.css +95 -0
- package/style/layerBrowser.css +29 -1
- package/style/leftPanel.css +25 -0
- package/style/shared/button.css +12 -0
- package/style/shared/dropdownMenu.css +9 -0
- package/style/shared/nativeSelect.css +75 -0
- package/style/shared/tabs.css +1 -1
- package/style/spectaProgressBar.css +0 -1
- package/style/storyPanel.css +330 -9
- package/style/storySpectaArticleOverlay.css +129 -0
- package/style/symbologyDialog.css +522 -27
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.d.ts +0 -4
- package/lib/dialogs/symbology/tiff_layer/TiffRendering.js +0 -42
- package/lib/dialogs/symbology/tiff_layer/components/BandRow.d.ts +0 -23
- package/lib/dialogs/symbology/tiff_layer/components/BandRow.js +0 -59
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.d.ts +0 -4
- package/lib/dialogs/symbology/tiff_layer/types/MultibandColor.js +0 -92
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.d.ts +0 -5
- package/lib/dialogs/symbology/tiff_layer/types/SingleBandPseudoColor.js +0 -313
- package/lib/dialogs/symbology/vector_layer/VectorRendering.d.ts +0 -4
- package/lib/dialogs/symbology/vector_layer/VectorRendering.js +0 -112
- package/lib/dialogs/symbology/vector_layer/components/ValueSelect.d.ts +0 -8
- package/lib/dialogs/symbology/vector_layer/components/ValueSelect.js +0 -9
- package/lib/dialogs/symbology/vector_layer/types/Canonical.d.ts +0 -4
- package/lib/dialogs/symbology/vector_layer/types/Canonical.js +0 -130
- package/lib/dialogs/symbology/vector_layer/types/Categorized.d.ts +0 -4
- package/lib/dialogs/symbology/vector_layer/types/Categorized.js +0 -243
- package/lib/dialogs/symbology/vector_layer/types/Graduated.d.ts +0 -4
- package/lib/dialogs/symbology/vector_layer/types/Graduated.js +0 -362
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.d.ts +0 -4
- package/lib/dialogs/symbology/vector_layer/types/Heatmap.js +0 -89
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.d.ts +0 -4
- package/lib/dialogs/symbology/vector_layer/types/SimpleSymbol.js +0 -120
- package/lib/formbuilder/index.d.ts +0 -4
- package/lib/formbuilder/index.js +0 -4
- package/lib/formbuilder/objectform/layer/heatmapLayerForm.js +0 -96
- package/lib/formbuilder/objectform/layer/storySegmentLayerForm.js +0 -95
- package/lib/formbuilder/objectform/layer/webGlLayerForm.d.ts +0 -3
- package/lib/formbuilder/objectform/process/index.d.ts +0 -1
- package/lib/formbuilder/objectform/process/index.js +0 -1
- package/lib/panelview/components/legendItem.js +0 -210
- package/lib/panelview/identify-panel/IdentifyPanel.js +0 -102
- package/lib/panelview/leftpanel.js +0 -139
- package/lib/panelview/story-maps/StoryViewerPanel.js +0 -116
- package/lib/panelview/story-maps/components/SpectaDesktopView.js +0 -49
- package/lib/processing/index.js +0 -200
- package/lib/processing/processingCommands.js +0 -67
- /package/lib/{panelview/annotationPanel.d.ts → features/annotations/AnnotationsPanel.d.ts} +0 -0
- /package/lib/{annotations → features/annotations}/components/Annotation.d.ts +0 -0
- /package/lib/{annotations → features/annotations}/components/Annotation.js +0 -0
- /package/lib/{annotations → features/annotations}/components/AnnotationFloater.d.ts +0 -0
- /package/lib/{annotations → features/annotations}/components/AnnotationFloater.js +0 -0
- /package/lib/{annotations → features/annotations}/components/Message.d.ts +0 -0
- /package/lib/{annotations → features/annotations}/components/Message.js +0 -0
- /package/lib/{annotations → features/annotations}/model.d.ts +0 -0
- /package/lib/{annotations → features/annotations}/model.js +0 -0
- /package/lib/{console → features/console}/consoleview.d.ts +0 -0
- /package/lib/{console → features/console}/index.d.ts +0 -0
- /package/lib/{console → features/console}/index.js +0 -0
- /package/lib/{panelview/filter-panel → features/filter}/Filter.d.ts +0 -0
- /package/lib/{stacBrowser/types/types.js → features/identify/types/editorTypes.js} +0 -0
- /package/lib/{dialogs/layerBrowserDialog.d.ts → features/layer-browser/index.d.ts} +0 -0
- /package/lib/{formbuilder/objectform → features/layers/forms}/layer/hillshadeLayerForm.d.ts +0 -0
- /package/lib/{formbuilder/objectform → features/layers/forms}/layer/storySegmentLayerForm.d.ts +0 -0
- /package/lib/{formbuilder/objectform → features/layers/forms}/layer/vectorlayerform.d.ts +0 -0
- /package/lib/{formbuilder/objectform → features/layers/forms}/source/geojsonsource.d.ts +0 -0
- /package/lib/{formbuilder/objectform → features/layers/forms}/source/geotiffsource.d.ts +0 -0
- /package/lib/{formbuilder/objectform → features/layers/forms}/source/pathbasedsource.d.ts +0 -0
- /package/lib/{formbuilder/objectform → features/layers/forms}/source/tilesourceform.d.ts +0 -0
- /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampSelectorEntry.d.ts +0 -0
- /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ColorRampSelectorEntry.js +0 -0
- /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/ModeSelectRow.js +0 -0
- /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/RgbaColorPicker.d.ts +0 -0
- /package/lib/{dialogs → features/layers}/symbology/components/color_ramp/cmocean.json +0 -0
- /package/lib/{dialogs → features/layers}/symbology/components/color_stops/StopContainer.d.ts +0 -0
- /package/lib/{dialogs → features/layers}/symbology/hooks/useEffectiveSymbologyParams.d.ts +0 -0
- /package/lib/{dialogs → features/layers}/symbology/hooks/useEffectiveSymbologyParams.js +0 -0
- /package/lib/{dialogs → features/layers}/symbology/hooks/useGetProperties.d.ts +0 -0
- /package/lib/{dialogs → features/layers}/symbology/hooks/useGetSymbology.d.ts +0 -0
- /package/lib/{dialogs → features/layers}/symbology/hooks/useOkSignal.d.ts +0 -0
- /package/lib/{dialogs → features/layers}/symbology/hooks/useOkSignal.js +0 -0
- /package/lib/{formbuilder/objectform/process → features/processing/forms}/dissolveProcessForm.d.ts +0 -0
- /package/lib/{processing → features/processing}/processingFormToParam.d.ts +0 -0
- /package/lib/{processing → features/processing}/processingFormToParam.js +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/StacPanel.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/QueryableComboBox.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/QueryableRow.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/StacFilterExtensionPanel.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/filter-extension/StacQueryableFilters.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/geodes/StacFilterSection.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/geodes/StacGeodesFilterPanel.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/geodes/StacGeodesFilterPanel.js +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/shared/StacPanelResults.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/shared/StacSpatialExtent.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/components/shared/StacTemporalExtent.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/constants.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/constants.js +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/context/StacResultsContext.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/hooks/useGeodesSearch.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/hooks/useStacFilterExtension.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/hooks/useStacSearch.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/index.d.ts +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/index.js +0 -0
- /package/lib/{stacBrowser → features/stac-browser}/types/types.d.ts +0 -0
- /package/lib/{panelview/story-maps → features/story}/StoryEditorPanel.d.ts +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/PreviewModeSwitch.d.ts +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/PreviewModeSwitch.js +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StoryContentSection.d.ts +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StoryContentSection.js +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StoryImageSection.d.ts +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StoryImageSection.js +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StoryNavBar.d.ts +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StoryNavBar.js +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StorySubtitleSection.d.ts +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StorySubtitleSection.js +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StoryTitleSection.d.ts +0 -0
- /package/lib/{panelview/story-maps → features/story}/components/StoryTitleSection.js +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/creationform.d.ts +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/editform.d.ts +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/SchemaForm.js +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/components/LayerSelect.d.ts +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/components/OpacitySlider.d.ts +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/components/OpacitySlider.js +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/components/SegmentFormSymbology.d.ts +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/components/SourcePropertiesField.d.ts +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/components/StorySegmentReset.d.ts +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/components/WmsTileSourceUrlInput.d.ts +0 -0
- /package/lib/{formbuilder → shared/formbuilder}/objectform/fileselectorwidget.d.ts +0 -0
- /package/lib/{store.d.ts → shared/store.d.ts} +0 -0
- /package/lib/{store.js → shared/store.js} +0 -0
- /package/lib/{menus.d.ts → workspace/menus.d.ts} +0 -0
- /package/lib/{panelview → workspace/panels}/components/layers.d.ts +0 -0
- /package/lib/{panelview → workspace/panels}/components/legendItem.d.ts +0 -0
- /package/lib/{panelview → workspace/panels}/header.d.ts +0 -0
- /package/lib/{panelview → workspace/panels}/header.js +0 -0
- /package/lib/{statusbar → workspace/statusbar}/SpectaPresentationProgressBar.d.ts +0 -0
- /package/lib/{statusbar → workspace/statusbar}/SpectaPresentationProgressBar.js +0 -0
- /package/lib/{statusbar → workspace/statusbar}/StatusBar.d.ts +0 -0
- /package/lib/{toolbar → workspace/toolbar}/index.d.ts +0 -0
- /package/lib/{toolbar → workspace/toolbar}/index.js +0 -0
package/lib/mainview/mainView.js
CHANGED
|
@@ -16,38 +16,70 @@ 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
|
|
26
|
+
import Draw from 'ol/interaction/Draw';
|
|
27
|
+
import Modify from 'ol/interaction/Modify';
|
|
28
|
+
import Snap from 'ol/interaction/Snap';
|
|
29
|
+
import { 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 {
|
|
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";
|
|
41
|
-
import
|
|
46
|
+
import AnnotationFloater from "../features/annotations/components/AnnotationFloater";
|
|
47
|
+
import FeatureFloater from "../features/identify/components/FeatureFloater";
|
|
48
|
+
import { getFeatureIdentifier } from "../features/identify/utils/getFeatureIdentifier";
|
|
42
49
|
import useMediaQuery from "../shared/hooks/useMediaQuery";
|
|
43
|
-
import
|
|
44
|
-
import { debounce, isLightTheme, loadFile, throttle } from "../tools";
|
|
45
|
-
import
|
|
46
|
-
import {
|
|
50
|
+
import { markerIcon } from "../shared/icons";
|
|
51
|
+
import { debounce, INTERNAL_PROXY_BASE, isJupyterLite, isLightTheme, loadFile, throttle, } from "../tools";
|
|
52
|
+
import StatusBar from "../workspace/statusbar/StatusBar";
|
|
53
|
+
import { OpenEOTileLayer, OpenEOTileSource } from './OpenEOTileLayer';
|
|
47
54
|
import TemporalSlider from './TemporalSlider';
|
|
48
|
-
import {
|
|
49
|
-
import {
|
|
50
|
-
import {
|
|
55
|
+
import { MainViewMapSurface } from './components/MainViewMapSurface';
|
|
56
|
+
import { MainViewOverlayLayer } from './components/MainViewOverlayLayer';
|
|
57
|
+
import { MainViewSidePanels } from './components/MainViewSidePanels';
|
|
58
|
+
import { MainViewSpectaPanel } from './components/MainViewSpectaPanel';
|
|
59
|
+
import { MainViewStoryStage } from './components/MainViewStoryStage';
|
|
60
|
+
import { PositionedFloater } from './components/PositionedFloater';
|
|
61
|
+
import { createGeoJSONFeaturePatcher, } from './geoJsonFeaturePatch';
|
|
62
|
+
import { ensureHighlightLayer } from '../features/identify/utils/highlightLayer';
|
|
63
|
+
import { buildHighlightStyle } from '../features/identify/utils/highlightStyle';
|
|
64
|
+
import { grammarToOLLayer } from '../features/layers/symbology/grammarToOLLayer';
|
|
65
|
+
import { extractEncodingFieldValues, grammarToOLStyle, } from '../features/layers/symbology/grammarToOLStyle';
|
|
66
|
+
import { DEFAULT_FLAT_STYLE } from '../features/layers/symbology/styleBuilder';
|
|
67
|
+
import { STORY_TYPE } from '../types';
|
|
68
|
+
const drawInteractionStyle = new Style({
|
|
69
|
+
fill: new Fill({
|
|
70
|
+
color: 'rgba(255, 255, 255, 0.2)',
|
|
71
|
+
}),
|
|
72
|
+
stroke: new Stroke({
|
|
73
|
+
color: '#ffcc33',
|
|
74
|
+
width: 2,
|
|
75
|
+
}),
|
|
76
|
+
image: new CircleStyle({
|
|
77
|
+
radius: 7,
|
|
78
|
+
fill: new Fill({
|
|
79
|
+
color: '#ffcc33',
|
|
80
|
+
}),
|
|
81
|
+
}),
|
|
82
|
+
});
|
|
51
83
|
export class MainView extends React.Component {
|
|
52
84
|
constructor(props) {
|
|
53
85
|
super(props);
|
|
@@ -64,48 +96,8 @@ export class MainView extends React.Component {
|
|
|
64
96
|
return transformExtent(extent, view.getProjection(), targetProjection);
|
|
65
97
|
};
|
|
66
98
|
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
99
|
const selectInteraction = new Select({
|
|
108
|
-
hitTolerance:
|
|
100
|
+
hitTolerance: 3,
|
|
109
101
|
multi: true,
|
|
110
102
|
layers: layer => {
|
|
111
103
|
var _a, _b;
|
|
@@ -115,19 +107,84 @@ export class MainView extends React.Component {
|
|
|
115
107
|
return false;
|
|
116
108
|
}
|
|
117
109
|
const selectedLayerId = Object.keys(selectedLayers)[0];
|
|
118
|
-
|
|
110
|
+
const expected = this.getLayer(selectedLayerId);
|
|
111
|
+
if (layer === expected) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
// Grammar multi-layer symbology wraps sub-layers in a LayerGroup.
|
|
115
|
+
// OL Select flattens groups, so we receive leaf layers, not the group.
|
|
116
|
+
if (expected instanceof LayerGroup) {
|
|
117
|
+
return expected.getLayers().getArray().includes(layer);
|
|
118
|
+
}
|
|
119
|
+
return false;
|
|
119
120
|
},
|
|
120
121
|
condition: (event) => {
|
|
121
122
|
return singleClick(event) && this._model.currentMode === 'identifying';
|
|
122
123
|
},
|
|
123
|
-
style
|
|
124
|
+
// Use the layer's own style so selected features keep their original
|
|
125
|
+
// appearance. Visual highlight feedback comes from _highlightLayer.
|
|
126
|
+
style: null,
|
|
124
127
|
});
|
|
125
128
|
selectInteraction.on('select', event => {
|
|
129
|
+
var _a, _b, _c;
|
|
126
130
|
const identifiedFeatures = [];
|
|
131
|
+
const highlightFeatures = [];
|
|
132
|
+
// Look up the selected layer's style function for adaptive highlights.
|
|
133
|
+
const localState = (_a = this._model) === null || _a === void 0 ? void 0 : _a.sharedModel.awareness.getLocalState();
|
|
134
|
+
const selectedLayers = (_b = localState === null || localState === void 0 ? void 0 : localState.selected) === null || _b === void 0 ? void 0 : _b.value;
|
|
135
|
+
const selectedLayerId = selectedLayers
|
|
136
|
+
? Object.keys(selectedLayers)[0]
|
|
137
|
+
: undefined;
|
|
138
|
+
const mapLayer = selectedLayerId
|
|
139
|
+
? this.getLayer(selectedLayerId)
|
|
140
|
+
: undefined;
|
|
141
|
+
// For LayerGroup (multi-layer grammar), collect style functions from
|
|
142
|
+
// all sub-layers so we can match the right one per feature.
|
|
143
|
+
const styleFnCandidates = [];
|
|
144
|
+
if (mapLayer instanceof LayerGroup) {
|
|
145
|
+
for (const sub of mapLayer.getLayers().getArray()) {
|
|
146
|
+
if ('getStyleFunction' in sub) {
|
|
147
|
+
styleFnCandidates.push(sub.getStyleFunction());
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (mapLayer && 'getStyleFunction' in mapLayer) {
|
|
152
|
+
styleFnCandidates.push(mapLayer.getStyleFunction());
|
|
153
|
+
}
|
|
154
|
+
const resolution = (_c = this._Map.getView().getResolution()) !== null && _c !== void 0 ? _c : 1;
|
|
127
155
|
selectInteraction.getFeatures().forEach(feature => {
|
|
128
|
-
identifiedFeatures.push(
|
|
156
|
+
identifiedFeatures.push({
|
|
157
|
+
feature: feature.getProperties(),
|
|
158
|
+
floaterOpen: false,
|
|
159
|
+
});
|
|
160
|
+
const geom = feature.getGeometry();
|
|
161
|
+
if (geom) {
|
|
162
|
+
const hlFeature = new Feature({ geometry: geom });
|
|
163
|
+
// Try each style function candidate; use the first that resolves
|
|
164
|
+
// a non-empty style array (important for LayerGroup sub-layers
|
|
165
|
+
// where only one sub-layer's style applies to this feature).
|
|
166
|
+
for (const fn of styleFnCandidates) {
|
|
167
|
+
if (!fn) {
|
|
168
|
+
continue;
|
|
169
|
+
}
|
|
170
|
+
const resolved = fn(feature, resolution);
|
|
171
|
+
const styles = Array.isArray(resolved)
|
|
172
|
+
? resolved
|
|
173
|
+
: resolved
|
|
174
|
+
? [resolved]
|
|
175
|
+
: [];
|
|
176
|
+
if (styles.length > 0) {
|
|
177
|
+
const gType = geom.getType();
|
|
178
|
+
hlFeature.setStyle(styles.map(s => this._buildHighlightStyle(s, gType)));
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
highlightFeatures.push(hlFeature);
|
|
183
|
+
}
|
|
129
184
|
});
|
|
130
185
|
this._model.syncIdentifiedFeatures(identifiedFeatures, this._mainViewModel.id);
|
|
186
|
+
// Sync _highlightLayer with the current selection (clears on deselect).
|
|
187
|
+
this._setHighlightFeatures(highlightFeatures);
|
|
131
188
|
});
|
|
132
189
|
this._Map.addInteraction(selectInteraction);
|
|
133
190
|
};
|
|
@@ -210,25 +267,16 @@ export class MainView extends React.Component {
|
|
|
210
267
|
rank: 2,
|
|
211
268
|
});
|
|
212
269
|
};
|
|
270
|
+
// Used by VectorTileLayer (which shares a flat-style API with Grammar output).
|
|
213
271
|
this.vectorLayerStyleRuleBuilder = (layer) => {
|
|
214
272
|
var _a, _b;
|
|
215
273
|
const layerParams = layer.parameters;
|
|
216
|
-
|
|
217
|
-
|
|
274
|
+
const ss = layerParams === null || layerParams === void 0 ? void 0 : layerParams.symbologyState;
|
|
275
|
+
if (!ss || Object.keys(ss).length === 0) {
|
|
276
|
+
return [{ style: DEFAULT_FLAT_STYLE }];
|
|
218
277
|
}
|
|
219
|
-
const
|
|
220
|
-
|
|
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);
|
|
278
|
+
const flatStyle = grammarToOLStyle(layerParams.symbologyState, []);
|
|
279
|
+
const layerStyle = { style: flatStyle };
|
|
232
280
|
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
281
|
const buildCondition = (filter) => {
|
|
234
282
|
const base = [filter.operator, ['get', filter.feature]];
|
|
@@ -236,89 +284,13 @@ export class MainView extends React.Component {
|
|
|
236
284
|
? [...base, filter.betweenMin, filter.betweenMax]
|
|
237
285
|
: [...base, filter.value];
|
|
238
286
|
};
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
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
|
-
}
|
|
287
|
+
layerStyle.filter =
|
|
288
|
+
layer.filters.appliedFilters.length === 1
|
|
289
|
+
? buildCondition(layer.filters.appliedFilters[0])
|
|
290
|
+
: [
|
|
291
|
+
layer.filters.logicalOp,
|
|
292
|
+
...layer.filters.appliedFilters.map(buildCondition),
|
|
293
|
+
];
|
|
322
294
|
}
|
|
323
295
|
return [layerStyle];
|
|
324
296
|
};
|
|
@@ -369,140 +341,36 @@ export class MainView extends React.Component {
|
|
|
369
341
|
const scaled = ['*', 255, cosIncidence];
|
|
370
342
|
return scaled;
|
|
371
343
|
};
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
*/
|
|
377
|
-
this.handleTemporalController = (id, layer) => {
|
|
378
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
379
|
-
const selectedLayer = (_c = (_b = (_a = this._model) === null || _a === void 0 ? void 0 : _a.localState) === null || _b === void 0 ? void 0 : _b.selected) === null || _c === void 0 ? void 0 : _c.value;
|
|
380
|
-
// Temporal Controller shouldn't be active if more than one layer is selected
|
|
381
|
-
if (!selectedLayer || Object.keys(selectedLayer).length !== 1) {
|
|
344
|
+
this._handleSelectedChanged = () => {
|
|
345
|
+
var _a;
|
|
346
|
+
const localState = this._model.localState;
|
|
347
|
+
if (!localState) {
|
|
382
348
|
return;
|
|
383
349
|
}
|
|
384
|
-
const
|
|
385
|
-
|
|
386
|
-
if (selectedLayerId !== id) {
|
|
350
|
+
const selectedLayers = (_a = localState.selected) === null || _a === void 0 ? void 0 : _a.value;
|
|
351
|
+
if (!selectedLayers) {
|
|
387
352
|
return;
|
|
388
353
|
}
|
|
389
|
-
const
|
|
390
|
-
const
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
const activeFilter = layer.filters.appliedFilters[0];
|
|
394
|
-
// Save original features on first filter application
|
|
395
|
-
if (!Object.keys(this._originalFeatures).includes(id)) {
|
|
396
|
-
this._originalFeatures[id] = (_e = source.getFeatures()) !== null && _e !== void 0 ? _e : [];
|
|
397
|
-
}
|
|
398
|
-
// clear current features
|
|
399
|
-
source.clear();
|
|
400
|
-
const startTime = (_f = activeFilter.betweenMin) !== null && _f !== void 0 ? _f : 0;
|
|
401
|
-
const endTime = (_g = activeFilter.betweenMax) !== null && _g !== void 0 ? _g : 1000;
|
|
402
|
-
const filteredFeatures = ((_h = this._originalFeatures[id]) !== null && _h !== void 0 ? _h : []).filter(feature => {
|
|
403
|
-
const featureTime = feature.get(activeFilter.feature);
|
|
404
|
-
return featureTime >= startTime && featureTime <= endTime;
|
|
405
|
-
});
|
|
406
|
-
// set state for restoration
|
|
407
|
-
this.setState(old => (Object.assign(Object.assign({}, old), { filterStates: Object.assign(Object.assign({}, this.state.filterStates), { [selectedLayerId]: activeFilter }) })));
|
|
408
|
-
source.addFeatures(filteredFeatures);
|
|
409
|
-
}
|
|
410
|
-
else {
|
|
411
|
-
// Restore original features when no filters are applied
|
|
412
|
-
source.addFeatures((_j = this._originalFeatures[id]) !== null && _j !== void 0 ? _j : []);
|
|
413
|
-
delete this._originalFeatures[id];
|
|
354
|
+
const selectedLayerId = Object.keys(selectedLayers)[0];
|
|
355
|
+
const JGISLayer = this._model.getLayer(selectedLayerId);
|
|
356
|
+
if (!JGISLayer) {
|
|
357
|
+
return;
|
|
414
358
|
}
|
|
359
|
+
this._syncVectorDrawingFromSelection(JGISLayer, selectedLayerId);
|
|
415
360
|
};
|
|
416
|
-
this.
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
361
|
+
this._syncVectorDrawingFromSelection = (layer, selectedLayerId) => {
|
|
362
|
+
const decision = this._getVectorDrawingSelectionDecision(layer, selectedLayerId);
|
|
363
|
+
if (decision.disableEditing) {
|
|
364
|
+
this._model.editingVectorLayer = false;
|
|
365
|
+
this._updateEditingVectorLayer();
|
|
420
366
|
return;
|
|
421
367
|
}
|
|
422
|
-
|
|
423
|
-
|
|
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
|
-
}
|
|
438
|
-
}
|
|
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
|
-
}
|
|
448
|
-
}
|
|
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);
|
|
368
|
+
if (!decision.shouldRebind) {
|
|
369
|
+
return;
|
|
505
370
|
}
|
|
371
|
+
this._previousDrawLayerID = selectedLayerId;
|
|
372
|
+
this._currentDrawLayerID = selectedLayerId;
|
|
373
|
+
this._editVectorLayer();
|
|
506
374
|
};
|
|
507
375
|
this._onSharedModelStateChange = (_, change) => {
|
|
508
376
|
var _a;
|
|
@@ -521,6 +389,10 @@ export class MainView extends React.Component {
|
|
|
521
389
|
}
|
|
522
390
|
}
|
|
523
391
|
};
|
|
392
|
+
this._handleIdentifiedFeaturesChanged = () => {
|
|
393
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { identifyFeatureFloatersVersion: old.identifyFeatureFloatersVersion + 1 })));
|
|
394
|
+
this._clearHighlightWhenIdentifyDisabled();
|
|
395
|
+
};
|
|
524
396
|
/**
|
|
525
397
|
* Handler for when story maps change in the model.
|
|
526
398
|
* Updates specta state and presentation colors when story data becomes available.
|
|
@@ -561,7 +433,6 @@ export class MainView extends React.Component {
|
|
|
561
433
|
});
|
|
562
434
|
};
|
|
563
435
|
this._setupStoryScrollListener = () => {
|
|
564
|
-
var _a, _b;
|
|
565
436
|
// Guard: block wheel-driven segment change until transition has ended
|
|
566
437
|
let segmentChangeInProgress = false;
|
|
567
438
|
const clearGuard = () => {
|
|
@@ -569,16 +440,42 @@ export class MainView extends React.Component {
|
|
|
569
440
|
};
|
|
570
441
|
this._clearStoryScrollGuard = clearGuard;
|
|
571
442
|
let accumulatedDeltaY = 0;
|
|
572
|
-
let scrollContainer =
|
|
443
|
+
let scrollContainer = null;
|
|
444
|
+
const resolveStoryScrollContainer = () => {
|
|
445
|
+
var _a, _b;
|
|
446
|
+
const fromPanel = (_b = (_a = this.storyViewerPanelRef.current) === null || _a === void 0 ? void 0 : _a.getScrollContainer()) !== null && _b !== void 0 ? _b : null;
|
|
447
|
+
if (fromPanel && document.contains(fromPanel)) {
|
|
448
|
+
return fromPanel;
|
|
449
|
+
}
|
|
450
|
+
const mobileScroll = document.querySelector('.jgis-story-mobile-list-scroll');
|
|
451
|
+
if (mobileScroll instanceof HTMLDivElement) {
|
|
452
|
+
return mobileScroll;
|
|
453
|
+
}
|
|
454
|
+
return null;
|
|
455
|
+
};
|
|
456
|
+
scrollContainer = resolveStoryScrollContainer();
|
|
573
457
|
const processStoryScrollFrame = () => {
|
|
458
|
+
var _a;
|
|
574
459
|
this._pendingStoryScrollRafId = null;
|
|
460
|
+
if (!scrollContainer || !document.contains(scrollContainer)) {
|
|
461
|
+
scrollContainer = resolveStoryScrollContainer();
|
|
462
|
+
}
|
|
575
463
|
const currentPanelHandle = this.storyViewerPanelRef.current;
|
|
576
|
-
|
|
464
|
+
const storyType = (_a = this._model.getSelectedStory().story) === null || _a === void 0 ? void 0 : _a.storyType;
|
|
465
|
+
if (!scrollContainer) {
|
|
577
466
|
accumulatedDeltaY = 0;
|
|
578
467
|
return;
|
|
579
468
|
}
|
|
580
469
|
const deltaY = accumulatedDeltaY;
|
|
581
470
|
accumulatedDeltaY = 0;
|
|
471
|
+
// Don't want to handle next/prev logic in list mode
|
|
472
|
+
if (storyType === STORY_TYPE.verticalScroll) {
|
|
473
|
+
scrollContainer.scrollBy({ top: deltaY });
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
if (!currentPanelHandle) {
|
|
477
|
+
return;
|
|
478
|
+
}
|
|
582
479
|
const isScrollingUp = deltaY < 0;
|
|
583
480
|
const isScrollingDown = deltaY > 0;
|
|
584
481
|
const isAtTop = currentPanelHandle.getAtTop();
|
|
@@ -601,11 +498,10 @@ export class MainView extends React.Component {
|
|
|
601
498
|
scrollContainer.scrollBy({ top: deltaY });
|
|
602
499
|
};
|
|
603
500
|
const handleScroll = (event) => {
|
|
604
|
-
|
|
501
|
+
const wheelEvent = event;
|
|
605
502
|
event.preventDefault();
|
|
606
503
|
if (!scrollContainer || !document.contains(scrollContainer)) {
|
|
607
|
-
scrollContainer =
|
|
608
|
-
(_b = (_a = this.storyViewerPanelRef.current) === null || _a === void 0 ? void 0 : _a.getScrollContainer()) !== null && _b !== void 0 ? _b : null;
|
|
504
|
+
scrollContainer = resolveStoryScrollContainer();
|
|
609
505
|
}
|
|
610
506
|
if (!scrollContainer) {
|
|
611
507
|
return;
|
|
@@ -614,7 +510,7 @@ export class MainView extends React.Component {
|
|
|
614
510
|
// frames on slow hardware). We accumulate deltaY and run flush once per
|
|
615
511
|
// frame via rAF—the frame boundary batches events without adding delay.
|
|
616
512
|
// So one scroll means one segment/scroll decision.
|
|
617
|
-
accumulatedDeltaY +=
|
|
513
|
+
accumulatedDeltaY += wheelEvent.deltaY;
|
|
618
514
|
if (this._pendingStoryScrollRafId === null) {
|
|
619
515
|
this._pendingStoryScrollRafId = requestAnimationFrame(processStoryScrollFrame);
|
|
620
516
|
}
|
|
@@ -652,7 +548,7 @@ export class MainView extends React.Component {
|
|
|
652
548
|
jsonData = JSON.parse(data);
|
|
653
549
|
}
|
|
654
550
|
catch (e) {
|
|
655
|
-
|
|
551
|
+
this._log('warning', `Failed to parse annotation data for ${key}: ${e}`);
|
|
656
552
|
return;
|
|
657
553
|
}
|
|
658
554
|
}
|
|
@@ -682,6 +578,9 @@ export class MainView extends React.Component {
|
|
|
682
578
|
this._handleWindowResize = () => {
|
|
683
579
|
// TODO SOMETHING
|
|
684
580
|
};
|
|
581
|
+
this._handleSegmentTransitionChange = (payload) => {
|
|
582
|
+
this.setState({ segmentTransition: payload });
|
|
583
|
+
};
|
|
685
584
|
this._handleSpectaTouchStart = (e) => {
|
|
686
585
|
if (e.touches.length > 0) {
|
|
687
586
|
this._spectaTouchStartX = e.touches[0].clientX;
|
|
@@ -708,6 +607,148 @@ export class MainView extends React.Component {
|
|
|
708
607
|
this._model.setCurrentSegmentIndex(current + 1);
|
|
709
608
|
}
|
|
710
609
|
};
|
|
610
|
+
this._handleDrawGeometryTypeChange = (
|
|
611
|
+
/* handle with the change of geometry and instantiate new draw interaction and other ones accordingly*/
|
|
612
|
+
event) => {
|
|
613
|
+
const drawGeometryLabel = event.target.value;
|
|
614
|
+
this._currentDrawGeometry = drawGeometryLabel;
|
|
615
|
+
this._updateInteractions();
|
|
616
|
+
this._updateDrawSource();
|
|
617
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { drawGeometryLabel })));
|
|
618
|
+
};
|
|
619
|
+
this._getVectorSourceFromLayerID = (layerID) => {
|
|
620
|
+
/* get the OpenLayers VectorSource corresponding to the JGIS currentDrawLayerID */
|
|
621
|
+
const layers = this._Map.getLayers();
|
|
622
|
+
const layerArray = layers.getArray();
|
|
623
|
+
const matchingLayer = layerArray.find(layer => layer.get('id') === layerID);
|
|
624
|
+
const source = matchingLayer === null || matchingLayer === void 0 ? void 0 : matchingLayer.get('source');
|
|
625
|
+
this._currentVectorSource = source;
|
|
626
|
+
return this._currentVectorSource;
|
|
627
|
+
};
|
|
628
|
+
this._getDrawSourceFromSelectedLayer = () => {
|
|
629
|
+
var _a, _b, _c, _d;
|
|
630
|
+
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;
|
|
631
|
+
if (!selectedLayers) {
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const selectedLayerID = Object.keys(selectedLayers)[0];
|
|
635
|
+
this._currentDrawLayerID = selectedLayerID;
|
|
636
|
+
const JGISLayer = this._model.getLayer(selectedLayerID);
|
|
637
|
+
this._currentDrawSourceID = (_d = JGISLayer === null || JGISLayer === void 0 ? void 0 : JGISLayer.parameters) === null || _d === void 0 ? void 0 : _d.source;
|
|
638
|
+
if (this._currentDrawSourceID) {
|
|
639
|
+
this._currentDrawSource = this._model.getSource(this._currentDrawSourceID);
|
|
640
|
+
}
|
|
641
|
+
};
|
|
642
|
+
this._onVectorSourceChange = () => {
|
|
643
|
+
if (!this._currentVectorSource ||
|
|
644
|
+
!this._currentDrawSource ||
|
|
645
|
+
!this._currentDrawSourceID) {
|
|
646
|
+
return;
|
|
647
|
+
}
|
|
648
|
+
const geojsonWriter = new GeoJSON({
|
|
649
|
+
featureProjection: this._Map.getView().getProjection(),
|
|
650
|
+
});
|
|
651
|
+
const features = this._currentVectorSource
|
|
652
|
+
.getFeatures()
|
|
653
|
+
.map(feature => geojsonWriter.writeFeatureObject(feature));
|
|
654
|
+
const updatedData = {
|
|
655
|
+
type: 'FeatureCollection',
|
|
656
|
+
features: features,
|
|
657
|
+
};
|
|
658
|
+
const updatedJGISLayerSource = {
|
|
659
|
+
name: this._currentDrawSource.name,
|
|
660
|
+
type: this._currentDrawSource.type,
|
|
661
|
+
parameters: {
|
|
662
|
+
data: updatedData,
|
|
663
|
+
},
|
|
664
|
+
};
|
|
665
|
+
this._currentDrawSource = updatedJGISLayerSource;
|
|
666
|
+
this._model.sharedModel.updateSource(this._currentDrawSourceID, updatedJGISLayerSource);
|
|
667
|
+
};
|
|
668
|
+
this._updateDrawSource = () => {
|
|
669
|
+
if (this._currentVectorSource) {
|
|
670
|
+
this._currentVectorSource.on('change', this._onVectorSourceChange);
|
|
671
|
+
}
|
|
672
|
+
};
|
|
673
|
+
this._updateInteractions = () => {
|
|
674
|
+
if (this._draw) {
|
|
675
|
+
this._removeDrawInteraction();
|
|
676
|
+
}
|
|
677
|
+
if (this._select) {
|
|
678
|
+
this._removeSelectInteraction();
|
|
679
|
+
}
|
|
680
|
+
if (this._modify) {
|
|
681
|
+
this._removeModifyInteraction();
|
|
682
|
+
}
|
|
683
|
+
if (this._snap) {
|
|
684
|
+
this._removeSnapInteraction();
|
|
685
|
+
}
|
|
686
|
+
this._draw = new Draw({
|
|
687
|
+
style: drawInteractionStyle,
|
|
688
|
+
type: this._currentDrawGeometry,
|
|
689
|
+
source: this._currentVectorSource,
|
|
690
|
+
});
|
|
691
|
+
this._draw.on('drawend', this._handleDrawEnd);
|
|
692
|
+
this._select = new Select();
|
|
693
|
+
this._modify = new Modify({
|
|
694
|
+
features: this._select.getFeatures(),
|
|
695
|
+
});
|
|
696
|
+
this._snap = new Snap({
|
|
697
|
+
source: this._currentVectorSource,
|
|
698
|
+
});
|
|
699
|
+
this._Map.addInteraction(this._draw);
|
|
700
|
+
this._Map.addInteraction(this._select);
|
|
701
|
+
this._Map.addInteraction(this._modify);
|
|
702
|
+
this._Map.addInteraction(this._snap);
|
|
703
|
+
this._draw.setActive(true);
|
|
704
|
+
this._select.setActive(false);
|
|
705
|
+
this._modify.setActive(false);
|
|
706
|
+
this._snap.setActive(true);
|
|
707
|
+
};
|
|
708
|
+
this._handleDrawEnd = (event) => {
|
|
709
|
+
const feature = event.feature;
|
|
710
|
+
feature.set('_id', UUID.uuid4());
|
|
711
|
+
feature.set('_createdAt', new Date().toISOString());
|
|
712
|
+
feature.set('_creatorClientId', this._model.getClientId().toString());
|
|
713
|
+
feature.set('_fromDrawTool', true);
|
|
714
|
+
feature.set('Label', 'New Label');
|
|
715
|
+
};
|
|
716
|
+
this._editVectorLayer = () => {
|
|
717
|
+
this._getDrawSourceFromSelectedLayer();
|
|
718
|
+
if (!this._currentDrawLayerID) {
|
|
719
|
+
return;
|
|
720
|
+
}
|
|
721
|
+
this._currentVectorSource = this._getVectorSourceFromLayerID(this._currentDrawLayerID);
|
|
722
|
+
if (!this._currentVectorSource || !this._currentDrawGeometry) {
|
|
723
|
+
return;
|
|
724
|
+
}
|
|
725
|
+
this._updateInteractions(); /* remove previous interactions and instantiate new ones */
|
|
726
|
+
this._updateDrawSource(); /*add new features, update source and get changes reported to the JGIS Document in geoJSON format */
|
|
727
|
+
};
|
|
728
|
+
this._removeDrawInteraction = () => {
|
|
729
|
+
this._draw.setActive(false);
|
|
730
|
+
this._Map.removeInteraction(this._draw);
|
|
731
|
+
};
|
|
732
|
+
this._removeSelectInteraction = () => {
|
|
733
|
+
this._select.setActive(false);
|
|
734
|
+
this._Map.removeInteraction(this._select);
|
|
735
|
+
};
|
|
736
|
+
this._removeSnapInteraction = () => {
|
|
737
|
+
this._snap.setActive(false);
|
|
738
|
+
this._Map.removeInteraction(this._snap);
|
|
739
|
+
};
|
|
740
|
+
this._removeModifyInteraction = () => {
|
|
741
|
+
this._modify.setActive(false);
|
|
742
|
+
this._Map.removeInteraction(this._modify);
|
|
743
|
+
};
|
|
744
|
+
/**
|
|
745
|
+
* Shared source update wrapper for child components that need to mutate a
|
|
746
|
+
* source and refresh corresponding map layers.
|
|
747
|
+
*/
|
|
748
|
+
this.persistAndRefreshSource = async (id, source) => {
|
|
749
|
+
this._model.sharedModel.updateSource(id, source);
|
|
750
|
+
await this.updateSource(id, source);
|
|
751
|
+
};
|
|
711
752
|
this._isPositionInitialized = false;
|
|
712
753
|
this.divRef = React.createRef(); // Reference of render div
|
|
713
754
|
this.mainViewRef = React.createRef();
|
|
@@ -717,6 +758,9 @@ export class MainView extends React.Component {
|
|
|
717
758
|
this._ready = false;
|
|
718
759
|
this._sourceToLayerMap = new Map();
|
|
719
760
|
this._originalFeatures = {};
|
|
761
|
+
this._highlightLayerRef = { current: null };
|
|
762
|
+
this._addLayerForPanels = (id, layer, index) => this.addLayer(id, layer, index);
|
|
763
|
+
this._removeLayerForPanels = (id) => this.removeLayer(id);
|
|
720
764
|
this._featurePropertyCache = new Map();
|
|
721
765
|
this._isSpectaPresentationInitialized = false;
|
|
722
766
|
this._storyScrollHandler = null;
|
|
@@ -725,6 +769,7 @@ export class MainView extends React.Component {
|
|
|
725
769
|
this._state = props.state;
|
|
726
770
|
this._formSchemaRegistry = props.formSchemaRegistry;
|
|
727
771
|
this._annotationModel = props.annotationModel;
|
|
772
|
+
this._loggerRegistry = props.loggerRegistry;
|
|
728
773
|
// Enforce the map to take the full available width in the case of Jupyter Notebook viewer
|
|
729
774
|
const el = document.getElementById('main-panel');
|
|
730
775
|
if (el) {
|
|
@@ -747,31 +792,39 @@ export class MainView extends React.Component {
|
|
|
747
792
|
this._mainViewModel = this.props.viewModel;
|
|
748
793
|
this._mainViewModel.viewSettingChanged.connect(this._onViewChanged, this);
|
|
749
794
|
this._model = this._mainViewModel.jGISModel;
|
|
795
|
+
this._patchGeoJSONFeatureProperties = createGeoJSONFeaturePatcher({
|
|
796
|
+
model: this._model,
|
|
797
|
+
persistAndRefreshSource: this.persistAndRefreshSource,
|
|
798
|
+
});
|
|
750
799
|
this._model.themeChanged.connect(this._handleThemeChange, this);
|
|
751
800
|
this._model.sharedOptionsChanged.connect(this._onSharedOptionsChanged, this);
|
|
752
|
-
this._model.
|
|
801
|
+
this._model.temporalControllerActiveChanged.connect(this._handleTemporalControllerActiveChanged, this);
|
|
802
|
+
const remoteUserSignals = [
|
|
803
|
+
this._model.remoteUserChanged,
|
|
804
|
+
this._model.viewportStateChanged,
|
|
805
|
+
];
|
|
806
|
+
remoteUserSignals.forEach(signal => signal.connect(this._handleRemoteUserChanged, this));
|
|
807
|
+
this._model.pointerChanged.connect(this._handlePointerChanged, this);
|
|
808
|
+
this._model.selectedChanged.connect(this._handleTemporalControllerActiveChanged, this);
|
|
809
|
+
this._model.selectedChanged.connect(this._handleSelectedChanged, this);
|
|
753
810
|
this._model.sharedLayersChanged.connect(this._onLayersChanged, this);
|
|
754
811
|
this._model.sharedLayerTreeChanged.connect(this._onLayerTreeChange, this);
|
|
755
812
|
this._model.sharedSourcesChanged.connect(this._onSourcesChange, this);
|
|
756
813
|
this._model.sharedModel.changed.connect(this._onSharedModelStateChange);
|
|
757
814
|
this._model.sharedMetadataChanged.connect(this._onSharedMetadataChanged, this);
|
|
815
|
+
this._model.identifiedFeaturesChanged.connect(this._handleIdentifiedFeaturesChanged, this);
|
|
758
816
|
this._model.zoomToPositionSignal.connect(this._onZoomToPosition, this);
|
|
759
817
|
this._model.settingsChanged.connect(this._onSettingsChanged, this);
|
|
760
818
|
this._model.updateLayerSignal.connect(this._triggerLayerUpdate, this);
|
|
761
819
|
this._model.addFeatureAsMsSignal.connect(this._convertFeatureToMs, this);
|
|
762
820
|
this._model.geolocationChanged.connect(this._handleGeolocationChanged, this);
|
|
821
|
+
// Keep draw editing UI/interactions in sync with the shared editing mode.
|
|
822
|
+
this._model.editingVectorLayerChanged.connect(this._updateEditingVectorLayer, this);
|
|
763
823
|
this._model.flyToGeometrySignal.connect(this.flyToGeometry, this);
|
|
764
824
|
this._model.highlightFeatureSignal.connect(this.highlightFeatureOnMap, this);
|
|
765
825
|
Promise.resolve().then(() => {
|
|
766
826
|
this._syncSettingsFromRegistry();
|
|
767
827
|
});
|
|
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
828
|
this.state = {
|
|
776
829
|
id: this._mainViewModel.id,
|
|
777
830
|
lightTheme: isLightTheme(),
|
|
@@ -784,9 +837,13 @@ export class MainView extends React.Component {
|
|
|
784
837
|
loadingErrors: [],
|
|
785
838
|
displayTemporalController: false,
|
|
786
839
|
filterStates: {},
|
|
840
|
+
editingVectorLayer: false,
|
|
841
|
+
drawGeometryLabel: '',
|
|
787
842
|
jgisSettings: this._model.jgisSettings,
|
|
788
843
|
isSpectaPresentation: this._model.isSpectaMode(),
|
|
789
844
|
initialLayersReady: false,
|
|
845
|
+
identifyFeatureFloatersVersion: 0,
|
|
846
|
+
segmentTransition: null,
|
|
790
847
|
};
|
|
791
848
|
this._sources = [];
|
|
792
849
|
this._loadingLayers = new Set();
|
|
@@ -798,6 +855,10 @@ export class MainView extends React.Component {
|
|
|
798
855
|
}
|
|
799
856
|
async componentDidMount() {
|
|
800
857
|
var _a;
|
|
858
|
+
if (this._loggerRegistry) {
|
|
859
|
+
const logger = this._loggerRegistry.getLogger(this._model.filePath);
|
|
860
|
+
logger.level = 'debug';
|
|
861
|
+
}
|
|
801
862
|
window.addEventListener('resize', this._handleWindowResize);
|
|
802
863
|
const options = this._model.getOptions();
|
|
803
864
|
const projection = (_a = options.projection) !== null && _a !== void 0 ? _a : DEFAULT_PROJECTION;
|
|
@@ -806,6 +867,10 @@ export class MainView extends React.Component {
|
|
|
806
867
|
: [0, 0];
|
|
807
868
|
const zoom = options.zoom !== undefined ? options.zoom : 1;
|
|
808
869
|
await this.generateMap(center, zoom, projection);
|
|
870
|
+
this._handleRemoteUserChanged();
|
|
871
|
+
this._handlePointerChanged();
|
|
872
|
+
this._handleTemporalControllerActiveChanged();
|
|
873
|
+
this._handleSelectedChanged();
|
|
809
874
|
this._mainViewModel.initSignal();
|
|
810
875
|
if (window.jupytergisMaps !== undefined && this._documentPath) {
|
|
811
876
|
window.jupytergisMaps[this._documentPath] = this._Map;
|
|
@@ -828,7 +893,16 @@ export class MainView extends React.Component {
|
|
|
828
893
|
this._model.themeChanged.disconnect(this._handleThemeChange, this);
|
|
829
894
|
this._model.settingsChanged.disconnect(this._onSettingsChanged, this);
|
|
830
895
|
this._model.sharedOptionsChanged.disconnect(this._onSharedOptionsChanged, this);
|
|
831
|
-
this._model.
|
|
896
|
+
this._model.temporalControllerActiveChanged.disconnect(this._handleTemporalControllerActiveChanged, this);
|
|
897
|
+
const remoteUserSignals = [
|
|
898
|
+
this._model.remoteUserChanged,
|
|
899
|
+
this._model.viewportStateChanged,
|
|
900
|
+
];
|
|
901
|
+
remoteUserSignals.forEach(signal => signal.disconnect(this._handleRemoteUserChanged, this));
|
|
902
|
+
this._model.pointerChanged.disconnect(this._handlePointerChanged, this);
|
|
903
|
+
this._model.selectedChanged.disconnect(this._handleTemporalControllerActiveChanged, this);
|
|
904
|
+
this._model.selectedChanged.disconnect(this._handleSelectedChanged, this);
|
|
905
|
+
this._model.identifiedFeaturesChanged.disconnect(this._handleIdentifiedFeaturesChanged, this);
|
|
832
906
|
// Clean up story scroll listener
|
|
833
907
|
this._cleanupStoryScrollListener();
|
|
834
908
|
this._mainViewModel.dispose();
|
|
@@ -836,17 +910,13 @@ export class MainView extends React.Component {
|
|
|
836
910
|
async generateMap(center, zoom, projection = DEFAULT_PROJECTION) {
|
|
837
911
|
const layers = this._model.getLayers();
|
|
838
912
|
this._initialLayersCount = Object.values(layers).filter(layer => layer.type !== 'StorySegmentLayer').length;
|
|
839
|
-
const
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
});
|
|
845
|
-
const controls = [scaleLine, fullScreen];
|
|
913
|
+
const controlsToolbar = this.controlsToolbarRef.current || undefined;
|
|
914
|
+
const controls = [new ScaleLine({ target: controlsToolbar })];
|
|
915
|
+
if (!this._model.isSpectaMode()) {
|
|
916
|
+
controls.push(new FullScreen({ target: controlsToolbar }));
|
|
917
|
+
}
|
|
846
918
|
if (this._model.jgisSettings.zoomButtonsEnabled) {
|
|
847
|
-
this._zoomControl = new Zoom({
|
|
848
|
-
target: this.controlsToolbarRef.current || undefined,
|
|
849
|
-
});
|
|
919
|
+
this._zoomControl = new Zoom({ target: controlsToolbar });
|
|
850
920
|
controls.push(this._zoomControl);
|
|
851
921
|
}
|
|
852
922
|
if (this.divRef.current) {
|
|
@@ -892,9 +962,7 @@ export class MainView extends React.Component {
|
|
|
892
962
|
this._Map.addInteraction(dragAndDropInteraction);
|
|
893
963
|
this.createSelectInteraction();
|
|
894
964
|
const view = this._Map.getView();
|
|
895
|
-
|
|
896
|
-
// TODO: Note for the future, will need to update listeners if view changes
|
|
897
|
-
view.on('change:center', throttle(() => {
|
|
965
|
+
const syncViewportThrottled = throttle(() => {
|
|
898
966
|
var _a;
|
|
899
967
|
// Not syncing center if following someone else
|
|
900
968
|
if ((_a = this._model.localState) === null || _a === void 0 ? void 0 : _a.remoteUser) {
|
|
@@ -906,18 +974,30 @@ export class MainView extends React.Component {
|
|
|
906
974
|
if (!center || !zoom) {
|
|
907
975
|
return;
|
|
908
976
|
}
|
|
977
|
+
const currentExtent = view.calculateExtent(this._Map.getSize());
|
|
909
978
|
this._model.syncViewport({
|
|
910
979
|
coordinates: {
|
|
911
980
|
x: center[0],
|
|
912
981
|
y: center[1],
|
|
913
982
|
},
|
|
914
983
|
zoom,
|
|
984
|
+
extent: [
|
|
985
|
+
currentExtent[0],
|
|
986
|
+
currentExtent[1],
|
|
987
|
+
currentExtent[2],
|
|
988
|
+
currentExtent[3],
|
|
989
|
+
],
|
|
915
990
|
}, this._mainViewModel.id);
|
|
916
|
-
})
|
|
991
|
+
}, 200);
|
|
992
|
+
view.on('change:center', () => {
|
|
993
|
+
this._updateCenter();
|
|
994
|
+
syncViewportThrottled();
|
|
995
|
+
});
|
|
917
996
|
this._Map.on('postrender', () => {
|
|
918
997
|
if (this.state.annotations) {
|
|
919
998
|
this._updateAnnotation();
|
|
920
999
|
}
|
|
1000
|
+
this._updateFeatureFloaters();
|
|
921
1001
|
});
|
|
922
1002
|
this._Map.on('moveend', () => {
|
|
923
1003
|
var _a;
|
|
@@ -981,241 +1061,359 @@ export class MainView extends React.Component {
|
|
|
981
1061
|
* @param source - the source object.
|
|
982
1062
|
*/
|
|
983
1063
|
async addSource(id, source) {
|
|
984
|
-
var _a, _b, _c;
|
|
1064
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
1065
|
+
this._log('info', `Loading source "${(_a = source.name) !== null && _a !== void 0 ? _a : id}" (${source.type})`);
|
|
985
1066
|
let newSource;
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
sourceParameters.url.endsWith('pmtiles
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
1067
|
+
try {
|
|
1068
|
+
switch (source.type) {
|
|
1069
|
+
case 'RasterSource': {
|
|
1070
|
+
const sourceParameters = source.parameters;
|
|
1071
|
+
const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
|
|
1072
|
+
sourceParameters.url.endsWith('pmtiles.gz');
|
|
1073
|
+
const url = this.computeSourceUrl(source);
|
|
1074
|
+
if (!pmTiles) {
|
|
1075
|
+
newSource = new XYZSource({
|
|
1076
|
+
interpolate: sourceParameters.interpolate,
|
|
1077
|
+
attributions: sourceParameters.attribution,
|
|
1078
|
+
minZoom: sourceParameters.minZoom,
|
|
1079
|
+
maxZoom: sourceParameters.maxZoom,
|
|
1080
|
+
tileSize: 256,
|
|
1081
|
+
url: url,
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
newSource = new PMTilesRasterSource({
|
|
1086
|
+
interpolate: sourceParameters.interpolate,
|
|
1087
|
+
attributions: sourceParameters.attribution,
|
|
1088
|
+
tileSize: 256,
|
|
1089
|
+
url: url,
|
|
1090
|
+
});
|
|
1091
|
+
}
|
|
1092
|
+
break;
|
|
1093
|
+
}
|
|
1094
|
+
case 'RasterDemSource': {
|
|
1095
|
+
const sourceParameters = source.parameters;
|
|
1096
|
+
newSource = new ImageTileSource({
|
|
994
1097
|
interpolate: sourceParameters.interpolate,
|
|
1098
|
+
url: this.computeSourceUrl(source),
|
|
995
1099
|
attributions: sourceParameters.attribution,
|
|
996
|
-
minZoom: sourceParameters.minZoom,
|
|
997
|
-
maxZoom: sourceParameters.maxZoom,
|
|
998
|
-
tileSize: 256,
|
|
999
|
-
url: url,
|
|
1000
1100
|
});
|
|
1101
|
+
break;
|
|
1001
1102
|
}
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1103
|
+
case 'VectorTileSource': {
|
|
1104
|
+
const sourceParameters = source.parameters;
|
|
1105
|
+
const pmTiles = sourceParameters.url.endsWith('.pmtiles') ||
|
|
1106
|
+
sourceParameters.url.endsWith('pmtiles.gz');
|
|
1107
|
+
const url = this.computeSourceUrl(source);
|
|
1108
|
+
if (!pmTiles) {
|
|
1109
|
+
const vtSourceOptions = {
|
|
1110
|
+
attributions: sourceParameters.attribution,
|
|
1111
|
+
minZoom: sourceParameters.minZoom,
|
|
1112
|
+
maxZoom: sourceParameters.maxZoom,
|
|
1113
|
+
url: url,
|
|
1114
|
+
format: new MVT({
|
|
1115
|
+
featureClass: RenderFeature,
|
|
1116
|
+
}),
|
|
1117
|
+
};
|
|
1118
|
+
if (sourceParameters.useProxy) {
|
|
1119
|
+
const extraHeaders = (_b = sourceParameters.httpHeaders) !== null && _b !== void 0 ? _b : {};
|
|
1120
|
+
const headersParam = Object.keys(extraHeaders).length > 0
|
|
1121
|
+
? `&headers=${encodeURIComponent(JSON.stringify(extraHeaders))}`
|
|
1122
|
+
: '';
|
|
1123
|
+
const proxyBase = isJupyterLite()
|
|
1124
|
+
? `${this._model.jgisSettings.proxyUrl}/`
|
|
1125
|
+
: `${INTERNAL_PROXY_BASE}`;
|
|
1126
|
+
vtSourceOptions.tileLoadFunction = (tile, tileUrl) => {
|
|
1127
|
+
const vtTile = tile;
|
|
1128
|
+
const proxyUrl = `${proxyBase}?url=${encodeURIComponent(tileUrl)}${headersParam}`;
|
|
1129
|
+
vtTile.setLoader((extent, _resolution, projection) => {
|
|
1130
|
+
return fetch(proxyUrl)
|
|
1131
|
+
.then(response => {
|
|
1132
|
+
if (!response.ok) {
|
|
1133
|
+
throw new Error(`Tile proxy request failed: ${response.status} ${response.statusText}`);
|
|
1134
|
+
}
|
|
1135
|
+
return response.arrayBuffer();
|
|
1136
|
+
})
|
|
1137
|
+
.then(data => {
|
|
1138
|
+
const features = vtTile.getFormat().readFeatures(data, {
|
|
1139
|
+
extent,
|
|
1140
|
+
featureProjection: projection,
|
|
1141
|
+
});
|
|
1142
|
+
vtTile.setFeatures(features);
|
|
1143
|
+
this._log('debug', `Proxy tile loaded: ${tileUrl}`);
|
|
1144
|
+
return features;
|
|
1145
|
+
})
|
|
1146
|
+
.catch((err) => {
|
|
1147
|
+
this._log('error', `Proxy tile error for ${tileUrl}: ${err.message}`);
|
|
1148
|
+
tile.setState(TileState.ERROR);
|
|
1149
|
+
return [];
|
|
1150
|
+
});
|
|
1151
|
+
});
|
|
1152
|
+
};
|
|
1153
|
+
}
|
|
1154
|
+
newSource = new VectorTileSource(vtSourceOptions);
|
|
1155
|
+
}
|
|
1156
|
+
else {
|
|
1157
|
+
newSource = new PMTilesVectorSource({
|
|
1158
|
+
attributions: sourceParameters.attribution,
|
|
1159
|
+
url: url,
|
|
1160
|
+
});
|
|
1161
|
+
}
|
|
1162
|
+
newSource.on('tileloadend', (event) => {
|
|
1163
|
+
const tile = event.tile;
|
|
1164
|
+
const features = tile.getFeatures();
|
|
1165
|
+
if (features && features.length > 0) {
|
|
1166
|
+
this._model.syncTileFeatures({
|
|
1167
|
+
sourceId: id,
|
|
1168
|
+
features,
|
|
1169
|
+
});
|
|
1170
|
+
}
|
|
1008
1171
|
});
|
|
1172
|
+
break;
|
|
1009
1173
|
}
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
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
|
-
}),
|
|
1174
|
+
case 'OpenEOTileSource': {
|
|
1175
|
+
const sourceParameters = source.parameters;
|
|
1176
|
+
newSource = new OpenEOTileSource({
|
|
1177
|
+
connectionInfo: {
|
|
1178
|
+
url: sourceParameters.serverUrl,
|
|
1179
|
+
authBearer: sourceParameters.authBearer,
|
|
1180
|
+
},
|
|
1181
|
+
processGraph: sourceParameters.processGraph,
|
|
1035
1182
|
});
|
|
1183
|
+
break;
|
|
1036
1184
|
}
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1185
|
+
case 'GeoJSONSource': {
|
|
1186
|
+
const data = ((_c = source.parameters) === null || _c === void 0 ? void 0 : _c.data) ||
|
|
1187
|
+
(await loadFile({
|
|
1188
|
+
filepath: (_d = source.parameters) === null || _d === void 0 ? void 0 : _d.path,
|
|
1189
|
+
type: 'GeoJSONSource',
|
|
1190
|
+
model: this._model,
|
|
1191
|
+
}));
|
|
1192
|
+
const format = new GeoJSON({
|
|
1193
|
+
featureProjection: this._Map.getView().getProjection(),
|
|
1194
|
+
});
|
|
1195
|
+
const featureArray = format.readFeatures(data, {
|
|
1196
|
+
featureProjection: this._Map.getView().getProjection(),
|
|
1197
|
+
});
|
|
1198
|
+
const featureCollection = new Collection(featureArray);
|
|
1199
|
+
featureCollection.forEach(feature => {
|
|
1200
|
+
feature.setId(getUid(feature));
|
|
1041
1201
|
});
|
|
1202
|
+
newSource = new VectorSource({
|
|
1203
|
+
features: featureCollection,
|
|
1204
|
+
});
|
|
1205
|
+
break;
|
|
1042
1206
|
}
|
|
1043
|
-
|
|
1044
|
-
const
|
|
1045
|
-
const
|
|
1046
|
-
|
|
1047
|
-
|
|
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',
|
|
1207
|
+
case 'ShapefileSource': {
|
|
1208
|
+
const parameters = source.parameters;
|
|
1209
|
+
const geojson = await loadFile({
|
|
1210
|
+
filepath: parameters.path,
|
|
1211
|
+
type: 'ShapefileSource',
|
|
1060
1212
|
model: this._model,
|
|
1213
|
+
});
|
|
1214
|
+
const geojsonData = Array.isArray(geojson) ? geojson[0] : geojson;
|
|
1215
|
+
const format = new GeoJSON();
|
|
1216
|
+
newSource = new VectorSource({
|
|
1217
|
+
features: format.readFeatures(geojsonData, {
|
|
1218
|
+
dataProjection: 'EPSG:4326',
|
|
1219
|
+
featureProjection: this._Map.getView().getProjection(),
|
|
1220
|
+
}),
|
|
1221
|
+
});
|
|
1222
|
+
break;
|
|
1223
|
+
}
|
|
1224
|
+
case 'ImageSource': {
|
|
1225
|
+
const sourceParameters = source.parameters;
|
|
1226
|
+
// Convert lon/lat array to extent
|
|
1227
|
+
// Get lon/lat from source coordinates
|
|
1228
|
+
const leftSide = Math.min(...sourceParameters.coordinates.map(corner => corner[0]));
|
|
1229
|
+
const bottomSide = Math.min(...sourceParameters.coordinates.map(corner => corner[1]));
|
|
1230
|
+
const rightSide = Math.max(...sourceParameters.coordinates.map(corner => corner[0]));
|
|
1231
|
+
const topSide = Math.max(...sourceParameters.coordinates.map(corner => corner[1]));
|
|
1232
|
+
// Convert lon/lat to OpenLayer coordinates
|
|
1233
|
+
const topLeft = fromLonLat([leftSide, topSide]);
|
|
1234
|
+
const bottomRight = fromLonLat([rightSide, bottomSide]);
|
|
1235
|
+
// Get extent from coordinates
|
|
1236
|
+
const minX = topLeft[0];
|
|
1237
|
+
const maxY = topLeft[1];
|
|
1238
|
+
const maxX = bottomRight[0];
|
|
1239
|
+
const minY = bottomRight[1];
|
|
1240
|
+
const extent = [minX, minY, maxX, maxY];
|
|
1241
|
+
const imageUrl = await loadFile({
|
|
1242
|
+
filepath: sourceParameters.path,
|
|
1243
|
+
type: 'ImageSource',
|
|
1244
|
+
model: this._model,
|
|
1245
|
+
});
|
|
1246
|
+
newSource = new Static({
|
|
1247
|
+
interpolate: sourceParameters.interpolate,
|
|
1248
|
+
imageExtent: extent,
|
|
1249
|
+
url: imageUrl,
|
|
1250
|
+
crossOrigin: '',
|
|
1251
|
+
});
|
|
1252
|
+
break;
|
|
1253
|
+
}
|
|
1254
|
+
case 'VideoSource': {
|
|
1255
|
+
this._log('warning', 'Video Tiles not supported with Open Layers');
|
|
1256
|
+
break;
|
|
1257
|
+
}
|
|
1258
|
+
case 'GeoTiffSource': {
|
|
1259
|
+
const sourceParameters = source.parameters;
|
|
1260
|
+
const addNoData = (url) => {
|
|
1261
|
+
return Object.assign(Object.assign({}, url), { nodata: 0 });
|
|
1262
|
+
};
|
|
1263
|
+
const sources = await Promise.all(sourceParameters.urls.map(async (sourceInfo) => {
|
|
1264
|
+
var _a, _b, _c, _d;
|
|
1265
|
+
const isRemote = ((_a = sourceInfo.url) === null || _a === void 0 ? void 0 : _a.startsWith('http://')) ||
|
|
1266
|
+
((_b = sourceInfo.url) === null || _b === void 0 ? void 0 : _b.startsWith('https://'));
|
|
1267
|
+
const isDataUrl = (_c = sourceInfo.url) === null || _c === void 0 ? void 0 : _c.startsWith('data:');
|
|
1268
|
+
if (isRemote) {
|
|
1269
|
+
return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, url: sourceInfo.url });
|
|
1270
|
+
}
|
|
1271
|
+
else if (isDataUrl) {
|
|
1272
|
+
// Inline base64 GeoTIFF embedded in the .jGIS doc.
|
|
1273
|
+
const blob = await (await fetch(sourceInfo.url)).blob();
|
|
1274
|
+
return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, url: URL.createObjectURL(blob) });
|
|
1275
|
+
}
|
|
1276
|
+
else {
|
|
1277
|
+
const geotiff = await loadFile({
|
|
1278
|
+
filepath: (_d = sourceInfo.url) !== null && _d !== void 0 ? _d : '',
|
|
1279
|
+
type: 'GeoTiffSource',
|
|
1280
|
+
model: this._model,
|
|
1281
|
+
});
|
|
1282
|
+
return Object.assign(Object.assign({}, addNoData(sourceInfo)), { min: sourceInfo.min, max: sourceInfo.max, geotiff, url: URL.createObjectURL(geotiff.file) });
|
|
1283
|
+
}
|
|
1061
1284
|
}));
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
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 });
|
|
1285
|
+
newSource = new GeoTIFFSource({
|
|
1286
|
+
interpolate: sourceParameters.interpolate,
|
|
1287
|
+
sources,
|
|
1288
|
+
normalize: sourceParameters.normalize,
|
|
1289
|
+
wrapX: sourceParameters.wrapX,
|
|
1290
|
+
});
|
|
1291
|
+
break;
|
|
1292
|
+
}
|
|
1293
|
+
case 'GeoPackageVectorSource': {
|
|
1294
|
+
const sourceParameters = source.parameters;
|
|
1295
|
+
if (!sourceParameters) {
|
|
1296
|
+
throw new Error('GeoPackageSource has no parameters');
|
|
1139
1297
|
}
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1298
|
+
const tableMap = await loadFile({
|
|
1299
|
+
filepath: sourceParameters.path,
|
|
1300
|
+
type: 'GeoPackageVectorSource',
|
|
1301
|
+
model: this._model,
|
|
1302
|
+
});
|
|
1303
|
+
const table = tableMap[sourceParameters.tables];
|
|
1304
|
+
const vectorSource = table.source;
|
|
1305
|
+
vectorSource['projection'] = getProjection(sourceParameters.projection);
|
|
1306
|
+
newSource = vectorSource;
|
|
1307
|
+
break;
|
|
1308
|
+
}
|
|
1309
|
+
case 'GeoPackageRasterSource': {
|
|
1310
|
+
const sourceParameters = source.parameters;
|
|
1311
|
+
if (!sourceParameters) {
|
|
1312
|
+
throw new Error('GeoPackageSource has no parameters');
|
|
1147
1313
|
}
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
})
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
}
|
|
1212
|
-
|
|
1213
|
-
|
|
1314
|
+
const tableMap = await loadFile({
|
|
1315
|
+
filepath: sourceParameters.path,
|
|
1316
|
+
type: 'GeoPackageRasterSource',
|
|
1317
|
+
model: this._model,
|
|
1318
|
+
});
|
|
1319
|
+
const { gpr, tileDao } = tableMap[sourceParameters.tables];
|
|
1320
|
+
const rasterSource = new XYZSource({
|
|
1321
|
+
minZoom: (_e = sourceParameters.minZoom) !== null && _e !== void 0 ? _e : tileDao.minWebMapZoom,
|
|
1322
|
+
maxZoom: (_f = sourceParameters.maxZoom) !== null && _f !== void 0 ? _f : tileDao.maxWebMapZoom,
|
|
1323
|
+
interpolate: sourceParameters.interpolate,
|
|
1324
|
+
url: '{z},{x},{y}',
|
|
1325
|
+
tileLoadFunction(tile, src) {
|
|
1326
|
+
const [z, x, y] = src.split(',').map(Number);
|
|
1327
|
+
gpr
|
|
1328
|
+
.getTile(x, y, z)
|
|
1329
|
+
.then((dataUri) => (tile.getImage().src = dataUri));
|
|
1330
|
+
},
|
|
1331
|
+
attributions: sourceParameters.attribution,
|
|
1332
|
+
});
|
|
1333
|
+
newSource = rasterSource;
|
|
1334
|
+
break;
|
|
1335
|
+
}
|
|
1336
|
+
case 'GeoParquetSource': {
|
|
1337
|
+
const parameters = source.parameters;
|
|
1338
|
+
const geojson = await loadFile({
|
|
1339
|
+
filepath: parameters.path,
|
|
1340
|
+
type: 'GeoParquetSource',
|
|
1341
|
+
model: this._model,
|
|
1342
|
+
});
|
|
1343
|
+
const geojsonData = Array.isArray(geojson) ? geojson[0] : geojson;
|
|
1344
|
+
const format = new GeoJSON();
|
|
1345
|
+
newSource = new VectorSource({
|
|
1346
|
+
features: format.readFeatures(geojsonData, {
|
|
1347
|
+
dataProjection: parameters.projection,
|
|
1348
|
+
featureProjection: this._Map.getView().getProjection(),
|
|
1349
|
+
}),
|
|
1350
|
+
});
|
|
1351
|
+
break;
|
|
1352
|
+
}
|
|
1353
|
+
case 'MarkerSource': {
|
|
1354
|
+
const parameters = source.parameters;
|
|
1355
|
+
const point = new Point(parameters.feature.coords);
|
|
1356
|
+
const marker = new Feature({
|
|
1357
|
+
type: 'icon',
|
|
1358
|
+
geometry: point,
|
|
1359
|
+
});
|
|
1360
|
+
// Replace color placeholder in SVG with the parameter color
|
|
1361
|
+
const markerColor = parameters.color || '#3463a0';
|
|
1362
|
+
const svgString = markerIcon.svgstr
|
|
1363
|
+
.replace('{{COLOR}}', markerColor)
|
|
1364
|
+
.replace('<svg', '<svg width="128" height="128"');
|
|
1365
|
+
const iconStyle = new Style({
|
|
1366
|
+
image: new Icon({
|
|
1367
|
+
src: `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`,
|
|
1368
|
+
scale: 0.25,
|
|
1369
|
+
anchor: [0.5, 1],
|
|
1370
|
+
anchorXUnits: 'fraction',
|
|
1371
|
+
anchorYUnits: 'fraction',
|
|
1372
|
+
}),
|
|
1373
|
+
});
|
|
1374
|
+
marker.setStyle(iconStyle);
|
|
1375
|
+
newSource = new VectorSource({
|
|
1376
|
+
features: [marker],
|
|
1377
|
+
});
|
|
1378
|
+
break;
|
|
1379
|
+
}
|
|
1380
|
+
case 'WmsTileSource': {
|
|
1381
|
+
const sourceParameters = source.parameters;
|
|
1382
|
+
const url = sourceParameters.url;
|
|
1383
|
+
const selectedLayer = (_g = sourceParameters === null || sourceParameters === void 0 ? void 0 : sourceParameters.params) === null || _g === void 0 ? void 0 : _g.layers;
|
|
1384
|
+
newSource = new TileWMSSource({
|
|
1385
|
+
attributions: sourceParameters === null || sourceParameters === void 0 ? void 0 : sourceParameters.attribution,
|
|
1386
|
+
url,
|
|
1387
|
+
params: {
|
|
1388
|
+
LAYERS: selectedLayer,
|
|
1389
|
+
TILED: true,
|
|
1390
|
+
},
|
|
1391
|
+
});
|
|
1392
|
+
break;
|
|
1393
|
+
}
|
|
1214
1394
|
}
|
|
1215
1395
|
}
|
|
1396
|
+
catch (err) {
|
|
1397
|
+
this._log('error', `Failed to load source "${(_h = source.name) !== null && _h !== void 0 ? _h : id}" (${source.type}): ${err.message}`);
|
|
1398
|
+
return;
|
|
1399
|
+
}
|
|
1400
|
+
this._log('info', `Source "${(_j = source.name) !== null && _j !== void 0 ? _j : id}" (${source.type}) loaded successfully`);
|
|
1216
1401
|
newSource.set('id', id);
|
|
1402
|
+
// Forward OL tile/feature load errors to the JupyterLab log console.
|
|
1403
|
+
// These errors (CORS failures, network errors, etc.) are written directly
|
|
1404
|
+
// by the browser to DevTools and cannot be captured by console patching —
|
|
1405
|
+
// OL's own events are the only reliable interception point.
|
|
1406
|
+
newSource.on('tileloaderror', (evt) => {
|
|
1407
|
+
var _a, _b, _c;
|
|
1408
|
+
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 : '';
|
|
1409
|
+
this._log('error', `Tile load error for source "${id}"${url ? ': ' + url : ''}`);
|
|
1410
|
+
});
|
|
1411
|
+
newSource.on('featuresloaderror', () => {
|
|
1412
|
+
this._log('error', `Features load error for source "${id}"`);
|
|
1413
|
+
});
|
|
1217
1414
|
// _sources is a list of OpenLayers sources
|
|
1218
1415
|
this._sources[id] = newSource;
|
|
1416
|
+
this._trackSourceExtZoom(id, newSource);
|
|
1219
1417
|
}
|
|
1220
1418
|
computeSourceUrl(source) {
|
|
1221
1419
|
const parameters = source.parameters;
|
|
@@ -1286,8 +1484,11 @@ export class MainView extends React.Component {
|
|
|
1286
1484
|
for (let targetLayerPosition = 0; targetLayerPosition < layerIds.length; targetLayerPosition++) {
|
|
1287
1485
|
const layerId = layerIds[targetLayerPosition];
|
|
1288
1486
|
const layer = this._model.sharedModel.getLayer(layerId);
|
|
1487
|
+
if (this._loadingLayers.has(layerId)) {
|
|
1488
|
+
continue;
|
|
1489
|
+
}
|
|
1289
1490
|
if (!layer) {
|
|
1290
|
-
|
|
1491
|
+
this._log('warning', `Layer with ID ${layerId} does not exist in the shared model.`);
|
|
1291
1492
|
continue;
|
|
1292
1493
|
}
|
|
1293
1494
|
const mapLayer = this.getLayer(layerId);
|
|
@@ -1319,7 +1520,7 @@ export class MainView extends React.Component {
|
|
|
1319
1520
|
* @returns - the map layer.
|
|
1320
1521
|
*/
|
|
1321
1522
|
async _buildMapLayer(id, layer) {
|
|
1322
|
-
var _a, _b, _c;
|
|
1523
|
+
var _a, _b, _c, _d, _e;
|
|
1323
1524
|
this.setState(old => (Object.assign(Object.assign({}, old), { loadingLayer: true })));
|
|
1324
1525
|
this._loadingLayers.add(id);
|
|
1325
1526
|
let newMapLayer;
|
|
@@ -1354,12 +1555,23 @@ export class MainView extends React.Component {
|
|
|
1354
1555
|
}
|
|
1355
1556
|
case 'VectorLayer': {
|
|
1356
1557
|
layerParameters = layer.parameters;
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1558
|
+
if (Array.isArray((_b = layerParameters.symbologyState) === null || _b === void 0 ? void 0 : _b.layers)) {
|
|
1559
|
+
const olSource = this._sources[layerParameters.source];
|
|
1560
|
+
const grammarState = layerParameters.symbologyState;
|
|
1561
|
+
const rows = olSource instanceof VectorSource
|
|
1562
|
+
? olSource.getFeatures().map(f => f.getProperties())
|
|
1563
|
+
: [];
|
|
1564
|
+
const featureValues = extractEncodingFieldValues(grammarState, rows);
|
|
1565
|
+
newMapLayer = grammarToOLLayer(layerParameters.symbologyState, olSource, layerParameters.opacity, layer.visible, featureValues);
|
|
1566
|
+
}
|
|
1567
|
+
else {
|
|
1568
|
+
newMapLayer = new VectorImageLayer({
|
|
1569
|
+
opacity: layerParameters.opacity,
|
|
1570
|
+
visible: layer.visible,
|
|
1571
|
+
source: this._sources[layerParameters.source],
|
|
1572
|
+
style: this.vectorLayerStyleRuleBuilder(layer),
|
|
1573
|
+
});
|
|
1574
|
+
}
|
|
1363
1575
|
break;
|
|
1364
1576
|
}
|
|
1365
1577
|
case 'VectorTileLayer': {
|
|
@@ -1374,7 +1586,7 @@ export class MainView extends React.Component {
|
|
|
1374
1586
|
}
|
|
1375
1587
|
case 'HillshadeLayer': {
|
|
1376
1588
|
layerParameters = layer.parameters;
|
|
1377
|
-
newMapLayer = new
|
|
1589
|
+
newMapLayer = new GeoTiffLayer({
|
|
1378
1590
|
opacity: 0.3,
|
|
1379
1591
|
visible: layer.visible,
|
|
1380
1592
|
source: this._sources[layerParameters.source],
|
|
@@ -1393,32 +1605,35 @@ export class MainView extends React.Component {
|
|
|
1393
1605
|
});
|
|
1394
1606
|
break;
|
|
1395
1607
|
}
|
|
1396
|
-
case '
|
|
1608
|
+
case 'OpenEOTileLayer': {
|
|
1397
1609
|
layerParameters = layer.parameters;
|
|
1398
|
-
|
|
1399
|
-
const layerOptions = {
|
|
1610
|
+
newMapLayer = new OpenEOTileLayer({
|
|
1400
1611
|
opacity: layerParameters.opacity,
|
|
1401
1612
|
visible: layer.visible,
|
|
1402
1613
|
source: this._sources[layerParameters.source],
|
|
1403
|
-
};
|
|
1404
|
-
if (layerParameters.color) {
|
|
1405
|
-
layerOptions['style'] = {
|
|
1406
|
-
color: layerParameters.color,
|
|
1407
|
-
};
|
|
1408
|
-
}
|
|
1409
|
-
newMapLayer = new WebGlTileLayer(layerOptions);
|
|
1614
|
+
});
|
|
1410
1615
|
break;
|
|
1411
1616
|
}
|
|
1412
|
-
case '
|
|
1617
|
+
case 'GeoTiffLayer': {
|
|
1413
1618
|
layerParameters = layer.parameters;
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1619
|
+
const geoTiffSource = this._sources[layerParameters.source];
|
|
1620
|
+
if (Array.isArray((_c = layerParameters.symbologyState) === null || _c === void 0 ? void 0 : _c.layers)) {
|
|
1621
|
+
newMapLayer = grammarToOLLayer(layerParameters.symbologyState, geoTiffSource, (_d = layerParameters.opacity) !== null && _d !== void 0 ? _d : 1, (_e = layer.visible) !== null && _e !== void 0 ? _e : true, [], true);
|
|
1622
|
+
}
|
|
1623
|
+
else {
|
|
1624
|
+
// This is to handle python sending a None for the color
|
|
1625
|
+
const layerOptions = {
|
|
1626
|
+
opacity: layerParameters.opacity,
|
|
1627
|
+
visible: layer.visible,
|
|
1628
|
+
source: geoTiffSource,
|
|
1629
|
+
};
|
|
1630
|
+
if (layerParameters.color) {
|
|
1631
|
+
layerOptions['style'] = {
|
|
1632
|
+
color: layerParameters.color,
|
|
1633
|
+
};
|
|
1634
|
+
}
|
|
1635
|
+
newMapLayer = new GeoTiffLayer(layerOptions);
|
|
1636
|
+
}
|
|
1422
1637
|
break;
|
|
1423
1638
|
}
|
|
1424
1639
|
case 'StacLayer': {
|
|
@@ -1458,7 +1673,7 @@ export class MainView extends React.Component {
|
|
|
1458
1673
|
var _a;
|
|
1459
1674
|
const sourceProjection = (_a = newMapLayer.getSource()) === null || _a === void 0 ? void 0 : _a.getProjection();
|
|
1460
1675
|
if (!sourceProjection) {
|
|
1461
|
-
|
|
1676
|
+
this._log('warning', 'Layer source projection is undefined or invalid');
|
|
1462
1677
|
return;
|
|
1463
1678
|
}
|
|
1464
1679
|
const projectionCode = sourceProjection.getCode();
|
|
@@ -1466,7 +1681,7 @@ export class MainView extends React.Component {
|
|
|
1466
1681
|
if (!isProjectionRegistered) {
|
|
1467
1682
|
// Check if the projection exists in proj4list
|
|
1468
1683
|
if (!proj4list[projectionCode]) {
|
|
1469
|
-
|
|
1684
|
+
this._log('warning', `Projection code '${projectionCode}' not found in proj4list`);
|
|
1470
1685
|
return;
|
|
1471
1686
|
}
|
|
1472
1687
|
try {
|
|
@@ -1474,7 +1689,7 @@ export class MainView extends React.Component {
|
|
|
1474
1689
|
register(proj4);
|
|
1475
1690
|
}
|
|
1476
1691
|
catch (error) {
|
|
1477
|
-
|
|
1692
|
+
this._log('warning', `Failed to register projection '${projectionCode}'. Error: ${error.message}`);
|
|
1478
1693
|
return;
|
|
1479
1694
|
}
|
|
1480
1695
|
}
|
|
@@ -1499,12 +1714,14 @@ export class MainView extends React.Component {
|
|
|
1499
1714
|
const numLayers = this._Map.getLayers().getLength();
|
|
1500
1715
|
const safeIndex = Math.min(index, numLayers);
|
|
1501
1716
|
this._Map.getLayers().insertAt(safeIndex, newMapLayer);
|
|
1717
|
+
this._trackLayerViewState(id, newMapLayer);
|
|
1502
1718
|
// doing +1 instead of calling method again
|
|
1503
1719
|
if (!this.state.initialLayersReady &&
|
|
1504
1720
|
numLayers + 1 === this._initialLayersCount) {
|
|
1505
1721
|
this.setState(old => (Object.assign(Object.assign({}, old), { initialLayersReady: true })));
|
|
1506
1722
|
}
|
|
1507
1723
|
}
|
|
1724
|
+
this._model.syncSelected({ [id]: { type: 'layer' } }, this._model.getClientId().toString());
|
|
1508
1725
|
}
|
|
1509
1726
|
catch (error) {
|
|
1510
1727
|
if (this.state.loadingErrors.find(item => item.id === id && item.error === error.message)) {
|
|
@@ -1539,6 +1756,11 @@ export class MainView extends React.Component {
|
|
|
1539
1756
|
}
|
|
1540
1757
|
case 'VectorLayer': {
|
|
1541
1758
|
const layerParams = layer.parameters;
|
|
1759
|
+
if (Array.isArray((_b = layerParams.symbologyState) === null || _b === void 0 ? void 0 : _b.layers)) {
|
|
1760
|
+
// Grammar layers may change structure (e.g. KDE added/removed) — rebuild.
|
|
1761
|
+
this.replaceLayer(id, layer);
|
|
1762
|
+
break;
|
|
1763
|
+
}
|
|
1542
1764
|
mapLayer.setOpacity(layerParams.opacity || 1);
|
|
1543
1765
|
mapLayer.setStyle(this.vectorLayerStyleRuleBuilder(layer));
|
|
1544
1766
|
break;
|
|
@@ -1556,23 +1778,24 @@ export class MainView extends React.Component {
|
|
|
1556
1778
|
case 'ImageLayer': {
|
|
1557
1779
|
break;
|
|
1558
1780
|
}
|
|
1559
|
-
case '
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1781
|
+
case 'GeoTiffLayer': {
|
|
1782
|
+
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)) {
|
|
1783
|
+
this.replaceLayer(id, layer);
|
|
1784
|
+
}
|
|
1785
|
+
else {
|
|
1786
|
+
mapLayer.setOpacity((_e = layer.parameters) === null || _e === void 0 ? void 0 : _e.opacity);
|
|
1787
|
+
if ((_f = layer === null || layer === void 0 ? void 0 : layer.parameters) === null || _f === void 0 ? void 0 : _f.color) {
|
|
1788
|
+
mapLayer.setStyle({
|
|
1789
|
+
color: layer.parameters.color,
|
|
1790
|
+
});
|
|
1791
|
+
}
|
|
1565
1792
|
}
|
|
1566
1793
|
break;
|
|
1567
1794
|
}
|
|
1568
|
-
case '
|
|
1795
|
+
case 'OpenEOTileLayer': {
|
|
1569
1796
|
const layerParams = layer.parameters;
|
|
1570
|
-
const
|
|
1571
|
-
|
|
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']);
|
|
1575
|
-
this.handleTemporalController(id, layer);
|
|
1797
|
+
const openeoLayer = mapLayer;
|
|
1798
|
+
openeoLayer.setOpacity((_g = layerParams.opacity) !== null && _g !== void 0 ? _g : 1);
|
|
1576
1799
|
break;
|
|
1577
1800
|
}
|
|
1578
1801
|
case 'StacLayer':
|
|
@@ -1582,7 +1805,7 @@ export class MainView extends React.Component {
|
|
|
1582
1805
|
}
|
|
1583
1806
|
flyToGeometry(sender, geometry) {
|
|
1584
1807
|
if (!geometry || typeof geometry.getExtent !== 'function') {
|
|
1585
|
-
|
|
1808
|
+
this._log('warning', `Invalid geometry for flyToGeometry: ${geometry}`);
|
|
1586
1809
|
return;
|
|
1587
1810
|
}
|
|
1588
1811
|
const view = this._Map.getView();
|
|
@@ -1594,11 +1817,12 @@ export class MainView extends React.Component {
|
|
|
1594
1817
|
});
|
|
1595
1818
|
}
|
|
1596
1819
|
highlightFeatureOnMap(sender, featureOrGeometry) {
|
|
1820
|
+
var _a;
|
|
1597
1821
|
const geometry = (featureOrGeometry === null || featureOrGeometry === void 0 ? void 0 : featureOrGeometry.geometry) ||
|
|
1598
1822
|
(featureOrGeometry === null || featureOrGeometry === void 0 ? void 0 : featureOrGeometry._geometry) ||
|
|
1599
1823
|
featureOrGeometry;
|
|
1600
1824
|
if (!geometry) {
|
|
1601
|
-
|
|
1825
|
+
this._log('warning', `No geometry found in feature: ${featureOrGeometry}`);
|
|
1602
1826
|
return;
|
|
1603
1827
|
}
|
|
1604
1828
|
const isOlGeometry = typeof geometry.getCoordinates === 'function';
|
|
@@ -1608,63 +1832,128 @@ export class MainView extends React.Component {
|
|
|
1608
1832
|
featureProjection: this._Map.getView().getProjection(),
|
|
1609
1833
|
});
|
|
1610
1834
|
const olFeature = new Feature(Object.assign({ geometry: parsedGeometry }, (geometry !== featureOrGeometry ? featureOrGeometry : {})));
|
|
1611
|
-
|
|
1612
|
-
|
|
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();
|
|
1835
|
+
this._ensureHighlightLayer();
|
|
1836
|
+
const source = (_a = this._highlightLayerRef.current) === null || _a === void 0 ? void 0 : _a.getSource();
|
|
1665
1837
|
source === null || source === void 0 ? void 0 : source.clear();
|
|
1666
1838
|
source === null || source === void 0 ? void 0 : source.addFeature(olFeature);
|
|
1667
1839
|
}
|
|
1840
|
+
_ensureHighlightLayer() {
|
|
1841
|
+
ensureHighlightLayer(this._Map, this._highlightLayerRef);
|
|
1842
|
+
}
|
|
1843
|
+
/**
|
|
1844
|
+
* Replace the highlight layer contents with the given geometries.
|
|
1845
|
+
* Clears the source first so that stale highlights are always removed,
|
|
1846
|
+
* including when the selection becomes empty (geometries = []).
|
|
1847
|
+
*/
|
|
1848
|
+
_setHighlightGeometries(geometries) {
|
|
1849
|
+
var _a;
|
|
1850
|
+
this._ensureHighlightLayer();
|
|
1851
|
+
const source = (_a = this._highlightLayerRef.current) === null || _a === void 0 ? void 0 : _a.getSource();
|
|
1852
|
+
source === null || source === void 0 ? void 0 : source.clear();
|
|
1853
|
+
for (const geom of geometries) {
|
|
1854
|
+
source === null || source === void 0 ? void 0 : source.addFeature(new Feature({ geometry: geom }));
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
/**
|
|
1858
|
+
* Replace the highlight layer contents with pre-styled features.
|
|
1859
|
+
* Each feature carries its own highlight style via feature.setStyle().
|
|
1860
|
+
*/
|
|
1861
|
+
_setHighlightFeatures(features) {
|
|
1862
|
+
var _a;
|
|
1863
|
+
this._ensureHighlightLayer();
|
|
1864
|
+
const source = (_a = this._highlightLayerRef.current) === null || _a === void 0 ? void 0 : _a.getSource();
|
|
1865
|
+
source === null || source === void 0 ? void 0 : source.clear();
|
|
1866
|
+
for (const f of features) {
|
|
1867
|
+
source === null || source === void 0 ? void 0 : source.addFeature(f);
|
|
1868
|
+
}
|
|
1869
|
+
}
|
|
1870
|
+
_buildHighlightStyle(original, geomType) {
|
|
1871
|
+
return buildHighlightStyle(original, geomType);
|
|
1872
|
+
}
|
|
1873
|
+
/**
|
|
1874
|
+
* Compute extent for layer or source
|
|
1875
|
+
*/
|
|
1876
|
+
_computeExtent(layer, source) {
|
|
1877
|
+
try {
|
|
1878
|
+
if (source instanceof VectorSource) {
|
|
1879
|
+
const extent = source.getExtent();
|
|
1880
|
+
if (extent) {
|
|
1881
|
+
return extent;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
if (source instanceof TileSource || source instanceof VectorTileSource) {
|
|
1885
|
+
const tileGrid = source.getTileGrid();
|
|
1886
|
+
const extent = tileGrid === null || tileGrid === void 0 ? void 0 : tileGrid.getExtent();
|
|
1887
|
+
if (extent) {
|
|
1888
|
+
return extent;
|
|
1889
|
+
}
|
|
1890
|
+
}
|
|
1891
|
+
if (layer instanceof StacLayer) {
|
|
1892
|
+
const extent = layer.getExtent();
|
|
1893
|
+
if (extent) {
|
|
1894
|
+
return extent;
|
|
1895
|
+
}
|
|
1896
|
+
}
|
|
1897
|
+
}
|
|
1898
|
+
catch (error) {
|
|
1899
|
+
this._log('warning', `Failed to compute extent: ${error}`);
|
|
1900
|
+
}
|
|
1901
|
+
return undefined;
|
|
1902
|
+
}
|
|
1903
|
+
_computeZoomFromExtent(extent) {
|
|
1904
|
+
var _a, _b;
|
|
1905
|
+
if (!this._Map) {
|
|
1906
|
+
return null;
|
|
1907
|
+
}
|
|
1908
|
+
const view = this._Map.getView();
|
|
1909
|
+
const size = (_a = this._Map.getSize()) !== null && _a !== void 0 ? _a : getSize(extent);
|
|
1910
|
+
const resolution = view.getResolutionForExtent(extent, size);
|
|
1911
|
+
const zoom = view.getZoomForResolution(resolution);
|
|
1912
|
+
return (_b = zoom !== null && zoom !== void 0 ? zoom : view.getZoom()) !== null && _b !== void 0 ? _b : 0;
|
|
1913
|
+
}
|
|
1914
|
+
/**
|
|
1915
|
+
* Track layer's extent and zoom in model's view state
|
|
1916
|
+
*/
|
|
1917
|
+
_trackLayerViewState(layerId, olLayer) {
|
|
1918
|
+
var _a;
|
|
1919
|
+
const effectiveLayer = olLayer instanceof LayerGroup
|
|
1920
|
+
? olLayer.getLayers().getArray()[0]
|
|
1921
|
+
: olLayer;
|
|
1922
|
+
if (!effectiveLayer) {
|
|
1923
|
+
return;
|
|
1924
|
+
}
|
|
1925
|
+
const source = effectiveLayer.getSource();
|
|
1926
|
+
const sourceId = (_a = source === null || source === void 0 ? void 0 : source.get) === null || _a === void 0 ? void 0 : _a.call(source, 'id');
|
|
1927
|
+
let extent = sourceId ? this._model.getExtent(sourceId) : undefined;
|
|
1928
|
+
if (!extent) {
|
|
1929
|
+
extent = this._computeExtent(effectiveLayer, source);
|
|
1930
|
+
}
|
|
1931
|
+
if (extent) {
|
|
1932
|
+
const zoom = this._computeZoomFromExtent(extent);
|
|
1933
|
+
if (zoom === null) {
|
|
1934
|
+
return;
|
|
1935
|
+
}
|
|
1936
|
+
const view = { extent, zoom };
|
|
1937
|
+
this._model.updateLayerViewState(layerId, view);
|
|
1938
|
+
}
|
|
1939
|
+
}
|
|
1940
|
+
/**
|
|
1941
|
+
* Track source's extent and zoom in model's view state
|
|
1942
|
+
*/
|
|
1943
|
+
_trackSourceExtZoom(sourceId, olSource) {
|
|
1944
|
+
var _a, _b, _c;
|
|
1945
|
+
const extent = this._computeExtent(undefined, olSource);
|
|
1946
|
+
if (extent) {
|
|
1947
|
+
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);
|
|
1948
|
+
const zoom = this._computeZoomFromExtent(extent);
|
|
1949
|
+
if (zoom === null) {
|
|
1950
|
+
return;
|
|
1951
|
+
}
|
|
1952
|
+
const view = Object.assign({ extent,
|
|
1953
|
+
zoom }, (projection && { projection }));
|
|
1954
|
+
this._model.updateLayerViewState(sourceId, view);
|
|
1955
|
+
}
|
|
1956
|
+
}
|
|
1668
1957
|
/**
|
|
1669
1958
|
* Wait for all layers to be loaded.
|
|
1670
1959
|
*/
|
|
@@ -1716,6 +2005,133 @@ export class MainView extends React.Component {
|
|
|
1716
2005
|
this._Map.removeLayer(mapLayer);
|
|
1717
2006
|
}
|
|
1718
2007
|
}
|
|
2008
|
+
/**
|
|
2009
|
+
* Decide how selection changes should affect vector drawing state.
|
|
2010
|
+
*
|
|
2011
|
+
* This helper only computes whether
|
|
2012
|
+
* draw mode must be disabled (non-draw layer selected) and whether draw
|
|
2013
|
+
* interactions should be rebound (draw mode enabled and selected draw layer
|
|
2014
|
+
* changed).
|
|
2015
|
+
*/
|
|
2016
|
+
_getVectorDrawingSelectionDecision(layer, selectedLayerId) {
|
|
2017
|
+
const isDrawVectorLayer = this._model.checkIfIsADrawVectorLayer(layer);
|
|
2018
|
+
if (!isDrawVectorLayer) {
|
|
2019
|
+
return { disableEditing: true, shouldRebind: false };
|
|
2020
|
+
}
|
|
2021
|
+
if (!this._model.editingVectorLayer) {
|
|
2022
|
+
return { disableEditing: false, shouldRebind: false };
|
|
2023
|
+
}
|
|
2024
|
+
if (selectedLayerId === this._previousDrawLayerID) {
|
|
2025
|
+
return { disableEditing: false, shouldRebind: false };
|
|
2026
|
+
}
|
|
2027
|
+
return { disableEditing: false, shouldRebind: true };
|
|
2028
|
+
}
|
|
2029
|
+
_handleTemporalControllerActiveChanged() {
|
|
2030
|
+
var _a, _b, _c;
|
|
2031
|
+
const localState = this._model.localState;
|
|
2032
|
+
if (!localState) {
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
const isTemporalControllerActive = localState.isTemporalControllerActive === true;
|
|
2036
|
+
const selectedLayers = (_a = localState.selected) === null || _a === void 0 ? void 0 : _a.value;
|
|
2037
|
+
const selectedLayerId = selectedLayers
|
|
2038
|
+
? ((_b = Object.keys(selectedLayers)[0]) !== null && _b !== void 0 ? _b : null)
|
|
2039
|
+
: null;
|
|
2040
|
+
const layerType = selectedLayerId
|
|
2041
|
+
? (_c = this._model.getLayer(selectedLayerId)) === null || _c === void 0 ? void 0 : _c.type
|
|
2042
|
+
: null;
|
|
2043
|
+
const isSelectionValid = !!selectedLayers &&
|
|
2044
|
+
Object.keys(selectedLayers).length === 1 &&
|
|
2045
|
+
!this._model.getSource(selectedLayerId) &&
|
|
2046
|
+
layerType === 'VectorLayer';
|
|
2047
|
+
const displayTemporalController = isTemporalControllerActive && isSelectionValid;
|
|
2048
|
+
if (displayTemporalController !== this.state.displayTemporalController) {
|
|
2049
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { displayTemporalController })));
|
|
2050
|
+
this._mainViewModel.commands.notifyCommandChanged(CommandIDs.temporalController);
|
|
2051
|
+
}
|
|
2052
|
+
}
|
|
2053
|
+
_handleRemoteUserChanged() {
|
|
2054
|
+
var _a, _b, _c;
|
|
2055
|
+
const localState = this._model.localState;
|
|
2056
|
+
if (!localState) {
|
|
2057
|
+
return;
|
|
2058
|
+
}
|
|
2059
|
+
const remoteUser = localState.remoteUser;
|
|
2060
|
+
const clients = this._model.sharedModel.awareness.getStates();
|
|
2061
|
+
// If we are in following mode, update UI and viewport from the remote user.
|
|
2062
|
+
if (remoteUser) {
|
|
2063
|
+
const remoteState = clients.get(remoteUser);
|
|
2064
|
+
if (!remoteState) {
|
|
2065
|
+
return;
|
|
2066
|
+
}
|
|
2067
|
+
if (((_a = remoteState.user) === null || _a === void 0 ? void 0 : _a.username) !== ((_b = this.state.remoteUser) === null || _b === void 0 ? void 0 : _b.username)) {
|
|
2068
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { remoteUser: remoteState.user })));
|
|
2069
|
+
}
|
|
2070
|
+
const remoteViewport = remoteState.viewportState;
|
|
2071
|
+
if (remoteViewport.value) {
|
|
2072
|
+
const { x, y } = remoteViewport.value.coordinates;
|
|
2073
|
+
const zoom = remoteViewport.value.zoom;
|
|
2074
|
+
this._moveToPosition({ x, y }, zoom, 0);
|
|
2075
|
+
}
|
|
2076
|
+
return;
|
|
2077
|
+
}
|
|
2078
|
+
// If we are unfollowing, reset to local viewport and clear follow UI.
|
|
2079
|
+
if (this.state.remoteUser !== null) {
|
|
2080
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { remoteUser: null })));
|
|
2081
|
+
const viewportState = (_c = localState.viewportState) === null || _c === void 0 ? void 0 : _c.value;
|
|
2082
|
+
if (viewportState) {
|
|
2083
|
+
this._moveToPosition(viewportState.coordinates, viewportState.zoom);
|
|
2084
|
+
}
|
|
2085
|
+
}
|
|
2086
|
+
}
|
|
2087
|
+
_handlePointerChanged() {
|
|
2088
|
+
const clients = this._model.sharedModel.awareness.getStates();
|
|
2089
|
+
const clientPointers = Object.assign({}, this.state.clientPointers);
|
|
2090
|
+
clients.forEach((client, clientId) => {
|
|
2091
|
+
var _a;
|
|
2092
|
+
if (!(client === null || client === void 0 ? void 0 : client.user) || this._model.getClientId() === clientId) {
|
|
2093
|
+
return;
|
|
2094
|
+
}
|
|
2095
|
+
const pointer = (_a = client.pointer) === null || _a === void 0 ? void 0 : _a.value;
|
|
2096
|
+
let currentClientPointer = clientPointers[clientId];
|
|
2097
|
+
if (pointer) {
|
|
2098
|
+
const pixel = this._Map.getPixelFromCoordinate([
|
|
2099
|
+
pointer.coordinates.x,
|
|
2100
|
+
pointer.coordinates.y,
|
|
2101
|
+
]);
|
|
2102
|
+
const lonLat = toLonLat([pointer.coordinates.x, pointer.coordinates.y]);
|
|
2103
|
+
if (!currentClientPointer) {
|
|
2104
|
+
currentClientPointer = {
|
|
2105
|
+
username: client.user.username,
|
|
2106
|
+
displayName: client.user.display_name,
|
|
2107
|
+
color: client.user.color,
|
|
2108
|
+
coordinates: {
|
|
2109
|
+
x: pixel[0],
|
|
2110
|
+
y: pixel[1],
|
|
2111
|
+
},
|
|
2112
|
+
lonLat: {
|
|
2113
|
+
longitude: lonLat[0],
|
|
2114
|
+
latitude: lonLat[1],
|
|
2115
|
+
},
|
|
2116
|
+
};
|
|
2117
|
+
}
|
|
2118
|
+
else {
|
|
2119
|
+
currentClientPointer = Object.assign(Object.assign({}, currentClientPointer), { coordinates: {
|
|
2120
|
+
x: pixel[0],
|
|
2121
|
+
y: pixel[1],
|
|
2122
|
+
}, lonLat: {
|
|
2123
|
+
longitude: lonLat[0],
|
|
2124
|
+
latitude: lonLat[1],
|
|
2125
|
+
} });
|
|
2126
|
+
}
|
|
2127
|
+
clientPointers[clientId] = currentClientPointer;
|
|
2128
|
+
}
|
|
2129
|
+
else {
|
|
2130
|
+
delete clientPointers[clientId];
|
|
2131
|
+
}
|
|
2132
|
+
});
|
|
2133
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { clientPointers })));
|
|
2134
|
+
}
|
|
1719
2135
|
_onSharedOptionsChanged() {
|
|
1720
2136
|
if (!this._Map) {
|
|
1721
2137
|
return;
|
|
@@ -1781,7 +2197,7 @@ export class MainView extends React.Component {
|
|
|
1781
2197
|
view = new View({ projection: newProjection });
|
|
1782
2198
|
}
|
|
1783
2199
|
else {
|
|
1784
|
-
|
|
2200
|
+
this._log('warning', `Invalid projection: ${projection}`);
|
|
1785
2201
|
return;
|
|
1786
2202
|
}
|
|
1787
2203
|
}
|
|
@@ -1876,6 +2292,11 @@ export class MainView extends React.Component {
|
|
|
1876
2292
|
const { id, oldValue: oldLayer, newValue: newLayer } = change;
|
|
1877
2293
|
if (!newLayer || Object.keys(newLayer).length === 0) {
|
|
1878
2294
|
this.removeLayer(id);
|
|
2295
|
+
if (this._model.checkIfIsADrawVectorLayer(oldLayer)) {
|
|
2296
|
+
this._model.editingVectorLayer = false;
|
|
2297
|
+
this._updateEditingVectorLayer();
|
|
2298
|
+
this._mainViewModel.commands.notifyCommandChanged(CommandIDs.toggleDrawFeatures);
|
|
2299
|
+
}
|
|
1879
2300
|
return;
|
|
1880
2301
|
}
|
|
1881
2302
|
if (oldLayer && oldLayer.type !== newLayer.type) {
|
|
@@ -1886,6 +2307,9 @@ export class MainView extends React.Component {
|
|
|
1886
2307
|
const layerTree = JupyterGISModel.getOrderedLayerIds(this._model);
|
|
1887
2308
|
if (layerTree.includes(id)) {
|
|
1888
2309
|
this.updateLayer(id, newLayer, mapLayer, oldLayer);
|
|
2310
|
+
if (mapLayer) {
|
|
2311
|
+
this._trackLayerViewState(id, mapLayer);
|
|
2312
|
+
}
|
|
1889
2313
|
}
|
|
1890
2314
|
else {
|
|
1891
2315
|
this.updateLayers(layerTree);
|
|
@@ -1914,6 +2338,14 @@ export class MainView extends React.Component {
|
|
|
1914
2338
|
}
|
|
1915
2339
|
}
|
|
1916
2340
|
});
|
|
2341
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { identifyFeatureFloatersVersion: old.identifyFeatureFloatersVersion + 1 })));
|
|
2342
|
+
}
|
|
2343
|
+
_clearHighlightWhenIdentifyDisabled() {
|
|
2344
|
+
var _a, _b;
|
|
2345
|
+
if (this._model.currentMode !== 'identifying' &&
|
|
2346
|
+
this._highlightLayerRef.current) {
|
|
2347
|
+
(_b = (_a = this._highlightLayerRef.current) === null || _a === void 0 ? void 0 : _a.getSource()) === null || _b === void 0 ? void 0 : _b.clear();
|
|
2348
|
+
}
|
|
1917
2349
|
}
|
|
1918
2350
|
_computeAnnotationPosition(annotation) {
|
|
1919
2351
|
const { x, y } = annotation.position;
|
|
@@ -1938,6 +2370,58 @@ export class MainView extends React.Component {
|
|
|
1938
2370
|
}
|
|
1939
2371
|
});
|
|
1940
2372
|
}
|
|
2373
|
+
_computeFeatureFloaterPosition(feature) {
|
|
2374
|
+
var _a;
|
|
2375
|
+
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;
|
|
2376
|
+
if (!geometry) {
|
|
2377
|
+
return undefined;
|
|
2378
|
+
}
|
|
2379
|
+
if (typeof geometry.getExtent === 'function') {
|
|
2380
|
+
const extent = geometry.getExtent();
|
|
2381
|
+
const center = getCenter(extent);
|
|
2382
|
+
const pixels = this._Map.getPixelFromCoordinate(center);
|
|
2383
|
+
if (pixels) {
|
|
2384
|
+
return { x: pixels[0], y: pixels[1] };
|
|
2385
|
+
}
|
|
2386
|
+
return undefined;
|
|
2387
|
+
}
|
|
2388
|
+
if (geometry.type === 'Point' && Array.isArray(geometry.coordinates)) {
|
|
2389
|
+
const pixels = this._Map.getPixelFromCoordinate(geometry.coordinates);
|
|
2390
|
+
if (pixels) {
|
|
2391
|
+
return { x: pixels[0], y: pixels[1] };
|
|
2392
|
+
}
|
|
2393
|
+
}
|
|
2394
|
+
return undefined;
|
|
2395
|
+
}
|
|
2396
|
+
_getVisibleDrawIdentifiedFeatures() {
|
|
2397
|
+
var _a, _b, _c;
|
|
2398
|
+
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 : [];
|
|
2399
|
+
const drawEntries = identifiedFeatures.filter(entry => entry.floaterOpen === true);
|
|
2400
|
+
const visibleFeatures = drawEntries
|
|
2401
|
+
.map(entry => {
|
|
2402
|
+
const featureId = getFeatureIdentifier(entry.feature);
|
|
2403
|
+
if (!featureId) {
|
|
2404
|
+
return undefined;
|
|
2405
|
+
}
|
|
2406
|
+
return [featureId, entry.feature];
|
|
2407
|
+
})
|
|
2408
|
+
.filter((entry) => !!entry);
|
|
2409
|
+
return visibleFeatures;
|
|
2410
|
+
}
|
|
2411
|
+
_updateFeatureFloaters() {
|
|
2412
|
+
this._getVisibleDrawIdentifiedFeatures().forEach(([floaterKey, feature]) => {
|
|
2413
|
+
const el = document.getElementById(`feature-floater-${floaterKey}`);
|
|
2414
|
+
if (!el) {
|
|
2415
|
+
return;
|
|
2416
|
+
}
|
|
2417
|
+
const screenPosition = this._computeFeatureFloaterPosition(feature);
|
|
2418
|
+
if (!screenPosition) {
|
|
2419
|
+
return;
|
|
2420
|
+
}
|
|
2421
|
+
el.style.left = `${Math.round(screenPosition.x)}px`;
|
|
2422
|
+
el.style.top = `${Math.round(screenPosition.y)}px`;
|
|
2423
|
+
});
|
|
2424
|
+
}
|
|
1941
2425
|
// TODO this and flyToPosition need a rework
|
|
1942
2426
|
_onZoomToPosition(_, id) {
|
|
1943
2427
|
var _a, _b, _c;
|
|
@@ -1948,7 +2432,6 @@ export class MainView extends React.Component {
|
|
|
1948
2432
|
return;
|
|
1949
2433
|
}
|
|
1950
2434
|
// The id is a layer
|
|
1951
|
-
let extent;
|
|
1952
2435
|
const layer = this.getLayer(id);
|
|
1953
2436
|
const source = layer === null || layer === void 0 ? void 0 : layer.getSource();
|
|
1954
2437
|
const jgisLayer = this._model.getLayer(id);
|
|
@@ -1998,19 +2481,13 @@ export class MainView extends React.Component {
|
|
|
1998
2481
|
return;
|
|
1999
2482
|
}
|
|
2000
2483
|
}
|
|
2001
|
-
|
|
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
|
-
}
|
|
2484
|
+
const extent = this._computeExtent(layer, source);
|
|
2012
2485
|
if (!extent) {
|
|
2013
|
-
|
|
2486
|
+
this._log('warning', 'Layer ${id} has no extent.');
|
|
2487
|
+
return;
|
|
2488
|
+
}
|
|
2489
|
+
if (!extent.every(value => Number.isFinite(value))) {
|
|
2490
|
+
this._log('warning', `Layer ${id} has an invalid extent: ${extent.join(', ')}`);
|
|
2014
2491
|
return;
|
|
2015
2492
|
}
|
|
2016
2493
|
// Convert layer extent value to view projection if needed
|
|
@@ -2019,6 +2496,10 @@ export class MainView extends React.Component {
|
|
|
2019
2496
|
const transformedExtent = sourceProjection && sourceProjection !== viewProjection
|
|
2020
2497
|
? transformExtent(extent, sourceProjection, viewProjection)
|
|
2021
2498
|
: extent;
|
|
2499
|
+
if (!transformedExtent.every(value => Number.isFinite(value))) {
|
|
2500
|
+
this._log('warning', `Layer ${id} has an invalid transformed extent: ${transformedExtent.join(', ')}`);
|
|
2501
|
+
return;
|
|
2502
|
+
}
|
|
2022
2503
|
this._Map.getView().fit(transformedExtent, {
|
|
2023
2504
|
size: this._Map.getSize(),
|
|
2024
2505
|
duration: 500,
|
|
@@ -2082,7 +2563,8 @@ export class MainView extends React.Component {
|
|
|
2082
2563
|
this._syncPointer(coordinates);
|
|
2083
2564
|
}
|
|
2084
2565
|
async _addMarker(e) {
|
|
2085
|
-
if (this.
|
|
2566
|
+
if (this.state.editingVectorLayer ||
|
|
2567
|
+
this._model.currentMode !== 'marking') {
|
|
2086
2568
|
return;
|
|
2087
2569
|
}
|
|
2088
2570
|
const coordinate = this._Map.getCoordinateFromPixel(e.pixel);
|
|
@@ -2094,7 +2576,7 @@ export class MainView extends React.Component {
|
|
|
2094
2576
|
const layerParams = {
|
|
2095
2577
|
opacity: 1.0,
|
|
2096
2578
|
source: sourceId,
|
|
2097
|
-
symbologyState: {
|
|
2579
|
+
symbologyState: { layers: [] },
|
|
2098
2580
|
};
|
|
2099
2581
|
const sourceModel = {
|
|
2100
2582
|
type: 'MarkerSource',
|
|
@@ -2114,25 +2596,29 @@ export class MainView extends React.Component {
|
|
|
2114
2596
|
}
|
|
2115
2597
|
_identifyFeature(e) {
|
|
2116
2598
|
var _a, _b;
|
|
2117
|
-
if (this.
|
|
2599
|
+
if (this.state.editingVectorLayer ||
|
|
2600
|
+
this._model.currentMode !== 'identifying') {
|
|
2118
2601
|
return;
|
|
2119
2602
|
}
|
|
2120
2603
|
const localState = (_a = this._model) === null || _a === void 0 ? void 0 : _a.sharedModel.awareness.getLocalState();
|
|
2121
2604
|
const selectedLayer = (_b = localState === null || localState === void 0 ? void 0 : localState.selected) === null || _b === void 0 ? void 0 : _b.value;
|
|
2122
2605
|
if (!selectedLayer) {
|
|
2123
|
-
|
|
2606
|
+
this._log('warning', 'Layer must be selected to use identify tool');
|
|
2124
2607
|
return;
|
|
2125
2608
|
}
|
|
2126
2609
|
const layerId = Object.keys(selectedLayer)[0];
|
|
2127
2610
|
const jgisLayer = this._model.getLayer(layerId);
|
|
2128
2611
|
switch (jgisLayer === null || jgisLayer === void 0 ? void 0 : jgisLayer.type) {
|
|
2612
|
+
case 'VectorLayer':
|
|
2613
|
+
// Handled by selectInteraction (createSelectInteraction).
|
|
2614
|
+
break;
|
|
2129
2615
|
case 'VectorTileLayer': {
|
|
2130
2616
|
const geometries = [];
|
|
2131
2617
|
const features = [];
|
|
2132
|
-
let
|
|
2618
|
+
let foundAnyFeatures = false;
|
|
2133
2619
|
this._Map.forEachFeatureAtPixel(e.pixel, (feature) => {
|
|
2134
2620
|
var _a, _b;
|
|
2135
|
-
|
|
2621
|
+
foundAnyFeatures = true;
|
|
2136
2622
|
let geom;
|
|
2137
2623
|
let props = {};
|
|
2138
2624
|
if (feature instanceof RenderFeature) {
|
|
@@ -2158,15 +2644,18 @@ export class MainView extends React.Component {
|
|
|
2158
2644
|
geometries.push(geom);
|
|
2159
2645
|
}
|
|
2160
2646
|
if (props && Object.keys(props).length > 0) {
|
|
2161
|
-
features.push(
|
|
2647
|
+
features.push({
|
|
2648
|
+
feature: props,
|
|
2649
|
+
floaterOpen: false,
|
|
2650
|
+
});
|
|
2162
2651
|
}
|
|
2163
2652
|
return true;
|
|
2164
2653
|
});
|
|
2165
2654
|
if (features.length > 0) {
|
|
2166
|
-
this._model.syncIdentifiedFeatures(features, this.
|
|
2655
|
+
this._model.syncIdentifiedFeatures(features, this._model.getClientId().toString());
|
|
2167
2656
|
}
|
|
2168
|
-
else if (!
|
|
2169
|
-
this._model.syncIdentifiedFeatures([], this.
|
|
2657
|
+
else if (!foundAnyFeatures) {
|
|
2658
|
+
this._model.syncIdentifiedFeatures([], this._model.getClientId().toString());
|
|
2170
2659
|
}
|
|
2171
2660
|
if (geometries.length > 0) {
|
|
2172
2661
|
for (const geom of geometries) {
|
|
@@ -2180,7 +2669,7 @@ export class MainView extends React.Component {
|
|
|
2180
2669
|
}
|
|
2181
2670
|
break;
|
|
2182
2671
|
}
|
|
2183
|
-
case '
|
|
2672
|
+
case 'GeoTiffLayer': {
|
|
2184
2673
|
const layer = this.getLayer(layerId);
|
|
2185
2674
|
const data = layer.getData(e.pixel);
|
|
2186
2675
|
// TODO: Handle dataviews?
|
|
@@ -2194,7 +2683,7 @@ export class MainView extends React.Component {
|
|
|
2194
2683
|
}
|
|
2195
2684
|
// last element is alpha
|
|
2196
2685
|
bandValues['Alpha'] = data[data.length - 1];
|
|
2197
|
-
this._model.syncIdentifiedFeatures([bandValues], this._mainViewModel.id);
|
|
2686
|
+
this._model.syncIdentifiedFeatures([{ feature: bandValues, floaterOpen: false }], this._mainViewModel.id);
|
|
2198
2687
|
const coordinate = this._Map.getCoordinateFromPixel(e.pixel);
|
|
2199
2688
|
const point = new Point(coordinate);
|
|
2200
2689
|
// trigger highlight via signal
|
|
@@ -2213,7 +2702,7 @@ export class MainView extends React.Component {
|
|
|
2213
2702
|
this.updateSource(layerId, jgisLayer);
|
|
2214
2703
|
}
|
|
2215
2704
|
if (!jgisLayer || !olLayer) {
|
|
2216
|
-
|
|
2705
|
+
this._log('error', 'Failed to update layer -- layer not found');
|
|
2217
2706
|
return;
|
|
2218
2707
|
}
|
|
2219
2708
|
this.updateLayer(layerId, jgisLayer, olLayer);
|
|
@@ -2223,6 +2712,9 @@ export class MainView extends React.Component {
|
|
|
2223
2712
|
const { id: layerId, selectedFeature } = json;
|
|
2224
2713
|
const olLayer = this.getLayer(layerId);
|
|
2225
2714
|
const source = olLayer.getSource();
|
|
2715
|
+
if (typeof source.forEachFeature !== 'function') {
|
|
2716
|
+
return;
|
|
2717
|
+
}
|
|
2226
2718
|
source.forEachFeature(feature => {
|
|
2227
2719
|
const time = feature.get(selectedFeature);
|
|
2228
2720
|
const parsedTime = typeof time === 'string' ? Date.parse(time) : time;
|
|
@@ -2239,48 +2731,86 @@ export class MainView extends React.Component {
|
|
|
2239
2731
|
throw new Error('Could not move to geolocation, because current zoom is not defined.');
|
|
2240
2732
|
}
|
|
2241
2733
|
}
|
|
2734
|
+
_updateEditingVectorLayer() {
|
|
2735
|
+
const editingVectorLayer = this._model.editingVectorLayer;
|
|
2736
|
+
this.setState(old => (Object.assign(Object.assign({}, old), { editingVectorLayer })));
|
|
2737
|
+
if (editingVectorLayer === true) {
|
|
2738
|
+
this._editVectorLayer();
|
|
2739
|
+
}
|
|
2740
|
+
if (editingVectorLayer === false && this._draw) {
|
|
2741
|
+
this._removeDrawInteraction();
|
|
2742
|
+
this._currentDrawLayerID = undefined;
|
|
2743
|
+
}
|
|
2744
|
+
}
|
|
2745
|
+
_renderAnnotationFloaters() {
|
|
2746
|
+
const annotationModel = this._model.annotationModel;
|
|
2747
|
+
if (!annotationModel) {
|
|
2748
|
+
return null;
|
|
2749
|
+
}
|
|
2750
|
+
return Object.entries(this.state.annotations).map(([key, annotation]) => {
|
|
2751
|
+
const screenPosition = this._computeAnnotationPosition(annotation);
|
|
2752
|
+
if (!screenPosition) {
|
|
2753
|
+
return null;
|
|
2754
|
+
}
|
|
2755
|
+
return (React.createElement(PositionedFloater, { key: key, id: key, className: "jGIS-Popup-Wrapper", left: screenPosition.x, top: screenPosition.y },
|
|
2756
|
+
React.createElement(AnnotationFloater, { itemId: key, annotationModel: annotationModel })));
|
|
2757
|
+
});
|
|
2758
|
+
}
|
|
2759
|
+
_renderFeatureFloaters() {
|
|
2760
|
+
return this._getVisibleDrawIdentifiedFeatures().map(([floaterKey, feature]) => {
|
|
2761
|
+
const screenPosition = this._computeFeatureFloaterPosition(feature);
|
|
2762
|
+
if (!screenPosition) {
|
|
2763
|
+
return null;
|
|
2764
|
+
}
|
|
2765
|
+
const id = `feature-floater-${floaterKey}`;
|
|
2766
|
+
return (React.createElement(PositionedFloater, { key: id, id: id, className: "jGIS-Popup-Wrapper jGIS-FeatureFloater-Wrapper", left: screenPosition.x, top: screenPosition.y },
|
|
2767
|
+
React.createElement(FeatureFloater, { feature: feature })));
|
|
2768
|
+
});
|
|
2769
|
+
}
|
|
2242
2770
|
render() {
|
|
2771
|
+
const { clientPointers, displayTemporalController, drawGeometryLabel, editingVectorLayer, filterStates, initialLayersReady, isSpectaPresentation, jgisSettings, loading, loadingLayer, remoteUser, scale, segmentTransition, viewProjection, } = this.state;
|
|
2772
|
+
const { isMobile } = this.props;
|
|
2773
|
+
const selectedStory = this._model.getSelectedStory().story;
|
|
2774
|
+
const isListStory = isSpectaPresentation &&
|
|
2775
|
+
(selectedStory === null || selectedStory === void 0 ? void 0 : selectedStory.storyType) === STORY_TYPE.verticalScroll;
|
|
2776
|
+
const showSidePanels = !isSpectaPresentation;
|
|
2777
|
+
const showMergedMobilePanel = isMobile &&
|
|
2778
|
+
Boolean(this._state) &&
|
|
2779
|
+
Boolean(this._formSchemaRegistry) &&
|
|
2780
|
+
Boolean(this._annotationModel);
|
|
2781
|
+
const spectaMobileTouch = isSpectaPresentation && isMobile;
|
|
2243
2782
|
return (React.createElement(React.Fragment, null,
|
|
2244
|
-
|
|
2245
|
-
if (!this._model.annotationModel) {
|
|
2246
|
-
return null;
|
|
2247
|
-
}
|
|
2248
|
-
const screenPosition = this._computeAnnotationPosition(annotation);
|
|
2249
|
-
return (screenPosition && (React.createElement("div", { key: key, id: key, style: {
|
|
2250
|
-
left: screenPosition.x,
|
|
2251
|
-
top: screenPosition.y,
|
|
2252
|
-
}, className: 'jGIS-Popup-Wrapper' },
|
|
2253
|
-
React.createElement(AnnotationFloater, { itemId: key, annotationModel: this._model.annotationModel }))));
|
|
2254
|
-
}),
|
|
2783
|
+
React.createElement(MainViewOverlayLayer, { annotationFloaters: this._renderAnnotationFloaters(), featureFloaters: this._renderFeatureFloaters(), editingVectorLayer: editingVectorLayer, drawGeometryLabel: drawGeometryLabel, onDrawGeometryTypeChange: this._handleDrawGeometryTypeChange }),
|
|
2255
2784
|
React.createElement("div", { className: "jGIS-Mainview-Container" },
|
|
2256
|
-
|
|
2257
|
-
React.createElement(
|
|
2258
|
-
|
|
2259
|
-
|
|
2260
|
-
|
|
2261
|
-
|
|
2262
|
-
|
|
2263
|
-
|
|
2264
|
-
|
|
2265
|
-
|
|
2266
|
-
|
|
2267
|
-
|
|
2268
|
-
|
|
2269
|
-
|
|
2270
|
-
|
|
2271
|
-
|
|
2272
|
-
|
|
2273
|
-
|
|
2274
|
-
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2785
|
+
displayTemporalController ? (React.createElement(TemporalSlider, { model: this._model, filterStates: filterStates })) : null,
|
|
2786
|
+
React.createElement(MainViewMapSurface, { mainViewRef: this.mainViewRef, loading: loading, remoteUser: remoteUser, clientPointers: clientPointers, spectaMobileTouch: spectaMobileTouch, onTouchStart: this._handleSpectaTouchStart, onTouchEnd: this._handleSpectaTouchEnd },
|
|
2787
|
+
React.createElement(MainViewStoryStage, { model: this._model, isListStory: isListStory, isMobile: isMobile, segmentTransition: segmentTransition, stageRef: this.divRef, controlsToolbarRef: this.controlsToolbarRef, panels: showSidePanels ? (React.createElement(MainViewSidePanels, { model: this._model, commands: this._mainViewModel.commands, settings: jgisSettings, showMergedMobilePanel: showMergedMobilePanel, state: this._state, formSchemaRegistry: this._formSchemaRegistry, annotationModel: this._annotationModel, addLayer: this.addLayer.bind(this), removeLayer: this.removeLayer.bind(this), patchGeoJSONFeatureProperties: this._patchGeoJSONFeatureProperties })) : (React.createElement(MainViewSpectaPanel, { model: this._model, isSpecta: isSpectaPresentation, isMobile: isMobile, initialLayersReady: initialLayersReady, containerRef: this.spectaContainerRef, storyViewerPanelRef: this.storyViewerPanelRef, addLayer: this._addLayerForPanels, removeLayer: this._removeLayerForPanels, onSegmentTransitionEnd: this._clearStoryScrollGuard, onSegmentTransitionChange: this._handleSegmentTransitionChange })) })),
|
|
2788
|
+
!isSpectaPresentation ? (React.createElement(StatusBar, { jgisModel: this._model, loading: loadingLayer, projection: viewProjection, scale: scale })) : null)));
|
|
2789
|
+
}
|
|
2790
|
+
_log(level, message) {
|
|
2791
|
+
var _a;
|
|
2792
|
+
// Always mirror to the browser console regardless of whether the JupyterLab
|
|
2793
|
+
// logger is available.
|
|
2794
|
+
if (level === 'error' || level === 'critical') {
|
|
2795
|
+
// eslint-disable-next-line no-console
|
|
2796
|
+
console.error(message);
|
|
2797
|
+
}
|
|
2798
|
+
else if (level === 'warning') {
|
|
2799
|
+
// eslint-disable-next-line no-console
|
|
2800
|
+
console.warn(message);
|
|
2801
|
+
}
|
|
2802
|
+
else {
|
|
2803
|
+
// eslint-disable-next-line no-console
|
|
2804
|
+
console.log(message);
|
|
2805
|
+
}
|
|
2806
|
+
// Forward to JupyterLab log console when available.
|
|
2807
|
+
(_a = this._loggerRegistry) === null || _a === void 0 ? void 0 : _a.getLogger(this._model.filePath).log({ type: 'text', level, data: message });
|
|
2278
2808
|
}
|
|
2279
2809
|
}
|
|
2280
2810
|
// ! TODO make mainview a modern react component instead of a class
|
|
2281
2811
|
/** Thin wrapper that injects isMobile from useMediaQuery so MainView can use it in JSX. */
|
|
2282
2812
|
function MainViewWithMediaQuery(props) {
|
|
2283
|
-
const isMobile = useMediaQuery('(max-width:
|
|
2813
|
+
const isMobile = useMediaQuery('(max-width: 960px)');
|
|
2284
2814
|
return React.createElement(MainView, Object.assign({}, props, { isMobile: isMobile }));
|
|
2285
2815
|
}
|
|
2286
2816
|
export { MainViewWithMediaQuery };
|