Skip to content

Commit 13acf0b

Browse files
gshiromaGitHub Enterprise
authored andcommitted
Update base class for products with RSLC (rather than SLC) group (#773)
* fix Base class for SLC/RSLC and L2 productTypes * make Product.cpp general for SLC/RSLC and S-SAR * fix Base class setProductType() method * add RRSD to the list of NISAR products * add ProductFactory() class * add ProductFactory() class (2) * make method setProductType() private * set productType as RSLC regardless of the HDF5 group RSLC/SLC * update value assignment of productType in Base and SLC classes * instantiate existing product classes before creating a new ProductFactory object * simplify _setProduct method of SLC class after changes in ProductFactory * make Base.py general for LSAR and SSAR frequency bands * avoid memory allocation and string copying * add comments to Product.cpp and limit the maximum number of grids/swaths and identification groups to 1 * add function to find unique group paths (excluding repeated occurrences) * add unitest to ProductFactory class * fix ProductFactory for GSLC products * fix ProductFactory for GSLC products (2) * fix extractWithIterator for val undefined if KeyError exception is raised * create open_nisar_product and move ProductFactory to GenericProduct * rename open_nisar_product() to open_product() * substitute backslashes with parenthesis for line continuation * substitute get_product_type_from_hdf5File() with get_product_type_from_hdf5_file() * improve docstrings and function/variable names * if logger is declared (not None) use logger to raise an exception in h5prep * send exceptions to pyre journal rather than raising default python exceptions * declare pyre error channel locally
1 parent e3afbb3 commit 13acf0b

File tree

11 files changed

+469
-46
lines changed

11 files changed

+469
-46
lines changed

cxx/isce3/product/Product.cpp

Lines changed: 124 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,140 @@
11
#include "Product.h"
22
#include <isce3/product/Serialization.h>
33

4+
/** Find unique group path excluding repeated occurrences
5+
*/
6+
std::vector<std::string> _findGroupPath(isce3::io::IGroup& group,
7+
const std::string group_name) {
8+
9+
auto group_vector = group.find(group_name, ".", "GROUP");
10+
11+
std::vector<std::string> filtered_group_vector;
12+
13+
// Filter unique group paths
14+
for (const auto& full_group_path: group_vector) {
15+
std::size_t group_name_position = full_group_path.find(group_name);
16+
17+
// Get base group path (remove sub-groups)
18+
std::string base_group = full_group_path.substr(
19+
0, group_name_position + group_name.length());
20+
21+
/* If base group is not in filtered_group_vector,
22+
append it to the vector
23+
*/
24+
if (find(filtered_group_vector.begin(),
25+
filtered_group_vector.end(),
26+
base_group) == filtered_group_vector.end()) {
27+
filtered_group_vector.push_back(base_group);
28+
}
29+
}
30+
31+
return filtered_group_vector;
32+
}
33+
34+
435
/** @param[in] file IH5File object for product. */
536
isce3::product::Product::
637
Product(isce3::io::IH5File & file) {
7-
// Get swaths group
8-
isce3::io::IGroup imGroup = file.openGroup("/science/LSAR/SLC/swaths");
38+
39+
std::string base_dir = "/science/";
40+
41+
isce3::io::IGroup base_group = file.openGroup(base_dir);
42+
43+
bool flag_has_swaths = false;
44+
45+
/*
46+
In this section, we look for grids or swaths group within base_group.
47+
We start by assigning an empty string to image_group_str in case
48+
grids and swaths group are not found.
49+
*/
50+
std::vector<std::string> key_vector = {"grids", "swaths"};
51+
std::string image_group_str = "", metadata_group_str;
52+
for (const auto& key: key_vector) {
53+
54+
// Look for HDF5 groups that match key (i.e., "grids" or "swaths")
55+
auto group_vector = _findGroupPath(base_group, key);
56+
57+
if (group_vector.size() > 1) {
58+
/*
59+
NISAR products should have only one grids or swaths group
60+
within base_group:
61+
*/
62+
std::string error_msg = ("ERROR there should be at most one " +
63+
key + " group in " +file.getFileName());
64+
throw isce3::except::RuntimeError(ISCE_SRCINFO(), error_msg);
65+
}
66+
else if (group_vector.size() > 0) {
67+
68+
// Group swaths or grid was found
69+
70+
/*
71+
Assign the group (grids or swaths) to image_group_str,
72+
locate the group metadata by substituting "grids"/"swaths" with
73+
"metadata", and save it on metadata_group_str
74+
*/
75+
image_group_str = base_dir + group_vector[0];
76+
metadata_group_str = image_group_str;
77+
std::size_t key_position = metadata_group_str.rfind(key);
78+
metadata_group_str.replace(key_position, key.length(), "metadata");
79+
if (key == "swaths") {
80+
/*
81+
flag_has_swaths indicates if image_group_str reffers to swaths or
82+
grids
83+
*/
84+
flag_has_swaths = true;
85+
}
86+
break;
87+
}
88+
}
89+
90+
// If did not find HDF5 groups swaths or grids
91+
if (image_group_str.size() == 0) {
92+
std::string error_msg = ("ERROR swaths and grids groups"
93+
" not found in " + file.getFileName());
94+
throw isce3::except::RuntimeError(ISCE_SRCINFO(), error_msg);
95+
}
96+
97+
// Get swaths/grids group
98+
isce3::io::IGroup imGroup = file.openGroup(image_group_str);
99+
9100
// Configure swaths
10-
loadFromH5(imGroup, _swaths);
101+
if (flag_has_swaths) {
102+
loadFromH5(imGroup, _swaths);
103+
}
104+
/*
105+
Not implemented yet:
106+
else {
107+
loadFromH5(imGroup, _grids);
108+
}
109+
*/
110+
11111
// Get metadata group
12-
isce3::io::IGroup metaGroup = file.openGroup("/science/LSAR/SLC/metadata");
112+
isce3::io::IGroup metaGroup = file.openGroup(metadata_group_str);
13113
// Configure metadata
114+
14115
loadFromH5(metaGroup, _metadata);
116+
15117
// Get look direction
118+
auto identification_vector = _findGroupPath(
119+
base_group, "identification");
120+
if (identification_vector.size() == 0) {
121+
std::string error_msg = ("ERROR identification group not found in " +
122+
file.getFileName());
123+
throw isce3::except::RuntimeError(ISCE_SRCINFO(), error_msg);
124+
}
125+
else if (identification_vector.size() > 1) {
126+
std::string error_msg = ("ERROR there should be only one identification"
127+
" group in " + file.getFileName());
128+
throw isce3::except::RuntimeError(ISCE_SRCINFO(), error_msg);
129+
}
130+
131+
std::string identification_group_str = base_dir + identification_vector[0];
16132
std::string lookDir;
17-
isce3::io::loadFromH5(file, "/science/LSAR/identification/lookDirection", lookDir);
133+
isce3::io::loadFromH5(file, identification_group_str + "/lookDirection",
134+
lookDir);
18135
lookSide(lookDir);
136+
19137
// Save the filename
20138
_filename = file.filename();
139+
21140
}

python/packages/pybind_nisar/h5/h5utils.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,12 +41,16 @@ def extractWithIterator(h5grp, key, iterType, logger=None, msg=None):
4141
val = h5grp[key][()]
4242
val = [iterType(x) for x in val]
4343
except KeyError:
44+
errmsg = '{0} not found at {1}'.format(key, h5grp.name)
4445
if logger:
45-
logger.log('{0} not found at {1}'.format(key, h5grp.name))
46+
logger.log(errmsg)
47+
raise KeyError(errmsg)
4648
except:
4749
errmsg = 'Something went wrong when trying to parse {0}/{1} \n'.format(h5grp.name, key)
4850
if msg is not None:
4951
errmsg += msg
52+
if logger:
53+
logger.log(errmsg)
5054
raise ValueError(errmsg)
5155

5256
return val

python/packages/pybind_nisar/products/readers/Base/Base.py

Lines changed: 62 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,51 @@
22

33
import h5py
44
import os
5+
import journal
56
import pyre
67
import pybind_isce3 as isce3
78
from ..protocols import ProductReader
89

10+
11+
def get_hdf5_file_root_path(filename: str, root_path: str = None) -> str:
12+
'''
13+
Return root path from HDF5 file. If a root path is provided as
14+
a parameter and it exists, it will have precedence over the
15+
root path from the HDF5 file
16+
17+
Parameters
18+
----------
19+
filename : str
20+
HDF5 filename
21+
root_path : str
22+
Preliminary root path to check (e.g., XSAR, PSAR) before default root
23+
path list
24+
25+
Returns
26+
-------
27+
str
28+
Root path from HDF5 file
29+
30+
'''
31+
32+
error_channel = journal.error('get_hdf5_file_root_path')
33+
34+
SCIENCE_PATH = '/science/'
35+
NISAR_FREQ_BAND_LIST = ['SSAR', 'LSAR']
36+
with h5py.File(filename, 'r', libver='latest', swmr=True) as f:
37+
if root_path is not None and root_path in f:
38+
return root_path
39+
science_group = f[SCIENCE_PATH]
40+
for freq_band in NISAR_FREQ_BAND_LIST:
41+
if freq_band not in science_group:
42+
continue
43+
return SCIENCE_PATH + freq_band
44+
45+
error_msg = ("HDF5 could not find NISAR frequency"
46+
f" band group LSAR or SSAR in file: {filename}")
47+
48+
error_channel.log(error_msg)
49+
950
class Base(pyre.component,
1051
family='nisar.productreader.base',
1152
implements=ProductReader):
@@ -17,13 +58,13 @@ class Base(pyre.component,
1758
_CFPath = pyre.properties.str(default='/')
1859
_CFPath.doc = 'Absolute path to scan for CF convention metadata'
1960

20-
_RootPath = pyre.properties.str(default='/science/LSAR')
61+
_RootPath = pyre.properties.str(default=None)
2162
_RootPath.doc = 'Absolute path to SAR data from L-SAR/S-SAR'
2263

2364
_IdentificationPath = pyre.properties.str(default='identification')
2465
_IdentificationPath.doc = 'Absolute path ath to unique product identification information'
2566

26-
_ProductType = pyre.properties.str(default='SLC')
67+
_ProductType = pyre.properties.str(default=None)
2768
_ProductType.doc = 'The type of the product.'
2869

2970
_MetadataPath = pyre.properties.str(default='metadata')
@@ -41,10 +82,19 @@ class Base(pyre.component,
4182
productValidationType = pyre.properties.str(default='BASE')
4283
productValidationType.doc = 'Validation tag to compare identification information against to ensure that the right product type is being used.'
4384

44-
def __init__(self, hdf5file='None', **kwds):
85+
def __init__(self, hdf5file=None, **kwds):
4586
'''
4687
Constructor.
4788
'''
89+
90+
# Set error channel
91+
self.error_channel = journal.error('Base')
92+
93+
# Check hdf5file
94+
if hdf5file is None:
95+
err_str = f"Please provide an input HDF5 file"
96+
self.error_channel.log(err_str)
97+
4898
# Filename
4999
self.filename = hdf5file
50100

@@ -55,8 +105,12 @@ def __init__(self, hdf5file='None', **kwds):
55105
self.polarizations = {}
56106

57107
self.populateIdentification()
58-
#For now, Needs to be an assertion check in the future
59-
self.identification.productType = self.productValidationType
108+
109+
self.identification.productType = self._ProductType
110+
111+
if self._ProductType is None:
112+
return
113+
60114
self.parsePolarizations()
61115

62116
@pyre.export
@@ -141,28 +195,6 @@ def getSlantRange(self, frequency='A'):
141195

142196
return slantRange
143197

144-
145-
def getSlcDataset(self, frequency, polarization):
146-
'''
147-
Return SLC dataset of given frequency and polarization from hdf5 file
148-
'''
149-
150-
# TODO add checks for (1) file open error (2) path check
151-
slcDataset = None
152-
153-
# open H5 with swmr mode enabled
154-
fid = h5py.File(self.filename, 'r', libver='latest', swmr=True)
155-
156-
# build path the desired dataset
157-
dsPath = self.slcPath(frequency, polarization)
158-
159-
# get dataset
160-
slcDataset = fid[dsPath]
161-
162-
# return dataset
163-
return slcDataset
164-
165-
166198
def parsePolarizations(self):
167199
'''
168200
Parse HDF5 and identify polarization channels available for each frequency.
@@ -195,6 +227,9 @@ def CFPath(self):
195227

196228
@property
197229
def RootPath(self):
230+
if self._RootPath is None:
231+
self._RootPath = get_hdf5_file_root_path(
232+
self.filename)
198233
return self._RootPath
199234

200235
@property
@@ -255,12 +290,4 @@ def computeBoundingBox(self, epsg=4326):
255290
'''
256291
raise NotImplementedError
257292

258-
def slcPath(self, frequency, polarization):
259-
'''
260-
return path to hdf5 dataset of given frequency and polarization
261-
'''
262-
datasetPath = os.path.join(self.SwathPath, f'frequency{frequency}', polarization)
263-
return datasetPath
264-
265-
266293
# end of file
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# -*- coding: utf-8 -*-
22

3-
from .Base import Base
3+
from .Base import Base, get_hdf5_file_root_path
44

55
# end of file

0 commit comments

Comments
 (0)