@opengeoweb/soundings 13.1.1 → 14.0.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/index.esm.js CHANGED
@@ -3,7 +3,7 @@ import React, { useRef, useEffect, useMemo, useState, Fragment as Fragment$1 } f
3
3
  import { useSelector, useDispatch } from 'react-redux';
4
4
  import { Box, Stack, Menu, ListSubheader, Typography, MenuItem, Tooltip, LinearProgress, Card, List, ListItem, ListItemIcon, Checkbox, ListItemText, Grid, ListItemButton } from '@mui/material';
5
5
  import { safelyInitI18nForTestsAndStories, sharedTranslations, SHARED_NAMESPACE, dateUtils, CustomSlider, CustomToggleButton, AlertBanner, ToolContainerDraggable, StyledSelect, SearchField, CustomAccordion } from '@opengeoweb/shared';
6
- import { selectorMemoizationOptions, uiSelectors, uiTypes, uiActions, genericActions, useSetupDialog, storeReducerMap, storeMiddlewares } from '@opengeoweb/store';
6
+ import { selectorMemoizationOptions, uiSelectors, uiTypes, uiActions, genericSelectors, mapSelectors, genericActions, syncGroupsActions, mapActions, useSetupDialog, storeReducerMap, storeMiddlewares } from '@opengeoweb/store';
7
7
  import { ParseISOTimeRangeDuration, parseISO8601IntervalToDateInterval, webmapUtils } from '@opengeoweb/webmap';
8
8
  import { MapFeatureClass } from '@opengeoweb/webmap-react';
9
9
  import * as d3 from 'd3';
