diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml index 43377053..8702d25b 100644 --- a/.github/workflows/changelog.yml +++ b/.github/workflows/changelog.yml @@ -14,6 +14,6 @@ on: jobs: call-changelog-check-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-changelog-check.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-changelog-check.yml@v0.8.1 secrets: USER_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/create-jira-issue.yml b/.github/workflows/create-jira-issue.yml index 8ce649de..a2ee3bb1 100644 --- a/.github/workflows/create-jira-issue.yml +++ b/.github/workflows/create-jira-issue.yml @@ -6,7 +6,7 @@ on: jobs: call-create-jira-issue-workflow: - uses: ASFHyP3/actions/.github/workflows/reusable-create-jira-issue.yml@v0.8.0 + uses: ASFHyP3/actions/.github/workflows/reusable-create-jira-issue.yml@v0.8.1 secrets: JIRA_BASE_URL: ${{ secrets.JIRA_BASE_URL }} JIRA_USER_EMAIL: ${{ secrets.JIRA_USER_EMAIL }} diff --git a/.github/workflows/labeled-pr.yml b/.github/workflows/labeled-pr.yml index fffab935..205ed76c 100644 --- a/.github/workflows/labeled-pr.yml +++ b/.github/workflows/labeled-pr.yml @@ -13,4 +13,4 @@ on: jobs: call-labeled-pr-check-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-labeled-pr-check.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-labeled-pr-check.yml@v0.8.1 diff --git a/.github/workflows/release-checklist-comment.yml b/.github/workflows/release-checklist-comment.yml index 169dd376..c5ff9af3 100644 --- a/.github/workflows/release-checklist-comment.yml +++ b/.github/workflows/release-checklist-comment.yml @@ -10,7 +10,7 @@ on: jobs: call-release-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-release-checklist-comment.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-release-checklist-comment.yml@v0.8.1 permissions: pull-requests: write secrets: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b01863d..ee8e0814 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -8,7 +8,7 @@ on: jobs: call-release-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-release.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-release.yml@v0.8.1 with: release_prefix: HyP3 ISCE2 secrets: diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 1624cd3a..f6197023 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -5,10 +5,10 @@ on: push jobs: call-secrets-analysis-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-secrets-analysis.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-secrets-analysis.yml@v0.8.1 call-flake8-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-flake8.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-flake8.yml@v0.8.1 with: local_package_names: hyp3_isce2 diff --git a/.github/workflows/tag-version.yml b/.github/workflows/tag-version.yml index e85846c9..c8f3d71a 100644 --- a/.github/workflows/tag-version.yml +++ b/.github/workflows/tag-version.yml @@ -8,7 +8,7 @@ on: jobs: call-bump-version-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-bump-version.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-bump-version.yml@v0.8.1 with: user: tools-bot email: UAF-asf-apd@alaska.edu diff --git a/.github/workflows/test-and-build.yml b/.github/workflows/test-and-build.yml index c7a82e12..7d05cba4 100644 --- a/.github/workflows/test-and-build.yml +++ b/.github/workflows/test-and-build.yml @@ -13,7 +13,7 @@ on: jobs: call-pytest-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-pytest.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-pytest.yml@v0.8.1 with: local_package_name: hyp3_isce2 python_versions: >- @@ -21,12 +21,12 @@ jobs: call-version-info-workflow: # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-version-info.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-version-info.yml@v0.8.1 call-docker-ghcr-workflow: needs: call-version-info-workflow # Docs: https://github.com/ASFHyP3/actions - uses: ASFHyP3/actions/.github/workflows/reusable-docker-ghcr.yml@v0.7.1 + uses: ASFHyP3/actions/.github/workflows/reusable-docker-ghcr.yml@v0.8.1 with: version_tag: ${{ needs.call-version-info-workflow.outputs.version_tag }} release_branch: main diff --git a/CHANGELOG.md b/CHANGELOG.md index ab6ce40a..a674f579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [PEP 440](https://www.python.org/dev/peps/pep-0440/) and uses [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.6.2] +### Changed +* The geocoding DEM is now resampled to ~20m in the case of 5x1 looks. + ## [0.6.1] ### Added * `apply_water_mask` optional argument to apply water mask in the wrapped and unwrapped phase geotiff files diff --git a/src/hyp3_isce2/dem.py b/src/hyp3_isce2/dem.py index f7fc56b7..fa3f9116 100644 --- a/src/hyp3_isce2/dem.py +++ b/src/hyp3_isce2/dem.py @@ -56,11 +56,32 @@ def buffer_extent(extent: list, buffer: float) -> list: ] +def distance_meters_to_degrees(distance_meters, latitude): + """Convert a distance from meters to degrees in longitude and latitude + + Args: + distance_meters: Arc length in meters. + latitude: The line of latitude at which the calculation takes place. + Returns: + The length in degrees for longitude and latitude, respectively. + """ + if np.abs(latitude) == 90: + # np.cos won't return exactly 0, so we must manually raise this exception. + raise ZeroDivisionError('A Latitude of 90 degrees results in dividing by zero.') + EARTHS_CIRCUMFERENCE = 40_030_173.59204114 # 2 * pi * 6,371,000.0 (Earth's average radius in meters) + latitude_circumference = EARTHS_CIRCUMFERENCE * np.cos(np.radians(latitude)) + distance_degrees_lon = distance_meters / latitude_circumference * 360 + distance_degrees_lat = distance_meters / EARTHS_CIRCUMFERENCE * 360 + return (np.round(distance_degrees_lon, 15), np.round(distance_degrees_lat, 15)) + + def download_dem_for_isce2( extent: list, dem_name: str = 'glo_30', dem_dir: Path = None, - buffer: float = .4) -> Path: + buffer: float = .4, + resample_20m: bool = False +) -> Path: """Download the given DEM for the given extent. Args: @@ -69,6 +90,7 @@ def download_dem_for_isce2( dem_dir: The output directory. buffer: The extent buffer in degrees, by default .4, which is about 44 km at the equator (or about 2.5 bursts at the equator). + resample_20m: Whether or not the DEM should be resampled to 20 meters. Returns: The path to the downloaded DEM. """ @@ -77,13 +99,27 @@ def download_dem_for_isce2( extent_buffered = buffer_extent(extent, buffer) - dem_array, dem_profile = dem_stitcher.stitch_dem( - extent_buffered, - dem_name, - dst_ellipsoidal_height=True, - dst_area_or_point='Point', - n_threads_downloading=5, - ) + if resample_20m: + res_degrees = distance_meters_to_degrees(20.0, extent_buffered[1]) + dem_array, dem_profile = dem_stitcher.stitch_dem( + extent_buffered, + dem_name, + dst_ellipsoidal_height=True, + dst_area_or_point='Point', + n_threads_downloading=5, + dst_resolution=res_degrees + ) + dem_path = dem_dir / 'full_res_geocode.dem.wgs84' + else: + dem_array, dem_profile = dem_stitcher.stitch_dem( + extent_buffered, + dem_name, + dst_ellipsoidal_height=True, + dst_area_or_point='Point', + n_threads_downloading=5, + ) + dem_path = dem_dir / 'full_res.dem.wgs84' + dem_array[np.isnan(dem_array)] = 0. dem_profile['nodata'] = None @@ -93,7 +129,6 @@ def download_dem_for_isce2( for key in ['blockxsize', 'blockysize', 'compress', 'interleave', 'tiled']: del dem_profile[key] - dem_path = dem_dir / 'full_res.dem.wgs84' with rasterio.open(dem_path, 'w', **dem_profile) as ds: ds.write(dem_array, 1) diff --git a/src/hyp3_isce2/insar_tops_burst.py b/src/hyp3_isce2/insar_tops_burst.py index f0807b90..5fe392ee 100644 --- a/src/hyp3_isce2/insar_tops_burst.py +++ b/src/hyp3_isce2/insar_tops_burst.py @@ -84,9 +84,26 @@ def insar_tops_burst( log.info(f'InSAR ROI: {insar_roi}') log.info(f'DEM ROI: {dem_roi}') - dem_path = download_dem_for_isce2(dem_roi, dem_name='glo_30', dem_dir=dem_dir, buffer=0) + dem_path = download_dem_for_isce2( + dem_roi, + dem_name='glo_30', + dem_dir=dem_dir, + buffer=0, + resample_20m=False + ) download_aux_cal(aux_cal_dir) + if range_looks == 5: + geocode_dem_path = download_dem_for_isce2( + dem_roi, + dem_name='glo_30', + dem_dir=dem_dir, + buffer=0, + resample_20m=True + ) + else: + geocode_dem_path = dem_path + orbit_dir.mkdir(exist_ok=True, parents=True) for granule in (ref_params.granule, sec_params.granule): downloadSentinelOrbitFile(granule, str(orbit_dir)) @@ -98,6 +115,7 @@ def insar_tops_burst( aux_cal_directory=str(aux_cal_dir), roi=insar_roi, dem_filename=str(dem_path), + geocode_dem_filename=str(geocode_dem_path), swaths=swath_number, azimuth_looks=azimuth_looks, range_looks=range_looks, diff --git a/src/hyp3_isce2/topsapp.py b/src/hyp3_isce2/topsapp.py index baee5338..3906080a 100644 --- a/src/hyp3_isce2/topsapp.py +++ b/src/hyp3_isce2/topsapp.py @@ -51,6 +51,7 @@ def __init__( orbit_directory: str, aux_cal_directory: str, dem_filename: str, + geocode_dem_filename: str, roi: Iterable[float], swaths: int or Iterable[int] = [1, 2, 3], azimuth_looks: int = 4, @@ -63,7 +64,7 @@ def __init__( self.aux_cal_directory = aux_cal_directory self.roi = [roi[1], roi[3], roi[0], roi[2]] self.dem_filename = dem_filename - self.geocode_dem_filename = dem_filename + self.geocode_dem_filename = geocode_dem_filename self.azimuth_looks = azimuth_looks self.range_looks = range_looks self.do_unwrap = do_unwrap diff --git a/tests/test_dem.py b/tests/test_dem.py index e8efb2b2..319b9950 100644 --- a/tests/test_dem.py +++ b/tests/test_dem.py @@ -4,6 +4,7 @@ import rasterio from affine import Affine from lxml import etree +from pytest import raises from rasterio import CRS from hyp3_isce2 import dem @@ -78,3 +79,16 @@ def test_buffer_extent(): assert dem.buffer_extent(extent2, 0.1) == [-170, 53, -167, 55] assert dem.buffer_extent(extent2, 0.3) == [-170, 53, -167, 55] assert dem.buffer_extent(extent2, 0.4) == [-171, 52, -166, 56] + + +def test_distance_meters_to_degrees(): + assert dem.distance_meters_to_degrees(distance_meters=20, latitude=0) == (0.000179864321184, 0.000179864321184) + assert dem.distance_meters_to_degrees(distance_meters=20, latitude=45) == (0.000254366562405, 0.000179864321184) + assert dem.distance_meters_to_degrees(distance_meters=20, latitude=89.9) == (0.103054717208573, 0.000179864321184) + assert dem.distance_meters_to_degrees(distance_meters=20, latitude=-45) == (0.000254366562405, 0.000179864321184) + assert dem.distance_meters_to_degrees(distance_meters=20, latitude=-89.9) == (0.103054717208573, 0.000179864321184) + # This is since cos(90) = 0, leading to a divide by zero issue. + with raises(ZeroDivisionError): + dem.distance_meters_to_degrees(20, 90) + with raises(ZeroDivisionError): + dem.distance_meters_to_degrees(20, -90) diff --git a/tests/test_topsapp.py b/tests/test_topsapp.py index eb5338bd..c069c07f 100644 --- a/tests/test_topsapp.py +++ b/tests/test_topsapp.py @@ -11,6 +11,7 @@ def test_topsapp_burst_config(tmp_path): aux_cal_directory='aux_cal', roi=[-118.0, 37.0, -117.0, 38.0], dem_filename='dem.tif', + geocode_dem_filename='dem_geocode.tif', swaths=1, ) @@ -25,6 +26,7 @@ def test_topsapp_burst_config(tmp_path): assert 'orbits' in template assert 'aux_cal' in template assert 'dem.tif' in template + assert 'dem_geocode.tif' in template assert '[37.0, 38.0, -118.0, -117.0]' in template assert '[1]' in template @@ -55,6 +57,7 @@ def test_run_topsapp_burst(tmp_path, monkeypatch): aux_cal_directory='', roi=[0, 1, 2, 3], dem_filename='', + geocode_dem_filename='', swaths=1, azimuth_looks=1, range_looks=1,