@opengeoweb/core 19.6.0 → 19.6.1-nightly-20260910

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/dist/index.esm.js CHANGED
@@ -2,19 +2,19 @@ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { FormControl, MenuItem, Grid, InputLabel, Paper, Box, Typography, CircularProgress, List, ListSubheader, ListItemButton, ListItemText, DialogContentText, TextField, InputAdornment, Button, useTheme, Tooltip, ListItem, ListItemIcon, Checkbox, Popper, Fade, styled, LinearProgress, Autocomplete, Switch, Select } from '@mui/material';
3
3
  import React, { useMemo, useState, useCallback, useEffect, useContext } from 'react';
4
4
  import { safelyInitI18nForTestsAndStories, sharedTranslations, SHARED_NAMESPACE, TooltipSelect, CustomIconButton, CustomDialog, CustomTooltip, ToggleMenu, AlertIcon, useConfirmationDialog, useScrollingInIsolation, sliderHeaderStyle, CustomSlider, SearchField, SearchHighlight, tooltipContainerStyles, renderCounter, AlertBanner, CustomAccordion, PROJECTION, calculateStartSize, useWheelStopPropagation, ToolContainerDraggable, dateUtils, sessionStorageProvider, useDebounce, usePrevious } from '@opengeoweb/shared';
5
- import { webmapUtils, queryWMSLayersTree, invalidateWMSGetCapabilities, LayerType, WMJSDimension, WMDefaultStyleName, WMJSMAP_LONLAT_EPSGCODE, queryWMSServiceInfo, queryWMSLayers, getWMSServiceId, handleDateUtilsISOString, generateLayerId, getWMLayerById, getMapDimURL, WMLayer } from '@opengeoweb/webmap';
5
+ import { webmapUtils, queryWMSLayersTree, invalidateWMSGetCapabilities, LayerType, WMJSDimension, WMDefaultStyleName, queryWMSServiceInfo, queryWMSLayers, getWMSServiceId, handleDateUtilsISOString, generateLayerId, getWMLayerById, getMapDimURL, WMLayer } from '@opengeoweb/webmap';
6
6
  import _, { isEqual, isArray, range, zip, throttle } from 'lodash';
7
7
  import { useDispatch, useSelector, connect, useStore, shallowEqual, Provider } from 'react-redux';
8
8
  import { mapSelectors, layerSelectors, layerActions, layerTypes, filterLayers, mapActions, mapEnums, loadingIndicatorSelectors, layerUtils, serviceSelectors, serviceActions, uiSelectors, uiTypes, useSetupDialog, getUserAddedServices, uiActions, genericActions, drawingToolSelectors, syncGroupsSelectors, syncConstants, getViewObjectForSyncGroup, getViewObjectForMapId, loadingIndicatorActions, genericSelectors, PUBLIC_WARNING_DRAW_TOOL_ID, defaultLayers, getSingularDrawtoolDrawLayerId, syncGroupsSelector, syncGroupsActions, routerUtils, mapUtils } from '@opengeoweb/store';
9
9
  import i18n from 'i18next';
10
10
  import { useTranslation, Trans, I18nextProvider } from 'react-i18next';
11
- import { TIME_SLIDER_NAMESPACE, timesliderTranslations, defaultTimeSpan, AnimationLengthButton, AnimationLength, AutoUpdateButton, BackwardForwardStepButton, NowButton, handleSetNowEvent, PlayButton, defaultTimeStep, SpeedButton, TimeSpanButton, secondsPerPxFromCanvasWidth, getNewCenterOfFixedPointZoom, TimeStepButton, OptionsMenuButton, TimeSliderButtons, TimeSliderCurrentTimeBox, TimeSliderLegend, onsetNewDateDebounced, getFilteredTime, defaultSecondsPerPx, moveSelectedTimePx, TimeSlider } from '@opengeoweb/time-slider';
12
- import { webmapReactTranslations, WEBMAP_REACT_NAMESPACE, webmapTranslations, WEBMAP_NAMESPACE, openLayersGetMapImageStore, LayerInfoButton, useStacCollection, useGetWMLayerInstance, useEDRGetCollectionDetails, getStacCollectionStyles, resolvePreferredStyleName, defaultEdrStyles, LayerInfoDialog, MapControlButton, MapDimensionSelect, dimensionConfig, DimensionSelectDialog, DimensionSelectButton, Clock, LegendDialog, LegendButton, MapContext, emptyGeoJSON, MapFeatureClass, setMapCenter, makeTimeList, getIsInsideAcceptanceTime, OpenLayersLayer, isGeoJSONGeometryEmpty, OpenLayersFeatureLayer, OpenLayersMapDraw, MapMeasure, useSetIntervalWhenVisible, TimeContext, OpenLayersMapView, WMSLayer, genericOpenLayersFeatureStyle, OpenLayersGetFeatureInfo, MapControls, OpenLayersZoomControl, getLayerUpdateInfo, publicLayers } from '@opengeoweb/webmap-react';
11
+ import { TIME_SLIDER_NAMESPACE, timesliderTranslations, defaultTimeSpan, AnimationLengthPanel, exactAnimationLength, AnimationLengthMode, isValidNowAnimationLength, isWithinAnimationStepLimit, resolveNowAnimationRange, AutoUpdateButton, BackwardForwardStepButton, NowButton, handleSetNowEvent, PlayButton, defaultTimeStep, SpeedButton, TimeSpanButton, secondsPerPxFromCanvasWidth, getNewCenterOfFixedPointZoom, TimeStepButton, OptionsMenuButton, TimeSliderButtons, TimeSliderCurrentTimeBox, TimeSliderLegend, onsetNewDateDebounced, getFilteredTime, defaultSecondsPerPx, moveSelectedTimePx, TimeSlider } from '@opengeoweb/time-slider';
12
+ import { webmapReactTranslations, WEBMAP_REACT_NAMESPACE, webmapTranslations, WEBMAP_NAMESPACE, openLayersGetMapImageStore, LayerInfoButton, useStacCollection, useGetWMLayerInstance, getLayerExtentForProjection, useEDRGetCollectionDetails, getStacCollectionStyles, resolvePreferredStyleName, defaultEdrStyles, LayerInfoDialog, MapControlButton, MapDimensionSelect, dimensionConfig, DimensionSelectDialog, DimensionSelectButton, Clock, LegendDialog, LegendButton, MapContext, emptyGeoJSON, MapFeatureClass, setMapCenter, makeTimeList, getIsInsideAcceptanceTime, OpenLayersLayer, isGeoJSONGeometryEmpty, calculateNewSelection, shouldExitEditModeForMultiSelect, shouldEnterEditMode, calculateBoxSelection, OpenLayersFeatureLayer, OpenLayersMapDraw, DRAWMODE, getOlFeatureKey, MapMeasure, useViewFromLayer, useSetIntervalWhenVisible, TimeContext, OpenLayersMapView, WMSLayer, genericOpenLayersFeatureStyle, OpenLayersGetFeatureInfo, MapControls, OpenLayersZoomControl, getLayerUpdateInfo, publicLayers } from '@opengeoweb/webmap-react';
13
+ export { getAffectedIndices } from '@opengeoweb/webmap-react';
13
14
  import { Cached, Add, MapAdd, CollapseSmall, CollapseMedium, CollapseLarge, CollapseWindow, ExpandWindow, LayersAdd, CloudLoading, ColumnCollapsed, ColumnCollapse, DragHandle as DragHandle$1, None, FastForward, AutoUpdateActive, Both, Delete, Visibility, VisibilityOff, Copy, ZoomToLayer, Overlayers, WorldMapProjection, Layers, Search, Exclamation, MyLocation, Airplane, Region, Pinned, Close, ExitDomain, LocationInfo, Measure, Options, Info, ThemeWrapper, lightTheme, FlagBritain, FlagNetherlands, FlagFinland, FlagNorway } from '@opengeoweb/theme';
14
15
  import { ReactSortable } from 'react-sortablejs';
15
16
  import Sortable from 'sortablejs';
16
17
  import { Box as Box$1, Stack } from '@mui/system';
17
- import { transformExtent, toLonLat, fromLonLat } from 'ol/proj';
18
18
  import { LayerSelectButtonConnect, LayerSelectConnect, DataExplorerButtonConnect } from '@opengeoweb/layer-select';
19
19
  import { unByKey } from 'ol/Observable';
20
20
  import { useQuery, QueryClient, QueryClientProvider } from '@tanstack/react-query';
@@ -23,6 +23,7 @@ import { bbox } from '@turf/turf';
23
23
  import proj4 from 'proj4';
24
24
  import { getIntersection } from 'ol/extent';
25
25
  import GeoJSON from 'ol/format/GeoJSON';
