Completed
Push — develop ( 213254...f5a9f3 )
by Adam
11s
created

RelativeSpectralResponse.integral()   A

Complexity

Conditions 2

Size

Total Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
c 0
b 0
f 0
dl 0
loc 10
rs 9.4285
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.config 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
        """Create the instance either from platform name and instrument or from
50
        filename and load the data"""
51
        self.platform_name = platform_name
52
        self.instrument = instrument
53
        self.filename = None
54
        if not self.instrument or not self.platform_name:
55
            if 'filename' in kwargs:
56
                self.filename = kwargs['filename']
57
            else:
58
                raise IOError(
59
                    "platform name and sensor or filename must be specified")
60
        else:
61
            self._check_instrument()
62
63
        self.rsr = {}
64
        self.description = "Unknown"
65
        self.band_names = None
66
        self.unit = '1e-6 m'
67
        self.si_scale = 1e-6  # How to scale the wavelengths to become SI unit
68
        self._wavespace = WAVL
69
70
        options = get_config()
71
        self.rsr_dir = options['rsr_dir']
72
        self.do_download = False
73
74
        if 'download_from_internet' in options and options['download_from_internet']:
75
            self.do_download = True
76
77
        if not self.filename:
78
            self._get_filename()
79
80
        if not os.path.exists(self.filename) or not os.path.isfile(self.filename):
81
            errmsg = ('pyspectral RSR file does not exist! Filename = ' +
82
                      str(self.filename))
83
            if self.instrument and self.platform_name:
84
                fmatch = glob(
85
                    os.path.join(self.rsr_dir, '*{0}*{1}*.h5'.format(self.instrument,
86
                                                                     self.platform_name)))
87
                errmsg = (errmsg +
88
                          '\nFiles matching instrument and satellite platform' +
89
                          ': ' + str(fmatch))
90
91
            raise IOError(errmsg)
92
93
        LOG.debug('Filename: %s', str(self.filename))
94
        self.load()
95
96
    def _check_instrument(self):
97
        """Check and try fix instrument name if needed"""
98
        instr = INSTRUMENTS.get(self.platform_name, self.instrument)
99
        if instr != self.instrument:
100
            self.instrument = instr
101
            LOG.warning("Inconsistent instrument/satellite input - " +
102
                        "instrument set to %s", self.instrument)
103
104
        self.instrument = self.instrument.replace('/', '')
105
106
    def _get_filename(self):
107
        """Get the rsr filname from platform and instrument names, and download if not
108
           available.
109
110
        """
111
        self.filename = expanduser(
112
            os.path.join(self.rsr_dir, 'rsr_{0}_{1}.h5'.format(self.instrument,
113
                                                               self.platform_name)))
114
115
        LOG.debug('Filename: %s', str(self.filename))
116
        if not os.path.exists(self.filename) or not os.path.isfile(self.filename):
117
            # Try download from the internet!
118
            LOG.warning("No rsr file %s on disk", self.filename)
119
            if self.do_download:
120
                LOG.info("Will download from internet...")
121
                download_rsr()
122
123
    def load(self):
124
        """Read the internally formatet hdf5 relative spectral response data"""
125
        import h5py
126
127
        no_detectors_message = False
128
        with h5py.File(self.filename, 'r') as h5f:
129
            self.band_names = h5f.attrs['band_names'].tolist()
130
            self.description = h5f.attrs['description']
131
            if not self.platform_name:
132
                try:
133
                    self.platform_name = h5f.attrs['platform_name']
134
                except KeyError:
135
                    LOG.warning("No platform_name in HDF5 file")
136
                    try:
137
                        self.platform_name = h5f.attrs[
138
                            'platform'] + '-' + h5f.attrs['satnum']
139
                    except KeyError:
140
                        LOG.warning(
141
                            "Unable to determine platform name from HDF5 file content")
142
                        self.platform_name = None
143
144
            if not self.instrument:
145
                try:
146
                    self.instrument = h5f.attrs['sensor']
147
                except KeyError:
148
                    LOG.warning("No sensor name specified in HDF5 file")
149
                    self.instrument = INSTRUMENTS.get(self.platform_name)
150
151
            for bandname in self.band_names:
152
                self.rsr[bandname] = {}
153
                try:
154
                    num_of_det = h5f[bandname].attrs['number_of_detectors']
155
                except KeyError:
156
                    if not no_detectors_message:
157
                        LOG.debug("No detectors found - assume only one...")
158
                    num_of_det = 1
159
                    no_detectors_message = True
160
161
                for i in range(1, num_of_det + 1):
162
                    dname = 'det-{0:d}'.format(i)
163
                    self.rsr[bandname][dname] = {}
164
                    try:
165
                        resp = h5f[bandname][dname]['response'][:]
166
                    except KeyError:
167
                        resp = h5f[bandname]['response'][:]
168
169
                    self.rsr[bandname][dname]['response'] = resp
170
171
                    try:
172
                        wvl = (h5f[bandname][dname]['wavelength'][:] *
173
                               h5f[bandname][dname][
174
                                   'wavelength'].attrs['scale'])
175
                    except KeyError:
176
                        wvl = (h5f[bandname]['wavelength'][:] *
177
                               h5f[bandname]['wavelength'].attrs['scale'])
178
179
                    # The wavelength is given in micro meters!
180
                    self.rsr[bandname][dname]['wavelength'] = wvl * 1e6
181
182
                    try:
183
                        central_wvl = h5f[bandname][
184
                            dname].attrs['central_wavelength']
185
                    except KeyError:
186
                        central_wvl = h5f[bandname].attrs['central_wavelength']
187
188
                    self.rsr[bandname][dname][
189
                        'central_wavelength'] = central_wvl
190
191
    def integral(self, bandname):
192
        """Calculate the integral of the spectral response function for each
193
        detector.
194
        """
195
        intg = {}
196
        for det in self.rsr[bandname].keys():
197
            wvl = self.rsr[bandname][det]['wavelength']
198
            resp = self.rsr[bandname][det]['response']
199
            intg[det] = np.trapz(resp, wvl)
200
        return intg
201
202
    def convert(self):
203
        """Convert spectral response functions from wavelength to wavenumber"""
204
205
        from pyspectral.utils import convert2wavenumber
206
        if self._wavespace == WAVL:
207
            rsr, info = convert2wavenumber(self.rsr)
208
            for band in rsr.keys():
209
                for det in rsr[band].keys():
210
                    self.rsr[band][det]['wavenumber'] = rsr[
211
                        band][det]['wavenumber']
212
                    self.rsr[band][det]['response'] = rsr[
213
                        band][det]['response']
214
                    self.unit = info['unit']
215
                    self.si_scale = info['si_scale']
216
            self._wavespace = WAVN
217
        else:
218
            raise NotImplementedError("Conversion from wavenumber to " +
219
                                      "wavelength not supported yet")
220
221
222
def main():
223
    """Main"""
224
    modis = RelativeSpectralResponse('EOS-Terra', 'modis')
225
    del(modis)
226
227
if __name__ == "__main__":
228
    main()
229