rms-starcat 0.0.1__py3-none-any.whl → 1.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rms-starcat might be problematic. Click here for more details.

starcat/ucac4.py CHANGED
@@ -2,138 +2,237 @@
2
2
  # starcat/ucac4.py
3
3
  ################################################################################
4
4
 
5
- # Zacharias, N. et al. 2013, The Astronomical Journal, 145, 44
5
+ # Zacharias, N. et al. 2013, The Astronomical Journal, 145, 44
6
6
 
7
- from __future__ import print_function
7
+ from __future__ import annotations
8
8
 
9
- try:
10
- from starcatalog import *
11
- except ImportError:
12
- from .starcatalog import *
13
- import numpy as np
14
- import os.path
9
+ import os
10
+ from pathlib import Path
15
11
  import struct
12
+ from typing import Any, Iterator, Optional, cast
13
+
14
+ from filecache import FCPath
15
+ import numpy as np
16
+
17
+ from .starcatalog import (HALFPI,
18
+ MAS_TO_RAD,
19
+ YEAR_TO_SEC,
20
+ Star,
21
+ StarCatalog
22
+ )
16
23
 
17
- UCAC4_OBJ_TYPE_CLEAN = 0
18
- UCAC4_OBJ_TYPE_NEAR_OVEREXPOSED = 1
19
- UCAC4_OBJ_TYPE_STREAK = 2
20
- UCAC4_OBJ_TYPE_HPM = 3
21
- UCAC4_OBJ_TYPE_EXT_HPM = 4
22
- UCAC4_OBJ_TYPE_POOR_PM = 5
23
- UCAC4_OBJ_TYPE_SUBST_ASTROMETRY = 6
24
- UCAC4_OBJ_TYPE_SUPPL = 7
25
- UCAC4_OBJ_TYPE_HPM_NOT_MATCHED = 8
26
- UCAC4_OBJ_TYPE_HPM_DISCREPANT = 9
27
-
28
- UCAC4_OBJ_TYPE_STRINGS = ['CLEAN', 'NEAR_OVEREXPOSED', 'STREAK', 'HPM',
29
- 'EXT_HPM', 'POOR_PM', 'SUBST_ASTROMETRY', 'SUPPL',
30
- 'HPM_NOT_MATCHED', 'HPM_DISCREPANT']
31
-
32
- #CATMATCH_TYCHO = 0
33
- #CATMATCH_AC2000 = 1
34
- #CATMATCH_AGK2_BONN = 2
35
- #CATMATCH_AGK2_HAMBURG = 3
36
- #CATMATCH_ZONE_ASTROGRAPH = 4
37
- #CATMATCH_BLACK_BIRCH = 5
38
- #CATMATCH_LICK_ASTROGRAPH = 6
39
- #CATMATCH_NPM_LICK = 7
40
- #CATMATCH_SPM_YSJ1 = 8
41
-
42
- UCAC4_DOUBLE_STAR_FLAG_SINGLE = 0
43
- UCAC4_DOUBLE_STAR_FLAG_COMP1 = 1
44
- UCAC4_DOUBLE_STAR_FLAG_COMP2 = 2
45
- UCAC4_DOUBLE_STAR_FLAG_BLENDED = 3
46
-
47
- UCAC4_DOUBLE_STAR_FLAG_STRINGS = ['SINGLE', 'COMP1', 'COMP2', 'BLENDED']
48
-
49
- UCAC4_DOUBLE_STAR_TYPE_NONE = 0
50
- UCAC4_DOUBLE_STAR_TYPE_1PEAK = 1
51
- UCAC4_DOUBLE_STAR_TYPE_2PEAK = 2
52
- UCAC4_DOUBLE_STAR_TYPE_SECONDARY_PEAK = 3
53
- UCAC4_DOUBLE_STAR_TYPE_1PEAK_FIT = 4
54
- UCAC4_DOUBLE_STAR_TYPE_2PEAK_FIT = 5
55
- UCAC4_DOUBLE_STAR_TYPE_SECONDARY_PEAK_FIT = 6
56
-
57
- UCAC4_DOUBLE_STAR_TYPE_STRINGS = ['NONE', '1PEAK', '2PEAK', 'SECONDARY_PEAK',
58
- '1PEAK_FIT', '2PEAK_FIT',
59
- 'SECONDARY_PEAK_FIT']
60
24
 
61
25
  class UCAC4Star(Star):
62
26
  """A holder for star attributes.
63
27
 
64
28
  This class includes attributes unique to the UCAC4 catalog."""
65
29
 
