Skip to content

Commit d665c9a

Browse files
priit111Achaad
andauthored
fix: Fix issue with improper geolocation of IMP products. (#51)
* Fix issue with improper geolocation of IMP products. Fix antenna gain with IMS mode. * style: extract code into auxiliary functions * style: extract code into auxiliary functions * style: added missing type hints --------- Co-authored-by: Achaad <achaad@achaad.eu>
1 parent e56d638 commit d665c9a

2 files changed

Lines changed: 305 additions & 188 deletions

File tree

asar_xarray/asar.py

Lines changed: 53 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ def get_metadata(gdal_dataset: gdal.Dataset) -> Dict[str, Any]:
2525
:param gdal_dataset: Dataset with metadata
2626
:return: Dictionary with metadata attributes
2727
"""
28-
attributes: dict[str, Any] = dict()
28+
attributes: dict[str, Any] = {}
2929
process_general_metadata(gdal_dataset, attributes)
3030
process_records_metadata(gdal_dataset, attributes)
3131
process_derived_subdatasets_metadata(gdal_dataset, attributes)
@@ -35,35 +35,36 @@ def get_metadata(gdal_dataset: gdal.Dataset) -> Dict[str, Any]:
3535
return attributes
3636

3737

38-
def open_asar_dataset(filepath: str | os.PathLike[Any] | ReadBuffer[Any] | AbstractDataStore) -> xr.Dataset:
38+
def open_asar_dataset(filename_or_obj: str | os.PathLike[Any] | ReadBuffer[
39+
Any] | bytes | memoryview | AbstractDataStore) -> xr.Dataset:
3940
"""
4041
Open an ASAR dataset and converts it into an xarray Dataset.
4142
4243
This function reads the metadata and pixel data from the given ASAR dataset file,
4344
processes the metadata into attributes, and constructs an xarray Dataset.
4445
45-
:param filepath: The path to the ASAR dataset file. It can be a string,
46+
:param filename_or_obj: The path to the ASAR dataset file. It can be a string,
4647
a PathLike object, a ReadBuffer, or an AbstractDataStore.
4748
:return: An xarray Dataset containing the pixel data and metadata attributes.
4849
:raises NotImplementedError: If the filepath is not a string.
4950
"""
50-
if not isinstance(filepath, str):
51-
raise NotImplementedError(f'Filepath type {type(filepath)} is not supported')
51+
if not isinstance(filename_or_obj, str):
52+
raise NotImplementedError(f'Filepath type {type(filename_or_obj)} is not supported')
5253

53-
logger.debug('Opening ASAR dataset {}', filepath)
54+
logger.debug('Opening ASAR dataset {}', filename_or_obj)
5455

5556
# Open the dataset using a custom reader
56-
gdal_dataset: gdal.Dataset = reader.get_gdal_dataset(filepath)
57+
gdal_dataset: gdal.Dataset = reader.get_gdal_dataset(filename_or_obj)
5758

5859
# Extract metadata attributes
5960
metadata = get_metadata(gdal_dataset)
6061

6162
# Duplicate, read directly from file, as gdal does not parse some necessary metadata
6263

63-
metadata["direct_parse"] = envisat_direct.parse_direct(filepath, metadata)
64+
metadata["direct_parse"] = envisat_direct.parse_direct(filename_or_obj, metadata)
6465

6566
# Create an xarray Dataset with pixel data and metadata attributes
66-
dataset: xr.Dataset = create_dataset(metadata, filepath)
67+
dataset: xr.Dataset = create_dataset(metadata, filename_or_obj)
6768

6869
return dataset
6970

@@ -89,10 +90,16 @@ def create_dataset(metadata: dict[str, Any], filepath: str) -> xr.Dataset:
8990
range_sampling_rate = metadata["records"]["main_processing_params"]["range_samp_rate"]
9091
image_slant_range_time = metadata["direct_parse"]["slant_time_first"] * 1e-9
9192

92-
if metadata["sph_descriptor"] == "Image Mode SLC Image":
93+
range_pixel_spacing = metadata["records"]["main_processing_params"]["range_spacing"]
94+
95+
product_str = metadata["sph_descriptor"]
96+
97+
if product_str == "Image Mode SLC Image":
9398
product_type = "SLC"
99+
elif product_str == "Image Mode Precision Image":
100+
product_type = "GRD"
94101
else:
95-
raise RuntimeError("Only Image mode SLC files(IMS) supported for now")
102+
raise RuntimeError("Only Image mode files supported(IMS & IMP) supported for now")
96103

97104
attrs = {
98105
"family_name": "Envisat",
@@ -106,8 +113,7 @@ def create_dataset(metadata: dict[str, Any], filepath: str) -> xr.Dataset:
106113
"product_type": product_type,
107114
"start_time": product_first_line_utc_time,
108115
"stop_time": product_last_line_utc_time,
109-
"range_pixel_spacing" : metadata["range_spacing"],
110-
"azimuth_pixel_spacing" : metadata["azimuth_spacing"],
116+
"range_pixel_spacing": metadata["range_spacing"],
111117
"radar_frequency": metadata["records"]["main_processing_params"]["radar_freq"] / 1e9,
112118
"ascending_node_time": "",
113119
"azimuth_pixel_spacing": metadata["records"]["main_processing_params"]["azimuth_spacing"],
@@ -116,15 +122,14 @@ def create_dataset(metadata: dict[str, Any], filepath: str) -> xr.Dataset:
116122
"azimuth_time_interval": azimuth_time_interval,
117123
"image_slant_range_time": image_slant_range_time,
118124
"range_sampling_rate": range_sampling_rate,
119-
"incidence_angle_mid_swath": metadata["direct_parse"]["incidence_angle_center"] * 2 * math.pi / 360 ,
125+
"incidence_angle_mid_swath": metadata["direct_parse"]["incidence_angle_center"] * 2 * math.pi / 360,
120126
"metadata": metadata
121127
}
122128

123129
azimuth_time = compute_azimuth_time(
124130
product_first_line_utc_time, product_last_line_utc_time, number_of_lines
125131
)
126132

127-
128133
swap_dims = {"line": "azimuth_time", "pixel": "slant_range_time"}
129134

130135
coords: dict[str, Any] = {
@@ -140,12 +145,34 @@ def create_dataset(metadata: dict[str, Any], filepath: str) -> xr.Dataset:
140145
),
141146
}
142147

143-
slant_range_time = np.linspace(
144-
image_slant_range_time,
145-
image_slant_range_time + (number_of_samples - 1) / range_sampling_rate,
146-
number_of_samples,
147-
)
148-
coords["slant_range_time"] = ("pixel", slant_range_time)
148+
if product_type == "SLC":
149+
slant_range_time = np.linspace(
150+
image_slant_range_time,
151+
image_slant_range_time + (number_of_samples - 1) / range_sampling_rate,
152+
number_of_samples,
153+
)
154+
coords["slant_range_time"] = ("pixel", slant_range_time)
155+
else:
156+
157+
# generate slant range times from ground ranges for ground range products
158+
# this is easier due to the Envisat only having GRSR metadata unlike Sentinel1
159+
# however xarray/Sarsen handle GRD conversion differently for S1
160+
ground_range = np.linspace(
161+
0,
162+
range_pixel_spacing * (number_of_samples - 1),
163+
number_of_samples,
164+
)
165+
166+
# numpy polyval expects the polynomial top be highest ranked first
167+
coeffs = list(reversed(metadata["direct_parse"]["grsr_coeffs"]))
168+
169+
slant_ranges = np.polyval(coeffs, ground_range)
170+
slant_ranges *= 2
171+
172+
c = 299792458
173+
slant_range_times = slant_ranges / c
174+
175+
coords["slant_range_time"] = ("pixel", slant_range_times)
149176

150177
data = xr.open_dataarray(filepath, engine='rasterio')
151178
data.encoding.clear()
@@ -168,19 +195,17 @@ def get_chirp_parameters(dataset: gdal.Dataset) -> dict[str, Any]:
168195
:return: dictionary with chirp parameters.
169196
"""
170197
metadata = dataset.GetMetadata(domain='RECORDS')
171-
params: dict[str, Any] = dict()
198+
params: dict[str, Any] = {
199+
'zero_doppler_time': utils.get_envisat_time(metadata.get('CHIRP_PARAMS_ADS_ZERO_DOPPLER_TIME')),
200+
'attach_flag': bool(int(metadata.get('CHIRP_PARAMS_ADS_ATTACH_FLAG', '0'))),
201+
'beam_id': metadata.get('CHIRP_PARAMS_ADS_BEAM_ID'), 'polarisation': metadata.get('CHIRP_PARAMS_ADS_POLAR'),
202+
'chirp': {}}
172203
# Non-float values
173-
params['zero_doppler_time'] = utils.get_envisat_time(metadata.get('CHIRP_PARAMS_ADS_ZERO_DOPPLER_TIME'))
174-
params['attach_flag'] = bool(int(metadata.get('CHIRP_PARAMS_ADS_ATTACH_FLAG', '0')))
175-
params['beam_id'] = metadata.get('CHIRP_PARAMS_ADS_BEAM_ID')
176-
params['polarisation'] = metadata.get('CHIRP_PARAMS_ADS_POLAR')
177-
params['chirp'] = dict()
178204
for key, value in metadata.items():
179205
if 'CHIRP_PARAMS_ADS_CHIRP' in key:
180206
new_key = key.replace('CHIRP_PARAMS_ADS_CHIRP_', '').lower()
181207
params['chirp'][new_key] = float(value)
182208

183-
#params['elev_corr_factor'] = float(metadata.get('CHIRP_PARAMS_ADS_ELEV_CORR_FACTOR'))
184209
params['cal_pulse_info'] = get_chirp_cal_pulse_info(metadata)
185210

186211
return params

0 commit comments

Comments
 (0)