26
+ import { toLonLat, fromLonLat } from 'ol/proj';
26
27
  import { ImageWMS } from 'ol/source';
27
28
  import DOMPurify from 'dompurify';
28
29
  import { snackbarTypes, snackbarActions, SnackbarWrapperConnect } from '@opengeoweb/snackbar';
@@ -4511,13 +4512,12 @@ var LayerManagerMenuButtonConnect = function LayerManagerMenuButtonConnect(_ref)
4511
4512
  if (!layerId || !mapSrs || !wmLayer) {
4512
4513
  return;
4513
4514
  }
4514
- var crs = wmLayer.getCRSByName(mapSrs);
4515
- if (crs != null && crs.bbox) {
4516
- var _crs$bbox$getExtent = crs.bbox.getExtent(),
4517
- left = _crs$bbox$getExtent[0],
4518
- bottom = _crs$bbox$getExtent[1],
4519
- right = _crs$bbox$getExtent[2],
4520
- top = _crs$bbox$getExtent[3];
4515
+ var extent = getLayerExtentForProjection(wmLayer, mapSrs);
4516
+ if (extent) {
4517
+ var left = extent[0],
4518
+ bottom = extent[1],
4519
+ right = extent[2],
4520
+ top = extent[3];
4521
4521
  dispatch(mapActions.setBbox({
4522
4522
  mapId: mapId,
4523
4523
  srs: mapSrs,
@@ -4529,26 +4529,6 @@ var LayerManagerMenuButtonConnect = function LayerManagerMenuButtonConnect(_ref)
4529
4529
  },
4530
4530
  origin: layerTypes.LayerActionOrigin.layerManager
4531
4531
  }));
4532
- return;
4533
- }
4534
- var geo = wmLayer.geographicBoundingBox;
4535
- if (geo) {
4536
- var _transformExtent = transformExtent([parseFloat(geo.west), parseFloat(geo.south), parseFloat(geo.east), parseFloat(geo.north)], WMJSMAP_LONLAT_EPSGCODE, mapSrs),
4537
- _left = _transformExtent[0],
4538
- _bottom = _transformExtent[1],
4539
- _right = _transformExtent[2],
4540
- _top = _transformExtent[3];
4541
- dispatch(mapActions.setBbox({
4542
- mapId: mapId,
4543
- srs: mapSrs,
4544
- bbox: {
4545
- left: _left,
4546
- bottom: _bottom,
4547
- right: _right,
4548
- top: _top
4549
- },
4550
- origin: layerTypes.LayerActionOrigin.layerManager
4551
- }));
4552
4532
  }
4553
4533
  }, [dispatch, layerId, mapId, mapSrs, wmLayer]);
4554
4534
  var duplicateLayer = React.useCallback(function (_ref3) {
@@ -4579,9 +4559,9 @@ var LayerManagerMenuButtonConnect = function LayerManagerMenuButtonConnect(_ref)
4579
4559
  icon: icon,
4580
4560
  isFeatureLayer: isFeatureLayer,
4581
4561
  isFeatureSyncLayer: isFeatureSyncLayer,
4562
+ isTiledStacCollection: isStacGeoTIFFLayer ? isTiledStacCollection : undefined,
4582
4563
  layerId: layerId,
4583
4564
  mapId: mapId,
4584
- isTiledStacCollection: isStacGeoTIFFLayer ? isTiledStacCollection : undefined,
4585
4565
  onChangeStacDataLayout: isStacGeoTIFFLayer ? changeStacDataLayout : undefined,
4586
4566
  onLayerCopyToOtherMaps: copyLayerToOtherMaps,
4587
4567
  onLayerDuplicate: function onLayerDuplicate() {
@@ -6610,56 +6590,52 @@ var MultiMapMultiDimensionSelectConnect = function MultiMapMultiDimensionSelectC
6610
6590
  });
6611
6591
  };
6612
6592
 