66
- def __init__(self):
30
+ UCAC4_OBJ_TYPE_CLEAN = 0
31
+ UCAC4_OBJ_TYPE_NEAR_OVEREXPOSED = 1
32
+ UCAC4_OBJ_TYPE_STREAK = 2
33
+ UCAC4_OBJ_TYPE_HPM = 3
34
+ UCAC4_OBJ_TYPE_EXT_HPM = 4
35
+ UCAC4_OBJ_TYPE_POOR_PM = 5
36
+ UCAC4_OBJ_TYPE_SUBST_ASTROMETRY = 6
37
+ UCAC4_OBJ_TYPE_SUPPL = 7
38
+ UCAC4_OBJ_TYPE_HPM_NOT_MATCHED = 8
39
+ UCAC4_OBJ_TYPE_HPM_DISCREPANT = 9
40
+
41
+ UCAC4_OBJ_TYPE_STRINGS = ['CLEAN', 'NEAR_OVEREXPOSED', 'STREAK', 'HPM',
42
+ 'EXT_HPM', 'POOR_PM', 'SUBST_ASTROMETRY', 'SUPPL',
43
+ 'HPM_NOT_MATCHED', 'HPM_DISCREPANT']
44
+
45
+ #CATMATCH_TYCHO = 0
46
+ #CATMATCH_AC2000 = 1
47
+ #CATMATCH_AGK2_BONN = 2
48
+ #CATMATCH_AGK2_HAMBURG = 3
49
+ #CATMATCH_ZONE_ASTROGRAPH = 4
50
+ #CATMATCH_BLACK_BIRCH = 5
51
+ #CATMATCH_LICK_ASTROGRAPH = 6
52
+ #CATMATCH_NPM_LICK = 7
53
+ #CATMATCH_SPM_YSJ1 = 8
54
+
55
+ UCAC4_DOUBLE_STAR_FLAG_SINGLE = 0
56
+ UCAC4_DOUBLE_STAR_FLAG_COMP1 = 1
57
+ UCAC4_DOUBLE_STAR_FLAG_COMP2 = 2
58
+ UCAC4_DOUBLE_STAR_FLAG_BLENDED = 3
59
+
60
+ UCAC4_DOUBLE_STAR_FLAG_STRINGS = ['SINGLE', 'COMP1', 'COMP2', 'BLENDED']
61
+
62
+ UCAC4_DOUBLE_STAR_TYPE_NONE = 0
63
+ UCAC4_DOUBLE_STAR_TYPE_1PEAK = 1
64
+ UCAC4_DOUBLE_STAR_TYPE_2PEAK = 2
65
+ UCAC4_DOUBLE_STAR_TYPE_SECONDARY_PEAK = 3
66
+ UCAC4_DOUBLE_STAR_TYPE_1PEAK_FIT = 4
67
+ UCAC4_DOUBLE_STAR_TYPE_2PEAK_FIT = 5
68
+ UCAC4_DOUBLE_STAR_TYPE_SECONDARY_PEAK_FIT = 6
69
+
70
+ UCAC4_DOUBLE_STAR_TYPE_STRINGS = ['NONE', '1PEAK', '2PEAK', 'SECONDARY_PEAK',
71
+ '1PEAK_FIT', '2PEAK_FIT',
72
+ 'SECONDARY_PEAK_FIT']
73
+
74
+ def __init__(self) -> None:
67
75
  # Initialize the standard fields
68
- Star.__init__(self)
76
+ super().__init__()
69
77
 
70
78
  # Initialize the UCAC4-specific fields
