@opengeoweb/soundings 12.1.2 → 12.2.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.
Files changed (37) hide show
  1. package/index.esm.js +1136 -6
  2. package/package.json +13 -2
  3. package/src/index.d.ts +3 -1
  4. package/src/lib/api/edr-api/api.d.ts +6 -0
  5. package/src/lib/api/edr-api/crss-types.d.ts +26 -0
  6. package/src/lib/api/edr-api/hooks.d.ts +4 -0
  7. package/src/lib/api/edr-api/types.d.ts +118 -0
  8. package/src/lib/components/DrawUtils.d.ts +40 -0
  9. package/src/lib/components/EDRFetchAndParse.stories.d.ts +7 -0
  10. package/src/lib/components/Soundings.d.ts +14 -0
  11. package/src/lib/components/Soundings.spec.d.ts +1 -0
  12. package/src/lib/components/Soundings.stories.d.ts +7 -0
  13. package/src/lib/components/SoundingsConnect.d.ts +5 -0
  14. package/src/lib/components/SoundingsMenuBar/LocationsMenu.d.ts +11 -0
  15. package/src/lib/components/SoundingsMenuBar/LocationsMenuConnect.d.ts +6 -0
  16. package/src/lib/components/SoundingsMenuBar/MenuBar.d.ts +11 -0
  17. package/src/lib/components/SoundingsMenuBar/MenuBar.stories.d.ts +26 -0
  18. package/src/lib/components/SoundingsMenuBar/MenuBarConnect.d.ts +8 -0
  19. package/src/lib/components/SoundingsMenuBar/MenuBarConnect.spec.d.ts +1 -0
  20. package/src/lib/components/SoundingsSourceDialogConnect.d.ts +7 -0
  21. package/src/lib/components/SoundingsSourceMenu.d.ts +9 -0
  22. package/src/lib/components/StaticLines/drawDryAdiabats.d.ts +11 -0
  23. package/src/lib/components/StaticLines/drawIsobars.d.ts +10 -0
  24. package/src/lib/components/StaticLines/drawIsohumes.d.ts +10 -0
  25. package/src/lib/components/StaticLines/drawIsotherms.d.ts +10 -0
  26. package/src/lib/components/StaticLines/drawMoistAdiabats.d.ts +16 -0
  27. package/src/lib/components/StaticLines/utils.d.ts +15 -0
  28. package/src/lib/components/index.d.ts +3 -0
  29. package/src/lib/components/types.d.ts +5 -0
  30. package/src/lib/i18n.d.ts +6 -0
  31. package/src/lib/parser/coverages.d.ts +2 -0
  32. package/src/lib/utils/conversions.d.ts +11 -0
  33. package/src/lib/utils/soundingsLookUp.d.ts +8 -0
  34. package/src/lib/utils/testHandlers.d.ts +1 -0
  35. package/src/lib/components/Sounding.d.ts +0 -2
  36. package/src/lib/components/Sounding.spec.d.ts +0 -0
  37. package/src/lib/components/soundings.stories.d.ts +0 -9
package/index.esm.js CHANGED
@@ -1,10 +1,1140 @@
1
- import { jsx } from 'react/jsx-runtime';
1
+ import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
+ import React, { useRef, useEffect, useState, Fragment as Fragment$1, useMemo } from 'react';
3
+ import * as d3 from 'd3';
4
+ import { Card, List, ListItem, ListItemIcon, Checkbox, MenuItem, Box, ListItemText, ListSubheader, ListItemButton, Collapse, Button } from '@mui/material';
5
+ import { useSetupDialog, uiTypes, uiSelectors, uiActions } from '@opengeoweb/store';
6
+ import { ToolContainerDraggable, StyledSelect, dateUtils } from '@opengeoweb/shared';
7
+ import 'i18next';
8
+ import { useTranslation } from 'react-i18next';
9
+ import { getHeaders, handleResponse } from '@opengeoweb/api';
10
+ import { useQueries } from '@tanstack/react-query';
11
+ import { ChevronDown, Sources } from '@opengeoweb/theme';
12
+ import { useDispatch, useSelector } from 'react-redux';
2
13
 