6613
- var AnimationLengthButtonConnect = function AnimationLengthButtonConnect(_ref) {
6593
+ var AnimationLengthPanelConnect = function AnimationLengthPanelConnect(_ref) {
6614
6594
  var mapId = _ref.mapId;
6615
6595
  var dispatch = useDispatch();
6616
6596
  var isAnimating = useSelector(function (store) {
6617
6597
  return mapSelectors.isAnimating(store, mapId);
6618
6598
  });
6619
- var isAnimationLengthAuto = useSelector(function (store) {
6620
- return mapSelectors.isAnimationLengthAuto(store, mapId);
6599
+ var animationLength = useSelector(function (store) {
6600
+ return mapSelectors.getAnimationLength(store, mapId);
6621
6601
  });
6622
6602
  var _useSelector = useSelector(function (store) {
6623
6603
  return mapSelectors.getAnimationRange(store, mapId);
6624
6604
  }),
6625
6605
  animationStartTime = _useSelector.animationStartTime,
6626
6606
  animationEndTime = _useSelector.animationEndTime;
6627
- var currentDiffInMinutes = dateUtils.differenceInMinutes(dateUtils.utc(animationEndTime), dateUtils.utc(animationStartTime));
6628
- var handlechangeAnimationLength = function handlechangeAnimationLength(length) {
6629
- var newStartTime = dateUtils.dateToIsoString(dateUtils.add(dateUtils.utc(animationEndTime), {
6630
- minutes: -length
6631
- }));
6632
- dispatch(mapActions.setAnimationStartTime({
6633
- mapId: mapId,
6634
- animationStartTime: newStartTime || dateUtils.dateToIsoString(dateUtils.utc()),
6635
- origin: mapEnums.MapActionOrigin.map
6636
- }));
6637
- dispatch(mapActions.setAnimationEndTime({
6638
- mapId: mapId,
6639
- animationEndTime: animationEndTime || dateUtils.dateToIsoString(dateUtils.utc()),
6640
- origin: mapEnums.MapActionOrigin.map
6641
- }));
6642
- };
6643
- var toggleAnimationLengthAuto = React.useCallback(function () {
6644
- dispatch(mapActions.toggleAnimationLengthAuto({
6645
- mapId: mapId,
6646
- autoAnimationLength: true,
6647
- origin: mapEnums.MapActionOrigin.map
6648
- }));
6649
- }, [dispatch, mapId]);
6650
- var autoTimestepLayerId = useSelector(function (store) {
6607
+ var autoTimeStepLayerId = useSelector(function (store) {
6651
6608
  return mapSelectors.getAutoTimeStepLayerId(store, mapId);
6652
6609
  });
6610
+ var autoTimeStepLayerName = useSelector(function (store) {
6611
+ return autoTimeStepLayerId ? layerSelectors.getLayerTitleFromService(store, autoTimeStepLayerId) : undefined;
6612
+ });
6653
6613
  var timeStepFromLayer = useSelector(function (store) {
6654
- return layerSelectors.getTimeStepForLayerId(store, autoTimestepLayerId);
6614
+ return layerSelectors.getTimeStepForLayerId(store, autoTimeStepLayerId);
6655
6615
  });
6656
- return jsx(AnimationLengthButton, {
6657
- animationLength: currentDiffInMinutes || Number(AnimationLength.Hours24),
6616
+ var timeStep = useSelector(function (store) {
6617
+ return mapSelectors.getMapTimeStep(store, mapId);
6618
+ });
6619
+ var changeAnimationLength = function changeAnimationLength(animationLength) {
6620
+ var followsClock = animationLength.mode === AnimationLengthMode.Now;
6621
+ var isUsableRange = isValidNowAnimationLength(animationLength) && isWithinAnimationStepLimit(animationLength, timeStep);
6622
+ var resolvedRange = followsClock && isUsableRange ? resolveNowAnimationRange(animationLength) : undefined;
6623
+ dispatch(mapActions.setAnimationLength(Object.assign({
6624
+ mapId: mapId,
6625
+ animationLength: animationLength
6626
+ }, resolvedRange, {
6627
+ origin: mapEnums.MapActionOrigin.map
6628
+ })));
6629
+ };
6630
+ return jsx(AnimationLengthPanel, {
6631
+ animationEndTime: animationEndTime,
6632
+ animationStartTime: animationStartTime,
6633
+ autoTimeStepLayerName: autoTimeStepLayerName,
6658
6634
  disabled: isAnimating,
6659
- isAuto: isAnimationLengthAuto,
6660
- layerHasTimeStep: Boolean(timeStepFromLayer),
6661
- onChangeAnimationLength: handlechangeAnimationLength,
6662
- toggleAnimationLengthAuto: toggleAnimationLengthAuto
6635
+ isAutoDisabled: !timeStepFromLayer,
6636
+ timeStep: timeStep,
6637
+ onChange: changeAnimationLength,
6638
+ setting: animationLength != null ? animationLength : exactAnimationLength
6663
6639
  });
6664
6640
  };
6665
6641
 
@@ -6973,9 +6949,9 @@ var OptionsMenu = function OptionsMenu(_ref) {
6973
6949
  children: [jsx(NowButtonConnect, {
6974
6950
  mapId: mapId,
6975
6951
  sourceId: mapId
6976
- }), jsx(TimeSpanButtonConnect, {
6952
+ }), jsx(AnimationLengthPanelConnect, {
6977
6953
  mapId: mapId
6978
- }), jsx(AnimationLengthButtonConnect, {
6954
+ }), jsx(TimeSpanButtonConnect, {
6979
6955
  mapId: mapId
6980
6956
  }), jsx(_TimeStepButtonConnect, {
6981
6957
  mapId: mapId
@@ -8357,9 +8333,91 @@ var OlMapViewLayerConnect = function OlMapViewLayerConnect(layerProps) {
8357
8333
  }), "layer-" + (layer == null ? void 0 : layer.id));
8358
8334
  };
8359
8335
 
8360
- var getCurrentSelectedIndices = function getCurrentSelectedIndices(_ref) {
8361
- var geojson = _ref.geojson,
8362
- selectedFeatureIds = _ref.selectedFeatureIds;
8336
+ /** Escape drops a multi selection only. A sole selection is kept, matching the
8337
+ * plain click behaviour, and edit mode keeps its own Escape handling. */
8338
+ var shouldDeselectAllOnEscape = function shouldDeselectAllOnEscape(selectedFeatureCount, isInEditMode, hasRegisteredDrawTool) {
8339
+ return hasRegisteredDrawTool && !isInEditMode && selectedFeatureCount > 1;
8340
+ };
8341
+ var useDeselectAllOnEscape = function useDeselectAllOnEscape(_ref) {
8342
+ var selectedFeatureCount = _ref.selectedFeatureCount,
8343
+ isInEditMode = _ref.isInEditMode,
8344
+ hasRegisteredDrawTool = _ref.hasRegisteredDrawTool,
8345
+ onDeselectAll = _ref.onDeselectAll;
8346
+ React.useEffect(function () {
8347
+ if (!shouldDeselectAllOnEscape(selectedFeatureCount, isInEditMode, hasRegisteredDrawTool)) {
8348
+ return undefined;
8349
+ }
8350
+ var onKeyDown = function onKeyDown(event) {
8351
+ if (event.key !== 'Escape' || document.activeElement !== document.body) {
8352
+ return;
8353
+ }
8354
+ onDeselectAll();
8355
+ };
8356
+ window.addEventListener('keydown', onKeyDown);
8357
+ return function () {
8358
+ return window.removeEventListener('keydown', onKeyDown);
8359
+ };
8360
+ }, [selectedFeatureCount, isInEditMode, hasRegisteredDrawTool, onDeselectAll]);
8361
+ };
8362
+ var shouldClearSelectionOnEmptyClick = function shouldClearSelectionOnEmptyClick(activeDrawToolId, hasRegisteredDrawTool) {
8363
+ return !activeDrawToolId && hasRegisteredDrawTool;
8364
+ };
8365
+ var isSketchInProgress = function isSketchInProgress(isInEditMode, drawMode, activeFeature) {
8366
+ if (!isInEditMode) {
8367
+ return false;
8368
+ }
8369
+ if (drawMode === DRAWMODE.SPLIT) {
8370
+ return true;
8371
+ }
8372
+ return Boolean(activeFeature && isGeoJSONGeometryEmpty(activeFeature));
8373
+ };
8374
+ /** Soundings and time series set their own highlight, so they must stay out of this. */
8375
+ var usesFeatureIdSelection = function usesFeatureIdSelection(isSelectable, hasRegisteredDrawTool) {
8376
+ return Boolean(isSelectable) || hasRegisteredDrawTool;
8377
+ };
8378
+ var getSelectionProperties = function getSelectionProperties(usesIdSelection, selectedFeatureIdSet, featureId) {
8379
+ if (!usesIdSelection) {
8380
+ return undefined;
8381
+ }
8382
+ var isSelected = featureId !== undefined && selectedFeatureIdSet.has(featureId);
8383
+ return {
8384
+ isSelected: isSelected,
8385
+ highlightSelectedFeature: isSelected
8386
+ };
8387
+ };
8388
+ var getClickedFeatureKey = getOlFeatureKey;
8389
+ var findFeatureIndexByKey = function findFeatureIndexByKey(geojson, feature) {
8390
+ var key = getClickedFeatureKey(feature);
8391
+ // -1 when the feature has no key, so an unidentifiable click never matches feature 0
8392
+ if (key === undefined || !geojson) {
8393
+ return -1;
8394
+ }
8395
+ return geojson.features.findIndex(function (feat) {
8396
+ return layerUtils.getFeatureSelectionKey(feat) === key;
8397
+ });
8398
+ };
8399
+ var getMovableSelectedFeatureIds = function getMovableSelectedFeatureIds(geojson, selectedFeatureIds) {
8400
+ if (!(geojson != null && geojson.features.length) || !(selectedFeatureIds != null && selectedFeatureIds.length)) {
8401
+ return [];
8402
+ }
8403
+ var selectedFeatureIdSet = new Set(selectedFeatureIds);
8404
+ return geojson.features.reduce(function (featureIds, feature) {
8405
+ var _feature$properties;
8406
+ var featureKey = layerUtils.getFeatureSelectionKey(feature);
8407
+ if (featureKey && selectedFeatureIdSet.has(featureKey) && ((_feature$properties = feature.properties) == null ? void 0 : _feature$properties.isReadonly) !== true) {
8408
+ featureIds.push(featureKey);
8409
+ }
8410
+ return featureIds;
8411
+ }, []);
8412
+ };
8413
+ /** Moving a set of shapes belongs to the drawing tool only: selection-only
8414
+ * reference layers stay selectable but must never be dragged. */
8415
+ var shouldEnableMultiShapeMove = function shouldEnableMultiShapeMove(hasRegisteredDrawTool, isInEditMode, selectedFeatureCount, movableFeatureCount) {
8416
+ return hasRegisteredDrawTool && !isInEditMode && selectedFeatureCount > 1 && movableFeatureCount > 0;
8417
+ };
8418
+ var getCurrentSelectedIndices = function getCurrentSelectedIndices(_ref2) {
8419
+ var geojson = _ref2.geojson,
8420
+ selectedFeatureIds = _ref2.selectedFeatureIds;
8363
8421
  if (!(geojson != null && geojson.features.length) || !(selectedFeatureIds != null && selectedFeatureIds.length)) {
8364
8422
  return [];
8365
8423
  }
@@ -8372,46 +8430,12 @@ var getCurrentSelectedIndices = function getCurrentSelectedIndices(_ref) {
8372
8430
  return indices;
8373
8431
  }, []);
8374
8432
  };
8375
- var calculateNewSelection = function calculateNewSelection(currentSelectedIndices, clickedIndex, isShiftClick) {
8376
- if (isShiftClick) {
8377
- if (currentSelectedIndices.includes(clickedIndex)) {
8378
- return currentSelectedIndices.filter(function (i) {
8379
- return i !== clickedIndex;
8380
- });
8381
- }
8382
- return [].concat(currentSelectedIndices, [clickedIndex]);
8383
- }
8384
- var isSoleSelection = currentSelectedIndices.length === 1 && currentSelectedIndices[0] === clickedIndex;
8385
- return isSoleSelection ? [] : [clickedIndex];
8386
- };
8387
- var getAffectedIndices = function getAffectedIndices(oldSelection, newSelection) {
8388
- var allIndices = new Set([].concat(oldSelection, newSelection));
8389
- return Array.from(allIndices).filter(function (idx) {
8390
- return oldSelection.includes(idx) !== newSelection.includes(idx);
8391
- });
8392
- };
8393
- var shouldEnterEditMode = function shouldEnterEditMode(isReadonly, isShiftClick, newSelectionCount) {
8394
- return !isReadonly && !isShiftClick && newSelectionCount === 1;
8395
- };
8396
- var updateFeatureSelectionProperties = function updateFeatureSelectionProperties(dispatch, layerId, affectedIndices, newSelection) {
8397
- affectedIndices.forEach(function (idx) {
8398
- var isNowSelected = newSelection.includes(idx);
8399
- dispatch(layerActions.updateFeatureProperties({
8400
- layerId: layerId,
8401
- properties: {
8402
- highlightSelectedFeature: isNowSelected,
8403
- isSelected: isNowSelected
8404
- },
8405
- targetFeatureIndex: idx
8406
- }));
8407
- });
8408
- };
8409
- var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2) {
8410
- var layer = _ref2.layer,
8411
- layerId = _ref2.layerId,
8412
- zIndex = _ref2.zIndex,
8413
- style = _ref2.style,
8414
- hoverSelect = _ref2.hoverSelect;
8433
+ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref3) {
8434
+ var layer = _ref3.layer,
8435
+ layerId = _ref3.layerId,
8436
+ zIndex = _ref3.zIndex,
8437
+ style = _ref3.style,
8438
+ hoverSelect = _ref3.hoverSelect;
8415
8439
  var store = useStore();
8416
8440
  var dispatch = useDispatch();
8417
8441
  var activeDrawToolId = useSelector(drawingToolSelectors.getActiveDrawToolId);
@@ -8433,7 +8457,7 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8433
8457
  return drawingToolSelectors.getGeoJSONIntersectionBoundsLayerId(store, activeDrawToolId);
8434
8458
  });
8435
8459
  var activeFeatureIndex = React.useMemo(function () {
8436
- var _layer$geojson, _currentSelectedFeatu, _ref3;
8460
+ var _layer$geojson, _currentSelectedFeatu, _ref4;
8437
8461
  var currentActiveFeatureIndex = layerUtils.getSelectedFeatureIndexByIds(layer.geojson, layer.selectedFeatureIds);
8438
8462
  if (!shouldAllowMultipleShapes || !(activeDrawMode != null && activeDrawMode.selectionType) || !((_layer$geojson = layer.geojson) != null && _layer$geojson.features.length)) {
8439
8463
  return currentActiveFeatureIndex != null ? currentActiveFeatureIndex : 0;
@@ -8444,9 +8468,9 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8444
8468
  }
8445
8469
  var matchingFeatureIndex;
8446
8470
  for (var index = layer.geojson.features.length - 1; index >= 0; index -= 1) {
8447
- var _feature$properties;
8471
+ var _feature$properties2;
8448
8472
  var feature = layer.geojson.features[index];
8449
- if (((_feature$properties = feature.properties) == null ? void 0 : _feature$properties.selectionType) === activeDrawMode.selectionType) {
8473
+ if (((_feature$properties2 = feature.properties) == null ? void 0 : _feature$properties2.selectionType) === activeDrawMode.selectionType) {
8450
8474
  if (isGeoJSONGeometryEmpty(feature)) {
8451
8475
  return index;
8452
8476
  }
@@ -8455,32 +8479,55 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8455
8479
  }
8456
8480
  }
8457
8481
  }
8458
- return (_ref3 = matchingFeatureIndex != null ? matchingFeatureIndex : currentActiveFeatureIndex) != null ? _ref3 : 0;
8482
+ return (_ref4 = matchingFeatureIndex != null ? matchingFeatureIndex : currentActiveFeatureIndex) != null ? _ref4 : 0;
8459
8483
  }, [activeDrawMode == null ? void 0 : activeDrawMode.selectionType, layer.geojson, layer.selectedFeatureIds, shouldAllowMultipleShapes]);
8484
+ var sketchInProgress = React.useMemo(function () {
8485
+ var _layer$geojson2;
8486
+ return isSketchInProgress(layer.isInEditMode, layer.drawMode, (_layer$geojson2 = layer.geojson) == null ? void 0 : _layer$geojson2.features[activeFeatureIndex]);
8487
+ }, [layer.isInEditMode, layer.drawMode, layer.geojson, activeFeatureIndex]);
8488
+ var usesIdSelection = usesFeatureIdSelection(layer.isSelectable, hasRegisteredDrawTool);
8460
8489
  var displayGeojson = React.useMemo(function () {
8461
- var _layer$hoveredFeature;
8490
+ var _layer$hoveredFeature, _layer$selectedFeatur;
8462
8491
  if (!layer.geojson) {
8463
8492
  return undefined;
8464
8493
  }
8465
8494
  var hoveredFeatureIds = new Set((_layer$hoveredFeature = layer.hoveredFeatureIds) != null ? _layer$hoveredFeature : []);
8495
+ var selectedFeatureIdSet = new Set((_layer$selectedFeatur = layer.selectedFeatureIds) != null ? _layer$selectedFeatur : []);
8466
8496
  return Object.assign({}, layer.geojson, {
8467
8497
  features: layer.geojson.features.map(function (feature) {
8468
- var _feature$properties2;
8498
+ var _feature$properties3;
8469
8499
  var featureId = feature.id !== undefined ? String(feature.id) : undefined;
8470
- var originalId = typeof ((_feature$properties2 = feature.properties) == null ? void 0 : _feature$properties2.originalId) === 'string' ? feature.properties.originalId : layer.originalId;
8500
+ var originalId = typeof ((_feature$properties3 = feature.properties) == null ? void 0 : _feature$properties3.originalId) === 'string' ? feature.properties.originalId : layer.originalId;
8501
+ var selectionProperties = getSelectionProperties(usesIdSelection, selectedFeatureIdSet, featureId);
8471
8502
  return Object.assign({}, feature, {
8472
8503
  properties: Object.assign({}, feature.properties, {
8473
8504
  hover: featureId !== undefined && hoveredFeatureIds.has(featureId) || typeof originalId === 'string' && hoveredFeatureIds.has(originalId)
8474
- })
8505
+ }, selectionProperties)
8475
8506
  });
8476
8507
  })
8477
8508
  });
8478
- }, [layer.geojson, layer.hoveredFeatureIds, layer.originalId]);
8509
+ }, [layer.geojson, layer.hoveredFeatureIds, layer.originalId, layer.selectedFeatureIds, usesIdSelection]);
8510
+ var clearSelection = React.useCallback(function () {
8511
+ if (!shouldClearSelectionOnEmptyClick(activeDrawToolId, hasRegisteredDrawTool)) {
8512
+ return;
8513
+ }
8514
+ dispatch(layerActions.setSelectedFeatures({
8515
+ layerId: layerId,
8516
+ selectedFeatureIds: []
8517
+ }));
8518
+ dispatch(layerActions.toggleFeatureMode({
8519
+ layerId: layerId,
8520
+ isInEditMode: false,
8521
+ drawMode: ''
8522
+ }));
8523
+ }, [activeDrawToolId, hasRegisteredDrawTool, dispatch, layerId]);
8479
8524
  var onFeatureClick = React.useCallback(function (feature, isShiftClick) {
8480
8525
  var _clickedFeature$prope;
8481
- var clickedIndex = layer.geojson.features.findIndex(function (feat) {
8482
- return feature[0] && feat.id === feature[0].getId();
8483
- });
8526
+ if (!feature.length) {
8527
+ clearSelection();
8528
+ return;
8529
+ }
8530
+ var clickedIndex = findFeatureIndexByKey(layer.geojson, feature[0]);
8484
8531
  if (clickedIndex === -1) {
8485
8532
  return;
8486
8533
  }
@@ -8490,10 +8537,9 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8490
8537
  geojson: layer.geojson,
8491
8538
  selectedFeatureIds: layer.selectedFeatureIds
8492
8539
  });
8493
- // When a draw tool is registered for this layer, keep a sole selected
8494
- // feature selected on normal click. Deselecting the active edit target
8495
- // causes edit mode to collapse immediately; clearing selection should
8496
- // instead happen via empty-space click or multi-select interactions.
8540
+ // Drawing layers only: deselecting the active edit target collapses edit mode,
8541
+ // so a plain click on the sole selection keeps it. Clearing happens through
8542
+ // empty space or shift+click instead.
8497
8543
  var shouldPreventDeselect = hasRegisteredDrawTool && !isReadonly && !isShiftClick && currentSelection.length === 1 && currentSelection[0] === clickedIndex;
8498
8544
  var newSelection = shouldPreventDeselect ? [clickedIndex] : calculateNewSelection(currentSelection, clickedIndex, isShiftClick != null ? isShiftClick : false);
8499
8545
  var nextSelectedFeatureIds = newSelection.map(function (index) {
@@ -8505,25 +8551,13 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8505
8551
  layerId: layerId,
8506
8552
  selectedFeatureIds: nextSelectedFeatureIds
8507
8553
  }));
8508
- if (newSelection.length === 0) {
8554
+ if (newSelection.length === 0 || shouldExitEditModeForMultiSelect(newSelection.length)) {
8509
8555
  dispatch(layerActions.toggleFeatureMode({
8510
8556
  layerId: layerId,
8511
8557
  isInEditMode: false,
8512
8558
  drawMode: ''
8513
8559
  }));
8514
8560
  }
8515
- var affectedIndices = getAffectedIndices(currentSelection, newSelection);
8516
- affectedIndices.forEach(function (idx) {
8517
- var isNowSelected = newSelection.includes(idx);
8518
- dispatch(layerActions.updateFeatureProperties({
8519
- layerId: layerId,
8520
- properties: {
8521
- highlightSelectedFeature: isNowSelected,
8522
- isSelected: isNowSelected
8523
- },
8524
- targetFeatureIndex: idx
8525
- }));
8526
- });
8527
8561
  if (shouldEnterEditMode(isReadonly, isShiftClick != null ? isShiftClick : false, newSelection.length)) {
8528
8562
  dispatch(layerActions.toggleFeatureMode({
8529
8563
  layerId: layerId,
@@ -8531,27 +8565,15 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8531
8565
  drawMode: ''
8532
8566
  }));
8533
8567
  }
8534
- }, [hasRegisteredDrawTool, dispatch, layer.geojson, layer.selectedFeatureIds, layerId]);
8535
- var onNoFeatureClick = React.useCallback(function () {
8536
- if (activeDrawToolId) {
8537
- // do not disable edit mode when a drawing tool is active
8538
- return;
8539
- }
8540
- dispatch(layerActions.toggleFeatureMode({
8541
- layerId: layerId,
8542
- isInEditMode: false,
8543
- drawMode: ''
8544
- }));
8545
- }, [activeDrawToolId, dispatch, layerId]);
8568
+ }, [hasRegisteredDrawTool, dispatch, layer.geojson, layer.selectedFeatureIds, layerId, clearSelection]);
8569
+ var onNoFeatureClick = clearSelection;
8546
8570
  var onBoxSelect = React.useCallback(function (features, addToSelection) {
8547
- var _layer$geojson2;
8548
- if (!((_layer$geojson2 = layer.geojson) != null && _layer$geojson2.features.length)) {
8571
+ var _layer$geojson3;
8572
+ if (!((_layer$geojson3 = layer.geojson) != null && _layer$geojson3.features.length)) {
8549
8573
  return;
8550
8574
  }
8551
8575
  var boxSelectedIndices = features.map(function (feature) {
8552
- return layer.geojson.features.findIndex(function (feat) {
8553
- return feat.id === feature.getId();
8554
- });
8576
+ return findFeatureIndexByKey(layer.geojson, feature);
8555
8577
  }).filter(function (idx) {
8556
8578
  return idx !== -1;
8557
8579
  });
@@ -8562,7 +8584,7 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8562
8584
  geojson: layer.geojson,
8563
8585
  selectedFeatureIds: layer.selectedFeatureIds
8564
8586
  });
8565
- var newSelection = addToSelection ? Array.from(new Set([].concat(previousSelection, boxSelectedIndices))) : boxSelectedIndices;
8587
+ var newSelection = calculateBoxSelection(previousSelection, boxSelectedIndices, addToSelection);
8566
8588
  var nextSelectedFeatureIds = newSelection.map(function (index) {
8567
8589
  return layerUtils.getFeatureSelectionKey(layer.geojson.features[index]);
8568
8590
  }).filter(function (featureId) {
@@ -8572,21 +8594,66 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8572
8594
  layerId: layerId,
8573
8595
  selectedFeatureIds: nextSelectedFeatureIds
8574
8596
  }));
