@opengeoweb/soundings 12.12.0 → 13.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/index.esm.js CHANGED
@@ -3,17 +3,18 @@ import React, { useRef, useEffect, useMemo, useState, Fragment as Fragment$1 } f
3
3
  import { useSelector, useDispatch } from 'react-redux';
4
4
  import { Box, Stack, Menu, ListSubheader, Typography, MenuItem, Tooltip, LinearProgress, Card, List, ListItem, ListItemIcon, Checkbox, ListItemText, Grid, ListItemButton } from '@mui/material';
5
5
  import { safelyInitI18nForTestsAndStories, sharedTranslations, SHARED_NAMESPACE, dateUtils, CustomSlider, CustomToggleButton, AlertBanner, ToolContainerDraggable, StyledSelect, SearchField, CustomAccordion } from '@opengeoweb/shared';
6
+ import { selectorMemoizationOptions, uiSelectors, uiTypes, uiActions, genericActions, useSetupDialog, storeReducerMap, storeMiddlewares } from '@opengeoweb/store';
7
+ import { ParseISOTimeRangeDuration, parseISO8601IntervalToDateInterval, webmapUtils } from '@opengeoweb/webmap';
8
+ import { MapFeatureClass } from '@opengeoweb/webmap-react';
6
9
  import * as d3 from 'd3';
7
10
  import { useQueries } from '@tanstack/react-query';
8
11
  import { getHeaders, handleResponse } from '@opengeoweb/api';
9
- import { ParseISOTimeRangeDuration, parseISO8601IntervalToDateInterval } from '@opengeoweb/webmap';
10
12
  import { range, minBy } from 'lodash';
11
- import { timesliderTranslations, TIMESLIDER_NAMESPACE, BackwardForwardStepButton } from '@opengeoweb/timeslider';
13
+ import { timesliderTranslations, TIME_SLIDER_NAMESPACE, BackwardForwardStepButton } from '@opengeoweb/time-slider';
12
14
  import i18n from 'i18next';
13
15
  export { default as i18n } from 'i18next';
14
16
  import { useTranslation } from 'react-i18next';
15
17
  import { createSlice, createSelector, combineReducers, configureStore } from '@reduxjs/toolkit';
16
- import { selectorMemoizationOptions, uiSelectors, uiTypes, uiActions, useSetupDialog, storeReducerMap, storeMiddlewares } from '@opengeoweb/store';
17
18
  import { Sources } from '@opengeoweb/theme';
18
19
  import { FixedSizeList } from 'react-window';
19
20
 
