Skip to content

Commit ce0172b

Browse files
committed
DAS-2238 - updates to enable 3D variables without dimension scales to work
1 parent cc433c8 commit ce0172b

10 files changed

+6014
-69
lines changed

CHANGELOG.md

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## v1.1.3
2+
### 2025-01-29
3+
4+
This version of HOSS supports configuration updates to hoss_config.json to
5+
add dimension configurations for 3D variables. Functions were updated to provide the
6+
ability to spatial subset 3D variables for products like SMAP L3 which did
7+
not have dimension arrays.
8+
19
## v1.1.2
210
### 2025-01-20
311

@@ -13,8 +21,10 @@
1321
This version of HOSS merges the feature branch that contains V1.1.0 to the main branch.
1422
Additional updates included code quality improvements with additional unit tests, revised methodology
1523
in functions that selected the data points from the coordinate datasets and calculation of the dimension
16-
arrays. Functions were added to determine the dimension order for 2D variables.
17-
24+
arrays. Functions were added to determine the dimension order for 2D variables. These updates,
25+
V1.1.1 and V1.1.0 are entirely to support SMAP L3 data - in particular SPL2SMAP_S, SPL3SMAP, SPL3SMA -
26+
all of which have “anonymous” dimensions (without dimension names and dimension variables). No functional
27+
updates are present, nor changes that effect existing collection support.
1828

1929
## v1.1.0
2030
### 2024-11-25

hoss/coordinate_utilities.py

