1 __all__ = [
"getIndexPath",
"getConfigFromEnvironment",
"AstrometryNetCatalog",
"generateCache"]
6 from astropy.io
import fits
10 from .astrometry_net
import MultiIndex, healpixDistance
11 from .astrometryNetDataConfig
import AstrometryNetDataConfig
15 """!Get the path to the specified astrometry.net index file 17 No effort is made to confirm that the file exists, so it may be used to 18 locate the path to a non-existent file (e.g., to write). 20 @param[in] fn path to index file; if relative, then relative to 21 astrometry_net_data if that product is setup, else relative to the 22 current working directory 23 @return the absolute path to the index file 31 return os.path.abspath(fn)
32 return os.path.join(andir, fn)
36 """Find the config file from the environment 38 The andConfig.py file is in the astrometry_net_data directory. 44 andConfigPath =
"andConfig.py" 45 if not os.path.exists(andConfigPath):
46 raise RuntimeError(
"Unable to find andConfig.py in the current directory. " 47 "Did you forget to setup astrometry_net_data?")
49 andConfigPath = os.path.join(anDir,
"andConfig.py")
50 if not os.path.exists(andConfigPath):
51 raise RuntimeError(
"Unable to find andConfig.py in astrometry_net_data directory %s" % (anDir,))
54 andConfig.load(andConfigPath)
59 """A wrapper for the multiindex_t, which only reads the data when it 62 The MultiIndexCache may be instantiated directly, or via the 63 'fromFilenameList' class method, which loads it from a list of filenames. 66 def __init__(self, filenameList, healpix, nside):
69 @param filenameList List of filenames; first is the multiindex, then 70 follows the individual index files 71 @param healpix Healpix number 72 @param nside Healpix nside 74 if len(filenameList) < 2:
75 raise RuntimeError(
"Insufficient filenames provided for multiindex (%s): expected >= 2" %
82 self.
log = Log.getDefaultLogger()
86 """Construct from a list of filenames 88 The list of filenames should contain the multiindex filename first, 89 then the individual index filenames. The healpix and nside are 90 determined by reading the indices, so this is not very efficient. 92 self = cls(filenameList, 0, 0)
94 healpixes = set(self[i].healpix
for i
in range(len(self)))
95 nsides = set(self[i].hpnside
for i
in range(len(self)))
96 assert len(healpixes) == 1
97 assert len(nsides) == 1
103 """Read the indices""" 104 if self.
_mi is not None:
107 if not os.path.exists(fn):
109 "Unable to get filename for astrometry star file %s" % (self.
_filenameList[0],))
113 raise RuntimeError(
'Failed to read stars from astrometry multiindex filename "%s"' % fn)
116 self.
log.debug(
'Unable to find index part of multiindex %s', fn)
119 if not os.path.exists(fn):
120 self.
log.warn(
"Unable to get filename for astrometry index %s", fn)
122 self.
log.debug(
'Reading index from multiindex file "%s"', fn)
123 self.
_mi.addIndex(fn,
False)
125 self.
log.debug(
' index %i, hp %i (nside %i), nstars %i, nquads %i',
126 ind.indexid, ind.healpix, ind.hpnside, ind.nstars, ind.nquads)
129 """Reload the indices.""" 139 """Unload the indices""" 146 """!Is the index within range of the provided coordinates? 148 @param coord ICRS coordinate to check (lsst.geom.SpherPoint) 149 @param distance Angular distance (lsst.geom.Angle) 162 return iter(self.
_mi)
166 """An interface to an astrometry.net catalog 168 Behaves like a list of MultiIndexCache (or multiindex_t). 170 These should usually be constructed using the 'fromEnvironment' 171 class method, which wraps the 'fromIndexFiles' and 'fromCache' 172 alternative class methods. 174 _cacheFilename =
"andCache.fits" 179 @param andConfig Configuration (an AstrometryNetDataConfig) 183 if self.
config.allowCache
and os.path.exists(cacheName):
188 def _initFromIndexFiles(self, andConfig):
189 """Initialise from the index files in an AstrometryNetDataConfig""" 190 indexFiles = list(zip(andConfig.indexFiles, andConfig.indexFiles)) + andConfig.multiIndexFiles
191 self.
_multiInds = [MultiIndexCache.fromFilenameList(fnList)
for fnList
in indexFiles]
194 """Write a cache file 196 The cache file is a FITS file with all the required information to 197 build the AstrometryNetCatalog quickly. The first table extension 198 contains a row for each multiindex, storing the healpix and nside 199 values. The second table extension contains a row for each filename 200 in all the multiindexes. The two may be JOINed through the 'id' 204 numFilenames = sum(len(ind._filenameList)
for ind
in self.
_multiInds)
205 maxLength = max(len(fn)
for ind
in self.
_multiInds for fn
in ind._filenameList) + 1
208 first = fits.BinTableHDU.from_columns([fits.Column(name=
"id", format=
"K"),
209 fits.Column(name=
"healpix", format=
"K"),
210 fits.Column(name=
"nside", format=
"K"),
212 first.data.field(
"id")[:] = np.arange(len(self.
_multiInds), dtype=int)
213 first.data.field(
"healpix")[:] = np.array([ind._healpix
for ind
in self.
_multiInds])
214 first.data.field(
"nside")[:] = np.array([ind._nside
for ind
in self.
_multiInds])
217 second = fits.BinTableHDU.from_columns([fits.Column(name=
"id", format=
"K"),
218 fits.Column(name=
"filename", format=
"%dA" % (maxLength)),
219 ], nrows=numFilenames)
220 ident = second.data.field(
"id")
221 filenames = second.data.field(
"filename")
224 for fn
in ind._filenameList:
229 fits.HDUList([fits.PrimaryHDU(), first, second]).writeto(outName, overwrite=
True)
231 def _initFromCache(self, filename):
232 """Initialise from a cache file 234 Ingest the cache file written by the 'writeCache' method and 235 use that to quickly instantiate the AstrometryNetCatalog. 237 with fits.open(filename)
as hduList:
238 first = hduList[1].data
239 second = hduList[2].data
242 filenames = {i: []
for i
in first.field(
"id")}
243 for id2, fn
in zip(second.field(
"id"), second.field(
"filename")):
244 filenames[id2].append(fn)
246 zip(first.field(
"id"), first.field(
"healpix"), first.field(
"nside"))]
249 cacheFiles = set(second.field(
"filename"))
250 configFiles = set(sum(self.
config.multiIndexFiles, []) + self.
config.indexFiles)
251 assert(cacheFiles == configFiles)
264 """Generate a cache file""" 265 if andConfig
is None:
269 for index
in catalog:
273 for index
in catalog:
def _initFromIndexFiles(self, andConfig)
def _initFromCache(self, filename)
def isWithinRange(self, coord, distance)
Is the index within range of the provided coordinates?
std::string getPackageDir(std::string const &packageName)
def getIndexPath(fn)
Get the path to the specified astrometry.net index file.
lsst::geom::Angle healpixDistance(int hp, int nside, lsst::geom::SpherePoint const &coord)
Calculate the distance from coordinates to a healpix.
def generateCache(andConfig=None)
def __init__(self, filenameList, healpix, nside)
Constructor.
def getConfigFromEnvironment()
def __init__(self, andConfig)
Constructor.
def __getitem__(self, ii)
def fromFilenameList(cls, filenameList)