Commit 33d08993 authored by Daniel Scheffler's avatar Daniel Scheffler
Browse files

Implemented sensor specifications and possibility to ignore pan and thermal bands.


Signed-off-by: Daniel Scheffler's avatarDaniel Scheffler <danschef@gfz-potsdam.de>
parent 154da29e
......@@ -5,7 +5,6 @@
import collections
import os
from glob import glob
from typing import Dict, Union, List
import numpy as np
......@@ -14,23 +13,32 @@ from matplotlib import pyplot as plt
from scipy.interpolate import interp1d
from . import __path__
from .sensorspecs import get_LayerBandsAssignment
def RSR_reader(satellite, sensor, no_thermal=False, no_pan=False, v=False):
# type: (str, str, bool, bool, bool) -> collections.OrderedDict
def RSR_reader(satellite, sensor, subsystem='',
LayerBandsAssignment=None, sort_by_cwl=False, no_thermal=False, no_pan=False, v=False):
# type: (str, str, str, list, bool, bool, bool, bool) -> collections.OrderedDict
"""Read RSR for any sensor and return a dictionary containing band names as keys and RSR numpy arrays as values.
:param satellite: satellite to read the relative spectral response for
:param sensor: sensor to read the relative spectral response for
:param subsystem: subsystem to read the relative spectral response for
:param LayerBandsAssignment: custom list of bands to read, e.g., ['1', '3', '8']
:param sort_by_cwl: whether to sort the returned bands list by central wavelength position
(default: False)
:param no_thermal: whether to exclude thermal bands from the returned bands list (default: False)
:param no_pan: whether to exclude panchromatic bands from the returned bands list (default: False)
:param v: verbose mode
"""
# LayerBandsAssignment = META.get_LayerBandsAssignment(GMS_id, no_thermal=no_thermal, no_pan=no_pan)
RSR_dict = collections.OrderedDict()
RSR_dir = os.path.join(__path__[0], 'data', satellite, sensor)
LBA = LayerBandsAssignment or get_LayerBandsAssignment(satellite, sensor, subsystem,
no_thermal=no_thermal, no_pan=no_pan,
sort_by_cwl=sort_by_cwl)
bandnames = ['band_%s' % b for b in LBA]
for bandname in glob(os.path.join(RSR_dir, 'band_*')):
for bandname in bandnames:
RSR_path = os.path.join(RSR_dir, bandname)
try:
RSR_dict[bandname] = np.loadtxt(RSR_path, skiprows=1)
......@@ -44,9 +52,9 @@ def RSR_reader(satellite, sensor, no_thermal=False, no_pan=False, v=False):
class RelativeSpectralResponse(object):
def __init__(self, satellite, sensor, wvl_unit='nanometers', specres_nm=1, format_bandnames=False,
no_thermal=False, no_pan=False, v=False):
# type: (str, str, str, float, bool, bool, bool, bool) -> None
def __init__(self, satellite, sensor, subsystem='', wvl_unit='nanometers', specres_nm=1, format_bandnames=False,
LayerBandsAssignment=None, sort_by_cwl=False, no_thermal=False, no_pan=False, v=False):
# type: (str, str, str, str, float, bool, list, bool, bool, bool, bool) -> None
"""RelativeSpectralResponse instance provides relative spectral response functions, wavelength positions, etc..
:param satellite: satellite to create the RelativeSpectralResponse instance for
......@@ -55,6 +63,9 @@ class RelativeSpectralResponse(object):
('nanometers' or 'micrometers)
:param specres_nm: output spectral resolution of RSRs in nanometers
:param format_bandnames: whether to format default strings from LayerBandsAssignment as 'B01', 'B02' etc..
:param LayerBandsAssignment: custom list of bands to include, e.g., ['1', '3', '8']
:param sort_by_cwl: whether to sort the returned bands list by central wavelength position
(default: False)
:param no_thermal whether to exclude thermal bands from the returned bands list
(default: False)
:param no_pan: whether to exclude panchromatic bands from the returned bands list
......@@ -76,16 +87,16 @@ class RelativeSpectralResponse(object):
self.satellite = satellite
self.sensor = sensor
self.conv = {}
self.LayerBandsAssignment = []
self.LayerBandsAssignment = LayerBandsAssignment or []
self.no_thermal = no_thermal
self.no_pan = no_pan
self.v = v
self.from_satellite_sensor(satellite, sensor)
self.from_satellite_sensor(satellite, sensor, subsystem,
sort_by_cwl=sort_by_cwl, no_thermal=no_thermal, no_pan=no_pan, v=v)
def from_satellite_sensor(self, satellite, sensor):
rsr_dict = RSR_reader(satellite, sensor, no_thermal=self.no_thermal, no_pan=self.no_pan,
v=self.v) # type: collections.OrderedDict # (ordered according to LBA)
def from_satellite_sensor(self, satellite, sensor, subsystem='', **kwargs):
rsr_dict = RSR_reader(satellite, sensor, subsystem, **kwargs) # (ordered according to LBA)
return self.from_dict(rsr_dict)
def from_dict(self, rsr_dict):
......
# -*- coding: utf-8 -*-
"""Sensor specifications needed by pyrsr."""
from pandas import DataFrame
import numpy as np
sensors = {
'AVNIR-2': {
'satellite': 'ALOS',
'sensor': 'AVNIR-2',
'LBA': ['1', '2', '3', '4'],
},
'AST_full': {
'satellite': 'Terra',
'sensor': 'ASTER',
'LBA': ['1', '2', '3N', '3B', '4', '5', '6', '7', '8', '9', '10', '11', '12', '13', '14'],
},
'AST_V1': {
'satellite': 'Terra',
'sensor': 'ASTER',
'subsystem': 'VNIR1',
'LBA': ['1', '2', '3N'],
},
'AST_V2': {
'satellite': 'Terra',
'sensor': 'ASTER',
'subsystem': 'VNIR2',
'LBA': ['3B'],
},
'AST_S': {
'satellite': 'Terra',
'sensor': 'ASTER',
'subsystem': 'SWIR',
'LBA': ['4', '5', '6', '7', '8', '9'],
},
'AST_T': {
'satellite': 'Terra',
'sensor': 'ASTER',
'subsystem': 'TIR',
'LBA': ['10', '11', '12', '13', '14'],
'thermal': ['10', '11', '12', '13', '14']
},
'TM4': {
'satellite': 'Landsat-4',
'sensor': 'TM',
'subsystem': 'SAM',
'LBA': ['1', '2', '3', '4', '5', '6', '7'],
'LBA_sorted': ['1', '2', '3', '4', '5', '7', '6'],
'thermal': ['6']
},
'TM5': {
'satellite': 'Landsat-5',
'sensor': 'TM',
'subsystem': 'SAM',
'LBA': ['1', '2', '3', '4', '5', '6', '7'],
'LBA_sorted': ['1', '2', '3', '4', '5', '7', '6'],
'thermal': ['6']
},
'TM7': {
'satellite': 'Landsat-7',
'sensor': 'ETM+',
'subsystem': 'SAM',
'LBA': ['1', '2', '3', '4', '5', '6L', '6H', '7', '8'],
'LBA_sorted': ['1', '2', '3', '8', '4', '5', '7', '6L', '6H'],
'pan': ['8'],
'thermal': ['6L', '6H']
},
'LDCM': {
'satellite': 'Landsat-8',
'sensor': 'LDCM',
'LBA': ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'],
'LBA_sorted': ['1', '2', '3', '4', '5', '9', '6', '7', '8', '10', '11'],
'pan': ['8'],
'thermal': ['10', '11']
},
'OLI_TIRS': {
'satellite': 'Landsat-8',
'sensor': 'OLI_TIRS',
'LBA': ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11'],
'LBA_sorted': ['1', '2', '3', '4', '5', '9', '6', '7', '8', '10', '11'],
'pan': ['8'],
'thermal': ['10', '11']
},
'OLI': {
'satellite': 'Landsat-8',
'sensor': 'OLI',
'LBA': ['1', '2', '3', '4', '5', '6', '7', '8', '9'],
'LBA_sorted': ['1', '2', '3', '4', '5', '9', '6', '7', '8'],
'pan': ['8']
},
'TIRS': {
'satellite': 'Landsat-8',
'sensor': 'TIRS',
'LBA': ['10', '11'],
'thermal': ['10', '11']
},
'SPOT1a': {
'satellite': 'SPOT-1',
'sensor': 'HRV1',
'LBA': ['1', '2', '3', '4'],
'pan': ['4'],
},
'SPOT2a': {
'satellite': 'SPOT-2',
'sensor': 'HRV1',
'LBA': ['1', '2', '3', '4'],
'pan': ['4'],
},
'SPOT3a': {
'satellite': 'SPOT-3',
'sensor': 'HRV1',
'LBA': ['1', '2', '3', '4'],
'pan': ['4'],
},
'SPOT4a': {
'satellite': 'SPOT-4',
'sensor': 'HRVIR1',
'LBA': ['1', '2', '3', '4', '5'],
'pan': ['5']
},
'SPOT5a': {
'satellite': 'SPOT-5',
'sensor': 'HRG1',
'LBA': ['1', '2', '3', '4', '5'],
'pan': ['5']
},
'SPOT1b': {
'satellite': 'SPOT-1',
'sensor': 'HRV2',
'LBA': ['1', '2', '3', '4'],
'pan': ['4'],
},
'SPOT2b': {
'satellite': 'SPOT-2',
'sensor': 'HRV2',
'LBA': ['1', '2', '3', '4'],
'pan': ['4'],
},
'SPOT3b': {
'satellite': 'SPOT-3',
'sensor': 'HRV2',
'LBA': ['1', '2', '3', '4'],
'pan': ['4'],
},
'SPOT4b': {
'satellite': 'SPOT-4',
'sensor': 'HRVIR2',
'LBA': ['1', '2', '3', '4', '5'],
'pan': ['5']
},
'SPOT5b': {
'satellite': 'SPOT-5',
'sensor': 'HRG2',
'LBA': ['1', '2', '3', '4', '5'],
'pan': ['5']
},
'RE1': {
'satellite': 'RapidEye-1',
'sensor': 'MSI',
'LBA': ['1', '2', '3', '4', '5'],
},
'RE2': {
'satellite': 'RapidEye-2',
'sensor': 'MSI',
'LBA': ['1', '2', '3', '4', '5'],
},
'RE3': {
'satellite': 'RapidEye-3',
'sensor': 'MSI',
'LBA': ['1', '2', '3', '4', '5'],
},
'RE4': {
'satellite': 'RapidEye-4',
'sensor': 'MSI',
'LBA': ['1', '2', '3', '4', '5'],
},
'RE5': {
'satellite': 'RapidEye-5',
'sensor': 'MSI',
'LBA': ['1', '2', '3', '4', '5'],
},
'S2A_full': {
'satellite': 'Sentinel-2A',
'sensor': 'MSI',
'LBA': ['1', '2', '3', '4', '5', '6', '7', '8', '8A', '9', '10', '11', '12'],
},
'S2B_full': {
'satellite': 'Sentinel-2B',
'sensor': 'MSI',
'LBA': ['1', '2', '3', '4', '5', '6', '7', '8', '8A', '9', '10', '11', '12'],
},
'S2A10': {
'satellite': 'Sentinel-2A',
'sensor': 'MSI',
'subsystem': 'S2A10',
'LBA': ['2', '3', '4', '8'],
},
'S2A20': {
'satellite': 'Sentinel-2A',
'sensor': 'MSI',
'subsystem': 'S2A20',
'LBA': ['5', '6', '7', '8A', '11', '12'],
},
'S2A60': {
'satellite': 'Sentinel-2A',
'sensor': 'MSI',
'subsystem': 'S2A60',
'LBA': ['1', '9', '10'],
},
'S2B10': {
'satellite': 'Sentinel-2B',
'sensor': 'MSI',
'subsystem': 'S2A10',
'LBA': ['2', '3', '4', '8'],
},
'S2B20': {
'satellite': 'Sentinel-2B',
'sensor': 'MSI',
'subsystem': 'S2A20',
'LBA': ['5', '6', '7', '8A', '11', '12'],
},
'S2B60': {
'satellite': 'Sentinel-2B',
'sensor': 'MSI',
'subsystem': 'S2A60',
'LBA': ['1', '9', '10'],
},
}
df_sensors = \
DataFrame\
.from_dict(sensors, orient='index')\
.loc[:, ['satellite', 'sensor', 'subsystem', 'LBA', 'LBA_sorted', 'pan', 'thermal']]
# class SensorSpecs(DataFrame):
# def __init__(self, satellite, sensor, subsystem='', **kwargs):
# super(SensorSpecs, self).__init__()
#
# self.satellite =
def get_sensorspecs(satellite, sensor, subsystem='', no_thermal=False, no_pan=False, sort_by_cwl=False):
if (satellite, sensor) in [('Landsat-4', 'TM'), ('Landsat-5', 'TM'), ('Landsat-7', 'ETM+')] and not subsystem:
subsystem = 'SAM'
if satellite.startswith('RapidEye'):
satellite = 'RapidEye-5'
sub = df_sensors[(df_sensors.satellite == satellite) &
(df_sensors.sensor == sensor) &
((df_sensors.subsystem == subsystem) if subsystem else df_sensors.subsystem.isnull())].copy()
if len(sub.index) == 0:
raise ValueError("No sensor specifications for combination '%s' '%s' '%s' found."
% (satellite, sensor, subsystem))
elif len(sub.index) > 1:
raise ValueError("Multiple sensor specifications found for combination '%s' '%s' '%s': \n %s"
% (satellite, sensor, subsystem, sub))
specs = sub.to_dict(orient='records')[0]
specs = dict(zip(specs.keys(), [v if v is not np.nan else None for v in specs.values()]))
return specs
def get_LayerBandsAssignment(satellite, sensor, subsystem='', no_thermal=False, no_pan=False, sort_by_cwl=False):
specs = get_sensorspecs(satellite, sensor, subsystem)
LBA = specs['LBA']
if sort_by_cwl and 'LBA_sorted' in specs:
LBA = specs['LBA_sorted']
if no_thermal and 'thermal' in specs:
LBA = [i for i in LBA if i not in specs['thermal']]
if no_pan and 'pan' in specs:
LBA = [i for i in LBA if i not in specs['pan']]
return LBA
......@@ -6,11 +6,12 @@
import unittest
from pyrsr.rsr import RelativeSpectralResponse, RelativeSpectralResponse
from pyrsr.rsr import RelativeSpectralResponse
from pyrsr.sensorspecs import get_LayerBandsAssignment
class TestPyrsr(unittest.TestCase):
"""Tests for `pyrsr` package."""
class TestRelativeSpectralResponse(unittest.TestCase):
"""Tests for `pyrsr.RelativeSpectralResponse` class."""
def test_Landsat8(self):
RelativeSpectralResponse(satellite='Landsat-8', sensor='OLI_TIRS')
......@@ -55,3 +56,24 @@ class TestPyrsr(unittest.TestCase):
def test_Aster(self):
RelativeSpectralResponse(satellite='Terra', sensor='ASTER')
class Test_get_LayerBandsAssignment(unittest.TestCase):
def test_fullLBA(self):
LBA = get_LayerBandsAssignment('Landsat-7', 'ETM+')
self.assertEqual(len(LBA), 9)
def test_nopan_nothermal(self):
LBA = get_LayerBandsAssignment('Landsat-7', 'ETM+', no_pan=True)
self.assertEqual(len(LBA), 8)
LBA = get_LayerBandsAssignment('Landsat-7', 'ETM+', no_thermal=True)
self.assertEqual(len(LBA), 7)
LBA = get_LayerBandsAssignment('Landsat-7', 'ETM+', no_pan=True, no_thermal=True)
self.assertEqual(len(LBA), 6)
def test_sort_by_cwl(self):
LBA = get_LayerBandsAssignment('Landsat-7', 'ETM+', no_thermal=True, sort_by_cwl=True)
self.assertEqual(len(LBA), 7)
self.assertEqual(LBA, ['1', '2', '3', '8', '4', '5', '7'])
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment