#!/usr/bin/python
# Copyright (c) 2018 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/>.
"""Unittests for generic image reader."""
import os
from datetime import datetime, timezone
import dask.array as da
import numpy as np
import pytest
import xarray as xr
from pyresample.geometry import AreaDefinition
from satpy import Scene
metadata_text = b"""<?xml version="1.0" encoding="UTF-8"?>
<LANDSAT_METADATA_FILE>
<PRODUCT_CONTENTS>
<ORIGIN>Image courtesy of the U.S. Geological Survey</ORIGIN>
<DIGITAL_OBJECT_IDENTIFIER>https://doi.org/10.5066/P975CC9B</DIGITAL_OBJECT_IDENTIFIER>
<LANDSAT_PRODUCT_ID>LC08_L1GT_026200_20240502_20240513_02_T2</LANDSAT_PRODUCT_ID>
<PROCESSING_LEVEL>L1GT</PROCESSING_LEVEL>
<COLLECTION_NUMBER>02</COLLECTION_NUMBER>
<COLLECTION_CATEGORY>T2</COLLECTION_CATEGORY>
<OUTPUT_FORMAT>GEOTIFF</OUTPUT_FORMAT>
<FILE_NAME_BAND_1>LC08_L1GT_026200_20240502_20240513_02_T2_B1.TIF</FILE_NAME_BAND_1>
<FILE_NAME_BAND_2>LC08_L1GT_026200_20240502_20240513_02_T2_B2.TIF</FILE_NAME_BAND_2>
<FILE_NAME_BAND_3>LC08_L1GT_026200_20240502_20240513_02_T2_B3.TIF</FILE_NAME_BAND_3>
<FILE_NAME_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_B4.TIF</FILE_NAME_BAND_4>
<FILE_NAME_BAND_5>LC08_L1GT_026200_20240502_20240513_02_T2_B5.TIF</FILE_NAME_BAND_5>
<FILE_NAME_BAND_6>LC08_L1GT_026200_20240502_20240513_02_T2_B6.TIF</FILE_NAME_BAND_6>
<FILE_NAME_BAND_7>LC08_L1GT_026200_20240502_20240513_02_T2_B7.TIF</FILE_NAME_BAND_7>
<FILE_NAME_BAND_8>LC08_L1GT_026200_20240502_20240513_02_T2_B8.TIF</FILE_NAME_BAND_8>
<FILE_NAME_BAND_9>LC08_L1GT_026200_20240502_20240513_02_T2_B9.TIF</FILE_NAME_BAND_9>
<FILE_NAME_BAND_10>LC08_L1GT_026200_20240502_20240513_02_T2_B10.TIF</FILE_NAME_BAND_10>
<FILE_NAME_BAND_11>LC08_L1GT_026200_20240502_20240513_02_T2_B11.TIF</FILE_NAME_BAND_11>
<FILE_NAME_QUALITY_L1_PIXEL>LC08_L1GT_026200_20240502_20240513_02_T2_QA_PIXEL.TIF</FILE_NAME_QUALITY_L1_PIXEL>
<FILE_NAME_QUALITY_L1_RADIOMETRIC_SATURATION>LC08_L1GT_026200_20240502_20240513_02_T2_QA_RADSAT.TIF</FILE_NAME_QUALITY_L1_RADIOMETRIC_SATURATION>
<FILE_NAME_ANGLE_COEFFICIENT>LC08_L1GT_026200_20240502_20240513_02_T2_ANG.txt</FILE_NAME_ANGLE_COEFFICIENT>
<FILE_NAME_ANGLE_SENSOR_AZIMUTH_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_VAA.TIF</FILE_NAME_ANGLE_SENSOR_AZIMUTH_BAND_4>
<FILE_NAME_ANGLE_SENSOR_ZENITH_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_VZA.TIF</FILE_NAME_ANGLE_SENSOR_ZENITH_BAND_4>
<FILE_NAME_ANGLE_SOLAR_AZIMUTH_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_SAA.TIF</FILE_NAME_ANGLE_SOLAR_AZIMUTH_BAND_4>
<FILE_NAME_ANGLE_SOLAR_ZENITH_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_SZA.TIF</FILE_NAME_ANGLE_SOLAR_ZENITH_BAND_4>
<FILE_NAME_METADATA_ODL>LC08_L1GT_026200_20240502_20240513_02_T2_MTL.txt</FILE_NAME_METADATA_ODL>
<FILE_NAME_METADATA_XML>LC08_L1GT_026200_20240502_20240513_02_T2_MTL.xml</FILE_NAME_METADATA_XML>
<DATA_TYPE_BAND_1>UINT16</DATA_TYPE_BAND_1>
<DATA_TYPE_BAND_2>UINT16</DATA_TYPE_BAND_2>
<DATA_TYPE_BAND_3>UINT16</DATA_TYPE_BAND_3>
<DATA_TYPE_BAND_4>UINT16</DATA_TYPE_BAND_4>
<DATA_TYPE_BAND_5>UINT16</DATA_TYPE_BAND_5>
<DATA_TYPE_BAND_6>UINT16</DATA_TYPE_BAND_6>
<DATA_TYPE_BAND_7>UINT16</DATA_TYPE_BAND_7>
<DATA_TYPE_BAND_8>UINT16</DATA_TYPE_BAND_8>
<DATA_TYPE_BAND_9>UINT16</DATA_TYPE_BAND_9>
<DATA_TYPE_BAND_10>UINT16</DATA_TYPE_BAND_10>
<DATA_TYPE_BAND_11>UINT16</DATA_TYPE_BAND_11>
<DATA_TYPE_QUALITY_L1_PIXEL>UINT16</DATA_TYPE_QUALITY_L1_PIXEL>
<DATA_TYPE_QUALITY_L1_RADIOMETRIC_SATURATION>UINT16</DATA_TYPE_QUALITY_L1_RADIOMETRIC_SATURATION>
<DATA_TYPE_ANGLE_SENSOR_AZIMUTH_BAND_4>INT16</DATA_TYPE_ANGLE_SENSOR_AZIMUTH_BAND_4>
<DATA_TYPE_ANGLE_SENSOR_ZENITH_BAND_4>INT16</DATA_TYPE_ANGLE_SENSOR_ZENITH_BAND_4>
<DATA_TYPE_ANGLE_SOLAR_AZIMUTH_BAND_4>INT16</DATA_TYPE_ANGLE_SOLAR_AZIMUTH_BAND_4>
<DATA_TYPE_ANGLE_SOLAR_ZENITH_BAND_4>INT16</DATA_TYPE_ANGLE_SOLAR_ZENITH_BAND_4>
</PRODUCT_CONTENTS>
<IMAGE_ATTRIBUTES>
<SPACECRAFT_ID>LANDSAT_8</SPACECRAFT_ID>
<SENSOR_ID>OLI_TIRS</SENSOR_ID>
<WRS_TYPE>2</WRS_TYPE>
<WRS_PATH>26</WRS_PATH>
<WRS_ROW>200</WRS_ROW>
<NADIR_OFFNADIR>NADIR</NADIR_OFFNADIR>
<TARGET_WRS_PATH>26</TARGET_WRS_PATH>
<TARGET_WRS_ROW>200</TARGET_WRS_ROW>
<DATE_ACQUIRED>2024-05-02</DATE_ACQUIRED>
<SCENE_CENTER_TIME>18:00:24.6148649Z</SCENE_CENTER_TIME>
<STATION_ID>LGN</STATION_ID>
<CLOUD_COVER>0.85</CLOUD_COVER>
<CLOUD_COVER_LAND>-1</CLOUD_COVER_LAND>
<IMAGE_QUALITY_OLI>9</IMAGE_QUALITY_OLI>
<IMAGE_QUALITY_TIRS>9</IMAGE_QUALITY_TIRS>
<SATURATION_BAND_1>N</SATURATION_BAND_1>
<SATURATION_BAND_2>N</SATURATION_BAND_2>
<SATURATION_BAND_3>N</SATURATION_BAND_3>
<SATURATION_BAND_4>Y</SATURATION_BAND_4>
<SATURATION_BAND_5>N</SATURATION_BAND_5>
<SATURATION_BAND_6>N</SATURATION_BAND_6>
<SATURATION_BAND_7>N</SATURATION_BAND_7>
<SATURATION_BAND_8>N</SATURATION_BAND_8>
<SATURATION_BAND_9>N</SATURATION_BAND_9>
<ROLL_ANGLE>-0.000</ROLL_ANGLE>
<SUN_AZIMUTH>-39.71362413</SUN_AZIMUTH>
<SUN_ELEVATION>-41.46228969</SUN_ELEVATION>
<EARTH_SUN_DISTANCE>1.0079981</EARTH_SUN_DISTANCE>
<TRUNCATION_OLI>UPPER</TRUNCATION_OLI>
<TIRS_SSM_MODEL>FINAL</TIRS_SSM_MODEL>
<TIRS_SSM_POSITION_STATUS>ESTIMATED</TIRS_SSM_POSITION_STATUS>
</IMAGE_ATTRIBUTES>
<PROJECTION_ATTRIBUTES>
<MAP_PROJECTION>UTM</MAP_PROJECTION>
<DATUM>WGS84</DATUM>
<ELLIPSOID>WGS84</ELLIPSOID>
<UTM_ZONE>40</UTM_ZONE>
<GRID_CELL_SIZE_PANCHROMATIC>15.00</GRID_CELL_SIZE_PANCHROMATIC>
<GRID_CELL_SIZE_REFLECTIVE>30.00</GRID_CELL_SIZE_REFLECTIVE>
<GRID_CELL_SIZE_THERMAL>30.00</GRID_CELL_SIZE_THERMAL>
<PANCHROMATIC_LINES>200</PANCHROMATIC_LINES>
<PANCHROMATIC_SAMPLES>200</PANCHROMATIC_SAMPLES>
<REFLECTIVE_LINES>100</REFLECTIVE_LINES>
<REFLECTIVE_SAMPLES>100</REFLECTIVE_SAMPLES>
<THERMAL_LINES>100</THERMAL_LINES>
<THERMAL_SAMPLES>100</THERMAL_SAMPLES>
<ORIENTATION>NORTH_UP</ORIENTATION>
<CORNER_UL_LAT_PRODUCT>24.18941</CORNER_UL_LAT_PRODUCT>
<CORNER_UL_LON_PRODUCT>58.17657</CORNER_UL_LON_PRODUCT>
<CORNER_UR_LAT_PRODUCT>24.15493</CORNER_UR_LAT_PRODUCT>
<CORNER_UR_LON_PRODUCT>60.44878</CORNER_UR_LON_PRODUCT>
<CORNER_LL_LAT_PRODUCT>22.06522</CORNER_LL_LAT_PRODUCT>
<CORNER_LL_LON_PRODUCT>58.15819</CORNER_LL_LON_PRODUCT>
<CORNER_LR_LAT_PRODUCT>22.03410</CORNER_LR_LAT_PRODUCT>
<CORNER_LR_LON_PRODUCT>60.39501</CORNER_LR_LON_PRODUCT>
<CORNER_UL_PROJECTION_X_PRODUCT>619500.000</CORNER_UL_PROJECTION_X_PRODUCT>
<CORNER_UL_PROJECTION_Y_PRODUCT>2675700.000</CORNER_UL_PROJECTION_Y_PRODUCT>
<CORNER_UR_PROJECTION_X_PRODUCT>850500.000</CORNER_UR_PROJECTION_X_PRODUCT>
<CORNER_UR_PROJECTION_Y_PRODUCT>2675700.000</CORNER_UR_PROJECTION_Y_PRODUCT>
<CORNER_LL_PROJECTION_X_PRODUCT>619500.000</CORNER_LL_PROJECTION_X_PRODUCT>
<CORNER_LL_PROJECTION_Y_PRODUCT>2440500.000</CORNER_LL_PROJECTION_Y_PRODUCT>
<CORNER_LR_PROJECTION_X_PRODUCT>850500.000</CORNER_LR_PROJECTION_X_PRODUCT>
<CORNER_LR_PROJECTION_Y_PRODUCT>2440500.000</CORNER_LR_PROJECTION_Y_PRODUCT>
</PROJECTION_ATTRIBUTES>
<LEVEL1_PROCESSING_RECORD>
<ORIGIN>Image courtesy of the U.S. Geological Survey</ORIGIN>
<DIGITAL_OBJECT_IDENTIFIER>https://doi.org/10.5066/P975CC9B</DIGITAL_OBJECT_IDENTIFIER>
<REQUEST_ID>1885324_00001</REQUEST_ID>
<LANDSAT_SCENE_ID>LC80262002024123LGN00</LANDSAT_SCENE_ID>
<LANDSAT_PRODUCT_ID>LC08_L1GT_026200_20240502_20240513_02_T2</LANDSAT_PRODUCT_ID>
<PROCESSING_LEVEL>L1GT</PROCESSING_LEVEL>
<COLLECTION_CATEGORY>T2</COLLECTION_CATEGORY>
<OUTPUT_FORMAT>GEOTIFF</OUTPUT_FORMAT>
<DATE_PRODUCT_GENERATED>2024-05-13T15:32:54Z</DATE_PRODUCT_GENERATED>
<PROCESSING_SOFTWARE_VERSION>LPGS_16.4.0</PROCESSING_SOFTWARE_VERSION>
<FILE_NAME_BAND_1>LC08_L1GT_026200_20240502_20240513_02_T2_B1.TIF</FILE_NAME_BAND_1>
<FILE_NAME_BAND_2>LC08_L1GT_026200_20240502_20240513_02_T2_B2.TIF</FILE_NAME_BAND_2>
<FILE_NAME_BAND_3>LC08_L1GT_026200_20240502_20240513_02_T2_B3.TIF</FILE_NAME_BAND_3>
<FILE_NAME_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_B4.TIF</FILE_NAME_BAND_4>
<FILE_NAME_BAND_5>LC08_L1GT_026200_20240502_20240513_02_T2_B5.TIF</FILE_NAME_BAND_5>
<FILE_NAME_BAND_6>LC08_L1GT_026200_20240502_20240513_02_T2_B6.TIF</FILE_NAME_BAND_6>
<FILE_NAME_BAND_7>LC08_L1GT_026200_20240502_20240513_02_T2_B7.TIF</FILE_NAME_BAND_7>
<FILE_NAME_BAND_8>LC08_L1GT_026200_20240502_20240513_02_T2_B8.TIF</FILE_NAME_BAND_8>
<FILE_NAME_BAND_9>LC08_L1GT_026200_20240502_20240513_02_T2_B9.TIF</FILE_NAME_BAND_9>
<FILE_NAME_BAND_10>LC08_L1GT_026200_20240502_20240513_02_T2_B10.TIF</FILE_NAME_BAND_10>
<FILE_NAME_BAND_11>LC08_L1GT_026200_20240502_20240513_02_T2_B11.TIF</FILE_NAME_BAND_11>
<FILE_NAME_QUALITY_L1_PIXEL>LC08_L1GT_026200_20240502_20240513_02_T2_QA_PIXEL.TIF</FILE_NAME_QUALITY_L1_PIXEL>
<FILE_NAME_QUALITY_L1_RADIOMETRIC_SATURATION>LC08_L1GT_026200_20240502_20240513_02_T2_QA_RADSAT.TIF</FILE_NAME_QUALITY_L1_RADIOMETRIC_SATURATION>
<FILE_NAME_ANGLE_COEFFICIENT>LC08_L1GT_026200_20240502_20240513_02_T2_ANG.txt</FILE_NAME_ANGLE_COEFFICIENT>
<FILE_NAME_ANGLE_SENSOR_AZIMUTH_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_VAA.TIF</FILE_NAME_ANGLE_SENSOR_AZIMUTH_BAND_4>
<FILE_NAME_ANGLE_SENSOR_ZENITH_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_VZA.TIF</FILE_NAME_ANGLE_SENSOR_ZENITH_BAND_4>
<FILE_NAME_ANGLE_SOLAR_AZIMUTH_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_SAA.TIF</FILE_NAME_ANGLE_SOLAR_AZIMUTH_BAND_4>
<FILE_NAME_ANGLE_SOLAR_ZENITH_BAND_4>LC08_L1GT_026200_20240502_20240513_02_T2_SZA.TIF</FILE_NAME_ANGLE_SOLAR_ZENITH_BAND_4>
<FILE_NAME_METADATA_ODL>LC08_L1GT_026200_20240502_20240513_02_T2_MTL.txt</FILE_NAME_METADATA_ODL>
<FILE_NAME_METADATA_XML>LC08_L1GT_026200_20240502_20240513_02_T2_MTL.xml</FILE_NAME_METADATA_XML>
<FILE_NAME_CPF>LC08CPF_20240429_20240630_02.03</FILE_NAME_CPF>
<FILE_NAME_BPF_OLI>LO8BPF20240502162846_20240502181430.01</FILE_NAME_BPF_OLI>
<FILE_NAME_BPF_TIRS>LT8BPF20240502144307_20240510102926.01</FILE_NAME_BPF_TIRS>
<FILE_NAME_RLUT>LC08RLUT_20150303_20431231_02_01.h5</FILE_NAME_RLUT>
<DATA_SOURCE_TIRS_STRAY_LIGHT_CORRECTION>TIRS</DATA_SOURCE_TIRS_STRAY_LIGHT_CORRECTION>
<DATA_SOURCE_ELEVATION>GLS2000</DATA_SOURCE_ELEVATION>
</LEVEL1_PROCESSING_RECORD>
<LEVEL1_MIN_MAX_RADIANCE>
<RADIANCE_MAXIMUM_BAND_1>748.04883</RADIANCE_MAXIMUM_BAND_1>
<RADIANCE_MINIMUM_BAND_1>-61.77412</RADIANCE_MINIMUM_BAND_1>
<RADIANCE_MAXIMUM_BAND_2>766.01111</RADIANCE_MAXIMUM_BAND_2>
<RADIANCE_MINIMUM_BAND_2>-63.25745</RADIANCE_MINIMUM_BAND_2>
<RADIANCE_MAXIMUM_BAND_3>705.87274</RADIANCE_MAXIMUM_BAND_3>
<RADIANCE_MINIMUM_BAND_3>-58.29120</RADIANCE_MINIMUM_BAND_3>
<RADIANCE_MAXIMUM_BAND_4>595.23163</RADIANCE_MAXIMUM_BAND_4>
<RADIANCE_MINIMUM_BAND_4>-49.15442</RADIANCE_MINIMUM_BAND_4>
<RADIANCE_MAXIMUM_BAND_5>364.25208</RADIANCE_MAXIMUM_BAND_5>
<RADIANCE_MINIMUM_BAND_5>-30.08006</RADIANCE_MINIMUM_BAND_5>
<RADIANCE_MAXIMUM_BAND_6>90.58618</RADIANCE_MAXIMUM_BAND_6>
<RADIANCE_MINIMUM_BAND_6>-7.48064</RADIANCE_MINIMUM_BAND_6>
<RADIANCE_MAXIMUM_BAND_7>30.53239</RADIANCE_MAXIMUM_BAND_7>
<RADIANCE_MINIMUM_BAND_7>-2.52137</RADIANCE_MINIMUM_BAND_7>
<RADIANCE_MAXIMUM_BAND_8>673.63843</RADIANCE_MAXIMUM_BAND_8>
<RADIANCE_MINIMUM_BAND_8>-55.62928</RADIANCE_MINIMUM_BAND_8>
<RADIANCE_MAXIMUM_BAND_9>142.35797</RADIANCE_MAXIMUM_BAND_9>
<RADIANCE_MINIMUM_BAND_9>-11.75597</RADIANCE_MINIMUM_BAND_9>
<RADIANCE_MAXIMUM_BAND_10>22.00180</RADIANCE_MAXIMUM_BAND_10>
<RADIANCE_MINIMUM_BAND_10>0.10033</RADIANCE_MINIMUM_BAND_10>
<RADIANCE_MAXIMUM_BAND_11>22.00180</RADIANCE_MAXIMUM_BAND_11>
<RADIANCE_MINIMUM_BAND_11>0.10033</RADIANCE_MINIMUM_BAND_11>
</LEVEL1_MIN_MAX_RADIANCE>
<LEVEL1_MIN_MAX_REFLECTANCE>
<REFLECTANCE_MAXIMUM_BAND_1>1.210700</REFLECTANCE_MAXIMUM_BAND_1>
<REFLECTANCE_MINIMUM_BAND_1>-0.099980</REFLECTANCE_MINIMUM_BAND_1>
<REFLECTANCE_MAXIMUM_BAND_2>1.210700</REFLECTANCE_MAXIMUM_BAND_2>
<REFLECTANCE_MINIMUM_BAND_2>-0.099980</REFLECTANCE_MINIMUM_BAND_2>
<REFLECTANCE_MAXIMUM_BAND_3>1.210700</REFLECTANCE_MAXIMUM_BAND_3>
<REFLECTANCE_MINIMUM_BAND_3>-0.099980</REFLECTANCE_MINIMUM_BAND_3>
<REFLECTANCE_MAXIMUM_BAND_4>1.210700</REFLECTANCE_MAXIMUM_BAND_4>
<REFLECTANCE_MINIMUM_BAND_4>-0.099980</REFLECTANCE_MINIMUM_BAND_4>
<REFLECTANCE_MAXIMUM_BAND_5>1.210700</REFLECTANCE_MAXIMUM_BAND_5>
<REFLECTANCE_MINIMUM_BAND_5>-0.099980</REFLECTANCE_MINIMUM_BAND_5>
<REFLECTANCE_MAXIMUM_BAND_6>1.210700</REFLECTANCE_MAXIMUM_BAND_6>
<REFLECTANCE_MINIMUM_BAND_6>-0.099980</REFLECTANCE_MINIMUM_BAND_6>
<REFLECTANCE_MAXIMUM_BAND_7>1.210700</REFLECTANCE_MAXIMUM_BAND_7>
<REFLECTANCE_MINIMUM_BAND_7>-0.099980</REFLECTANCE_MINIMUM_BAND_7>
<REFLECTANCE_MAXIMUM_BAND_8>1.210700</REFLECTANCE_MAXIMUM_BAND_8>
<REFLECTANCE_MINIMUM_BAND_8>-0.099980</REFLECTANCE_MINIMUM_BAND_8>
<REFLECTANCE_MAXIMUM_BAND_9>1.210700</REFLECTANCE_MAXIMUM_BAND_9>
<REFLECTANCE_MINIMUM_BAND_9>-0.099980</REFLECTANCE_MINIMUM_BAND_9>
</LEVEL1_MIN_MAX_REFLECTANCE>
<LEVEL1_MIN_MAX_PIXEL_VALUE>
<QUANTIZE_CAL_MAX_BAND_1>65535</QUANTIZE_CAL_MAX_BAND_1>
<QUANTIZE_CAL_MIN_BAND_1>1</QUANTIZE_CAL_MIN_BAND_1>
<QUANTIZE_CAL_MAX_BAND_2>65535</QUANTIZE_CAL_MAX_BAND_2>
<QUANTIZE_CAL_MIN_BAND_2>1</QUANTIZE_CAL_MIN_BAND_2>
<QUANTIZE_CAL_MAX_BAND_3>65535</QUANTIZE_CAL_MAX_BAND_3>
<QUANTIZE_CAL_MIN_BAND_3>1</QUANTIZE_CAL_MIN_BAND_3>
<QUANTIZE_CAL_MAX_BAND_4>65535</QUANTIZE_CAL_MAX_BAND_4>
<QUANTIZE_CAL_MIN_BAND_4>1</QUANTIZE_CAL_MIN_BAND_4>
<QUANTIZE_CAL_MAX_BAND_5>65535</QUANTIZE_CAL_MAX_BAND_5>
<QUANTIZE_CAL_MIN_BAND_5>1</QUANTIZE_CAL_MIN_BAND_5>
<QUANTIZE_CAL_MAX_BAND_6>65535</QUANTIZE_CAL_MAX_BAND_6>
<QUANTIZE_CAL_MIN_BAND_6>1</QUANTIZE_CAL_MIN_BAND_6>
<QUANTIZE_CAL_MAX_BAND_7>65535</QUANTIZE_CAL_MAX_BAND_7>
<QUANTIZE_CAL_MIN_BAND_7>1</QUANTIZE_CAL_MIN_BAND_7>
<QUANTIZE_CAL_MAX_BAND_8>65535</QUANTIZE_CAL_MAX_BAND_8>
<QUANTIZE_CAL_MIN_BAND_8>1</QUANTIZE_CAL_MIN_BAND_8>
<QUANTIZE_CAL_MAX_BAND_9>65535</QUANTIZE_CAL_MAX_BAND_9>
<QUANTIZE_CAL_MIN_BAND_9>1</QUANTIZE_CAL_MIN_BAND_9>
<QUANTIZE_CAL_MAX_BAND_10>65535</QUANTIZE_CAL_MAX_BAND_10>
<QUANTIZE_CAL_MIN_BAND_10>1</QUANTIZE_CAL_MIN_BAND_10>
<QUANTIZE_CAL_MAX_BAND_11>65535</QUANTIZE_CAL_MAX_BAND_11>
<QUANTIZE_CAL_MIN_BAND_11>1</QUANTIZE_CAL_MIN_BAND_11>
</LEVEL1_MIN_MAX_PIXEL_VALUE>
<LEVEL1_RADIOMETRIC_RESCALING>
<RADIANCE_MULT_BAND_1>1.2357E-02</RADIANCE_MULT_BAND_1>
<RADIANCE_MULT_BAND_2>1.2654E-02</RADIANCE_MULT_BAND_2>
<RADIANCE_MULT_BAND_3>1.1661E-02</RADIANCE_MULT_BAND_3>
<RADIANCE_MULT_BAND_4>9.8329E-03</RADIANCE_MULT_BAND_4>
<RADIANCE_MULT_BAND_5>6.0172E-03</RADIANCE_MULT_BAND_5>
<RADIANCE_MULT_BAND_6>1.4964E-03</RADIANCE_MULT_BAND_6>
<RADIANCE_MULT_BAND_7>5.0438E-04</RADIANCE_MULT_BAND_7>
<RADIANCE_MULT_BAND_8>1.1128E-02</RADIANCE_MULT_BAND_8>
<RADIANCE_MULT_BAND_9>2.3517E-03</RADIANCE_MULT_BAND_9>
<RADIANCE_MULT_BAND_10>3.3420E-04</RADIANCE_MULT_BAND_10>
<RADIANCE_MULT_BAND_11>3.3420E-04</RADIANCE_MULT_BAND_11>
<RADIANCE_ADD_BAND_1>-61.78647</RADIANCE_ADD_BAND_1>
<RADIANCE_ADD_BAND_2>-63.27010</RADIANCE_ADD_BAND_2>
<RADIANCE_ADD_BAND_3>-58.30286</RADIANCE_ADD_BAND_3>
<RADIANCE_ADD_BAND_4>-49.16426</RADIANCE_ADD_BAND_4>
<RADIANCE_ADD_BAND_5>-30.08607</RADIANCE_ADD_BAND_5>
<RADIANCE_ADD_BAND_6>-7.48213</RADIANCE_ADD_BAND_6>
<RADIANCE_ADD_BAND_7>-2.52188</RADIANCE_ADD_BAND_7>
<RADIANCE_ADD_BAND_8>-55.64041</RADIANCE_ADD_BAND_8>
<RADIANCE_ADD_BAND_9>-11.75832</RADIANCE_ADD_BAND_9>
<RADIANCE_ADD_BAND_10>0.10000</RADIANCE_ADD_BAND_10>
<RADIANCE_ADD_BAND_11>0.10000</RADIANCE_ADD_BAND_11>
<REFLECTANCE_MULT_BAND_1>2.0000E-05</REFLECTANCE_MULT_BAND_1>
<REFLECTANCE_MULT_BAND_2>2.0000E-05</REFLECTANCE_MULT_BAND_2>
<REFLECTANCE_MULT_BAND_3>2.0000E-05</REFLECTANCE_MULT_BAND_3>
<REFLECTANCE_MULT_BAND_4>2.0000E-05</REFLECTANCE_MULT_BAND_4>
<REFLECTANCE_MULT_BAND_5>2.0000E-05</REFLECTANCE_MULT_BAND_5>
<REFLECTANCE_MULT_BAND_6>2.0000E-05</REFLECTANCE_MULT_BAND_6>
<REFLECTANCE_MULT_BAND_7>2.0000E-05</REFLECTANCE_MULT_BAND_7>
<REFLECTANCE_MULT_BAND_8>2.0000E-05</REFLECTANCE_MULT_BAND_8>
<REFLECTANCE_MULT_BAND_9>2.0000E-05</REFLECTANCE_MULT_BAND_9>
<REFLECTANCE_ADD_BAND_1>-0.100000</REFLECTANCE_ADD_BAND_1>
<REFLECTANCE_ADD_BAND_2>-0.100000</REFLECTANCE_ADD_BAND_2>
<REFLECTANCE_ADD_BAND_3>-0.100000</REFLECTANCE_ADD_BAND_3>
<REFLECTANCE_ADD_BAND_4>-0.100000</REFLECTANCE_ADD_BAND_4>
<REFLECTANCE_ADD_BAND_5>-0.100000</REFLECTANCE_ADD_BAND_5>
<REFLECTANCE_ADD_BAND_6>-0.100000</REFLECTANCE_ADD_BAND_6>
<REFLECTANCE_ADD_BAND_7>-0.100000</REFLECTANCE_ADD_BAND_7>
<REFLECTANCE_ADD_BAND_8>-0.100000</REFLECTANCE_ADD_BAND_8>
<REFLECTANCE_ADD_BAND_9>-0.100000</REFLECTANCE_ADD_BAND_9>
</LEVEL1_RADIOMETRIC_RESCALING>
<LEVEL1_THERMAL_CONSTANTS>
<K1_CONSTANT_BAND_10>774.8853</K1_CONSTANT_BAND_10>
<K2_CONSTANT_BAND_10>1321.0789</K2_CONSTANT_BAND_10>
<K1_CONSTANT_BAND_11>480.8883</K1_CONSTANT_BAND_11>
<K2_CONSTANT_BAND_11>1201.1442</K2_CONSTANT_BAND_11>
</LEVEL1_THERMAL_CONSTANTS>
<LEVEL1_PROJECTION_PARAMETERS>
<MAP_PROJECTION>UTM</MAP_PROJECTION>
<DATUM>WGS84</DATUM>
<ELLIPSOID>WGS84</ELLIPSOID>
<UTM_ZONE>40</UTM_ZONE>
<GRID_CELL_SIZE_PANCHROMATIC>15.00</GRID_CELL_SIZE_PANCHROMATIC>
<GRID_CELL_SIZE_REFLECTIVE>30.00</GRID_CELL_SIZE_REFLECTIVE>
<GRID_CELL_SIZE_THERMAL>30.00</GRID_CELL_SIZE_THERMAL>
<ORIENTATION>NORTH_UP</ORIENTATION>
<RESAMPLING_OPTION>CUBIC_CONVOLUTION</RESAMPLING_OPTION>
</LEVEL1_PROJECTION_PARAMETERS>
</LANDSAT_METADATA_FILE>
"""
x_size = 100
y_size = 100
date = datetime(2024, 5, 12, tzinfo=timezone.utc)
[docs]
@pytest.fixture(scope="session")
def l1_area():
"""Get the landsat 1 area def."""
pcs_id = "WGS 84 / UTM zone 40N"
proj4_dict = {"proj": "utm", "zone": 40, "datum": "WGS84", "units": "m", "no_defs": None, "type": "crs"}
area_extent = (619485., 2440485., 850515., 2675715.)
return AreaDefinition("geotiff_area", pcs_id, pcs_id,
proj4_dict, x_size, y_size,
area_extent)
[docs]
@pytest.fixture(scope="session")
def b4_data():
"""Get the data for the b4 channel."""
return da.random.randint(12000, 16000,
size=(y_size, x_size),
chunks=(50, 50)).astype(np.uint16)
[docs]
@pytest.fixture(scope="session")
def b11_data():
"""Get the data for the b11 channel."""
return da.random.randint(8000, 14000,
size=(y_size, x_size),
chunks=(50, 50)).astype(np.uint16)
[docs]
@pytest.fixture(scope="session")
def sza_data():
"""Get the data for the sza."""
return da.random.randint(1, 10000,
size=(y_size, x_size),
chunks=(50, 50)).astype(np.uint16)
[docs]
def create_tif_file(data, name, area, filename):
"""Create a tif file."""
data_array = xr.DataArray(data,
dims=("y", "x"),
attrs={"name": name,
"start_time": date})
scn = Scene()
scn["band_data"] = data_array
scn["band_data"].attrs["area"] = area
scn.save_dataset("band_data", writer="geotiff", enhance=False, fill_value=0,
filename=os.fspath(filename))
[docs]
@pytest.fixture(scope="session")
def l1_files_path(tmp_path_factory):
"""Create the path for l1 files."""
return tmp_path_factory.mktemp("l1_files")
[docs]
@pytest.fixture(scope="session")
def b4_file(l1_files_path, b4_data, l1_area):
"""Create the file for the b4 channel."""
data = b4_data
filename = l1_files_path / "LC08_L1GT_026200_20240502_20240513_02_T2_B4.TIF"
name = "B4"
create_tif_file(data, name, l1_area, filename)
return os.fspath(filename)
[docs]
@pytest.fixture(scope="session")
def b11_file(l1_files_path, b11_data, l1_area):
"""Create the file for the b11 channel."""
data = b11_data
filename = l1_files_path / "LC08_L1GT_026200_20240502_20240513_02_T2_B11.TIF"
name = "B11"
create_tif_file(data, name, l1_area, filename)
return os.fspath(filename)
[docs]
@pytest.fixture(scope="session")
def sza_file(l1_files_path, sza_data, l1_area):
"""Create the file for the sza."""
data = sza_data
filename = l1_files_path / "LC08_L1GT_026200_20240502_20240513_02_T2_SZA.TIF"
name = "sza"
create_tif_file(data, name, l1_area, filename)
return os.fspath(filename)
[docs]
@pytest.fixture(scope="session")
def mda_file(l1_files_path):
"""Create the metadata xml file."""
filename = l1_files_path / "LC08_L1GT_026200_20240502_20240513_02_T2_MTL.xml"
with open(filename, "wb") as f:
f.write(metadata_text)
return os.fspath(filename)
[docs]
@pytest.fixture(scope="session")
def all_files(b4_file, b11_file, mda_file, sza_file):
"""Return all the files."""
return b4_file, b11_file, mda_file, sza_file
[docs]
class TestOLITIRSL1:
"""Test generic image reader."""
[docs]
def setup_method(self, tmp_path):
"""Set up the filename and filetype info dicts.."""
self.filename_info = dict(observation_date=datetime(2024, 5, 3),
platform_type="L",
process_level_correction="L1TP",
spacecraft_id="08",
data_type="C")
self.ftype_info = {"file_type": "granule_B4"}
[docs]
def test_basicload(self, l1_area, b4_file, b11_file, mda_file):
"""Test loading a Landsat Scene."""
scn = Scene(reader="oli_tirs_l1_tif", filenames=[b4_file,
b11_file,
mda_file])
scn.load(["B4", "B11"])
# Check dataset is loaded correctly
assert scn["B4"].shape == (100, 100)
assert scn["B4"].attrs["area"] == l1_area
assert scn["B4"].attrs["saturated"]
assert scn["B11"].shape == (100, 100)
assert scn["B11"].attrs["area"] == l1_area
with pytest.raises(KeyError, match="saturated"):
assert not scn["B11"].attrs["saturated"]
[docs]
def test_ch_startend(self, b4_file, sza_file, mda_file):
"""Test correct retrieval of start/end times."""
scn = Scene(reader="oli_tirs_l1_tif", filenames=[b4_file, sza_file, mda_file])
bnds = scn.available_dataset_names()
assert bnds == ["B4", "solar_zenith_angle"]
scn.load(["B4"])
assert scn.start_time == datetime(2024, 5, 2, 18, 0, 24, tzinfo=timezone.utc)
assert scn.end_time == datetime(2024, 5, 2, 18, 0, 24, tzinfo=timezone.utc)
[docs]
def test_loading_gd(self, mda_file, b4_file):
"""Test loading a Landsat Scene with good channel requests."""
from satpy.readers.oli_tirs_l1_tif import OLITIRSCHReader, OLITIRSMDReader
good_mda = OLITIRSMDReader(mda_file, self.filename_info, {})
rdr = OLITIRSCHReader(b4_file, self.filename_info, self.ftype_info, good_mda)
# Check case with good file data and load request
rdr.get_dataset({"name": "B4", "calibration": "counts"}, {"standard_name": "test_data", "units": "test_units"})
[docs]
def test_loading_badfil(self, mda_file, b4_file):
"""Test loading a Landsat Scene with bad channel requests."""
from satpy.readers.oli_tirs_l1_tif import OLITIRSCHReader, OLITIRSMDReader
good_mda = OLITIRSMDReader(mda_file, self.filename_info, {})
rdr = OLITIRSCHReader(b4_file, self.filename_info, self.ftype_info, good_mda)
ftype = {"standard_name": "test_data", "units": "test_units"}
# Check case with request to load channel not matching filename
with pytest.raises(ValueError, match="Requested channel B5 does not match the reader channel B4"):
rdr.get_dataset({"name": "B5", "calibration": "counts"}, ftype)
[docs]
def test_loading_badchan(self, mda_file, b11_file):
"""Test loading a Landsat Scene with bad channel requests."""
from satpy.readers.oli_tirs_l1_tif import OLITIRSCHReader, OLITIRSMDReader
good_mda = OLITIRSMDReader(mda_file, self.filename_info, {})
ftype = {"standard_name": "test_data", "units": "test_units"}
bad_finfo = self.filename_info.copy()
bad_finfo["data_type"] = "T"
# Check loading invalid channel for data type
rdr = OLITIRSCHReader(b11_file, bad_finfo, self.ftype_info, good_mda)
with pytest.raises(ValueError, match="Requested channel B4 is not available in this granule"):
rdr.get_dataset({"name": "B4", "calibration": "counts"}, ftype)
bad_finfo["data_type"] = "O"
ftype_b11 = self.ftype_info.copy()
ftype_b11["file_type"] = "granule_B11"
rdr = OLITIRSCHReader(b11_file, bad_finfo, ftype_b11, good_mda)
with pytest.raises(ValueError, match="Requested channel B11 is not available in this granule"):
rdr.get_dataset({"name": "B11", "calibration": "counts"}, ftype)
[docs]
def test_badfiles(self, mda_file, b4_file):
"""Test loading a Landsat Scene with bad data."""
from satpy.readers.oli_tirs_l1_tif import OLITIRSCHReader, OLITIRSMDReader
bad_fname_info = self.filename_info.copy()
bad_fname_info["platform_type"] = "B"
ftype = {"standard_name": "test_data", "units": "test_units"}
# Test that metadata reader initialises with correct filename
good_mda = OLITIRSMDReader(mda_file, self.filename_info, ftype)
# Check metadata reader fails if platform type is wrong
with pytest.raises(ValueError, match="This reader only supports Landsat data"):
OLITIRSMDReader(mda_file, bad_fname_info, ftype)
# Test that metadata reader initialises with correct filename
OLITIRSCHReader(b4_file, self.filename_info, self.ftype_info, good_mda)
# Check metadata reader fails if platform type is wrong
with pytest.raises(ValueError, match="This reader only supports Landsat data"):
OLITIRSCHReader(b4_file, bad_fname_info, self.ftype_info, good_mda)
bad_ftype_info = self.ftype_info.copy()
bad_ftype_info["file_type"] = "granule-b05"
with pytest.raises(ValueError, match="Invalid file type: granule-b05"):
OLITIRSCHReader(b4_file, self.filename_info, bad_ftype_info, good_mda)
[docs]
def test_calibration_counts(self, all_files, b4_data, b11_data):
"""Test counts calibration mode for the reader."""
from satpy import Scene
scn = Scene(reader="oli_tirs_l1_tif", filenames=all_files)
scn.load(["B4", "B11"], calibration="counts")
np.testing.assert_allclose(scn["B4"].values, b4_data)
np.testing.assert_allclose(scn["B11"].values, b11_data)
assert scn["B4"].attrs["units"] == "1"
assert scn["B11"].attrs["units"] == "1"
assert scn["B4"].attrs["standard_name"] == "counts"
assert scn["B11"].attrs["standard_name"] == "counts"
[docs]
def test_calibration_radiance(self, all_files, b4_data, b11_data):
"""Test radiance calibration mode for the reader."""
from satpy import Scene
exp_b04 = (b4_data * 0.0098329 - 49.16426).astype(np.float32)
exp_b11 = (b11_data * 0.0003342 + 0.100000).astype(np.float32)
scn = Scene(reader="oli_tirs_l1_tif", filenames=all_files)
scn.load(["B4", "B11"], calibration="radiance")
assert scn["B4"].attrs["units"] == "W m-2 um-1 sr-1"
assert scn["B11"].attrs["units"] == "W m-2 um-1 sr-1"
assert scn["B4"].attrs["standard_name"] == "toa_outgoing_radiance_per_unit_wavelength"
assert scn["B11"].attrs["standard_name"] == "toa_outgoing_radiance_per_unit_wavelength"
np.testing.assert_allclose(scn["B4"].values, exp_b04, rtol=1e-4)
np.testing.assert_allclose(scn["B11"].values, exp_b11, rtol=1e-4)
[docs]
def test_calibration_highlevel(self, all_files, b4_data, b11_data):
"""Test high level calibration modes for the reader."""
from satpy import Scene
exp_b04 = (b4_data * 2e-05 - 0.1).astype(np.float32) * 100
exp_b11 = (b11_data * 0.0003342 + 0.100000)
exp_b11 = (1201.1442 / np.log((480.8883 / exp_b11) + 1)).astype(np.float32)
scn = Scene(reader="oli_tirs_l1_tif", filenames=all_files)
scn.load(["B4", "B11"])
assert scn["B4"].attrs["units"] == "%"
assert scn["B11"].attrs["units"] == "K"
assert scn["B4"].attrs["standard_name"] == "toa_bidirectional_reflectance"
assert scn["B11"].attrs["standard_name"] == "brightness_temperature"
np.testing.assert_allclose(np.array(scn["B4"].values), np.array(exp_b04), rtol=1e-4)
np.testing.assert_allclose(scn["B11"].values, exp_b11, rtol=1e-6)
[docs]
def test_angles(self, all_files, sza_data):
"""Test calibration modes for the reader."""
from satpy import Scene
# Check angles are calculated correctly
scn = Scene(reader="oli_tirs_l1_tif", filenames=all_files)
scn.load(["solar_zenith_angle"])
assert scn["solar_zenith_angle"].attrs["units"] == "degrees"
assert scn["solar_zenith_angle"].attrs["standard_name"] == "solar_zenith_angle"
np.testing.assert_allclose(scn["solar_zenith_angle"].values * 100,
np.array(sza_data),
atol=0.01,
rtol=1e-3)
[docs]
def test_area_def(self, mda_file):
"""Check we can get the area defs properly."""
from satpy.readers.oli_tirs_l1_tif import OLITIRSMDReader
mda = OLITIRSMDReader(mda_file, self.filename_info, {})
standard_area = mda.build_area_def("B1")
pan_area = mda.build_area_def("B8")
assert standard_area.area_extent == (619485.0, 2440485.0, 850515.0, 2675715.0)
assert pan_area.area_extent == (619492.5, 2440492.5, 850507.5, 2675707.5)