@@ -89,11 +90,12 @@ var soundingsTranslations = {
89
90
  * Copyright 2025 - Finnish Meteorological Institute (FMI)
90
91
  * Copyright 2025 - The Norwegian Meteorological Institute (MET Norway)
91
92
  * */
92
- // Define pressure and temperature values for static lines
93
+ // Define pressure [hPa] and temperature [°C] values for static lines
93
94
  const maxPressure = 1050;
94
95
  const minPressure = 150;
95
96
  const staticPressureArray = [maxPressure];
96
97
  const dp = -5;
98
+ const minEmagramTemperature = -80;
97
99
  const minTemperature = -40;
98
100
  const maxTemperature = 40.6;
99
101
  let pressure = maxPressure;
@@ -298,17 +300,64 @@ const drawAxes = ({
298
300
  if (diagramType !== 'BIJVOET') {
299
301
  // Create x-axis with intervals
300
302
  const xTicks = xScale.ticks(10);
301
- const xMiddleValue = xTicks[Math.floor(xTicks.length / 2)];
303
+ const xNegativeDisplacement = () => {
304
+ if (diagramType === 'SKEWT') {
305
+ return '-0.25em';
306
+ }
307
+ if (diagramType === 'EMAGRAM') {
308
+ return '-.5em';
309
+ }
310
+ return '0em';
311
+ };
312
+ const xPositiveDisplacement = () => {
313
+ if (diagramType === 'SKEWT') {
314
+ return '-0.2em';
315
+ }
316
+ if (diagramType === 'EMAGRAM') {
317
+ return '0.0em';
318
+ }
319
+ return '0em';
320
+ };
321
+ const xStringToSignedNumber = xString => {
322
+ const str = xString.replace(/[^\d.-]/g, '');
323
+ const xStringHasSomeLeadingCharacters = xString.length > str.length;
324
+ const xValue = xStringHasSomeLeadingCharacters ? -parseInt(str, 10) : parseInt(xString, 10);
325
+ return xValue;
326
+ };
302
327
  svg.append('g').attr('type', 'x-axis').attr('transform', `translate(0,${height})`).call(d3.axisBottom(xScale).tickValues(xTicks).tickSizeInner(0).tickSizeOuter(0));
303
- // Reformat in the case of the middle tick label
328
+ // Reformat in the case of the zero tick label
304
329
  // to add the unit part
305
- svg.selectAll('g[type="x-axis"] .tick').filter(function isMiddle() {
306
- return parseInt(d3.select(this).select('text').text(), 10) === xMiddleValue;
330
+ svg.selectAll('g[type="x-axis"] .tick').filter(function isZero() {
331
+ const xString = d3.select(this).select('text').text();
332
+ const xValue = xStringToSignedNumber(xString);
333
+ return xValue === 0; // Always true for SkewT and Emagram as they have a 0°C crossing
307
334
  }).each(function txt() {
308
335
  const unitPart = '°C';
309
336
  d3.select(this).select('text')
310
337
  // tspan necessary to use a different font-size
311
- .append('tspan').text(unitPart).attr('font-size', '11px').attr('dy', '0.04em');
338
+ .append('tspan').text(unitPart).attr('font-size', '11px').attr('dy', '0.04em').attr('dx', '0.0em');
339
+ });
340
+ svg.selectAll('g[type="x-axis"] .tick').filter(function isLessThanZero() {
341
+ const xString = d3.select(this).select('text').text();
342
+ const xValue = xStringToSignedNumber(xString);
343
+ return xValue < 0;
344
+ }).each(function txt() {
345
+ d3.select(this).select('text').attr('dx', xNegativeDisplacement());
346
+ });
347
+ svg.selectAll('g[type="x-axis"] .tick').filter(function isGreaterThanZero() {
348
+ const xString = d3.select(this).select('text').text();
349
+ const xValue = xStringToSignedNumber(xString);
350
+ return xValue > 0;
351
+ }).each(function txt() {
352
+ d3.select(this).select('text').attr('dx', xPositiveDisplacement());
353
+ });
354
+ const xMiddleValue = xTicks[xTicks.length - 1];
355
+ svg.selectAll('g[type="x-axis"] .tick').filter(function isLast() {
356
+ const xString = d3.select(this).select('text').text();
357
+ const xValue = xStringToSignedNumber(xString);
358
+ return xValue === xMiddleValue;
359
+ }).each(function txt() {
360
+ d3.select(this).select('text').attr('dx', xNegativeDisplacement());
312
361
  });
313
362
  }
314
363
  // Create y-axis with intervals
@@ -965,7 +1014,8 @@ const drawDryAdiabats = ({
965
1014
  dp,
966
1015
  diagramType
967
1016
  }) => {
968
- for (let Tbase = -40; Tbase < 240; Tbase += 10) {
1017
+ const minT = diagramType === 'EMAGRAM' ? minEmagramTemperature : minTemperature;
1018
+ for (let Tbase = minT; Tbase < 240; Tbase += 10) {
969
1019
  let Pnew = staticPressureArray[0];
970
1020
  let Tnew = Tbase;
971
1021
  const Px = [];
@@ -1453,8 +1503,10 @@ const Soundings = ({
1453
1503
  diagramType
1454
1504
  });
1455
1505
  const svgRectangle = svgRef.current.getBoundingClientRect();
1506
+ const minT = diagramType === 'EMAGRAM' ? minEmagramTemperature : minTemperature;
1507
+ const maxT = maxTemperature;
1456
1508
  // Set scales for temperature and pressure
1457
- const [xScale, yScale] = createScales(diagramType, width, height, minPressure, maxPressure, minTemperature, maxTemperature);
1509
+ const [xScale, yScale] = createScales(diagramType, width, height, minPressure, maxPressure, minT, maxT);
1458
1510
  // Add axis'
1459
1511
  drawAxes({
1460
1512
  svg,
@@ -1468,8 +1520,8 @@ const Soundings = ({
1468
1520
  diagramType,
1469
1521
  dp,
1470
1522
  staticPressureArray,
1471
- minTemperature,
1472
- maxTemperature,
1523
+ minTemperature: minT,
1524
+ maxTemperature: maxT,
1473
1525
  height,
1474
1526
  width,
1475
1527
  xScale,
@@ -1656,6 +1708,9 @@ const getCollectionLocations = (collectionId, baseUrl) => fetch(`${baseUrl}/coll
1656
1708
  const getCollectionInstances = (collectionId, baseUrl) => fetch(`${baseUrl}/collections/${collectionId}/instances`, {
1657
1709
  headers: getHeaders()
1658
1710
  }).then(handleResponse);
1711
+ const getCollectionDetails = (collectionId, baseUrl) => fetch(`${baseUrl}/collections/${collectionId}`, {
1712
+ headers: getHeaders()
1713
+ }).then(handleResponse);
1659
1714
  const getEdrCollectionForLocation = (collectionId, baseUrl, instanceId, locationId, parameters, datetime, verticalRange) => {
1660
1715
  const url = new URL(`${baseUrl}/collections/${collectionId}${instanceId ? `/instances/${instanceId}` : ''}/locations/${locationId}`);
1661
1716
  url.searchParams.append('parameter-name', parameters.join(','));
@@ -1872,12 +1927,19 @@ const parseDurationWithRepeats = dateString => {
1872
1927
  const numOfRepeats = Number(parts[0].substring(1));
1873
1928
  const start = new Date(parts[1]).getTime();
1874
1929
  const interval = parseISO8601IntervalToDateInterval(parts[2]).getTime();
1930
+ // If number of values too big, it will mess up the timeslider, set to 1500 max
1931
+ if (numOfRepeats >= 1500) {
1932
+ const iterations = range(1500);
1933
+ const newStart = start + interval * (numOfRepeats - 1499); // Move start so its 1500 steps from end
1934
+ const dateStrings = iterations.map(i => newStart + i * interval).map(millis => new Date(millis).toISOString());
1935
+ return dateStrings;
1936
+ }
1875
1937
  const iterations = range(numOfRepeats);
1876
1938
  const dateStrings = iterations.map(i => start + i * interval).map(millis => new Date(millis).toISOString());
1877
1939
  return dateStrings;
1878
1940
  };
1879
- const getTimestepsFromInstance = instance => {
1880
- const timeExtentValues = instance?.extent?.temporal?.values;
1941
+ const getTimestepsFromInstanceOrCollection = instanceOrCollection => {
1942
+ const timeExtentValues = instanceOrCollection?.extent?.temporal?.values;
1881
1943
  if (!timeExtentValues) {
1882
1944
  return [];
1883
1945
  }
@@ -1890,7 +1952,10 @@ const getTimestepsFromInstance = instance => {
1890
1952
  }
1891
1953
  // <start>/<end>[/P<period>]
1892
1954
  if (currentValue.match(/.*\/.*P?.*/)) {
1893
- const parseObject = new ParseISOTimeRangeDuration(currentValue);
1955
+ const parts = currentValue.split('/');
1956
+ // In case no period defined, set to default 1H
1957
+ const defaultInterval = parts[2] === undefined ? 'PT1H' : undefined;
1958
+ const parseObject = new ParseISOTimeRangeDuration(currentValue, defaultInterval);
1894
1959
  const timeValues = range(parseObject.getTimeSteps()).map(timestep => parseObject.getDateAtTimeStep(timestep).toISOString());
1895
1960
  return [...parsedSteps, ...timeValues];
1896
1961
  }
@@ -2033,12 +2098,21 @@ const useTimesteps = sourceSelections => {
2033
2098
  }) => ({
2034
2099
  queryKey: ['edr-soundings-instances', collectionId],
2035
2100
  queryFn: async () => {
2036
- const data = await getCollectionInstances(collectionId, url);
2101
+ // If instance id passed, use this to fetch time extent
2102
+ if (instanceId && instanceId !== '') {
2103
+ const data = await getCollectionInstances(collectionId, url);
2104
+ return data;
2105
+ }
2106
+ // Otherwise, use collection details
2107
+ const data = await getCollectionDetails(collectionId, url);
2037
2108
  return data;
2038
2109
  },
2039
2110
  select: data => {
2040
- const instance = data.instances.find(instance => instance.id === instanceId);
2041
- return getTimestepsFromInstance(instance);
2111
+ if (data.instances !== undefined) {
2112
+ const instance = data.instances.find(instance => instance.id === instanceId);
2113
+ return getTimestepsFromInstanceOrCollection(instance);
2114
+ }
2115
+ return getTimestepsFromInstanceOrCollection(data);
2042
2116
  }
2043
2117
  })), [sourceSelections]);
2044
2118
  const results = useQueries({
@@ -2067,19 +2141,57 @@ const useGetCollectionsInstances = (collections, isDialogOpen) => {
2067
2141
  url: collection.url
2068
2142
  };
2069
2143
  }) || [], [collections]);
2070
- const queries = useMemo(() => collectionIds.map(({
2144
+ // Fetch collection details
2145
+ const queriesCollection = useMemo(() => collectionIds.map(({
2071
2146
  id,
2072
2147
  url
2073
2148
  }) => {
2074
2149
  return {
2075
- queryKey: ['edr-soundings-instances', id],
2150
+ queryKey: ['edr-soundings-collection-details', id, url],
2076
2151
  queryFn: async () => {
2077
- const data = await getCollectionInstances(id, url);
2152
+ const data = await getCollectionDetails(id, url);
2078
2153
  return data;
2079
2154
  },
2080
2155
  enabled: isDialogOpen
2081
2156
  };
2082
2157
  }), [collectionIds, isDialogOpen]);
2158
+ const resultsCollection = useQueries({
2159
+ queries: queriesCollection,
2160
+ combine: queryResults => {
2161
+ const data = queryResults.map(result => {
2162
+ return result.data?.data_queries?.instances !== undefined;
2163
+ });
2164
+ const isPending = queryResults.some(result => result.isPending);
2165
+ const error = queryResults.find(result => result?.error)?.error;
2166
+ return {
2167
+ data,
2168
+ isPending,
2169
+ error
2170
+ };
2171
+ }
2172
+ });
2173
+ // Fetch instance details for collections that have instances
2174
+ const queries = useMemo(() => {
2175
+ return collectionIds.map(({
2176
+ id,
2177
+ url
2178
+ }, index) => {
2179
+ return {
2180
+ queryKey: ['edr-soundings-instances', id, resultsCollection.data],
2181
+ queryFn: async () => {
2182
+ // Only fetch if there are instances for this collection
2183
+ if (resultsCollection.data[index]) {
2184
+ const data = await getCollectionInstances(id, url);
2185
+ return data;
2186
+ }
2187
+ return {
2188
+ instances: []
2189
+ };
2190
+ },
2191
+ enabled: isDialogOpen && resultsCollection.data !== undefined
2192
+ };
2193
+ });
2194
+ }, [collectionIds, isDialogOpen, resultsCollection]);
2083
2195
  const results = useQueries({
2084
2196
  queries,
2085
2197
  combine: queryResults => {
@@ -2102,12 +2214,16 @@ const useGetCollectionsInstances = (collections, isDialogOpen) => {
2102
2214
  instances: results.data[index] || []
2103
2215
  };
2104
2216
  });
2217
+ const errorResult = results.error || resultsCollection.error;
2218
+ const isPendingResult = results.isPending || resultsCollection.isPending;
2105
2219
  return {
2106
2220
  ...results,
2221
+ isPending: isPendingResult,
2222
+ error: errorResult,
2107
2223
  data: dataWithCollectionInfo
2108
2224
  };
2109
2225
  };
2110
- const useGetCollectionLocations = (collections, isDialogOpen) => {
2226
+ const useGetCollectionLocations = (collections, enabled) => {
2111
2227
  const edrQueries = useMemo(() => collections?.map(collection => ({
2112
2228
  queryKey: ['edr-locations', collection.id],
2113
2229
  queryFn: async () => {
@@ -2118,8 +2234,8 @@ const useGetCollectionLocations = (collections, isDialogOpen) => {
2118
2234
  name: collection.name
2119
2235
  };
2120
2236
  },
2121
- enabled: isDialogOpen
2122
- })), [collections, isDialogOpen]);
2237
+ enabled
2238
+ })), [collections, enabled]);
2123
2239
  return useQueries({
2124
2240
  queries: edrQueries,
2125
2241
  combine: results => {
@@ -2158,14 +2274,14 @@ const initSoundingsI18n = () => {
2158
2274
  ns: SOUNDINGS_NAMESPACE,
2159
2275
  translations: soundingsTranslations
2160
2276
  }, {
2161
- ns: TIMESLIDER_NAMESPACE,
2277
+ ns: TIME_SLIDER_NAMESPACE,
2162
2278
  translations: timesliderTranslations
2163
2279
  }, {
2164
2280
  ns: SHARED_NAMESPACE,
2165
2281
  translations: sharedTranslations
2166
2282
  }]);
2167
2283
  };
2168
- const ns = [SOUNDINGS_NAMESPACE, TIMESLIDER_NAMESPACE, SHARED_NAMESPACE];
2284
+ const ns = [SOUNDINGS_NAMESPACE, TIME_SLIDER_NAMESPACE, SHARED_NAMESPACE];
2169
2285
  const translateKeyOutsideComponents = (key, params = undefined) => i18n.t(key, {
2170
2286
  ns,
2171
2287
  ...params
@@ -2643,9 +2759,50 @@ const SoundingsConnect = ({
2643
2759
  diagramType = 'SKEWT'
2644
2760
  }) => {
2645
2761
  const [chosenDiagramType, setChosenDiagramType] = React.useState(diagramType);
2762
+ const geojsonLayerId = React.useRef(webmapUtils.generateLayerId()).current;
2763
+ const dispatch = useDispatch();
2646
2764
  const selectedTime = useSelector(getSelectedTime);
2647
2765
  const sourceSelections = useSelector(getSelectedSources);
2648
2766
  const locationSelections = useSelector(getLocationSelections);
2767
+ const collections = sourceSelections.map(({
2768
+ collectionId,
2769
+ url,
2770
+ name
2771
+ }) => ({
2772
+ id: collectionId,
2773
+ url,
2774
+ name: name || collectionId
2775
+ }));
2776
+ const {
2777
+ data: locationData
2778
+ } = useGetCollectionLocations(collections, true);
2779
+ useEffect(() => {
2780
+ const simpleGeoJson = {
2781
+ type: 'FeatureCollection',
2782
+ features: (locationData || []).flatMap(({
2783
+ id: serviceId,
2784
+ features
2785
+ }) => features.map(feature => ({
2786
+ type: 'Feature',
2787
+ geometry: feature.geometry,
2788
+ properties: {
2789
+ ...feature.properties,
2790
+ mapFeatureClass: MapFeatureClass.ObservationStation
2791
+ },
2792
+ id: feature.id,
2793
+ serviceId // TODO: remove in follow-up if not needed https://gitlab.com/opengeoweb/opengeoweb/-/issues/6312
2794
+ })))
2795
+ };
2796
+ dispatch(genericActions.addSharedData({
2797
+ panelId: viewId,
2798
+ data: {
2799
+ features: [{
2800
+ id: geojsonLayerId,
2801
+ geoJSON: simpleGeoJson
2802
+ }]
2803
+ }
2804
+ }));
2805
+ }, [collections, dispatch, geojsonLayerId, locationData, sourceSelections, viewId]);
2649
2806
  const {
2650
2807
  error,
2651
2808
  data,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengeoweb/soundings",
3
- "version": "12.12.0",
3
+ "version": "13.0.0",
4
4
  "description": "GeoWeb soundings library for the opengeoweb project",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -8,23 +8,24 @@
8
8
  "url": "git@gitlab.com:opengeoweb/opengeoweb.git"
9
9
  },
10
10
  "dependencies": {
11
- "@opengeoweb/api": "12.12.0",
12
- "@tanstack/react-query": "^5.69.2",
11
+ "@opengeoweb/api": "13.0.0",
12
+ "@tanstack/react-query": "^5.85.5",
13
13
  "react": "^18.3.1",
14
14
  "d3": "^7.9.0",
15
15
  "react-i18next": "^15.1.1",
16
16
  "i18next": "^25.0.1",
17
- "@opengeoweb/shared": "12.12.0",
17
+ "@opengeoweb/shared": "13.0.0",
18
18
  "@mui/material": "^7.0.1",
19
- "@opengeoweb/store": "12.12.0",
19
+ "@opengeoweb/store": "13.0.0",
20
20
  "react-redux": "^9.2.0",
21
- "@opengeoweb/theme": "12.12.0",
21
+ "@opengeoweb/theme": "13.0.0",
22
22
  "@reduxjs/toolkit": "^2.6.1",
23
- "@opengeoweb/timeslider": "12.12.0",
23
+ "@opengeoweb/time-slider": "13.0.0",
24
24
  "react-window": "^1.8.10",
25
- "@opengeoweb/webmap": "12.12.0",
25
+ "@opengeoweb/webmap": "13.0.0",
26
26
  "lodash": "^4.17.21",
27
- "@types/geojson": "^7946.0.14"
27
+ "@types/geojson": "^7946.0.14",
28
+ "@opengeoweb/webmap-react": "13.0.0"
28
29
  },
29
30
  "devDependencies": {
30
31
  "eslint-plugin-storybook": "9.0.17"
@@ -1,6 +1,7 @@
1
1
  import { CoverageCollection } from '@opengeoweb/shared';
2
- import { LocationsResponseFromBE, SoundingsInstances } from './types';
2
+ import { CollectionDetails, LocationsResponseFromBE, SoundingsInstances } from './types';
3
3
  export declare const getCollectionLocations: (collectionId: string, baseUrl: string) => Promise<LocationsResponseFromBE>;
4
4
  export declare const getCollectionInstances: (collectionId: string, baseUrl: string) => Promise<SoundingsInstances>;
5
+ export declare const getCollectionDetails: (collectionId: string, baseUrl: string) => Promise<CollectionDetails>;
5
6
  export declare const getEdrCollectionForLocation: (collectionId: string, baseUrl: string, instanceId: string | undefined, locationId: string, parameters: string[], datetime: string, verticalRange: string) => Promise<CoverageCollection>;
6
7
  export declare const getEdrCollectionForPosition: (collectionId: string, baseUrl: string, instanceId: string | undefined, coordinates: [number, number], parameters: string[], datetime: string, verticalRange: string) => Promise<CoverageCollection>;
@@ -1 +1,16 @@
1
+ export declare const helsinkiLat = 60.192059;
2
+ export declare const helsinkiLon = 24.945831;
3
+ export declare const helsinki = "Helsinki";
4
+ export declare const mockLocations: {
5
+ features: {
6
+ id: string;
7
+ geometry: {
8
+ type: string;
9
+ coordinates: number[];
10
+ };
11
+ properties: {
12
+ name: string;
13
+ };
14
+ }[];
15
+ };
1
16
  export declare const fmiEdrCollectionsHandlers: import("msw").HttpHandler[];
@@ -33,7 +33,11 @@ export declare const useGetCollectionsInstances: (collections?: SoundingsCollect
33
33
  isPending: boolean;
34
34
  error: Error | null | undefined;
35
35
  };
36
- export declare const useGetCollectionLocations: (collections: SoundingsCollection[], isDialogOpen?: boolean) => {
36
+ export declare const useGetCollectionLocations: <SoundingsCollectionLike extends {
37
+ id: string;
38
+ url: string;
39
+ name: string;
40
+ }>(collections: SoundingsCollectionLike[], enabled?: boolean) => {
37
41
  data: ServiceWithLocations[] | undefined;
38
42
  isPending: boolean;
39
43
  error: Error | null | undefined;
@@ -1,5 +1,5 @@
1
1
  import { Geometry } from 'geojson';
2
- import { EDRInstance } from '@opengeoweb/shared';
2
+ import { EDRInstance, EdrParameter } from '@opengeoweb/shared';
3
3
  export interface FeaturesFromBE {
4
4
  id: string;
5
5
  type: string;
@@ -31,5 +31,8 @@ export type SoundingsInstance = EDRInstance;
31
31
  export interface SoundingsInstances {
32
32
  instances: SoundingsInstance[];
33
33
  }
34
+ export interface CollectionDetails extends EDRInstance {
35
+ parameter_names: Record<string, EdrParameter>;
36
+ }
34
37
  export declare const soundingDiagrams: readonly ["SKEWT", "BIJVOET", "EMAGRAM"];
35
38
  export type SoundingDiagramType = (typeof soundingDiagrams)[number];
@@ -1,5 +1,5 @@
1
1
  import { CoverageCollection } from '@opengeoweb/shared';
2
- import { SoundingFeature, SoundingsInstance } from './types';
2
+ import { CollectionDetails, SoundingFeature, SoundingsInstance } from './types';
3
3
  export declare function parseCoverageCollection(response: CoverageCollection, parameterMappings: Record<string, string>): SoundingFeature[];
4
4
  /**
5
5
  * Parse duration with repeats
@@ -7,4 +7,4 @@ export declare function parseCoverageCollection(response: CoverageCollection, pa
7
7
  * @returns Array of ISO datetime strings, e.g., ['2025-04-22T09:00:00.000Z', ...]
8
8
  */
9
9
  export declare const parseDurationWithRepeats: (dateString: string) => string[];
10
- export declare const getTimestepsFromInstance: (instance?: SoundingsInstance) => string[];
10
+ export declare const getTimestepsFromInstanceOrCollection: (instanceOrCollection?: SoundingsInstance | CollectionDetails) => string[];
@@ -2,6 +2,7 @@ export declare const maxPressure = 1050;
2
2
  export declare const minPressure = 150;
3
3
  export declare const staticPressureArray: number[];
4
4
  export declare const dp = -5;
5
+ export declare const minEmagramTemperature = -80;
5
6
  export declare const minTemperature = -40;
6
7
  export declare const maxTemperature = 40.6;
7
8
  export declare const T_KAPPA = 0.2857;