Source code for satpy.tests.reader_tests.test_sar_c_safe

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Copyright (c) 2019 Satpy developers
#
# This file is part of satpy.
#
# satpy is free software: you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation, either version 3 of the License, or (at your option) any later
# version.
#
# satpy is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
# A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along with
# satpy.  If not, see <http://www.gnu.org/licenses/>.
"""Module for testing the satpy.readers.sar-c_safe module."""

import os
from datetime import datetime
from enum import Enum
from pathlib import Path

import numpy as np
import pytest
import yaml

from satpy._config import PACKAGE_CONFIG_PATH
from satpy.dataset import DataQuery
from satpy.dataset.dataid import DataID
from satpy.readers.sar_c_safe import Calibrator, Denoiser, SAFEXMLAnnotation

rasterio = pytest.importorskip("rasterio")


dirname_suffix = "20190201T024655_20190201T024720_025730_02DC2A_AE07"
filename_suffix = "20190201t024655-20190201t024720-025730-02dc2a"

START_TIME = datetime(2019, 2, 1, 2, 46, 55)
END_TIME = datetime(2019, 2, 1, 2, 47, 20)

[docs] @pytest.fixture(scope="module") def granule_directory(tmp_path_factory): """Create a granule directory.""" data_dir = tmp_path_factory.mktemp("data") gdir = data_dir / f"S1A_IW_GRDH_1SDV_{dirname_suffix}.SAFE" os.mkdir(gdir) return gdir
[docs] @pytest.fixture(scope="module") def annotation_file(granule_directory): """Create an annotation file.""" ann_dir = granule_directory / "annotation" os.makedirs(ann_dir, exist_ok=True) annotation_file = ann_dir / f"s1a-iw-grd-vv-{filename_suffix}-001.xml" with open(annotation_file, "wb") as fd: fd.write(annotation_xml) return annotation_file
[docs] @pytest.fixture(scope="module") def annotation_filehandler(annotation_file): """Create an annotation filehandler.""" filename_info = dict(start_time=START_TIME, end_time=END_TIME, polarization="vv") return SAFEXMLAnnotation(annotation_file, filename_info, None)
[docs] @pytest.fixture(scope="module") def calibration_file(granule_directory): """Create a calibration file.""" cal_dir = granule_directory / "annotation" / "calibration" os.makedirs(cal_dir, exist_ok=True) calibration_file = cal_dir / f"calibration-s1a-iw-grd-vv-{filename_suffix}-001.xml" with open(calibration_file, "wb") as fd: fd.write(calibration_xml) return Path(calibration_file)
[docs] @pytest.fixture(scope="module") def calibration_filehandler(calibration_file, annotation_filehandler): """Create a calibration filehandler.""" filename_info = dict(start_time=START_TIME, end_time=END_TIME, polarization="vv") return Calibrator(calibration_file, filename_info, None, image_shape=annotation_filehandler.image_shape)
[docs] @pytest.fixture(scope="module") def noise_file(granule_directory): """Create a noise file.""" noise_dir = granule_directory / "annotation" / "calibration" os.makedirs(noise_dir, exist_ok=True) noise_file = noise_dir / f"noise-s1a-iw-grd-vv-{filename_suffix}-001.xml" with open(noise_file, "wb") as fd: fd.write(noise_xml) return noise_file
[docs] @pytest.fixture(scope="module") def noise_filehandler(noise_file, annotation_filehandler): """Create a noise filehandler.""" filename_info = dict(start_time=START_TIME, end_time=END_TIME, polarization="vv") return Denoiser(noise_file, filename_info, None, image_shape=annotation_filehandler.image_shape)
[docs] @pytest.fixture(scope="module") def noise_with_holes_filehandler(annotation_filehandler, tmpdir_factory): """Create a noise filehandler from data with holes.""" filename_info = dict(start_time=START_TIME, end_time=END_TIME, polarization="vv") noise_xml_file = tmpdir_factory.mktemp("data").join("noise_with_holes.xml") with open(noise_xml_file, "wb") as fd: fd.write(noise_xml_with_holes) noise_filehandler = Denoiser(noise_xml_file, filename_info, None, image_shape=annotation_filehandler.image_shape) return noise_filehandler
[docs] @pytest.fixture(scope="module") def measurement_file(granule_directory): """Create a tiff measurement file.""" GCP = rasterio.control.GroundControlPoint gcps = [GCP(0, 0, 0, 0, 0), GCP(0, 3, 1, 0, 0), GCP(3, 0, 0, 1, 0), GCP(3, 3, 1, 1, 0), GCP(0, 7, 2, 0, 0), GCP(3, 7, 2, 1, 0), GCP(7, 7, 2, 2, 0), GCP(7, 3, 1, 2, 0), GCP(7, 0, 0, 2, 0), GCP(0, 15, 3, 0, 0), GCP(3, 15, 3, 1, 0), GCP(7, 15, 3, 2, 0), GCP(15, 15, 3, 3, 0), GCP(15, 7, 2, 3, 0), GCP(15, 3, 1, 3, 0), GCP(15, 0, 0, 3, 0), ] Z = np.linspace(0, 30000, 100, dtype=np.uint16).reshape((10, 10)) m_dir = granule_directory / "measurement" os.makedirs(m_dir, exist_ok=True) filename = m_dir / f"s1a-iw-grd-vv-{filename_suffix}-001.tiff" with rasterio.open( filename, "w", driver="GTiff", height=Z.shape[0], width=Z.shape[1], count=1, dtype=Z.dtype, crs="+proj=latlong", gcps=gcps) as dst: dst.write(Z, 1) return Path(filename)
[docs] @pytest.fixture(scope="module") def measurement_filehandler(measurement_file, noise_filehandler, calibration_filehandler): """Create a measurement filehandler.""" filename_info = {"mission_id": "S1A", "dataset_name": "foo", "start_time": START_TIME, "end_time": END_TIME, "polarization": "vv"} filetype_info = None from satpy.readers.sar_c_safe import SAFEGRD filehandler = SAFEGRD(measurement_file, filename_info, filetype_info, calibration_filehandler, noise_filehandler) return filehandler
expected_longitudes = np.array([[3.79492915e-16, 5.91666667e-01, 9.09722222e-01, 1.00000000e+00, 9.08333333e-01, 6.80555556e-01, 3.62500000e-01, 8.32667268e-17, -3.61111111e-01, -6.75000000e-01, -8.95833333e-01, -9.77777778e-01, -8.75000000e-01, -5.41666667e-01, 6.80555556e-02, 1.00000000e+00], [1.19166667e+00, 1.32437500e+00, 1.36941964e+00, 1.34166667e+00, 1.25598214e+00, 1.12723214e+00, 9.70282738e-01, 8.00000000e-01, 6.31250000e-01, 4.78898810e-01, 3.57812500e-01, 2.82857143e-01, 2.68898810e-01, 3.30803571e-01, 4.83437500e-01, 7.41666667e-01], [1.82638889e+00, 1.77596726e+00, 1.72667765e+00, 1.67757937e+00, 1.62773172e+00, 1.57619402e+00, 1.52202558e+00, 1.46428571e+00, 1.40203373e+00, 1.33432894e+00, 1.26023065e+00, 1.17879819e+00, 1.08909084e+00, 9.90167942e-01, 8.81088790e-01, 7.60912698e-01], [2.00000000e+00, 1.99166667e+00, 1.99305556e+00, 2.00000000e+00, 2.00833333e+00, 2.01388889e+00, 2.01250000e+00, 2.00000000e+00, 1.97222222e+00, 1.92500000e+00, 1.85416667e+00, 1.75555556e+00, 1.62500000e+00, 1.45833333e+00, 1.25138889e+00, 1.00000000e+00], [1.80833333e+00, 2.01669643e+00, 2.18011267e+00, 2.30119048e+00, 2.38253827e+00, 2.42676446e+00, 2.43647747e+00, 2.41428571e+00, 2.36279762e+00, 2.28462160e+00, 2.18236607e+00, 2.05863946e+00, 1.91605017e+00, 1.75720663e+00, 1.58471726e+00, 1.40119048e+00], [1.34722222e+00, 1.89627976e+00, 2.29940830e+00, 2.57341270e+00, 2.73509779e+00, 2.80126842e+00, 2.78872945e+00, 2.71428571e+00, 2.59474206e+00, 2.44690334e+00, 2.28757440e+00, 2.13356009e+00, 2.00166525e+00, 1.90869473e+00, 1.87145337e+00, 1.90674603e+00], [7.12500000e-01, 1.67563988e+00, 2.36250177e+00, 2.80892857e+00, 3.05076318e+00, 3.12384850e+00, 3.06402742e+00, 2.90714286e+00, 2.68903770e+00, 2.44555485e+00, 2.21253720e+00, 2.02582766e+00, 1.92126913e+00, 1.93470451e+00, 2.10197669e+00, 2.45892857e+00], [5.55111512e-16, 1.40000000e+00, 2.38095238e+00, 3.00000000e+00, 3.31428571e+00, 3.38095238e+00, 3.25714286e+00, 3.00000000e+00, 2.66666667e+00, 2.31428571e+00, 2.00000000e+00, 1.78095238e+00, 1.71428571e+00, 1.85714286e+00, 2.26666667e+00, 3.00000000e+00], [-6.94444444e-01, 1.11458333e+00, 2.36631944e+00, 3.13888889e+00, 3.51041667e+00, 3.55902778e+00, 3.36284722e+00, 3.00000000e+00, 2.54861111e+00, 2.08680556e+00, 1.69270833e+00, 1.44444444e+00, 1.42013889e+00, 1.69791667e+00, 2.35590278e+00, 3.47222222e+00], [-1.27500000e+00, 8.64613095e-01, 2.33016227e+00, 3.21785714e+00, 3.62390731e+00, 3.64452239e+00, 3.37591199e+00, 2.91428571e+00, 2.35585317e+00, 1.79682398e+00, 1.33340774e+00, 1.06181406e+00, 1.07825255e+00, 1.47893282e+00, 2.36006448e+00, 3.81785714e+00], [-1.64583333e+00, 6.95312500e-01, 2.28404018e+00, 3.22916667e+00, 3.63950893e+00, 3.62388393e+00, 3.29110863e+00, 2.75000000e+00, 2.10937500e+00, 1.47805060e+00, 9.64843750e-01, 6.78571429e-01, 7.28050595e-01, 1.22209821e+00, 2.26953125e+00, 3.97916667e+00], [-1.71111111e+00, 6.51904762e-01, 2.23951247e+00, 3.16507937e+00, 3.54197279e+00, 3.48356009e+00, 3.10320862e+00, 2.51428571e+00, 1.83015873e+00, 1.16419501e+00, 6.29761905e-01, 3.40226757e-01, 4.08956916e-01, 9.49319728e-01, 2.07468254e+00, 3.89841270e+00], [-1.37500000e+00, 7.79613095e-01, 2.20813846e+00, 3.01785714e+00, 3.31605017e+00, 3.20999858e+00, 2.80698342e+00, 2.21428571e+00, 1.53918651e+00, 8.88966837e-01, 3.70907738e-01, 9.22902494e-02, 1.60395408e-01, 6.82504252e-01, 1.76589782e+00, 3.51785714e+00], [-5.41666667e-01, 1.12366071e+00, 2.20147747e+00, 2.77976190e+00, 2.94649235e+00, 2.78964711e+00, 2.39720451e+00, 1.85714286e+00, 1.25744048e+00, 6.86075680e-01, 2.31026786e-01, -1.97278912e-02, 2.17899660e-02, 4.43558673e-01, 1.33355655e+00, 2.77976190e+00], [8.84722222e-01, 1.72927083e+00, 2.23108879e+00, 2.44305556e+00, 2.41805060e+00, 2.20895337e+00, 1.86864335e+00, 1.45000000e+00, 1.00590278e+00, 5.89231151e-01, 2.52864583e-01, 4.96825397e-02, 3.25644841e-02, 2.54389881e-01, 7.68038194e-01, 1.62638889e+00], [3.00000000e+00, 2.64166667e+00, 2.30853175e+00, 2.00000000e+00, 1.71547619e+00, 1.45436508e+00, 1.21607143e+00, 1.00000000e+00, 8.05555556e-01, 6.32142857e-01, 4.79166667e-01, 3.46031746e-01, 2.32142857e-01, 1.36904762e-01, 5.97222222e-02, 0.00000000e+00]])
[docs] class Calibration(Enum): """Calibration levels.""" gamma = 1 sigma_nought = 2 beta_nought = 3 dn = 4
[docs] class TestSAFEGRD: """Test the SAFE GRD file handler."""
[docs] def test_read_calibrated_natural(self, measurement_filehandler): """Test the calibration routines.""" calibration = Calibration.sigma_nought xarr = measurement_filehandler.get_dataset(DataQuery(name="measurement", polarization="vv", calibration=calibration, quantity="natural"), info=dict()) expected = np.array([[np.nan, 0.02707529], [2.55858416, 3.27611055]]) np.testing.assert_allclose(xarr.values[:2, :2], expected, rtol=2e-7)
[docs] def test_read_calibrated_dB(self, measurement_filehandler): """Test the calibration routines.""" calibration = Calibration.sigma_nought xarr = measurement_filehandler.get_dataset(DataQuery(name="measurement", polarization="vv", calibration=calibration, quantity="dB"), info=dict()) expected = np.array([[np.nan, -15.674268], [4.079997, 5.153585]]) np.testing.assert_allclose(xarr.values[:2, :2], expected)
[docs] def test_read_lon_lats(self, measurement_filehandler): """Test reading lons and lats.""" query = DataQuery(name="longitude", polarization="vv") xarr = measurement_filehandler.get_dataset(query, info=dict()) expected = expected_longitudes np.testing.assert_allclose(xarr.values, expected[:10, :10], atol=1e-3)
annotation_xml = b"""<?xml version="1.0" encoding="UTF-8"?> <product> <adsHeader> <missionId>S1B</missionId> <productType>GRD</productType> <polarisation>HH</polarisation> <mode>EW</mode> <swath>EW</swath> <startTime>2020-03-15T05:04:28.137817</startTime> <stopTime>2020-03-15T05:05:32.416171</stopTime> <absoluteOrbitNumber>20698</absoluteOrbitNumber> <missionDataTakeId>160707</missionDataTakeId> <imageNumber>001</imageNumber> </adsHeader> <imageAnnotation> <imageInformation> <productFirstLineUtcTime>2020-03-15T05:04:28.137817</productFirstLineUtcTime> <productLastLineUtcTime>2020-03-15T05:05:32.416171</productLastLineUtcTime> <ascendingNodeTime>2020-03-15T04:33:22.256260</ascendingNodeTime> <anchorTime>2020-03-15T05:04:28.320641</anchorTime> <productComposition>Slice</productComposition> <sliceNumber>1</sliceNumber> <sliceList count="3"> <slice> <sliceNumber>1</sliceNumber> <sensingStartTime>2020-03-15T05:04:29.485847</sensingStartTime> <sensingStopTime>2020-03-15T05:05:36.317420</sensingStopTime> </slice> <slice> <sliceNumber>2</sliceNumber> <sensingStartTime>2020-03-15T05:05:30.253413</sensingStartTime> <sensingStopTime>2020-03-15T05:06:34.046608</sensingStopTime> </slice> <slice> <sliceNumber>3</sliceNumber> <sensingStartTime>2020-03-15T05:06:31.020979</sensingStartTime> <sensingStopTime>2020-03-15T05:07:31.775796</sensingStopTime> </slice> </sliceList> <slantRangeTime>4.955163637998161e-03</slantRangeTime> <pixelValue>Detected</pixelValue> <outputPixels>16 bit Unsigned Integer</outputPixels> <rangePixelSpacing>4.000000e+01</rangePixelSpacing> <azimuthPixelSpacing>4.000000e+01</azimuthPixelSpacing> <azimuthTimeInterval>5.998353361537205e-03</azimuthTimeInterval> <azimuthFrequency>3.425601970000000e+02</azimuthFrequency> <numberOfSamples>10</numberOfSamples> <numberOfLines>10</numberOfLines> <zeroDopMinusAcqTime>-1.366569000000000e+00</zeroDopMinusAcqTime> <incidenceAngleMidSwath>3.468272707039038e+01</incidenceAngleMidSwath> <imageStatistics> <outputDataMean> <re>4.873919e+02</re> <im>0.000000e+00</im> </outputDataMean> <outputDataStdDev> <re>2.451083e+02</re> <im>0.000000e+00</im> </outputDataStdDev> </imageStatistics> </imageInformation> </imageAnnotation> <geolocationGrid> <geolocationGridPointList count="4"> <geolocationGridPoint> <azimuthTime>2018-02-12T03:24:58.493342</azimuthTime> <slantRangeTime>4.964462411376810e-03</slantRangeTime> <line>0</line> <pixel>0</pixel> <latitude>7.021017981690355e+01</latitude> <longitude>5.609684402205929e+01</longitude> <height>8.234046399593353e-04</height> <incidenceAngle>1.918318045731997e+01</incidenceAngle> <elevationAngle>1.720012646010728e+01</elevationAngle> </geolocationGridPoint> <geolocationGridPoint> <azimuthTime>2018-02-12T03:24:58.493342</azimuthTime> <slantRangeTime>4.964462411376810e-03</slantRangeTime> <line>0</line> <pixel>9</pixel> <latitude>7.021017981690355e+01</latitude> <longitude>5.609684402205929e+01</longitude> <height>8.234046399593353e-04</height> <incidenceAngle>1.918318045731997e+01</incidenceAngle> <elevationAngle>1.720012646010728e+01</elevationAngle> </geolocationGridPoint> <geolocationGridPoint> <azimuthTime>2018-02-12T03:24:58.493342</azimuthTime> <slantRangeTime>4.964462411376810e-03</slantRangeTime> <line>9</line> <pixel>0</pixel> <latitude>7.021017981690355e+01</latitude> <longitude>5.609684402205929e+01</longitude> <height>8.234046399593353e-04</height> <incidenceAngle>1.918318045731997e+01</incidenceAngle> <elevationAngle>1.720012646010728e+01</elevationAngle> </geolocationGridPoint> <geolocationGridPoint> <azimuthTime>2018-02-12T03:24:58.493342</azimuthTime> <slantRangeTime>4.964462411376810e-03</slantRangeTime> <line>9</line> <pixel>9</pixel> <latitude>7.021017981690355e+01</latitude> <longitude>5.609684402205929e+01</longitude> <height>8.234046399593353e-04</height> <incidenceAngle>1.918318045731997e+01</incidenceAngle> <elevationAngle>1.720012646010728e+01</elevationAngle> </geolocationGridPoint> </geolocationGridPointList> </geolocationGrid> </product> """ noise_xml = b"""<?xml version="1.0" encoding="UTF-8"?> <noise> <noiseRangeVectorList count="3"> <noiseRangeVector> <azimuthTime>2020-03-15T05:04:28.137817</azimuthTime> <line>0</line> <pixel count="6">0 2 4 6 8 9</pixel> <noiseRangeLut count="6">0.00000e+00 2.00000e+00 4.00000e+00 6.00000e+00 8.00000e+00 9.00000e+00</noiseRangeLut> </noiseRangeVector> <noiseRangeVector> <azimuthTime>2020-03-15T05:04:28.137817</azimuthTime> <line>5</line> <pixel count="6">0 2 4 7 8 9</pixel> <noiseRangeLut count="6">0.00000e+00 2.00000e+00 4.00000e+00 7.00000e+00 8.00000e+00 9.00000e+00</noiseRangeLut> </noiseRangeVector> <noiseRangeVector> <azimuthTime>2020-03-15T05:04:28.137817</azimuthTime> <line>9</line> <pixel count="6">0 2 5 7 8 9</pixel> <noiseRangeLut count="6">0.00000e+00 2.00000e+00 5.00000e+00 7.00000e+00 8.00000e+00 9.00000e+00</noiseRangeLut> </noiseRangeVector> </noiseRangeVectorList> <noiseAzimuthVectorList count="8"> <noiseAzimuthVector> <swath>IW1</swath> <firstAzimuthLine>0</firstAzimuthLine> <firstRangeSample>1</firstRangeSample> <lastAzimuthLine>1</lastAzimuthLine> <lastRangeSample>3</lastRangeSample> <line count="1">0</line> <noiseAzimuthLut count="1">1.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW1</swath> <firstAzimuthLine>2</firstAzimuthLine> <firstRangeSample>0</firstRangeSample> <lastAzimuthLine>9</lastAzimuthLine> <lastRangeSample>1</lastRangeSample> <line count="4">2 4 6 8</line> <noiseAzimuthLut count="4">2.000000e+00 2.000000e+00 2.000000e+00 2.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW2</swath> <firstAzimuthLine>2</firstAzimuthLine> <firstRangeSample>2</firstRangeSample> <lastAzimuthLine>4</lastAzimuthLine> <lastRangeSample>4</lastRangeSample> <line count="2">2 4</line> <noiseAzimuthLut count="2">3.000000e+00 3.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW3</swath> <firstAzimuthLine>2</firstAzimuthLine> <firstRangeSample>5</firstRangeSample> <lastAzimuthLine>4</lastAzimuthLine> <lastRangeSample>8</lastRangeSample> <line count="2">2 4</line> <noiseAzimuthLut count="2">4.000000e+00 4.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW2</swath> <firstAzimuthLine>5</firstAzimuthLine> <firstRangeSample>2</firstRangeSample> <lastAzimuthLine>7</lastAzimuthLine> <lastRangeSample>5</lastRangeSample> <line count="2">5 6</line> <noiseAzimuthLut count="2">5.000000e+00 5.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW3</swath> <firstAzimuthLine>5</firstAzimuthLine> <firstRangeSample>6</firstRangeSample> <lastAzimuthLine>7</lastAzimuthLine> <lastRangeSample>9</lastRangeSample> <line count="2">5 6</line> <noiseAzimuthLut count="2">6.000000e+00 6.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW2</swath> <firstAzimuthLine>8</firstAzimuthLine> <firstRangeSample>2</firstRangeSample> <lastAzimuthLine>9</lastAzimuthLine> <lastRangeSample>6</lastRangeSample> <line count="1">8</line> <noiseAzimuthLut count="1">7.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW3</swath> <firstAzimuthLine>8</firstAzimuthLine> <firstRangeSample>7</firstRangeSample> <lastAzimuthLine>9</lastAzimuthLine> <lastRangeSample>9</lastRangeSample> <line count="1">8</line> <noiseAzimuthLut count="1">8.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> </noiseAzimuthVectorList> </noise> """ noise_xml_with_holes = b"""<?xml version="1.0" encoding="UTF-8"?> <noise> <noiseRangeVectorList count="3"> <noiseRangeVector> <azimuthTime>2020-03-15T05:04:28.137817</azimuthTime> <line>0</line> <pixel count="6">0 2 4 6 8 9</pixel> <noiseRangeLut count="6">0.00000e+00 2.00000e+00 4.00000e+00 6.00000e+00 8.00000e+00 9.00000e+00</noiseRangeLut> </noiseRangeVector> <noiseRangeVector> <azimuthTime>2020-03-15T05:04:28.137817</azimuthTime> <line>5</line> <pixel count="6">0 2 4 7 8 9</pixel> <noiseRangeLut count="6">0.00000e+00 2.00000e+00 4.00000e+00 7.00000e+00 8.00000e+00 9.00000e+00</noiseRangeLut> </noiseRangeVector> <noiseRangeVector> <azimuthTime>2020-03-15T05:04:28.137817</azimuthTime> <line>9</line> <pixel count="6">0 2 5 7 8 9</pixel> <noiseRangeLut count="6">0.00000e+00 2.00000e+00 5.00000e+00 7.00000e+00 8.00000e+00 9.00000e+00</noiseRangeLut> </noiseRangeVector> </noiseRangeVectorList> <noiseAzimuthVectorList count="12"> <noiseAzimuthVector> <swath>IW1</swath> <firstAzimuthLine>0</firstAzimuthLine> <firstRangeSample>3</firstRangeSample> <lastAzimuthLine>2</lastAzimuthLine> <lastRangeSample>5</lastRangeSample> <line count="1">0</line> <noiseAzimuthLut count="1">1.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW1</swath> <firstAzimuthLine>1</firstAzimuthLine> <firstRangeSample>0</firstRangeSample> <lastAzimuthLine>5</lastAzimuthLine> <lastRangeSample>1</lastRangeSample> <line count="4">2 4 5</line> <noiseAzimuthLut count="4">2.000000e+00 2.000000e+00 2.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW2</swath> <firstAzimuthLine>2</firstAzimuthLine> <firstRangeSample>8</firstRangeSample> <lastAzimuthLine>4</lastAzimuthLine> <lastRangeSample>9</lastRangeSample> <line count="2">2 4</line> <noiseAzimuthLut count="2">3.000000e+00 3.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW3</swath> <firstAzimuthLine>3</firstAzimuthLine> <firstRangeSample>2</firstRangeSample> <lastAzimuthLine>5</lastAzimuthLine> <lastRangeSample>3</lastRangeSample> <line count="2">3 5</line> <noiseAzimuthLut count="2">4.000000e+00 4.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW2</swath> <firstAzimuthLine>3</firstAzimuthLine> <firstRangeSample>4</firstRangeSample> <lastAzimuthLine>4</lastAzimuthLine> <lastRangeSample>5</lastRangeSample> <line count="2">3 4</line> <noiseAzimuthLut count="2">5.000000e+00 5.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW3</swath> <firstAzimuthLine>4</firstAzimuthLine> <firstRangeSample>6</firstRangeSample> <lastAzimuthLine>4</lastAzimuthLine> <lastRangeSample>7</lastRangeSample> <line count="2">4</line> <noiseAzimuthLut count="2">6.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW2</swath> <firstAzimuthLine>5</firstAzimuthLine> <firstRangeSample>4</firstRangeSample> <lastAzimuthLine>7</lastAzimuthLine> <lastRangeSample>6</lastRangeSample> <line count="1">5 7</line> <noiseAzimuthLut count="1">7.000000e+00 7.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW3</swath> <firstAzimuthLine>5</firstAzimuthLine> <firstRangeSample>7</firstRangeSample> <lastAzimuthLine>7</lastAzimuthLine> <lastRangeSample>9</lastRangeSample> <line count="1">6</line> <noiseAzimuthLut count="1">8.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW2</swath> <firstAzimuthLine>6</firstAzimuthLine> <firstRangeSample>0</firstRangeSample> <lastAzimuthLine>7</lastAzimuthLine> <lastRangeSample>3</lastRangeSample> <line count="2">6 7</line> <noiseAzimuthLut count="2">9.000000e+00 9.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW3</swath> <firstAzimuthLine>8</firstAzimuthLine> <firstRangeSample>0</firstRangeSample> <lastAzimuthLine>9</lastAzimuthLine> <lastRangeSample>0</lastRangeSample> <line count="2">8</line> <noiseAzimuthLut count="2">10.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW2</swath> <firstAzimuthLine>8</firstAzimuthLine> <firstRangeSample>2</firstRangeSample> <lastAzimuthLine>9</lastAzimuthLine> <lastRangeSample>3</lastRangeSample> <line count="1">8 9</line> <noiseAzimuthLut count="1">11.000000e+00 11.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> <noiseAzimuthVector> <swath>IW3</swath> <firstAzimuthLine>8</firstAzimuthLine> <firstRangeSample>4</firstRangeSample> <lastAzimuthLine>8</lastAzimuthLine> <lastRangeSample>5</lastRangeSample> <line count="1">8</line> <noiseAzimuthLut count="1">12.000000e+00</noiseAzimuthLut> </noiseAzimuthVector> </noiseAzimuthVectorList> </noise> """ calibration_xml = b"""<?xml version="1.0" encoding="UTF-8"?> <calibration> <adsHeader> <missionId>S1A</missionId> <productType>GRD</productType> <polarisation>VV</polarisation> <mode>IW</mode> <swath>IW</swath> <startTime>2018-02-12T03:24:58.493726</startTime> <stopTime>2018-02-12T03:25:01.493726</stopTime> <absoluteOrbitNumber>20568</absoluteOrbitNumber> <missionDataTakeId>144162</missionDataTakeId> <imageNumber>001</imageNumber> </adsHeader> <calibrationInformation> <absoluteCalibrationConstant>1.000000e+00</absoluteCalibrationConstant> </calibrationInformation> <calibrationVectorList count="4"> <calibrationVector> <azimuthTime>2018-02-12T03:24:58.493726</azimuthTime> <line>0</line> <pixel count="6">0 2 4 6 8 9</pixel> <sigmaNought count="6">1.894274e+03 1.788593e+03 1.320240e+03 1.277968e+03 1.277968e+03 1.277968e+03</sigmaNought> <betaNought count="6">1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03</betaNought> <gamma count="6">1.840695e+03 1.718649e+03 1.187203e+03 1.185249e+03 1.183303e+03 1.181365e+03</gamma> <dn count="6">1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03</dn> </calibrationVector> <calibrationVector> <azimuthTime>2018-02-12T03:24:59.493726</azimuthTime> <line>3</line> <pixel count="6">0 2 4 6 8 9</pixel> <sigmaNought count="6">1.894274e+03 1.788593e+03 1.320240e+03 1.277968e+03 1.277968e+03 1.277968e+03</sigmaNought> <betaNought count="6">1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03</betaNought> <gamma count="6">1.840695e+03 1.718649e+03 1.187203e+03 1.185249e+03 1.183303e+03 1.181365e+03</gamma> <dn count="6">1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03</dn> </calibrationVector> <calibrationVector> <azimuthTime>2018-02-12T03:25:00.493726</azimuthTime> <line>6</line> <pixel count="6">0 2 4 6 8 9</pixel> <sigmaNought count="6">1.894274e+03 1.788593e+03 1.320240e+03 1.277968e+03 1.277968e+03 1.277968e+03</sigmaNought> <betaNought count="6">1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03</betaNought> <gamma count="6">1.840695e+03 1.718649e+03 1.187203e+03 1.185249e+03 1.183303e+03 1.181365e+03</gamma> <dn count="6">1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03</dn> </calibrationVector> <calibrationVector> <azimuthTime>2018-02-12T03:25:01.493726</azimuthTime> <line>9</line> <pixel count="6">0 2 4 6 8 9</pixel> <sigmaNought count="6">1.894274e+03 1.788593e+03 1.320240e+03 1.277968e+03 1.277968e+03 1.277968e+03</sigmaNought> <betaNought count="6">1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03</betaNought> <gamma count="6">1.840695e+03 1.718649e+03 1.187203e+03 1.185249e+03 1.183303e+03 1.181365e+03</gamma> <dn count="6">1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03 1.0870e+03</dn> </calibrationVector> </calibrationVectorList> </calibration> """
[docs] class TestSAFEXMLNoise: """Test the SAFE XML Noise file handler."""
[docs] def setup_method(self): """Set up the test case.""" self.expected_azimuth_noise = np.array([[np.nan, 1, 1, 1, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], [np.nan, 1, 1, 1, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan], [2, 2, 3, 3, 3, 4, 4, 4, 4, np.nan], [2, 2, 3, 3, 3, 4, 4, 4, 4, np.nan], [2, 2, 3, 3, 3, 4, 4, 4, 4, np.nan], [2, 2, 5, 5, 5, 5, 6, 6, 6, 6], [2, 2, 5, 5, 5, 5, 6, 6, 6, 6], [2, 2, 5, 5, 5, 5, 6, 6, 6, 6], [2, 2, 7, 7, 7, 7, 7, 8, 8, 8], [2, 2, 7, 7, 7, 7, 7, 8, 8, 8], ]) self.expected_range_noise = np.array([[0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], [0, 1, 2, 3, 4, 5, 6, 7, 8, 9], ]) self.expected_azimuth_noise_with_holes = np.array( [[np.nan, np.nan, np.nan, 1, 1, 1, np.nan, np.nan, np.nan, np.nan], [2, 2, np.nan, 1, 1, 1, np.nan, np.nan, np.nan, np.nan], [2, 2, np.nan, 1, 1, 1, np.nan, np.nan, 3, 3], [2, 2, 4, 4, 5, 5, np.nan, np.nan, 3, 3], [2, 2, 4, 4, 5, 5, 6, 6, 3, 3], [2, 2, 4, 4, 7, 7, 7, 8, 8, 8], [9, 9, 9, 9, 7, 7, 7, 8, 8, 8], [9, 9, 9, 9, 7, 7, 7, 8, 8, 8], [10, np.nan, 11, 11, 12, 12, np.nan, np.nan, np.nan, np.nan], [10, np.nan, 11, 11, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan] ])
[docs] def test_azimuth_noise_array(self, noise_filehandler): """Test reading the azimuth-noise array.""" res = noise_filehandler.azimuth_noise_reader.read_azimuth_noise_array() np.testing.assert_array_equal(res, self.expected_azimuth_noise)
[docs] def test_azimuth_noise_array_with_holes(self, noise_with_holes_filehandler): """Test reading the azimuth-noise array.""" res = noise_with_holes_filehandler.azimuth_noise_reader.read_azimuth_noise_array() np.testing.assert_array_equal(res, self.expected_azimuth_noise_with_holes)
[docs] def test_range_noise_array(self, noise_filehandler): """Test reading the range-noise array.""" res = noise_filehandler.read_range_noise_array(chunks=5) np.testing.assert_allclose(res, self.expected_range_noise)
[docs] def test_get_noise_dataset(self, noise_filehandler): """Test using get_dataset for the noise.""" query = DataQuery(name="noise", polarization="vv") res = noise_filehandler.get_dataset(query, {}) np.testing.assert_allclose(res, self.expected_azimuth_noise * self.expected_range_noise)
[docs] def test_get_noise_dataset_has_right_chunk_size(self, noise_filehandler): """Test using get_dataset for the noise has right chunk size in result.""" query = DataQuery(name="noise", polarization="vv") res = noise_filehandler.get_dataset(query, {}, chunks=3) assert res.data.chunksize == (3, 3)
[docs] class TestSAFEXMLCalibration: """Test the SAFE XML Calibration file handler."""
[docs] def setup_method(self): """Set up testing.""" self.expected_gamma = np.array([[1840.695, 1779.672, 1718.649, 1452.926, 1187.203, 1186.226, 1185.249, 1184.276, 1183.303, 1181.365]]) * np.ones((10, 1))
[docs] def test_dn_calibration_array(self, calibration_filehandler): """Test reading the dn calibration array.""" expected_dn = np.ones((10, 10)) * 1087 res = calibration_filehandler.get_calibration(Calibration.dn, chunks=5) np.testing.assert_allclose(res, expected_dn)
[docs] def test_beta_calibration_array(self, calibration_filehandler): """Test reading the beta calibration array.""" expected_beta = np.ones((10, 10)) * 1087 res = calibration_filehandler.get_calibration(Calibration.beta_nought, chunks=5) np.testing.assert_allclose(res, expected_beta)
[docs] def test_sigma_calibration_array(self, calibration_filehandler): """Test reading the sigma calibration array.""" expected_sigma = np.array([[1894.274, 1841.4335, 1788.593, 1554.4165, 1320.24, 1299.104, 1277.968, 1277.968, 1277.968, 1277.968]]) * np.ones((10, 1)) res = calibration_filehandler.get_calibration(Calibration.sigma_nought, chunks=5) np.testing.assert_allclose(res, expected_sigma)
[docs] def test_gamma_calibration_array(self, calibration_filehandler): """Test reading the gamma calibration array.""" res = calibration_filehandler.get_calibration(Calibration.gamma, chunks=5) np.testing.assert_allclose(res, self.expected_gamma)
[docs] def test_get_calibration_dataset(self, calibration_filehandler): """Test using get_dataset for the calibration.""" query = DataQuery(name="gamma", polarization="vv") res = calibration_filehandler.get_dataset(query, {}) np.testing.assert_allclose(res, self.expected_gamma)
[docs] def test_get_calibration_dataset_has_right_chunk_size(self, calibration_filehandler): """Test using get_dataset for the calibration yields array with right chunksize.""" query = DataQuery(name="gamma", polarization="vv") res = calibration_filehandler.get_dataset(query, {}, chunks=3) assert res.data.chunksize == (3, 3) np.testing.assert_allclose(res, self.expected_gamma)
[docs] def test_get_calibration_constant(self, calibration_filehandler): """Test getting the calibration constant.""" query = DataQuery(name="calibration_constant", polarization="vv") res = calibration_filehandler.get_dataset(query, {}) assert res == 1
[docs] def test_incidence_angle(annotation_filehandler): """Test reading the incidence angle in an annotation file.""" query = DataQuery(name="incidence_angle", polarization="vv") res = annotation_filehandler.get_dataset(query, {}) np.testing.assert_allclose(res, 19.18318046)
[docs] def test_reading_from_reader(measurement_file, calibration_file, noise_file, annotation_file): """Test reading using the reader defined in the config.""" with open(Path(PACKAGE_CONFIG_PATH) / "readers" / "sar-c_safe.yaml") as fd: config = yaml.load(fd, Loader=yaml.UnsafeLoader) reader_class = config["reader"]["reader"] reader = reader_class(config) files = [measurement_file, calibration_file, noise_file, annotation_file] reader.create_storage_items(files) query = DataQuery(name="measurement", polarization="vv", calibration="sigma_nought", quantity="dB") query = DataID(reader._id_keys, **query.to_dict()) dataset_dict = reader.load([query]) array = dataset_dict["measurement"] np.testing.assert_allclose(array.attrs["area"].lons, expected_longitudes[:10, :10], atol=1e-3) expected_db = np.array([[np.nan, -15.674268], [4.079997, 5.153585]]) np.testing.assert_allclose(array.values[:2, :2], expected_db)
[docs] def test_filename_filtering_from_reader(measurement_file, calibration_file, noise_file, annotation_file, tmp_path): """Test that filenames get filtered before filehandlers are created.""" with open(Path(PACKAGE_CONFIG_PATH) / "readers" / "sar-c_safe.yaml") as fd: config = yaml.load(fd, Loader=yaml.UnsafeLoader) reader_class = config["reader"]["reader"] filter_parameters = {"start_time": datetime(2019, 2, 1, 0, 0, 0), "end_time": datetime(2019, 2, 1, 12, 0, 0)} reader = reader_class(config, filter_parameters) spurious_file = (tmp_path / "S1A_IW_GRDH_1SDV_20190202T024655_20190202T024720_025730_02DC2A_AE07.SAFE" / "measurement" / "s1a-iw-grd-vv-20190202t024655-20190202t024720-025730-02dc2a-001.tiff") files = [spurious_file, measurement_file, calibration_file, noise_file, annotation_file] files = reader.filter_selected_filenames(files) assert spurious_file not in files try: reader.create_storage_items(files) except rasterio.RasterioIOError as err: pytest.fail(str(err))
[docs] def test_swath_def_contains_gcps(measurement_file, calibration_file, noise_file, annotation_file): """Test reading using the reader defined in the config.""" with open(Path(PACKAGE_CONFIG_PATH) / "readers" / "sar-c_safe.yaml") as fd: config = yaml.load(fd, Loader=yaml.UnsafeLoader) reader_class = config["reader"]["reader"] reader = reader_class(config) files = [measurement_file, calibration_file, noise_file, annotation_file] reader.create_storage_items(files) query = DataQuery(name="measurement", polarization="vv", calibration="sigma_nought", quantity="dB") query = DataID(reader._id_keys, **query.to_dict()) dataset_dict = reader.load([query]) array = dataset_dict["measurement"] assert array.attrs["area"].attrs["gcps"] is not None