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

geotiepoints = pytest.importorskip("geotiepoints", "1.7.5")

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

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([[-0., 0.54230055, 0.87563228, 1., 0.91541479, 0.62184442, 0.26733714, -0., -0.18015287, -0.27312165], [1.0883956 , 1.25662247, 1.34380634, 1.34995884, 1.2750712 , 1.11911385, 0.9390845 , 0.79202785, 0.67796547, 0.59691204], [1.75505196, 1.74123364, 1.71731849, 1.68330292, 1.63918145, 1.58494674, 1.52376394, 1.45880655, 1.39007883, 1.31758574], [2., 1.99615628, 1.99615609, 2., 2.00768917, 2.0192253 , 2.02115051, 2. , 1.95576762, 1.88845002], [1.82332931, 2.02143515, 2.18032829, 2.30002491, 2.38053511, 2.4218612 , 2.43113105, 2.41546985, 2.37487052, 2.3093278 ], [1.22479001, 1.81701462, 2.26984318, 2.58335874, 2.75765719, 2.79279164, 2.75366973, 2.70519769, 2.64737395, 2.58019762], [0.51375081, 1.53781389, 2.3082042 , 2.82500549, 3.0885147 , 3.09893859, 2.98922885, 2.89232293, 2.8082302 , 2.7369586 ], [0., 1.33889733, 2.33891557, 3., 3.32266837, 3.30731797, 3.1383157 , 3., 2.8923933 , 2.81551297], [-0.31638932, 1.22031759, 2.36197571, 3.10836734, 3.46019271, 3.41800603, 3.20098223, 3.02826595, 2.89989242, 2.81588745], [-0.43541441, 1.18211505, 2.37738272, 3.1501186 , 3.50112948, 3.43104055, 3.17724665, 2.97712796, 2.83072911, 2.73808164]])
[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]], dtype=np.float32) np.testing.assert_allclose(xarr.values[:2, :2], expected, rtol=2e-7) assert xarr.dtype == np.float32 assert xarr.compute().dtype == np.float32
[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]], dtype=np.float32) np.testing.assert_allclose(xarr.values[:2, :2], expected, rtol=1e-6) assert xarr.dtype == np.float32 assert xarr.compute().dtype == np.float32
[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()) np.testing.assert_allclose(xarr.values, expected_longitudes) assert xarr.dtype == np.float64 assert xarr.compute().dtype == np.float64
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) assert res.dtype == np.float32 assert res.compute().dtype == np.float32
[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) assert res.dtype == np.float32 assert res.compute().dtype == np.float32
[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) assert res.dtype == np.float32 assert res.compute().dtype == np.float32
[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) assert res.dtype == np.float32 assert res.compute().dtype == np.float32
[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) assert res.dtype == np.float32 assert res.compute().dtype == np.float32
[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) assert res.dtype == np.float32 assert res.compute().dtype == np.float32
[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 assert type(res) is np.float32
[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) assert res.dtype == np.float32 assert res.compute().dtype == np.float32
[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) expected_db = np.array([[np.nan, -15.674268], [4.079997, 5.153585]]) np.testing.assert_allclose(array.values[:2, :2], expected_db, rtol=1e-6) assert array.dtype == np.float32 assert array.compute().dtype == np.float32
[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_and_bounding_box(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 assert array.attrs["area"].attrs["bounding_box"] is not None