Completed
Pull Request — develop (#12)
by Adam
30s
created

RelativeSpectralResponse.convert()   A

Complexity

Conditions 4

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
c 0
b 0
f 0
dl 0
loc 18
rs 9.2
1
#!/usr/bin/env python
2
# -*- coding: utf-8 -*-
3
4
# Copyright (c) 2014-2017 Adam.Dybbroe
5
6
# Author(s):
7
8
#   Adam.Dybbroe <[email protected]>
9
#   Panu Lahtinen <[email protected]>
10
11
# This program is free software: you can redistribute it and/or modify
12
# it under the terms of the GNU General Public License as published by
13
# the Free Software Foundation, either version 3 of the License, or
14
# (at your option) any later version.
15
16
# This program is distributed in the hope that it will be useful,
17
# but WITHOUT ANY WARRANTY; without even the implied warranty of
18
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19
# GNU General Public License for more details.
20
21
# You should have received a copy of the GNU General Public License
22
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
23
24
"""Reading the spectral responses in the internal pyspectral hdf5 format"""
25
26
import os
27
import numpy as np
28
from glob import glob
29
from os.path import expanduser
30
31
import logging
32
LOG = logging.getLogger(__name__)
33
34
from pyspectral import get_config
35
36
WAVL = 'wavelength'
37
WAVN = 'wavenumber'
38
39
from pyspectral.utils import (INSTRUMENTS, download_rsr)
40
41
42
class RelativeSpectralResponse(object):
43
44
    """Container for the relative spectral response functions for various
45
    satellite imagers
46
    """
47
48
    def __init__(self, platform_name=None, instrument=None, **kwargs):
49
        self.platform_name = platform_name
50
        self.instrument = instrument
51
        self.filename = None
52
        if not self.instrument or not self.platform_name:
53
            if 'filename' in kwargs:
54
                self.filename = kwargs['filename']
55
            else:
56
                raise IOError(
57
                    "platform name and sensor or filename must be specified")
58
        else:
59
            self._check_instrument()
60
61
        self.rsr = {}
62
        self.description = "Unknown"
63
        self.band_names = None
64
        self.unit = '1e-6 m'
65
        self.si_scale = 1e-6  # How to scale the wavelengths to become SI unit
66
        self._wavespace = WAVL
67
68
        options = get_config()
69
        self.rsr_dir = options['rsr_dir']
70
        if 'download_from_internet' in options and options['download_from_internet'] == 'True':
71
            self.do_download = True
72
        else:
73
            self.do_download = False
74
75
        if not self.filename:
76
            self._get_filename()
77
78
        if not os.path.exists(self.filename) or not os.path.isfile(self.filename):
79
            errmsg = ('pyspectral RSR file does not exist! Filename = ' +
80
                      str(self.filename))
81
            if self.instrument and self.platform_name:
82
                fmatch = glob(
83
                    os.path.join(self.rsr_dir, '*{0}*{1}*.h5'.format(self.instrument,
84
                                                                     self.platform_name)))
85
                errmsg = (errmsg +
86
                          '\nFiles matching instrument and satellite platform' +
87
                          ': ' + str(fmatch))
88
89
            raise IOError(errmsg)
90
91
        LOG.debug('Filename: %s', str(self.filename))
92
        self.load()
93
94
    def _check_instrument(self):
95
        """Check and try fix instrument name if needed"""
96
        instr = INSTRUMENTS.get(self.platform_name, self.instrument)
97
        if instr != self.instrument:
98
            self.instrument = instr
99
            LOG.warning("Inconsistent instrument/satellite input - " +
100
                        "instrument set to %s", self.instrument)
101
102
        self.instrument = self.instrument.replace('/', '')
103
104
    def _get_filename(self):
105
        """Get the rsr filname from platform and instrument names, and download if not
106
           available.
107
108
        """
109
        self.filename = expanduser(
110
            os.path.join(self.rsr_dir, 'rsr_{0}_{1}.h5'.format(self.instrument,
111
                                                               self.platform_name)))
112
113
        LOG.debug('Filename: %s', str(self.filename))
114
        if not os.path.exists(self.filename) or not os.path.isfile(self.filename):
115
            # Try download from the internet!
116
            LOG.warning("No rsr file %s on disk", self.filename)
117
            if self.do_download:
118
                LOG.info("Will download from internet...")
119
                download_rsr()
120
121
    def load(self):
122
        """Read the internally formatet hdf5 relative spectral response data"""
123
        import h5py
124
125
        no_detectors_message = False
126
        with h5py.File(self.filename, 'r') as h5f:
127
            self.band_names = h5f.attrs['band_names'].tolist()
128
            self.description = h5f.attrs['description']
129
            if not self.platform_name:
130
                self.platform_name = h5f.attrs['platform_name']
131
            if not self.instrument:
132
                try:
133
                    self.instrument = h5f.attrs['sensor']
134
                except KeyError:
135
                    LOG.warning("No sensor name specified in HDF5 file")
136
                    self.instrument = INSTRUMENTS.get(self.platform_name)
137
138
            for bandname in self.band_names:
139
                self.rsr[bandname] = {}
140
                try:
141
                    num_of_det = h5f[bandname].attrs['number_of_detectors']
142
                except KeyError:
143
                    if not no_detectors_message:
144
                        LOG.debug("No detectors found - assume only one...")
145
                    num_of_det = 1
146
                    no_detectors_message = True
147
148
                for i in range(1, num_of_det + 1):
149
                    dname = 'det-{0:d}'.format(i)
150
                    self.rsr[bandname][dname] = {}
151
                    try:
152
                        resp = h5f[bandname][dname]['response'][:]
153
                    except KeyError:
154
                        resp = h5f[bandname]['response'][:]
155
156
                    self.rsr[bandname][dname]['response'] = resp
157
158
                    try:
159
                        wvl = (h5f[bandname][dname]['wavelength'][:] *
160
                               h5f[bandname][dname][
161
                                   'wavelength'].attrs['scale'])
162
                    except KeyError:
163
                        wvl = (h5f[bandname]['wavelength'][:] *
164
                               h5f[bandname]['wavelength'].attrs['scale'])
165
166
                    # The wavelength is given in micro meters!
167
                    self.rsr[bandname][dname]['wavelength'] = wvl * 1e6
168
169
                    try:
170
                        central_wvl = h5f[bandname][
171
                            dname].attrs['central_wavelength']
172
                    except KeyError:
173
                        central_wvl = h5f[bandname].attrs['central_wavelength']
174
175
                    self.rsr[bandname][dname][
176
                        'central_wavelength'] = central_wvl
177
178
    def integral(self, bandname):
179
        """Calculate the integral of the spectral response function for each
180
        detector.
181
        """
182
        intg = {}
183
        for det in self.rsr[bandname].keys():
184
            wvl = self.rsr[bandname][det]['wavelength']
185
            resp = self.rsr[bandname][det]['response']
186
            intg[det] = np.trapz(resp, wvl)
187
        return intg
188
189
    def convert(self):
190
        """Convert spectral response functions from wavelength to wavenumber"""
191
192
        from pyspectral.utils import convert2wavenumber
193
        if self._wavespace == WAVL:
194
            rsr, info = convert2wavenumber(self.rsr)
195
            for band in rsr.keys():
196
                for det in rsr[band].keys():
197
                    self.rsr[band][det]['wavenumber'] = rsr[
198
                        band][det]['wavenumber']
199
                    self.rsr[band][det]['response'] = rsr[
200
                        band][det]['response']
201
                    self.unit = info['unit']
202
                    self.si_scale = info['si_scale']
203
            self._wavespace = WAVN
204
        else:
205
            raise NotImplementedError("Conversion from wavenumber to " +
206
                                      "wavelength not supported yet")
207
208
209
def main():
210
    """Main"""
211
    modis = RelativeSpectralResponse('EOS-Terra', 'modis')
212
    del(modis)
213
214
if __name__ == "__main__":
215
    main()
216