hawc_hal.flat_sky_projection   A
last analyzed

Complexity

Total Complexity 14

Size/Duplication

Total Lines 202
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 72
dl 0
loc 202
rs 10
c 0
b 0
f 0
wmc 14

2 Functions

Rating   Name   Duplication   Size   Complexity  
A _get_all_ra_dec() 0 14 1
A _get_header() 0 11 1

10 Methods

Rating   Name   Duplication   Size   Complexity  
A FlatSkyProjection.ras() 0 6 1
A FlatSkyProjection.npix_width() 0 6 1
A FlatSkyProjection.wcs() 0 6 1
A FlatSkyProjection.dec_center() 0 6 1
A FlatSkyProjection.npix_height() 0 6 1
A FlatSkyProjection.ra_center() 0 6 1
A FlatSkyProjection.pixel_size() 0 6 1
A FlatSkyProjection.decs() 0 6 1
A FlatSkyProjection.project_plane_pixel_area() 0 6 1
A FlatSkyProjection.__init__() 0 50 3
1
from astropy.io import fits
0 ignored issues
show
Coding Style introduced by
This module should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
2
from astropy.wcs import WCS
3
from astropy.wcs.utils import proj_plane_pixel_area
4
import numpy as np
5
6
from util import cartesian
0 ignored issues
show
Coding Style introduced by
Relative import 'util', should be 'hawc_hal.util'
Loading history...
7
from hawc_hal.sphere_dist import sphere_dist
0 ignored issues
show
Unused Code introduced by
Unused sphere_dist imported from hawc_hal.sphere_dist
Loading history...
8
9
10
_fits_header = """
0 ignored issues
show
Coding Style Naming introduced by
Constant name "_fits_header" doesn't conform to UPPER_CASE naming style ('(([A-Z_][A-Z0-9_]*)|(__.*__))$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
11
NAXIS   =                    2
12
NAXIS1  =                   %i
13
NAXIS2  =                   %i
14
CTYPE1  = 'RA---AIT'
15
CRPIX1  =                   %i
16
CRVAL1  =                   %s
17
CDELT1  =                  -%f
18
CUNIT1  = 'deg     '
19
CTYPE2  = 'DEC--AIT'
20
CRPIX2  =                   %i
21
CRVAL2  =                   %s
22
CDELT2  =                   %f
23
CUNIT2  = 'deg     '
24
COORDSYS= '%s'
25
"""
26
27
28
def _get_header(ra, dec, pixel_size, coordsys, h, w):
0 ignored issues
show
best-practice introduced by
Too many arguments (6/5)
Loading history...
Coding Style Naming introduced by
Argument name "ra" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
Argument name "h" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
Argument name "w" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
29
30
    assert 0 <= ra <= 360
31
32
    header = fits.Header.fromstring(_fits_header % (h, w,
33
                                                    h / 2, ra, pixel_size,
34
                                                    w / 2, dec, pixel_size,
35
                                                    coordsys),
36
                                    sep='\n')
37
38
    return header
39
40
41
def _get_all_ra_dec(input_wcs, h, w):
0 ignored issues
show
Coding Style Naming introduced by
Argument name "h" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
Coding Style Naming introduced by
Argument name "w" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
42
43
    # An array of all the possible permutation of (i,j) for i=0..999 and j=0..999
44
45
    xx = np.arange(0.5, h + 0.5, 1, dtype=np.int16)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "xx" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
46
    yy = np.arange(0.5, w + 0.5, 1, dtype=np.int16)
0 ignored issues
show
Coding Style Naming introduced by
Variable name "yy" doesn't conform to snake_case naming style ('(([a-z_][a-z0-9_]2,30)|(_[a-z0-9_]*)|(__[a-z][a-z0-9_]+__))$' pattern)

This check looks for invalid names for a range of different identifiers.

You can set regular expressions to which the identifiers must conform if the defaults do not match your requirements.

If your project includes a Pylint configuration file, the settings contained in that file take precedence.

To find out more about Pylint, please refer to their site.

Loading history...
47
48
    _ij_grid = cartesian((xx,
49
                          yy))
50
51
    # Convert pixel coordinates to world coordinates
52
    world = input_wcs.all_pix2world(_ij_grid, 0, ra_dec_order=True)
53
54
    return world[:, 0], world[:, 1]
55
56
57
class FlatSkyProjection(object):
0 ignored issues
show
Coding Style introduced by
This class should have a docstring.

The coding style of this project requires that you add a docstring to this code element. Below, you find an example for methods:

class SomeClass:
    def some_method(self):
        """Do x and return foo."""

If you would like to know more about docstrings, we recommend to read PEP-257: Docstring Conventions.

Loading history...
best-practice introduced by
Too many instance attributes (10/7)
Loading history...
58
59
    def __init__(self, ra_center, dec_center, pixel_size_deg, npix_height, npix_width):
0 ignored issues
show
best-practice introduced by
Too many arguments (6/5)
Loading history...
60
61
        assert npix_height % 2 == 0, "Number of height pixels must be even"
62
        assert npix_width % 2 == 0, "Number of width pixels must be even"
63
64
        if isinstance(npix_height, float):
65
66
            assert npix_height.is_integer(), "This is a bug"
67
68
        if isinstance(npix_width, float):
69
70
            assert npix_width.is_integer(), "This is a bug"
71
72
        self._npix_height = int(npix_height)
73
        self._npix_width = int(npix_width)
74
75
        assert 0 <= ra_center <= 360.0, "Right Ascension must be between 0 and 360"
76
        assert -90.0 <= dec_center <= 90.0, "Declination must be between -90.0 and 90.0"
77
78
        self._ra_center = float(ra_center)
79
        self._dec_center = float(dec_center)
