@opengeoweb/soundings 19.2.0 → 19.4.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/dist/index.esm.js CHANGED
@@ -1,15 +1,15 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { Stack, Box, Tooltip, Typography, styled, Card, IconButton, Menu, ListSubheader, MenuItem, LinearProgress, List as List$1, ListItem, ListItemIcon, Checkbox, ListItemText, Grid, ListItemButton, Switch, ToggleButtonGroup, ToggleButton } from '@mui/material';
3
3
  import { range, isEqual } from 'lodash';
4
- import React, { useMemo, useRef, useEffect, useState, Fragment as Fragment$1, useCallback } from 'react';
4
+ import React, { useMemo, useRef, useEffect, useId, useState, Fragment as Fragment$1, useCallback } from 'react';
5
5
  import { useDispatch, useSelector, shallowEqual } from 'react-redux';
6
- import { dateUtils, referenceTimeUtils, safelyInitI18nForTestsAndStories, sharedTranslations, SHARED_NAMESPACE, SwitchButton, CustomToggleButton, CustomSlider, AlertBanner, ToolContainerDraggable, StyledSelect, SearchField, CustomAccordion } from '@opengeoweb/shared';
7
- import { selectorMemoizationOptions, uiSelectors, uiTypes, uiActions, genericActions, useRegisterFeatureSyncSource, genericSelectors, useSyncgroupTimeState, mapSelectors, mapActions, useUpdateFeatureSyncLayers, useSetupDialog, layerActions, storeReducerMap, storeMiddlewares } from '@opengeoweb/store';
6
+ import { dateUtils, referenceTimeUtils, safelyInitI18nForTestsAndStories, sharedTranslations, SHARED_NAMESPACE, SwitchButton, CustomToggleButton, LegendToggleButton, CustomSlider, AlertBanner, ToolContainerDraggable, TooltipSelect, AlertIcon, useResizeObserver, SearchField, CustomAccordion } from '@opengeoweb/shared';
7
+ import { selectorMemoizationOptions, uiSelectors, uiTypes, uiActions, genericActions, useSyncgroupTimeState, useRegisterFeatureSyncSource, genericSelectors, mapSelectors, mapActions, useUpdateFeatureSyncLayers, layerActions, useSetupDialog, storeReducerMap, storeMiddlewares } from '@opengeoweb/store';
8
8
  import { Layers, Sources, Location, Equalizer, ArrowDropDownDown, DragHandle, Close, Visibility, VisibilityOff } from '@opengeoweb/theme';
9
9
  import { ParseISOTimeRangeDuration, parseISO8601IntervalToDateInterval, handleDateUtilsISOString, webmapUtils } from '@opengeoweb/webmap';
10
10
  import { MapFeatureClass } from '@opengeoweb/webmap-react';
11
11
  import * as d3 from 'd3';
12
- import { useQueries } from '@tanstack/react-query';
12
+ import { queryOptions, useQueryClient, useQueries } from '@tanstack/react-query';
13
13
  import { withJsonFormat, getHeaders, handleResponse } from '@opengeoweb/api';
14
14
  import { createSlice, createSelector, combineReducers, configureStore } from '@reduxjs/toolkit';
15
15
  import i18n from 'i18next';
@@ -35,6 +35,9 @@ var en = {
35
35
  "soundings-time-mismatch-title": "Selected time in the sounding view does not match map view time.",
36
36
  "soundings-toggle-location-markers-label": "Markers",
37
37
  "soundings-toggle-location-markers-tooltip": "Show/hide location markers",
38
+ "soundings-legend": "Legend",
39
+ "soundings-legend-tooltip-show": "Show legend",
40
+ "soundings-legend-tooltip-hide": "Hide legend",
38
41
  "no-sounding-data-available": "No sounding data available"
39
42
  };
