Skip to content

Commit 315dea0

Browse files
yunjunzLiangJYu
andauthored
s1_reader: refactor track_burst_num + optional noise + add safe_filename (#70)
+ s1_reader: - add `get_track_burst_num()` to read the start/stop burst number info from ESA. This returns a dict, which could simplify the logic of converting the local burst num to the global version. - `burst_from_xml()`: skip reading the noise annotation files if not necessary, to support downloaded metadata using asfsmd without noise/slc data - `_burst_from_safe_dir()`: replace warning with print, because 1) it's very common if using metadata downloaded from `asfsmd`, and a lot of warning msg can annoying and not helpful; 2) consistent with _burst_from_zip() in term if log/print msg level. + s1_burst_slc.Sentinel1BurstSlc: - add new field "safe_filename" - adjust the swath_name() from the previous version of burst IDs to the latest ESA's version. + README: turn ON auto_download in the example usage Co-authored-by: Liang Yu <[email protected]>
1 parent c30aaf1 commit 315dea0

File tree

3 files changed

+64
-39
lines changed

3 files changed

+64
-39
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ pol = "VV"
4949

5050
# read orbits
5151
orbit_dir = '/home/user/data/sentinel1_orbits'
52-
orbit_path = s1reader.get_orbit_file_from_dir(zip_path, orbit_dir)
52+
orbit_path = s1reader.get_orbit_file_from_dir(zip_path, orbit_dir, auto_download=True)
5353

5454
# returns the list of the bursts
5555
bursts = s1reader.load_bursts(zip_path, orbit_path, swath_num, pol)

src/s1reader/s1_burst_slc.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,7 @@ class Sentinel1BurstSlc:
148148
polarization: str # {VV, VH, HH, HV}
149149
burst_id: str # t{track_number}_{burst_index}_iw{1,2,3}
150150
platform_id: str # S1{A,B}
151+
safe_filename: str # SAFE file name
151152
center: tuple # {center lon, center lat} in degrees
152153
border: list # list of lon, lat coordinate tuples (in degrees) representing burst border
153154
orbit: isce3.core.Orbit
@@ -625,7 +626,7 @@ def width(self):
625626
@property
626627
def swath_name(self):
627628
'''Swath name in iw1, iw2, iw3.'''
628-
return self.burst_id.split('_')[1]
629+
return self.burst_id.split('_')[2]
629630

630631
@property
631632
def bbox(self):

src/s1reader/s1_reader.py

Lines changed: 61 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -307,6 +307,31 @@ def is_eap_correction_necessary(ipf_version: version.Version) -> SimpleNamespace
307307

308308
return eap
309309

310+
def get_track_burst_num(track_burst_num_file: str = esa_track_burst_id_file):
311+
'''Read the start / stop burst number info of each track from ESA.
312+
313+
Parameters:
314+
-----------
315+
track_burst_num_file : str
316+
Path to the track burst number files.
317+
318+
Returns:
319+
--------
320+
track_burst_num : dict
321+
Dictionary where each key is the track number, and each value is a list
322+
of two integers for the start and stop burst number
323+
'''
324+
325+
# read the text file to list
326+
track_burst_info = np.loadtxt(track_burst_num_file, dtype=int)
327+
328+
# convert lists into dict
329+
track_burst_num = dict()
330+
for track_num, burst_num0, burst_num1 in track_burst_info:
331+
track_burst_num[track_num] = [burst_num0, burst_num1]
332+
333+
return track_burst_num
334+
310335
def burst_from_xml(annotation_path: str, orbit_path: str, tiff_path: str,
311336
iw2_annotation_path: str, open_method=open):
312337
'''Parse bursts in Sentinel-1 annotation XML.
@@ -331,16 +356,13 @@ def burst_from_xml(annotation_path: str, orbit_path: str, tiff_path: str,
331356
List of Sentinel1BurstSlc objects found in annotation XML.
332357
'''
333358