Lines changed: 48 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,13 @@ def get_variables_with_anonymous_dims(
6060
return set(
6161
variable
6262
for variable in variables
63-
if (len(varinfo.get_variable(variable).dimensions) == 0)
64-
or (any_absent_dimension_variables(varinfo, variable))
63+
if (
64+
varinfo.get_variable(variable)
65+
and (
66+
(len(varinfo.get_variable(variable).dimensions) == 0)
67+
or (any_absent_dimension_variables(varinfo, variable))
68+
)
69+
)
6570
)
6671

6772

@@ -70,42 +75,56 @@ def any_absent_dimension_variables(varinfo: VarInfoFromDmr, variable: str) -> bo
7075
that have been created by opendap, but are not really
7176
dimension variables
7277
"""
78+
7379
return any(
7480
varinfo.get_variable(dimension) is None
7581
for dimension in varinfo.get_variable(variable).dimensions
7682
)
7783

7884

79-
def get_dimension_array_names_from_coordinate_variables(
85+
def get_dimension_array_names(
8086
varinfo: VarInfoFromDmr,
8187
variable_name: str,
8288
) -> list[str]:
8389
"""
84-
Returns the dimensions names from coordinate variables
90+
Returns the dimensions names from coordinate variables or from
91+
configuration
8592
"""
93+
variable = varinfo.get_variable(variable_name)
94+
dimension_names = varinfo.get_variable(variable_name).dimensions
95+
96+
if len(dimension_names) >= 2:
97+
# if the dimension is not a variable, was added as a
98+
# configuration entry,
99+
if variable is not None:
100+
return dimension_names
86101

87102
latitude_coordinates, longitude_coordinates = get_coordinate_variables(
88103
varinfo, [variable_name]
89104
)
90105

91106
# for one variable, the coordinate array length will always be 1 or 0
92107
if len(latitude_coordinates) == 1 and len(longitude_coordinates) == 1:
93-
dimension_array_names = get_dimension_array_names(
108+
dimension_array_names = get_dimension_array_names_from_coordinates(
94109
varinfo, latitude_coordinates[0]
95110
)
96111
# if variable does not have coordinates (len = 0)
97112
elif (
98113
varinfo.get_variable(variable_name).is_latitude()
99114
or varinfo.get_variable(variable_name).is_longitude()
100115
):
101-
dimension_array_names = get_dimension_array_names(varinfo, variable_name)
116+
dimension_array_names = get_dimension_array_names_from_coordinates(
117+
varinfo, variable_name
118+
)
102119
else:
103120
dimension_array_names = []
104121

105122
return dimension_array_names
106123

107124

108-
def get_dimension_array_names(varinfo: VarInfoFromDmr, variable_name: str) -> str:
125+
def get_dimension_array_names_from_coordinates(
126+
varinfo: VarInfoFromDmr, variable_name: str
127+
) -> str:
109128
"""returns the x-y variable names that would
110129
match the group of the input variable. The 'dim_y' dimension
111130
and 'dim_x' names are returned with the group pathname
@@ -172,13 +191,19 @@ def create_dimension_arrays_from_coordinates(
172191
col_dim_values, np.transpose(col_indices)[1], col_size
173192
)
174193

175-
projected_y, projected_x = tuple(projected_dimension_names)
194+
if len(projected_dimension_names) >= 2:
195+
projected_y, projected_x = (
196+
projected_dimension_names[-2],
197+
projected_dimension_names[-1],
198+
)
199+
200+
if dim_order_is_y_x:
201+
return {projected_y: y_dim, projected_x: x_dim}
202+
raise UnsupportedDimensionOrder('x,y')
203+
# this is not currently supported in the calling function in spatial.py
204+
# return {projected_x: x_dim, projected_y: y_dim}
176205

177-
if dim_order_is_y_x:
178-
return {projected_y: y_dim, projected_x: x_dim}
179-
raise UnsupportedDimensionOrder('x,y')
180-
# this is not currently supported in the calling function in spatial.py
181-
# return {projected_x: x_dim, projected_y: y_dim}
206+
raise UnsupportedDimensionOrder(projected_dimension_names)
182207

183208

184209
def get_2d_coordinate_array(
@@ -322,8 +347,16 @@ def get_dimension_order_and_dim_values(
322347
projected spatial dimension. The input lat lon arrays and dimension
323348
indices are assumed to be 2D in this implementation of the function.
324349
"""
325-
lat_arr_values = [lat_array_points[i][j] for i, j in grid_dimension_indices]
326-
lon_arr_values = [lon_array_points[i][j] for i, j in grid_dimension_indices]
350+
if lat_array_points.ndim == 1 and lon_array_points.ndim == 1:
351+
lat_arr_values = lat_array_points
352+
lon_arr_values = lon_array_points
353+
elif lat_array_points.ndim == 2 and lon_array_points.ndim == 2:
354+
lat_arr_values = [lat_array_points[i][j] for i, j in grid_dimension_indices]
355+
lon_arr_values = [lon_array_points[i][j] for i, j in grid_dimension_indices]
356+
else:
357+
# assuming a nominal z,y,x order
358+
lat_arr_values = [lat_array_points[0][i][j] for i, j in grid_dimension_indices]
359+
lon_arr_values = [lon_array_points[0][i][j] for i, j in grid_dimension_indices]
327360

328361
from_geo_transformer = Transformer.from_crs(4326, crs)
329362
x_values, y_values = ( # pylint: disable=unpacking-non-sequence

hoss/dimension_utilities.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525
from hoss.coordinate_utilities import (
2626
get_coordinate_variables,
27-
get_dimension_array_names_from_coordinate_variables,
27+
get_dimension_array_names,
2828
)
2929
from hoss.exceptions import (
3030
InvalidIndexSubsetRequest,
@@ -441,17 +441,17 @@ def add_index_range(
441441
if variable.dimensions:
442442
variable_dimensions = variable.dimensions
443443
else:
444-
# Anonymous dimensions, so check for dimension derived from coordinates:
445-
variable_dimensions = get_dimension_array_names_from_coordinate_variables(
446-
varinfo, variable_name
447-
)
444+
# Anonymous dimensions, so check for dimension derived from coordinates
445+
# or from configuration
446+
variable_dimensions = get_dimension_array_names(varinfo, variable_name)
448447

449448
range_strings = get_range_strings(variable_dimensions, index_ranges)
450449

451450
if all(range_string == '[]' for range_string in range_strings):
452451
indices_string = ''
453452
else:
454453
indices_string = ''.join(range_strings)
454+
455455
return f'{variable_name}{indices_string}'
456456

457457

hoss/hoss_config.json

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"Identification": "hoss_config",
3-
"Version": 19,
3+
"Version": 20,
44
"CollectionShortNamePath": [
55
"/HDF5_GLOBAL/short_name",
66
"/NC_GLOBAL/short_name",
@@ -314,6 +314,48 @@
314314
],
315315
"_Description": "Ensure variables in /Soil_Moisture_Retrieval_Data_Polar_PM group point to correct coordinate variables."
316316
},
317+
{
318+
"Applicability": {
319+
"Mission": "SMAP",
320+
"ShortNamePath": "SPL3FT(A|P|P_E)",
321+
"VariablePattern": "^/Freeze_Thaw_Retrieval_Data_(Global|Polar)/(transition_direction$|transition_state_flag$)"
322+
},
323+
"Attributes": [
324+
{
325+
"Name": "dimensions",
326+
"Value": "y_dim x_dim"
327+
}
328+
],
329+
"_Description": "Only these fully referenced variables are 2D, with this rule overriding the 3D rule defined broadly for all variables"
330+
},
331+
{
332+
"Applicability": {
333+
"Mission": "SMAP",
334+
"ShortNamePath": "SPL3FT(A|P|P_E)",
335+
"VariablePattern": "^/Freeze_Thaw_Retrieval_Data_(Global|Polar)/((?!transition_state_flag$)(?!transition_direction$).)*$|/Radar_Data/.*"
336+
},
337+
"Attributes": [
338+
{
339+
"Name": "dimensions",
340+
"Value": "am_pm y_dim x_dim"
341+
}
342+
],
343+
"_Description": "SMAP L3 data are HDF5 and without dimension settings. Overrides here define the dimensions, a useful reference name, and critically, the dimension order."
344+
},
345+
{
346+
"Applicability": {
347+
"Mission": "SMAP",
348+
"ShortNamePath": "SPL3SMP",
349+
"VariablePattern": ".*landcover.*"
350+
},
351+
"Attributes": [
352+
{
353+
"Name": "dimensions",
354+
"Value": "y_dim x_dim lc_type"
355+
}
356+
],
357+
"_Description": "SMAP L3 data are HDF5 and without dimension settings. Overrides here define the dimensions, a useful reference name, and critically, the dimension order."
358+
},
317359
{
318360
"Applicability": {
319361
"Mission": "ICESat2",

hoss/spatial.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@
3838
from hoss.coordinate_utilities import (
3939
create_dimension_arrays_from_coordinates,
4040
get_coordinate_variables,
41-
get_dimension_array_names_from_coordinate_variables,
41+
get_dimension_array_names,
4242
get_variables_with_anonymous_dims,
4343
)
4444
from hoss.dimension_utilities import (
@@ -247,9 +247,7 @@ def get_x_y_index_ranges_from_coordinates(
247247

248248
crs = get_variable_crs(non_spatial_variable, varinfo)
249249

250-
projected_dimension_names = get_dimension_array_names_from_coordinate_variables(
251-
varinfo, non_spatial_variable
252-
)
250+
projected_dimension_names = get_dimension_array_names(varinfo, non_spatial_variable)
253251

254252
dimension_arrays = create_dimension_arrays_from_coordinates(
255253
prefetch_coordinate_datasets,

0 commit comments

Comments
 (0)