dist-s1-enumerator 1.0.4__tar.gz → 1.0.6__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.
Files changed (54) hide show
  1. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/CHANGELOG.md +14 -1
  2. {dist_s1_enumerator-1.0.4/src/dist_s1_enumerator.egg-info → dist_s1_enumerator-1.0.6}/PKG-INFO +10 -1
  3. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/README.md +9 -0
  4. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/asf.py +23 -0
  5. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/data/jpl_burst_geo.parquet +0 -0
  6. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6/src/dist_s1_enumerator.egg-info}/PKG-INFO +10 -1
  7. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/test_asf.py +22 -1
  8. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/test_mgrs_burst_data.py +18 -1
  9. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/dependabot.yml +0 -0
  10. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/workflows/bump-tag-for-release.yml +0 -0
  11. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/workflows/changelog-updated.yml +0 -0
  12. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/workflows/github-release.yml +0 -0
  13. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/workflows/labeled-pr.yml +0 -0
  14. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/workflows/nb_tests.yml +0 -0
  15. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/workflows/pypi-release.yml +0 -0
  16. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/workflows/static_analysis.yml +0 -0
  17. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.github/workflows/tests.yml +0 -0
  18. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/.gitignore +0 -0
  19. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/LICENSE +0 -0
  20. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/environment.yml +0 -0
  21. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/notebooks/A__Staging_Inputs_for_One_MGRS_Tile.ipynb +0 -0
  22. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/notebooks/B__Enumerate_MGRS_tile.ipynb +0 -0
  23. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/pyproject.toml +0 -0
  24. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/setup.cfg +0 -0
  25. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/__init__.py +0 -0
  26. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/data/mgrs.parquet +0 -0
  27. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/data/mgrs_burst_lookup_table.parquet +0 -0
  28. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/dist_enum.py +0 -0
  29. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/dist_enum_inputs.py +0 -0
  30. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/exceptions.py +0 -0
  31. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/mgrs_burst_data.py +0 -0
  32. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/param_models.py +0 -0
  33. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/py.typed +0 -0
  34. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/rtc_s1_io.py +0 -0
  35. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator/tabular_models.py +0 -0
  36. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator.egg-info/SOURCES.txt +0 -0
  37. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator.egg-info/dependency_links.txt +0 -0
  38. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator.egg-info/not-zip-safe +0 -0
  39. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator.egg-info/requires.txt +0 -0
  40. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/src/dist_s1_enumerator.egg-info/top_level.txt +0 -0
  41. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/conftest.py +0 -0
  42. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/data/mock_ts_data_for_enum.ipynb +0 -0
  43. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/data/rtc_s1_ts_metadata/chile_19HBD.parquet +0 -0
  44. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/data/rtc_s1_ts_metadata/mgrs01UBT.parquet +0 -0
  45. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/data/rtc_s1_ts_metadata/mgrs11SLT_11SLU_11SMT.parquet +0 -0
  46. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/data/rtc_s1_ts_metadata/mgrs15RXN__track63.parquet +0 -0
  47. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/data/rtc_s1_ts_metadata/mgrs22WFD.parquet +0 -0
  48. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/data/ts_data_for_workflow_inputs.ipynb +0 -0
  49. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/test_dist_enum.py +0 -0
  50. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/test_dist_enum_inputs.py +0 -0
  51. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/test_notebooks.py +0 -0
  52. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/test_package.py +0 -0
  53. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/test_param_model.py +0 -0
  54. {dist_s1_enumerator-1.0.4 → dist_s1_enumerator-1.0.6}/tests/test_rtc_s1_io.py +0 -0