3
- const Sounding = () => {
4
- // eslint-disable-next-line i18next/no-literal-string
5
- return jsx("div", {
6
- children: "This is an example component"
14
+ /* *
15
+ * Licensed under the Apache License, Version 2.0 (the "License");
16
+ * you may not use this file except in compliance with the License.
17
+ * You may obtain a copy of the License at
18
+ *
19
+ * http://www.apache.org/licenses/LICENSE-2.0
20
+ *
21
+ * Unless required by applicable law or agreed to in writing, software
22
+ * distributed under the License is distributed on an "AS IS" BASIS,
23
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
24
+ * See the License for the specific language governing permissions and
25
+ * limitations under the License.
26
+ *
27
+ * Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
28
+ * Copyright 2025 - Finnish Meteorological Institute (FMI)
29
+ * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
30
+ * */
31
+ const SOUNDING_COLORS = ['#4487f3'];
32
+ // vfx code
33
+ const initializeSvg = ({
34
+ svgRef,
35
+ width,
36
+ height,
37
+ margin
38
+ }) => {
39
+ const svg = d3.select(svgRef.current).attr('width', width + (margin.left + margin.right)).attr('height', height + (margin.top + margin.bottom)).attr('id', 'skewT').attr('data-testid', 'skewT').append('g').attr('transform', `translate(${margin.left + margin.right},${margin.top})`);
40
+ svg.append('clipPath').attr('id', 'clip-skewTd3').append('rect').attr('width', width).attr('height', height).attr('x', 0).attr('y', 0);
41
+ svg.append('rect').attr('height', height).attr('width', width).attr('fill', 'white').attr('opacity', 1).attr('id', 'skewTd3').attr('clip-path', 'url(#clip-skewTd3)');
42
+ return svg;
43
+ };
44
+ const drawAxes = ({
45
+ svg,
46
+ xScale,
47
+ yScale,
48
+ height,
49
+ width
50
+ }) => {
51
+ // Create x-axis with intervals
52
+ svg.append('g').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).ticks(10)).append('text').attr('fill', '#000').attr('x', `${width - 8}px`).attr('y', `${12}px`).attr('dy', '1em').attr('font-size', '12px').attr('text-anchor', 'middle').text('°C');
53
+ // Create y-axis with intervals
54
+ svg.append('g').call(d3.axisLeft(yScale).ticks(0, '')).append('text').attr('fill', '#000').attr('transform', 'translate(0)').attr('y', `${0}px`).attr('x', `${ -16}px`).attr('font-size', '12px').attr('dy', '1em').attr('text-anchor', 'middle').text('hPa');
55
+ };
56
+ const addLegends = ({
57
+ svg,
58
+ width
59
+ }) => {
60
+ // Add a legend for the temperature line
61
+ svg.append('text').attr('x', width - 160).attr('y', 20).attr('fill', SOUNDING_COLORS[0]).text('Temperature (°C)');
62
+ // Add a legend for the dewpoint line
63
+ svg.append('text').attr('x', width - 137).attr('y', 40).attr('fill', SOUNDING_COLORS[0]).text('Dewpoint (°C)');
64
+ };
65
+ const plotSounding = ({
66
+ temperatureValues,
67
+ pressureValues,
68
+ color,
69
+ lineStyle,
70
+ xScale,
71
+ yScale,
72
+ height,
73
+ svg
74
+ }) => {
75
+ // Map temp and pressure to pixels
76
+ const Px = d3.zip(pressureValues, temperatureValues).map(([pressure, temperature]) => [xScale(temperature), yScale(pressure)]);
77
+ // Skew/shear transformation for skew-t
78
+ const skewedPx = Px.map(([x, y]) => [x + height - y, y]);
79
+ const lineGenerator = d3.line();
80
+ const pathString = lineGenerator(skewedPx);
81
+ svg.append('path').attr('d', pathString).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-skewTd3)');
82
+ };
83
+
84
+ /* *
85
+ * Licensed under the Apache License, Version 2.0 (the "License");
86
+ * you may not use this file except in compliance with the License.
87
+ * You may obtain a copy of the License at
88
+ *
89
+ * http://www.apache.org/licenses/LICENSE-2.0
90
+ *
91
+ * Unless required by applicable law or agreed to in writing, software
92
+ * distributed under the License is distributed on an "AS IS" BASIS,
93
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
94
+ * See the License for the specific language governing permissions and
95
+ * limitations under the License.
96
+ *
97
+ * Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
98
+ * Copyright 2025 - Finnish Meteorological Institute (FMI)
99
+ * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
100
+ * */
101
+ const CP$1 = 1.03e3;
102
+ const Po = 1000.0;
103
+ const dryAdiabaticLapserate = (theta, pressure, temperature, dp) => {
104
+ const Tt = (theta + 273.15) * (Po / pressure) ** (-287 / CP$1) - 273.15;
105
+ const dt = Tt - temperature;
106
+ return [dp, dt];
107
+ };
108
+ const drawDryAdiabats = ({
109
+ staticPressureArray,
110
+ height,
111
+ xScale,
112
+ yScale,
113
+ svg,
114
+ dp
115
+ }) => {
116
+ for (let Tbase = -40; Tbase < 240; Tbase += 10) {
117
+ let Pnew = staticPressureArray[0];
118
+ let Tnew = Tbase;
119
+ const Px = [];
120
+ for (const pressure of staticPressureArray) {
121
+ const DPDT = dryAdiabaticLapserate(Tbase, Pnew, Tnew, dp);
122
+ Tnew += DPDT[1];
123
+ Pnew += DPDT[0];
124
+ Px.push([xScale(Tnew), yScale(pressure)]);
125
+ }
126
+ // Skew/shear transformation for skew-t
127
+ for (const PxElement of Px) {
128
+ PxElement[0] = PxElement[0] + height - PxElement[1];
129
+ }
130
+ const lineGenerator = d3.line();
131
+ const daPathString = lineGenerator(Px);
132
+ svg.append('path').attr('d', daPathString).style('fill', 'none').style('stroke-width', 1).style('stroke', 'green').style('opacity', 0.3).attr('clip-path', 'url(#clip-skewTd3)');
133
+ }
134
+ };
135
+
136
+ /* *
137
+ * Licensed under the Apache License, Version 2.0 (the "License");
138
+ * you may not use this file except in compliance with the License.
139
+ * You may obtain a copy of the License at
140
+ *
141
+ * http://www.apache.org/licenses/LICENSE-2.0
142
+ *
143
+ * Unless required by applicable law or agreed to in writing, software
144
+ * distributed under the License is distributed on an "AS IS" BASIS,
145
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
146
+ * See the License for the specific language governing permissions and
147
+ * limitations under the License.
148
+ *
149
+ * Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
150
+ * Copyright 2025 - Finnish Meteorological Institute (FMI)
151
+ * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
152
+ * */
153
+ const isobarValues = [200, 300, 400, 500, 700, 850, 925, 1000];
154
+ const drawIsobars = ({
155
+ minTemperature,
156
+ maxTemperature,
157
+ xScale,
158
+ yScale,
159
+ svg
160
+ }) => {
161
+ // Calculate X and Y coordinates for each pressure level
162
+ for (const pressureVal of isobarValues) {
163
+ const Px = [];
164
+ const tempBounds = [minTemperature, maxTemperature];
165
+ const Ppx = yScale(pressureVal);
166
+ for (const bound of tempBounds) {
167
+ Px.push([xScale(bound), yScale(pressureVal)]);
168
+ }
169
+ const lineGenerator = d3.line();
170
+ const isoPathString = lineGenerator(Px);
171
+ const strID = `${pressureVal}textId`;
172
+ svg.append('path').attr('id', strID).attr('d', isoPathString).style('fill', 'none').style('stroke-width', 1.5).style('stroke', '#B5B51F').style('stroke-opacity', 0.5).attr('clip-path', 'url(#clip-skewTd3)');
173
+ svg.append('text').text(pressureVal).attr('text-anchor', 'middle').attr('x', `${ -24}px`).attr('y', Ppx + 5) // Y-coordinate of the label
174
+ .attr('fill', '#000').attr('font-size', '12px');
175
+ }
176
+ };
177
+
178
+ /* *
179
+ * Licensed under the Apache License, Version 2.0 (the "License");
180
+ * you may not use this file except in compliance with the License.
181
+ * You may obtain a copy of the License at
182
+ *
183
+ * http://www.apache.org/licenses/LICENSE-2.0
184
+ *
185
+ * Unless required by applicable law or agreed to in writing, software
186
+ * distributed under the License is distributed on an "AS IS" BASIS,
187
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
188
+ * See the License for the specific language governing permissions and
189
+ * limitations under the License.
190
+ *
191
+ * Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
192
+ * Copyright 2025 - Finnish Meteorological Institute (FMI)
193
+ * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
194
+ * */
195
+ const isohumeValues = [0.001, 0.01, 0.1, 0.5, 1.0, 2.0, 5.0, 8.0, 12.0, 16.0, 20.0];
196
+ const drawIsohumes = ({
197
+ staticPressureArray,
198
+ height,
199
+ xScale,
200
+ yScale,
201
+ svg
202
+ }) => {
203
+ for (const isohumeVal of isohumeValues) {
204
+ const Px = [];
205
+ for (const pressureVal of staticPressureArray) {
206
+ const es = pressureVal * isohumeVal / (isohumeVal + 622.0);
207
+ const logthing = Math.log(es / 6.11);
208
+ const temp = 237.3 * logthing / (17.269 - logthing);
209
+ Px.push([xScale(temp), yScale(pressureVal)]);
210
+ }
211
+ // Skew/shear transformation for skew-t
212
+ for (const PxElement of Px) {
213
+ PxElement[0] = PxElement[0] + height - PxElement[1];
214
+ }
215
+ const lineGenerator = d3.line();
216
+ const pathStringQ = lineGenerator(Px);
217
+ const strID = `${isohumeVal}isohume`;
218
+ svg.append('path').attr('id', strID).attr('d', pathStringQ).style('fill', 'none').style('stroke-width', 1).style('stroke-dasharray', '15, 8').style('stroke', '#5267A9').style('stroke-opacity', 0.5).attr('clip-path', 'url(#clip-skewTd3)');
219
+ svg.append('text').text(isohumeVal).attr('text-anchor', 'middle').attr('x', `${Px[0][0] + 20}px`).attr('y', `${height - 10}px`).attr('fill', '#5267A9').attr('font-size', '10px');
220
+ }
221
+ };
222
+
223
+ /* *
224
+ * Licensed under the Apache License, Version 2.0 (the "License");
225
+ * you may not use this file except in compliance with the License.
226
+ * You may obtain a copy of the License at
227
+ *
228
+ * http://www.apache.org/licenses/LICENSE-2.0
229
+ *
230
+ * Unless required by applicable law or agreed to in writing, software
231
+ * distributed under the License is distributed on an "AS IS" BASIS,
232
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
233
+ * See the License for the specific language governing permissions and
234
+ * limitations under the License.
235
+ *
236
+ * Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
237
+ * Copyright 2025 - Finnish Meteorological Institute (FMI)
238
+ * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
239
+ * */
240
+ const drawIsotherms = ({
241
+ staticPressureArray,
242
+ height,
243
+ xScale,
244
+ yScale,
245
+ svg
246
+ }) => {
247
+ // lines of constant temperature (10 degree interval from -80...+40C)
248
+ for (let temp = -80; temp <= 40; temp += 10) {
249
+ const points = [];
250
+ for (const pressure of staticPressureArray) {
251
+ const Ppx = yScale(pressure);
252
+ const Tpx = xScale(temp);
253
+ const skewedX = Tpx + (height - Ppx);
254
+ points.push([skewedX, Ppx]);
255
+ }
256
+ const lineGenerator = d3.line();
257
+ const pathString = lineGenerator(points);
258
+ svg.append('path').attr('d', pathString).style('fill', 'none').style('stroke-width', 1).style('stroke', 'orange').style('stroke-opacity', 0.5).attr('clip-path', 'url(#clip-skewTd3)');
259
+ }
260
+ };
261
+
262
+ /* *
263
+ * Licensed under the Apache License, Version 2.0 (the "License");
264
+ * you may not use this file except in compliance with the License.
265
+ * You may obtain a copy of the License at
266
+ *
267
+ * http://www.apache.org/licenses/LICENSE-2.0
268
+ *
269
+ * Unless required by applicable law or agreed to in writing, software
270
+ * distributed under the License is distributed on an "AS IS" BASIS,
271
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
272
+ * See the License for the specific language governing permissions and
273
+ * limitations under the License.
274
+ *
275
+ * Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
276
+ * Copyright 2025 - Finnish Meteorological Institute (FMI)
277
+ * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
278
+ * */
279
+ const CP = 1.03e3;
280
+ const KELVIN = 273.15;
281
+ const L_CONST = 2.5e6;
282
+ const RD = 287.0;
283
+ const RV = 461.0;
284
+ const moistAdiabaticLapserate = (minTemp, pressure, temperature, dp) => {
285
+ const kelvin = temperature + KELVIN;
286
+ const lsbc = L_CONST / RV * (1.0 / KELVIN - 1.0 / kelvin);
287
+ const rw = 6.11 * Math.exp(lsbc) * (0.622 / pressure);
288
+ const lrwbt = L_CONST * rw / (RD * kelvin);
289
+ const nume = RD * kelvin / (CP * pressure) * (1.0 + lrwbt);
290
+ const deno = 1.0 + lrwbt * (0.622 * L_CONST / (CP * kelvin));
291
+ const gradi = nume / deno;
292
+ const dt = dp * gradi;
293
+ return [dp, dt];
294
+ };
295
+ const drawMoistAdiabats = ({
296
+ staticPressureArray,
297
+ minTemperature,
298
+ maxTemperature,
299
+ minPressure,
300
+ maxPressure,
301
+ width,
302
+ height,
303
+ xScale,
304
+ yScale,
305
+ svg,
306
+ dp
307
+ }) => {
308
+ /* We need the moist adiabat to cross the isotherm at 1000hPa
309
+ Therefore we start the calculations from 1000hPa and work upwards (towards 0)
310
+ then draw a separate line starting from 1000hPa working down (towards defined maxPressure) */
311
+ for (let Tbase = -40; Tbase < 240; Tbase += 10) {
312
+ let Pnew = 1000;
313
+ let Tnew = Tbase;
314
+ const Px = [];
315
+ // First calculate position of moist adiabat at a pressure of 1000hPa
316
+ const Temp = Tnew + -1 * minTemperature;
317
+ const Tpx1000 = width * Temp / (maxTemperature - minTemperature);
318
+ const Ppx1000 = height * (Math.log(Pnew) - Math.log(minPressure)) / (Math.log(maxPressure) - Math.log(minPressure));
319
+ Px.push([Tpx1000, Ppx1000]);
320
+ // Then calculate the rest of the moist adiabat for pressures <1000hPa
321
+ for (const pressure of staticPressureArray) {
322
+ if (pressure < 1000) {
323
+ const DPDT = moistAdiabaticLapserate(-80, Pnew, Tnew, dp);
324
+ Tnew += DPDT[1];
325
+ Pnew += DPDT[0];
326
+ Px.push([xScale(Tnew), yScale(pressure)]);
327
+ }
328
+ }
329
+ for (const PxElement of Px) {
330
+ PxElement[0] = PxElement[0] + height - PxElement[1];
331
+ }
332
+ const lineGenerator = d3.line();
333
+ const maPathString = lineGenerator(Px);
334
+ // Draw this first section of the line
335
+ svg.append('path').attr('d', maPathString).style('fill', 'none').style('stroke-width', 1).style('stroke', 'purple').style('stroke-opacity', 0.5).attr('clip-path', 'url(#clip-skewTd3)');
336
+ // Now work backwards from 1000hPa to calculate most adiabat until the bottom of the plot
337
+ const PxBottom = [];
338
+ PxBottom.push([Tpx1000, Ppx1000]);
339
+ let PnewBottom = 1000;
340
+ let TnewBottom = Tbase;
341
+ const startIndex = staticPressureArray.findIndex(value => value === 1000);
342
+ for (let i = startIndex - 1; i >= 0; i -= 1) {
343
+ const DPDT = moistAdiabaticLapserate(-80, PnewBottom, TnewBottom, 5);
344
+ TnewBottom += DPDT[1];
345
+ PnewBottom += DPDT[0];
346
+ PxBottom.push([xScale(TnewBottom), yScale(staticPressureArray[i])]);
347
+ }
348
+ // Skew/shear transformation for skew-t
349
+ for (const PxElement of PxBottom) {
350
+ PxElement[0] = PxElement[0] + height - PxElement[1];
351
+ }
352
+ const maPathStringBottom = lineGenerator(PxBottom);
353
+ svg.append('path').attr('d', maPathStringBottom).style('fill', 'none').style('stroke-width', 1).style('stroke', 'purple').style('stroke-opacity', 0.5).attr('clip-path', 'url(#clip-skewTd3)');
354
+ }
355
+ };
356
+
357
+ /* *
358
+ * Licensed under the Apache License, Version 2.0 (the "License");
359
+ * you may not use this file except in compliance with the License.
360
+ * You may obtain a copy of the License at
361
+ *
362
+ * http://www.apache.org/licenses/LICENSE-2.0
363
+ *
364
+ * Unless required by applicable law or agreed to in writing, software
365
+ * distributed under the License is distributed on an "AS IS" BASIS,
366
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
367
+ * See the License for the specific language governing permissions and
368
+ * limitations under the License.
369
+ *
370
+ * Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
371
+ * Copyright 2025 - Finnish Meteorological Institute (FMI)
372
+ * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
373
+ * */
374
+ const drawStaticLines = ({
375
+ dp,
376
+ staticPressureArray,
377
+ minTemperature,
378
+ maxTemperature,
379
+ height,
380
+ width,
381
+ xScale,
382
+ yScale,
383
+ minPressure,
384
+ maxPressure,
385
+ svg
386
+ }) => {
387
+ drawIsobars({
388
+ minTemperature,
389
+ maxTemperature,
390
+ xScale,
391
+ yScale,
392
+ svg
393
+ });
394
+ drawIsotherms({
395
+ staticPressureArray,
396
+ height,
397
+ xScale,
398
+ yScale,
399
+ svg
400
+ });
401
+ drawIsohumes({
402
+ staticPressureArray,
403
+ height,
404
+ xScale,
405
+ yScale,
406
+ svg
407
+ });
408
+ drawDryAdiabats({
409
+ staticPressureArray,
410
+ height,
411
+ xScale,
412
+ yScale,
413
+ svg,
414
+ dp
7
415
  });
416
+ drawMoistAdiabats({
417
+ staticPressureArray,
418
+ minTemperature,
419
+ maxTemperature,
420
+ minPressure,
421
+ maxPressure,
422
+ width,
423
+ height,
424
+ xScale,
425
+ yScale,
426
+ svg,
427
+ dp
428
+ });
429
+ };
430
+
431
+ const Soundings = ({
432
+ data: {
433
+ features
434
+ },
435
+ svgDimensions
436
+ // tooltip
437
+ }) => {
438
+ const svgRef = useRef(null);
439
+ const svgDimensionsOptions = {
440
+ width: svgDimensions?.width || 812,
441
+ height: svgDimensions?.height || 802,
442
+ margin: {
443
+ top: svgDimensions?.margin?.top || 12,
444
+ right: svgDimensions?.margin?.right || 20,
445
+ bottom: svgDimensions?.margin?.bottom || 25,
446
+ left: svgDimensions?.margin?.left || 20
447
+ }
448
+ };
449
+ // Set SVG dimensions
450
+ const margin = {
451
+ top: svgDimensionsOptions?.margin?.top,
452
+ right: svgDimensionsOptions?.margin?.right,
453
+ bottom: svgDimensionsOptions?.margin?.bottom,
454
+ left: svgDimensionsOptions?.margin?.left
455
+ };
456
+ const baseWidth = svgDimensionsOptions?.width;
457
+ const baseHeight = svgDimensionsOptions?.height;
458
+ const width = baseWidth - (margin.left + margin.right);
459
+ const height = baseHeight - (margin.top + margin.bottom);
460
+ useEffect(() => {
461
+ if (!svgRef.current) {
462
+ return;
463
+ }
464
+ // Define svg
465
+ const svg = initializeSvg({
466
+ svgRef,
467
+ width,
468
+ height,
469
+ margin
470
+ });
471
+ // Define pressure and temperature values for static lines
472
+ const maxPressure = 1050;
473
+ const minPressure = 150;
474
+ const staticPressureArray = [maxPressure];
475
+ const dp = -5;
476
+ const minTemperature = -40;
477
+ const maxTemperature = 40;
478
+ let pressure = maxPressure;
479
+ while (pressure > minPressure) {
480
+ pressure += dp;
481
+ staticPressureArray.push(pressure);
482
+ }
483
+ // Set scales for temperature and pressure
484
+ const xScale = d3.scaleLinear().domain([minTemperature, maxTemperature]).range([0, width]);
485
+ const yScale = d3.scaleLog().domain([maxPressure, minPressure]).range([height, 0]);
486
+ // Add axis'
487
+ drawAxes({
488
+ svg,
489
+ xScale,
490
+ yScale,
491
+ height,
492
+ width
493
+ });
494
+ // Draw static lines
495
+ drawStaticLines({
496
+ dp,
497
+ staticPressureArray,
498
+ minTemperature,
499
+ maxTemperature,
500
+ height,
501
+ width,
502
+ xScale,
503
+ yScale,
504
+ minPressure,
505
+ maxPressure,
506
+ svg
507
+ });
508
+ // Add legends
509
+ addLegends({
510
+ svg,
511
+ width
512
+ });
513
+ const temperatureValues = features.map(feature => feature.properties.temp);
514
+ const pressureValues = features.map(feature => feature.properties.pressure);
515
+ // Draw Temperature
516
+ plotSounding({
517
+ temperatureValues,
518
+ pressureValues,
519
+ color: SOUNDING_COLORS[0],
520
+ lineStyle: 'none',
521
+ xScale,
522
+ yScale,
523
+ height,
524
+ svg
525
+ });
526
+ const dewpointValues = features.map(feature => feature.properties.dewpoint);
527
+ // Dewpoint
528
+ plotSounding({
529
+ temperatureValues: dewpointValues,
530
+ color: SOUNDING_COLORS[0],
531
+ pressureValues,
532
+ lineStyle: '8, 2',
533
+ xScale,
534
+ yScale,
535
+ height,
536
+ svg
537
+ });
538
+ // eslint-disable-next-line consistent-return
539
+ return () => {
540
+ svg.selectAll('*').remove();
541
+ // svg.on('mousemove', null)
542
+ };
543
+ // eslint-disable-next-line react-hooks/exhaustive-deps
544
+ }, []);
545
+ return jsx(Card, {
546
+ children: jsx("svg", {
547
+ ref: svgRef
548
+ })
549
+ });
550
+ };
551
+
552
+ /* *
553
+ * Licensed under the Apache License, Version 2.0 (the "License");
554
+ * you may not use this file except in compliance with the License.
555
+ * You may obtain a copy of the License at
556
+ *
557
+ * http://www.apache.org/licenses/LICENSE-2.0
558
+ *
559
+ * Unless required by applicable law or agreed to in writing, software
560
+ * distributed under the License is distributed on an "AS IS" BASIS,
561
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
562
+ * See the License for the specific language governing permissions and
563
+ * limitations under the License.
564
+ *
565
+ * Copyright 2023 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
566
+ * Copyright 2023 - Finnish Meteorological Institute (FMI)
567
+ * Copyright 2024 - The Norwegian Meteorological Institute (MET Norway)
568
+ * */
569
+ const SOUNDINGS_NAMESPACE = 'soundings';
570
+ const useSoundingsTranslation = () => useTranslation(SOUNDINGS_NAMESPACE);
571
+
572
+ const SoundingsSourceMenu = ({
573
+ soundingsCollections,
574
+ isOpen = false,
575
+ handleClose
576
+ }) => {
577
+ const {
578
+ t
579
+ } = useSoundingsTranslation();
580
+ const [currentSelection, setCurrentSelection] = React.useState({});
581
+ React.useEffect(() => {
582
+ const initialSelection = {};
583
+ soundingsCollections?.forEach(collection => {
584
+ const initialInstanceId = collection.instances.length > 0 ? collection.instances[0].id : undefined;
585
+ initialSelection[collection.id] = {
586
+ isChecked: false,
587
+ selectedInstanceId: initialInstanceId
588
+ };
589
+ });
590
+ setCurrentSelection(initialSelection);
591
+ }, [soundingsCollections]);
592
+ // TODO move to redux
593
+ const handleSelectionChange = (collectionId, update) => {
594
+ setCurrentSelection(prev => {
595
+ const currentCollectionState = prev[collectionId] || {};
596
+ return {
597
+ ...prev,
598
+ [collectionId]: {
599
+ ...currentCollectionState,
600
+ ...update
601
+ }
602
+ };
603
+ });
604
+ };
605
+ const getDisplayValue = (collection, instanceId) => {
606
+ if (!instanceId) {
607
+ return jsx(Fragment, {
608
+ children: t('noInstancesAvailable')
609
+ });
610
+ }
611
+ const instanceIndex = collection.instances.findIndex(i => i.id === instanceId);
612
+ if (instanceIndex === 0) {
613
+ return jsxs(Box, {
614
+ display: "flex",
615
+ children: [jsx(Box, {
616
+ sx: {
617
+ minWidth: 200
618
+ },
619
+ children: collection.name
620
+ }), jsx(Box, {
621
+ sx: {
622
+ textOverflow: 'ellipsis',
623
+ overflow: 'hidden'
624
+ },
625
+ children: t('latest')
626
+ })]
627
+ });
628
+ }
629
+ return jsxs(Box, {
630
+ display: "flex",
631
+ children: [jsx(Box, {
632
+ sx: {
633
+ minWidth: 200
634
+ },
635
+ children: collection.name
636
+ }), jsx(Box, {
637
+ sx: {
638
+ textOverflow: 'ellipsis',
639
+ overflow: 'hidden'
640
+ },
641
+ children: dateUtils.dateToIsoString(dateUtils.isoStringToDate(instanceId))
642
+ })]
643
+ });
644
+ };
645
+ return jsx(ToolContainerDraggable, {
646
+ startSize: {
647
+ width: 320
648
+ },
649
+ minWidth: 300,
650
+ title: t('sources'),
651
+ isOpen: isOpen,
652
+ onClose: () => handleClose(),
653
+ bounds: "parent",
654
+ children: jsx(List, {
655
+ sx: {
656
+ pr: 1
657
+ },
658
+ children: soundingsCollections && soundingsCollections.length > 0 ? soundingsCollections.map(collection => {
659
+ const currentCollectionState = currentSelection[collection.id];
660
+ const isChecked = currentCollectionState?.isChecked ?? false;
661
+ const selectedInstanceId = currentCollectionState?.selectedInstanceId ?? ''; // Use empty string for Select value if undefined
662
+ const hasInstances = collection.instances.length > 0;
663
+ return jsxs(ListItem, {
664
+ disablePadding: true,
665
+ children: [jsx(ListItemIcon, {
666
+ sx: {
667
+ minWidth: 42
668
+ },
669
+ children: jsx(Checkbox, {
670
+ checked: isChecked,
671
+ onChange: event => handleSelectionChange(collection.id, {
672
+ isChecked: event.target.checked
673
+ }),
674
+ disableRipple: true
675
+ })
676
+ }), jsxs(StyledSelect, {
677
+ isEnabled: true,
678
+ skipLocalStyling: false,
679
+ value: selectedInstanceId,
680
+ onChange: event => handleSelectionChange(collection.id, {
681
+ selectedInstanceId: event.target.value
682
+ }),
683
+ renderValue: value => jsx(Box, {
684
+ sx: {
685
+ textOverflow: 'ellipsis',
686
+ overflow: 'hidden'
687
+ },
688
+ children: getDisplayValue(collection, value)
689
+ }),
690
+ children: [!hasInstances && jsx(MenuItem, {
691
+ value: "",
692
+ disabled: true,
693
+ sx: {
694
+ fontSize: 12
695
+ },
696
+ children: t('noInstancesAvailable')
697
+ }), collection.instances.map((instance, index) => jsx(MenuItem, {
698
+ value: instance.id,
699
+ children: index === 0 ? t('latest') : dateUtils.dateToIsoString(dateUtils.isoStringToDate(instance.id)) || instance.id
700
+ }, instance.id))]
701
+ })]
702
+ }, collection.id);
703
+ }) : jsx(ListItem, {
704
+ children: jsx(ListItemText, {
705
+ primary: t('noServicesFound'),
706
+ sx: {
707
+ pl: 4
708
+ }
709
+ })
710
+ })
711
+ })
712
+ });
713
+ };
714
+
715
+ /* *
716
+ * Licensed under the Apache License, Version 2.0 (the "License");
717
+ * you may not use this file except in compliance with the License.
718
+ * You may obtain a copy of the License at
719
+ *
720
+ * http://www.apache.org/licenses/LICENSE-2.0
721
+ *
722
+ * Unless required by applicable law or agreed to in writing, software
723
+ * distributed under the License is distributed on an "AS IS" BASIS,
724
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
725
+ * See the License for the specific language governing permissions and
726
+ * limitations under the License.
727
+ *
728
+ * Copyright 2025 - Koninklijk Nederlands Meteorologisch Instituut (KNMI)
729
+ * Copyright 2025 - Finnish Meteorological Institute (FMI)
730
+ * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
731
+ * */
732
+ const BASE_URL = 'https://opendata.fmi.fi/edr';
733
+ const getCollectionLocations = collectionId => fetch(`${BASE_URL}/collections/${collectionId}/locations`, {
734
+ headers: getHeaders()
735
+ }).then(handleResponse);
736
+ const getCollectionInstance = (baseURL, collectionId) => fetch(`${baseURL}/collections/${collectionId}/instances`, {
737
+ headers: getHeaders()
738
+ }).then(handleResponse);
739
+
740
+ const SoundingsSourceDialogConnect = ({
741
+ soundingsCollections
742
+ }) => {
743
+ const [soundingsCollectionsWithInstances, setSoundingsCollectionsWithInstances] = React.useState();
744
+ const {
745
+ isDialogOpen,
746
+ onCloseDialog
747
+ } = useSetupDialog(uiTypes.DialogTypes.SoundingsSources);
748
+ React.useEffect(() => {
749
+ if (soundingsCollections && isDialogOpen) {
750
+ try {
751
+ void Promise.all(soundingsCollections.map(async collection => {
752
+ const data = await getCollectionInstance(collection.url, collection.id);
753
+ return {
754
+ ...collection,
755
+ instances: data.instances.reverse()
756
+ }; // Reverse to get latest first
757
+ })).then(results => setSoundingsCollectionsWithInstances(results));
758
+ } catch (error) {
759
+ console.error('Error fetching soundings instances:', error);
760
+ }
761
+ }
762
+ }, [soundingsCollections, isDialogOpen]);
763
+ return jsx(SoundingsSourceMenu, {
764
+ soundingsCollections: soundingsCollectionsWithInstances,
765
+ isOpen: isDialogOpen,
766
+ handleClose: () => onCloseDialog()
767
+ });
768
+ };
769
+
770
+ const LocationsMenu = ({
771
+ services,
772
+ isOpen,
773
+ onClose
774
+ }) => {
775
+ const [collapseMap, setCollapseMap] = useState({});
776
+ const {
777
+ t
778
+ } = useSoundingsTranslation();
779
+ return jsx(ToolContainerDraggable, {
780
+ startSize: {
781
+ width: 320,
782
+ height: 500
783
+ },
784
+ title: t('locations'),
785
+ headerSize: "medium",
786
+ isOpen: isOpen,
787
+ onClose: () => onClose(),
788
+ bounds: "parent",
789
+ children: jsx(List, {
790
+ disablePadding: true,
791
+ sx: {
792
+ maxHeight: '100%',
793
+ overflowY: 'auto'
794
+ },
795
+ children: services.map(service => jsxs(Fragment$1, {
796
+ children: [jsx(ListSubheader, {
797
+ disableGutters: true,
798
+ sx: {
799
+ borderBottom: '1px solid'
800
+ },
801
+ children: jsxs(ListItemButton, {
802
+ onClick: () => setCollapseMap({
803
+ ...collapseMap,
804
+ [service.id]: !collapseMap[service.id]
805
+ }),
806
+ children: [jsx(ListItemText, {
807
+ children: service.id
808
+ }), jsx(ListItemIcon, {
809
+ children: jsx(ChevronDown, {})
810
+ })]
811
+ })
812
+ }), jsx(Collapse, {
813
+ in: collapseMap[service.id],
814
+ children: jsx(List, {
815
+ component: "div",
816
+ disablePadding: true,
817
+ sx: {
818
+ overflowY: 'auto',
819
+ overflow: 'hidden'
820
+ },
821
+ children: service.features?.map(location => jsx(ListItem, {
822
+ sx: {
823
+ pl: 4
824
+ },
825
+ children: location.properties.name
826
+ }, `${service.id}-${location.id}`))
827
+ })
828
+ })]
829
+ }, service.id))
830
+ })
831
+ });
832
+ };
833
+
834
+ const LocationsMenuConnect = ({
835
+ soundingsCollections
836
+ }) => {
837
+ const {
838
+ isDialogOpen,
839
+ onCloseDialog
840
+ } = useSetupDialog(uiTypes.DialogTypes.soundingsLocations);
841
+ const edrQueries = useMemo(() => soundingsCollections.map(collection => ({
842
+ queryKey: ['edr-locations', collection.id],
843
+ queryFn: async () => {
844
+ const locations = await getCollectionLocations(collection.id);
845
+ return {
846
+ ...locations,
847
+ id: collection.id
848
+ };
849
+ }
850
+ })), [soundingsCollections]);
851
+ const services = useQueries({
852
+ queries: edrQueries,
853
+ combine: results => results.map(result => result.data).filter(result => !!result)
854
+ });
855
+ return jsx(LocationsMenu, {
856
+ isOpen: isDialogOpen,
857
+ onClose: onCloseDialog,
858
+ services: services
859
+ });
860
+ };
861
+
862
+ var SoundingFeatures = [
863
+ {
864
+ type: "Feature",
865
+ geometry: {
866
+ type: "Point",
867
+ coordinates: [
868
+ 24.9727,
869
+ 60.3294
870
+ ]
871
+ },
872
+ properties: {
873
+ time: "2025-02-05T00:00:00Z",
874
+ temp: -4.74961,
875
+ dewpoint: -5.646749999999998,
876
+ pressure: 1000,
877
+ windSpeed: 9.291457267156751,
878
+ windDirection: -18.803465916723095
879
+ }
880
+ },
881
+ {
882
+ type: "Feature",
883
+ geometry: {
884
+ type: "Point",
885
+ coordinates: [
886
+ 24.9727,
887
+ 60.3294
888
+ ]
889
+ },
890
+ properties: {
891
+ time: "2025-02-05T00:00:00Z",
892
+ temp: -5.72214,
893
+ dewpoint: -5.740760000000001,
894
+ pressure: 925,
895
+ windSpeed: 11.482972536081412,
896
+ windDirection: 18.797638010822038
897
+ }
898
+ },
899
+ {
900
+ type: "Feature",
901
+ geometry: {
902
+ type: "Point",
903
+ coordinates: [
904
+ 24.9727,
905
+ 60.3294
906
+ ]
907
+ },
908
+ properties: {
909
+ time: "2025-02-05T00:00:00Z",
910
+ temp: -5.94461,
911
+ dewpoint: -6.284489999999999,
912
+ pressure: 850,
913
+ windSpeed: 10.355155067433804,
914
+ windDirection: 46.95919904362452
915
+ }
916
+ },
917
+ {
918
+ type: "Feature",
919
+ geometry: {
920
+ type: "Point",
921
+ coordinates: [
922
+ 24.9727,
923
+ 60.3294
924
+ ]
925
+ },
926
+ properties: {
927
+ time: "2025-02-05T00:00:00Z",
928
+ temp: -7.85121,
929
+ dewpoint: -7.903229999999999,
930
+ pressure: 700,
931
+ windSpeed: 10.216260991987234,
932
+ windDirection: 125.33681135255785
933
+ }
934
+ },
935
+ {
936
+ type: "Feature",
937
+ geometry: {
938
+ type: "Point",
939
+ coordinates: [
940
+ 24.9727,
941
+ 60.3294
942
+ ]
943
+ },
944
+ properties: {
945
+ time: "2025-02-05T00:00:00Z",
946
+ temp: -20.9667,
947
+ dewpoint: -22.12218,
948
+ pressure: 500,
949
+ windSpeed: 14.222681508365433,
950
+ windDirection: 120.16238041101973
951
+ }
952
+ },
953
+ {
954
+ type: "Feature",
955
+ geometry: {
956
+ type: "Point",
957
+ coordinates: [
958
+ 24.9727,
959
+ 60.3294
960
+ ]
961
+ },
962
+ properties: {
963
+ time: "2025-02-05T00:00:00Z",
964
+ temp: -48.8678,
965
+ dewpoint: -60.802800000000005,
966
+ pressure: 300,
967
+ windSpeed: 23.436991331013456,
968
+ windDirection: 128.0547684118523
969
+ }
970
+ },
971
+ {
972
+ type: "Feature",
973
+ geometry: {
974
+ type: "Point",
975
+ coordinates: [
976
+ 24.9727,
977
+ 60.3294
978
+ ]
979
+ },
980
+ properties: {
981
+ time: "2025-02-05T00:00:00Z",
982
+ temp: -58.3,
983
+ dewpoint: -69.68567999999999,
984
+ pressure: 250,
985
+ windSpeed: 28.155356371568097,
986
+ windDirection: 133.50317473958455
987
+ }
988
+ },
989
+ {
990
+ type: "Feature",
991
+ geometry: {
992
+ type: "Point",
993
+ coordinates: [
994
+ 24.9727,
995
+ 60.3294
996
+ ]
997
+ },
998
+ properties: {
999
+ time: "2025-02-05T00:00:00Z",
1000
+ temp: -68.6166,
1001
+ dewpoint: -71.9641,
1002
+ pressure: 200,
1003
+ windSpeed: 24.833678713593763,
1004
+ windDirection: 137.492458365928
1005
+ }
1006
+ },
1007
+ {
1008
+ type: "Feature",
1009
+ geometry: {
1010
+ type: "Point",
1011
+ coordinates: [
1012
+ 24.9727,
1013
+ 60.3294
1014
+ ]
1015
+ },
1016
+ properties: {
1017
+ time: "2025-02-05T00:00:00Z",
1018
+ temp: -69.4935,
1019
+ dewpoint: -87.827772,
1020
+ pressure: 50,
1021
+ windSpeed: 35.1450619746217,
1022
+ windDirection: 129.8915278264372
1023
+ }
1024
+ }
1025
+ ];
1026
+
1027
+ var MenuOptions;
1028
+ (function (MenuOptions) {
1029
+ MenuOptions["sources"] = "sources";
1030
+ MenuOptions["locations"] = "locations";
1031
+ })(MenuOptions || (MenuOptions = {}));
1032
+ const SoundingsMenuBar = ({
1033
+ handleClick,
1034
+ isOpen
1035
+ }) => {
1036
+ const {
1037
+ t
1038
+ } = useSoundingsTranslation();
1039
+ return jsxs(Box, {
1040
+ display: "flex",
1041
+ gap: 1,
1042
+ children: [jsx(Button, {
1043
+ variant: isOpen[MenuOptions.sources] ? 'primary' : 'tertiary',
1044
+ disableRipple: true,
1045
+ "aria-pressed": isOpen[MenuOptions.sources],
1046
+ "data-testid": "soundingsSourcesButton",
1047
+ onClick: () => handleClick(MenuOptions.sources),
1048
+ startIcon: jsx(Sources, {}),
1049
+ size: "small",
1050
+ children: t('sources')
1051
+ }), jsx(Button, {
1052
+ variant: isOpen[MenuOptions.locations] ? 'primary' : 'tertiary',
1053
+ disableRipple: true,
1054
+ "data-testid": "soundingsLocationsButton",
1055
+ "aria-pressed": isOpen[MenuOptions.locations],
1056
+ onClick: () => handleClick(MenuOptions.locations),
1057
+ size: "small",
1058
+ children: t('locations')
1059
+ })]
1060
+ });
1061
+ };
1062
+
1063
+ const MenuBarConnect = ({
1064
+ viewId,
1065
+ source
1066
+ }) => {
1067
+ const dispatch = useDispatch();
1068
+ const currentActiveMapId = useSelector(store => uiSelectors.getDialogMapId(store, uiTypes.DialogTypes.SoundingsSources));
1069
+ const isSourcesOpenInStore = useSelector(store => uiSelectors.getisDialogOpen(store, uiTypes.DialogTypes.SoundingsSources));
1070
+ const isLocationsOpenInStore = useSelector(store => uiSelectors.getisDialogOpen(store, uiTypes.DialogTypes.soundingsLocations));
1071
+ const dispatchActiveMapId = React.useCallback((dialogType, isOpen) => void dispatch(uiActions.setActiveMapIdForDialog({
1072
+ type: dialogType,
1073
+ mapId: viewId,
1074
+ setOpen: currentActiveMapId !== viewId ? true : !isOpen,
1075
+ source
1076
+ })), [dispatch, currentActiveMapId, viewId, source]);
1077
+ const handleClick = React.useCallback(menuType => {
1078
+ switch (menuType) {
1079
+ case MenuOptions.sources:
1080
+ dispatchActiveMapId(uiTypes.DialogTypes.SoundingsSources, isSourcesOpenInStore);
1081
+ break;
1082
+ case MenuOptions.locations:
1083
+ dispatchActiveMapId(uiTypes.DialogTypes.soundingsLocations, isLocationsOpenInStore);
1084
+ break;
1085
+ }
1086
+ }, [dispatchActiveMapId, isSourcesOpenInStore, isLocationsOpenInStore]);
1087
+ const isOpen = {
1088
+ [MenuOptions.sources]: currentActiveMapId === viewId && isSourcesOpenInStore,
1089
+ [MenuOptions.locations]: currentActiveMapId === viewId && isLocationsOpenInStore
1090
+ };
1091
+ return jsx(SoundingsMenuBar, {
1092
+ isOpen: isOpen,
1093
+ handleClick: handleClick
1094
+ });
1095
+ };
1096
+
1097
+ const SoundingsConnect = ({
1098
+ viewId
1099
+ }) => {
1100
+ return jsxs(Box, {
1101
+ sx: {
1102
+ padding: 2
1103
+ },
1104
+ children: [jsx(Box, {
1105
+ paddingLeft: 5,
1106
+ children: jsx(MenuBarConnect, {
1107
+ viewId: viewId
1108
+ })
1109
+ }), jsx(Soundings
1110
+ // TODO: use real data
1111
+ , {
1112
+ // TODO: use real data
1113
+ data: {
1114
+ features: SoundingFeatures
1115
+ },
1116
+ svgDimensions: {
1117
+ width: 600,
1118
+ height: 600,
1119
+ margin: {
1120
+ bottom: 0,
1121
+ left: 0,
1122
+ right: 0,
1123
+ top: 0
1124
+ }
1125
+ }
1126
+ })]
1127
+ });
1128
+ };
1129
+
1130
+ const soundingsComponentType = 'Soundings';
1131
+ const soundingsLookUp = payload => {
1132
+ if (payload.componentType === soundingsComponentType) {
1133
+ return jsx(SoundingsConnect, {
1134
+ viewId: payload.id
1135
+ });
1136
+ }
1137
+ return null;
8
1138
  };
9
1139
 
10
- export { Sounding };
1140
+ export { LocationsMenuConnect, Soundings, SoundingsSourceDialogConnect, soundingsComponentType, soundingsLookUp };