71
- self.vmag_model = None
72
- self.obj_type = None
73
- self.double_star_flag = None
74
- self.double_star_type = None
75
- self.galaxy_match = None
76
- self.extended_source = None
77
- self.pm_rac = None
78
- self.pm_rac_sigma = None
79
- self.rac_sigma = None
80
- self.num_img_total = None
81
- self.num_img_used = None
82
- self.num_cat_pm = None
83
- self.ra_mean_epoch = None
84
- self.dec_mean_epoch = None
85
- self.cat_match = None
86
- self.apass_mag_b = None
87
- self.apass_mag_v = None
88
- self.apass_mag_g = None
89
- self.apass_mag_r = None
90
- self.apass_mag_i = None
91
- self.apass_mag_b_sigma = None
92
- self.apass_mag_v_sigma = None
93
- self.apass_mag_g_sigma = None
94
- self.apass_mag_r_sigma = None
95
- self.apass_mag_i_sigma = None
96
- self.johnson_mag_b = None
97
- self.johnson_mag_v = None
98
- self.id_str = None
99
- self.id_str_ucac2 = None
100
- self.spectral_class = None
101
- self.temperature = None
102
-
103
- def __str__(self):
79
+ self.vmag_model: Optional[float] = None
80
+ """Fit model magnitude"""
81
+
82
+ self.obj_type: Optional[int] = None
83
+ """The object type used to identify possible problems with a star or
84
+ the source of data:
85
+ 0 = good, clean star (from MPOS), no known problem;
86
+ 1 = largest flag of any image = near overexposed star (from MPOS);
87
+ 2 = largest flag of any image = possible streak object (from MPOS);
88
+ 3 = high proper motion (HPM) star, match with external PM file (MPOS);
89
+ 4 = actually use external HPM data instead of UCAC4 observ.data
90
+ (accuracy of positions varies between catalogs);
91
+ 5 = poor proper motion solution, report only CCD epoch position;
92
+ 6 = substitute poor astrometric results by FK6/Hip/Tycho-2 data;
93
+ 7 = added supplement star (no CCD data) from FK6/Hip/Tycho-2 data,
94
+ and 2 stars added from high proper motion surveys;
95
+ 8 = high proper motion solution in UCAC4, star not matched with PPMXL;
96
+ 9 = high proper motion solution in UCAC4, discrepant PM to PPMXL
97
+ (see discussion of flags 8,9 in redcution section 2e above)
98
+ """
99
+
100
+ self.double_star_flag: Optional[bool] = None
101
+ """Double star flag overall classification:
102
+ 0 = single star;
103
+ 1 = component #1 of "good" double star;
104
+ 2 = component #2 of "good" double star;
105
+ 3 = blended image
106
+ """
107
+
108
+ self.double_star_type: Optional[int] = None
109
+ """Double star type:
110
+ 0 = no double star, not sufficient #pixels or elongation
111
+ to even call double star fit subroutine;
112
+ 1 = elongated image but no more than 1 peak detected;
113
+ 2 = 2 separate peaks detected -> try double star fit;
114
+ 3 = secondary peak found on each side of primary;
115
+ 4 = case 1 after successful double fit (small separ. blended image);
116
+ 5 = case 2 after successful double fit (most likely real double);
117
+ 6 = case 3 after successful double fit (brighter secondary picked)
118
+ """
119
+
120
+ self.galaxy_match: Optional[float] = None
121
+ """LEDA galaxy match flag:
122
+ This flag is either 0 (no match) or contains the log10 of
123
+ the apparent total diameter for I-band (object size) information
124
+ (unit = 0.1 arcmin) copied from the LEDA catalog (galaxies).
125
+ A size value of less than 1 has been rounded up to 1.
126
+ """
127
+
128
+ self.extended_source: Optional[bool] = None
129
+ """2MASS extended source flag:
130
+ This flag is either 0 (no match) or contains the length of
131
+ the semi-major axis of the fiducial ellipse at the K-band
132
+ (object size) information copied from the 2MASS extended source
133
+ catalog.
134
+ """
135
+
136
+ self.num_img_total: Optional[int] = None
137
+ """Total # of CCD images of this star"""
138
+
139
+ self.num_img_used: Optional[int] = None
140
+ """# of CCD images used for this star.
141
+ A zero for the number of used images indicates that all images
142
+ have some "problem" (such as overexposure). In that case an unweighted
143
+ mean over all available images (na) is taken to derive the mean
144
+ position, while normally a weighted mean was calculated based on
145
+ the "good" images, excluding possible problem images (nu <= na).
146
+ """
147
+
148
+ self.num_cat_pm: Optional[int] = None
149
+ """# catalogs (epochs) used for proper motions"""
150
+
151
+ self.ra_mean_epoch: Optional[float] = None
152
+ """Central epoch for mean RA, minus 1900"""
153
+
154
+ self.dec_mean_epoch: Optional[int] = None
155
+ """Central epoch for mean Dec, minus 1900"""
156
+
157
+ self.cat_match: Optional[list[int]] = None
158
+ """A list of ints indicating which catalogs this star has matched against.
159
+ There are 10 entries: Yale SPM, FK6-Hipparcos-Tycho, AC2000, AGK2 Bonn,
160
+ AKG2 Hamburg, Zone Astrog., Black Birch, Lick Astrog., NPM Lick, and
161
+ SPM YSJ1. See the comments in this file for more details.
162
+ """
163
+
164
+ self.apass_mag_b: Optional[float] = None
165
+ """B magnitude from APASS"""
166
+
167
+ self.apass_mag_v: Optional[float] = None
168
+ """V magnitude from APASS"""
169
+
170
+ self.apass_mag_g: Optional[float] = None
171
+ """G magnitude from APASS"""
172
+
173
+ self.apass_mag_r: Optional[float] = None
174
+ """R magnitude from APASS"""
175
+
176
+ self.apass_mag_i: Optional[float] = None
177
+ """I magnitude from APASS"""
178
+
179
+ self.apass_mag_b_sigma: Optional[float] = None
180
+ """Uncertainty of B magnitude from APASS"""
181
+
182
+ self.apass_mag_v_sigma: Optional[float] = None
183
+ """Uncertainty of V magnitude from APASS"""
184
+
185
+ self.apass_mag_g_sigma: Optional[float] = None
186
+ """Uncertainty of G magnitude from APASS"""
187
+
188
+ self.apass_mag_r_sigma: Optional[float] = None
189
+ """Uncertainty of R magnitude from APASS"""
190
+
191
+ self.apass_mag_i_sigma: Optional[float] = None
192
+ """Uncertainty of I magnitude from APASS"""
193
+
194
+ self.johnson_mag_b: Optional[float] = None
195
+ """The Johnson B magnitude derived from APASS measurements."""
196
+
197
+ self.johnson_mag_v: Optional[float] = None
198
+ """The Johnson V magnitude derived from APASS measurements."""
199
+
200
+ self.id_str: Optional[str] = None
201
+ """A unique name indicating the position in the UCAC4 catalog"""
202
+
203
+ self.id_str_ucac2: Optional[str] = None
204
+ """A unique name indicating the position in the UCAC2 catalog"""
205
+
206
+ def __str__(self) -> str:
207
+
104
208
  ret = Star.__str__(self)
105
209
 
106
210
  ret += 'OBJTYPE '