8575
- var affectedIndices = getAffectedIndices(previousSelection, newSelection);
8576
- updateFeatureSelectionProperties(dispatch, layerId, affectedIndices, newSelection);
8597
+ if (shouldExitEditModeForMultiSelect(newSelection.length)) {
8598
+ dispatch(layerActions.toggleFeatureMode({
8599
+ layerId: layerId,
8600
+ isInEditMode: false,
8601
+ drawMode: ''
8602
+ }));
8603
+ }
8577
8604
  }, [dispatch, layer.geojson, layer.selectedFeatureIds, layerId]);
8578
8605
  var boxSelectConfig = React.useMemo(function () {
8579
- return layer.isSelectable && !layer.isInEditMode ? {
8606
+ return usesIdSelection && !layer.isInEditMode ? {
8607
+ onBoxSelect: onBoxSelect,
8608
+ active: true
8609
+ } : undefined;
8610
+ }, [usesIdSelection, layer.isInEditMode, onBoxSelect]);
8611
+ // Mutually exclusive with boxSelectConfig, so only one DragBox is ever live.
8612
+ var editModeBoxSelectConfig = React.useMemo(function () {
8613
+ return usesIdSelection && layer.isInEditMode ? {
8580
8614
  onBoxSelect: onBoxSelect,
8581
8615
  active: true
8582
8616
  } : undefined;
8583
- }, [layer.isSelectable, layer.isInEditMode, onBoxSelect]);
8584
- var onViewOnlyFeatureClick = React.useCallback(function (feature) {
8585
- if (activeDrawToolId) {
8617
+ }, [usesIdSelection, layer.isInEditMode, onBoxSelect]);
8618
+ var selectedFeatureIndices = React.useMemo(function () {
8619
+ return getCurrentSelectedIndices({
8620
+ geojson: layer.geojson,
8621
+ selectedFeatureIds: layer.selectedFeatureIds
8622
+ });
8623
+ }, [layer.geojson, layer.selectedFeatureIds]);
8624
+ useDeselectAllOnEscape({
8625
+ selectedFeatureCount: selectedFeatureIndices.length,
8626
+ isInEditMode: layer.isInEditMode,
8627
+ hasRegisteredDrawTool: hasRegisteredDrawTool,
8628
+ onDeselectAll: clearSelection
8629
+ });
8630
+ var movableSelectedFeatureIds = React.useMemo(function () {
8631
+ return getMovableSelectedFeatureIds(layer.geojson, layer.selectedFeatureIds);
8632
+ }, [layer.geojson, layer.selectedFeatureIds]);
8633
+ var onMoveEnd = React.useCallback(function (movedFeatures) {
8634
+ if (!layerId || !movedFeatures.length) {
8635
+ return;
8636
+ }
8637
+ dispatch(layerActions.moveFeatures({
8638
+ layerId: layerId,
8639
+ movedFeatures: movedFeatures,
8640
+ geoJSONIntersectionLayerId: geoJSONIntersectionLayerId,
8641
+ geoJSONIntersectionBoundsLayerId: geoJSONIntersectionBoundsLayerId
8642
+ }));
8643
+ }, [dispatch, layerId, geoJSONIntersectionLayerId, geoJSONIntersectionBoundsLayerId]);
8644
+ var moveFeaturesConfig = React.useMemo(function () {
8645
+ var _layer$selectedFeatur2, _layer$selectedFeatur3;
8646
+ return shouldEnableMultiShapeMove(hasRegisteredDrawTool, layer.isInEditMode, (_layer$selectedFeatur2 = (_layer$selectedFeatur3 = layer.selectedFeatureIds) == null ? void 0 : _layer$selectedFeatur3.length) != null ? _layer$selectedFeatur2 : 0, movableSelectedFeatureIds.length) ? {
8647
+ onMoveEnd: onMoveEnd,
8648
+ active: true
8649
+ } : undefined;
8650
+ }, [hasRegisteredDrawTool, layer.isInEditMode, layer.selectedFeatureIds, movableSelectedFeatureIds, onMoveEnd]);
8651
+ var onViewOnlyFeatureClick = React.useCallback(function (feature, isShiftClick) {
8652
+ if (sketchInProgress) {
8586
8653
  return;
8587
8654
  }
8588
- onFeatureClick(feature);
8589
- }, [activeDrawToolId, onFeatureClick]);
8655
+ onFeatureClick(feature, isShiftClick);
8656
+ }, [sketchInProgress, onFeatureClick]);
8590
8657
  var updateFeature = React.useCallback(function (geojson, reason) {
8591
8658
  if (!layerId) {
8592
8659
  window.console.error('updateFeature without layerId');
@@ -8617,18 +8684,20 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8617
8684
  return jsxs(Fragment, {
8618
8685
  children: [jsx(OpenLayersFeatureLayer, {
8619
8686
  boxSelect: boxSelectConfig,
8620
- clickOnFeature: layer.isSelectable ? {
8687
+ clickOnFeature: usesIdSelection && !layer.isInEditMode ? {
8621
8688
  onClick: onFeatureClick,
8622
8689
  hitTolerance: 5
8623
8690
  } : undefined,
8624
8691
  featureCollection: !layer.isInEditMode ? displayGeojson : undefined,
8625
8692
  hoverSelect: hoverSelect,
8626
8693
  layerId: layerId,
8694
+ moveFeatures: moveFeaturesConfig,
8627
8695
  opacity: layer.opacity,
8628
8696
  style: style,
8629
8697
  visible: layer.enabled,
8630
8698
  zIndex: zIndex
8631
8699
  }), jsx(OpenLayersMapDraw, {
8700
+ boxSelect: editModeBoxSelectConfig,
8632
8701
  drawMode: layer.drawMode,
8633
8702
  exitDrawModeCallback: exitFeatureDrawMode,
8634
8703
  geojson: layer.geojson,
@@ -8636,6 +8705,7 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8636
8705
  onNoFeatureClick: onNoFeatureClick,
8637
8706
  onViewOnlyFeatureClick: onViewOnlyFeatureClick,
8638
8707
  selectedFeatureIndex: activeFeatureIndex,
8708
+ selectedFeatureIndices: selectedFeatureIndices,
8639
8709
  updateGeojson: updateFeature,
8640
8710
  viewOnlyStyle: style
8641
8711
  })]
@@ -8659,6 +8729,14 @@ var OpenLayersFeatureLayerConnect = function OpenLayersFeatureLayerConnect(_ref2
8659
8729
  * Copyright 2024 - Finnish Meteorological Institute (FMI)
8660
8730
  * Copyright 2024 - The Norwegian Meteorological Institute (MET Norway)
8661
8731
  * */
8732
+ var getStoredViewObjectForMap = function getStoredViewObjectForMap(mapId, store) {
8733
+ var syncGroupState = syncGroupsSelectors.getSynchronizationGroupState(store.getState());
8734
+ var thisAreaSyncGroupId = syncGroupState.groups.allIds.find(function (id) {
8735
+ var group = syncGroupState.groups.byId[id];
8736
+ return group.type === syncConstants.SYNCGROUPS_TYPE_SETBBOX && group.targets.allIds.includes(mapId);
8737
+ });
8738
+ return thisAreaSyncGroupId ? getViewObjectForSyncGroup(thisAreaSyncGroupId) : getViewObjectForMapId(mapId);
8739
+ };
8662
8740
  var useGetOpenLayersView = function useGetOpenLayersView(mapId, store) {
8663
8741
  var _useState = useState(),
8664
8742
  view = _useState[0],
@@ -8666,17 +8744,9 @@ var useGetOpenLayersView = function useGetOpenLayersView(mapId, store) {
8666
8744
  var _useState2 = useState(),
8667
8745
  name = _useState2[0],
8668
8746
  setName = _useState2[1];
8669
- var syncGroupState = syncGroupsSelectors.getSynchronizationGroupState(store.getState());
8670
- var syncGroupView = useMemo(function () {
8671
- var thisAreaSyncGroupId = syncGroupState.groups.allIds.find(function (id) {
8672
- var group = syncGroupState.groups.byId[id];
8673
- return group.type === syncConstants.SYNCGROUPS_TYPE_SETBBOX && group.targets.allIds.includes(mapId);
8674
- });
8675
- return thisAreaSyncGroupId ? getViewObjectForSyncGroup(thisAreaSyncGroupId) : null;
8676
- }, [mapId, syncGroupState.groups]);
8747
+ var storedView = getStoredViewObjectForMap(mapId, store);
8677
8748
  useEffect(function () {
8678
8749
  var mapBbox = mapSelectors.getBbox(store.getState(), mapId);
8679
- var storedView = syncGroupView || getViewObjectForMapId(mapId);
8680
8750
  if (storedView.view.getCenter() === undefined && mapBbox.left !== undefined) {
8681
8751
  storedView.view.fit([mapBbox.left, mapBbox.bottom, mapBbox.right, mapBbox.top]);
8682
8752
  }
@@ -8689,7 +8759,7 @@ var useGetOpenLayersView = function useGetOpenLayersView(mapId, store) {
8689
8759
  return function () {
8690
8760
  unByKey(eventKey);
8691
8761
  };
8692
- }, [syncGroupView, mapId, store]);
8762
+ }, [storedView, mapId, store]);
8693
8763
  return {
8694
8764
  view: view,
8695
8765
  name: name
@@ -9338,6 +9408,60 @@ var MapOptionsButtonConnect = function MapOptionsButtonConnect(_ref) {
9338
9408
  });
9339
9409
  };
9340
9410
 
9411
+ /* *
9412
+ * Licensed under the Apache License, Version 2.0 (the "License");
9413
+ * you may not use this file except in compliance with the License.
9414
+ * You may obtain a copy of the License at
9415
+ *
9416
+ * http://www.apache.org/licenses/LICENSE-2.0
9417
+ *
9418
+ * Unless required by applicable law or agreed to in writing, software
9419
+ * distributed under the License is distributed on an "AS IS" BASIS,
9420
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
9421
+ * See the License for the specific language governing permissions and
9422
+ * limitations under the License.
9423
+ *
9424
+ * Copyright 2026 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
9425
+ * Copyright 2026 - Finnish Meteorological Institute (FMI)
9426
+ * Copyright 2026 - The Norwegian Meteorological Institute (MET Norway)
9427
+ * */
9428
+ var InitialZoomToLayerConnect = function InitialZoomToLayerConnect(_ref) {
9429
+ var mapId = _ref.mapId,
9430
+ onHomeExtentResolved = _ref.onHomeExtentResolved;
9431
+ var initialZoomToLayerId = useSelector(function (store) {
9432
+ return mapSelectors.getInitialZoomToLayerId(store, mapId);
9433
+ });
9434
+ var layer = useSelector(function (store) {
9435
+ return initialZoomToLayerId ? layerSelectors.getLayerById(store, initialZoomToLayerId) : undefined;
9436
+ });
9437
+ var store = useStore();
9438
+ var view = useViewFromLayer(layer);
9439
+ var _useGetWMLayerInstanc = useGetWMLayerInstance(layer),
9440
+ wmLayer = _useGetWMLayerInstanc.wmLayer;
9441
+ var hasResolvedInitialLayerView = React.useRef(false);
9442
+ React.useEffect(function () {
9443
+ var storedView = getStoredViewObjectForMap(mapId, store);
9444
+ if (storedView.view !== view) {
9445
+ storedView.setView(view);
9446
+ }
9447
+ }, [mapId, store, view]);
9448
+ React.useEffect(function () {
9449
+ if (hasResolvedInitialLayerView.current || !wmLayer) {
9450
+ return;
9451
+ }
9452
+ var extent = getLayerExtentForProjection(wmLayer, view.getProjection().getCode());
9453
+ if (extent) {
9454
+ onHomeExtentResolved([].concat(extent));
9455
+ }
9456
+ hasResolvedInitialLayerView.current = true;
9457
+ store.dispatch(mapActions.setPendingInitialZoomToLayer({
9458
+ mapId: mapId,
9459
+ layerId: undefined
9460
+ }));
9461
+ }, [mapId, onHomeExtentResolved, store, view, wmLayer]);
9462
+ return null;
9463
+ };
9464
+
9341
9465
  /**
9342
9466
  * Connected component used to display the map and selected layers.
9343
9467
  * Includes options to disable the map controls and legend.
@@ -9368,6 +9492,9 @@ var OlMapViewConnect = function OlMapViewConnect(_ref) {
9368
9492
  var _React$useState = React.useState(),
9369
9493
  hoverLinkedFeature = _React$useState[0],
9370
9494
  setHoverLinkedFeature = _React$useState[1];
9495
+ var _React$useState2 = React.useState(undefined),
9496
+ derivedHomeExtent = _React$useState2[0],
9497
+ setDerivedHomeExtent = _React$useState2[1];
9371
9498
  var linkedPanelIds = useSelector(function (store) {
9372
9499
  return genericSelectors.selectLinkedPanelIdsForMap(store, mapId);
9373
9500
  });
@@ -9796,11 +9923,11 @@ var OlMapViewConnect = function OlMapViewConnect(_ref) {
9796
9923
  checkIfDataOutdated();
9797
9924
  }, [checkIfDataOutdated]);
9798
9925
  useMapViewLoadingIndicator(mapId, mapLayerIds);
9799
- var _React$useState2 = React.useState({
9926
+ var _React$useState3 = React.useState({
9800
9927
  debugMode: false
9801
9928
  }),
9802
- mapMode = _React$useState2[0],
9803
- setMapMode = _React$useState2[1];
9929
+ mapMode = _React$useState3[0],
9930
+ setMapMode = _React$useState3[1];
9804
9931
  React.useEffect(function () {
9805
9932
  var handleKeyPress = function handleKeyPress(event) {
9806
9933
  if (event.isTrusted && document.activeElement === document.body) {
@@ -9818,9 +9945,12 @@ var OlMapViewConnect = function OlMapViewConnect(_ref) {
9818
9945
  document.removeEventListener('keyup', handleKeyPress);
9819
9946
  };
9820
9947
  }, [mapMode]);
9948
+ var handleInitialHomeExtentResolved = React.useCallback(function (extent) {
9949
+ setDerivedHomeExtent(extent);
9950
+ }, []);
9821
9951
  var homeExtent = React.useMemo(function () {
9822
- return initialBbox && [initialBbox.left, initialBbox.bottom, initialBbox.right, initialBbox.top];
9823
- }, [initialBbox]);
9952
+ return initialBbox ? [initialBbox.left, initialBbox.bottom, initialBbox.right, initialBbox.top] : derivedHomeExtent;
9953
+ }, [derivedHomeExtent, initialBbox]);
9824
9954
  var dialogType = uiTypes.DialogTypes.Search + "-" + mapId;
9825
9955
  var _useSetupDialog = useSetupDialog(dialogType),
9826
9956
  isSearchDialogOpen = _useSetupDialog.isDialogOpen;
@@ -9864,9 +9994,12 @@ var OlMapViewConnect = function OlMapViewConnect(_ref) {
9864
9994
  features: linkedFeatureCollectionsWithHover.features
9865
9995
  };
9866
9996
  }, [linkedFeatureCollectionsWithHover]);
9867
- return jsx(TimeContext.Provider, {
9997
+ return jsxs(TimeContext.Provider, {
9868
9998
  value: prefetchTimespan,
9869
- children: jsxs(OpenLayersMapView, {
9999
+ children: [jsx(InitialZoomToLayerConnect, {
10000
+ mapId: mapId,
10001
+ onHomeExtentResolved: handleInitialHomeExtentResolved
10002
+ }), jsxs(OpenLayersMapView, {
9870
10003
  holdShiftToScroll: holdShiftToScroll,
9871
10004
  isFocused: isActiveWindowId,
9872
10005
  isMouseWheelZoomEnabled: !isTimeScrollingEnabled,
@@ -9958,7 +10091,7 @@ var OlMapViewConnect = function OlMapViewConnect(_ref) {
9958
10091
  }), !!displayTimeInMap && jsx(ClockConnect, {
9959
10092
  mapId: mapId
9960
10093
  }), children]
9961
- })
10094
+ })]
9962
10095
  });
9963
10096
  };
9964
10097
 
@@ -9974,7 +10107,7 @@ var OptionsMenuButtonConnect = function OptionsMenuButtonConnect(_ref) {
9974
10107
  var mapId = _ref.mapId,
9975
10108
  enableKeyboardShortcut = _ref.enableKeyboardShortcut;
9976
10109
  return jsx(OptionsMenuButton, {
9977
- animationLengthBtn: jsx(AnimationLengthButtonConnect, {
10110
+ animationLengthBtn: jsx(AnimationLengthPanelConnect, {
9978
10111
  mapId: mapId
9979
10112
  }),
9980
10113
  enableKeyboardShortcut: enableKeyboardShortcut,
@@ -12276,4 +12409,4 @@ var storyUtils = /*#__PURE__*/Object.freeze({
12276
12409
  useDefaultMapSettings: useDefaultMapSettings
12277
12410
  });
12278
12411
 
12279
- export { CORE_NAMESPACE, _ConfigurableMapConnect as ConfigurableMapConnect, CoreI18nProvider, CoreStoreProvider, CoreThemeProvider, CoreThemeStoreProvider, DemoWrapper, DemoWrapperConnect, HarmonieTempAndPrecipPreset, LanguageSelect, LayerInfoButtonConnect, LayerInfoDialogConnect, _LayerManager as LayerManager, BaseLayerRow as LayerManagerBaseLayerRow, LayerManagerConnect, DescriptionRow as LayerManagerDescriptionRow, HeaderOptions as LayerManagerHeaderOptions, LayerContainerRow as LayerManagerLayerContainerRow, LayerManagerMapButtonConnect, LegendConnect, LegendMapButtonConnect, LoadingIndicatorProgressBar, LocationSearch, LocationSearchConnect, MultiDimensionSelectMapButtonsConnect, MultiMapMultiDimensionSelectConnect as MultiMapDimensionSelectConnect, MultiMapViewConnect, MyMapLocation, OlMapViewConnect, RouterWrapperConnect, SearchControlButtonConnect, SelectedFeaturesDialogConnect, SyncGroupViewerConnect, TIME_SLIDER_Z_INDEX, TimeSliderConnect, componentsLookUp, coreTranslations, defaultBbox, defaultConfigurations, filterMapPresets, filterServices, filterTimeSeriesServices, getAffectedIndices, getCenterOfLinkedFeature, getCurrentSelectedIndices, getIsOutdated, getLanguageFromLocalStorage, getRenderedDimensions, languageLocalStorageKey, makeLayerInfoPayload, makePrefetchTimeSpan, saveLanguageToLocalStorage, storyUtils, testLayers, useFetchServices, useFitAndHighlightWarningFeature, useGetOpenLayersView, useLocationDetail, useLocationList, usePrefetchTimeSpan, useUpdateTimeSpan, useUpdateTimestep };
12412
+ export { CORE_NAMESPACE, _ConfigurableMapConnect as ConfigurableMapConnect, CoreI18nProvider, CoreStoreProvider, CoreThemeProvider, CoreThemeStoreProvider, DemoWrapper, DemoWrapperConnect, HarmonieTempAndPrecipPreset, LanguageSelect, LayerInfoButtonConnect, LayerInfoDialogConnect, _LayerManager as LayerManager, BaseLayerRow as LayerManagerBaseLayerRow, LayerManagerConnect, DescriptionRow as LayerManagerDescriptionRow, HeaderOptions as LayerManagerHeaderOptions, LayerContainerRow as LayerManagerLayerContainerRow, LayerManagerMapButtonConnect, LegendConnect, LegendMapButtonConnect, LoadingIndicatorProgressBar, LocationSearch, LocationSearchConnect, MultiDimensionSelectMapButtonsConnect, MultiMapMultiDimensionSelectConnect as MultiMapDimensionSelectConnect, MultiMapViewConnect, MyMapLocation, OlMapViewConnect, RouterWrapperConnect, SearchControlButtonConnect, SelectedFeaturesDialogConnect, SyncGroupViewerConnect, TIME_SLIDER_Z_INDEX, TimeSliderConnect, componentsLookUp, coreTranslations, defaultBbox, defaultConfigurations, filterMapPresets, filterServices, filterTimeSeriesServices, getCenterOfLinkedFeature, getCurrentSelectedIndices, getIsOutdated, getLanguageFromLocalStorage, getRenderedDimensions, getStoredViewObjectForMap, languageLocalStorageKey, makeLayerInfoPayload, makePrefetchTimeSpan, saveLanguageToLocalStorage, storyUtils, testLayers, useFetchServices, useFitAndHighlightWarningFeature, useGetOpenLayersView, useLocationDetail, useLocationList, usePrefetchTimeSpan, useUpdateTimeSpan, useUpdateTimestep };
@@ -1,15 +1,39 @@
1
+ import type { Feature } from 'ol';
1
2
  import type { StyleLike } from 'ol/style/Style';
2
3
  import React from 'react';
3
4
  import type { layerTypes } from '@opengeoweb/store';
4
5
  import type { HoverSelect } from '@opengeoweb/webmap-react';
6
+ import { getAffectedIndices } from '@opengeoweb/webmap-react';
7
+ export { getAffectedIndices };
8
+ /** Escape drops a multi selection only. A sole selection is kept, matching the
9
+ * plain click behaviour, and edit mode keeps its own Escape handling. */
10
+ export declare const shouldDeselectAllOnEscape: (selectedFeatureCount: number, isInEditMode: boolean | undefined, hasRegisteredDrawTool: boolean) => boolean;
11
+ interface DeselectAllOnEscapeOptions {
12
+ selectedFeatureCount: number;
13
+ isInEditMode: boolean | undefined;
14
+ hasRegisteredDrawTool: boolean;
15
+ onDeselectAll: () => void;
16
+ }
17
+ export declare const useDeselectAllOnEscape: ({ selectedFeatureCount, isInEditMode, hasRegisteredDrawTool, onDeselectAll, }: DeselectAllOnEscapeOptions) => void;
18
+ export declare const shouldClearSelectionOnEmptyClick: (activeDrawToolId: string | undefined, hasRegisteredDrawTool: boolean) => boolean;
19
+ export declare const isSketchInProgress: (isInEditMode: boolean | undefined, drawMode: string | undefined, activeFeature: GeoJSON.Feature | undefined) => boolean;
20
+ /** Soundings and time series set their own highlight, so they must stay out of this. */
21
+ export declare const usesFeatureIdSelection: (isSelectable: boolean | undefined, hasRegisteredDrawTool: boolean) => boolean;
22
+ export declare const getSelectionProperties: (usesIdSelection: boolean, selectedFeatureIdSet: Set<string>, featureId: string | undefined) => {
23
+ isSelected: boolean;
24
+ highlightSelectedFeature: boolean;
25
+ } | undefined;
26
+ export declare const getClickedFeatureKey: (feature?: Feature) => string | undefined;
27
+ export declare const findFeatureIndexByKey: (geojson: GeoJSON.FeatureCollection | undefined, feature?: Feature) => number;
28
+ export declare const getMovableSelectedFeatureIds: (geojson: GeoJSON.FeatureCollection | undefined, selectedFeatureIds: string[] | undefined) => string[];
29
+ /** Moving a set of shapes belongs to the drawing tool only: selection-only
30
+ * reference layers stay selectable but must never be dragged. */
31
+ export declare const shouldEnableMultiShapeMove: (hasRegisteredDrawTool: boolean, isInEditMode: boolean | undefined, selectedFeatureCount: number, movableFeatureCount: number) => boolean;
5
32
  interface SelectionState {
6
33
  geojson?: GeoJSON.FeatureCollection;
7
34
  selectedFeatureIds?: string[];
8
35
  }
9
36
  export declare const getCurrentSelectedIndices: ({ geojson, selectedFeatureIds, }: SelectionState) => number[];
10
- export declare const calculateNewSelection: (currentSelectedIndices: number[], clickedIndex: number, isShiftClick: boolean) => number[];
11
- export declare const getAffectedIndices: (oldSelection: number[], newSelection: number[]) => number[];
12
- export declare const shouldEnterEditMode: (isReadonly: boolean, isShiftClick: boolean, newSelectionCount: number) => boolean;
13
37
  interface OpenLayersFeatureLayerConnectProps {
14
38
  layer: layerTypes.ReduxLayer;
15
39
  layerId?: string;
@@ -18,4 +42,3 @@ interface OpenLayersFeatureLayerConnectProps {
18
42
  hoverSelect?: HoverSelect;
19
43
  }
20
44
  export declare const OpenLayersFeatureLayerConnect: React.FC<OpenLayersFeatureLayerConnectProps>;
21
- export {};
@@ -0,0 +1,8 @@
1
+ import type { Extent } from 'ol/extent';
2
+ import React from 'react';
3
+ interface InitialZoomToLayerConnectProps {
4
+ mapId: string;
5
+ onHomeExtentResolved: (extent: Extent) => void;
6
+ }
7
+ declare const InitialZoomToLayerConnect: React.FC<InitialZoomToLayerConnectProps>;
8
+ export default InitialZoomToLayerConnect;
@@ -1,9 +1,11 @@
1
1
  import type { Store } from '@reduxjs/toolkit';
2
2
  import type { View } from 'ol';
3
+ import { getViewObjectForMapId } from '@opengeoweb/store';
3
4
  import type { CoreAppStore } from '@opengeoweb/store';
4
5
  interface UseGetOpenLayersViewValue {
5
6
  view?: View;
6
7
  name?: string;
7
8
  }
9
+ export declare const getStoredViewObjectForMap: (mapId: string, store: Store<CoreAppStore>) => ReturnType<typeof getViewObjectForMapId>;
8
10
  export declare const useGetOpenLayersView: (mapId: string, store: Store<CoreAppStore>) => UseGetOpenLayersViewValue;
9
11
  export {};
@@ -0,0 +1,5 @@
1
+ import React from 'react';
2
+ export interface AnimationLengthPanelConnectProps {
3
+ mapId: string;
4
+ }
5
+ export declare const AnimationLengthPanelConnect: React.FC<AnimationLengthPanelConnectProps>;
package/package.json CHANGED
@@ -1,64 +1,64 @@
1
1
  {
2
- "name": "@opengeoweb/core",
3
- "version": "19.6.0",
4
- "description": "GeoWeb Core library for the opengeoweb project",
5
- "license": "Apache-2.0",
6
- "repository": {
7
- "type": "git",
8
- "url": "git@gitlab.com:opengeoweb/opengeoweb.git"
9
- },
10
- "main": "./dist/index.esm.js",
11
- "module": "./dist/index.esm.js",
12
- "types": "./dist/index.d.ts",
13
- "exports": {
14
- "./package.json": "./package.json",
15
- ".": {
16
- "@opengeoweb/source": "./src/index.ts",
17
- "types": "./dist/index.d.ts",
18
- "import": "./dist/index.esm.js",
19
- "default": "./dist/index.esm.js"
20
- }
21
- },
22
- "dependencies": {
23
- "@opengeoweb/shared": "*",
24
- "react-redux": "^9.2.0",
25
- "@opengeoweb/store": "*",
26
- "@opengeoweb/time-slider": "*",
27
- "@reduxjs/toolkit": "^2.6.1",
28
- "@opengeoweb/webmap-react": "*",
29
- "@opengeoweb/webmap": "*",
30
- "@opengeoweb/theme": "*",
31
- "@opengeoweb/layer-select": "*",
32
- "lodash": "^4.18.1",
33
- "ol": "^10.4.0",
34
- "react-sortablejs": "^6.1.4",
35
- "sortablejs": "^1.15.1",
36
- "@mui/system": "^9.0.1",
37
- "@opengeoweb/snackbar": "*",
38
- "react-draggable": "^4.4.6",
39
- "i18next": "^25.0.1",
40
- "react-i18next": "^15.1.1",
41
- "dompurify": "^3.4.13",
42
- "@mui/material": "^9.0.1",
43
- "@tanstack/react-query": "^5.85.5",
44
- "@opengeoweb/api": "*",
45
- "@types/geojson": "^7946.0.14",
46
- "@turf/turf": "^7.2.0",
47
- "proj4": "^2.15.0",
48
- "react-router": "^7.18.2"
49
- },
50
- "peerDependencies": {
51
- "react": "19",
52
- "@emotion/react": "*",
53
- "@emotion/styled": "*"
54
- },
55
- "devDependencies": {
56
- "eslint-plugin-storybook": "10.3.3"
57
- },
58
- "files": [
59
- "dist",
60
- "!dist/package.json",
61
- "README.md",
62
- "package.json"
63
- ]
64
- }
2
+ "name": "@opengeoweb/core",
3
+ "version": "19.6.1-nightly-20260910",
4
+ "description": "GeoWeb Core library for the opengeoweb project",
5
+ "license": "Apache-2.0",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "git@gitlab.com:opengeoweb/opengeoweb.git"
9
+ },
10
+ "main": "./dist/index.esm.js",
11
+ "module": "./dist/index.esm.js",
12
+ "types": "./dist/index.d.ts",
13
+ "exports": {
14
+ "./package.json": "./package.json",
15
+ ".": {
16
+ "@opengeoweb/source": "./src/index.ts",
17
+ "types": "./dist/index.d.ts",
18
+ "import": "./dist/index.esm.js",
19
+ "default": "./dist/index.esm.js"
20
+ }
21
+ },
22
+ "dependencies": {
23
+ "@opengeoweb/shared": "19.6.1-nightly-20260910",
24
+ "react-redux": "^9.2.0",
25
+ "@opengeoweb/store": "19.6.1-nightly-20260910",
26
+ "@opengeoweb/time-slider": "19.6.1-nightly-20260910",
27
+ "@reduxjs/toolkit": "^2.6.1",
28
+ "@opengeoweb/webmap-react": "19.6.1-nightly-20260910",
29
+ "@opengeoweb/webmap": "19.6.1-nightly-20260910",
30
+ "@opengeoweb/theme": "19.6.1-nightly-20260910",
31
+ "@opengeoweb/layer-select": "19.6.1-nightly-20260910",
32
+ "lodash": "^4.18.1",
33
+ "ol": "^10.4.0",
34
+ "react-sortablejs": "^6.1.4",
35
+ "sortablejs": "^1.15.1",
36
+ "@mui/system": "^9.0.1",
37
+ "@opengeoweb/snackbar": "19.6.1-nightly-20260910",
38
+ "react-draggable": "^4.4.6",
39
+ "i18next": "^25.0.1",
40
+ "react-i18next": "^15.1.1",
41
+ "dompurify": "^3.4.13",
42
+ "@mui/material": "^9.0.1",
43
+ "@tanstack/react-query": "^5.85.5",
44
+ "@opengeoweb/api": "19.6.1-nightly-20260910",
45
+ "@types/geojson": "^7946.0.14",
46
+ "@turf/turf": "^7.2.0",
47
+ "proj4": "^2.15.0",
48
+ "react-router": "^7.18.2"
49
+ },
50
+ "peerDependencies": {
51
+ "react": "19",
52
+ "@emotion/react": "*",
53
+ "@emotion/styled": "*"
54
+ },
55
+ "devDependencies": {
56
+ "eslint-plugin-storybook": "10.3.3"
57
+ },
58
+ "files": [
59
+ "dist",
60
+ "!dist/package.json",
61
+ "README.md",
62
+ "package.json"
63
+ ]
64
+ }