@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 +207 -45
- package/package.json +8 -8
- package/src/lib/api/edr-api/hooks.d.ts +1 -3
- package/src/lib/components/MenuBar/LocationsMenu.d.ts +2 -1
- package/src/lib/components/SoundingsTimeSlider/SoundingsTimeSlider.d.ts +1 -0
- package/src/lib/store/selectors.d.ts +4 -4
- package/src/lib/store/types.d.ts +5 -3
- package/src/lib/utils/soundingsLookUp.d.ts +2 -2
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 (
|
|
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 (
|
|
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
|
|
2292
|
-
const
|
|
2293
|
-
|
|
2294
|
-
|
|
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
|
-
|
|
2327
|
-
marginRight: '
|
|
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
|
-
|
|
2382
|
-
|
|
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
|
-
|
|
2455
|
+
location
|
|
2429
2456
|
} = payloadAction.payload;
|
|
2430
|
-
draft.selectedLocations[sourceId] =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
|
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
|
-
|
|
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:
|
|
2880
|
+
geoJSON: geoJsonForMap
|
|
2802
2881
|
}]
|
|
2803
2882
|
}
|
|
2804
2883
|
}));
|
|
2805
|
-
}, [
|
|
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(
|
|
2930
|
+
}), jsxs(Stack, {
|
|
2822
2931
|
sx: {
|
|
2823
|
-
padding:
|
|
2824
|
-
|
|
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
|
-
|
|
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
|
-
})
|
|
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) =>
|
|
3244
|
-
|
|
3245
|
-
|
|
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 ===
|
|
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,
|
|
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": "
|
|
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": "
|
|
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": "
|
|
17
|
+
"@opengeoweb/shared": "14.0.0",
|
|
18
18
|
"@mui/material": "^7.0.1",
|
|
19
|
-
"@opengeoweb/store": "
|
|
19
|
+
"@opengeoweb/store": "14.0.0",
|
|
20
20
|
"react-redux": "^9.2.0",
|
|
21
|
-
"@opengeoweb/theme": "
|
|
21
|
+
"@opengeoweb/theme": "14.0.0",
|
|
22
22
|
"@reduxjs/toolkit": "^2.6.1",
|
|
23
|
-
"@opengeoweb/time-slider": "
|
|
23
|
+
"@opengeoweb/time-slider": "14.0.0",
|
|
24
24
|
"react-window": "^1.8.10",
|
|
25
|
-
"@opengeoweb/webmap": "
|
|
25
|
+
"@opengeoweb/webmap": "14.0.0",
|
|
26
26
|
"lodash": "^4.17.21",
|
|
27
27
|
"@types/geojson": "^7946.0.14",
|
|
28
|
-
"@opengeoweb/webmap-react": "
|
|
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;
|
|
@@ -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;
|
package/src/lib/store/types.d.ts
CHANGED
|
@@ -20,16 +20,18 @@ export interface SourceSelection {
|
|
|
20
20
|
}
|
|
21
21
|
export interface UpdateLocationPayload {
|
|
22
22
|
sourceId: string;
|
|
23
|
-
|
|
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
|
|
2
|
+
export type SoundingsComponentType = 'Soundings';
|
|
3
3
|
export interface SoundingsLookUpPayload {
|
|
4
|
-
componentType:
|
|
4
|
+
componentType: SoundingsComponentType;
|
|
5
5
|
id: string;
|
|
6
6
|
title?: string;
|
|
7
7
|
diagramType?: SoundingDiagramType;
|