107
211
  if self.obj_type is None:
108
212
  ret += 'None'
109
213
  else:
110
- ret += UCAC4_OBJ_TYPE_STRINGS[self.obj_type]
214
+ ret += UCAC4Star.UCAC4_OBJ_TYPE_STRINGS[self.obj_type]
111
215
  ret += ' | '
112
216
 
113
217
  if self.vmag_model is None:
114
218
  ret += 'APER VMAG None'
115
219
  else:
116
- ret += 'APER VMAG %6.3f' % self.vmag_model
220
+ ret += f'APER VMAG {self.vmag_model:6.3f}'
117
221
  ret += ' | '
118
222
 
119
- if self.temperature is None:
120
- ret += 'TEMP None'
121
- else:
122
- ret += 'TEMP %5d' % (self.temperature)
123
- ret += ' | SCLASS %2s' % (str(self.spectral_class))
124
223
  ret += '\n'
125
224
 
126
225
  ret += 'DBL STAR FLAG='
127
226
  if self.double_star_flag is None:
128
227
  ret += 'None'
129
228
  else:
130
- ret += UCAC4_DOUBLE_STAR_FLAG_STRINGS[self.double_star_flag]
229
+ ret += UCAC4Star.UCAC4_DOUBLE_STAR_FLAG_STRINGS[self.double_star_flag]
131
230
 
132
231
  ret += ' TYPE='
133
232
  if self.double_star_type is None:
134
233
  ret += 'None'
135
234
  else:
136
- ret += UCAC4_DOUBLE_STAR_TYPE_STRINGS[self.double_star_type]
235
+ ret += UCAC4Star.UCAC4_DOUBLE_STAR_TYPE_STRINGS[self.double_star_type]
137
236
 
138
237
  ret += ' | GALAXY '
139
238
  if self.galaxy_match is None:
@@ -156,35 +255,34 @@ class UCAC4Star(Star):
156
255
  if self.apass_mag_b is None:
157
256
  ret += 'B None '
158
257
  else:
159
- ret += 'B %6.3f +/- %6.3f ' % (self.apass_mag_b,
160
- self.apass_mag_b_sigma)
258
+ ret += f'B {self.apass_mag_b:6.3f} +/- {self.apass_mag_b_sigma:6.3f} '
259
+
161
260
  if self.apass_mag_v is None:
162
261
  ret += 'V None '
163
262
  else:
164
- ret += 'V %6.3f +/- %6.3f ' % (self.apass_mag_v,
165
- self.apass_mag_v_sigma)
263
+ ret += f'V {self.apass_mag_v:6.3f} +/- {self.apass_mag_v_sigma:6.3f} '
264
+
166
265
  if self.apass_mag_g is None:
167
266
  ret += 'G None '
168
267
  else:
169
- ret += 'G %6.3f +/- %6.3f ' % (self.apass_mag_g,
170
- self.apass_mag_g_sigma)
268
+ ret += f'G {self.apass_mag_g:6.3f} +/- {self.apass_mag_g_sigma:6.3f} '
269
+
171
270
  if self.apass_mag_r is None:
172
271
  ret += 'R None '
173
272
  else:
174
- ret += 'R %6.3f +/- %6.3f ' % (self.apass_mag_r,
175
- self.apass_mag_r_sigma)
273
+ ret += f'R {self.apass_mag_r:6.3f} +/- {self.apass_mag_r_sigma:6.3f} '
274
+
176
275
  if self.apass_mag_i is None:
177
276
  ret += 'I None'
178
277
  else:
179
- ret += 'I %6.3f +/- %6.3f' % (self.apass_mag_i,
180
- self.apass_mag_i_sigma)
278
+ ret += f'I {self.apass_mag_i:6.3f} +/- {self.apass_mag_i_sigma:6.3f}'
279
+
181
280
  ret += '\n'
182
281
 
183
282
  if self.johnson_mag_b is None or self.johnson_mag_v is None:
184
283
  ret += 'JOHNSON B None V None'
185
284
  else:
186
- ret += 'JOHNSON B %6.3f V %6.3f' % (self.johnson_mag_b,
187
- self.johnson_mag_v)
285
+ ret += f'JOHNSON B {self.johnson_mag_b:6.3f} V {self.johnson_mag_v:6.3f}'
188
286
  ret += '\n'
189
287
 
190
288
  return ret
@@ -270,62 +368,99 @@ UCAC4_FMT_RA = '=i'
270
368
  UCAC4_RECORD_SIZE_RA = 4
271
369
  assert struct.calcsize(UCAC4_FMT_RA) == UCAC4_RECORD_SIZE_RA
272
370
 
371
+
273
372
  class UCAC4StarCatalog(StarCatalog):
