dist-s1-enumerator 1.0.2__tar.gz → 1.0.4__tar.gz
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.
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/CHANGELOG.md +19 -3
- {dist_s1_enumerator-1.0.2/src/dist_s1_enumerator.egg-info → dist_s1_enumerator-1.0.4}/PKG-INFO +1 -1
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/asf.py +18 -18
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/dist_enum.py +37 -18
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/dist_enum_inputs.py +1 -1
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4/src/dist_s1_enumerator.egg-info}/PKG-INFO +1 -1
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/test_dist_enum.py +111 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/dependabot.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/bump-tag-for-release.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/changelog-updated.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/github-release.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/labeled-pr.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/nb_tests.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/pypi-release.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/static_analysis.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/tests.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.gitignore +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/LICENSE +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/README.md +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/environment.yml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/notebooks/A__Staging_Inputs_for_One_MGRS_Tile.ipynb +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/notebooks/B__Enumerate_MGRS_tile.ipynb +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/pyproject.toml +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/setup.cfg +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/__init__.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/data/jpl_burst_geo.parquet +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/data/mgrs.parquet +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/data/mgrs_burst_lookup_table.parquet +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/exceptions.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/mgrs_burst_data.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/param_models.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/py.typed +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/rtc_s1_io.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/tabular_models.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/SOURCES.txt +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/dependency_links.txt +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/not-zip-safe +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/requires.txt +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/top_level.txt +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/conftest.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/mock_ts_data_for_enum.ipynb +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/rtc_s1_ts_metadata/chile_19HBD.parquet +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/rtc_s1_ts_metadata/mgrs01UBT.parquet +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/rtc_s1_ts_metadata/mgrs11SLT_11SLU_11SMT.parquet +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/rtc_s1_ts_metadata/mgrs15RXN__track63.parquet +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/rtc_s1_ts_metadata/mgrs22WFD.parquet +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/ts_data_for_workflow_inputs.ipynb +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/test_asf.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/test_dist_enum_inputs.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/test_mgrs_burst_data.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/test_notebooks.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/test_package.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/test_param_model.py +0 -0
- {dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/test_rtc_s1_io.py +0 -0
|
@@ -6,15 +6,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
|
6
6
|
and this project adheres to [PEP 440](https://www.python.org/dev/peps/pep-0440/)
|
|
7
7
|
and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
8
8
|
|
|
9
|
+
## [1.0.4] - 2025-09-29
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
* Update time-series enumeration for multiple polarizations within an MGRS tile.
|
|
13
|
+
- We now ensure that for each MGRS tile, a single fixed spatial burst creates a baseline (set of pre-images) for a given RTC-S1 burst product. That is, if the recent data was VV+VH in a burst, then the baseline for that burst VV+VH. Multiple dual polarization (i.e. both VV+VH and HH+HV) data can be used within a single MGRS tile.
|
|
14
|
+
* We now ensure that single polarization data is excluded from baselines and not used in the creation of the post-image set.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
* Bug in enumerating 1 product.
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## [1.0.3] - 2025-09-09
|
|
21
|
+
|
|
22
|
+
### Fixed
|
|
23
|
+
* Defaults for `lookback_delta_days` from 0 to 365 in enumeration of multiple products. Leading to submission of jobs that had no baseline (see Issue: https://github.com/opera-adt/dist-s1-enumerator/issues/44)
|
|
24
|
+
* Renamed variables for easier tracking in `enumerat_dist_s1_products`.
|
|
25
|
+
|
|
9
26
|
## [1.0.2] - 2025-09-09
|
|
10
27
|
|
|
11
|
-
|
|
28
|
+
### Changed
|
|
12
29
|
* `backoff` library is removed and we now use `tenacity`
|
|
13
30
|
|
|
14
|
-
|
|
31
|
+
### Added
|
|
15
32
|
* Uses sessions and adapters to handle mutiple concurrent requests more reliably.
|
|
16
33
|
|
|
17
|
-
|
|
18
34
|
## [1.0.1] - 2025-08-07
|
|
19
35
|
|
|
20
36
|
### Added
|
{dist_s1_enumerator-1.0.2/src/dist_s1_enumerator.egg-info → dist_s1_enumerator-1.0.4}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dist-s1-enumerator
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Enumeration and ops library for the OPERA DIST-S1 project
|
|
5
5
|
Author-email: "Richard West, Charlie Marshak, Talib Oliver-Cabrera, and Jungkyo Jung" <charlie.z.marshak@jpl.nasa.gov>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -72,6 +72,7 @@ def get_rtc_s1_ts_metadata_by_burst_ids(
|
|
|
72
72
|
start_acq_dt: str | datetime | None | pd.Timestamp = None,
|
|
73
73
|
stop_acq_dt: str | datetime | None | pd.Timestamp = None,
|
|
74
74
|
polarizations: str | None = None,
|
|
75
|
+
include_single_polarization: bool = False,
|
|
75
76
|
) -> gpd.GeoDataFrame:
|
|
76
77
|
"""Wrap/format the ASF search API for RTC-S1 metadata search. All searches go through this function.
|
|
77
78
|
|
|
@@ -138,34 +139,31 @@ def get_rtc_s1_ts_metadata_by_burst_ids(
|
|
|
138
139
|
df_rtc['polarizations'] = df_rtc['polarizations'].map(format_polarization)
|
|
139
140
|
if polarizations is not None:
|
|
140
141
|
ind_pol = df_rtc['polarizations'] == polarizations
|
|
141
|
-
|
|
142
|
+
elif not include_single_polarization:
|
|
142
143
|
ind_pol = df_rtc['polarizations'].isin(['HH+HV', 'VV+VH'])
|
|
144
|
+
else:
|
|
145
|
+
ind_pol = df_rtc['polarizations'].isin(['HH+HV', 'VV+VH', 'HH', 'HV', 'VV', 'VH'])
|
|
143
146
|
if not ind_pol.any():
|
|
144
|
-
|
|
147
|
+
warn(f'No valid dual polarization images found for {burst_ids}.')
|
|
145
148
|
# First get all the dual-polarizations images
|
|
146
149
|
df_rtc = df_rtc[ind_pol].reset_index(drop=True)
|
|
147
|
-
# Then check all the dual-polarizations are the same (either HH+HV or VV+VH)
|
|
148
|
-
# TODO: if there are mixtures, can DIST-S1 still be generated assuming they look the same?
|
|
149
|
-
polarizations_unique = df_rtc['polarizations'].unique().tolist()
|
|
150
|
-
if len(polarizations_unique) > 1:
|
|
151
|
-
raise ValueError(
|
|
152
|
-
f'Mixed dual polarizations found for {burst_ids}. That is, some images are HH+HV and others are VV+HV.'
|
|
153
|
-
)
|
|
154
|
-
else:
|
|
155
|
-
# Either HH+HV or VV+VH
|
|
156
|
-
copol, crosspol = polarizations_unique[0].split('+')
|
|
157
150
|
|
|
158
151
|
def get_url_by_polarization(prod_urls: list[str], polarization_token: str) -> list[str]:
|
|
159
|
-
|
|
152
|
+
if polarization_token == 'copol':
|
|
153
|
+
polarizations_allowed = ['VV', 'HH']
|
|
154
|
+
elif polarization_token == 'crosspol':
|
|
155
|
+
polarizations_allowed = ['HV', 'VH']
|
|
156
|
+
else:
|
|
157
|
+
raise ValueError(f'Invalid polarization token: {polarization_token}. Must be one of: copol, crosspol.')
|
|
158
|
+
possible_urls = [url for pol in polarizations_allowed for url in prod_urls if f'_{pol}.tif' == url[-7:]]
|
|
160
159
|
if len(possible_urls) == 0:
|
|
161
|
-
raise ValueError(f'No {
|
|
160
|
+
raise ValueError(f'No {polarizations_allowed} urls found')
|
|
162
161
|
if len(possible_urls) > 1:
|
|
163
|
-
|
|
164
|
-
raise ValueError(f'Multiple {polarization_token} urls found')
|
|
162
|
+
raise ValueError(f'Multiple {polarization_token} urls found: {", ".join(possible_urls)}')
|
|
165
163
|
return possible_urls[0]
|
|
166
164
|
|
|
167
|
-
url_copol = df_rtc.all_urls.map(lambda urls_for_prod: get_url_by_polarization(urls_for_prod, copol))
|
|
168
|
-
url_crosspol = df_rtc.all_urls.map(lambda urls_for_prod: get_url_by_polarization(urls_for_prod, crosspol))
|
|
165
|
+
url_copol = df_rtc.all_urls.map(lambda urls_for_prod: get_url_by_polarization(urls_for_prod, 'copol'))
|
|
166
|
+
url_crosspol = df_rtc.all_urls.map(lambda urls_for_prod: get_url_by_polarization(urls_for_prod, 'crosspol'))
|
|
169
167
|
|
|
170
168
|
df_rtc['url_copol'] = url_copol
|
|
171
169
|
df_rtc['url_crosspol'] = url_crosspol
|
|
@@ -187,6 +185,7 @@ def get_rtc_s1_metadata_from_acq_group(
|
|
|
187
185
|
start_acq_dt: datetime | str | None = None,
|
|
188
186
|
stop_acq_dt: datetime | str | None = None,
|
|
189
187
|
max_variation_seconds: float | None = None,
|
|
188
|
+
polarizations: str | None = None,
|
|
190
189
|
) -> gpd.GeoDataFrame:
|
|
191
190
|
"""
|
|
192
191
|
Meant for acquiring a pre-image or post-image set from MGRS tiles for a given S1 pass.
|
|
@@ -241,6 +240,7 @@ def get_rtc_s1_metadata_from_acq_group(
|
|
|
241
240
|
burst_ids,
|
|
242
241
|
start_acq_dt=start_acq_dt,
|
|
243
242
|
stop_acq_dt=stop_acq_dt,
|
|
243
|
+
polarizations=polarizations,
|
|
244
244
|
)
|
|
245
245
|
# Assumes that each group is ordered by date (earliest first and most recent last)
|
|
246
246
|
columns = df_rtc.columns
|
|
@@ -117,7 +117,6 @@ def enumerate_one_dist_s1_product(
|
|
|
117
117
|
max_variation_seconds=300,
|
|
118
118
|
n_images_per_burst=1,
|
|
119
119
|
)
|
|
120
|
-
|
|
121
120
|
if df_rtc_post.empty:
|
|
122
121
|
raise ValueError(f'No RTC-S1 post-images found for track {track_number} in MGRS tile {mgrs_tile_id}.')
|
|
123
122
|
|
|
@@ -137,6 +136,11 @@ def enumerate_one_dist_s1_product(
|
|
|
137
136
|
stop_acq_dt=stop_acq_dt,
|
|
138
137
|
n_images_per_burst=max_pre_imgs_per_burst,
|
|
139
138
|
)
|
|
139
|
+
df_unique_keys = df_rtc_post[['jpl_burst_id', 'polarizations']].drop_duplicates()
|
|
140
|
+
|
|
141
|
+
df_rtc_pre = pd.merge(df_rtc_pre, df_unique_keys, on=['jpl_burst_id', 'polarizations'], how='inner')
|
|
142
|
+
|
|
143
|
+
df_rtc_pre['input_category'] = 'pre'
|
|
140
144
|
|
|
141
145
|
elif lookback_strategy == 'multi_window':
|
|
142
146
|
df_rtc_pre_list = []
|
|
@@ -155,16 +159,22 @@ def enumerate_one_dist_s1_product(
|
|
|
155
159
|
latest_lookback = delta_lookback_day
|
|
156
160
|
start_acq_dt = post_date_min - timedelta(days=latest_lookback)
|
|
157
161
|
stop_acq_dt = post_date_min - timedelta(days=earliest_lookback)
|
|
158
|
-
|
|
162
|
+
df_rtc_pre_window = get_rtc_s1_metadata_from_acq_group(
|
|
159
163
|
[mgrs_tile_id],
|
|
160
164
|
track_numbers=track_numbers,
|
|
161
165
|
start_acq_dt=start_acq_dt,
|
|
162
166
|
stop_acq_dt=stop_acq_dt,
|
|
163
167
|
n_images_per_burst=max_pre_img_per_burst,
|
|
168
|
+
polarizations=None,
|
|
164
169
|
)
|
|
170
|
+
df_unique_keys = df_rtc_post[['jpl_burst_id', 'polarizations']].drop_duplicates()
|
|
165
171
|
|
|
166
|
-
|
|
167
|
-
|
|
172
|
+
df_rtc_pre_window = pd.merge(
|
|
173
|
+
df_rtc_pre_window, df_unique_keys, on=['jpl_burst_id', 'polarizations'], how='inner'
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
if not df_rtc_pre_window.empty:
|
|
177
|
+
df_rtc_pre_list.append(df_rtc_pre_window)
|
|
168
178
|
|
|
169
179
|
df_rtc_pre = pd.concat(df_rtc_pre_list, ignore_index=True) if df_rtc_pre_list else pd.DataFrame()
|
|
170
180
|
|
|
@@ -179,7 +189,7 @@ def enumerate_one_dist_s1_product(
|
|
|
179
189
|
df_rtc_pre = df_rtc_pre[df_rtc_pre.jpl_burst_id.isin(burst_ids_with_min_pre_images)].reset_index(drop=True)
|
|
180
190
|
|
|
181
191
|
post_burst_ids = df_rtc_post.jpl_burst_id.unique().tolist()
|
|
182
|
-
pre_burst_ids =
|
|
192
|
+
pre_burst_ids = df_rtc_pre.jpl_burst_id.unique().tolist()
|
|
183
193
|
|
|
184
194
|
final_burst_ids = list(set(post_burst_ids) & set(pre_burst_ids))
|
|
185
195
|
df_rtc_pre = df_rtc_pre[df_rtc_pre.jpl_burst_id.isin(final_burst_ids)].reset_index(drop=True)
|
|
@@ -218,7 +228,7 @@ def enumerate_dist_s1_products(
|
|
|
218
228
|
max_pre_imgs_per_burst: int = (5, 5, 5),
|
|
219
229
|
min_pre_imgs_per_burst: int = 1,
|
|
220
230
|
tqdm_enabled: bool = True,
|
|
221
|
-
delta_lookback_days: int =
|
|
231
|
+
delta_lookback_days: int = 365,
|
|
222
232
|
delta_window_days: int = 365,
|
|
223
233
|
) -> gpd.GeoDataFrame:
|
|
224
234
|
"""
|
|
@@ -254,7 +264,7 @@ def enumerate_dist_s1_products(
|
|
|
254
264
|
tqdm_enabled : bool, optional
|
|
255
265
|
Whether to enable tqdm progress bars, by default True.
|
|
256
266
|
delta_lookback_days : int, optional
|
|
257
|
-
When to set the most recent pre-image date. Default is
|
|
267
|
+
When to set the most recent pre-image date. Default is 365.
|
|
258
268
|
If lookback strategy is 'multi_window', this means the maximum number of days to search for pre-images on each
|
|
259
269
|
anniversary date where `post_date - n * lookback_days` are the anniversary dates for n = 1,....
|
|
260
270
|
If lookback strategy is 'immediate_lookback', this must be set to 0.
|
|
@@ -308,10 +318,15 @@ def enumerate_dist_s1_products(
|
|
|
308
318
|
ind_time = (df_rtc_ts_tile_track.acq_dt < window_stop) & (
|
|
309
319
|
df_rtc_ts_tile_track.acq_dt >= window_start
|
|
310
320
|
)
|
|
321
|
+
df_rtc_ts_tile_track_filtered = df_rtc_ts_tile_track[ind_time].reset_index(drop=True)
|
|
311
322
|
# Select images that are present in the post-image
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
323
|
+
df_unique_keys = df_rtc_post[['jpl_burst_id', 'polarizations']].drop_duplicates()
|
|
324
|
+
df_rtc_pre = pd.merge(
|
|
325
|
+
df_rtc_ts_tile_track_filtered,
|
|
326
|
+
df_unique_keys,
|
|
327
|
+
on=['jpl_burst_id', 'polarizations'],
|
|
328
|
+
how='inner',
|
|
329
|
+
)
|
|
315
330
|
df_rtc_pre['input_category'] = 'pre'
|
|
316
331
|
|
|
317
332
|
# It is unclear how merging when multiple MGRS tiles are provided will impact order so this
|
|
@@ -332,7 +347,7 @@ def enumerate_dist_s1_products(
|
|
|
332
347
|
# Loop over the different lookback days
|
|
333
348
|
df_rtc_pre_list = []
|
|
334
349
|
zipped_data = list(zip(params.delta_lookback_days, params.max_pre_imgs_per_burst))
|
|
335
|
-
for delta_lookback_day,
|
|
350
|
+
for delta_lookback_day, max_pre_img_per_burst_param in zipped_data:
|
|
336
351
|
delta_lookback_timedelta = pd.Timedelta(delta_lookback_day, unit='D')
|
|
337
352
|
delta_window_timedelta = pd.Timedelta(params.delta_window_days, unit='D')
|
|
338
353
|
window_start = post_date - delta_lookback_timedelta - delta_window_timedelta
|
|
@@ -343,10 +358,15 @@ def enumerate_dist_s1_products(
|
|
|
343
358
|
ind_time = (df_rtc_ts_tile_track.acq_dt < window_stop) & (
|
|
344
359
|
df_rtc_ts_tile_track.acq_dt >= window_start
|
|
345
360
|
)
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
df_rtc_pre =
|
|
361
|
+
df_rtc_ts_tile_track_filtered = df_rtc_ts_tile_track[ind_time].reset_index(drop=True)
|
|
362
|
+
|
|
363
|
+
df_unique_keys = df_rtc_post[['jpl_burst_id', 'polarizations']].drop_duplicates()
|
|
364
|
+
df_rtc_pre = pd.merge(
|
|
365
|
+
df_rtc_ts_tile_track_filtered,
|
|
366
|
+
df_unique_keys,
|
|
367
|
+
on=['jpl_burst_id', 'polarizations'],
|
|
368
|
+
how='inner',
|
|
369
|
+
)
|
|
350
370
|
df_rtc_pre['input_category'] = 'pre'
|
|
351
371
|
|
|
352
372
|
# It is unclear how merging when multiple MGRS tiles are provided will impact order so this
|
|
@@ -354,20 +374,19 @@ def enumerate_dist_s1_products(
|
|
|
354
374
|
df_rtc_pre = df_rtc_pre.sort_values(by='acq_dt', ascending=True).reset_index(drop=True)
|
|
355
375
|
# Assume the data is sorted by acquisition date
|
|
356
376
|
df_rtc_pre = (
|
|
357
|
-
df_rtc_pre.groupby('jpl_burst_id').tail(
|
|
377
|
+
df_rtc_pre.groupby('jpl_burst_id').tail(max_pre_img_per_burst_param).reset_index(drop=True)
|
|
358
378
|
)
|
|
359
379
|
|
|
360
380
|
if df_rtc_pre.empty:
|
|
361
381
|
continue
|
|
362
382
|
|
|
363
383
|
if not df_rtc_pre.empty:
|
|
364
|
-
df_rtc_pre_list.append(df_rtc_pre)
|
|
384
|
+
df_rtc_pre_list.append(df_rtc_pre)
|
|
365
385
|
|
|
366
386
|
# Concatenate all df_rtc_pre into a single DataFrame
|
|
367
387
|
df_rtc_pre_final = (
|
|
368
388
|
pd.concat(df_rtc_pre_list, ignore_index=True) if df_rtc_pre_list else pd.DataFrame()
|
|
369
389
|
)
|
|
370
|
-
# product and provenance
|
|
371
390
|
df_rtc_product = pd.concat([df_rtc_pre_final, df_rtc_post]).reset_index(drop=True)
|
|
372
391
|
df_rtc_product['product_id'] = product_id
|
|
373
392
|
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/dist_enum_inputs.py
RENAMED
|
@@ -33,7 +33,7 @@ def enumerate_dist_s1_workflow_inputs(
|
|
|
33
33
|
lookback_strategy: str = 'multi_window',
|
|
34
34
|
max_pre_imgs_per_burst: int | list[int] | tuple[int, ...] = (5, 5, 5),
|
|
35
35
|
min_pre_imgs_per_burst: int = 1,
|
|
36
|
-
delta_lookback_days: int | list[int] | tuple[int, ...] =
|
|
36
|
+
delta_lookback_days: int | list[int] | tuple[int, ...] = 365,
|
|
37
37
|
delta_window_days: int = 365,
|
|
38
38
|
df_ts: gpd.GeoDataFrame | None = None,
|
|
39
39
|
) -> list[dict]:
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4/src/dist_s1_enumerator.egg-info}/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dist-s1-enumerator
|
|
3
|
-
Version: 1.0.
|
|
3
|
+
Version: 1.0.4
|
|
4
4
|
Summary: Enumeration and ops library for the OPERA DIST-S1 project
|
|
5
5
|
Author-email: "Richard West, Charlie Marshak, Talib Oliver-Cabrera, and Jungkyo Jung" <charlie.z.marshak@jpl.nasa.gov>
|
|
6
6
|
License: Apache-2.0
|
|
@@ -247,3 +247,114 @@ def test_burst_ids_consistent_between_pre_and_post(mgrs_tile_ids: list[str], tra
|
|
|
247
247
|
df_pre = df_product[df_product['input_category'] == 'pre'].reset_index(drop=True)
|
|
248
248
|
df_post = df_product[df_product['input_category'] == 'post'].reset_index(drop=True)
|
|
249
249
|
assert sorted(df_pre['jpl_burst_id'].unique().tolist()) == sorted(df_post['jpl_burst_id'].unique().tolist())
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
@pytest.mark.integration
|
|
253
|
+
def test_dist_enum_one_with_multi_window_with_multiple_polarizations_and_asf_daac() -> None:
|
|
254
|
+
"""Test enumeration of 1 product with multi_window strategy with multiple dual polarization data.
|
|
255
|
+
|
|
256
|
+
Context: MGRS Tile 20TLP: https://search.asf.alaska.edu/#/?polygon=
|
|
257
|
+
POLYGON((-65.5041%2044.226,-65.4632%2043.2383,-64.1113%2043.2594,-64.1298%2044.2478,-65.5041%2044.226))
|
|
258
|
+
&start=2025-09-18T07:00:00Z&end=2025-09-20T06:59:59Z&resultsLoaded=true&zoom=8.078
|
|
259
|
+
¢er=-63.112,42.844&dataset=OPERA-S1&productTypes=RTC
|
|
260
|
+
&granule=OPERA_L2_RTC-S1_T171-365960-IW2_20250919T102314Z_20250919T135744Z_S1C_30_v1.0
|
|
261
|
+
"""
|
|
262
|
+
df_product = enumerate_one_dist_s1_product(
|
|
263
|
+
'20TLP',
|
|
264
|
+
track_number=171,
|
|
265
|
+
post_date='2025-09-19',
|
|
266
|
+
lookback_strategy='multi_window',
|
|
267
|
+
# Need to look back further for valid VV+VH data
|
|
268
|
+
delta_lookback_days=(1460, 1095, 730, 365),
|
|
269
|
+
max_pre_imgs_per_burst=(3, 3, 3, 4),
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
assert sorted(df_product.polarizations.unique().tolist()) == ['HH+HV', 'VV+VH']
|
|
273
|
+
|
|
274
|
+
df_sample_vvvh_burst = df_product[df_product.jpl_burst_id == 'T171-365965-IW3'].reset_index(drop=True)
|
|
275
|
+
dates_for_sample_vvvh_burst = sorted(df_sample_vvvh_burst['acq_date_for_mgrs_pass'].unique().tolist())
|
|
276
|
+
# Note the last date is the post date
|
|
277
|
+
expected_dates = ['2020-09-21', '2021-05-19', '2021-05-31', '2025-09-19']
|
|
278
|
+
assert dates_for_sample_vvvh_burst == expected_dates
|
|
279
|
+
|
|
280
|
+
# Check baseline data
|
|
281
|
+
# The post image is VV+VH
|
|
282
|
+
# Ref: https://search.asf.alaska.edu/#/?dataset=OPERA-S1&productTypes=RTC&operaBurstID=T171_365965_IW3&zoom=3.000
|
|
283
|
+
# ¢er=-74.108,31.979
|
|
284
|
+
# &resultsLoaded=true&granule=OPERA_L2_RTC-S1_T171-365965-IW3_20250919T102329Z_20250919T145901Z_S1C_30_v1.0
|
|
285
|
+
opera_ids = df_sample_vvvh_burst.opera_id.unique().tolist()
|
|
286
|
+
opera_ids_trunc = ['_'.join(op_id.split('_')[:5]) for op_id in opera_ids]
|
|
287
|
+
# another VV+VH image
|
|
288
|
+
assert 'OPERA_L2_RTC-S1_T171-365965-IW3_20200921T102347Z' in opera_ids_trunc
|
|
289
|
+
# a HH+HV image in the time series - there is only one image from 2024 so should be in if it weren't 2024
|
|
290
|
+
assert 'OPERA_L2_RTC-S1_T171-365965-IW3_20240427T102443Z' not in opera_ids_trunc
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
@pytest.mark.integration
|
|
294
|
+
def test_dist_enum_one_with_multi_window_with_asf_daac() -> None:
|
|
295
|
+
df_product = enumerate_one_dist_s1_product(
|
|
296
|
+
'11SLT',
|
|
297
|
+
track_number=144,
|
|
298
|
+
post_date='2025-06-19',
|
|
299
|
+
lookback_strategy='multi_window',
|
|
300
|
+
delta_lookback_days=(1095, 730, 365),
|
|
301
|
+
max_pre_imgs_per_burst=(3, 3, 4),
|
|
302
|
+
)
|
|
303
|
+
burst_ids_expected = [
|
|
304
|
+
'T144-308024-IW1',
|
|
305
|
+
'T144-308025-IW1',
|
|
306
|
+
'T144-308026-IW1',
|
|
307
|
+
'T144-308027-IW1',
|
|
308
|
+
'T144-308028-IW1',
|
|
309
|
+
'T144-308029-IW1',
|
|
310
|
+
'T144-308030-IW1',
|
|
311
|
+
'T144-308031-IW1',
|
|
312
|
+
]
|
|
313
|
+
assert sorted(df_product['jpl_burst_id'].unique().tolist()) == sorted(burst_ids_expected)
|
|
314
|
+
|
|
315
|
+
post_ind = df_product.input_category == 'post'
|
|
316
|
+
df_product_post = df_product[post_ind].reset_index(drop=True)
|
|
317
|
+
|
|
318
|
+
pre_ind = df_product.input_category == 'pre'
|
|
319
|
+
df_product_pre = df_product[pre_ind].reset_index(drop=True)
|
|
320
|
+
|
|
321
|
+
pre_dates_expected = [
|
|
322
|
+
'2024-06-12',
|
|
323
|
+
'2024-05-31',
|
|
324
|
+
'2024-05-19',
|
|
325
|
+
'2024-05-07',
|
|
326
|
+
'2023-06-18',
|
|
327
|
+
'2023-06-06',
|
|
328
|
+
'2023-05-25',
|
|
329
|
+
'2022-06-11',
|
|
330
|
+
'2022-05-30',
|
|
331
|
+
'2022-05-18',
|
|
332
|
+
]
|
|
333
|
+
assert sorted(df_product_pre['acq_date_for_mgrs_pass'].unique().tolist()) == sorted(pre_dates_expected)
|
|
334
|
+
|
|
335
|
+
assert df_product_post['acq_date_for_mgrs_pass'].unique().tolist() == ['2025-06-19']
|
|
336
|
+
|
|
337
|
+
|
|
338
|
+
@pytest.mark.integration
|
|
339
|
+
def test_dist_enum_one_with_multi_window_with_asf_daac_single_polarization() -> None:
|
|
340
|
+
"""
|
|
341
|
+
Test enumeration of 1 product with multi_window strategy with single polarization data in post-image set.
|
|
342
|
+
|
|
343
|
+
The dataframe should be empty!
|
|
344
|
+
|
|
345
|
+
https://search.asf.alaska.edu/#/?maxResults=250&zoom=4.562¢er=144.313,-7.683
|
|
346
|
+
&polygon=POLYGON((-242.1832%205.9478,-231.2276%205.9478,-231.2276%2018.4899,-242.1832%2018.4899,-242.1832%205.9478))
|
|
347
|
+
&dataset=OPERA-S1&productTypes=RTC&start=2024-10-18T08:00:00Z
|
|
348
|
+
&end=2024-10-31T07:59:59Z&resultsLoaded=true
|
|
349
|
+
&granule=OPERA_L2_RTC-S1_T069-146165-IW2_20241029T100013Z_20241029T204425Z_S1A_30_v1.0
|
|
350
|
+
&flightDirs=Ascending
|
|
351
|
+
"""
|
|
352
|
+
with pytest.raises(ValueError, match='No RTC-S1 post-images found for track 69 in MGRS tile 51QUU.'):
|
|
353
|
+
_ = enumerate_one_dist_s1_product(
|
|
354
|
+
'51QUU',
|
|
355
|
+
track_number=69,
|
|
356
|
+
post_date='2024-10-29',
|
|
357
|
+
lookback_strategy='multi_window',
|
|
358
|
+
delta_lookback_days=(730, 365),
|
|
359
|
+
max_pre_imgs_per_burst=(3, 4),
|
|
360
|
+
)
|
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/bump-tag-for-release.yml
RENAMED
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/.github/workflows/changelog-updated.yml
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/notebooks/B__Enumerate_MGRS_tile.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/data/mgrs.parquet
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/mgrs_burst_data.py
RENAMED
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/param_models.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator/tabular_models.py
RENAMED
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/not-zip-safe
RENAMED
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/requires.txt
RENAMED
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/src/dist_s1_enumerator.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/mock_ts_data_for_enum.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dist_s1_enumerator-1.0.2 → dist_s1_enumerator-1.0.4}/tests/data/ts_data_for_workflow_inputs.ipynb
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|