@@ -6,6 +6,19 @@ 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.6] - 2025-10-09
10
+
11
+ ### Added
12
+ * Tests for MGRS tiles and burst geometries ensuring they overlap the antimeridian if and only if they are multipolygons.
13
+ * Updated readme with information about these mgrs/burst geometries and the dateline.
14
+ * Updated burst geometries using geopackage geometry from opera_adt/burst_db. See this issue: https://github.com/opera-adt/burst_db/issues/120 - some dateline geometries were cut off. The new geometries are larger and the table went from 24 MB to 37 MB. See this notebook: https://github.com/OPERA-Cal-Val/dist-s1-research/blob/dev/marshak/Zb_update_burst_table/Update%20Burst%20Geometries%20Using%20Geopackage.ipynb
15
+
16
+ ## [1.0.5] - 2025-09-29
17
+
18
+ ### Fixed
19
+ * CMR metadata does not have correctly migrated urls from ASF datapool to ASF cumulus - see https://github.com/opera-adt/dist-s1/issues/158.
20
+
21
+
9
22
  ## [1.0.4] - 2025-09-29
10
23
 
11
24
  ### Added
@@ -14,7 +27,7 @@ and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
14
27
  * We now ensure that single polarization data is excluded from baselines and not used in the creation of the post-image set.
15
28
 
16
29
  ### Fixed
17
- * Bug in enumerating 1 product.
30
+ * Bug in enumerating 1 product - we did not ensure spatial bursts were consistent between pre-/post-image sets.
18
31
 
19
32
 
20
33
  ## [1.0.3] - 2025-09-09
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dist-s1-enumerator
3
- Version: 1.0.4
3
+ Version: 1.0.6
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
@@ -125,6 +125,15 @@ There are two category of tests: unit tests and integration tests. The former ca
125
125
  The integration tests that are the most time consuming are represented by the notebooks and are run only upon a release PR.
126
126
  These notebook tests are tagged with `notebooks` and can be excluded from the other tests with `pytest tests -m 'not notebooks'`.
127
127
 
128
+ # Remarks about the Dateline/Dateline and Geometry
129
+
130
+ The antimeridian (or dateline) is the line at the -180 longitude mark that global CRS tiles are wrapped by standard global reference systems.
131
+ The geometries of the bursts and the MGRS tiles in this package are all in `epsg:4326` (standard lon/lat).
132
+ The geometries are all between -180 and 180 so those geometries that cross the antimeridian/dateline are generally wrapped.
133
+ For MGRS tiles, the statement that a geometry overlaps the antimeridian occurs if and only if the geometry is a Polygon.
134
+ The same is true for burst geometries.
135
+ See `test_antimeridian_crossing` in [`tests/test_mgrs_burst_data.py`](tests/test_mgrs_burst_data.py).
136
+
128
137
  # Contributing
129
138
 
130
139
  We welcome contributions to this open-source package. To do so:
@@ -102,6 +102,15 @@ There are two category of tests: unit tests and integration tests. The former ca
102
102
  The integration tests that are the most time consuming are represented by the notebooks and are run only upon a release PR.
103
103
  These notebook tests are tagged with `notebooks` and can be excluded from the other tests with `pytest tests -m 'not notebooks'`.
104
104
 
105
+ # Remarks about the Dateline/Dateline and Geometry
106
+
107
+ The antimeridian (or dateline) is the line at the -180 longitude mark that global CRS tiles are wrapped by standard global reference systems.
108
+ The geometries of the bursts and the MGRS tiles in this package are all in `epsg:4326` (standard lon/lat).
109
+ The geometries are all between -180 and 180 so those geometries that cross the antimeridian/dateline are generally wrapped.
110
+ For MGRS tiles, the statement that a geometry overlaps the antimeridian occurs if and only if the geometry is a Polygon.
111
+ The same is true for burst geometries.
112
+ See `test_antimeridian_crossing` in [`tests/test_mgrs_burst_data.py`](tests/test_mgrs_burst_data.py).
113
+
105
114
  # Contributing
106
115
 
107
116
  We welcome contributions to this open-source package. To do so:
@@ -12,6 +12,27 @@ from dist_s1_enumerator.mgrs_burst_data import get_burst_ids_in_mgrs_tiles, get_
12
12
  from dist_s1_enumerator.tabular_models import reorder_columns, rtc_s1_resp_schema, rtc_s1_schema