274
- def __init__(self, dir=None):
373
+ """A UCAC4 star catalog.
374
+
375
+ This class adds the following options to `find_stars`::
376
+
377
+ require_clean (bool, default True): Return only "clean" stars. Skip stars
378
+ that are streaks or have high proper motion that is not matched
379
+ or discrepant.
380
+ allow_double (bool, default True): If True, include double stars.
381
+ allow_galaxy (bool, default False): If True, include galaxies.
382
+ require_pm (bool, default True): If True, only return stars with measured proper
383
+ motion.
384
+ return_everything (bool, default False): If True, override the above options to
385
+ return all stars and galaxies.
386
+ optimize_ra (bool, default True): Optimize searching of RA through zone files.
387
+ Only useful for internal development.
388
+ """
389
+
390
+ def __init__(self,
391
+ dir: Optional[str | Path | FCPath] = None) -> None:
392
+ """Create a UCAC4StarCatalog.
393
+
394
+ Parameters:
395
+ dir: The path to the star catalog directory (may be a URL). Within
396
+ this directory should be the directory ``u4b``.
397
+ """
398
+
399
+ super().__init__()
400
+
275
401
  if dir is None:
276
- self.dirname = os.environ["UCAC4_DIR"]
402
+ self._dirname = FCPath(os.environ['UCAC4_PATH'])
277
403
  else:
278
- self.dirname = dir
404
+ self._dirname = FCPath(dir)
279
405
  self.debug_level = 0
280
406
 
281
- def _find_stars(self, ra_min, ra_max, dec_min, dec_max, **kwargs):
282
- """Yield the results for all zones in the DEC range.
283
-
284
- Optional arguments: DEFAULT
285
- ra_min, ra_max 0, 2PI RA range in radians
286
- dec_min, dec_max -PI, PI DEC range in radians
287
- vmag_min, vmag_max ALL Magnitude range
288
- require_clean True Only return stars that are clean
289
- detections
290
- allow_double False Allow double stars
291
- allow_galaxy False Allow galaxies and extended objects
292
- require_pm True Only return stars with known proper
293
- motion
294
- return_everything False Override require_clean,
295
- allow_double, allow_galaxy, and
296
- require_pm so that every entry
297
- is returned.
298
- full_result True Populate all fields
299
- False: Stop after fields required
300
- for matching criteria
301
- optimize_ra True Use a binary search to find the
302
- starting RA
303
- """
304
-
305
- start_znum = int(max(np.floor((dec_min*DPR+90)*5)+1, 1))
306
- end_znum = int(min(np.floor((dec_max*DPR+90-1e-15)*5)+1, 900))
407
+ def _find_stars(self,
408
+ ra_min: float,
409
+ ra_max: float,
410
+ dec_min: float,
411
+ dec_max: float,
412
+ vmag_min: Optional[float] = None,
413
+ vmag_max: Optional[float] = None,
414
+ full_result: bool = True,
415
+ **kwargs: Any) -> Iterator[UCAC4Star]:
416
+
417
+ # We do this here instead of as specific arguments because it works better
418
+ # with mypy
419
+ require_clean: bool = kwargs.pop('require_clean', True)
420
+ allow_double: bool = kwargs.pop('allow_double', True)
421
+ allow_galaxy: bool = kwargs.pop('allow_galaxy', False)
422
+ require_pm: bool = kwargs.pop('require_pm', True)
423
+ return_everything: bool = kwargs.pop('return_everything', False)
424
+ optimize_ra: bool = kwargs.pop('optimize_ra', True)
425
+
426
+ start_znum = int(max(np.floor((np.degrees(dec_min)+90)*5)+1, 1))
427
+ end_znum = int(min(np.floor((np.degrees(dec_max)+90-1e-15)*5)+1, 900))
307
428
 
308
429
  for znum in range(start_znum, end_znum+1):
309
430
  fn = self._zone_filename(znum)
310
- with open(fn, 'rb') as fp:
431
+ with fn.open(mode='rb') as fp:
311
432
  for star in self._find_stars_one_file(znum, fp,
312
433
  ra_min, ra_max,
313
434
  dec_min, dec_max,
314
- **kwargs):
435
+ vmag_min=vmag_min,
436
+ vmag_max=vmag_max,
437
+ require_clean=require_clean,
438
+ allow_double=allow_double,
439
+ allow_galaxy=allow_galaxy,
440
+ require_pm=require_pm,
441
+ return_everything=return_everything,
442
+ optimize_ra=optimize_ra,
443
+ full_result=full_result):
315
444
  yield star
316
445
 
317
- def _find_stars_one_file(self, znum, fp, ra_min, ra_max, dec_min, dec_max,
318
- **kwargs):
446
+ def _find_stars_one_file(self,
447
+ znum: int,
448
+ fp: Any,
449
+ ra_min: float,
450
+ ra_max: float,
451
+ dec_min: float,
452
+ dec_max: float,
453
+ vmag_min: Optional[float] = None,
454
+ vmag_max: Optional[float] = None,
455
+ require_clean: bool = True,
456
+ allow_double: bool = False,
457
+ allow_galaxy: bool = False,
458
+ require_pm: bool = True,
459
+ return_everything: bool = False,
460
+ optimize_ra: bool = True,
461
+ full_result: bool = True,
462
+ **kwargs: Any) -> Iterator[UCAC4Star]:
319
463
  """Yield the results for a single zone."""
