import numpy as np
from pkg_resources import resource_filename, Requirement, DistributionNotFound
from os.path import dirname, join, isfile
import zipfile
import io
from pyrsr import RelativeSpectralResponse
[docs]
class __SensorSRF(object):
def __init__(self, srfs, srfs_wvl):
"""
Baseclass for instrument sicor srfs.
:param srfs: dictionary with [band_name]:[numpy array with spectral response function table for srfs_wvl]
:param srfs_wvl: spectral axis for all srf's in srfs
"""
self.srfs = srfs
self.srfs_wvl = srfs_wvl
self.bands = sorted(self.srfs.keys())
self.wvl = [int(np.trapz(x=self.srfs_wvl, y=self.srfs_wvl * self.srfs[band])) for band in self.bands]
self.conv = {}
self.conv.update({key: value for key, value in zip(self.bands, self.wvl)})
self.conv.update({value: key for key, value in zip(self.bands, self.wvl)})
[docs]
def instrument(self, bands):
instrument = {
'rspf': np.vstack([self[band] for band in bands]),
'wvl_rsp': np.copy(self.srfs_wvl),
'sol_irr': None
}
instrument['wvl_inst'] = np.trapz(x=instrument['wvl_rsp'], y=instrument['rspf'][:, :] * instrument['wvl_rsp'])
return instrument
def __call__(self, band):
return self.srfs[band]
def __getitem__(self, band):
return self.srfs[band]
[docs]
class SensorSRF(__SensorSRF):
def __init__(self, sensor="S2A"):
"""
Instrument spectral response functions for all supported sensors.
:param sensor: String with support instrument identifier, possible is: 'S2A','Landsat-8'
:returns: Sicor __SensorSRF object
"""
if sensor in ["S2A", "S2B"]:
srfs, srfs_wvl = self.sentinel2(sensor=sensor)
elif "Landsat-" in sensor:
srfs, srfs_wvl = self.landsat(sensor=sensor)
else:
raise ValueError("SRF's for sensor %s is unknown." % sensor)
super().__init__(srfs=srfs, srfs_wvl=srfs_wvl)
self.fn_srf = ""
[docs]
def landsat(self, sensor):
"""
Read Landsat srf's from zipfile which includes srf's for multiple instruments
:param sensor: "Landsat-X", X=8,...,
:return: srfs, wvl for __SensorSRF.__init__
"""
from scipy.interpolate import interp1d # import here to avoid static TLS ImportError
fn_srf = "landsat/data/SRF_Landsat.zip"
try:
fn = resource_filename(Requirement.parse("sicor"), fn_srf)
except DistributionNotFound:
fn = join(dirname(__file__), fn_srf)
if isfile(fn) is False:
raise FileNotFoundError(fn_srf)
else:
if isfile(fn) is False:
fn = join(dirname(__file__), fn_srf)
if isfile(fn) is False:
raise FileNotFoundError(fn_srf)
self.fn_srf = fn
srfs = {}
wvl = np.arange(400, 2500, 1)
with zipfile.ZipFile(self.fn_srf) as zipper:
for fl in [fl for fl in zipper.namelist() if sensor in fl and "band" in fl]:
with io.BufferedReader(zipper.open(fl, mode='r')) as f:
bf = np.loadtxt(f, skiprows=1)
srfs[fl.split("band_")[-1]] = interp1d(
x=bf[:, 0] * 1000, y=bf[:, 1], fill_value=0.0, bounds_error=False)(wvl)
if len(srfs) == 0:
raise ValueError("No data found for %s, update: %s" % (sensor, self.fn_srf))
return srfs, wvl
[docs]
def sentinel2(self, sensor="S2A"):
"""
Load Sentinel-2A spectral response function.
:param sensor: name of Sentinel sensor (2A or 2B)
:return: srfs, wvl for __SensorSRF.__init__
"""
if sensor == "S2A":
satellite = "Sentinel-2A"
else:
satellite = "Sentinel-2B"
RSR = RelativeSpectralResponse(satellite=satellite, sensor='MSI')
wvl = RSR.rsrs_wvl
band_map = {'1': 'B01',
'2': 'B02',
'3': 'B03',
'4': 'B04',
'5': 'B05',
'6': 'B06',
'7': 'B07',
'8': 'B08',
'8A': 'B8A',
'9': 'B09',
'10': 'B10',
'11': 'B11',
'12': 'B12'}
srfs = {}
for key in band_map.keys():
srfs[band_map[key]] = RSR.rsrs[key]
return srfs, wvl