Skip to main content
  • Home
  • Development
  • Documentation
  • Donate
  • Operational login
  • Browse the archive

swh logo
SoftwareHeritage
Software
Heritage
Archive
Features
  • Search

  • Downloads

  • Save code now

  • Add forge now

  • Help

https://github.com/GeoscienceAustralia/PyRate
09 August 2023, 08:52:18 UTC
  • Code
  • Branches (23)
  • Releases (1)
  • Visits
    • Branches
    • Releases
    • HEAD
    • refs/heads/CI-patch
    • refs/heads/data
    • refs/heads/dependabot/pip/joblib-1.2.0
    • refs/heads/dependabot/pip/numpy-1.22.0
    • refs/heads/dependabot/pip/scipy-1.10.0
    • refs/heads/develop
    • refs/heads/gh-pages
    • refs/heads/master
    • refs/heads/mg/actions
    • refs/heads/sb/largetifs-enhancements
    • refs/heads/sb/orbfit-independent-method
    • refs/heads/sb/orbital-correction-experiements
    • refs/heads/sb/phase-closure-correction
    • refs/heads/sb/upgrade-ci-ubuntu
    • refs/heads/sb/use-mpi-shared
    • refs/tags/0.3.0
    • refs/tags/0.4.0
    • refs/tags/0.4.1
    • refs/tags/0.4.2
    • refs/tags/0.4.3
    • refs/tags/0.5.0
    • refs/tags/0.6.0
    • refs/tags/0.6.1
    • 0.2.0
  • 198e66c
  • /
  • tests
  • /
  • test_geometry.py
Raw File Download
Take a new snapshot of a software origin

If the archived software origin currently browsed is not synchronized with its upstream version (for instance when new commits have been issued), you can explicitly request Software Heritage to take a new snapshot of it.

Use the form below to proceed. Once a request has been submitted and accepted, it will be processed as soon as possible. You can then check its processing state by visiting this dedicated page.
swh spinner

Processing "take a new snapshot" request ...

Permalinks

To reference or cite the objects present in the Software Heritage archive, permalinks based on SoftWare Hash IDentifiers (SWHIDs) must be used.
Select below a type of object currently browsed in order to display its associated SWHID and permalink.

  • content
  • directory
  • revision
  • snapshot
origin badgecontent badge Iframe embedding
swh:1:cnt:fa5f5b35fa02c4438092e4b2fe15ffdd97bc3eae
origin badgedirectory badge Iframe embedding
swh:1:dir:55270c2ea96cf3a368f0bbf0e5a8de1551b6d891
origin badgerevision badge
swh:1:rev:3579612cbfec20e43cb2c7b8311c50851ae4fc4a
origin badgesnapshot badge
swh:1:snp:e85aafb5fc900c1df2eebb773a8b8e11798084c1
Citations

This interface enables to generate software citations, provided that the root directory of browsed objects contains a citation.cff or codemeta.json file.
Select below a type of object currently browsed in order to generate citations for them.

  • content
  • directory
  • revision
  • snapshot
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Generate software citation in BibTex format (requires biblatex-software package)
Generating citation ...
Tip revision: 3579612cbfec20e43cb2c7b8311c50851ae4fc4a authored by S M T Chua on 25 February 2022, 04:08:37 UTC
Merge pull request #380 from GeoscienceAustralia/develop
Tip revision: 3579612
test_geometry.py
import shutil
from typing import Tuple
import numpy as np
from os.path import join
import pytest

import pyrate.constants as C
from pyrate.core import ifgconstants as ifc
from pyrate.core.geometry import get_lonlat_coords, get_sat_positions, vincinv
from pyrate.core.refpixel import convert_pixel_value_to_geographic_coordinate
from tests import common
from pyrate.configuration import Configuration
from subprocess import run, PIPE
from pyrate import prepifg, correct
from pyrate.core.shared import Ifg, Geometry


def get_lonlat_coords_slow(ifg: Ifg) -> Tuple[np.ndarray, np.ndarray]:
    """
    Function to get longitude and latitude coordinates for each pixel in the multi-looked.
    interferogram dataset. Coordinates are identical for each interferogram in the stack.
    :param ifg: pyrate.core.shared.Ifg Class object.
    :return: lon: Longitude for each pixel (decimal degrees)
    :return: lat: Latitude for each pixel (decimal degrees)
    """
    # assume all interferograms have same projection and will share the same transform
    transform = ifg.dataset.GetGeoTransform()
    # number of rows and columns in dataset
    nrows, ncols = ifg.shape
    lon = np.zeros((nrows, ncols))  # pre-allocate 2D numpy array
    lat = np.zeros((nrows, ncols))  # pre-allocate 2D numpy array
    for i in range(0, nrows):  # rows are y-direction
        for j in range(0, ncols):  # cols are x-direction
            lon[i, j], lat[i, j] = convert_pixel_value_to_geographic_coordinate(j, i, transform)

    return lon, lat