320
- full_result = kwargs.get('full_result', True)
321
- vmag_min = kwargs.get('vmag_min', None)
322
- vmag_max = kwargs.get('vmag_max', None)
323
- require_clean = kwargs.get('require_clean', True)
324
- allow_double = kwargs.get('allow_double', False)
325
- allow_galaxy = kwargs.get('allow_galaxy', False)
326
- require_pm = kwargs.get('require_pm', True)
327
- return_everything = kwargs.get('return_everything', False)
328
- optimize_ra = kwargs.get('optimize_ra', True)
329
464
 
330
465
  if return_everything:
331
466
  require_clean = False
@@ -334,7 +469,7 @@ class UCAC4StarCatalog(StarCatalog):
334
469
  require_pm = False
335
470
 
336
471
  if optimize_ra:
337
- rec_num = self._find_starting_ra(fp, ra_min) # 0-based
472
+ rec_num = self._find_starting_ra(fp, ra_min) # 0-based
338
473
  else:
339
474
  rec_num = 0
340
475
 
@@ -342,7 +477,7 @@ class UCAC4StarCatalog(StarCatalog):
342
477
  record = fp.read(UCAC4_RECORD_SIZE)
343
478
  if len(record) != UCAC4_RECORD_SIZE:
344
479
  break
345
- rec_num += 1 # This makes rec_num 1-based
480
+ rec_num += 1 # This makes rec_num 1-based
346
481
  parsed = struct.unpack(UCAC4_FMT, record)
347
482
  star = UCAC4Star()
348
483
 
@@ -361,16 +496,15 @@ class UCAC4StarCatalog(StarCatalog):
361
496
  # original UCAC observation cannot be recovered from these data.
362
497
  # The declination is given in south pole distance (spd) and can be
363
498
  # converted back to a true declination by subtracting 324000000 mas.
364
- star.ra = parsed[0] * MAS_TO_RAD
365
- star.dec = parsed[1] * MAS_TO_RAD - HALFPI
499
+ star.ra = cast(float, parsed[0] * MAS_TO_RAD)
500
+ star.dec = cast(float, parsed[1] * MAS_TO_RAD - HALFPI)
366
501
  if star.ra >= ra_max:
367
502
  # RA is in ascending order in the file
368
503
  if self.debug_level > 1:
369
- print('ID', parsed[42], 'SKIPPED RA AND REST OF FILE', end=' ')
370
- print(star.ra)
504
+ print('ID', parsed[42], 'SKIPPED RA AND REST OF FILE', star.ra)
371
505
  break
372
506
  if (star.ra < ra_min or
373
- star.dec < dec_min or star.dec >= dec_max):
507
+ star.dec < dec_min or star.dec >= dec_max):
374
508
  if self.debug_level > 1:
375
509
  print('ID', parsed[42], 'SKIPPED RA/DEC', star.ra, star.dec)
376
510
  continue
@@ -412,14 +546,12 @@ class UCAC4StarCatalog(StarCatalog):
412
546
  if vmag_min is not None:
413
547
  if star.vmag is None or star.vmag < vmag_min:
414
548
  if self.debug_level > 1:
415
- print('ID', parsed[42], 'SKIPPED MODEL MAG', end=' ')
416
- print(star.vmag_model)
549
+ print('ID', parsed[42], 'SKIPPED MODEL MAG', star.vmag_model)
417
550
  continue
418
551
  if vmag_max is not None:
419
552
  if star.vmag is None or star.vmag > vmag_max:
420
553
  if self.debug_level > 1:
421
- print('ID', parsed[42], 'SKIPPED MODEL MAG', end=' ')
422
- print(star.vmag_model)
554
+ print('ID', parsed[42], 'SKIPPED MODEL MAG', star.vmag_model)
423
555
  continue
424
556
 
425
557
  ###############
@@ -446,12 +578,12 @@ class UCAC4StarCatalog(StarCatalog):
446
578
  # and 2 stars added from high proper motion surveys
447
579
  # 8 = high proper motion solution in UCAC4, star not matched with PPMXL
448
580
  # 9 = high proper motion solution in UCAC4, discrepant PM to PPMXL
449
- # (see discussion of flags 8,9 in redcution section 2e above)
581
+ # (see discussion of flags 8,9 in reduction section 2e above)
450
582
  star.obj_type = parsed[5]
451
583
  if (require_clean and
452
- (star.obj_type == UCAC4_OBJ_TYPE_STREAK or
453
- star.obj_type == UCAC4_OBJ_TYPE_HPM_NOT_MATCHED or
454
- star.obj_type == UCAC4_OBJ_TYPE_HPM_DISCREPANT)):
584
+ (star.obj_type in (UCAC4Star.UCAC4_OBJ_TYPE_STREAK,
585
+ UCAC4Star.UCAC4_OBJ_TYPE_HPM_NOT_MATCHED,
586
+ UCAC4Star.UCAC4_OBJ_TYPE_HPM_DISCREPANT))):
455
587
  # Use with extreme caution
456
588
  if self.debug_level:
457
589
  print('ID', parsed[42], 'SKIPPED NOT CLEAN', star.obj_type)
@@ -516,14 +648,14 @@ class UCAC4StarCatalog(StarCatalog):
516
648
  # (object size) information copied from the 2MASS extended source
517
649
  # catalog.
518
650
  star.galaxy_match = parsed[40]
