22"""Utilities for interfacing with hpgeom. Originally implemented in
23http://github.com/LSSTDESC/dia_pipe and then translated to hpgeom.
26__all__ = [
"toIndex",
"toRaDec",
"eq2xyz",
"eq2xyzVec",
"convert_spherical",
27 "convert_spherical_array",
"query_disc",
"obj_id_to_ss_object_id",
"ss_object_id_to_obj_id"]
29from astropy.time
import Time
32import numpy.typing
as npt
36 """Return healpix index given ra, dec in degrees
41 Power of 2 nside healpix resolution.
45 Declination in degrees
50 Unique healpix pixel ID containing point RA, DEC at resolution nside.
52 return hpg.angle_to_pixel(nside, ra, dec, nest=
False)
56 """Convert from healpix index to ra,dec in degrees
61 Resolution of healpixel "grid".
63 Index of the healpix pixel we want to find the location of.
67 pos : `numpy.ndarray`, (2,)
68 RA and DEC of healpix pixel location in degrees.
70 ra, dec = hpg.pixel_to_angle(nside, index, nest=
False)
71 return np.dstack((ra, dec))[0]
75 """Convert from equatorial ra,dec in degrees to x,y,z on unit sphere.
82 Declination in degrees
86 xyz : `numpy.ndarray`, (3,)
87 Float xyz positions on the unit sphere.
90 theta = np.pi/2 - np.deg2rad(dec)
91 sintheta = np.sin(theta)
92 x = sintheta*np.cos(phi)
93 y = sintheta*np.sin(phi)
95 return np.array([x, y, z])
99 """Convert equatorial ra,dec in degrees to x,y,z on the unit sphere
102 Vectorized version of ``eq2xyz``
106 ra : array_like, (N,)
107 Array of RA in degrees.
108 dec : array_like, (N,)
109 Declination in degrees
113 vec : `numpy.ndarray`, (N,3)
114 Array of unitsphere 3-vectors.
116 ra = np.array(ra, dtype=
'f8', ndmin=1, copy=
False)
117 dec = np.array(dec, dtype=
'f8', ndmin=1, copy=
False)
118 if ra.size != dec.size:
119 raise ValueError(
"ra,dec not same size: %s,%s" % (ra.size, dec.size))
127 """Convert from ra,dec to spherical coordinates.
136 Declination in radians
138 return np.dstack([np.cos(dec*np.pi/180)*np.cos(ra*np.pi/180),
139 np.cos(dec*np.pi/180)*np.sin(ra*np.pi/180),
140 np.sin(dec*np.pi/180)])[0]
144 """Convert from and a array ra,dec to spherical coordinates.
150 array : `numpy.ndarray`, (N, 2)
151 (N, 2) Array of RA, DEC values.
155 vecs : `numpy.ndarray`, (N, 3)
156 Vectors on the unit sphere
164 """Get the list of healpix indices within max_rad, min_rad given in radians
165 around ra,dec given in degrees
170 Resolution of the healpixels to search/return.
174 Declination in degrees
176 Max distance in radians to search nearby healpixels.
177 min_rad : `float`, optional
178 Minimum distance in radians to search healpixels. Default = 0.
180 ra = np.atleast_1d(ra)
181 dec = np.atleast_1d(dec)
183 max_rad_deg = np.rad2deg(max_rad)
186 [hpg.query_circle(nside, a, b, max_rad_deg, nest=
False)
187 for (a, b)
in zip(ra, dec)])
189 if min_rad > 0
and len(pixels) > 0:
191 min_rad2 = min_rad**2
193 dsq = np.sum((vecs - vec0)**2, axis=1)
194 match = dsq > min_rad2
195 pixels = pixels[match]
207 """Convert from Minor Planet Center packed provisional object ID to
213 Minor Planet Center packed provisional designation for a small solar
214 system object. Must be fewer than eight characters.
224 Raised if either objID is shorter than 7 or longer than 8 characters or contains
225 illegal objID characters
228 raise ValueError(f
'objID longer than 8 characters: "{objID}"')
230 raise ValueError(f
'objID shorter than 7 characters: "{objID}"')
231 if any([ord(c) > 255
for c
in objID]):
232 raise ValueError(f
'{[c for c in objID if ord(c) > 255]} not legal objID characters (ascii [1, 255])')
234 ssObjectID = ord(objID[0])
235 for character
in objID[1:]:
237 ssObjectID += ord(character)
242 """Convert from Rubin ssObjectID to Minor Planet Center packed provisional
253 Minor Planet Center packed provisional designation.
258 if ssObjectID < 0
or ssObjectID >= (1 << 64):
259 raise ValueError(f
'ssObjectID ({ssObjectID}) outside [0, 2^64 - 1].')
261 objID =
''.join([chr((ssObjectID >> (8 * i)) % 256)
for i
in reversed(range(0, 8))])
262 objID = objID.replace(
'\x00',
'')
275 B612 code to pack a unpacked MPC designation. For example,
276 provisional designation 1998 SS162 will be packed to J98SG2S.
277 Permanent designation 323 will be packed to 00323.
279 TODO: add support for comet and natural satellite designations
284 MPC unpacked designation.
289 MPC packed form designation.
293 ValueError : If designation cannot be packed.
314 "Unpacked designation '{}' could not be packed.\n"
315 "It could not be recognized as any of the following:\n"
316 " - a numbered object (e.g. '3202', '203289', '3140113')\n"
317 " - a provisional designation (e.g. '1998 SV127', '2008 AA360')\n"
318 " - a survey designation (e.g. '2040 P-L', '3138 T-1')"
320 raise ValueError(err.format(designation))
323BASE62 =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
324BASE62_MAP = {BASE62[i]: i
for i
in range(len(BASE62))}
334 epoch_pf = str(epoch_pf)
335 year = int(epoch_pf[0], base=32) * 100 + int(epoch_pf[1:3])
336 month = int(epoch_pf[3], base=32)
337 day = int(epoch_pf[4], base=32)
338 isot_string =
"{:d}-{:02d}-{:02d}".format(year, month, day)
340 if len(epoch_pf) > 5:
341 fractional_day = float(
"." + epoch_pf[5:])
342 hours = int((24 * fractional_day))
343 minutes = int(60 * ((24 * fractional_day) - hours))
344 seconds = 3600 * (24 * fractional_day - hours - minutes / 60)
345 isot_string +=
"T{:02d}:{:02d}:{:09.6f}".format(hours, minutes, seconds)
347 return Time(isot_string, format=
"isot", scale=
"tt")
352 Convert MPC packed form dates (in the TT time scale) to
353 MJDs in TT. See: https://minorplanetcenter.net/iau/info/PackedDates.html
354 for details on the packed date format.
358 pf_tt : `~numpy.ndarray` (N)
359 MPC-style packed form epochs in the TT time scale.
363 mjd_tt : `~astropy.time.core.Time` (N)
375 Pack a numbered MPC designation.
377 Examples of numbered designations:
392 MPC numbered designation.
397 MPC packed numbered designation.
401 ValueError : If the numbered designation cannot be packed.
402 If the numbered designation is larger than 15396335.
404 number = int(designation)
405 if number > 15396335:
407 "Numbered designation is too large. Maximum supported is 15396335."
411 return "{:05}".format(number)
412 elif (number >= 100000)
and (number <= 619999):
413 bigpart, remainder = divmod(number, 10000)
414 return f
"{BASE62[bigpart]}{remainder:04}"
419 number_pf.append(BASE62[int(x % 62)])
423 return "~{}".format(
"".join(number_pf).zfill(4))
428 Pack a provisional MPC designation.
430 Examples of provisional designations:
445 MPC provisional designation.
450 MPC packed provisional designation.
454 ValueError : If the provisional designation cannot be packed.
455 The provisional designations is not at least 6 characters long.
456 The first 4 characters of the provisional designation are not a year.
457 The 5th character of the provisional designation is not a space.
458 The provisional designation contains a hyphen.
459 The half-month letter is I or Z.
461 if len(designation) < 6:
463 "Provisional designations should be at least 6 characters long."
465 if not designation[:3].isdecimal():
467 "Expected the first 4 characters of the provisional designation to be a year."
469 if designation[4] !=
" ":
471 "Expected the 5th character of the provisional designation to be a space."
473 if "-" in designation:
474 raise ValueError(
"Provisional designations cannot contain a hyphen.")
476 year = BASE62[int(designation[0:2])] + designation[2:4]
477 letter1 = designation[5]
478 letter2 = designation[6]
479 cycle = designation[7:]
481 if letter1
in {
"I",
"Z"}:
482 raise ValueError(
"Half-month letters cannot be I or Z.")
483 if letter1.isdecimal()
or letter2.isdecimal():
484 raise ValueError(
"Invalid provisional designation.")
488 cycle_int = int(cycle)
490 cycle_pf = str(cycle_int).zfill(2)
492 cycle_pf = BASE62[cycle_int // 10] + str(cycle_int % 10)
494 designation_pf =
"{}{}{}{}".format(year, letter1, cycle_pf, letter2)
495 return designation_pf
500 Pack a survey MPC designation.
502 Examples of survey designations:
512 MPC survey designation.
517 MPC packed survey designation.
521 ValueError : If the survey designation cannot be packed.
522 The survey designation does not start with P-L, T-1, T-2, or T-3.
524 number = designation[0:4]
525 survey = designation[5:]
530 elif survey[0:2] ==
"T-" and survey[2]
in {
"1",
"2",
"3"}:
531 survey_pf =
"T{}S".format(survey[2])
534 raise ValueError(
"Survey designations must start with P-L, T-1, T-2, T-3.")
536 designation_pf =
"{}{}".format(survey_pf, number.zfill(4))
537 return designation_pf
542 Unpack a numbered MPC designation.
544 Examples of numbered designations:
559 MPC packed numbered designation.
564 MPC unpacked numbered designation.
568 ValueError : If the numbered designation cannot be unpacked.
569 The packed numbered designation is not at least 4 characters long.
573 if designation_pf.isdecimal():
574 number = int(designation_pf)
577 elif designation_pf[0] ==
"~":
579 number_pf = designation_pf[1:]
580 for i, c
in enumerate(number_pf):
581 power = len(number_pf) - (i + 1)
582 number += BASE62_MAP[c] * (62**power)
586 number = BASE62_MAP[designation_pf[0]] * 10000 + int(designation_pf[1:])
589 raise ValueError(
"Packed numbered designation could not be unpacked.")
591 designation = str(number)
598 Unpack a provisional MPC designation.
600 Examples of provisional designations:
615 MPC packed provisional designation.
620 MPC unpacked provisional designation.
624 ValueError : If the provisional designation cannot be unpacked.
625 The packed provisional designation is not 7 characters long.
626 The packed provisional designation does not have a year.
628 if len(designation_pf) != 7:
629 raise ValueError(f
"Provisional designation must be 7 characters long: '{designation_pf}'")
630 if not designation_pf[1].isdecimal()
or not designation_pf[2].isdecimal():
631 raise ValueError(f
"Provisional designation must have a year: '{designation_pf}'")
632 year = str(BASE62_MAP[designation_pf[0]] * 100 + int(designation_pf[1:3]))
633 letter1 = designation_pf[3]
634 letter2 = designation_pf[6]
635 if letter1.isdecimal()
or letter2.isdecimal():
636 raise ValueError(f
"fourth or seventh char is decimal: '{designation_pf}'")
637 cycle1 = designation_pf[4]
638 cycle2 = designation_pf[5]
640 number = int(BASE62_MAP[cycle1]) * 10 + BASE62_MAP[cycle2]
644 number_str = str(number)
646 designation =
"{} {}{}{}".format(year, letter1, letter2, number_str)
653 Unpack a survey MPC designation.
655 Examples of survey designations:
665 MPC packed survey designation.
670 MPC unpacked survey designation.
674 ValueError : If the survey designation cannot be unpacked.
675 The packed survey designation does not start with PLS, T1S, T2S, or T3S.
677 number = int(designation_pf[3:8])
678 survey_pf = designation_pf[0:3]
679 if survey_pf
not in {
"PLS",
"T1S",
"T2S",
"T3S"}:
681 "Packed survey designation must start with PLS, T1S, T2S, or T3S."
684 if survey_pf ==
"PLS":
687 if survey_pf[0] ==
"T" and survey_pf[2] ==
"S":
688 survey =
"T-{}".format(survey_pf[1])
690 designation =
"{} {}".format(number, survey)
696 Unpack a packed MPC designation. For example, provisional
697 designation J98SG2S will be unpacked to 1998 SS162. Permanent
698 designation 00323 will be unpacked to 323.
700 TODO: add support for comet and natural satellite designations
705 MPC packed form designation.
710 MPC unpacked designation.
714 ValueError : If designation_pf cannot be unpacked.
736 "Packed form designation '{}' could not be unpacked.\n"
737 "It could not be recognized as any of the following:\n"
738 " - a numbered object (e.g. '03202', 'K3289', '~AZaz')\n"
739 " - a provisional designation (e.g. 'J98SC7V', 'K08Aa0A')\n"
740 " - a survey designation (e.g. 'PLS2040', 'T1S3138')"
742 raise ValueError(err.format(designation_pf))
obj_id_to_ss_object_id(objID)
str unpack_numbered_designation(str designation_pf)
str unpack_mpc_designation(str designation_pf)
str unpack_survey_designation(str designation_pf)
Time _unpack_mpc_date(str epoch_pf)
ss_object_id_to_obj_id(ssObjectID, packed=False)
str unpack_provisional_designation(str designation_pf)
convert_spherical_array(array)
str pack_mpc_designation(str designation)
str pack_provisional_designation(str designation)
packed_obj_id_to_ss_object_id(objID)
Time convert_mpc_packed_dates(npt.ArrayLike pf_tt)
convert_spherical(ra, dec)
query_disc(nside, ra, dec, max_rad, min_rad=0)
str pack_survey_designation(str designation)
str pack_numbered_designation(str designation)