def test_get_lonlat_coords_vectorised(dem):
    lon, lat = get_lonlat_coords_slow(dem)
    lon_v, lat_v = get_lonlat_coords(dem)
    np.testing.assert_array_almost_equal(lon, lon_v.data)
    np.testing.assert_array_almost_equal(lat, lat_v.data)


@pytest.fixture(params=[(29, 50, -2.94634866714478), (94, 58, -2.94684600830078)])
def point_azimuth(request):
    return request.param


@pytest.fixture(params=[(29, 50, 1.02217936515808), (94, 58, 1.0111095905304)])
def point_incidence(request):
    return request.param


class TestPyRateAngleFiles:

    @classmethod
    def setup_class(cls):
        cls.params = Configuration(common.MEXICO_CROPA_CONF).__dict__
        # run prepifg
        prepifg.main(cls.params)
        # copy IFGs to temp folder
        correct._copy_mlooked(cls.params)

    @classmethod
    def teardown_class(cls):
        shutil.rmtree(cls.params[C.OUT_DIR], ignore_errors=True)

    # the xy position in the original GAMMA geometry (before cropping and further multi-looking is required for
    # comparison of PyRate with GAMMA angle values. To get this position for a particular coordinate, the following
    # commands can be applied on Gadi:
    # cd /g/data/dg9/INSAR_ANALYSIS/MEXICO/S1/UNIT-TEST-DATA/CROP-A/GEOTIFF
    # gdallocationinfo -wgs84 cropA_20180106-20180130_VV_8rlks_eqa_unw.tif -99.06 19.37
    # -> outputs the cropped pixel coordinates: (94P,58L) -> x0 = 94, y0 = 58
    # cd /g/data/dg9/INSAR_ANALYSIS/MEXICO/S1/GAMMA/T005A/geotiff_files/unw_ifg_geotiffs/
    # gdallocationinfo -wgs84 20180106-20180130_VV_8rlks_eqa_unw.tif -99.06 19.37
    # -> outputs the original pixel coordinates: (940P,3271L)
    # cd /g/data/dg9/INSAR_ANALYSIS/MEXICO/S1/GAMMA/T005A/DEM/
    # gdallocationinfo 20180106_VV_8rlks_eqa_lv_phi.tif 940 3271
    # -> outputs the GAMMA azimuth angle: -2.94684600830078
    # gdallocationinfo 20180106_VV_8rlks_eqa_lv_theta.tif 940 3271
    # > outputs the GAMMA incidence angle: 1.0111095905304

    def get_pyrate_angle(self, x0, y0, tif_file):
        """
        Get angle at particular pixel in the azimuth/incidence tif file
        """
        # get azimuth angle value of PyRate file azimuth_angle.tif
        temp = run(['gdallocationinfo', tif_file, str(x0), str(y0)], universal_newlines=True, stdout=PIPE)
        out = temp.stdout
        angle_value = float(out.split('Value:')[1].strip('\n'))

        return angle_value

    def test_pyrate_azimuth_matches_gamma_azimuth(self, point_azimuth):
        x0, y0, exp = point_azimuth
        # azimuth angle validation value extracted from GAMMA azimuth file for pixel location corresponding
        # to (29,50) using gdallocationinfo 20180106_VV_8rlks_eqa_lv_phi.tif 613 3228
        # exp = -2.94634866714478  # GAMMMA azimuth angle at pixel location
        # azimuth angle validation value extracted from GAMMA azimuth file for pixel location corresponding
        # to (94,58) using gdallocationinfo 20180106_VV_8rlks_eqa_lv_phi.tif 940 3271
        # exp = -2.94684600830078
        # GAMMA azimuth is defined towards satellite in an anti-clockwise angle system, with East being zero
        # PyRate azimuth angle is defined towards the satellite in a clockwise angle system with North being zero
        tif_file = join(self.params[C.GEOMETRY_DIR], 'azimuth_angle.tif')
        azimuth_angle_pyrate = self.get_pyrate_angle(x0, y0, tif_file)
        # convert PyRate azimuth into GAMMA azimuth
        res = -(azimuth_angle_pyrate - np.pi / 2)
        np.testing.assert_array_almost_equal(exp, res, decimal=2)  # max difference < 0.01 rad
        # screen output of difference if need be:
        # print(exp - res)

    def test_pyrate_incidence_matches_gamma_incidence(self, point_incidence):
        x0, y0, exp = point_incidence
        # incidence angle validation value extracted from GAMMA incidence file for pixel location corresponding
        # to (29,50) using gdallocationinfo 20180106_VV_8rlks_eqa_lv_theta.tif 613 3228
        # exp = 1.02217936515808
        # incidence angle validation value extracted from GAMMA incidence file for pixel location corresponding
        # to (94,58) using gdallocationinfo 20180106_VV_8rlks_eqa_lv_theta.tif 940 3271
        # exp = 1.0111095905304
        # GAMMA angle is defined from the horizontal plane with the zenith direction being pi / 2 radians (i.e.90 deg)
        # PyRate angle is defined from the vertical axis with the zenith direction being zero
        tif_file = join(self.params[C.GEOMETRY_DIR], 'incidence_angle.tif')
        incidence_angle_pyrate = self.get_pyrate_angle(x0, y0, tif_file)
        # convert PyRate incidence into GAMMA incidence
        res = np.pi / 2 - incidence_angle_pyrate
        # convert PyRate incidence into GAMMA incidence
        np.testing.assert_array_almost_equal(exp, res, decimal=3)  # max difference < 0.001 rad
        # screen output of difference if need be:
        # print(exp - res)

    def test_azimuth_angle_calculation(self):
        """
         Calculate local azimuth angle using a spherical model and compare to result using Vincenty's equations
        """
        # get first IFG in stack to calculate lon/lat values
        multi_paths = self.params[C.INTERFEROGRAM_FILES]
        tmp_paths = [ifg_path.tmp_sampled_path for ifg_path in multi_paths]
        # keep only ifg files in path list (i.e. remove coherence and dem files)
        ifg_paths = [item for item in tmp_paths if 'ifg.tif' in item]
        # read and open the first IFG in list
        ifg0_path = ifg_paths[0]
        ifg0 = Ifg(ifg0_path)
        ifg0.open(readonly=True)
        lon, lat = get_lonlat_coords(ifg0)

        # read incidence and look angle files
        tif_file = join(self.params[C.GEOMETRY_DIR], 'incidence_angle.tif')
        geom = Geometry(tif_file)
        incidence_angle = geom.data
        tif_file = join(self.params[C.GEOMETRY_DIR], 'look_angle.tif')
        geom = Geometry(tif_file)
        look_angle = geom.data
        # get metadata
        a = float(ifg0.meta_data[ifc.PYRATE_SEMI_MAJOR_AXIS_METRES])
        b = float(ifg0.meta_data[ifc.PYRATE_SEMI_MINOR_AXIS_METRES])
        heading = float(ifg0.meta_data[ifc.PYRATE_HEADING_DEGREES])
        azimuth = float(ifg0.meta_data[ifc.PYRATE_AZIMUTH_DEGREES])

        # convert all angles from deg to radians
        lon = np.radians(lon.data)
        lat = np.radians(lat.data)
        heading = np.radians(heading)
        azimuth = np.radians(azimuth)

        # calculate satellite positions
        sat_lat, sat_lon = get_sat_positions(lat, lon, look_angle, incidence_angle, heading, azimuth)

        # calculate azimuth angle from pixel to satellite using spherical trigonometry
        # see Eq. 86 on page 4-11 in EARTH-REFERENCED AIRCRAFT NAVIGATION AND SURVEILLANCE ANALYSIS
        # (https://ntlrepository.blob.core.windows.net/lib/59000/59300/59358/DOT-VNTSC-FAA-16-12.pdf)
        azimuth_angle_spherical = np.arctan2(np.cos(sat_lat) * np.sin(sat_lon - lon), \
                                             np.sin(sat_lat) * np.cos(lat) - np.cos(sat_lat) * np.sin(lat) * \
                                             np.cos(sat_lon - lon))
        # add 2 pi in case an angle is below zero
        for azi in np.nditer(azimuth_angle_spherical, op_flags=['readwrite']):
            if azi < 0:
                azi[...] = azi + 2 * np.pi

        azimuth_angle_elliposidal = vincinv(lat, lon, sat_lat, sat_lon, a, b)

        # the difference between Vincenty's azimuth calculation and the spherical approximation is ~0.001 radians
        azimuth_angle_diff = azimuth_angle_spherical - azimuth_angle_elliposidal
        # max difference < 0.01 rad
        np.testing.assert_array_almost_equal(azimuth_angle_spherical, azimuth_angle_elliposidal, decimal=2)

Software Heritage — Copyright (C) 2015–2025, The Software Heritage developers. License: GNU AGPLv3+.
The source code of Software Heritage itself is available on our development forge.
The source code files archived by Software Heritage are available under their own copyright and licenses.
Terms of use: Archive access, API— Contact— JavaScript license information— Web API

back to top