334-
# a 1D array where the indices are the Sentinel-1 track number
335-
# and the data at each row are the corresponding cumulative ID
336-
# number for the last burst of the given track (i.e., line number)
337-
# get last burst ID number of each track and prepend 0
338-
tracks_burst_id = np.insert(np.loadtxt(esa_track_burst_id_file,
339-
usecols=[2], dtype=int),
340-
0, 0)
359+
# a dict where the key is the track number and the value is a list of
360+
# two integers for the start and stop burst number
361+
track_burst_num = get_track_burst_num()
341362

342363
_, tail = os.path.split(annotation_path)
343-
platform_id, subswath_id, _, pol = [x.upper() for x in tail.split('-')[:4]]
364+
platform_id, swath_name, _, pol = [x.upper() for x in tail.split('-')[:4]]
365+
safe_filename = os.path.basename(annotation_path.split('.SAFE')[0])
344366

345367
# For IW mode, one burst has a duration of ~2.75 seconds and a burst
346368
# overlap of approximately ~0.4 seconds.
@@ -360,17 +382,20 @@ def burst_from_xml(annotation_path: str, orbit_path: str, tiff_path: str,
360382
# tree_lads = ET.parse(f_lads)
361383
# product_annotation = s1_annotation.ProductAnnotation.from_et(tree_lads)
362384

363-
#load the Calibraton annotation
364-
calibration_annotation_path = annotation_path.replace('annotation/', 'annotation/calibration/calibration-')
365-
with open_method(calibration_annotation_path, 'r') as f_cads:
366-
tree_cads = ET.parse(f_cads)
367-
calibration_annotation = s1_annotation.CalibrationAnnotation.from_et(tree_cads)
385+
eap = is_eap_correction_necessary(ipf_version)
386+
if eap.magnitude_correction or eap.phase_correction:
368387

369-
#load the Noise annotation
370-
noise_annotation_path = annotation_path.replace('annotation/', 'annotation/calibration/noise-')
371-
with open_method(noise_annotation_path, 'r') as f_nads:
372-
tree_nads = ET.parse(f_nads)
373-
noise_annotation = s1_annotation.NoiseAnnotation.from_et(tree_nads, ipf_version)
388+
#load the Calibraton annotation
389+
calibration_annotation_path = annotation_path.replace('annotation/', 'annotation/calibration/calibration-')
390+
with open_method(calibration_annotation_path, 'r') as f_cads:
391+
tree_cads = ET.parse(f_cads)
392+
calibration_annotation = s1_annotation.CalibrationAnnotation.from_et(tree_cads)
393+
394+
#load the Noise annotation
395+
noise_annotation_path = annotation_path.replace('annotation/', 'annotation/calibration/noise-')
396+
with open_method(noise_annotation_path, 'r') as f_nads:
397+
tree_nads = ET.parse(f_nads)
398+
noise_annotation = s1_annotation.NoiseAnnotation.from_et(tree_nads, ipf_version)
374399

375400
# Nearly all metadata loaded here is common to all bursts in annotation XML
376401
with open_method(annotation_path, 'r') as f:
@@ -443,16 +468,11 @@ def burst_from_xml(annotation_path: str, orbit_path: str, tiff_path: str,
443468
sensing_starts[i] = sensing_start
444469
sensing_times[i] = as_datetime(burst_list_element.find('sensingTime').text)
445470
dt = sensing_times[i] - ascending_node_time
446-
id_burst = int((dt.seconds + dt.microseconds / 1e6) // burst_interval)
447-
448-
# To be consistent with ESA let's start the counter of the ID
449-
# from 1 instead of from 0, i,e, the ID of the first burst of the
450-
# first track is 1
451-
id_burst += 1
471+
# local burst_num within one track, starting from 0
472+
burst_num = int((dt.seconds + dt.microseconds / 1e6) // burst_interval)
452473

453-
# the IDs are currently local to one track. Let's adjust based on
454-
# the last ID of the previous track
455-
id_burst += tracks_burst_id[track_number-1]
474+
# convert the local burst_num to the global burst_num, starting from 1
475+
burst_num += track_burst_num[track_number][0]
456476

457477
# choose nearest azimuth FM rate
458478
d_seconds = 0.5 * (n_lines - 1) * azimuth_time_interval
@@ -487,29 +507,32 @@ def burst_from_xml(annotation_path: str, orbit_path: str, tiff_path: str,
487507
last_valid_samples[last_line])
488508

489509

490-
burst_id = f't{track_number:03d}_{id_burst}_{subswath_id.lower()}'
510+
burst_id = f't{track_number:03d}_{burst_num}_{swath_name.lower()}'
491511

492512

493513
#Extract burst-wise information for Calibration, Noise, and EAP correction
494-
burst_calibration = s1_annotation.BurstCalibration.from_calibration_annotation(calibration_annotation, sensing_start)
495-
bursts_noise=s1_annotation.BurstNoise()
496-
bursts_noise.from_noise_annotation(noise_annotation,sensing_start,i*n_lines,(i+1)*n_lines-1,ipf_version)
514+
if eap.magnitude_correction or eap.phase_correction:
515+
burst_calibration = s1_annotation.BurstCalibration.from_calibration_annotation(calibration_annotation, sensing_start)
516+
bursts_noise = s1_annotation.BurstNoise()
517+
bursts_noise.from_noise_annotation(noise_annotation,sensing_start,i*n_lines,(i+1)*n_lines-1,ipf_version)
518+
else:
519+
burst_calibration = None
520+
bursts_noise = None
497521

498522
bursts[i] = Sentinel1BurstSlc(ipf_version, sensing_start, radar_freq, wavelength,
499523
azimuth_steer_rate, azimuth_time_interval,
500524
slant_range_time, starting_range, iw2_mid_range,
501525
range_sampling_rate, range_pxl_spacing,
502526
(n_lines, n_samples), az_fm_rate, doppler,
503527
rng_processing_bandwidth, pol, burst_id,
504-
platform_id, center_pts[i],
528+
platform_id, safe_filename, center_pts[i],
505529
boundary_pts[i], orbit, orbit_direction, orbit_number,
506530
tiff_path, i, first_valid_sample,
507531
last_sample, first_valid_line, last_line,
508532
range_window_type, range_window_coeff,
509533
rank, prf_raw_data, range_chirp_ramp_rate,
510534
burst_calibration, bursts_noise, None)
511535

512-
513536
return bursts
514537

515538
def _is_zip_annotation_xml(path: str, id_str: str) -> bool:
@@ -534,8 +557,8 @@ def _is_zip_annotation_xml(path: str, id_str: str) -> bool:
534557
return True
535558
return False
536559

537-
def load_bursts(path: str, orbit_path: str, swath_num: int, pol: str='vv',
538-
burst_ids: list[str]=None):
560+
def load_bursts(path: str, orbit_path: str, swath_num: int, pol: str = 'vv',
561+
burst_ids: list[str] = None):
539562
'''Find bursts in a Sentinel-1 zip file or a SAFE structured directory.
540563
541564
Parameters:
@@ -685,8 +708,9 @@ def _burst_from_safe_dir(safe_dir_path: str, id_str: str, orbit_path: str):
685708
if 'measurement' in f and id_str in f and 'tiff' in f]
686709
f_tiff = f'{safe_dir_path}/measurement/{f_tiff[0]}' if f_tiff else ''
687710
else:
688-
warning_str = f'measurement directory not found in {safe_dir_path}'
689-
warnings.warn(warning_str)
711+
msg = f'measurement directory NOT found in {safe_dir_path}'
712+
msg += ', continue with metadata only.'
713+
print(msg)
690714
f_tiff = ''
691715

692716
bursts = burst_from_xml(f_annotation, orbit_path, f_tiff, iw2_f_annotation)

0 commit comments

Comments
 (0)