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