1 from __future__
import absolute_import, division, print_function
3 __all__ = [
"getIndexPath",
"getConfigFromEnvironment",
"AstrometryNetCatalog",
"generateCache"]
5 from builtins
import zip
6 from builtins
import range
7 from builtins
import object
14 from lsst.log
import Log
15 from .astrometry_net
import MultiIndex, healpixDistance
16 from .astrometryNetDataConfig
import AstrometryNetDataConfig
20 """!Get the path to the specified astrometry.net index file
22 No effort is made to confirm that the file exists, so it may be used to locate the
23 path to a non-existent file (e.g., to write).
25 @param[in] fn path to index file; if relative, then relative to astrometry_net_data
26 if that product is setup, else relative to the current working directory
27 @return the absolute path to the index file
32 andir = lsst.utils.getPackageDir(
'astrometry_net_data')
35 return os.path.abspath(fn)
36 return os.path.join(andir, fn)
40 """Find the config file from the environment
42 The andConfig.py file is in the astrometry_net_data directory.
45 anDir = lsst.utils.getPackageDir(
'astrometry_net_data')
48 andConfigPath =
"andConfig.py"
49 if not os.path.exists(andConfigPath):
50 raise RuntimeError(
"Unable to find andConfig.py in the current directory. "
51 "Did you forget to setup astrometry_net_data?")
53 andConfigPath = os.path.join(anDir,
"andConfig.py")
54 if not os.path.exists(andConfigPath):
55 raise RuntimeError(
"Unable to find andConfig.py in astrometry_net_data directory %s" % (anDir,))
57 andConfig = AstrometryNetDataConfig()
58 andConfig.load(andConfigPath)
63 """A wrapper for the multiindex_t, which only reads the data when it needs to
65 The MultiIndexCache may be instantiated directly, or via the 'fromFilenameList'
66 class method, which loads it from a list of filenames.
69 def __init__(self, filenameList, healpix, nside):
72 @param filenameList List of filenames; first is the multiindex, then
73 follows the individual index files
74 @param healpix Healpix number
75 @param nside Healpix nside
77 if len(filenameList) < 2:
78 raise RuntimeError(
"Insufficient filenames provided for multiindex (%s): expected >= 2" %
85 self.
log = Log.getDefaultLogger()
89 """Construct from a list of filenames
91 The list of filenames should contain the multiindex filename first,
92 then the individual index filenames. The healpix and nside are
93 determined by reading the indices, so this is not very efficient.
95 self = cls(filenameList, 0, 0)
97 healpixes = set(self[i].healpix
for i
in range(len(self)))
98 nsides = set(self[i].hpnside
for i
in range(len(self)))
99 assert len(healpixes) == 1
100 assert len(nsides) == 1
102 self.
_nside = nsides.pop()
106 """Read the indices"""
107 if self.
_mi is not None:
110 if not os.path.exists(fn):
112 "Unable to get filename for astrometry star file %s" % (self.
_filenameList[0],))
113 self.
_mi = MultiIndex(fn)
116 raise RuntimeError(
'Failed to read stars from astrometry multiindex filename "%s"' % fn)
119 self.log.debug(
'Unable to find index part of multiindex %s', fn)
122 if not os.path.exists(fn):
123 self.log.warn(
"Unable to get filename for astrometry index %s", fn)
125 self.log.debug(
'Reading index from multiindex file "%s"', fn)
126 self._mi.addIndex(fn,
False)
128 self.log.debug(
' index %i, hp %i (nside %i), nstars %i, nquads %i',
129 ind.indexid, ind.healpix, ind.hpnside, ind.nstars, ind.nquads)
132 """Reload the indices."""
142 """Unload the indices"""
149 """!Is the index within range of the provided coordinates?
151 @param coord Coordinate to check (lsst.afw.coord.Coord)
152 @param distance Angular distance (lsst.afw.geom.Angle)
165 return iter(self.
_mi)
169 """An interface to an astrometry.net catalog
171 Behaves like a list of MultiIndexCache (or multiindex_t).
173 These should usually be constructed using the 'fromEnvironment'
174 class method, which wraps the 'fromIndexFiles' and 'fromCache'
175 alternative class methods.
177 _cacheFilename =
"andCache.fits"
182 @param andConfig Configuration (an AstrometryNetDataConfig)
186 if self.config.allowCache
and os.path.exists(cacheName):
191 def _initFromIndexFiles(self, andConfig):
192 """Initialise from the index files in an AstrometryNetDataConfig"""
193 indexFiles = list(zip(andConfig.indexFiles, andConfig.indexFiles)) + andConfig.multiIndexFiles
194 self.
_multiInds = [MultiIndexCache.fromFilenameList(fnList)
for fnList
in indexFiles]
197 """Write a cache file
199 The cache file is a FITS file with all the required information to build the
200 AstrometryNetCatalog quickly. The first table extension contains a row for each multiindex,
201 storing the healpix and nside values. The second table extension contains a row
202 for each filename in all the multiindexes. The two may be JOINed through the
206 numFilenames = sum(len(ind._filenameList)
for ind
in self.
_multiInds)
207 maxLength = max(len(fn)
for ind
in self.
_multiInds for fn
in ind._filenameList) + 1
210 first = pyfits.new_table([pyfits.Column(name=
"id", format=
"K"),
211 pyfits.Column(name=
"healpix", format=
"K"),
212 pyfits.Column(name=
"nside", format=
"K"),
214 first.data.field(
"id")[:] = np.arange(len(self.
_multiInds), dtype=int)
215 first.data.field(
"healpix")[:] = np.array([ind._healpix
for ind
in self.
_multiInds])
216 first.data.field(
"nside")[:] = np.array([ind._nside
for ind
in self.
_multiInds])
219 second = pyfits.new_table([pyfits.Column(name=
"id", format=
"K"),
220 pyfits.Column(name=
"filename", format=
"%dA" % (maxLength)),
221 ], nrows=numFilenames)
222 ident = second.data.field(
"id")
223 filenames = second.data.field(
"filename")
226 for fn
in ind._filenameList:
231 pyfits.HDUList([pyfits.PrimaryHDU(), first, second]).writeto(outName, clobber=
True)
233 def _initFromCache(self, filename):
234 """Initialise from a cache file
236 Ingest the cache file written by the 'writeCache' method and
237 use that to quickly instantiate the AstrometryNetCatalog.
239 with pyfits.open(filename)
as hduList:
240 first = hduList[1].data
241 second = hduList[2].data
244 filenames = {i: []
for i
in first.field(
"id")}
245 for id2, fn
in zip(second.field(
"id"), second.field(
"filename")):
246 filenames[id2].append(fn)
248 zip(first.field(
"id"), first.field(
"healpix"), first.field(
"nside"))]
251 cacheFiles = set(second.field(
"filename"))
252 configFiles = set(sum(self.config.multiIndexFiles, []) + self.config.indexFiles)
253 assert(cacheFiles == configFiles)
266 """Generate a cache file"""
267 if andConfig
is None:
271 for index
in catalog:
275 for index
in catalog:
def getConfigFromEnvironment
def getIndexPath
Get the path to the specified astrometry.net index file.
def isWithinRange
Is the index within range of the provided coordinates?
lsst::afw::geom::Angle healpixDistance(int hp, int nside, lsst::afw::coord::Coord const &coord)
Calculate the distance from coordinates to a healpix.