13
13
 
14
14
 
15
+ def convert_asf_url_to_cumulus(url: str) -> str:
16
+ asf_base = 'https://datapool.asf.alaska.edu/RTC/OPERA-S1/'
17
+ cumulus_base = 'https://cumulus.asf.earthdatacloud.nasa.gov/OPERA/OPERA_L2_RTC-S1/'
18
+
19
+ if not (url.startswith(cumulus_base) or url.startswith(asf_base)):
20
+ warn(f'URL {url} is not a valid ASF datapool or cumulus earthdatacloud URL.')
21
+ return url
22
+
23
+ if not url.startswith(asf_base):
24
+ return url
25
+
26
+ filename = url.split('/')[-1]
27
+ granule_pol_parts = filename.rsplit('_', 1)
28
+ if len(granule_pol_parts) != 2:
29
+ raise ValueError(f'Could not extract granule name from filename: {filename}')
30
+
31
+ granule_name = granule_pol_parts[0]
32
+ new_url = f'{cumulus_base}{granule_name}/{filename}'
33
+ return new_url
34
+
35
+
15
36
  def format_polarization(pol: list | str) -> str:
16
37
  if isinstance(pol, list):
17
38
  if ('VV' in pol) and len(pol) == 2:
@@ -167,6 +188,8 @@ def get_rtc_s1_ts_metadata_by_burst_ids(
167
188
 
168
189
  df_rtc['url_copol'] = url_copol
169
190
  df_rtc['url_crosspol'] = url_crosspol
191
+ df_rtc['url_copol'] = df_rtc['url_copol'].map(convert_asf_url_to_cumulus)
192
+ df_rtc['url_crosspol'] = df_rtc['url_crosspol'].map(convert_asf_url_to_cumulus)
170
193
  df_rtc = df_rtc.drop(columns=['all_urls'])
171
194
 
172
195
  # Ensure the data is sorted by jpl_burst_id and acq_dt
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dist-s1-enumerator
3
- Version: 1.0.4
3
+ Version: 1.0.6
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
@@ -125,6 +125,15 @@ There are two category of tests: unit tests and integration tests. The former ca
125
125
  The integration tests that are the most time consuming are represented by the notebooks and are run only upon a release PR.
126
126
  These notebook tests are tagged with `notebooks` and can be excluded from the other tests with `pytest tests -m 'not notebooks'`.
127
127
 
128
+ # Remarks about the Dateline/Dateline and Geometry
129
+
130
+ The antimeridian (or dateline) is the line at the -180 longitude mark that global CRS tiles are wrapped by standard global reference systems.
131
+ The geometries of the bursts and the MGRS tiles in this package are all in `epsg:4326` (standard lon/lat).
132
+ The geometries are all between -180 and 180 so those geometries that cross the antimeridian/dateline are generally wrapped.
133
+ For MGRS tiles, the statement that a geometry overlaps the antimeridian occurs if and only if the geometry is a Polygon.
134
+ The same is true for burst geometries.
135
+ See `test_antimeridian_crossing` in [`tests/test_mgrs_burst_data.py`](tests/test_mgrs_burst_data.py).
136
+
128
137
  # Contributing
129
138
 
130
139
  We welcome contributions to this open-source package. To do so:
@@ -1,6 +1,6 @@
1
1
  import pytest
2
2
 
3
- from dist_s1_enumerator.asf import append_pass_data, get_rtc_s1_ts_metadata_by_burst_ids
3
+ from dist_s1_enumerator.asf import append_pass_data, convert_asf_url_to_cumulus, get_rtc_s1_ts_metadata_by_burst_ids
4
4
 
5
5
 
6
6
  @pytest.mark.integration
@@ -35,3 +35,24 @@ def test_appending_mgrs_tiles() -> None:
35
35
 
36
36
  df_rtc_formatted_no_rows = append_pass_data(df_rtc_resp, ['22NFF'])
37
37
  assert df_rtc_formatted_no_rows.empty
38
+
39
+
40
+ @pytest.mark.parametrize('pol_token', ['VV', 'VH', 'HH', 'HV'])
41
+ def test_convert_asf_url_to_cumulus_from_datapool(pol_token: str) -> None:
42
+ """Test converting ASF datapool URL to cumulus earthdatacloud URL."""
43
+ asf_url = f'https://datapool.asf.alaska.edu/RTC/OPERA-S1/OPERA_L2_RTC-S1_T001-000189-IW2_20211028T180924Z_20250703T015334Z_S1A_30_v1.0_{pol_token}.tif'
44
+ expected_cumulus_url = f'https://cumulus.asf.earthdatacloud.nasa.gov/OPERA/OPERA_L2_RTC-S1/OPERA_L2_RTC-S1_T001-000189-IW2_20211028T180924Z_20250703T015334Z_S1A_30_v1.0/OPERA_L2_RTC-S1_T001-000189-IW2_20211028T180924Z_20250703T015334Z_S1A_30_v1.0_{pol_token}.tif'
45
+
46
+ result = convert_asf_url_to_cumulus(asf_url)
47
+
48
+ assert result == expected_cumulus_url
49
+
50
+
51
+ @pytest.mark.parametrize('pol_token', ['VV', 'VH', 'HH', 'HV'])
52
+ def test_convert_asf_url_to_cumulus_already_cumulus(pol_token: str) -> None:
53
+ """Test that cumulus URLs are returned unchanged."""
54
+ cumulus_url = f'https://cumulus.asf.earthdatacloud.nasa.gov/OPERA/OPERA_L2_RTC-S1/OPERA_L2_RTC-S1_T001-000189-IW2_20211028T180924Z_20250703T015334Z_S1A_30_v1.0/OPERA_L2_RTC-S1_T001-000189-IW2_20211028T180924Z_20250703T015334Z_S1A_30_v1.0_{pol_token}.tif'
55
+
56
+ result = convert_asf_url_to_cumulus(cumulus_url)
57
+
58
+ assert result == cumulus_url
@@ -1,5 +1,5 @@
1
1
  import pytest
2
- from shapely.geometry import Point
2
+ from shapely.geometry import LineString, MultiPolygon, Point, Polygon
3
3
 
4
4
  from dist_s1_enumerator.exceptions import NoMGRSCoverage
5
5
  from dist_s1_enumerator.mgrs_burst_data import (
@@ -161,3 +161,20 @@ def test_all_bursts_in_lut() -> None:
161
161
  df_merged = df_bursts.merge(df_mgrs_lut, on='jpl_burst_id', indicator=True, how='left')
162
162
  burst_ids_not_in_lut = df_merged[df_merged['_merge'] == 'left_only'].jpl_burst_id.unique().tolist()
163
163
  assert len(burst_ids_not_in_lut) == 0
164
+
165
+
166
+ def test_antimeridian_crossing() -> None:
167
+ df_mgrs = get_mgrs_table()
168
+ df_burst = get_burst_table()
169
+ antimeridian_0 = LineString(coordinates=((-180, 90), (-180, -90))).buffer(0.00000001)
170
+ antimeridian_1 = LineString(coordinates=((180, 90), (180, -90))).buffer(0.00000001)
171
+
172
+ for df in [df_mgrs, df_burst]:
173
+ for antimeridian in [antimeridian_0, antimeridian_1]:
174
+ ind_anti = df.geometry.intersects(antimeridian)
175
+ df_antimerid = df[ind_anti].reset_index(drop=True)
176
+ any_polygons = (df_antimerid.geometry.map(lambda geo: isinstance(geo, Polygon))).sum()
177
+ assert any_polygons == 0
178
+ df_antimerid_not = df[~ind_anti].reset_index(drop=True)
179
+ any_multis = (df_antimerid_not.geometry.map(lambda geo: isinstance(geo, MultiPolygon))).sum()
180
+ assert any_multis == 0