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

RelativeSpectralResponse.load()   F

Complexity

Conditions 12

Size

Total Lines 56

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 12
dl 0
loc 56
rs 3.3458
c 2
b 0
f 0

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Complexity

Complex classes like RelativeSpectralResponse.load() often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

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
        """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
                self.platform_name = h5f.attrs['platform_name']
133
            if not self.instrument:
134
                try:
135
                    self.instrument = h5f.attrs['sensor']
136
                except KeyError:
137
                    LOG.warning("No sensor name specified in HDF5 file")
138
                    self.instrument = INSTRUMENTS.get(self.platform_name)
139
140
            for bandname in self.band_names:
141
                self.rsr[bandname] = {}
142
                try:
143
                    num_of_det = h5f[bandname].attrs['number_of_detectors']
144
                except KeyError:
145
                    if not no_detectors_message:
146
                        LOG.debug("No detectors found - assume only one...")
147
                    num_of_det = 1
148
                    no_detectors_message = True
149
150
                for i in range(1, num_of_det + 1):
151
                    dname = 'det-{0:d}'.format(i)
152
                    self.rsr[bandname][dname] = {}
153
                    try:
154
                        resp = h5f[bandname][dname]['response'][:]
155
                    except KeyError:
156
                        resp = h5f[bandname]['response'][:]
157
158
                    self.rsr[bandname][dname]['response'] = resp
159
160
                    try:
161
                        wvl = (h5f[bandname][dname]['wavelength'][:] *
162
                               h5f[bandname][dname][
163
                                   'wavelength'].attrs['scale'])
164
                    except KeyError:
165
                        wvl = (h5f[bandname]['wavelength'][:] *
166
                               h5f[bandname]['wavelength'].attrs['scale'])
167
168
                    # The wavelength is given in micro meters!
169
                    self.rsr[bandname][dname]['wavelength'] = wvl * 1e6
170
171
                    try:
172
                        central_wvl = h5f[bandname][
173
                            dname].attrs['central_wavelength']
174
                    except KeyError:
175
                        central_wvl = h5f[bandname].attrs['central_wavelength']
176
177
                    self.rsr[bandname][dname][
178
                        'central_wavelength'] = central_wvl
179
180
    def integral(self, bandname):
181
        """Calculate the integral of the spectral response function for each
182
        detector.
183
        """
184
        intg = {}
185
        for det in self.rsr[bandname].keys():
186
            wvl = self.rsr[bandname][det]['wavelength']
187
            resp = self.rsr[bandname][det]['response']
188
            intg[det] = np.trapz(resp, wvl)
189
        return intg
190
191
    def convert(self):
192
        """Convert spectral response functions from wavelength to wavenumber"""
193
194
        from pyspectral.utils import convert2wavenumber
195
        if self._wavespace == WAVL:
196
            rsr, info = convert2wavenumber(self.rsr)
197
            for band in rsr.keys():
198
                for det in rsr[band].keys():
199
                    self.rsr[band][det]['wavenumber'] = rsr[
200
                        band][det]['wavenumber']
201
                    self.rsr[band][det]['response'] = rsr[
202
                        band][det]['response']
203
                    self.unit = info['unit']
204
                    self.si_scale = info['si_scale']
205
            self._wavespace = WAVN
206
        else:
207
            raise NotImplementedError("Conversion from wavenumber to " +
208
                                      "wavelength not supported yet")
209
210
211
def main():
212
    """Main"""
213
    modis = RelativeSpectralResponse('EOS-Terra', 'modis')
214
    del(modis)
215
216
if __name__ == "__main__":
217
    main()
218