40
43
  var no = {
@@ -52,6 +55,9 @@ var no = {
52
55
  "soundings-time-mismatch-title": "Selected time in the sounding view does not match map view time.",
53
56
  "soundings-toggle-location-markers-label": "Lokasjoner",
54
57
  "soundings-toggle-location-markers-tooltip": "Vis/skjul lokasjoner",
58
+ "soundings-legend": "Forklaring",
59
+ "soundings-legend-tooltip-show": "Vis forklaring",
60
+ "soundings-legend-tooltip-hide": "Skjul forklaring",
55
61
  "no-sounding-data-available": "No sounding data available"
56
62
  };
57
63
  var nl = {
@@ -69,6 +75,9 @@ var nl = {
69
75
  "soundings-time-mismatch-title": "Selected time in the sounding view does not match map view time.",
70
76
  "soundings-toggle-location-markers-label": "Kaart locaties",
71
77
  "soundings-toggle-location-markers-tooltip": "Toon/Verberg locatie punten",
78
+ "soundings-legend": "Legenda",
79
+ "soundings-legend-tooltip-show": "Toon legenda",
80
+ "soundings-legend-tooltip-hide": "Verberg legenda",
72
81
  "no-sounding-data-available": "No sounding data available"
73
82
  };
74
83
  var fi = {
@@ -86,6 +95,9 @@ var fi = {
86
95
  "soundings-time-mismatch-title": "Valittu aika luotausnäytöllä ei vastaa karttanäytön aikaa",
87
96
  "soundings-toggle-location-markers-label": "Kohdemerkit",
88
97
  "soundings-toggle-location-markers-tooltip": "Näytä/piilota kohdemerkit",
98
+ "soundings-legend": "Selite",
99
+ "soundings-legend-tooltip-show": "Näytä selite",
100
+ "soundings-legend-tooltip-hide": "Piilota selite",
89
101
  "no-sounding-data-available": "Ei luotausdataa saatavilla"
90
102
  };
91
103
  var soundingsTranslations = {
@@ -139,6 +151,8 @@ var REFERENCE_PRESSURE = 1000 * 100; // 1000hPa = 100000Pa
139
151
  var EARTH_MEAN_RADIUS = 6371; // [km]
140
152
  var MAX_PROXIMITY_RADIUS = 100; // [km]
141
153
  var SAME_POINT_DISTANCE_KM = 0.05;
154
+ var SOUNDINGS_CONTAINER_BREAKPOINT_PX = '720px';
155
+ var SOUNDINGS_CONTAINER_NAME = 'soundings-container';
142
156
 
143
157
  /* *
144
158
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -308,6 +322,14 @@ var initializeSvg$1 = function initializeSvg(_ref) {
308
322
  // Ensure clipPath uses same coordinate system as the translated group
309
323
  // so width/height starting at x=0,y=0 align with axis origin.
310
324
  .attr('clipPathUnits', 'userSpaceOnUse').append('rect').attr('width', width).attr('height', height).attr('x', 0).attr('y', 0);
325
+ svg.append('clipPath').attr('id', "clip-" + d3GraphId + "-" + diagramType + "x-axis").attr('clipPathUnits', 'userSpaceOnUse').append('rect')
326
+ // Keep x-axis labels within the horizontal chart band while allowing them
327
+ // to use the reserved left and bottom margin.
328
+ .attr('width', width + margin.left).attr('height', height + margin.bottom).attr('x', -margin.left).attr('y', 0);
329
+ svg.append('clipPath').attr('id', "clip-" + d3GraphId + "-" + diagramType + "y-axis").attr('clipPathUnits', 'userSpaceOnUse').append('rect')
330
+ // Keep y-axis labels within the vertical chart band while allowing them
331
+ // to use the reserved left margin.
332
+ .attr('width', width + margin.left).attr('height', height).attr('x', -margin.left).attr('y', 0);
311
333
  svg.append('rect').attr('height', height).attr('width', width).attr('fill', '#ffffff00').attr('opacity', 1).attr('id', d3GraphId + "-" + diagramType + "d3").attr('clip-path', "url(#clip-" + d3GraphId + "-" + diagramType + "d3)");
312
334
  return svg;
313
335
  };
@@ -436,6 +458,7 @@ var plotSounding = function plotSounding(_ref7) {
436
458
  pressureValues = _ref7.pressureValues,
437
459
  color = _ref7.color,
438
460
  lineStyle = _ref7.lineStyle,
461
+ d3GraphId = _ref7.d3GraphId,
439
462
  xScale = _ref7.xScale,
440
463
  yScale = _ref7.yScale,
441
464
  height = _ref7.height,
@@ -448,7 +471,7 @@ var plotSounding = function plotSounding(_ref7) {
448
471
  var Px = mapToPlot(diagramType, filteredPressureValues, filteredTemperatureValues, xScale, yScale, height);
449
472
  var lineGenerator = d3.line();
450
473
  var pathString = lineGenerator(Px);
451
- svg.append('path').attr('d', pathString).attr('id', 'dataline').attr('x', 0).attr('y', 0).style('fill', 'none').style('stroke-width', 2).style('stroke', color).style('stroke-opacity', 1).style('stroke-dasharray', lineStyle).attr('clip-path', "url(#clip-" + diagramType + "d3)");
474
+ svg.append('path').attr('d', pathString).attr('id', 'dataline').attr('x', 0).attr('y', 0).style('fill', 'none').style('stroke-width', 2).style('stroke', color).style('stroke-opacity', 1).style('stroke-dasharray', lineStyle).attr('clip-path', "url(#clip-" + d3GraphId + "-" + diagramType + "d3)");
452
475
  };
453
476
  // Use this function to filter the pressure and parameter array so no NaN or null values are included
454
477
  var filterDomainRangeForParameter = function filterDomainRangeForParameter(pressureDomain, parameterDomain) {
@@ -461,8 +484,16 @@ var filterDomainRangeForParameter = function filterDomainRangeForParameter(press
461
484
  return [filteredPressureDomain, filteredParameterDomain];
462
485
  };
463
486
 
487
+ var defaultVerticalZoom = {
488
+ scale: 1,
489
+ y: 0,
490
+ sourceId: null
491
+ };
464
492
  var MouseYContext = /*#__PURE__*/React.createContext({
465
- clientY: null
493
+ clientY: null,
494
+ verticalZoom: defaultVerticalZoom,
495
+ publishVerticalZoom: function publishVerticalZoom() {},
496
+ resetVerticalZoom: function resetVerticalZoom() {}
466
497
  });
467
498
  var useSharedMouseY = function useSharedMouseY() {
468
499
  return React.useContext(MouseYContext);
@@ -477,6 +508,31 @@ var SharedMouseYProvider = function SharedMouseYProvider(_ref) {
477
508
  var _React$useState = React.useState(null),
478
509
  clientY = _React$useState[0],
479
510
  setClientY = _React$useState[1];
511
+ var _React$useState2 = React.useState(defaultVerticalZoom),
512
+ verticalZoom = _React$useState2[0],
513
+ setVerticalZoom = _React$useState2[1];
514
+ var publishVerticalZoom = React.useCallback(function (sourceId, scale, y) {
515
+ setVerticalZoom(function (previous) {
516
+ var hasSameValues = previous.sourceId === sourceId && Math.abs(previous.scale - scale) < 1e-6 && Math.abs(previous.y - y) < 1e-3;
517
+ if (hasSameValues) {
518
+ return previous;
519
+ }
520
+ return {
521
+ scale: scale,
522
+ y: y,
523
+ sourceId: sourceId
524
+ };
525
+ });
526
+ }, []);
527
+ var resetVerticalZoom = React.useCallback(function (sourceId) {
528
+ setVerticalZoom(function () {
529
+ return {
530
+ scale: 1,
531
+ y: 0,
532
+ sourceId: sourceId
533
+ };
534
+ });
535
+ }, []);
480
536
  React.useEffect(function () {
481
537
  var el = wrapperRef.current;
482
538
  if (!el) {
@@ -500,9 +556,12 @@ var SharedMouseYProvider = function SharedMouseYProvider(_ref) {
500
556
  }, []);
501
557
  var contextValue = React.useMemo(function () {
502
558
  return {
503
- clientY: clientY
559
+ clientY: clientY,
560
+ verticalZoom: verticalZoom,
561
+ publishVerticalZoom: publishVerticalZoom,
562
+ resetVerticalZoom: resetVerticalZoom
504
563
  };
505
- }, [clientY]);
564
+ }, [clientY, verticalZoom, publishVerticalZoom, resetVerticalZoom]);
506
565
  return jsx("div", {
507
566
  "data-testid": "shared-mouse-y-wrapper",
508
567
  ref: wrapperRef,
@@ -717,13 +776,13 @@ var SoundingsLegend = function SoundingsLegend(_ref) {
717
776
  return jsx(Stack, {
718
777
  direction: "row",
719
778
  spacing: 1,
720
- useFlexGap: true,
721
779
  sx: {
722
780
  justifyContent: 'center',
723
781
  flexWrap: 'wrap',
724
782
  paddingLeft: 2,
725
783
  paddingTop: 0.5
726
784
  },
785
+ useFlexGap: true,
727
786
  children: sourceSelections.map(function (source, index) {
728
787
  var resolvedInstanceId = sourceResolvedInstanceIds == null ? void 0 : sourceResolvedInstanceIds[source.collectionId];
729
788
  var title = getLegendTitle(source, resolvedInstanceId);
@@ -985,77 +1044,92 @@ var getCollectionLocations = /*#__PURE__*/function () {
985
1044
  return _ref.apply(this, arguments);
986
1045
  };
987
1046
  }();
988
- var getCollectionInstances = /*#__PURE__*/function () {
989
- var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(collectionId, baseUrl) {
1047
+ var getInstanceLocations = /*#__PURE__*/function () {
1048
+ var _ref2 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee2(collectionId, instanceId, baseUrl) {
990
1049
  return _regenerator().w(function (_context2) {
991
1050
  while (1) switch (_context2.n) {
992
1051
  case 0:
993
- return _context2.a(2, fetch(withJsonFormat(baseUrl + "/collections/" + collectionId + "/instances"), {
1052
+ return _context2.a(2, fetch(withJsonFormat(baseUrl + "/collections/" + collectionId + "/instances/" + instanceId + "/locations"), {
994
1053
  headers: getHeaders()
995
1054
  }).then(handleResponse));
996
1055
  }
997
1056
  }, _callee2);
998
1057
  }));
999
- return function getCollectionInstances(_x3, _x4) {
1058
+ return function getInstanceLocations(_x3, _x4, _x5) {
1000
1059
  return _ref2.apply(this, arguments);
1001
1060
  };
1002
1061
  }();
1003
- var getCollectionDetails = /*#__PURE__*/function () {
1062
+ var getCollectionInstances = /*#__PURE__*/function () {
1004
1063
  var _ref3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3(collectionId, baseUrl) {
1005
1064
  return _regenerator().w(function (_context3) {
1006
1065
  while (1) switch (_context3.n) {
1007
1066
  case 0:
1008
- return _context3.a(2, fetch(withJsonFormat(baseUrl + "/collections/" + collectionId), {
1067
+ return _context3.a(2, fetch(withJsonFormat(baseUrl + "/collections/" + collectionId + "/instances"), {
1009
1068
  headers: getHeaders()
1010
1069
  }).then(handleResponse));
1011
1070
  }
1012
1071
  }, _callee3);
1013
1072
  }));
1014
- return function getCollectionDetails(_x5, _x6) {
1073
+ return function getCollectionInstances(_x6, _x7) {
1015
1074
  return _ref3.apply(this, arguments);
1016
1075
  };
1017
1076
  }();
1018
- var getInstanceDetails = /*#__PURE__*/function () {
1019
- var _ref4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(collectionId, instanceId, baseUrl) {
1077
+ var getCollectionDetails = /*#__PURE__*/function () {
1078
+ var _ref4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4(collectionId, baseUrl) {
1020
1079
  return _regenerator().w(function (_context4) {
1021
1080
  while (1) switch (_context4.n) {
1022
1081
  case 0:
1023
- return _context4.a(2, fetch(withJsonFormat(baseUrl + "/collections/" + collectionId + "/instances/" + instanceId), {
1082
+ return _context4.a(2, fetch(withJsonFormat(baseUrl + "/collections/" + collectionId), {
1024
1083
  headers: getHeaders()
1025
1084
  }).then(handleResponse));
1026
1085
  }
1027
1086
  }, _callee4);
1028
1087
  }));
1029
- return function getInstanceDetails(_x7, _x8, _x9) {
1088
+ return function getCollectionDetails(_x8, _x9) {
1030
1089
  return _ref4.apply(this, arguments);
1031
1090
  };
1032
1091
  }();
1033
- var getEdrCollectionForLocation = /*#__PURE__*/function () {
1034
- var _ref5 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(collectionId, baseUrl, instanceId, locationId, parameters, datetime, verticalRange) {
1035
- var url;
1092
+ var getInstanceDetails = /*#__PURE__*/function () {
1093
+ var _ref5 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5(collectionId, instanceId, baseUrl) {
1036
1094
  return _regenerator().w(function (_context5) {
1037
1095
  while (1) switch (_context5.n) {
1096
+ case 0:
1097
+ return _context5.a(2, fetch(withJsonFormat(baseUrl + "/collections/" + collectionId + "/instances/" + instanceId), {
1098
+ headers: getHeaders()
1099
+ }).then(handleResponse));
1100
+ }
1101
+ }, _callee5);
1102
+ }));
1103
+ return function getInstanceDetails(_x0, _x1, _x10) {
1104
+ return _ref5.apply(this, arguments);
1105
+ };
1106
+ }();
1107
+ var getEdrCollectionForLocation = /*#__PURE__*/function () {
1108
+ var _ref6 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(collectionId, baseUrl, instanceId, locationId, parameters, datetime, verticalRange) {
1109
+ var url;
1110
+ return _regenerator().w(function (_context6) {
1111
+ while (1) switch (_context6.n) {
1038
1112
  case 0:
1039
1113
  url = new URL(baseUrl + "/collections/" + collectionId + (instanceId ? "/instances/" + instanceId : '') + "/locations/" + locationId);
1040
1114
  url.searchParams.append('parameter-name', parameters.join(','));
1041
1115
  url.searchParams.append('datetime', datetime);
1042
1116
  url.searchParams.append('z', verticalRange);
1043
1117
  url.searchParams.append('f', 'CoverageJSON');
1044
- return _context5.a(2, fetch(url.toString(), {
1118
+ return _context6.a(2, fetch(url.toString(), {
1045
1119
  headers: getHeaders()
1046
1120
  }).then(handleResponse));
1047
1121
  }
1048
- }, _callee5);
1122
+ }, _callee6);
1049
1123
  }));
1050
- return function getEdrCollectionForLocation(_x0, _x1, _x10, _x11, _x12, _x13, _x14) {
1051
- return _ref5.apply(this, arguments);
1124
+ return function getEdrCollectionForLocation(_x11, _x12, _x13, _x14, _x15, _x16, _x17) {
1125
+ return _ref6.apply(this, arguments);
1052
1126
  };
1053
1127
  }();
1054
1128
  var getEdrCollectionForPosition = /*#__PURE__*/function () {
1055
- var _ref6 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6(collectionId, baseUrl, instanceId, coordinates, parameters, datetime, verticalRange) {
1129
+ var _ref7 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(collectionId, baseUrl, instanceId, coordinates, parameters, datetime, verticalRange) {
1056
1130
  var url;
1057
- return _regenerator().w(function (_context6) {
1058
- while (1) switch (_context6.n) {
1131
+ return _regenerator().w(function (_context7) {
1132
+ while (1) switch (_context7.n) {
1059
1133
  case 0:
1060
1134
  url = new URL(baseUrl + "/collections/" + collectionId + (instanceId ? "/instances/" + instanceId : '') + "/position");
1061
1135
  url.searchParams.append('coords', "POINT(" + coordinates[0] + " " + coordinates[1] + ")");
@@ -1065,14 +1139,14 @@ var getEdrCollectionForPosition = /*#__PURE__*/function () {
1065
1139
  url.searchParams.append('z', verticalRange);
1066
1140
  }
1067
1141
  url.searchParams.append('f', 'CoverageJSON');
1068
- return _context6.a(2, fetch(url.toString(), {
1142
+ return _context7.a(2, fetch(url.toString(), {
1069
1143
  headers: getHeaders()
1070
1144
  }).then(handleResponse));
1071
1145
  }
1072
- }, _callee6);
1146
+ }, _callee7);
1073
1147
  }));
1074
- return function getEdrCollectionForPosition(_x15, _x16, _x17, _x18, _x19, _x20, _x21) {
1075
- return _ref6.apply(this, arguments);
1148
+ return function getEdrCollectionForPosition(_x18, _x19, _x20, _x21, _x22, _x23, _x24) {
1149
+ return _ref7.apply(this, arguments);
1076
1150
  };
1077
1151
  }();
1078
1152
  /**
@@ -1081,10 +1155,10 @@ var getEdrCollectionForPosition = /*#__PURE__*/function () {
1081
1155
  * Returns a CoverageCollection with coverages for each grid point within the radius.
1082
1156
  */
1083
1157
  var getEdrCollectionForRadius = /*#__PURE__*/function () {
1084
- var _ref7 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7(collectionId, baseUrl, instanceId, centerCoordinates, radiusKm, parameters, datetime, verticalRange) {
1158
+ var _ref8 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8(collectionId, baseUrl, instanceId, centerCoordinates, radiusKm, parameters, datetime, verticalRange) {
1085
1159
  var url;
1086
- return _regenerator().w(function (_context7) {
1087
- while (1) switch (_context7.n) {
1160
+ return _regenerator().w(function (_context8) {
1161
+ while (1) switch (_context8.n) {
1088
1162
  case 0:
1089
1163
  url = new URL(baseUrl + "/collections/" + collectionId + (instanceId ? "/instances/" + instanceId : '') + "/radius");
1090
1164
  url.searchParams.append('coords', "POINT(" + centerCoordinates[0] + " " + centerCoordinates[1] + ")");
@@ -1097,17 +1171,40 @@ var getEdrCollectionForRadius = /*#__PURE__*/function () {
1097
1171
  url.searchParams.append('z', verticalRange);
1098
1172
  }
1099
1173
  url.searchParams.append('f', 'CoverageJSON');
1100
- return _context7.a(2, fetch(url.toString(), {
1174
+ return _context8.a(2, fetch(url.toString(), {
1101
1175
  headers: getHeaders()
1102
1176
  }).then(handleResponse));
1103
1177
  }
1104
- }, _callee7);
1178
+ }, _callee8);
1105
1179
  }));
1106
- return function getEdrCollectionForRadius(_x22, _x23, _x24, _x25, _x26, _x27, _x28, _x29) {
1107
- return _ref7.apply(this, arguments);
1180
+ return function getEdrCollectionForRadius(_x25, _x26, _x27, _x28, _x29, _x30, _x31, _x32) {
1181
+ return _ref8.apply(this, arguments);
1108
1182
  };
1109
1183
  }();
1110
1184
 
1185
+ var soundingsQueryOptions = {
1186
+ getCollectionInstances: function getCollectionInstances$1(collectionId, url) {
1187
+ return queryOptions({
1188
+ queryKey: ['edr-soundings-instances', collectionId, url],
1189
+ queryFn: function () {
1190
+ var _queryFn = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {
1191
+ return _regenerator().w(function (_context) {
1192
+ while (1) switch (_context.n) {
1193
+ case 0:
1194
+ return _context.a(2, getCollectionInstances(collectionId, url));
1195
+ }
1196
+ }, _callee);
1197
+ }));
1198
+ function queryFn() {
1199
+ return _queryFn.apply(this, arguments);
1200
+ }
1201
+ return queryFn;
1202
+ }(),
1203
+ refetchInterval: 5 * 60 * 1000
1204
+ });
1205
+ }
1206
+ };
1207
+
1111
1208
  var epsilon = 0.622; // Ratio of molecular weights of water vapour and dry air
1112
1209
  var e0 = 611.2; // Reference vapor pressure [Pa]
1113
1210
  var aConstant = 17.67;
@@ -1625,28 +1722,12 @@ var useResolvedSourceSelections = function useResolvedSourceSelections(sources)
1625
1722
  var collectionId = _ref2.collectionId,
1626
1723
  url = _ref2.url,
1627
1724
  instanceId = _ref2.instanceId;
1628
- return {
1629
- queryKey: ['edr-soundings-instances', collectionId],
1630
- queryFn: function () {
1631
- var _queryFn3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
1632
- return _regenerator().w(function (_context3) {
1633
- while (1) switch (_context3.n) {
1634
- case 0:
1635
- return _context3.a(2, getCollectionInstances(collectionId, url));
1636
- }
1637
- }, _callee3);
1638
- }));
1639
- function queryFn() {
1640
- return _queryFn3.apply(this, arguments);
1641
- }
1642
- return queryFn;
1643
- }(),
1725
+ return Object.assign({}, soundingsQueryOptions.getCollectionInstances(collectionId, url), {
1644
1726
  enabled: referenceTimeUtils.isRelativeReferenceTime(instanceId),
1645
- refetchInterval: 5 * 60 * 1000,
1646
1727
  select: function select(data) {
1647
1728
  return data.instances;
1648
1729
  }
1649
- };
1730
+ });
1650
1731
  });
1651
1732
  }, [sources]);
1652
1733
  var instancesBySource = useQueries({
@@ -1678,6 +1759,7 @@ var useResolvedSourceSelections = function useResolvedSourceSelections(sources)
1678
1759
  }, [sources, instancesBySource]);
1679
1760
  };
1680
1761
  var useTimesteps = function useTimesteps(sourceSelections) {
1762
+ var queryClient = useQueryClient();
1681
1763
  var queries = React.useMemo(function () {
1682
1764
  return sourceSelections.map(function (_ref3) {
1683
1765
  var collectionId = _ref3.collectionId,
@@ -1686,42 +1768,38 @@ var useTimesteps = function useTimesteps(sourceSelections) {
1686
1768
  return {
1687
1769
  queryKey: ['edr-soundings-instance', collectionId, instanceId || ''],
1688
1770
  queryFn: function () {
1689
- var _queryFn4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
1690
- var _instanceData$extent, instanceData, timeExtentValues, allInstancesData, collectionData;
1691
- return _regenerator().w(function (_context4) {
1692
- while (1) switch (_context4.n) {
1771
+ var _queryFn3 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee3() {
1772
+ var _instanceData$extent, instanceData, timeExtentValues, collectionData;
1773
+ return _regenerator().w(function (_context3) {
1774
+ while (1) switch (_context3.n) {
1693
1775
  case 0:
1694
1776
  if (!(instanceId && instanceId !== '')) {
1695
- _context4.n = 4;
1777
+ _context3.n = 3;
1696
1778
  break;
1697
1779
  }
1698
- _context4.n = 1;
1780
+ _context3.n = 1;
1699
1781
  return getInstanceDetails(collectionId, instanceId, url);
1700
1782
  case 1:
1701
- instanceData = _context4.v;
1783
+ instanceData = _context3.v;
1702
1784
  timeExtentValues = instanceData == null || (_instanceData$extent = instanceData.extent) == null || (_instanceData$extent = _instanceData$extent.temporal) == null ? void 0 : _instanceData$extent.values;
1703
1785
  if (timeExtentValues) {
1704
- _context4.n = 3;
1786
+ _context3.n = 2;
1705
1787
  break;
1706
1788
  }
1707
- _context4.n = 2;
1708
- return getCollectionInstances(collectionId, url);
1789
+ return _context3.a(2, queryClient.fetchQuery(soundingsQueryOptions.getCollectionInstances(collectionId, url)));
1709
1790
  case 2:
1710
- allInstancesData = _context4.v;
1711
- return _context4.a(2, allInstancesData);
1791
+ return _context3.a(2, instanceData);
1712
1792
  case 3:
1713
- return _context4.a(2, instanceData);
1714
- case 4:
1715
- _context4.n = 5;
1793
+ _context3.n = 4;
1716
1794
  return getCollectionDetails(collectionId, url);
1717
- case 5:
1718
- collectionData = _context4.v;
1719
- return _context4.a(2, collectionData);
1795
+ case 4:
1796
+ collectionData = _context3.v;
1797
+ return _context3.a(2, collectionData);
1720
1798
  }
1721
- }, _callee4);
1799
+ }, _callee3);
1722
1800
  }));
1723
1801
  function queryFn() {
1724
- return _queryFn4.apply(this, arguments);
1802
+ return _queryFn3.apply(this, arguments);
1725
1803
  }
1726
1804
  return queryFn;
1727
1805
  }(),
@@ -1738,7 +1816,7 @@ var useTimesteps = function useTimesteps(sourceSelections) {
1738
1816
  refetchInterval: 5 * 60 * 1000
1739
1817
  };
1740
1818
  });
1741
- }, [sourceSelections]);
1819
+ }, [sourceSelections, queryClient]);
1742
1820
  var results = useQueries({
1743
1821
  queries: queries,
1744
1822
  combine: function combine(queryResults) {
@@ -1782,21 +1860,21 @@ var useGetCollectionsInstances = function useGetCollectionsInstances(collections
1782
1860
  return {
1783
1861
  queryKey: ['edr-soundings-collection-details', id, url],
1784
1862
  queryFn: function () {
1785
- var _queryFn5 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
1863
+ var _queryFn4 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee4() {
1786
1864
  var data;
1787
- return _regenerator().w(function (_context5) {
1788
- while (1) switch (_context5.n) {
1865
+ return _regenerator().w(function (_context4) {
1866
+ while (1) switch (_context4.n) {
1789
1867
  case 0:
1790
- _context5.n = 1;
1868
+ _context4.n = 1;
1791
1869
  return getCollectionDetails(id, url);
1792
1870
  case 1:
1793
- data = _context5.v;
1794
- return _context5.a(2, data);
1871
+ data = _context4.v;
1872
+ return _context4.a(2, data);
1795
1873
  }
1796
- }, _callee5);
1874
+ }, _callee4);
1797
1875
  }));
1798
1876
  function queryFn() {
1799
- return _queryFn5.apply(this, arguments);
1877
+ return _queryFn4.apply(this, arguments);
1800
1878
  }
1801
1879
  return queryFn;
1802
1880
  }(),
@@ -1810,10 +1888,13 @@ var useGetCollectionsInstances = function useGetCollectionsInstances(collections
1810
1888
  var _queryResults$find3;
1811
1889
  var data = queryResults.map(function (result) {
1812
1890
  var _result$data2;
1813
- return ((_result$data2 = result.data) == null || (_result$data2 = _result$data2.data_queries) == null ? void 0 : _result$data2.instances) !== undefined;
1891
+ return !!((_result$data2 = result.data) != null && (_result$data2 = _result$data2.data_queries) != null && _result$data2.instances);
1814
1892
  });
1815
- var isPending = queryResults.some(function (result) {
1816
- return result.isPending;
1893
+ // Only enabled queries should contribute to isPending to prevent a
1894
+ // loading indicator that never clears
1895
+ var isPending = queryResults.some(function (result, index) {
1896
+ var _queriesCollection$in;
1897
+ return !!((_queriesCollection$in = queriesCollection[index]) != null && _queriesCollection$in.enabled) && result.isPending;
1817
1898
  });
1818
1899
  var error = (_queryResults$find3 = queryResults.find(function (result) {
1819
1900
  return result == null ? void 0 : result.error;
@@ -1830,52 +1911,28 @@ var useGetCollectionsInstances = function useGetCollectionsInstances(collections
1830
1911
  return collectionIds.map(function (_ref5, index) {
1831
1912
  var id = _ref5.id,
1832
1913
  url = _ref5.url;
1833
- return {
1834
- queryKey: ['edr-soundings-instances', id, resultsCollection.data],
1835
- queryFn: function () {
1836
- var _queryFn6 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
1837
- var data;
1838
- return _regenerator().w(function (_context6) {
1839
- while (1) switch (_context6.n) {
1840
- case 0:
1841
- if (!resultsCollection.data[index]) {
1842
- _context6.n = 2;
1843
- break;
1844
- }
1845
- _context6.n = 1;
1846
- return getCollectionInstances(id, url);
1847
- case 1:
1848
- data = _context6.v;
1849
- return _context6.a(2, data);
1850
- case 2:
1851
- return _context6.a(2, {
1852
- instances: []
1853
- });
1854
- }
1855
- }, _callee6);
1856
- }));
1857
- function queryFn() {
1858
- return _queryFn6.apply(this, arguments);
1859
- }
1860
- return queryFn;
1861
- }(),
1862
- enabled: isDialogOpen && resultsCollection.data !== undefined
1863
- };
1914
+ var hasInstances = !!resultsCollection.data[index];
1915
+ return Object.assign({}, soundingsQueryOptions.getCollectionInstances(id, url), {
1916
+ enabled: !!isDialogOpen && hasInstances
1917
+ });
1864
1918
  });
1865
- }, [collectionIds, isDialogOpen, resultsCollection]);
1919
+ }, [collectionIds, isDialogOpen, resultsCollection.data]);
1866
1920
  var results = useQueries({
1867
1921
  queries: queries,
1868
1922
  combine: function combine(queryResults) {
1869
1923
  var _queryResults$find4;
1870
1924
  var data = queryResults.map(function (result) {
1871
- var _result$data3;
1872
- var instances = ((_result$data3 = result.data) == null ? void 0 : _result$data3.instances.sort(function (featureA, featureB) {
1925
+ var _result$data$instance, _result$data3;
1926
+ var instances = [].concat((_result$data$instance = (_result$data3 = result.data) == null ? void 0 : _result$data3.instances) != null ? _result$data$instance : []).sort(function (featureA, featureB) {
1873
1927
  return featureB.id.localeCompare(featureA.id);
1874
- })) || []; // Sort to get latest first
1928
+ }); // Sort to get latest first
1875
1929
  return instances;
1876
1930
  });
1877
- var isPending = queryResults.some(function (result) {
1878
- return result.isPending;
1931
+ // Mirror the collection-details behavior: only enabled instance queries
1932
+ // participate in the loading state.
1933
+ var isPending = queryResults.some(function (result, index) {
1934
+ var _queries$index;
1935
+ return !!((_queries$index = queries[index]) != null && _queries$index.enabled) && result.isPending;
1879
1936
  });
1880
1937
  var error = (_queryResults$find4 = queryResults.find(function (result) {
1881
1938
  return result == null ? void 0 : result.error;
@@ -1900,37 +1957,76 @@ var useGetCollectionsInstances = function useGetCollectionsInstances(collections
1900
1957
  data: dataWithCollectionInfo
1901
1958
  });
1902
1959
  };
1903
- var useGetCollectionLocations = function useGetCollectionLocations(collections, enabled) {
1960
+ var useGetLocations = function useGetLocations(collections, enabled) {
1961
+ var queryClient = useQueryClient();
1904
1962
  var edrQueries = useMemo(function () {
1905
1963
  return collections == null ? void 0 : collections.map(function (collection) {
1964
+ var _collection$instanceI;
1906
1965
  return {
1907
- queryKey: ['edr-locations', collection.id],
1966
+ queryKey: ['edr-locations', collection.id, collection.url, (_collection$instanceI = collection.instanceId) != null ? _collection$instanceI : ''],
1908
1967
  queryFn: function () {
1909
- var _queryFn7 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
1910
- var locations;
1911
- return _regenerator().w(function (_context7) {
1912
- while (1) switch (_context7.n) {
1968
+ var _queryFn5 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee5() {
1969
+ var resolvedInstanceId, instances, resolved, locationsExplicitlyUnsupported, _locations, locations;
1970
+ return _regenerator().w(function (_context5) {
1971
+ while (1) switch (_context5.p = _context5.n) {
1913
1972
  case 0:
1914
- _context7.n = 1;
1973
+ resolvedInstanceId = collection.instanceId;
1974
+ if (!(resolvedInstanceId && referenceTimeUtils.isRelativeReferenceTime(resolvedInstanceId))) {
1975
+ _context5.n = 4;
1976
+ break;
1977
+ }
1978
+ _context5.p = 1;
1979
+ _context5.n = 2;
1980
+ return queryClient.fetchQuery(soundingsQueryOptions.getCollectionInstances(collection.id, collection.url));
1981
+ case 2:
1982
+ instances = _context5.v;
1983
+ resolved = referenceTimeUtils.resolveReferenceTimeInstance(instances.instances, resolvedInstanceId); // Skip instance-level only when data_queries is present but explicitly excludes locations — if data_queries is absent, try instance-level anyway
1984
+ locationsExplicitlyUnsupported = (resolved == null ? void 0 : resolved.data_queries) !== undefined && !resolved.data_queries.locations;
1985
+ resolvedInstanceId = locationsExplicitlyUnsupported ? undefined : resolved == null ? void 0 : resolved.id;
1986
+ _context5.n = 4;
1987
+ break;
1988
+ case 3:
1989
+ _context5.p = 3;
1990
+ _context5.v;
1991
+ resolvedInstanceId = undefined;
1992
+ case 4:
1993
+ if (!resolvedInstanceId) {
1994
+ _context5.n = 8;
1995
+ break;
1996
+ }
1997
+ _context5.p = 5;
1998
+ _context5.n = 6;
1999
+ return getInstanceLocations(collection.id, resolvedInstanceId, collection.url);
2000
+ case 6:
2001
+ _locations = _context5.v;
2002
+ return _context5.a(2, Object.assign({}, _locations, {
2003
+ id: collection.id,
2004
+ name: collection.name
2005
+ }));
2006
+ case 7:
2007
+ _context5.p = 7;
2008
+ _context5.v;
2009
+ case 8:
2010
+ _context5.n = 9;
1915
2011
  return getCollectionLocations(collection.id, collection.url);
1916
- case 1:
1917
- locations = _context7.v;
1918
- return _context7.a(2, Object.assign({}, locations, {
2012
+ case 9:
2013
+ locations = _context5.v;
2014
+ return _context5.a(2, Object.assign({}, locations, {
1919
2015
  id: collection.id,
1920
2016
  name: collection.name
1921
2017
  }));
1922
2018
  }
1923
- }, _callee7);
2019
+ }, _callee5, null, [[5, 7], [1, 3]]);
1924
2020
  }));
1925
2021
  function queryFn() {
1926
- return _queryFn7.apply(this, arguments);
2022
+ return _queryFn5.apply(this, arguments);
1927
2023
  }
1928
2024
  return queryFn;
1929
2025
  }(),
1930
2026
  enabled: enabled
1931
2027
  };
1932
2028
  });
1933
- }, [collections, enabled]);
2029
+ }, [collections, enabled, queryClient]);
1934
2030
  return useQueries({
1935
2031
  queries: edrQueries,
1936
2032
  combine: function combine(results) {
@@ -1964,6 +2060,7 @@ var useGetCollectionLocations = function useGetCollectionLocations(collections,
1964
2060
  var useCheckPositionQuerySupportBySource = function useCheckPositionQuerySupportBySource(sources) {
1965
2061
  // Ref to stabilize data object reference when values haven't changed
1966
2062
  var dataRef = React.useRef({});
2063
+ var queryClient = useQueryClient();
1967
2064
  var queries = useMemo(function () {
1968
2065
  return sources.map(function (_ref6) {
1969
2066
  var collectionId = _ref6.collectionId,
@@ -1972,55 +2069,61 @@ var useCheckPositionQuerySupportBySource = function useCheckPositionQuerySupport
1972
2069
  return {
1973
2070
  queryKey: ['edr-position-support', collectionId, instanceId || ''],
1974
2071
  queryFn: function () {
1975
- var _queryFn8 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
2072
+ var _queryFn6 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee6() {
1976
2073
  var _details$data_queries, _instance$data_querie, instances, instance, details;
1977
- return _regenerator().w(function (_context8) {
1978
- while (1) switch (_context8.p = _context8.n) {
2074
+ return _regenerator().w(function (_context6) {
2075
+ while (1) switch (_context6.p = _context6.n) {
1979
2076
  case 0:
1980
- _context8.p = 0;
2077
+ _context6.p = 0;
1981
2078
  if (!instanceId) {
1982
- _context8.n = 2;
2079
+ _context6.n = 2;
1983
2080
  break;
1984
2081
  }
1985
- _context8.n = 1;
1986
- return getCollectionInstances(collectionId, url);
2082
+ _context6.n = 1;
2083
+ return queryClient.fetchQuery(soundingsQueryOptions.getCollectionInstances(collectionId, url));
1987
2084
  case 1:
1988
- instances = _context8.v;
2085
+ instances = _context6.v;
1989
2086
  instance = instances.instances.find(function (i) {
1990
2087
  return i.id === instanceId;
1991
2088
  });
1992
- return _context8.a(2, {
2089
+ return _context6.a(2, {
1993
2090
  collectionId: collectionId,
1994
2091
  supported: !!(instance != null && (_instance$data_querie = instance.data_queries) != null && _instance$data_querie.position)
1995
2092
  });
1996
2093
  case 2:
1997
- _context8.n = 3;
2094
+ _context6.n = 3;
1998
2095
  return getCollectionDetails(collectionId, url);
1999
2096
  case 3:
2000
- details = _context8.v;
2001
- return _context8.a(2, {
2097
+ details = _context6.v;
2098
+ return _context6.a(2, {
2002
2099
  collectionId: collectionId,
2003
2100
  supported: !!(details != null && (_details$data_queries = details.data_queries) != null && _details$data_queries.position)
2004
2101
  });
2005
2102
  case 4:
2006
- _context8.p = 4;
2007
- _context8.v;
2008
- return _context8.a(2, {
2103
+ _context6.p = 4;
2104
+ _context6.v;
2105
+ return _context6.a(2, {
2009
2106
  collectionId: collectionId,
2010
2107
  supported: false
2011
2108
  });
2012
2109
  }
2013
- }, _callee8, null, [[0, 4]]);
2110
+ }, _callee6, null, [[0, 4]]);
2014
2111
  }));
2015
2112
  function queryFn() {
2016
- return _queryFn8.apply(this, arguments);
2113
+ return _queryFn6.apply(this, arguments);
2017
2114
  }
2018
2115
  return queryFn;
2019
2116
  }(),
2020
- staleTime: 5 * 60 * 1000 // Cache for 5 minutes
2117
+ enabled: !referenceTimeUtils.isRelativeReferenceTime(instanceId),
2118
+ staleTime: 5 * 60 * 1000,
2119
+ // Cache for 5 minutes
2120
+ // Preserve previous result while a new instance check is in-flight to avoid spurious location updates.
2121
+ placeholderData: function placeholderData(prev) {
2122
+ return prev;
2123
+ }
2021
2124
  };
2022
2125
  });
2023
- }, [sources]);
2126
+ }, [sources, queryClient]);
2024
2127
  return useQueries({
2025
2128
  queries: queries,
2026
2129
  combine: function combine(queryResults) {
@@ -2054,6 +2157,7 @@ var useCheckPositionQuerySupportBySource = function useCheckPositionQuerySupport
2054
2157
  var useCheckRadiusQuerySupportBySource = function useCheckRadiusQuerySupportBySource(sources) {
2055
2158
  // Ref to stabilize data object reference when values haven't changed
2056
2159
  var dataRef = React.useRef({});
2160
+ var queryClient = useQueryClient();
2057
2161
  var queries = useMemo(function () {
2058
2162
  return sources.map(function (_ref7) {
2059
2163
  var collectionId = _ref7.collectionId,
@@ -2062,55 +2166,60 @@ var useCheckRadiusQuerySupportBySource = function useCheckRadiusQuerySupportBySo
2062
2166
  return {
2063
2167
  queryKey: ['edr-radius-support', collectionId, instanceId || ''],
2064
2168
  queryFn: function () {
2065
- var _queryFn9 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
2169
+ var _queryFn7 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee7() {
2066
2170
  var _details$data_queries2, _instance$data_querie2, instances, instance, details;
2067
- return _regenerator().w(function (_context9) {
2068
- while (1) switch (_context9.p = _context9.n) {
2171
+ return _regenerator().w(function (_context7) {
2172
+ while (1) switch (_context7.p = _context7.n) {
2069
2173
  case 0:
2070
- _context9.p = 0;
2174
+ _context7.p = 0;
2071
2175
  if (!instanceId) {
2072
- _context9.n = 2;
2176
+ _context7.n = 2;
2073
2177
  break;
2074
2178
  }
2075
- _context9.n = 1;
2076
- return getCollectionInstances(collectionId, url);
2179
+ _context7.n = 1;
2180
+ return queryClient.fetchQuery(soundingsQueryOptions.getCollectionInstances(collectionId, url));
2077
2181
  case 1:
2078
- instances = _context9.v;
2182
+ instances = _context7.v;
2079
2183
  instance = instances.instances.find(function (i) {
2080
2184
  return i.id === instanceId;
2081
2185
  });
2082
- return _context9.a(2, {
2186
+ return _context7.a(2, {
2083
2187
  collectionId: collectionId,
2084
2188
  supported: !!(instance != null && (_instance$data_querie2 = instance.data_queries) != null && _instance$data_querie2.radius)
2085
2189
  });
2086
2190
  case 2:
2087
- _context9.n = 3;
2191
+ _context7.n = 3;
2088
2192
  return getCollectionDetails(collectionId, url);
2089
2193
  case 3:
2090
- details = _context9.v;
2091
- return _context9.a(2, {
2194
+ details = _context7.v;
2195
+ return _context7.a(2, {
2092
2196
  collectionId: collectionId,
2093
2197
  supported: !!(details != null && (_details$data_queries2 = details.data_queries) != null && _details$data_queries2.radius)
2094
2198
  });
2095
2199
  case 4:
2096
- _context9.p = 4;
2097
- _context9.v;
2098
- return _context9.a(2, {
2200
+ _context7.p = 4;
2201
+ _context7.v;
2202
+ return _context7.a(2, {
2099
2203
  collectionId: collectionId,
2100
2204
  supported: false
2101
2205
  });
2102
2206
  }
2103
- }, _callee9, null, [[0, 4]]);
2207
+ }, _callee7, null, [[0, 4]]);
2104
2208
  }));
2105
2209
  function queryFn() {
2106
- return _queryFn9.apply(this, arguments);
2210
+ return _queryFn7.apply(this, arguments);
2107
2211
  }
2108
2212
  return queryFn;
2109
2213
  }(),
2110
- staleTime: 5 * 60 * 1000 // Cache for 5 minutes
2214
+ enabled: !referenceTimeUtils.isRelativeReferenceTime(instanceId),
2215
+ staleTime: 5 * 60 * 1000,
2216
+ // Cache for 5 minutes
2217
+ placeholderData: function placeholderData(prev) {
2218
+ return prev;
2219
+ }
2111
2220
  };
2112
2221
  });
2113
- }, [sources]);
2222
+ }, [sources, queryClient]);
2114
2223
  return useQueries({
2115
2224
  queries: queries,
2116
2225
  combine: function combine(queryResults) {
@@ -2160,20 +2269,20 @@ var useGridPointsFromRadius = function useGridPointsFromRadius(sources, radiusSu
2160
2269
  return {
2161
2270
  queryKey: ['edr-grid-points-disabled', collectionId],
2162
2271
  queryFn: function () {
2163
- var _queryFn0 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee0() {
2164
- return _regenerator().w(function (_context0) {
2165
- while (1) switch (_context0.n) {
2272
+ var _queryFn8 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee8() {
2273
+ return _regenerator().w(function (_context8) {
2274
+ while (1) switch (_context8.n) {
2166
2275
  case 0:
2167
- return _context0.a(2, {
2276
+ return _context8.a(2, {
2168
2277
  sourceId: collectionId,
2169
2278
  gridPoint: undefined,
2170
2279
  allGridPoints: []
2171
2280
  });
2172
2281
  }
2173
- }, _callee0);
2282
+ }, _callee8);
2174
2283
  }));
2175
2284
  function queryFn() {
2176
- return _queryFn0.apply(this, arguments);
2285
+ return _queryFn8.apply(this, arguments);
2177
2286
  }
2178
2287
  return queryFn;
2179
2288
  }(),
@@ -2198,22 +2307,22 @@ var useGridPointsFromRadius = function useGridPointsFromRadius(sources, radiusSu
2198
2307
  return {
2199
2308
  queryKey: ['edr-grid-points-radius', collectionId, instanceId || '', centerCoordinates.join(','), radiusKm.toString(), datetime],
2200
2309
  queryFn: function () {
2201
- var _queryFn1 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee1() {
2310
+ var _queryFn9 = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee9() {
2202
2311
  var minimalParameters, minimalVerticalRange, rawData, allGridPoints, nearestPoint, minSquaredDistance;
2203
- return _regenerator().w(function (_context1) {
2204
- while (1) switch (_context1.p = _context1.n) {
2312
+ return _regenerator().w(function (_context9) {
2313
+ while (1) switch (_context9.p = _context9.n) {
2205
2314
  case 0:
2206
2315
  if (supportsRadius) {
2207
- _context1.n = 1;
2316
+ _context9.n = 1;
2208
2317
  break;
2209
2318
  }
2210
- return _context1.a(2, {
2319
+ return _context9.a(2, {
2211
2320
  sourceId: collectionId,
2212
2321
  gridPoint: undefined,
2213
2322
  allGridPoints: []
2214
2323
  });
2215
2324
  case 1:
2216
- _context1.p = 1;
2325
+ _context9.p = 1;
2217
2326
  // Optimization: Only request a single parameter to minimize data transfer
2218
2327
  // We only need the grid point coordinates, not the actual sounding data
2219
2328
  minimalParameters = parameters.slice(0, 1); // Just one parameter
@@ -2221,10 +2330,10 @@ var useGridPointsFromRadius = function useGridPointsFromRadius(sources, radiusSu
2221
2330
  // (Don't hardcode '1000' as that only works for pressure-level sources,
2222
2331
  // not for hybrid/model-level sources which use levels 1-65)
2223
2332
  minimalVerticalRange = '';
2224
- _context1.n = 2;
2333
+ _context9.n = 2;
2225
2334
  return getEdrCollectionForRadius(collectionId, url, instanceId, centerCoordinates, radiusKm, minimalParameters, datetime, minimalVerticalRange);
2226
2335
  case 2:
2227
- rawData = _context1.v;
2336
+ rawData = _context9.v;
2228
2337
  allGridPoints = extractGridPointsFromCoverage(rawData); // Find the nearest grid point to the center coordinates
2229
2338
  minSquaredDistance = Infinity;
2230
2339
  allGridPoints.forEach(function (point) {
@@ -2239,24 +2348,24 @@ var useGridPointsFromRadius = function useGridPointsFromRadius(sources, radiusSu
2239
2348
  nearestPoint = point;
2240
2349
  }
2241
2350
  });
2242
- return _context1.a(2, {
2351
+ return _context9.a(2, {
2243
2352
  sourceId: collectionId,
2244
2353
  gridPoint: nearestPoint,
2245
2354
  allGridPoints: allGridPoints
2246
2355
  });
2247
2356
  case 3:
2248
- _context1.p = 3;
2249
- _context1.v;
2250
- return _context1.a(2, {
2357
+ _context9.p = 3;
2358
+ _context9.v;
2359
+ return _context9.a(2, {
2251
2360
  sourceId: collectionId,
2252
2361
  gridPoint: undefined,
2253
2362
  allGridPoints: []
2254
2363
  });
2255
2364
  }
2256
- }, _callee1, null, [[1, 3]]);
2365
+ }, _callee9, null, [[1, 3]]);
2257
2366
  }));
2258
2367
  function queryFn() {
2259
- return _queryFn1.apply(this, arguments);
2368
+ return _queryFn9.apply(this, arguments);
2260
2369
  }
2261
2370
  return queryFn;
2262
2371
  }(),
@@ -2564,6 +2673,7 @@ var initialSoundingsViewState = {
2564
2673
  },
2565
2674
  shouldHideLocationMarkers: false,
2566
2675
  isTimeSliderVisible: true,
2676
+ isLegendVisible: true,
2567
2677
  profileDataPreferences: DEFAULT_PROFILE_DATA_PREFERENCES,
2568
2678
  syncGroupsIds: []
2569
2679
  };
@@ -2633,7 +2743,7 @@ var slice = createSlice({
2633
2743
  }
2634
2744
  var preset = payloadAction.payload.preset;
2635
2745
  if (preset) {
2636
- var _preset$selectedSourc, _preset$selectedLocat, _preset$selectedTime, _preset$diagramType, _preset$shouldHideLoc, _preset$isTimeSliderV, _preset$profileDataPr;
2746
+ var _preset$selectedSourc, _preset$selectedLocat, _preset$selectedTime, _preset$diagramType, _preset$shouldHideLoc, _preset$isTimeSliderV, _preset$isLegendVisib, _preset$profileDataPr;
2637
2747
  panel.selectedSources = (_preset$selectedSourc = preset.selectedSources) != null ? _preset$selectedSourc : {};
2638
2748
  panel.selectedLocations = (_preset$selectedLocat = preset.selectedLocations) != null ? _preset$selectedLocat : {};
2639
2749
  panel.selectedTime = (_preset$selectedTime = preset.selectedTime) != null ? _preset$selectedTime : new Date().toISOString();
@@ -2655,6 +2765,7 @@ var slice = createSlice({
2655
2765
  }
2656
2766
  panel.shouldHideLocationMarkers = (_preset$shouldHideLoc = preset.shouldHideLocationMarkers) != null ? _preset$shouldHideLoc : false;
2657
2767
  panel.isTimeSliderVisible = (_preset$isTimeSliderV = preset.isTimeSliderVisible) != null ? _preset$isTimeSliderV : true;
2768
+ panel.isLegendVisible = (_preset$isLegendVisib = preset.isLegendVisible) != null ? _preset$isLegendVisib : true;
2658
2769
  panel.profileDataPreferences = (_preset$profileDataPr = preset.profileDataPreferences) != null ? _preset$profileDataPr : DEFAULT_PROFILE_DATA_PREFERENCES;
2659
2770
  }
2660
2771
  },
@@ -2713,6 +2824,13 @@ var slice = createSlice({
2713
2824
  }
2714
2825
  panel.isTimeSliderVisible = payloadAction.payload.isTimeSliderVisible;
2715
2826
  },
2827
+ toggleLegendVisibility: function toggleLegendVisibility(draft, payloadAction) {
2828
+ var panel = resolveSoundingsView(draft, payloadAction);
2829
+ if (!panel) {
2830
+ return;
2831
+ }
2832
+ panel.isLegendVisible = payloadAction.payload.isLegendVisible;
2833
+ },
2716
2834
  updateProfileDataPreferences: function updateProfileDataPreferences(draft, payloadAction) {
2717
2835
  var panel = resolveSoundingsView(draft, payloadAction);
2718
2836
  if (!panel) {
@@ -2891,6 +3009,10 @@ var getIsTimeSliderVisible = createSelector(getSoundingsViewState, function (sto
2891
3009
  var _store$isTimeSliderVi;
2892
3010
  return (_store$isTimeSliderVi = store.isTimeSliderVisible) != null ? _store$isTimeSliderVi : true;
2893
3011
  }, selectorMemoizationOptions);
3012
+ var getIsLegendVisible = createSelector(getSoundingsViewState, function (store) {
3013
+ var _store$isLegendVisibl;
3014
+ return (_store$isLegendVisibl = store.isLegendVisible) != null ? _store$isLegendVisibl : true;
3015
+ }, selectorMemoizationOptions);
2894
3016
  var getProfileDataPreferences = createSelector(getSoundingsViewState, function (store) {
2895
3017
  var _store$profileDataPre;
2896
3018
  return (_store$profileDataPre = store.profileDataPreferences) != null ? _store$profileDataPre : DEFAULT_PROFILE_DATA_PREFERENCES;
@@ -2945,6 +3067,7 @@ var getSoundingPanelsInOrder = createSelector(getSoundingsViewState, function (s
2945
3067
  var selectors = /*#__PURE__*/Object.freeze({
2946
3068
  __proto__: null,
2947
3069
  getDiagramType: getDiagramType,
3070
+ getIsLegendVisible: getIsLegendVisible,
2948
3071
  getIsTimeSliderVisible: getIsTimeSliderVisible,
2949
3072
  getLocationSelections: getLocationSelections,
2950
3073
  getPanelCustomWidth: getPanelCustomWidth,
@@ -3034,12 +3157,13 @@ var useSoundingsTranslation = function useSoundingsTranslation() {
3034
3157
  };
3035
3158
 
3036
3159
  var SoundingsMarkersSwitch = function SoundingsMarkersSwitch(_ref) {
3160
+ var _sx;
3037
3161
  var shouldHideMarkers = _ref.shouldHideMarkers,
3038
3162
  toggleMarkers = _ref.toggleMarkers;
3039
3163
  var _useSoundingsTranslat = useSoundingsTranslation(),
3040
3164
  t = _useSoundingsTranslat.t;
3041
3165
  return jsxs(Box, {
3042
- sx: {
3166
+ sx: (_sx = {
3043
3167
  display: 'flex',
3044
3168
  flexWrap: 'wrap',
3045
3169
  alignItems: 'flex-start',
@@ -3055,14 +3179,13 @@ var SoundingsMarkersSwitch = function SoundingsMarkersSwitch(_ref) {
3055
3179
  },
3056
3180
  '.markers-label': {
3057
3181
  display: 'none'
3058
- },
3059
- '@container soundings-container (min-width: 560px)': {
3060
- minWidth: '146px',
3061
- '.markers-label, .switch-button label': {
3062
- display: 'initial'
3063
- }
3064
3182
  }
3065
- },
3183
+ }, _sx["@container " + SOUNDINGS_CONTAINER_NAME + " (min-width: " + SOUNDINGS_CONTAINER_BREAKPOINT_PX + ")"] = {
3184
+ minWidth: '146px',
3185
+ '.markers-label, .switch-button label': {
3186
+ display: 'initial'
3187
+ }
3188
+ }, _sx),
3066
3189
  children: [jsx(Typography, {
3067
3190
  className: "markers-label",
3068
3191
  sx: {
@@ -3087,6 +3210,7 @@ var SoundingsMarkersSwitch = function SoundingsMarkersSwitch(_ref) {
3087
3210
  });
3088
3211
  };
3089
3212
 
3213
+ var _styled;
3090
3214
  var _excluded = ["icon", "shortText", "longText"];
3091
3215
  var SoundingsTopRowButtonUnstyled = /*#__PURE__*/React.forwardRef(function (_ref, ref) {
3092
3216
  var icon = _ref.icon,
@@ -3105,30 +3229,27 @@ var SoundingsTopRowButtonUnstyled = /*#__PURE__*/React.forwardRef(function (_ref
3105
3229
  })]
3106
3230
  }));
3107
3231
  });
3108
- var SoundingsTopRowButton = styled(SoundingsTopRowButtonUnstyled)({
3109
- height: '10px !important',
3110
- '@container soundings-container (width < 560px)': {
3111
- maxWidth: '90px',
3112
- '& .longText': {
3113
- display: 'none'
3114
- },
3115
- '& svg': {
3116
- marginRight: '0 !important'
3117
- }
3232
+ var SoundingsTopRowButton = styled(SoundingsTopRowButtonUnstyled)((_styled = {
3233
+ height: '10px !important'
3234
+ }, _styled["@container " + SOUNDINGS_CONTAINER_NAME + " (width < " + SOUNDINGS_CONTAINER_BREAKPOINT_PX + ")"] = {
3235
+ maxWidth: '90px',
3236
+ '& .longText': {
3237
+ display: 'none'
3118
3238
  },
3119
- '@container soundings-container (width >= 560px)': {
3120
- width: '130px',
3121
- '& .shortText': {
3122
- display: 'none'
3123
- }
3124
- },
3125
- '& span': {
3126
- fontSize: '9pt',
3127
- whiteSpace: 'nowrap',
3128
- textOverflow: 'ellipsis',
3129
- overflow: 'hidden'
3239
+ '& svg': {
3240
+ marginRight: '0 !important'
3130
3241
  }
3131
- });
3242
+ }, _styled["@container " + SOUNDINGS_CONTAINER_NAME + " (min-width: " + SOUNDINGS_CONTAINER_BREAKPOINT_PX + ")"] = {
3243
+ width: '130px',
3244
+ '& .shortText': {
3245
+ display: 'none'
3246
+ }
3247
+ }, _styled['& span'] = {
3248
+ fontSize: '9pt',
3249
+ whiteSpace: 'nowrap',
3250
+ textOverflow: 'ellipsis',
3251
+ overflow: 'hidden'
3252
+ }, _styled));
3132
3253
 
3133
3254
  var MenuOptions;
3134
3255
  (function (MenuOptions) {
@@ -3138,20 +3259,26 @@ var MenuOptions;
3138
3259
  MenuOptions["profileData"] = "profileData";
3139
3260
  })(MenuOptions || (MenuOptions = {}));
3140
3261
  var SoundingsMenuBar = function SoundingsMenuBar(_ref) {
3262
+ var _sx, _sx2;
3141
3263
  var handleClick = _ref.handleClick,
3142
3264
  isOpen = _ref.isOpen,
3143
3265
  _ref$selectedSourcesA = _ref.selectedSourcesAmount,
3144
3266
  selectedSourcesAmount = _ref$selectedSourcesA === void 0 ? 0 : _ref$selectedSourcesA,
3145
3267
  shouldHideMarkers = _ref.shouldHideMarkers,
3146
- toggleMarkers = _ref.toggleMarkers;
3268
+ toggleMarkers = _ref.toggleMarkers,
3269
+ isLegendVisible = _ref.isLegendVisible,
3270
+ toggleLegend = _ref.toggleLegend;
3147
3271
  var _useSoundingsTranslat = useSoundingsTranslation(),
3148
3272
  t = _useSoundingsTranslat.t;
3149
3273
  return jsxs(Stack, {
3150
3274
  direction: "row",
3151
3275
  spacing: 1,
3152
- sx: {
3153
- marginBottom: '4px'
3154
- },
3276
+ sx: (_sx = {
3277
+ marginBottom: '4px',
3278
+ overflowX: 'auto'
3279
+ }, _sx["@container " + SOUNDINGS_CONTAINER_NAME + " (min-width: " + SOUNDINGS_CONTAINER_BREAKPOINT_PX + ")"] = {
3280
+ overflowX: 'initial'
3281
+ }, _sx),
3155
3282
  children: [jsx(SoundingsTopRowButton, {
3156
3283
  "data-testid": "soundingsManagerButton",
3157
3284
  icon: jsx(Layers, {}),
@@ -3195,6 +3322,23 @@ var SoundingsMenuBar = function SoundingsMenuBar(_ref) {
3195
3322
  }), jsx(SoundingsMarkersSwitch, {
3196
3323
  shouldHideMarkers: shouldHideMarkers,
3197
3324
  toggleMarkers: toggleMarkers
3325
+ }), jsx(LegendToggleButton, {
3326
+ hideTooltipText: t('soundings-legend-tooltip-hide'),
3327
+ isLegendVisible: isLegendVisible,
3328
+ legendLabel: t('soundings-legend'),
3329
+ onToggle: toggleLegend,
3330
+ showTooltipText: t('soundings-legend-tooltip-show'),
3331
+ sx: (_sx2 = {}, _sx2["@container " + SOUNDINGS_CONTAINER_NAME + " (width < " + SOUNDINGS_CONTAINER_BREAKPOINT_PX + ")"] = {
3332
+ maxWidth: '90px',
3333
+ '& .longText': {
3334
+ display: 'none'
3335
+ },
3336
+ '& svg': {
3337
+ marginRight: '0 !important'
3338
+ }
3339
+ }, _sx2["@container " + SOUNDINGS_CONTAINER_NAME + " (min-width: " + SOUNDINGS_CONTAINER_BREAKPOINT_PX + ")"] = {
3340
+ width: '130px'
3341
+ }, _sx2)
3198
3342
  })]
3199
3343
  });
3200
3344
  };
@@ -3239,6 +3383,16 @@ var MenuBarConnect = function MenuBarConnect(_ref) {
3239
3383
  var shouldHideLocationMarkers = useSelector(function (state) {
3240
3384
  return getShouldHideLocationMarkers(state, viewId);
3241
3385
  });
3386
+ var isLegendVisible = useSelector(function (state) {
3387
+ return getIsLegendVisible(state, viewId);
3388
+ });
3389
+ var toggleLegend = React.useCallback(function () {
3390
+ dispatch(soundingsActions.toggleLegendVisibility({
3391
+ isLegendVisible: !isLegendVisible,
3392
+ viewId: viewId,
3393
+ origin: SoundingsActionOrigin.soundingsComponent
3394
+ }));
3395
+ }, [dispatch, isLegendVisible, viewId]);
3242
3396
  var toggleLocationMarkers = React.useCallback(function (shouldHide) {
3243
3397
  dispatch(soundingsActions.toggleLocationMarkers({
3244
3398
  shouldHideLocationMarkers: shouldHide,
@@ -3273,9 +3427,11 @@ var MenuBarConnect = function MenuBarConnect(_ref) {
3273
3427
  var isOpen = (_isOpen = {}, _isOpen[MenuOptions.sources] = currentActiveMapIdSources === viewId && isSourcesOpenInStore, _isOpen[MenuOptions.locations] = currentActiveMapIdLocations === viewId && isLocationsOpenInStore, _isOpen[MenuOptions.soundingsManager] = currentActiveMapIdsoundingsManager === viewId && isSoundingsManagerOpenInStore, _isOpen[MenuOptions.profileData] = currentActiveMapIdProfileData === viewId && isProfileDataOpenInStore, _isOpen);
3274
3428
  return jsx(SoundingsMenuBar, {
3275
3429
  handleClick: handleClick,
3430
+ isLegendVisible: isLegendVisible,
3276
3431
  isOpen: isOpen,
3277
3432
  selectedSourcesAmount: sourcesAmount,
3278
3433
  shouldHideMarkers: shouldHideLocationMarkers,
3434
+ toggleLegend: toggleLegend,
3279
3435
  toggleMarkers: toggleLocationMarkers
3280
3436
  });
3281
3437
  };
@@ -3891,7 +4047,7 @@ var Hodograph = function Hodograph(_ref) {
3891
4047
  });
3892
4048
  });
3893
4049
  // Allow zooming and panning
3894
- var zoomBehavior = d3.zoom().scaleExtent([1, 7]).translateExtent([[-0.2 * widthHeight, -0.2 * widthHeight], [1.2 * widthHeight, 1.2 * widthHeight]]).on('zoom', function (event) {
4050
+ var zoomBehavior = d3.zoom().scaleExtent([1, 7]).translateExtent([[0, 0], [widthHeight, widthHeight]]).on('zoom', function (event) {
3895
4051
  svg.select('g.hodograph').attr('transform', event.transform);
3896
4052
  });
3897
4053
  svg.call(zoomBehavior);
@@ -3930,13 +4086,13 @@ var getLegendItem = function getLegendItem(color, text) {
3930
4086
  var HodographLegend = function HodographLegend() {
3931
4087
  return jsxs(Stack, {
3932
4088
  direction: "column",
3933
- useFlexGap: true,
3934
4089
  sx: {
3935
4090
  justifyContent: 'center',
3936
4091
  flexWrap: 'wrap',
3937
4092
  paddingLeft: 2,
3938
4093
  paddingTop: 0.5
3939
4094
  },
4095
+ useFlexGap: true,
3940
4096
  children: [getLegendItem(HODOGRAPH_COLORS[0], '0 - 1 km'), getLegendItem(HODOGRAPH_COLORS[1], '1 - 3 km'), getLegendItem(HODOGRAPH_COLORS[2], '3 - 6 km'), getLegendItem(HODOGRAPH_COLORS[3], '6 - 9 km'), getLegendItem(HODOGRAPH_COLORS[4], '9 - 19 km')]
3941
4097
  });
3942
4098
  };
@@ -4245,23 +4401,21 @@ var extractPressureFromY = function extractPressureFromY(diagramType, y, yScale)
4245
4401
  };
4246
4402
  var drawSoundingsTooltip = function drawSoundingsTooltip(_ref) {
4247
4403
  var event = _ref.event,
4248
- yScale = _ref.yScale,
4249
4404
  svgRectangle = _ref.svgRectangle,
4405
+ hoveredPressure = _ref.hoveredPressure,
4250
4406
  pressureRefd3 = _ref.pressureRefd3,
4251
4407
  tempRefd3 = _ref.tempRefd3,
4252
4408
  dewpointRefd3 = _ref.dewpointRefd3,
4253
4409
  windRefd3 = _ref.windRefd3,
4254
4410
  margin = _ref.margin,
4255
4411
  width = _ref.width,
4256
- tooltipData = _ref.tooltipData,
4257
- diagramType = _ref.diagramType;
4412
+ tooltipData = _ref.tooltipData;
4413
+ _ref.diagramType;
4258
4414
  if (tooltipData.length === 0) {
4259
4415
  return;
4260
4416
  }
4261
- var _d3$pointer = d3.pointer(event),
4262
- y = _d3$pointer[1];
4263
4417
  var pressureY = event.clientY - svgRectangle.top - 10;
4264
- var pressure = extractPressureFromY(diagramType, y, yScale);
4418
+ var pressure = hoveredPressure;
4265
4419
  var interpolatedData = tooltipData.map(function (dataSet) {
4266
4420
  return {
4267
4421
  hoveredPressure: dataSet.interpolatePressure(pressure),
@@ -4460,6 +4614,7 @@ var drawIsothermsBijvoet = function drawIsothermsBijvoet(_ref2) {
4460
4614
  xScale = _ref2.xScale,
4461
4615
  yScale = _ref2.yScale,
4462
4616
  svg = _ref2.svg,
4617
+ labelSvg = _ref2.labelSvg,
4463
4618
  height = _ref2.height,
4464
4619
  d3GraphId = _ref2.d3GraphId;
4465
4620
  // lines of constant temperature (2 degree interval from -150...+40C)
@@ -4477,7 +4632,7 @@ var drawIsothermsBijvoet = function drawIsothermsBijvoet(_ref2) {
4477
4632
  var strID = temp + "-isotherm-BIJVOET";
4478
4633
  svg.append('path').attr('id', strID).attr('d', pathString).style('fill', 'none').style('stroke-width', temp % 10 === 0 ? 2 : 1).style('stroke', temp === 0 ? '#D27D2D' : 'orange').style('stroke-opacity', 0.5).attr('clip-path', "url(#clip-" + d3GraphId + "-BIJVOETd3)");
4479
4634
  if (temp % 10 === 0 && ((_points$ = points[0]) == null ? void 0 : _points$[1]) === height) {
4480
- svg.append('text').text(temp).attr('text-anchor', 'middle').attr('x', points[0][0] + "px").attr('y', height + 12 + "px").attr('fill', temp === 0 ? '#D27D2D' : 'orange').attr('font-size', '10px');
4635
+ (labelSvg != null ? labelSvg : svg).append('text').text(temp).attr('text-anchor', 'middle').attr('x', points[0][0] + "px").attr('y', height + 12 + "px").attr('fill', temp === 0 ? '#D27D2D' : 'orange').attr('font-size', '10px');
4481
4636
  }
4482
4637
  }
4483
4638
  };
@@ -4620,6 +4775,7 @@ var drawStaticLines = function drawStaticLines(_ref) {
4620
4775
  minPressure = _ref.minPressure,
4621
4776
  maxPressure = _ref.maxPressure,
4622
4777
  svg = _ref.svg,
4778
+ bijvoetLabelSvg = _ref.bijvoetLabelSvg,
4623
4779
  d3GraphId = _ref.d3GraphId;
4624
4780
  drawIsobars({
4625
4781
  diagramType: diagramType,
@@ -4643,6 +4799,7 @@ var drawStaticLines = function drawStaticLines(_ref) {
4643
4799
  xScale: xScale,
4644
4800
  yScale: yScale,
4645
4801
  svg: svg,
4802
+ labelSvg: bijvoetLabelSvg,
4646
4803
  height: height,
4647
4804
  d3GraphId: d3GraphId
4648
4805
  });
@@ -4731,6 +4888,11 @@ var soundingsStyle$1 = {
4731
4888
  fontSize: '12px',
4732
4889
  fontWeight: 'bold'
4733
4890
  }};
4891
+ var updateTransformToViewport = function updateTransformToViewport(transform, width, height) {
4892
+ var minX = width * (1 - transform.k);
4893
+ var minY = height * (1 - transform.k);
4894
+ return d3.zoomIdentity.translate(Math.min(0, Math.max(minX, transform.x)), Math.min(0, Math.max(minY, transform.y))).scale(transform.k);
4895
+ };
4734
4896
  var Soundings = function Soundings(_ref) {
4735
4897
  var d3GraphId = _ref.d3GraphId,
4736
4898
  data = _ref.data,
@@ -4739,6 +4901,8 @@ var Soundings = function Soundings(_ref) {
4739
4901
  showTooltip = _ref$showTooltip === void 0 ? true : _ref$showTooltip,
4740
4902
  _ref$diagramType = _ref.diagramType,
4741
4903
  diagramType = _ref$diagramType === void 0 ? 'SKEWT' : _ref$diagramType;
4904
+ var instanceId = useId().replace(/:/g, '_');
4905
+ var scopedGraphId = d3GraphId + "-" + instanceId;
4742
4906
  var svgRef = useRef(null);
4743
4907
  var canvasRef = useRef(null);
4744
4908
  var pressureRef = useRef(null);
@@ -4750,8 +4914,21 @@ var Soundings = function Soundings(_ref) {
4750
4914
  var widthRef = useRef(0);
4751
4915
  var svgRectRef = useRef(null);
4752
4916
  var tooltipDataRef = useRef(null);
4917
+ var zoomBehaviorRef = useRef(null);
4918
+ // Keep zoom target on the inner translated <g>; Soundings and WindBarbs must
4919
+ // transform the same coordinate space to stay aligned.
4920
+ var zoomTargetRef = useRef(null);
4921
+ var isApplyingSharedZoomRef = useRef(false);
4922
+ // Keep the last applied d3 zoom transform across SVG redraws.
4923
+ // Width/height changes recreate the chart, so this ref prevents zoom snapping
4924
+ // back to identity when panels are closed/resized.
4925
+ var persistedZoomTransformRef = useRef(d3.zoomIdentity);
4926
+ var previousDiagramTypeRef = useRef(null);
4753
4927
  var _useSharedMouseY = useSharedMouseY(),
4754
- clientY = _useSharedMouseY.clientY;
4928
+ clientY = _useSharedMouseY.clientY,
4929
+ publishVerticalZoom = _useSharedMouseY.publishVerticalZoom,
4930
+ verticalZoom = _useSharedMouseY.verticalZoom,
4931
+ resetVerticalZoom = _useSharedMouseY.resetVerticalZoom;
4755
4932
  // Set SVG dimensions (required props; no optional chaining needed)
4756
4933
  var baseWidth = svgDimensions.width,
4757
4934
  baseHeight = svgDimensions.height,
@@ -4759,17 +4936,31 @@ var Soundings = function Soundings(_ref) {
4759
4936
  var width = baseWidth - (margin.left + margin.right);
4760
4937
  var height = baseHeight - (margin.top + margin.bottom);
4761
4938
  useEffect(function () {
4762
- if (!svgRef.current) {
4939
+ if (!svgRef.current || width <= 0 || height <= 0) {
4763
4940
  return;
4764
4941
  }
4942
+ var isDiagramTypeChange = previousDiagramTypeRef.current !== null && previousDiagramTypeRef.current !== diagramType;
4943
+ if (isDiagramTypeChange) {
4944
+ persistedZoomTransformRef.current = d3.zoomIdentity;
4945
+ resetVerticalZoom(scopedGraphId);
4946
+ }
4947
+ var root = d3.select(svgRef.current);
4948
+ root.selectAll('*').remove();
4765
4949
  var svg = initializeSvg$1({
4766
4950
  svgRef: svgRef,
4767
4951
  width: width,
4768
4952
  height: height,
4769
4953
  margin: margin,
4770
4954
  diagramType: diagramType,
4771
- d3GraphId: d3GraphId
4955
+ d3GraphId: scopedGraphId
4772
4956
  });
4957
+ // Create a separate layer for axes so they can stay visible above the zoomed content.
4958
+ var axisLayer = svg.append('g').attr('data-layer', 'axis-layer');
4959
+ // Keep a static viewport group for clipping, and transform only the inner
4960
+ // content group during zoom so the visible area stays fixed.
4961
+ var zoomViewport = svg.append('g').attr('data-layer', 'zoom-viewport').attr('clip-path', "url(#clip-" + scopedGraphId + "-" + diagramType + "d3)");
4962
+ var zoomContent = zoomViewport.append('g').attr('data-layer', 'zoom-content');
4963
+ var bijvoetLabelLayer = svg.append('g').attr('data-layer', 'bijvoet-label-layer').style('pointer-events', 'none');
4773
4964
  var svgRectangle = svgRef.current.getBoundingClientRect();
4774
4965
  svgRectRef.current = svgRectangle;
4775
4966
  marginRef.current = margin;
@@ -4780,13 +4971,19 @@ var Soundings = function Soundings(_ref) {
4780
4971
  xScale = _createScales[0],
4781
4972
  yScale = _createScales[1];
4782
4973
  yScaleRef.current = yScale;
4974
+ var applyAxisClips = function applyAxisClips() {
4975
+ axisLayer.select('g[type="x-axis"] path.domain').attr('d', "M0.5,0.5H" + (width + 0.5));
4976
+ axisLayer.select('g[type="x-axis"]').attr('clip-path', "url(#clip-" + scopedGraphId + "-" + diagramType + "x-axis)");
4977
+ axisLayer.select('g[type="y-axis"]').attr('clip-path', "url(#clip-" + scopedGraphId + "-" + diagramType + "y-axis)");
4978
+ };
4783
4979
  drawAxes$1({
4784
- svg: svg,
4980
+ svg: axisLayer,
4785
4981
  xScale: xScale,
4786
4982
  yScale: yScale,
4787
4983
  height: height,
4788
4984
  diagramType: diagramType
4789
4985
  });
4986
+ applyAxisClips();
4790
4987
  drawStaticLines({
4791
4988
  diagramType: diagramType,
4792
4989
  dp: dp,
@@ -4799,8 +4996,9 @@ var Soundings = function Soundings(_ref) {
4799
4996
  yScale: yScale,
4800
4997
  minPressure: minPressure,
4801
4998
  maxPressure: maxPressure,
4802
- svg: svg,
4803
- d3GraphId: d3GraphId
4999
+ svg: zoomContent,
5000
+ bijvoetLabelSvg: bijvoetLabelLayer,
5001
+ d3GraphId: scopedGraphId
4804
5002
  });
4805
5003
  data.forEach(function (features, index) {
4806
5004
  var color = SOUNDING_COLORS[index] || SOUNDING_COLORS[0];
@@ -4815,10 +5013,11 @@ var Soundings = function Soundings(_ref) {
4815
5013
  pressureValues: pressureValues,
4816
5014
  color: color,
4817
5015
  lineStyle: 'none',
5016
+ d3GraphId: scopedGraphId,
4818
5017
  xScale: xScale,
4819
5018
  yScale: yScale,
4820
5019
  height: height,
4821
- svg: svg,
5020
+ svg: zoomContent,
4822
5021
  diagramType: diagramType
4823
5022
  });
4824
5023
  var dewpointValues = features.map(function (feature) {
@@ -4829,20 +5028,89 @@ var Soundings = function Soundings(_ref) {
4829
5028
  pressureValues: pressureValues,
4830
5029
  color: color,
4831
5030
  lineStyle: '8, 2',
5031
+ d3GraphId: scopedGraphId,
4832
5032
  xScale: xScale,
4833
5033
  yScale: yScale,
4834
5034
  height: height,
4835
- svg: svg,
5035
+ svg: zoomContent,
4836
5036
  diagramType: diagramType
4837
5037
  });
4838
5038
  });
4839
5039
  tooltipDataRef.current = constructTooltipData(data);
4840
- var svgRefCur = svgRef.current;
4841
- // eslint-disable-next-line consistent-return, @typescript-eslint/explicit-function-return-type
5040
+ var zoomTarget = zoomContent.node();
5041
+ if (!zoomTarget) {
5042
+ return;
5043
+ }
5044
+ zoomTargetRef.current = zoomTarget;
5045
+ var bijvoetLabelTarget = bijvoetLabelLayer.node();
5046
+ if (!bijvoetLabelTarget) {
5047
+ return;
5048
+ }
5049
+ var applyZoomTransform = function applyZoomTransform(transform) {
5050
+ d3.select(zoomTarget).attr('transform', transform.toString());
5051
+ d3.select(bijvoetLabelTarget).attr('transform', transform.toString());
5052
+ };
5053
+ // Allow zooming and panning
5054
+ var zoomBehavior = d3.zoom().scaleExtent([1, 7]).extent([[0, 0], [width, height]]).translateExtent([[0, 0], [width, height]]).on('zoom', function (event) {
5055
+ // Persist every interactive zoom/pan event so the transform can be
5056
+ // restored after dimension-driven rerenders.
5057
+ persistedZoomTransformRef.current = event.transform;
5058
+ applyZoomTransform(event.transform);
5059
+ var zoomedXScale = event.transform.rescaleX(xScale);
5060
+ var zoomedYScale = event.transform.rescaleY(yScale);
5061
+ var axisXScale = diagramType === 'SKEWT' ? xScale.copy().range(xScale.range().map(function (xValue) {
5062
+ // Skew-T X uses x + (height - y). To keep tick labels tied to
5063
+ // isotherm intersections on the bottom axis, apply the same
5064
+ // transformed bottom-edge offset to the axis scale.
5065
+ var skewBottomOffset = (event.transform.k - 1) * height + event.transform.y;
5066
+ return event.transform.k * xValue + event.transform.x + skewBottomOffset;
5067
+ })) : zoomedXScale;
5068
+ var axisYScale = diagramType === 'BIJVOET' ? yScale.copy().range(yScale.range().map(function (yValue) {
5069
+ return event.transform.k * yValue + event.transform.y;
5070
+ })) : zoomedYScale;
5071
+ axisLayer.selectAll('*').remove();
5072
+ drawAxes$1({
5073
+ svg: axisLayer,
5074
+ xScale: axisXScale,
5075
+ yScale: axisYScale,
5076
+ height: height,
5077
+ diagramType: diagramType
5078
+ });
5079
+ applyAxisClips();
5080
+ if (!isApplyingSharedZoomRef.current) {
5081
+ publishVerticalZoom(scopedGraphId, event.transform.k, event.transform.y);
5082
+ }
5083
+ });
5084
+ zoomBehaviorRef.current = zoomBehavior;
5085
+ svg.call(zoomBehavior);
5086
+ var previousTransform = updateTransformToViewport(persistedZoomTransformRef.current, width, height);
5087
+ var hasPersistedTransform = previousTransform.k > 1 || Math.abs(previousTransform.x) > 1e-3 || Math.abs(previousTransform.y) > 1e-3;
5088
+ // Re-apply the previous transform after rebuilding the SVG so resize
5089
+ // operations preserve the current zoom level/pan offset.
5090
+ if (hasPersistedTransform) {
5091
+ isApplyingSharedZoomRef.current = true;
5092
+ svg.call(zoomBehavior.transform, previousTransform);
5093
+ isApplyingSharedZoomRef.current = false;
5094
+ }
5095
+ // Keep shared zoom state aligned with the effective transform that was
5096
+ // restored after resize so companion panels (WindBarbs) do not wait for the
5097
+ // next interactive zoom event.
5098
+ publishVerticalZoom(scopedGraphId, previousTransform.k, previousTransform.y);
5099
+ previousDiagramTypeRef.current = diagramType;
5100
+ // eslint-disable-next-line consistent-return
4842
5101
  return function () {
4843
- d3.select(svgRefCur).selectAll('*').remove();
5102
+ // Read the transform directly from d3's internal zoom state before this
5103
+ // chart instance is torn down, then reuse it on the next mount pass.
5104
+ var zoomState = zoomTarget.__zoom;
5105
+ if (zoomState) {
5106
+ persistedZoomTransformRef.current = zoomState;
5107
+ }
5108
+ svg.on('zoom', null);
5109
+ zoomBehaviorRef.current = null;
5110
+ zoomTargetRef.current = null;
5111
+ root.selectAll('*').remove();
4844
5112
  };
4845
- }, [data, diagramType, width, height, d3GraphId, margin]);
5113
+ }, [data, diagramType, width, height, scopedGraphId, margin, publishVerticalZoom, resetVerticalZoom]);
4846
5114
  useEffect(function () {
4847
5115
  var _svgRectRef$current;
4848
5116
  if (!showTooltip || !svgRef.current) {
@@ -4876,19 +5144,22 @@ var Soundings = function Soundings(_ref) {
4876
5144
  var maxT = maxTemperature;
4877
5145
  var _createScales2 = createScales$1(diagramType, width, height, minPressure, maxPressure, minT, maxT),
4878
5146
  yScale = _createScales2[1];
5147
+ var pointerY = clientY - rect.top - marginRef.current.top;
5148
+ var unscaledY = (pointerY - verticalZoom.y) / verticalZoom.scale;
5149
+ var hoveredPressure = extractPressureFromY(diagramType, unscaledY, yScale);
4879
5150
  drawSoundingsTooltip({
4880
5151
  event: synthetic,
4881
- yScale: yScale,
4882
5152
  svgRectangle: rect,
4883
5153
  margin: marginRef.current,
4884
5154
  width: widthRef.current,
5155
+ hoveredPressure: hoveredPressure,
4885
5156
  pressureRefd3: pressureRefd3,
4886
5157
  tempRefd3: tempRefd3,
4887
5158
  dewpointRefd3: dewpointRefd3,
4888
5159
  tooltipData: tooltipDataRef.current,
4889
5160
  diagramType: diagramType
4890
5161
  });
4891
- }, [clientY, showTooltip, diagramType, data, width, height]);
5162
+ }, [clientY, showTooltip, diagramType, data, width, height, verticalZoom.scale, verticalZoom.y]);
4892
5163
  return jsxs(Box, {
4893
5164
  ref: canvasRef,
4894
5165
  sx: {
@@ -5098,7 +5369,10 @@ var SoundingsTimeSlider = function SoundingsTimeSlider(_ref) {
5098
5369
  * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
5099
5370
  * */
5100
5371
  var OFFSET_HEIGHT = 100; // Space reserved in layout
5101
- var useSoundingsDiagramSize = function useSoundingsDiagramSize(ref) {
5372
+ var useSoundingsDiagramSize = function useSoundingsDiagramSize(ref, useWidth) {
5373
+ if (useWidth === void 0) {
5374
+ useWidth = true;
5375
+ }
5102
5376
  var _React$useState = React.useState(0),
5103
5377
  size = _React$useState[0],
5104
5378
  setSize = _React$useState[1];
@@ -5115,14 +5389,14 @@ var useSoundingsDiagramSize = function useSoundingsDiagramSize(ref) {
5115
5389
  width = _entries$0$contentRec.width,
5116
5390
  height = _entries$0$contentRec.height;
5117
5391
  var availableHeight = height - TIMESLIDER_HEIGHT + OFFSET_HEIGHT;
5118
- var nextSize = Math.floor(Math.min(width, availableHeight));
5392
+ var nextSize = useWidth ? Math.floor(Math.min(width, availableHeight)) : Math.floor(availableHeight);
5119
5393
  setSize(nextSize > 0 ? nextSize : 0);
5120
5394
  });
5121
5395
  resizeObserver.observe(container);
5122
5396
  return function () {
5123
5397
  resizeObserver.disconnect();
5124
5398
  };
5125
- }, [ref]);
5399
+ }, [ref, useWidth]);
5126
5400
  return size;
5127
5401
  };
5128
5402
 
@@ -5377,7 +5651,9 @@ var drawWindBarbs = function drawWindBarbs(_ref) {
5377
5651
  _ref$barbColor = _ref.barbColor,
5378
5652
  barbColor = _ref$barbColor === void 0 ? 'black' : _ref$barbColor,
5379
5653
  _ref$columnOffsetX = _ref.columnOffsetX,
5380
- columnOffsetX = _ref$columnOffsetX === void 0 ? 0 : _ref$columnOffsetX;
5654
+ columnOffsetX = _ref$columnOffsetX === void 0 ? 0 : _ref$columnOffsetX,
5655
+ _ref$verticalScale = _ref.verticalScale,
5656
+ verticalScale = _ref$verticalScale === void 0 ? 1 : _ref$verticalScale;
5381
5657
  var barbsSize = 40;
5382
5658
  var scalingFactor = 0.5;
5383
5659
  var horizontalBarbMargin = barbsSize * scalingFactor + 5;
@@ -5391,14 +5667,14 @@ var drawWindBarbs = function drawWindBarbs(_ref) {
5391
5667
  if (!currentWidth) {
5392
5668
  return;
5393
5669
  }
5394
- // Add extra width to all isobars needed for the windbarbs
5395
- d3.selectAll('path[type="isobar"]').each(function (_d, i, nodes) {
5670
+ // Add extra width to isobars in this SVG only.
5671
+ rootSvg.selectAll('path[type="isobar"]').each(function (_d, i, nodes) {
5396
5672
  var path = d3.select(nodes[i]).attr('d');
5397
5673
  var newD = extendIsoBar(path, horizontalBarbMargin);
5398
5674
  d3.select(nodes[i]).attr('d', newD).attr('clip-path', null);
5399
5675
  });
5400
- // Add extra width to x-axis needed for the windbarbs
5401
- d3.select('g[type="x-axis"] path.domain').each(function (_d, i, nodes) {
5676
+ // Add extra width to x-axis in this SVG only.
5677
+ rootSvg.select('g[type="x-axis"] path.domain').each(function (_d, i, nodes) {
5402
5678
  var path = d3.select(nodes[i]).attr('d');
5403
5679
  var newD = extendXAxis(path, horizontalBarbMargin);
5404
5680
  d3.select(nodes[i]).attr('d', newD);
@@ -5408,10 +5684,10 @@ var drawWindBarbs = function drawWindBarbs(_ref) {
5408
5684
  // Place the barbs just to the right of the plotting area.
5409
5685
  // plotGroup's origin is already translated to (margin.left, margin.top),
5410
5686
  // and its width equals `width`.
5411
- var windBarbColumnX = width + 5 + columnOffsetX;
5412
- var barbGroup = plotGroup.append('g').attr('class', 'wind-group').attr('transform', // This translation should be revisited when implementing
5413
- // the plotting of several datasets at once
5414
- "translate(" + windBarbColumnX + ", 0)");
5687
+ var windBarbColumnX = width + 5 + columnOffsetX + horizontalBarbMargin;
5688
+ var barbGroup = plotGroup.append('g').attr('class', 'wind-group').attr('transform', // Apply translation and counter-scale to maintain aspect ratio during vertical zoom.
5689
+ // The counter-scale (1, 1/verticalScale) undoes the parent's vertical scaling.
5690
+ "translate(" + windBarbColumnX + ", 0) scale(1, " + 1 / verticalScale + ")");
5415
5691
  barbGroup.selectAll('g').data(visibleItems).enter().append('g').attr('id', 'wind-barb').each(function handleBarb(data) {
5416
5692
  var group = d3.select(this);
5417
5693
  var windSymbol = getWindSymbol(data.windSpeed);
@@ -5437,9 +5713,10 @@ var drawWindBarbs = function drawWindBarbs(_ref) {
5437
5713
  var cy = bbox.y + bbox.height / 2;
5438
5714
  var rotation = 180 + data.windDirection;
5439
5715
  var y = yScale(data.pressure);
5440
- // Place items in the correct vertical position
5441
- // Correcting for barbSize
5442
- group.attr('transform', "translate(0, " + (y - barbsSize * scalingFactor / 8) + ")");
5716
+ // Place items in the correct vertical position.
5717
+ // Apply verticalScale to compensate for the parent's counter-scale,
5718
+ // ensuring barbs appear at the correct zoomed Y position.
5719
+ group.attr('transform', "translate(0, " + (y - barbsSize * scalingFactor / 8) * verticalScale + ")");
5443
5720
  // Rotate accordingly, and scale
5444
5721
  innerGroup.attr('transform', "rotate(" + rotation + ") scale(" + scalingFactor + ") translate(" + -cx + ", " + -cy + ")");
5445
5722
  }
@@ -5495,6 +5772,10 @@ var soundingsStyle = {
5495
5772
  }
5496
5773
  };
5497
5774
  var WIND_BARB_OFFSET_X = 35;
5775
+ var getVerticalOnlyTransform = function getVerticalOnlyTransform(scale, yOffset) {
5776
+ // Keep x-axis unchanged, apply shared zoom on y-axis only.
5777
+ return "matrix(1, 0, 0, " + scale + ", 0, " + yOffset + ")";
5778
+ };
5498
5779
  /**
5499
5780
  * Check if the sounding data is effectively empty.
5500
5781
  * Data can be empty in two ways:
@@ -5530,8 +5811,12 @@ var WindBarbs = function WindBarbs(_ref) {
5530
5811
  var widthRef = useRef(0);
5531
5812
  var svgRectRef = useRef(null);
5532
5813
  var tooltipDataRef = useRef(null);
5814
+ // Must point to the inner translated <g> to match Soundings zoom coordinate space.
5815
+ var zoomTargetRef = useRef(null);
5533
5816
  var _useSharedMouseY = useSharedMouseY(),
5534
- clientY = _useSharedMouseY.clientY;
5817
+ clientY = _useSharedMouseY.clientY,
5818
+ verticalZoom = _useSharedMouseY.verticalZoom,
5819
+ resetVerticalZoom = _useSharedMouseY.resetVerticalZoom;
5535
5820
  // Set SVG dimensions (required props; no optional chaining needed)
5536
5821
  var baseWidth = svgDimensions.width,
5537
5822
  baseHeight = svgDimensions.height,
@@ -5539,7 +5824,7 @@ var WindBarbs = function WindBarbs(_ref) {
5539
5824
  var width = baseWidth - (margin.left + margin.right);
5540
5825
  var height = baseHeight - (margin.top + margin.bottom);
5541
5826
  useEffect(function () {
5542
- if (!svgRef.current) {
5827
+ if (!svgRef.current || width <= 0 || height <= 0) {
5543
5828
  return;
5544
5829
  }
5545
5830
  var root = d3.select(svgRef.current);
@@ -5552,8 +5837,9 @@ var WindBarbs = function WindBarbs(_ref) {
5552
5837
  diagramType: diagramType,
5553
5838
  d3GraphId: d3GraphId
5554
5839
  });
5555
- // When Soundings is dragged close, animate the Wind Barbs to resize back to the height where dragging started
5840
+ // When Soundings is dragged closed, reset the zoom level and animate the Wind Barbs to resize back to the height where dragging started
5556
5841
  if (animateResize) {
5842
+ resetVerticalZoom(d3GraphId);
5557
5843
  animateResizeTransform(svg, margin, height, prevInnerHeightRef);
5558
5844
  }
5559
5845
  var svgRectangle = svgRef.current.getBoundingClientRect();
@@ -5591,18 +5877,33 @@ var WindBarbs = function WindBarbs(_ref) {
5591
5877
  width: width,
5592
5878
  margin: margin,
5593
5879
  barbColor: color,
5594
- columnOffsetX: index * WIND_BARB_OFFSET_X
5880
+ columnOffsetX: index * WIND_BARB_OFFSET_X,
5881
+ verticalScale: verticalZoom.scale
5595
5882
  });
5596
5883
  });
5597
5884
  // Clear or update tooltip data based on whether we have actual data
5598
5885
  tooltipDataRef.current = isDataEmpty(data) ? null : constructTooltipData(data);
5599
5886
  // Update previous inner height after drawing so animation uses the prior value
5600
5887
  prevInnerHeightRef.current = height;
5888
+ var zoomTarget = svg.node();
5889
+ if (!zoomTarget) {
5890
+ return;
5891
+ }
5892
+ zoomTargetRef.current = zoomTarget;
5893
+ // Initialize with shared zoom state from Soundings.
5894
+ d3.select(zoomTarget).attr('transform', getVerticalOnlyTransform(verticalZoom.scale, verticalZoom.y));
5601
5895
  // eslint-disable-next-line consistent-return, @typescript-eslint/explicit-function-return-type
5602
5896
  return function () {
5897
+ zoomTargetRef.current = null;
5603
5898
  root.selectAll('*').remove();
5604
5899
  };
5605
- }, [data, diagramType, width, height, d3GraphId, margin, animateResize]);
5900
+ }, [data, diagramType, width, height, d3GraphId, margin, animateResize, verticalZoom.scale, verticalZoom.y, resetVerticalZoom]);
5901
+ useEffect(function () {
5902
+ if (!zoomTargetRef.current) {
5903
+ return;
5904
+ }
5905
+ d3.select(zoomTargetRef.current).attr('transform', getVerticalOnlyTransform(verticalZoom.scale, verticalZoom.y));
5906
+ }, [verticalZoom]);
5606
5907
  useEffect(function () {
5607
5908
  var _svgRectRef$current;
5608
5909
  if (!showTooltip || !svgRef.current || !windRef.current) {
@@ -5630,18 +5931,27 @@ var WindBarbs = function WindBarbs(_ref) {
5630
5931
  target: svgRef.current,
5631
5932
  currentTarget: svgRef.current
5632
5933
  };
5934
+ var pointerY = clientY - rect.top - marginRef.current.top;
5935
+ var unscaledY = (pointerY - verticalZoom.y) / verticalZoom.scale;
5936
+ var hoveredPressure = extractPressureFromY(diagramType, unscaledY, yScaleRef.current);
5937
+ var transformedY = yScaleRef.current(hoveredPressure) * verticalZoom.scale + verticalZoom.y;
5938
+ var pressureLineY = transformedY + marginRef.current.top;
5633
5939
  var windRefd3 = d3.select(windRef.current);
5634
5940
  drawSoundingsTooltip({
5635
5941
  event: synthetic,
5636
- yScale: yScaleRef.current,
5637
5942
  svgRectangle: rect,
5638
5943
  margin: marginRef.current,
5639
5944
  width: widthRef.current,
5945
+ hoveredPressure: hoveredPressure,
5640
5946
  windRefd3: windRefd3,
5641
5947
  tooltipData: tooltipDataRef.current,
5642
5948
  diagramType: diagramType
5643
5949
  });
5644
- }, [clientY, showTooltip, diagramType, data]);
5950
+ var tooltipHeight = windRef.current.getBoundingClientRect().height;
5951
+ var centeredTop = pressureLineY - tooltipHeight / 2;
5952
+ var clampedTop = Math.max(0, Math.min(centeredTop, rect.height - tooltipHeight));
5953
+ windRef.current.style.top = clampedTop + "px";
5954
+ }, [clientY, showTooltip, diagramType, data, verticalZoom.scale, verticalZoom.y]);
5645
5955
  return jsxs(Box, {
5646
5956
  ref: canvasRef,
5647
5957
  sx: {
@@ -5859,10 +6169,10 @@ var SoundingsProfileDataTable = function SoundingsProfileDataTable(_ref2) {
5859
6169
  py: 4
5860
6170
  },
5861
6171
  children: jsx(Typography, {
5862
- variant: "body2",
5863
6172
  sx: {
5864
6173
  color: 'text.secondary'
5865
6174
  },
6175
+ variant: "body2",
5866
6176
  children: t('no-sounding-data-available')
5867
6177
  })
5868
6178
  }) : jsx(Box, {
@@ -5884,7 +6194,9 @@ var SoundingsProfileDataTable = function SoundingsProfileDataTable(_ref2) {
5884
6194
  },
5885
6195
  style: {
5886
6196
  height: '100%',
5887
- width: '100%'
6197
+ width: '100%',
6198
+ minWidth: tableMinWidth,
6199
+ overflowX: 'hidden'
5888
6200
  }
5889
6201
  })
5890
6202
  })]
@@ -5903,32 +6215,42 @@ var SoundingsPanels = function SoundingsPanels(_ref) {
5903
6215
  panelComponent = _ref.panelComponent,
5904
6216
  profileDataPreferences = _ref.profileDataPreferences;
5905
6217
  var soundingsContentRef = React.useRef(null);
6218
+ var windBarbsContentRef = React.useRef(null);
5906
6219
  var diagramSize = useSoundingsDiagramSize(soundingsContentRef);
5907
- var _React$useState = React.useState(false),
5908
- soundingsIsClosed = _React$useState[0],
5909
- setSoundingsIsClosed = _React$useState[1];
5910
- var _React$useState2 = React.useState(null),
5911
- previousHeight = _React$useState2[0],
5912
- setPreviousHeight = _React$useState2[1];
6220
+ var windBarbsDiagramSize = useSoundingsDiagramSize(windBarbsContentRef, false);
6221
+ var soundingsIsOpen = React.useMemo(function () {
6222
+ var _soundingPanelsInOrde;
6223
+ return ((_soundingPanelsInOrde = soundingPanelsInOrder.find(function (panel) {
6224
+ return panel.id === 'soundings';
6225
+ })) == null ? void 0 : _soundingPanelsInOrde.isOpen) || false;
6226
+ }, [soundingPanelsInOrder]);
6227
+ var _React$useState = React.useState(null),
6228
+ previousHeight = _React$useState[0],
6229
+ setPreviousHeight = _React$useState[1];
5913
6230
  var firstSelectedSourceData = React.useMemo(function () {
5914
6231
  var _data$;
5915
6232
  return (_data$ = data[0]) != null ? _data$ : [];
5916
6233
  }, [data]);
6234
+ var windBarbsHeight = React.useMemo(function () {
6235
+ if (soundingsIsOpen) {
6236
+ return diagramSize;
6237
+ }
6238
+ return previousHeight != null ? previousHeight : windBarbsDiagramSize;
6239
+ }, [diagramSize, previousHeight, soundingsIsOpen, windBarbsDiagramSize]);
5917
6240
  var sizeRef = React.useRef(diagramSize);
5918
6241
  var isResizingRef = React.useRef(false);
5919
6242
  React.useEffect(function () {
5920
6243
  sizeRef.current = diagramSize;
5921
6244
  }, [diagramSize]);
5922
6245
  React.useEffect(function () {
5923
- if (previousHeight == null && diagramSize > 0) {
6246
+ if (previousHeight == null && diagramSize > 24) {
5924
6247
  setPreviousHeight(diagramSize);
5925
6248
  }
5926
6249
  }, [diagramSize, previousHeight]);
5927
6250
  var preserveWindBarbsGrowBackHeight = React.useCallback(function () {
5928
- if (sizeRef.current > 0) {
5929
- setPreviousHeight(sizeRef.current);
5930
- }
5931
- }, []);
6251
+ var nextHeight = sizeRef.current > 24 ? sizeRef.current : windBarbsDiagramSize;
6252
+ setPreviousHeight(nextHeight);
6253
+ }, [windBarbsDiagramSize]);
5932
6254
  var handleSoundingsResizeStart = React.useCallback(function () {
5933
6255
  isResizingRef.current = true;
5934
6256
  preserveWindBarbsGrowBackHeight();
@@ -5941,7 +6263,6 @@ var SoundingsPanels = function SoundingsPanels(_ref) {
5941
6263
  if (isClosingViaClick) {
5942
6264
  preserveWindBarbsGrowBackHeight();
5943
6265
  }
5944
- setSoundingsIsClosed(!isOpen);
5945
6266
  }, [preserveWindBarbsGrowBackHeight]);
5946
6267
  var panels = soundingPanelsInOrder.flatMap(function (_ref2) {
5947
6268
  var id = _ref2.id,
@@ -5985,9 +6306,10 @@ var SoundingsPanels = function SoundingsPanels(_ref) {
5985
6306
  name: 'Wind Barbs',
5986
6307
  isDisplayed: isDisplayed,
5987
6308
  maxWidth: 200,
6309
+ panelRef: windBarbsContentRef,
5988
6310
  render: function render() {
5989
6311
  return jsx(WindBarbs, {
5990
- animateResize: Boolean(soundingsIsClosed && previousHeight),
6312
+ animateResize: Boolean(!soundingsIsOpen && previousHeight),
5991
6313
  d3GraphId: normalizedId,
5992
6314
  data: data,
5993
6315
  diagramType: chosenDiagramType,
@@ -5999,7 +6321,7 @@ var SoundingsPanels = function SoundingsPanels(_ref) {
5999
6321
  right: 25,
6000
6322
  top: 0
6001
6323
  },
6002
- height: soundingsIsClosed ? previousHeight != null ? previousHeight : diagramSize : diagramSize
6324
+ height: windBarbsHeight
6003
6325
  }
6004
6326
  });
6005
6327
  }
@@ -6192,6 +6514,26 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6192
6514
  previousLocationsRef.current = {};
6193
6515
  }
6194
6516
  }, [currentSoundingsPreset, dispatch, soundingsPreset, viewId]);
6517
+ var syncgroupTimeState = useSyncgroupTimeState(viewId, 0);
6518
+ var syncgroupSelectedTime = useMemo(function () {
6519
+ if ((syncgroupTimeState == null ? void 0 : syncgroupTimeState.current) === undefined || !Number.isFinite(syncgroupTimeState.current)) {
6520
+ return undefined;
6521
+ }
6522
+ return dateUtils.fromUnix(syncgroupTimeState.current).toISOString();
6523
+ }, [syncgroupTimeState]);
6524
+ var isTimeSynced = !!syncgroupSelectedTime;
6525
+ React.useEffect(function () {
6526
+ if (soundingsPreset != null && soundingsPreset.selectedTime && isTimeSynced) {
6527
+ // trigger sync of time to map when preset has selectedTime
6528
+ dispatch(genericActions.setTime({
6529
+ sourceId: viewId,
6530
+ origin: 'SoundingsConnect',
6531
+ value: {
6532
+ currentTime: handleDateUtilsISOString(soundingsPreset.selectedTime)
6533
+ }
6534
+ }));
6535
+ }
6536
+ }, [dispatch, isTimeSynced, soundingsPreset == null ? void 0 : soundingsPreset.selectedTime, viewId]);
6195
6537
  useRegisterFeatureSyncSource(viewId);
6196
6538
  var selectedTime = useSelector(function (state) {
6197
6539
  return getSelectedTime(state, viewId);
@@ -6212,6 +6554,9 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6212
6554
  var locationSelections = useSelector(function (state) {
6213
6555
  return getLocationSelections(state, viewId);
6214
6556
  }, shallowEqual);
6557
+ var soundingsCollections = useSelector(function (state) {
6558
+ return getSoundingsCollections(state);
6559
+ });
6215
6560
  var diagramType = useSelector(function (state) {
6216
6561
  return getDiagramType(state, viewId);
6217
6562
  });
@@ -6221,9 +6566,15 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6221
6566
  var profileDataPreferences = useSelector(function (state) {
6222
6567
  return getProfileDataPreferences(state, viewId);
6223
6568
  });
6569
+ var isLegendVisible = useSelector(function (state) {
6570
+ return getIsLegendVisible(state, viewId);
6571
+ });
6224
6572
  var selectedMapIds = useSelector(function (state) {
6225
6573
  return genericSelectors.getLinkedTargetIds(state, viewId);
6226
6574
  }, shallowEqual);
6575
+ var ownedFeatureLayerIds = useSelector(function (state) {
6576
+ return genericSelectors.selectFeatureSyncLayerIdsForPanel(state, viewId);
6577
+ }, shallowEqual);
6227
6578
  var linkedPanelIdsByMapId = useSelector(function (state) {
6228
6579
  return Object.fromEntries(selectedMapIds.map(function (mapId) {
6229
6580
  return [mapId, genericSelectors.selectLinkedPanelIdsForMap(state, mapId)];
@@ -6232,14 +6583,6 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6232
6583
  var firstLinkedMapId = selectedMapIds[0];
6233
6584
  var _useTimesteps = useTimesteps(resolvedSourceSelections),
6234
6585
  sourceTimesteps = _useTimesteps.data;
6235
- var syncgroupTimeState = useSyncgroupTimeState(viewId, 0);
6236
- var syncgroupSelectedTime = useMemo(function () {
6237
- if ((syncgroupTimeState == null ? void 0 : syncgroupTimeState.current) === undefined || !Number.isFinite(syncgroupTimeState.current)) {
6238
- return undefined;
6239
- }
6240
- return dateUtils.fromUnix(syncgroupTimeState.current).toISOString();
6241
- }, [syncgroupTimeState]);
6242
- var isTimeSynced = !!syncgroupSelectedTime;
6243
6586
  var selectedTimeInUse = syncgroupSelectedTime || selectedTime;
6244
6587
  var timeMismatchInfo = useMemo(function () {
6245
6588
  return getTimeMismatchInfo(isTimeSynced, selectedTimeInUse, sourceTimesteps);
@@ -6270,23 +6613,23 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6270
6613
  // Ref for reading selectedMapPin in effects without causing dependency loops
6271
6614
  var selectedMapPinRef = React.useRef(selectedMapPin);
6272
6615
  selectedMapPinRef.current = selectedMapPin;
6273
- // Get map bbox and SRS for zoom-dependent overlap prevention
6274
- var mapBbox = useSelector(function (state) {
6275
- return mapSelectors.getBbox(state, firstLinkedMapId);
6276
- }, shallowEqual);
6277
- var mapSrs = useSelector(function (state) {
6278
- return mapSelectors.getSrs(state, firstLinkedMapId);
6279
- });
6280
6616
  var collectionsRef = React.useRef(undefined);
6281
6617
  var collections = useMemo(function () {
6618
+ // Only use instanceId for location fetching when the preset configures it, so collection-level sources (e.g. harmonie) are not affected
6619
+ var presetMap = new Map(soundingsCollections.map(function (collection) {
6620
+ return [collection.id, collection];
6621
+ }));
6282
6622
  var newCollections = sourceSelections.map(function (_ref2) {
6623
+ var _presetMap$get;
6283
6624
  var collectionId = _ref2.collectionId,
6284
6625
  url = _ref2.url,
6285
- name = _ref2.name;
6626
+ name = _ref2.name,
6627
+ instanceId = _ref2.instanceId;
6286
6628
  return {
6287
6629
  id: collectionId,
6288
6630
  url: url,
6289
- name: name || collectionId
6631
+ name: name || collectionId,
6632
+ instanceId: ((_presetMap$get = presetMap.get(collectionId)) == null ? void 0 : _presetMap$get.instanceId) !== undefined ? instanceId : undefined
6290
6633
  };
6291
6634
  });
6292
6635
  if (isEqual(newCollections, collectionsRef.current)) {
@@ -6294,9 +6637,9 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6294
6637
  }
6295
6638
  collectionsRef.current = newCollections;
6296
6639
  return newCollections;
6297
- }, [sourceSelections]);
6298
- var _useGetCollectionLoca = useGetCollectionLocations(collections, true),
6299
- rawLocationData = _useGetCollectionLoca.data;
6640
+ }, [sourceSelections, soundingsCollections]);
6641
+ var _useGetLocations = useGetLocations(collections, true),
6642
+ rawLocationData = _useGetLocations.data;
6300
6643
  var _useCheckPositionQuer = useCheckPositionQuerySupportBySource(resolvedSourceSelections),
6301
6644
  positionQuerySupportBySource = _useCheckPositionQuer.data;
6302
6645
  var _useCheckRadiusQueryS = useCheckRadiusQuerySupportBySource(resolvedSourceSelections),
@@ -6317,10 +6660,11 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6317
6660
  sourceSelections.forEach(function (source) {
6318
6661
  var selection = locationSelections == null ? void 0 : locationSelections[source.collectionId];
6319
6662
  if (typeof selection === 'string') {
6663
+ var _sourceLocations$feat;
6320
6664
  var sourceLocations = locationData == null ? void 0 : locationData.find(function (service) {
6321
6665
  return service.id === source.collectionId;
6322
6666
  });
6323
- var feature = sourceLocations == null ? void 0 : sourceLocations.features.find(function (feature) {
6667
+ var feature = sourceLocations == null || (_sourceLocations$feat = sourceLocations.features) == null ? void 0 : _sourceLocations$feat.find(function (feature) {
6324
6668
  return feature.id === selection;
6325
6669
  });
6326
6670
  if ((feature == null ? void 0 : feature.geometry.type) === 'Point') {
@@ -6335,6 +6679,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6335
6679
  var sourceLocationNames = useMemo(function () {
6336
6680
  var result = {};
6337
6681
  sourceSelections.forEach(function (source) {
6682
+ var _sourceLocations$feat2;
6338
6683
  var selection = locationSelections == null ? void 0 : locationSelections[source.collectionId];
6339
6684
  if (typeof selection !== 'string') {
6340
6685
  return;
@@ -6342,7 +6687,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6342
6687
  var sourceLocations = locationData == null ? void 0 : locationData.find(function (service) {
6343
6688
  return service.id === source.collectionId;
6344
6689
  });
6345
- var feature = sourceLocations == null ? void 0 : sourceLocations.features.find(function (feature) {
6690
+ var feature = sourceLocations == null || (_sourceLocations$feat2 = sourceLocations.features) == null ? void 0 : _sourceLocations$feat2.find(function (feature) {
6346
6691
  return feature.id === selection;
6347
6692
  });
6348
6693
  var name = feature == null ? void 0 : feature.properties.name;
@@ -6396,7 +6741,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6396
6741
  var locationFeatures = (locationData || []).flatMap(function (_ref3) {
6397
6742
  var serviceId = _ref3.id,
6398
6743
  features = _ref3.features;
6399
- return features.map(function (feature) {
6744
+ return (features != null ? features : []).map(function (feature) {
6400
6745
  // Check if this feature is highlighted by its source (for sources without position support)
6401
6746
  var isHighlightedBySource = highlightedFeaturesBySource[serviceId] === feature.id;
6402
6747
  // Get the source index to determine color (same color as sounding diagram)
@@ -6512,54 +6857,20 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6512
6857
  });
6513
6858
  // Small fixed offset for markers sharing same grid position (~200m)
6514
6859
  var SOURCE_OVERLAP_OFFSET_DEG = 0.002;
6515
- // Calculate zoom-dependent minimum separation from clicked marker
6516
- // Handle different map projections for bbox interpretation
6517
- var MARKER_DIAMETER_PIXELS = 5; // Small separation - only push when very close to clicked marker
6518
- var ASSUMED_MAP_WIDTH_PIXELS = 800;
6519
- var MIN_CLICKED_SEPARATION_DEG = 0.005; // Minimum ~500m
6520
- var minClickedSeparationDeg = MIN_CLICKED_SEPARATION_DEG;
6521
- if ((mapBbox == null ? void 0 : mapBbox.right) != null && (mapBbox == null ? void 0 : mapBbox.left) != null) {
6522
- var bboxExtent = mapBbox.right - mapBbox.left;
6523
- var extentDeg;
6524
- // Convert bbox extent to degrees based on map projection
6525
- if (mapSrs === 'EPSG:4326') {
6526
- // Geographic projection - bbox already in degrees
6527
- extentDeg = bboxExtent;
6528
- } else if (mapSrs === 'EPSG:3857') {
6529
- // Web Mercator - convert meters to degrees
6530
- var WEB_MERCATOR_MAX = 20037508.34; // Half of Earth's circumference in meters
6531
- extentDeg = bboxExtent * 180 / WEB_MERCATOR_MAX;
6532
- } else if (Math.abs(bboxExtent) > 360) {
6533
- // Other projections: use heuristic based on magnitude
6534
- // If extent > 360, assume it's in meters/similar units
6535
- // Likely meters - use Web Mercator conversion as approximation
6536
- var _WEB_MERCATOR_MAX = 20037508.34;
6537
- extentDeg = bboxExtent * 180 / _WEB_MERCATOR_MAX;
6538
- } else {
6539
- // Likely already in degrees or degree-like units
6540
- extentDeg = bboxExtent;
6541
- }
6542
- minClickedSeparationDeg = Math.max(MIN_CLICKED_SEPARATION_DEG, extentDeg / ASSUMED_MAP_WIDTH_PIXELS * MARKER_DIAMETER_PIXELS);
6543
- }
6544
- var clickedLon = targetLocation[0],
6545
- clickedLat = targetLocation[1];
6546
- // Place markers at their true geographic positions with two adjustments:
6547
- // 1. Offset if multiple sources share the same grid point
6548
- // 2. Push away from clicked marker if too close (zoom-dependent)
6860
+ // Place markers at their true geographic positions.
6861
+ // The only adjustment is a small fixed offset when multiple sources share the same grid point.
6549
6862
  return gridPoints.map(function (item) {
6550
6863
  var source = item.source,
6551
6864
  sourceIndex = item.sourceIndex,
6552
6865
  gridPoint = item.gridPoint;
6553
6866
  var baseLon = gridPoint[0],
6554
6867
  baseLat = gridPoint[1];
6555
- // Step 1: Handle source overlap (multiple sources at same grid position)
6556
6868
  var key = baseLon.toFixed(6) + "," + baseLat.toFixed(6);
6557
6869
  var group = positionGroups.get(key) || [];
6558
6870
  var lon = baseLon;
6559
6871
  var lat = baseLat;
6560
6872
  if (group.length > 1) {
6561
6873
  // Multiple sources share this grid point - offset in circle around it
6562
- // Direction is based on index (arbitrary but consistent)
6563
6874
  var indexInGroup = group.findIndex(function (gr) {
6564
6875
  return gr.sourceIndex === sourceIndex;
6565
6876
  });
@@ -6567,25 +6878,6 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6567
6878
  lon = baseLon + SOURCE_OVERLAP_OFFSET_DEG * Math.cos(offsetAngle);
6568
6879
  lat = baseLat + SOURCE_OVERLAP_OFFSET_DEG * Math.sin(offsetAngle);
6569
6880
  }
6570
- // Step 2: Check overlap with clicked marker
6571
- // Account for longitude convergence at higher latitudes (1° lon = cos(lat) * 1° lat in distance)
6572
- var meanLat = (lat + clickedLat) / 2;
6573
- var cosLat = Math.cos(meanLat * Math.PI / 180);
6574
- var deltaLon = lon - clickedLon;
6575
- var deltaLat = lat - clickedLat;
6576
- // Scale longitude difference by cos(lat) for accurate distance
6577
- var scaledDeltaLon = deltaLon * cosLat;
6578
- var distanceToClicked = Math.sqrt(scaledDeltaLon * scaledDeltaLon + deltaLat * deltaLat);
6579
- if (distanceToClicked < minClickedSeparationDeg) {
6580
- // Too close to clicked marker - push outward preserving direction
6581
- // Direction is from clicked point to marker's current position (in scaled coordinates)
6582
- var directionAngle = distanceToClicked > 0.0001 ? Math.atan2(deltaLat, scaledDeltaLon) :
6583
- // If exactly on clicked point, use direction to original grid point
6584
- Math.atan2(baseLat - clickedLat, (baseLon - clickedLon) * cosLat);
6585
- // When applying offset, divide lon component by cos(lat) to get correct degree offset
6586
- lon = clickedLon + minClickedSeparationDeg * Math.cos(directionAngle) / cosLat;
6587
- lat = clickedLat + minClickedSeparationDeg * Math.sin(directionAngle);
6588
- }
6589
6881
  return {
6590
6882
  type: 'Feature',
6591
6883
  geometry: {
@@ -6602,7 +6894,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6602
6894
  }
6603
6895
  };
6604
6896
  });
6605
- }, [isFreePositionClick, targetLocation, gridPointData, sourceSelections, shouldHideLocationMarkers, mapBbox, mapSrs]);
6897
+ }, [isFreePositionClick, targetLocation, gridPointData, sourceSelections, shouldHideLocationMarkers]);
6606
6898
  // Combine location features with grid point features for dispatch to map
6607
6899
  var combinedGeoJson = useMemo(function () {
6608
6900
  if (gridPointFeatures.length === 0) {
@@ -6646,7 +6938,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6646
6938
  var allNearestFeatures = [];
6647
6939
  sourceSelections.forEach(function (source) {
6648
6940
  if (source.collectionId && targetLocation) {
6649
- var _sourceData$features, _positionQuerySupport;
6941
+ var _sourceData$features, _positionQuerySupport, _radiusQuerySupportBy, _gridPointData$source;
6650
6942
  // Get features owned by this source
6651
6943
  var sourceData = locationData == null ? void 0 : locationData.find(function (service) {
6652
6944
  return service.id === source.collectionId;
@@ -6665,17 +6957,29 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6665
6957
  // Detect if user clicked directly on a location marker owned by this source
6666
6958
  // A direct click means selectedFeature has an id that matches a feature owned by this source
6667
6959
  var isDirectClickOnOwnedMarker = !!(currentSelectedFeature != null && currentSelectedFeature.id) && (nearestOwnedFeature == null ? void 0 : nearestOwnedFeature.id) === (currentSelectedFeature == null ? void 0 : currentSelectedFeature.id);
6960
+ // When clicking a free position, snap to the nearest grid point from the radius query
6961
+ // so the sounding data comes from an actual model grid point rather than an arbitrary coordinate.
6962
+ var supportsRadius = (_radiusQuerySupportBy = radiusQuerySupportBySource[source.collectionId]) != null ? _radiusQuerySupportBy : false;
6963
+ var isGridPointMarkerClick = typeof (currentMapPin == null ? void 0 : currentMapPin.id) === 'string' && currentMapPin.id.startsWith('grid-point-');
6964
+ var isLocalFreePositionClick = !(currentSelectedFeature != null && currentSelectedFeature.id) || isGridPointMarkerClick;
6965
+ var gridPoint = isLocalFreePositionClick && supportsRadius ? gridPointData == null || (_gridPointData$source = gridPointData[source.collectionId]) == null ? void 0 : _gridPointData$source.gridPoint : undefined;
6966
+ var radiusPending = isLocalFreePositionClick && supportsRadius && !(gridPointData != null && gridPointData[source.collectionId]);
6668
6967
  // Determine the location to use:
6669
6968
  // 1. If user clicked directly on a marker owned by this source: use that marker's ID (location query)
6670
- // 2. If position query is supported AND not a direct click: use exact clicked coordinates (position query)
6969
+ // 2. If position query is supported AND not a direct click: use nearest grid point coordinates
6970
+ // if available (from radius query), otherwise fall back to exact clicked coordinates
6671
6971
  // 3. If position query is NOT supported: fall back to nearest location marker (location query)
6672
6972
  var newLocation = locationSelectionsRef.current[source.collectionId];
6673
6973
  if (isDirectClickOnOwnedMarker && nearestOwnedFeature) {
6674
6974
  // Direct click on marker - use location query with marker ID
6675
6975
  newLocation = nearestOwnedFeature.id;
6676
6976
  } else if (supportsPosition && targetLocation) {
6677
- // Use position query with exact coordinates
6678
- newLocation = targetLocation;
6977
+ if (radiusPending) {
6978
+ // Radius query in flight — defer until gridPointData arrives
6979
+ return;
6980
+ }
6981
+ // Snap to nearest grid point when available, otherwise use exact clicked coordinates
6982
+ newLocation = gridPoint != null ? gridPoint : targetLocation;
6679
6983
  } else if (nearestOwnedFeature) {
6680
6984
  // Fall back to location query with nearest marker
6681
6985
  newLocation = nearestOwnedFeature.id;
@@ -6716,7 +7020,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6716
7020
  isProcessingLocationUpdateRef.current = false;
6717
7021
  }, 0);
6718
7022
  // Note: selectedFeature and selectedMapPin are read from refs to avoid dependency cycles
6719
- }, [dispatch, locationData, positionQuerySupportBySource, targetLocation, sourceSelections, viewId, updateHighlightedFeaturesBySource]);
7023
+ }, [dispatch, locationData, positionQuerySupportBySource, radiusQuerySupportBySource, gridPointData, targetLocation, sourceSelections, viewId, updateHighlightedFeaturesBySource]);
6720
7024
  // Effect 2 was removed - it was redundant with effect 1 which handles all cases
6721
7025
  // (with or without selectedFeature, falls back to targetLocation if no nearby feature)
6722
7026
  useEffect(function () {
@@ -6759,7 +7063,8 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6759
7063
  return;
6760
7064
  }
6761
7065
  if (typeof selectedLocation === 'string') {
6762
- var foundFeature = serviceData.features.find(function (feature) {
7066
+ var _serviceData$features;
7067
+ var foundFeature = (_serviceData$features = serviceData.features) == null ? void 0 : _serviceData$features.find(function (feature) {
6763
7068
  return feature.id === selectedLocation;
6764
7069
  });
6765
7070
  if ((foundFeature == null ? void 0 : foundFeature.geometry.type) === 'Point') {
@@ -6796,29 +7101,35 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6796
7101
  });
6797
7102
  }
6798
7103
  if (featureNeedsUpdate) {
6799
- dispatch(genericActions.addFeatureSyncState({
6800
- panelId: viewId,
6801
- data: {
6802
- selectedFeatureId: selectedLocation
6803
- }
6804
- }));
7104
+ ownedFeatureLayerIds.forEach(function (layerId) {
7105
+ dispatch(layerActions.setSelectedFeatures({
7106
+ layerId: layerId,
7107
+ selectedFeatureIds: [selectedLocation]
7108
+ }));
7109
+ });
6805
7110
  }
6806
7111
  }
6807
7112
  } else if (Array.isArray(selectedLocation)) {
6808
7113
  var _lon = selectedLocation[0],
6809
7114
  _lat = selectedLocation[1];
6810
7115
  var _currentMapPin = selectedMapPinRef.current;
6811
- var needsUpdate = (_currentMapPin == null ? void 0 : _currentMapPin.lon) !== _lon || (_currentMapPin == null ? void 0 : _currentMapPin.lat) !== _lat;
6812
- if (needsUpdate) {
6813
- selectedMapIds.forEach(function (mapId) {
6814
- dispatch(mapActions.setMapPinLocation({
6815
- mapId: mapId,
6816
- mapPinLocation: {
6817
- lon: _lon,
6818
- lat: _lat
6819
- }
6820
- }));
6821
- });
7116
+ // When locationSelections was snapped to a grid point (differs from the clicked
7117
+ // position), keep the map pin at the clicked position so targetLocation stays at the
7118
+ // click and the legend can compute a meaningful distance to the data origin.
7119
+ var isGridPointSnap = targetLocationRef.current !== undefined && !isEqual(selectedLocation, targetLocationRef.current);
7120
+ if (!isGridPointSnap) {
7121
+ var needsUpdate = (_currentMapPin == null ? void 0 : _currentMapPin.lon) !== _lon || (_currentMapPin == null ? void 0 : _currentMapPin.lat) !== _lat;
7122
+ if (needsUpdate) {
7123
+ selectedMapIds.forEach(function (mapId) {
7124
+ dispatch(mapActions.setMapPinLocation({
7125
+ mapId: mapId,
7126
+ mapPinLocation: {
7127
+ lon: _lon,
7128
+ lat: _lat
7129
+ }
7130
+ }));
7131
+ });
7132
+ }
6822
7133
  }
6823
7134
  }
6824
7135
  });
@@ -6826,7 +7137,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6826
7137
  setTimeout(function () {
6827
7138
  isProcessingLocationUpdateRef.current = false;
6828
7139
  }, 0);
6829
- }, [locationData, locationSelections, sourceSelections, selectedMapIds, dispatch, viewId, updateHighlightedFeaturesBySource
7140
+ }, [locationData, locationSelections, sourceSelections, selectedMapIds, ownedFeatureLayerIds, dispatch, viewId, updateHighlightedFeaturesBySource
6830
7141
  // Note: highlightedFeaturesBySource intentionally excluded - we use highlightedFeaturesBySourceRef.current
6831
7142
  // inside this effect, and including the state would cause infinite loops since we call
6832
7143
  // updateHighlightedFeaturesBySource within the effect
@@ -6875,7 +7186,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6875
7186
  backgroundColor: 'geowebColors.background.surfaceApp',
6876
7187
  position: 'relative',
6877
7188
  containerType: 'inline-size',
6878
- containerName: 'soundings-container'
7189
+ containerName: SOUNDINGS_CONTAINER_NAME
6879
7190
  },
6880
7191
  children: [jsx(Box, {
6881
7192
  sx: {
@@ -6930,7 +7241,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6930
7241
  fontSize: "small"
6931
7242
  })
6932
7243
  })]
6933
- }), jsx(Box, {
7244
+ }), isLegendVisible ? jsx(Box, {
6934
7245
  sx: {
6935
7246
  display: 'flex',
6936
7247
  justifyContent: 'center',
@@ -6946,7 +7257,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6946
7257
  sourceResolvedInstanceIds: sourceResolvedInstanceIds,
6947
7258
  sourceSelections: sourceSelections
6948
7259
  })
6949
- }), jsx(Box, {
7260
+ }) : null, jsx(Box, {
6950
7261
  sx: {
6951
7262
  flex: 1,
6952
7263
  minHeight: 0,
@@ -6981,6 +7292,7 @@ var SoundingsConnect = function SoundingsConnect(_ref) {
6981
7292
  });
6982
7293
  };
6983
7294
 
7295
+ var defaultInstanceId = 'latest-0';
6984
7296
  var extractVerticalRange = function extractVerticalRange(collection, selectedInstanceId) {
6985
7297
  return getVerticalRangeFromInstance(collection.instances.find(function (instance) {
6986
7298
  return instance.id === selectedInstanceId;
@@ -7007,7 +7319,11 @@ var SoundingsSourceMenu = function SoundingsSourceMenu(_ref) {
7007
7319
  order = _ref$order === void 0 ? 0 : _ref$order,
7008
7320
  _ref$isLoading = _ref.isLoading,
7009
7321
  isLoading = _ref$isLoading === void 0 ? false : _ref$isLoading,
7010
- error = _ref.error;
7322
+ error = _ref.error,
7323
+ _ref$startSize = _ref.startSize,
7324
+ startSize = _ref$startSize === void 0 ? {
7325
+ width: 320
7326
+ } : _ref$startSize;
7011
7327
  var _useSoundingsTranslat = useSoundingsTranslation(),
7012
7328
  t = _useSoundingsTranslat.t;
7013
7329
  return jsxs(ToolContainerDraggable, {
@@ -7022,9 +7338,7 @@ var SoundingsSourceMenu = function SoundingsSourceMenu(_ref) {
7022
7338
  top: 40,
7023
7339
  left: 50
7024
7340
  },
7025
- startSize: {
7026
- width: 320
7027
- },
7341
+ startSize: startSize,
7028
7342
  title: t('sources'),
7029
7343
  children: [!!isLoading && jsx(LinearProgress, {
7030
7344
  sx: {
@@ -7039,14 +7353,16 @@ var SoundingsSourceMenu = function SoundingsSourceMenu(_ref) {
7039
7353
  pr: 1
7040
7354
  },
7041
7355
  children: soundingsCollections && soundingsCollections.length > 0 ? soundingsCollections.map(function (collection) {
7042
- var _collection$instances;
7043
7356
  var _ref2 = sourceSelections[collection.id] || {},
7044
7357
  isChecked = _ref2.isChecked,
7045
7358
  instanceId = _ref2.instanceId,
7046
7359
  url = _ref2.url;
7047
7360
  var sourceUrl = url || collection.url;
7048
7361
  var hasInstances = collection.instances.length > 0;
7049
- var selectedInstanceId = instanceId || ((_collection$instances = collection.instances[0]) == null ? void 0 : _collection$instances.id) || undefined;
7362
+ var selectedInstanceId = hasInstances ? instanceId || defaultInstanceId : undefined;
7363
+ var hasNonExistingInstanceId = selectedInstanceId && !referenceTimeUtils.isRelativeReferenceTime(selectedInstanceId) && !collection.instances.some(function (instance) {
7364
+ return instance.id === selectedInstanceId;
7365
+ });
7050
7366
  return jsxs(ListItem, {
7051
7367
  disablePadding: true,
7052
7368
  children: [jsx(ListItemIcon, {
@@ -7066,7 +7382,7 @@ var SoundingsSourceMenu = function SoundingsSourceMenu(_ref) {
7066
7382
  });
7067
7383
  }
7068
7384
  })
7069
- }), jsxs(StyledSelect, {
7385
+ }), jsxs(TooltipSelect, {
7070
7386
  additionalStyling: {
7071
7387
  width: 'calc(100% - 42px)'
7072
7388
  },
@@ -7081,31 +7397,54 @@ var SoundingsSourceMenu = function SoundingsSourceMenu(_ref) {
7081
7397
  });
7082
7398
  },
7083
7399
  renderValue: function renderValue(value) {
7084
- return jsx(Box, {
7400
+ return jsxs(Box, {
7085
7401
  sx: {
7086
- textOverflow: 'ellipsis',
7087
- overflow: 'hidden'
7402
+ display: 'flex',
7403
+ alignItems: 'center',
7404
+ width: '100%',
7405
+ gap: 1
7088
7406
  },
7089
- children: jsxs(Box, {
7407
+ children: [jsx(Box, {
7408
+ sx: {
7409
+ flex: '4 1 0',
7410
+ minWidth: 0,
7411
+ textOverflow: 'ellipsis',
7412
+ overflow: 'hidden',
7413
+ whiteSpace: 'nowrap'
7414
+ },
7415
+ children: collection.name
7416
+ }), jsxs(Box, {
7090
7417
  sx: {
7091
- display: 'flex'
7418
+ flex: '1 1 0',
7419
+ minWidth: 0,
7420
+ display: 'flex',
7421
+ alignItems: 'center',
7422
+ justifyContent: 'space-between',
7423
+ gap: 0.5
7092
7424
  },
7093
7425
  children: [jsx(Box, {
7094
7426
  sx: {
7095
- minWidth: 200
7096
- },
7097
- children: collection.name
7098
- }), jsx(Box, {
7099
- sx: {
7427
+ minWidth: 0,
7100
7428
  textOverflow: 'ellipsis',
7101
- overflow: 'hidden'
7429
+ overflow: 'hidden',
7430
+ whiteSpace: 'nowrap'
7102
7431
  },
7103
7432
  children: getSelectedDisplayValue(value)
7104
- })]
7105
- })
7433
+ }), hasNonExistingInstanceId ? jsx(Box, {
7434
+ sx: {
7435
+ '& .MuiSvgIcon-root': {
7436
+ fill: 'currentcolor!important'
7437
+ }
7438
+ },
7439
+ children: jsx(AlertIcon, {
7440
+ severity: "error"
7441
+ })
7442
+ }) : null]
7443
+ })]
7106
7444
  });
7107
7445
  },
7108
7446
  skipLocalStyling: false,
7447
+ tooltip: collection.name + ": " + getSelectedDisplayValue(selectedInstanceId || t('noInstancesAvailable')),
7109
7448
  value: selectedInstanceId || t('noInstancesAvailable'),
7110
7449
  children: [!hasInstances && jsx(MenuItem, {
7111
7450
  disabled: true,
@@ -7120,10 +7459,12 @@ var SoundingsSourceMenu = function SoundingsSourceMenu(_ref) {
7120
7459
  value: option.token,
7121
7460
  children: option.label
7122
7461
  }, option.token);
7123
- }), collection.instances.map(function (instance) {
7124
- return jsx(MenuItem, {
7462
+ }), referenceTimeUtils.extendCollectionInstances(collection.instances, selectedInstanceId).map(function (instance, currentIndex) {
7463
+ return jsxs(MenuItem, {
7125
7464
  value: instance.id,
7126
- children: getDisplayValue(instance.id)
7465
+ children: [getDisplayValue(instance.id), currentIndex === collection.instances.length && jsx(AlertIcon, {
7466
+ severity: "error"
7467
+ })]
7127
7468
  }, instance.id);
7128
7469
  })]
7129
7470
  })]
@@ -7199,6 +7540,28 @@ var SoundingsSourceDialogConnect = function SoundingsSourceDialogConnect(_ref) {
7199
7540
  data = _useGetCollectionsIns.data,
7200
7541
  isPending = _useGetCollectionsIns.isPending,
7201
7542
  error = _useGetCollectionsIns.error;
7543
+ // Update checked sources without an instance once instances finish loading.
7544
+ React.useEffect(function () {
7545
+ data == null || data.forEach(function (collection) {
7546
+ var currentSelection = sourceSelections[collection.id];
7547
+ if (!(currentSelection != null && currentSelection.isChecked) || currentSelection.instanceId || collection.instances.length === 0) {
7548
+ return;
7549
+ }
7550
+ var resolvedInstance = referenceTimeUtils.resolveReferenceTimeInstance(collection.instances, defaultInstanceId);
7551
+ if (!resolvedInstance) {
7552
+ return;
7553
+ }
7554
+ dispatch(soundingsActions.updateSelectedSource({
7555
+ sourceId: collection.id,
7556
+ value: {
7557
+ instanceId: defaultInstanceId,
7558
+ verticalRange: getVerticalRangeFromInstance(resolvedInstance)
7559
+ },
7560
+ viewId: viewId,
7561
+ origin: SoundingsActionOrigin.soundingsManager
7562
+ }));
7563
+ });
7564
+ }, [data, dispatch, sourceSelections, viewId]);
7202
7565
  return jsx(SoundingsSourceMenu, {
7203
7566
  error: error,
7204
7567
  handleClose: onCloseDialog,
@@ -7267,6 +7630,7 @@ var LocationsMenu = function LocationsMenu(_ref2) {
7267
7630
  setSearchFilter = _useState[1];
7268
7631
  var _useSoundingsTranslat = useSoundingsTranslation(),
7269
7632
  t = _useSoundingsTranslat.t;
7633
+ var listContainerRef = useRef(null);
7270
7634
  var filteredResults = useMemo(function () {
7271
7635
  return services == null ? void 0 : services.map(function (service) {
7272
7636
  var filteredFeatures = service.features ? service.features.filter(function (feature) {
@@ -7278,6 +7642,7 @@ var LocationsMenu = function LocationsMenu(_ref2) {
7278
7642
  });
7279
7643
  });
7280
7644
  }, [services, searchFilter]);
7645
+ var heightObserver = useResizeObserver(listContainerRef, filteredResults);
7281
7646
  var isChecked = function isChecked(sourceId, locationId) {
7282
7647
  return selectedLocations[sourceId] === locationId;
7283
7648
  };
@@ -7310,58 +7675,65 @@ var LocationsMenu = function LocationsMenu(_ref2) {
7310
7675
  margin: 1
7311
7676
  },
7312
7677
  children: t('locations-menu-warning')
7313
- }) : jsxs(Grid, {
7314
- container: true,
7315
- children: [jsx(Box, {
7316
- sx: {
7317
- width: '100%',
7318
- padding: 1,
7319
- position: 'sticky',
7320
- top: 0,
7321
- zIndex: 1,
7322
- backgroundColor: 'geowebColors.background.surfaceApp'
7323
- },
7324
- children: jsx(SearchField, {
7325
- searchFilter: searchFilter,
7326
- setSearchFilter: setSearchFilter,
7327
- small: true
7328
- })
7329
- }), filteredResults.map(function (service) {
7330
- return jsx(Fragment$1, {
7331
- children: jsx(CustomAccordion, {
7332
- amountOfItems: service.features.length,
7333
- defaultExpanded: false,
7334
- sx: {
7335
- width: '100%',
7336
- margin: '0 8px 2px 8px',
7337
- '.MuiAccordionDetails-root': {
7338
- padding: 0
7339
- },
7340
- '.MuiAccordionSummary-root': {
7341
- minHeight: '40px'
7342
- },
7343
- '.MuiAccordionSummary-content': {
7344
- paddingLeft: 3
7345
- }
7346
- },
7347
- title: service.name,
7348
- children: jsx(List, {
7349
- rowComponent: RowComponent,
7350
- rowCount: service.features.length,
7351
- rowHeight: 40,
7352
- rowProps: {
7353
- service: service,
7354
- isChecked: isChecked,
7355
- updateSelectedLocation: updateSelectedLocation
7678
+ }) : jsx(Box, {
7679
+ ref: listContainerRef,
7680
+ sx: {
7681
+ width: '100%',
7682
+ height: '100%'
7683
+ },
7684
+ children: jsxs(Grid, {
7685
+ container: true,
7686
+ children: [jsx(Box, {
7687
+ sx: {
7688
+ width: '100%',
7689
+ padding: 1,
7690
+ position: 'sticky',
7691
+ top: 0,
7692
+ zIndex: 1,
7693
+ backgroundColor: 'geowebColors.background.surfaceApp'
7694
+ },
7695
+ children: jsx(SearchField, {
7696
+ searchFilter: searchFilter,
7697
+ setSearchFilter: setSearchFilter,
7698
+ small: true
7699
+ })
7700
+ }), filteredResults.map(function (service) {
7701
+ return jsx(Fragment$1, {
7702
+ children: jsx(CustomAccordion, {
7703
+ amountOfItems: service.features.length,
7704
+ defaultExpanded: false,
7705
+ sx: {
7706
+ width: '100%',
7707
+ margin: '0 8px 2px 8px',
7708
+ '.MuiAccordionDetails-root': {
7709
+ padding: 0
7710
+ },
7711
+ '.MuiAccordionSummary-root': {
7712
+ minHeight: '40px'
7713
+ },
7714
+ '.MuiAccordionSummary-content': {
7715
+ paddingLeft: 3
7716
+ }
7356
7717
  },
7357
- style: {
7358
- height: 320,
7359
- width: '100%'
7360
- }
7718
+ title: service.name,
7719
+ children: jsx(List, {
7720
+ rowComponent: RowComponent,
7721
+ rowCount: service.features.length,
7722
+ rowHeight: 40,
7723
+ rowProps: {
7724
+ service: service,
7725
+ isChecked: isChecked,
7726
+ updateSelectedLocation: updateSelectedLocation
7727
+ },
7728
+ style: {
7729
+ height: heightObserver,
7730
+ width: '100%'
7731
+ }
7732
+ })
7361
7733
  })
7362
- })
7363
- }, service.id);
7364
- })]
7734
+ }, service.id);
7735
+ })]
7736
+ })
7365
7737
  })]
7366
7738
  });
7367
7739
  };
@@ -7401,10 +7773,10 @@ var LocationsMenuConnect = function LocationsMenuConnect(_ref) {
7401
7773
  dispatch(soundingsActions.setSoundingsCollections(soundingsCollections));
7402
7774
  }
7403
7775
  }, [dispatch, soundingsCollections]);
7404
- var _useGetCollectionLoca = useGetCollectionLocations(collections, isDialogOpen),
7405
- data = _useGetCollectionLoca.data,
7406
- isPending = _useGetCollectionLoca.isPending,
7407
- error = _useGetCollectionLoca.error;
7776
+ var _useGetLocations = useGetLocations(collections, isDialogOpen),
7777
+ data = _useGetLocations.data,
7778
+ isPending = _useGetLocations.isPending,
7779
+ error = _useGetLocations.error;
7408
7780
  var services = useMemo(function () {
7409
7781
  return (data == null ? void 0 : data.filter(function (service) {
7410
7782
  return selectedSourceIds.includes(service.id);