519
- star.extended_source = parsed[41] # XXX What units is this in?
651
+ star.extended_source = parsed[41] # XXX What units is this in?
520
652
  if not allow_galaxy and (star.galaxy_match or star.extended_source):
521
653
  if self.debug_level:
522
- print('ID', parsed[42], 'SKIPPED GALAXY/EXTENDED', end=' ')
523
- print(star.galaxy_match, star.extended_source)
654
+ print('ID', parsed[42], 'SKIPPED GALAXY/EXTENDED',
655
+ star.galaxy_match, star.extended_source)
524
656
  continue
525
657
  if star.galaxy_match:
526
- star.galaxy_match = 10.**star.galaxy_match / 10. / 60. # Degrees
658
+ star.galaxy_match = 10.**star.galaxy_match / 10. / 60. # Degrees
527
659
 
528
660
  #################
529
661
  # PROPER MOTION #
@@ -587,7 +719,8 @@ class UCAC4StarCatalog(StarCatalog):
587
719
  if star.pm_rac_sigma is None:
588
720
  star.pm_ra_sigma = None
589
721
  else:
590
- star.pm_ra_sigma = star.pm_rac_sigma / np.cos(star.dec)
722
+ star.pm_ra_sigma = (star.pm_rac_sigma /
723
+ np.cos(star.dec))
591
724
 
592
725
  if pdse == 251:
593
726
  star.pm_dec_sigma = 27.5 * MAS_TO_RAD
@@ -634,7 +767,7 @@ class UCAC4StarCatalog(StarCatalog):
634
767
  # catalog like Hipparcos, Tycho or high proper motion data, a mean
635
768
  # error in position and proper motion depending on the catalog and
636
769
  # magnitude of the star was adopted.
637
- star.rac_sigma = (parsed[7]+128.) * MAS_TO_RAD # RA * COS(DEC)
770
+ star.rac_sigma = (parsed[7]+128.) * MAS_TO_RAD # RA * COS(DEC)
638
771
  star.ra_sigma = star.rac_sigma / np.cos(star.dec)
639
772
  star.dec_sigma = (parsed[8]+128.) * MAS_TO_RAD
640
773
 
@@ -817,8 +950,10 @@ class UCAC4StarCatalog(StarCatalog):
817
950
  star.johnson_mag_b = star.apass_mag_b
818
951
  star.johnson_mag_v = star.apass_mag_v
819
952
 
820
- if (parsed[28] < 20000 and parsed[33] == 0 and
821
- parsed[29] < 20000 and parsed[34] == 0):
953
+ if (star.apass_mag_b is not None and
954
+ parsed[33] == 0 and
955
+ star.apass_mag_v is not None and
956
+ parsed[34] == 0):
822
957
  star.johnson_mag_v = (star.apass_mag_v -
823
958
  0.09 * (star.apass_mag_b -
824
959
  star.apass_mag_v))
@@ -837,7 +972,6 @@ class UCAC4StarCatalog(StarCatalog):
837
972
  #37 60 (4) I*1 1/100 mag error of r magnitude from APASS (14)
838
973
  #38 61 (5) I*1 1/100 mag error of i magnitude from APASS (14)
839
974
 
840
-
841
975
  #######################
842
976
  # CATALOG MATCH FLAGS #
843
977
  #######################
@@ -935,26 +1069,25 @@ class UCAC4StarCatalog(StarCatalog):
935
1069
  if parsed[43] == 0:
936
1070
  star.id_str_ucac2 = None
937
1071
  else:
938
- star.id_str_ucac2 = 'UCAC2-%03d-%06d' % (parsed[43],
939
- parsed[44])
1072
+ star.id_str_ucac2 = f'UCAC2-{parsed[43]:03d}-{parsed[44]:06d}'
940
1073
 
941
1074
  ###############################
942
1075
  # UCAC4 IDENTIFICATION NUMBER #
943
1076
  ###############################
944
1077
 
945
- star.id_str = 'UCAC4-%03d-%06d' % (znum, rec_num)
1078
+ star.id_str = f'UCAC4-{znum:03d}-{rec_num:06d}'
946
1079
 
947
1080
  ##################################################
948
1081
  # COMPUTE SPECTRAL CLASS AND SURFACE TEMPERATURE #
949
1082
  ##################################################
950
1083
 
951
1084
  if (star.johnson_mag_b is not None and
952
- star.johnson_mag_v is not None):
953
- star.spectral_class = self.sclass_from_bv(star.johnson_mag_b,
1085
+ star.johnson_mag_v is not None):
1086
+ star.spectral_class = Star.sclass_from_bv(star.johnson_mag_b,
954
1087
  star.johnson_mag_v)
955
1088
 
956
1089
  if star.spectral_class is not None:
957
- star.temperature = self.temperature_from_sclass(star.
1090
+ star.temperature = Star.temperature_from_sclass(star.
958
1091
  spectral_class)
959
1092
 
960
1093
  if self.debug_level:
@@ -964,13 +1097,16 @@ class UCAC4StarCatalog(StarCatalog):
964
1097
 
965
1098
  #############################################################################
966
1099
 
967
- def _zone_filename(self, znum):
1100
+ def _zone_filename(self, znum: int) -> FCPath:
968
1101
  """Convert a UCAC4 zone number to an absolute pathspec."""
969
- fn = os.path.join(self.dirname, 'u4b', 'z%03d'%znum)
970
- return fn
971
1102
 
972
- def _find_starting_ra(self, fp, ra_min):
1103
+ return self._dirname / 'u4b' / f'z{znum:03d}'
1104
+
1105
+ def _find_starting_ra(self,
1106
+ fp: Any,
1107
+ ra_min: float) -> int:
973
1108
  """Efficiently find the first record >= RA_MIN."""
1109
+
974
1110
  if ra_min <= 0.:
975
1111
  # No point in searching!
976
1112
  return 0
@@ -995,81 +1131,8 @@ class UCAC4StarCatalog(StarCatalog):
995
1131
  else:
996
1132
  # Exact match!
997
1133
  fp.seek(mid*UCAC4_RECORD_SIZE, os.SEEK_SET)
998
- return mid // UCAC4_RECORD_SIZE
1134
+ return int(mid // UCAC4_RECORD_SIZE)
999
1135
 
1000
1136
  # At this point lo is the best we can do
1001
1137
  fp.seek(lo*UCAC4_RECORD_SIZE, os.SEEK_SET)
1002
- return lo // UCAC4_RECORD_SIZE
1003
-
1004
-
1005
- #===============================================================================
1006
- # UNIT TESTS
1007
- #===============================================================================
1008
-
1009
- import unittest
1010
-
1011
- class Test_UCAC4StarCatalog(unittest.TestCase):
1012
-
1013
- def runTest(self):
1014
- cat = UCAC4StarCatalog('t:/external/ucac4')
1015
-
1016
- # Zone 1
1017
- num_pm = cat.count_stars(require_clean=False, allow_double=True,
1018
- allow_galaxy=True, require_pm=True,
1019
- dec_min=-90*RPD, dec_max=-89.8*RPD)
1020
- num_all = cat.count_stars(require_clean=False, allow_double=True,
1021
- allow_galaxy=True, require_pm=False,
1022
- dec_min=-90*RPD, dec_max=-89.8*RPD)
1023
- self.assertEqual(num_all, 206)
1024
- self.assertEqual(num_all-num_pm, 5)
1025
-
1026
- # Zone 451
1027
- num_pm = cat.count_stars(require_clean=False, allow_double=True,
1028
- allow_galaxy=True, require_pm=True,
1029
- dec_min=0., dec_max=0.2*RPD)
1030
- num_all = cat.count_stars(require_clean=False, allow_double=True,
1031
- allow_galaxy=True, require_pm=False,
1032
- dec_min=0., dec_max=0.2*RPD)
1033
- self.assertEqual(num_all, 133410)
1034
- self.assertEqual(num_all-num_pm, 6509) # zone_stats says 6394??
1035
-
1036
- # Zone 900
1037
- num_pm = cat.count_stars(require_clean=False, allow_double=True,
1038
- allow_galaxy=True, require_pm=True,
1039
- dec_min=89.8*RPD, dec_max=90.*RPD)
1040
- num_all = cat.count_stars(require_clean=False, allow_double=True,
1041
- allow_galaxy=True, require_pm=False,
1042
- dec_min=89.8*RPD, dec_max=90.*RPD)
1043
- self.assertEqual(num_all, 171)
1044
- self.assertEqual(num_all-num_pm, 10) # zone_stats says 9??
1045
-
1046
- # Compare slicing directions
1047
- num_dec = 0
1048
- for idec in range(20):
1049
- num_dec += cat.count_stars(dec_min=0.2*idec*RPD,
1050
- dec_max=0.2*(idec+1)*RPD,
1051
- ra_min=60*RPD, ra_max=70*RPD)
1052
- num_ra = 0
1053
- for ira in range(10):
1054
- num_ra += cat.count_stars(dec_min=0., dec_max=4.*RPD,
1055
- ra_min=(ira+60)*RPD, ra_max=((ira+1)+60)*RPD)
1056
- self.assertEqual(num_dec, num_ra)
1057
-
1058
- # Compare optimized RA search with non-optimized
1059
- for dec_idx in range(10):
1060
- dec_min = (dec_idx*10-90.)*RPD
1061
- dec_max = (dec_idx*10-89.8)*RPD
1062
- for ra_min_idx in range(10):
1063
- ra_min = ra_min_idx * 10 * RPD
1064
- ra_max = ra_min + 10*RPD
1065
- num_opt = cat.count_stars(dec_min=dec_min, dec_max=dec_max,
1066
- ra_min=ra_min, ra_max=ra_max)
1067
- num_no_opt = cat.count_stars(dec_min=dec_min, dec_max=dec_max,
1068
- ra_min=ra_min, ra_max=ra_max,
1069
- optimize_ra=False)
1070
- self.assertEqual(num_opt, num_no_opt)
1071
-
1072
-
1073
-
1074
- if __name__ == '__main__':
1075
- unittest.main(verbosity=2)
1138
+ return int(lo // UCAC4_RECORD_SIZE)