80
81
        self._pixel_size_deg = float(pixel_size_deg)
82
83
        # Build projection, i.e., a World Coordinate System object
84
85
        self._wcs = WCS(_get_header(ra_center, dec_center, pixel_size_deg, 'icrs', npix_height, npix_width))
86
87
        # Pre-compute all R.A., Decs
88
        self._ras, self._decs = _get_all_ra_dec(self._wcs, npix_height, npix_width)
89
90
        # Make sure we have the right amount of coordinates
91
        assert self._ras.shape[0] == self._decs.shape[0]
92
        assert self._ras.shape[0] == npix_width * npix_height
93
94
        # Pre-compute pixel area
95
        self._pixel_area = proj_plane_pixel_area(self._wcs)
96
97
        # Pre-compute an oversampled version to be used for PSF integration
98
        # if oversample and pixel_size_deg > 0.025:
99
        #
100
        #     self._oversampled, self._oversample_factor = self._oversample(new_pixel_size=0.025)
101
        #
102
        # else:
103
        #
104
        #     self._oversampled = self
105
        #     self._oversample_factor = 1
106
107
        # Cache for angular distances from a point (see get_spherical_distances_from)
108
        self._distance_cache = {}
109
110
    # def _oversample(self, new_pixel_size):
111
    #     """Return a new instance oversampled by the provided factor"""
112
    #
113
    #     # Compute the oversampling factor (as a float because we need it for the division down)
114
    #     factor = float(np.ceil(self._pixel_size_deg / new_pixel_size))
115
    #
116
    #     if factor <= 1:
117
    #
118
    #         # The projection is already with a smaller pixel size than the oversampled version
119
    #         # No need to oversample
120
    #         return self, 1
121
    #
122
    #     else:
123
    #
124
    #         new_fp = FlatSkyProjection(self._ra_center, self._dec_center,
125
    #                                    self._pixel_size_deg / factor,
126
    #                                    self._npix_height * factor,
127
    #                                    self._npix_width * factor,
128
    #                                    oversample=False)
129
    #
130
    #         return new_fp, int(factor)
131
132
    # @property
133
    # def oversampled(self):
134
    #     return self._oversampled
135
    #
136
    # @property
137
    # def oversample_factor(self):
138
    #     return self._oversample_factor
139
140
    @property
141
    def ras(self):
142
        """
143
        :return: Right Ascension for all pixels
144
        """
145
        return self._ras
146
147
    @property
148
    def decs(self):
149
        """
150
        :return: Declination for all pixels
151
        """
152
        return self._decs
153
154
    @property
155
    def ra_center(self):
156
        """
157
        :return: R.A. for the center of the projection
158
        """
159
        return self._ra_center
160
161
    @property
162
    def dec_center(self):
163
        """
164
        :return: Declination for the center of the projection
165
        """
166
        return self._dec_center
167
168
    @property
169
    def pixel_size(self):
170
        """
171
        :return: size (in deg) of the pixel
172
        """
173
        return self._pixel_size_deg
174
175
    @property
176
    def wcs(self):
177
        """
178
        :return: World Coordinate System instance describing the projection
179
        """
180
        return self._wcs
181
182
    @property
183
    def npix_height(self):
184
        """
185
        :return: height of the projection in pixels
186
        """
187
        return self._npix_height
188
189
    @property
190
    def npix_width(self):
191
        """
192
        :return: width of the projection in pixels
193
        """
194
        return self._npix_width
195
196
    @property
197
    def project_plane_pixel_area(self):
198
        """
199
        :return: area of the pixels (remember, this is an equal-area projection so all pixels are equal)
200
        """
201
        return self._pixel_area
202
203
    # def get_spherical_distances_from(self, ra, dec, cutout_radius):
204
    #     """
205
    #     Returns the distances for all points in this grid from the given point
206
    #
207
    #     :param ra:
208
    #     :param dec:
209
    #     :param cutout_radius: do not consider elements beyond this radius (NOTE: we use a planar approximation on
210
    #     purpose, to make things fast, so the cut is not precise)
211
    #     :return: (angular distances of selected points from (ra, dec), selection indexes)
212
    #     """
213
    #
214
    #     # This is typically used sequentially on different energy bins, so we cache the result and re-use it
215
    #     # if we already computed it
216
    #
217
    #     key = (ra, dec, cutout_radius, self.ras.shape[0], self.decs.shape[0])
218
    #
219
    #     if key not in self._distance_cache:
220
    #
221
    #         # In order to gain speed, we use a planar approximation (instead of using the harversine formula we assume
222
    #         # plane geometry). This gets more and more unprecise the large the cutout radius, but we do not care here
223
    #
224
    #         selection_idx = (((ra - self.ras)**2 + (dec - self.decs)**2) <= (1.2*cutout_radius)**2)  # type: np.ndarray
0 ignored issues
show
Coding Style introduced by
This line is too long as per the coding-style (121/120).

This check looks for lines that are too long. You can specify the maximum line length.

Loading history...
225
    #
226
    #         ds = sphere_dist(ra, dec, self.ras[selection_idx], self.decs[selection_idx])
227
    #
228
    #         # Refine selection by putting to False all elements in the mask at a distance larger than the cutout
229
    #         # radius
230
    #         fine_selection_idx = (ds <= cutout_radius)
231
    #         selection_idx[selection_idx.nonzero()[0][~fine_selection_idx]] = False
232
    #
233
    #         # This is to make sure we only keep cached the last result, and the dictionary does not grow indefinitely
234
    #         self._distance_cache = {}
235
    #
236
    #         self._distance_cache[key] = (ds[fine_selection_idx], selection_idx)
237
    #
238
    #     return self._distance_cache[key]
239
0 ignored issues
show
coding-style introduced by
Trailing newlines
Loading history...
240