@@ -246,7 +246,7 @@ const drawIsobars = ({
246
246
  * Copyright 2025 - Finnish Meteorological Institute (FMI)
247
247
  * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
248
248
  * */
249
- const SOUNDING_COLORS = ['#4487f3', '#c3213c', '#509a00', '#ED6C06'];
249
+ const SOUNDING_COLORS = ['#4487f3', '#c3213c', '#509a00', '#ED6C06', '#9810FA', '#FFDF20', '#312C85'];
250
250
  const getDiagramName = (t, diagramType) => {
251
251
  switch (diagramType) {
252
252
  case 'BIJVOET':
@@ -1604,7 +1604,7 @@ const Soundings = ({
1604
1604
  svg.on('mousemove', null);
1605
1605
  };
1606
1606
  // eslint-disable-next-line react-hooks/exhaustive-deps
1607
- }, [showTooltip, data, diagramType]);
1607
+ }, [showTooltip, data, diagramType, width, height]);
1608
1608
  useEffect(() => {
1609
1609
  const handleMouseLeave = () => {
1610
1610
  if (pressureRef.current) {
@@ -1648,8 +1648,7 @@ const Soundings = ({
1648
1648
  return jsxs(Box, {
1649
1649
  ref: canvasRef,
1650
1650
  sx: {
1651
- position: 'relative',
1652
- paddingBottom: '20px'
1651
+ position: 'relative'
1653
1652
  },
1654
1653
  children: [jsx("svg", {
1655
1654
  ref: svgRef
@@ -2013,7 +2012,7 @@ const constructQueryParams = (collectionId, baseUrl, instanceId, location, datet
2013
2012
  const rawData = await getEdrCollectionForLocation(collectionId, baseUrl, instanceId, location, parameters, datetime, verticalRange);
2014
2013
  const parsedData = parseCoverageCollection(rawData, parameterMappings);
2015
2014
  return sortSoundingFeatures(parsedData);
2016
- } catch (err) {
2015
+ } catch (_err) {
2017
2016
  throw new Error('Error fetching EDR data collection by location');
2018
2017
  }
2019
2018
  }
@@ -2031,7 +2030,7 @@ const constructQueryParams = (collectionId, baseUrl, instanceId, location, datet
2031
2030
  const rawData = await getEdrCollectionForPosition(collectionId, baseUrl, instanceId, location, parameters, datetime, verticalRange);
2032
2031
  const parsedData = parseCoverageCollection(rawData, parameterMappings);
2033
2032
  return sortSoundingFeatures(parsedData);
2034
- } catch (err) {
2033
+ } catch (_err) {
2035
2034
  throw new Error('Error fetching EDR data collection by position');
2036
2035
  }
2037
2036
  }
@@ -2288,10 +2287,33 @@ const translateKeyOutsideComponents = (key, params = undefined) => i18n.t(key, {
2288
2287
  });
2289
2288
  const useSoundingsTranslation = () => useTranslation(ns);
2290
2289
 
2291
- const MAX_LABEL_COUNT = 15;
2292
- const shouldHideLabel = (nItems, index) => {
2293
- const nthLabelsToShow = Math.ceil(nItems / MAX_LABEL_COUNT);
2294
- return nItems > MAX_LABEL_COUNT &&
2290
+ const TIMESLIDER_HEIGHT = 108;
2291
+ const LABEL_MIN_WIDTH = 48;
2292
+ const useMaxLabelCount = ref => {
2293
+ const [width, setWidth] = React.useState(0);
2294
+ React.useLayoutEffect(() => {
2295
+ let resizeObserver;
2296
+ if (ref.current) {
2297
+ resizeObserver = new ResizeObserver(entry => {
2298
+ const {
2299
+ width
2300
+ } = entry[0].target.getBoundingClientRect();
2301
+ setWidth(width);
2302
+ });
2303
+ resizeObserver.observe(ref.current);
2304
+ }
2305
+ return () => {
2306
+ if (resizeObserver) {
2307
+ resizeObserver.disconnect();
2308
+ }
2309
+ };
2310
+ }, [ref]);
2311
+ const maxLabelCount = Math.max(1, Math.floor(width / LABEL_MIN_WIDTH));
2312
+ return maxLabelCount;
2313
+ };
2314
+ const shouldHideLabel = (nItems, index, maxLabelCount) => {
2315
+ const nthLabelsToShow = Math.ceil(nItems / maxLabelCount);
2316
+ return nItems > maxLabelCount &&
2295
2317
  // hide labels if too many items
2296
2318
  index !== nItems - 1 && (
2297
2319
  // not last item (always shown)
@@ -2303,12 +2325,14 @@ const SoundingsTimeSlider = ({
2303
2325
  selectedTime,
2304
2326
  updateSelectedTime
2305
2327
  }) => {
2328
+ const sliderRef = React.useRef(null);
2329
+ const maxLabelCount = useMaxLabelCount(sliderRef);
2306
2330
  const {
2307
2331
  i18n
2308
2332
  } = useSoundingsTranslation();
2309
2333
  const marks = timesteps.filter(date => dateUtils.isValidIsoDateString(date)).map((date, index, arr) => ({
2310
2334
  value: dateUtils.unix(dateUtils.getUtcFromString(date)),
2311
- label: shouldHideLabel(arr.length, index) ? '' : dateUtils.dateToString(dateUtils.getUtcFromString(date), 'iii d. HH:mm', true, i18n.language)
2335
+ label: shouldHideLabel(arr.length, index, maxLabelCount) ? '' : dateUtils.dateToString(dateUtils.getUtcFromString(date), 'iii d. HH:mm', true, i18n.language)
2312
2336
  }));
2313
2337
  const onStepBackward = React.useCallback(() => {
2314
2338
  const currentIndex = timesteps.indexOf(selectedTime);
@@ -2321,10 +2345,13 @@ const SoundingsTimeSlider = ({
2321
2345
  updateSelectedTime(timesteps[followingIndex]);
2322
2346
  }, [selectedTime, updateSelectedTime, timesteps]);
2323
2347
  return jsxs("div", {
2348
+ ref: sliderRef,
2324
2349
  style: {
2325
2350
  display: 'flex',
2326
- marginTop: '40px',
2327
- marginRight: '4rem'
2351
+ paddingTop: '16px',
2352
+ marginRight: '80px',
2353
+ marginLeft: '24px',
2354
+ height: `${TIMESLIDER_HEIGHT}px`
2328
2355
  },
2329
2356
  children: [jsxs("div", {
2330
2357
  style: {
@@ -2378,8 +2405,8 @@ const SoundingsTimeSlider = ({
2378
2405
  extraStyles: {
2379
2406
  margin: '0px 20px 0px 20px',
2380
2407
  '.MuiSlider-markLabel': {
2381
- rotate: '-45deg',
2382
- translate: '-41%'
2408
+ transform: 'translate(-90%, 20px) rotate(-45deg)',
2409
+ transformOrigin: 'top center'
2383
2410
  }
2384
2411
  }
2385
2412
  })]
@@ -2425,9 +2452,9 @@ const slice = createSlice({
2425
2452
  updateSelectedLocation: (draft, payloadAction) => {
2426
2453
  const {
2427
2454
  sourceId,
2428
- locationId
2455
+ location
2429
2456
  } = payloadAction.payload;
2430
- draft.selectedLocations[sourceId] = locationId;
2457
+ draft.selectedLocations[sourceId] = location;
2431
2458
  },
2432
2459
  updateSelectedTime: (draft, payloadAction) => {
2433
2460
  const {
@@ -2512,9 +2539,7 @@ const SoundingsTimeSliderConnect = () => {
2512
2539
  const {
2513
2540
  data: timesteps
2514
2541
  } = useTimesteps(sourceSelections);
2515
- const mergedTimesteps = Array.from(new Set(timesteps.flat())).sort();
2516
- // TODO: next line should work in Storybook but does not: https://gitlab.com/opengeoweb/geoweb-assets/-/issues/4025
2517
- // const mergedTimesteps = [...new Set(timestepArrays.flat())].sort();
2542
+ const mergedTimesteps = [...new Set(timesteps.flat())].sort();
2518
2543
  // Ensure that the selected time is always one of the available timesteps
2519
2544
  const validSelectedTime = roundToClosestTimestep(selectedTime, mergedTimesteps);
2520
2545
  useEffect(() => {
@@ -2753,18 +2778,53 @@ const SoundingsLegend = ({
2753
2778
  });
2754
2779
  };
2755
2780
 
2756
- const size = 600;
2781
+ const DIAGRAM_TYPE_BUTTON_HEIGHT = 28;
2782
+ const useGetSize = (viewRef, legendRef) => {
2783
+ const [size, setSize] = React.useState(0);
2784
+ React.useLayoutEffect(() => {
2785
+ let resizeObserver;
2786
+ if (viewRef.current) {
2787
+ resizeObserver = new ResizeObserver(entry => {
2788
+ const {
2789
+ width,
2790
+ height
2791
+ } = entry[0].target.getBoundingClientRect();
2792
+ const availableWidth = width; // currently ignoring the windbarbs as they will be moved to a separate panel
2793
+ const legendHeight = legendRef.current?.getBoundingClientRect().height || 0;
2794
+ const availableHeight = height - legendHeight - DIAGRAM_TYPE_BUTTON_HEIGHT - TIMESLIDER_HEIGHT;
2795
+ // set size to the smallest of available width and height to keep the diagram square
2796
+ const newSize = Math.min(availableWidth, availableHeight);
2797
+ setSize(newSize);
2798
+ });
2799
+ resizeObserver.observe(viewRef.current);
2800
+ }
2801
+ return () => {
2802
+ if (resizeObserver) {
2803
+ resizeObserver.disconnect();
2804
+ }
2805
+ };
2806
+ }, [legendRef, viewRef]);
2807
+ return size;
2808
+ };
2757
2809
  const SoundingsConnect = ({
2758
2810
  viewId,
2759
2811
  diagramType = 'SKEWT'
2760
2812
  }) => {
2813
+ const viewRef = React.useRef(null);
2814
+ const legendRef = React.useRef(null);
2815
+ const size = useGetSize(viewRef, legendRef);
2761
2816
  const [chosenDiagramType, setChosenDiagramType] = React.useState(diagramType);
2762
2817
  const geojsonLayerId = React.useRef(webmapUtils.generateLayerId()).current;
2763
2818
  const dispatch = useDispatch();
2764
2819
  const selectedTime = useSelector(getSelectedTime);
2765
2820
  const sourceSelections = useSelector(getSelectedSources);
2766
2821
  const locationSelections = useSelector(getLocationSelections);
2767
- const collections = sourceSelections.map(({
2822
+ const selectedFeature = useSelector(state => genericSelectors.getSelectedFeature(state.syncGroups, viewId));
2823
+ const selectedMapIds = useSelector(state => genericSelectors.getLinkedMaps(state, viewId));
2824
+ const selectedMapPin = useSelector(state =>
2825
+ // mapPins are synced so we can arbitrarily choose the first linked map
2826
+ mapSelectors.getPinLocation(state, selectedMapIds[0]));
2827
+ const collections = useMemo(() => sourceSelections.map(({
2768
2828
  collectionId,
2769
2829
  url,
2770
2830
  name
@@ -2772,12 +2832,29 @@ const SoundingsConnect = ({
2772
2832
  id: collectionId,
2773
2833
  url,
2774
2834
  name: name || collectionId
2775
- }));
2835
+ })), [sourceSelections]);
2776
2836
  const {
2777
2837
  data: locationData
2778
2838
  } = useGetCollectionLocations(collections, true);
2839
+ /* Make sure the map pin is visible when linking new maps */
2840
+ useEffect(() => {
2841
+ const toggleMapPinVisibility = displayMapPin => {
2842
+ selectedMapIds.forEach(mapId => {
2843
+ dispatch(mapActions.toggleMapPinIsVisible({
2844
+ mapId,
2845
+ displayMapPin
2846
+ }));
2847
+ });
2848
+ };
2849
+ toggleMapPinVisibility(true);
2850
+ return () => {
2851
+ // Hide the map pin when the component is unmounted
2852
+ toggleMapPinVisibility(false);
2853
+ };
2854
+ }, [dispatch, selectedMapIds]);
2855
+ // construct and send geojson to map for drawing locations
2779
2856
  useEffect(() => {
2780
- const simpleGeoJson = {
2857
+ const geoJsonForMap = {
2781
2858
  type: 'FeatureCollection',
2782
2859
  features: (locationData || []).flatMap(({
2783
2860
  id: serviceId,
@@ -2785,12 +2862,14 @@ const SoundingsConnect = ({
2785
2862
  }) => features.map(feature => ({
2786
2863
  type: 'Feature',
2787
2864
  geometry: feature.geometry,
2788
- properties: {
2789
- ...feature.properties,
2790
- mapFeatureClass: MapFeatureClass.ObservationStation
2791
- },
2792
2865
  id: feature.id,
2793
- serviceId // TODO: remove in follow-up if not needed https://gitlab.com/opengeoweb/opengeoweb/-/issues/6312
2866
+ properties: {
2867
+ name: feature.properties.name,
2868
+ mapFeatureClass: MapFeatureClass.ObservationStation,
2869
+ highlightSelectedFeature: selectedFeature?.id === feature.id,
2870
+ id: feature.id,
2871
+ serviceId
2872
+ }
2794
2873
  })))
2795
2874
  };
2796
2875
  dispatch(genericActions.addSharedData({
@@ -2798,11 +2877,41 @@ const SoundingsConnect = ({
2798
2877
  data: {
2799
2878
  features: [{
2800
2879
  id: geojsonLayerId,
2801
- geoJSON: simpleGeoJson
2880
+ geoJSON: geoJsonForMap
2802
2881
  }]
2803
2882
  }
2804
2883
  }));
2805
- }, [collections, dispatch, geojsonLayerId, locationData, sourceSelections, viewId]);
2884
+ }, [dispatch, geojsonLayerId, locationData, selectedFeature?.id, viewId]);
2885
+ useEffect(() => () => {
2886
+ if (viewId) {
2887
+ dispatch(syncGroupsActions.deleteSharedData({
2888
+ panelId: viewId
2889
+ }));
2890
+ }
2891
+ }, [dispatch, viewId]);
2892
+ // Map location selection has changed. Update soundings location
2893
+ // since the location menu may be out of sync
2894
+ useEffect(() => {
2895
+ const serviceId = selectedFeature?.serviceId;
2896
+ if (serviceId) {
2897
+ dispatch(soundingsActions.updateSelectedLocation({
2898
+ sourceId: serviceId,
2899
+ location: selectedFeature?.id
2900
+ }));
2901
+ }
2902
+ }, [dispatch, selectedFeature?.id, selectedFeature?.serviceId]);
2903
+ // Map pin change on its own implies position selection
2904
+ useEffect(() => {
2905
+ // Use first source. Multiple sources will be implemented later.
2906
+ // https://gitlab.com/opengeoweb/geoweb-assets/-/issues/4254
2907
+ const source = sourceSelections[0];
2908
+ if (source?.collectionId && !selectedFeature?.id && selectedMapPin?.lon && selectedMapPin.lat) {
2909
+ dispatch(soundingsActions.updateSelectedLocation({
2910
+ sourceId: source.collectionId,
2911
+ location: [selectedMapPin.lon, selectedMapPin.lat]
2912
+ }));
2913
+ }
2914
+ }, [selectedMapPin, selectedFeature?.id, sourceSelections, dispatch]);
2806
2915
  const {
2807
2916
  error,
2808
2917
  data,
@@ -2818,11 +2927,12 @@ const SoundingsConnect = ({
2818
2927
  position: 'absolute',
2819
2928
  width: '100%'
2820
2929
  }
2821
- }), jsxs(Box, {
2930
+ }), jsxs(Stack, {
2822
2931
  sx: {
2823
- padding: 2,
2824
- overflowY: 'scroll',
2825
- height: '100%'
2932
+ padding: '12px',
2933
+ width: '100%',
2934
+ height: '100%',
2935
+ backgroundColor: 'geowebColors.background.surfaceApp'
2826
2936
  },
2827
2937
  children: [jsx(Box, {
2828
2938
  paddingLeft: 5,
@@ -2830,14 +2940,21 @@ const SoundingsConnect = ({
2830
2940
  viewId: viewId
2831
2941
  })
2832
2942
  }), jsxs(Card, {
2943
+ ref: viewRef,
2944
+ sx: {
2945
+ width: '100%',
2946
+ height: '100%'
2947
+ },
2833
2948
  children: [jsx(Box, {
2834
2949
  sx: {
2835
2950
  display: 'flex',
2836
2951
  justifyContent: 'center',
2837
2952
  paddingTop: '2px',
2838
2953
  paddingRight: '2px',
2839
- width: size
2954
+ minHeight: '24px',
2955
+ width: '100%'
2840
2956
  },
2957
+ ref: legendRef,
2841
2958
  children: jsx(SoundingsLegend, {
2842
2959
  sourceSelections: sourceSelections
2843
2960
  })
@@ -2866,8 +2983,15 @@ const SoundingsConnect = ({
2866
2983
  }
2867
2984
  },
2868
2985
  diagramType: chosenDiagramType
2986
+ }), jsx(Box, {
2987
+ sx: {
2988
+ position: 'absolute',
2989
+ bottom: '8px',
2990
+ width: '100%'
2991
+ },
2992
+ children: jsx(SoundingsTimeSliderConnect, {})
2869
2993
  })]
2870
- }), jsx(SoundingsTimeSliderConnect, {})]
2994
+ })]
2871
2995
  })]
2872
2996
  });
2873
2997
  };
@@ -3226,6 +3350,8 @@ const LocationsMenuConnect = ({
3226
3350
  const locationSelections = useSelector(getLocationSelections);
3227
3351
  const selectedSourceIds = useSelector(getSelectedSourcesIds);
3228
3352
  const sourcesAmount = useSelector(getSelectedSourcesAmount);
3353
+ const panelId = useSelector(store => uiSelectors.getDialogMapId(store, uiTypes.DialogTypes.SoundingsLocations));
3354
+ const selectedMapIds = useSelector(state => genericSelectors.getLinkedMaps(state, panelId));
3229
3355
  const {
3230
3356
  data,
3231
3357
  isPending,
@@ -3234,16 +3360,53 @@ const LocationsMenuConnect = ({
3234
3360
  const services = useMemo(() => {
3235
3361
  return data?.filter(service => selectedSourceIds.includes(service.id)) || [];
3236
3362
  }, [data, selectedSourceIds]);
3363
+ const handleFeatureSelect = (panelId, featureId) => {
3364
+ dispatch(genericActions.addSharedData({
3365
+ panelId,
3366
+ data: {
3367
+ selectedFeatureId: featureId
3368
+ }
3369
+ }));
3370
+ };
3371
+ const setMapPinLocation = mapPinLocation => {
3372
+ selectedMapIds.forEach(mapId => {
3373
+ dispatch(mapActions.setMapPinLocation({
3374
+ mapId,
3375
+ mapPinLocation
3376
+ }));
3377
+ });
3378
+ };
3237
3379
  return jsx(LocationsMenu, {
3238
3380
  isOpen: isDialogOpen,
3239
3381
  onClose: onCloseDialog,
3240
3382
  services: services,
3241
3383
  selectedSourcesAmount: sourcesAmount,
3242
3384
  selectedLocations: locationSelections,
3243
- updateSelectedLocation: (sourceId, locationId) => void dispatch(soundingsActions.updateSelectedLocation({
3244
- sourceId,
3245
- locationId
3246
- })),
3385
+ updateSelectedLocation: (sourceId, locationId) => {
3386
+ dispatch(soundingsActions.updateSelectedLocation({
3387
+ sourceId,
3388
+ location: locationId
3389
+ }));
3390
+ // Use first source. Multiple sources will be implemented later.
3391
+ // https://gitlab.com/opengeoweb/geoweb-assets/-/issues/4254
3392
+ const isFirstSource = sourceId === selectedSourceIds[0];
3393
+ if (!isFirstSource) {
3394
+ return;
3395
+ }
3396
+ if (locationId) {
3397
+ handleFeatureSelect(panelId, locationId);
3398
+ }
3399
+ const foundFeature = services[0].features.find(feature => feature.id === locationId);
3400
+ // check type for typescript
3401
+ if (foundFeature?.geometry.type === 'Point') {
3402
+ const [lon, lat] = foundFeature.geometry.coordinates;
3403
+ setMapPinLocation({
3404
+ lon,
3405
+ lat,
3406
+ id: locationId
3407
+ });
3408
+ }
3409
+ },
3247
3410
  onMouseDown: setDialogOrder,
3248
3411
  order: dialogOrder,
3249
3412
  isLoading: isPending,
@@ -3251,9 +3414,8 @@ const LocationsMenuConnect = ({
3251
3414
  });
3252
3415
  };
3253
3416
 
3254
- const soundingsComponentType = 'Soundings';
3255
3417
  const soundingsLookUp = payload => {
3256
- if (payload.componentType === soundingsComponentType) {
3418
+ if (payload.componentType === 'Soundings') {
3257
3419
  return jsx(SoundingsConnect, {
3258
3420
  viewId: payload.id,
3259
3421
  diagramType: payload.diagramType ? payload.diagramType : 'SKEWT'
@@ -3290,4 +3452,4 @@ const createMockStore = mockState => configureStore({
3290
3452
  middleware: getDefaultMiddleware => getDefaultMiddleware().concat(storeMiddlewares)
3291
3453
  });
3292
3454
 
3293
- export { LocationsMenuConnect, SOUNDINGS_NAMESPACE, SoundingsConnect, SoundingsSourceDialogConnect, createMockStore, initSoundingsI18n, soundingDiagrams, soundingsComponentType, soundingsLookUp, soundingsReducersMap, soundingsTranslations, translateKeyOutsideComponents, useSoundingsTranslation };
3455
+ export { LocationsMenuConnect, SOUNDINGS_NAMESPACE, SoundingsConnect, SoundingsSourceDialogConnect, createMockStore, initSoundingsI18n, soundingDiagrams, soundingsLookUp, soundingsReducersMap, soundingsTranslations, translateKeyOutsideComponents, useSoundingsTranslation };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeoweb/soundings",
3
- "version": "13.1.1",
3
+ "version": "14.0.0",
4
4
  "description": "GeoWeb soundings library for the opengeoweb project",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,24 +8,24 @@
8
8
  "url": "git@gitlab.com:opengeoweb/opengeoweb.git"
9
9
  },
10
10
  "dependencies": {
11
- "@opengeoweb/api": "13.1.1",
11
+ "@opengeoweb/api": "14.0.0",
12
12
  "@tanstack/react-query": "^5.85.5",
13
13
  "react": "^18.3.1",
14
14
  "d3": "^7.9.0",
15
15
  "react-i18next": "^15.1.1",
16
16
  "i18next": "^25.0.1",
17
- "@opengeoweb/shared": "13.1.1",
17
+ "@opengeoweb/shared": "14.0.0",
18
18
  "@mui/material": "^7.0.1",
19
- "@opengeoweb/store": "13.1.1",
19
+ "@opengeoweb/store": "14.0.0",
20
20
  "react-redux": "^9.2.0",
21
- "@opengeoweb/theme": "13.1.1",
21
+ "@opengeoweb/theme": "14.0.0",
22
22
  "@reduxjs/toolkit": "^2.6.1",
23
- "@opengeoweb/time-slider": "13.1.1",
23
+ "@opengeoweb/time-slider": "14.0.0",
24
24
  "react-window": "^1.8.10",
25
- "@opengeoweb/webmap": "13.1.1",
25
+ "@opengeoweb/webmap": "14.0.0",
26
26
  "lodash": "^4.17.21",
27
27
  "@types/geojson": "^7946.0.14",
28
- "@opengeoweb/webmap-react": "13.1.1"
28
+ "@opengeoweb/webmap-react": "14.0.0"
29
29
  },
30
30
  "devDependencies": {
31
31
  "eslint-plugin-storybook": "9.0.17"
@@ -1,10 +1,9 @@
1
1
  import { SoundingsCollection } from '@opengeoweb/shared';
2
2
  import { SoundingFeature } from './types';
3
- import { SourceType } from '../../store/types';
3
+ import { Coordinates, SourceType } from '../../store/types';
4
4
  import { SoundingsCollectionsWithInstances } from '../../components/Soundings/types';
5
5
  import { ServiceWithLocations } from '../../components/MenuBar/LocationsMenu';
6
6
  export declare const sortSoundingFeatures: (data: SoundingFeature[]) => SoundingFeature[];
7
- type Coordinates = [number, number];
8
7
  export declare const defaultParameterMappings: {
9
8
  temperature: string;
10
9
  windDirection: string;
@@ -42,4 +41,3 @@ export declare const useGetCollectionLocations: <SoundingsCollectionLike extends
42
41
  isPending: boolean;
43
42
  error: Error | null | undefined;
44
43
  };
45
- export {};
@@ -1,4 +1,5 @@
1
1
  import { LocationsResponseFromBE } from '../../api/edr-api/types';
2
+ import { Coordinates } from '../../store/types';
2
3
  export type ServiceWithLocations = LocationsResponseFromBE & {
3
4
  id: string;
4
5
  name: string;
@@ -6,7 +7,7 @@ export type ServiceWithLocations = LocationsResponseFromBE & {
6
7
  interface Props {
7
8
  services: ServiceWithLocations[];
8
9
  selectedSourcesAmount: number;
9
- selectedLocations: Record<string, string | undefined>;
10
+ selectedLocations: Record<string, string | Coordinates | undefined>;
10
11
  isOpen: boolean;
11
12
  onClose: () => void;
12
13
  updateSelectedLocation: (sourceId: string, locationId: string | undefined) => void;
@@ -1,4 +1,5 @@
1
1
  import React from 'react';
2
+ export declare const TIMESLIDER_HEIGHT = 108;
2
3
  export interface SoundingsTimeSliderProps {
3
4
  timesteps: string[];
4
5
  selectedTime: string;
@@ -20,18 +20,18 @@ export declare const getSourceSelections: ((state: SoundingsModuleState) => Reco
20
20
  memoize: typeof import("reselect").weakMapMemoize;
21
21
  argsMemoize: typeof import("reselect").weakMapMemoize;
22
22
  };
23
- export declare const getLocationSelections: ((state: SoundingsModuleState) => Record<string, string | undefined>) & {
23
+ export declare const getLocationSelections: ((state: SoundingsModuleState) => Record<string, string | import("./types").Coordinates | undefined>) & {
24
24
  clearCache: () => void;
25
25
  resultsCount: () => number;
26
26
  resetResultsCount: () => void;
27
27
  } & {
28
- resultFunc: (resultFuncArgs_0: SoundingsStore) => Record<string, string | undefined>;
29
- memoizedResultFunc: ((resultFuncArgs_0: SoundingsStore) => Record<string, string | undefined>) & {
28
+ resultFunc: (resultFuncArgs_0: SoundingsStore) => Record<string, string | import("./types").Coordinates | undefined>;
29
+ memoizedResultFunc: ((resultFuncArgs_0: SoundingsStore) => Record<string, string | import("./types").Coordinates | undefined>) & {
30
30
  clearCache: () => void;
31
31
  resultsCount: () => number;
32
32
  resetResultsCount: () => void;
33
33
  };
34
- lastResult: () => Record<string, string | undefined>;
34
+ lastResult: () => Record<string, string | import("./types").Coordinates | undefined>;
35
35
  dependencies: [(store: SoundingsModuleState) => SoundingsStore];
36
36
  recomputations: () => number;
37
37
  resetRecomputations: () => void;
@@ -20,16 +20,18 @@ export interface SourceSelection {
20
20
  }
21
21
  export interface UpdateLocationPayload {
22
22
  sourceId: string;
23
- locationId: string | undefined;
23
+ location: string | Coordinates | undefined;
24
24
  }
25
25
  export interface UpdateTimePayload {
26
26
  selectedTime: string;
27
27
  }
28
+ /** [lon, lat] */
29
+ export type Coordinates = [number, number];
28
30
  export interface SoundingsStore {
29
31
  /** key: sourceId */
30
32
  selectedSources: Record<string, SourceSelection>;
31
- /** key: sourceId, value: locationId */
32
- selectedLocations: Record<string, string | undefined>;
33
+ /** key: sourceId, value: locationId or position coordinates */
34
+ selectedLocations: Record<string, string | Coordinates | undefined>;
33
35
  /** ISO date, e.g., 2025-01-02T12:00:00.000Z */
34
36
  selectedTime: string;
35
37
  }
@@ -1,7 +1,7 @@
1
1
  import { SoundingDiagramType } from '../api/edr-api/types';
2
- export declare const soundingsComponentType = "Soundings";
2
+ export type SoundingsComponentType = 'Soundings';
3
3
  export interface SoundingsLookUpPayload {
4
- componentType: typeof soundingsComponentType;
4
+ componentType: SoundingsComponentType;
5
5
  id: string;
6
6
  title?: string;
7
7
  diagramType?: SoundingDiagramType;