LSSTApplications  16.0-10-g0ee56ad+5,16.0-11-ga33d1f2+5,16.0-12-g3ef5c14+3,16.0-12-g71e5ef5+18,16.0-12-gbdf3636+3,16.0-13-g118c103+3,16.0-13-g8f68b0a+3,16.0-15-gbf5c1cb+4,16.0-16-gfd17674+3,16.0-17-g7c01f5c+3,16.0-18-g0a50484+1,16.0-20-ga20f992+8,16.0-21-g0e05fd4+6,16.0-21-g15e2d33+4,16.0-22-g62d8060+4,16.0-22-g847a80f+4,16.0-25-gf00d9b8+1,16.0-28-g3990c221+4,16.0-3-gf928089+3,16.0-32-g88a4f23+5,16.0-34-gd7987ad+3,16.0-37-gc7333cb+2,16.0-4-g10fc685+2,16.0-4-g18f3627+26,16.0-4-g5f3a788+26,16.0-5-gaf5c3d7+4,16.0-5-gcc1f4bb+1,16.0-6-g3b92700+4,16.0-6-g4412fcd+3,16.0-6-g7235603+4,16.0-69-g2562ce1b+2,16.0-8-g14ebd58+4,16.0-8-g2df868b+1,16.0-8-g4cec79c+6,16.0-8-gadf6c7a+1,16.0-8-gfc7ad86,16.0-82-g59ec2a54a+1,16.0-9-g5400cdc+2,16.0-9-ge6233d7+5,master-g2880f2d8cf+3,v17.0.rc1
LSSTDataManagementBasePackage
baseSkyMap.py
Go to the documentation of this file.
1 # LSST Data Management System
2 # Copyright 2008, 2009, 2010 LSST Corporation.
3 #
4 # This product includes software developed by the
5 # LSST Project (http://www.lsst.org/).
6 #
7 # This program is free software: you can redistribute it and/or modify
8 # it under the terms of the GNU General Public License as published by
9 # the Free Software Foundation, either version 3 of the License, or
10 # (at your option) any later version.
11 #
12 # This program is distributed in the hope that it will be useful,
13 # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 # GNU General Public License for more details.
16 #
17 # You should have received a copy of the LSST License Statement and
18 # the GNU General Public License along with this program. If not,
19 # see <http://www.lsstcorp.org/LegalNotices/>.
20 #
21 
22 """
23 todo: Consider tweaking pixel scale so the average scale is as specified,
24 rather than the scale at the center.
25 """
26 
27 __all__ = ["BaseSkyMapConfig", "BaseSkyMap"]
28 
29 import hashlib
30 import struct
31 
32 import lsst.pex.config as pexConfig
33 from lsst.geom import SpherePoint, Angle, arcseconds, degrees
34 from . import detail
35 
36 
37 class BaseSkyMapConfig(pexConfig.Config):
38  patchInnerDimensions = pexConfig.ListField(
39  doc="dimensions of inner region of patches (x,y pixels)",
40  dtype=int,
41  length=2,
42  default=(4000, 4000),
43  )
44  patchBorder = pexConfig.Field(
45  doc="border between patch inner and outer bbox (pixels)",
46  dtype=int,
47  default=100,
48  )
49  tractOverlap = pexConfig.Field(
50  doc="minimum overlap between adjacent sky tracts, on the sky (deg)",
51  dtype=float,
52  default=1.0,
53  )
54  pixelScale = pexConfig.Field(
55  doc="nominal pixel scale (arcsec/pixel)",
56  dtype=float,
57  default=0.333
58  )
59  projection = pexConfig.Field(
60  doc="one of the FITS WCS projection codes, such as:"
61  "- STG: stereographic projection"
62  "- MOL: Molleweide's projection"
63  "- TAN: tangent-plane projection",
64  dtype=str,
65  default="STG",
66  )
67  rotation = pexConfig.Field(
68  doc="Rotation for WCS (deg)",
69  dtype=float,
70  default=0,
71  )
72 
73 
74 class BaseSkyMap:
75  """A collection of overlapping Tracts that map part or all of the sky.
76 
77  See TractInfo for more information.
78 
79  Parameters
80  ----------
81  config : `BaseSkyMapConfig` or None (optional)
82  The configuration for this SkyMap; if None use the default config.
83 
84  Notes
85  -----
86  BaseSkyMap is an abstract base class. Subclasses must do the following:
87  define ``__init__`` and have it construct the TractInfo objects and put
88  them in ``__tractInfoList__`` define ``__getstate__`` and ``__setstate__``
89  to allow pickling (the butler saves sky maps using pickle);
90  see DodecaSkyMap for an example of how to do this. (Most of that code could
91  be moved into this base class, but that would make it harder to handle
92  older versions of pickle data.) define updateSha1 to add any
93  subclass-specific state to the hash.
94 
95  All SkyMap subclasses must be conceptually immutable; they must always
96  refer to the same set of mathematical tracts and patches even if the in-
97  memory representation of those objects changes.
98  """
99 
100  ConfigClass = BaseSkyMapConfig
101 
102  def __init__(self, config=None):
103  if config is None:
104  config = self.ConfigClass()
105  config.freeze() # just to be sure, e.g. for pickling
106  self.config = config
107  self._tractInfoList = []
109  pixelScale=Angle(self.config.pixelScale, arcseconds),
110  projection=self.config.projection,
111  rotation=Angle(self.config.rotation, degrees),
112  )
113  self._sha1 = None
114 
115  def findTract(self, coord):
116  """Find the tract whose center is nearest the specified coord.
117 
118  Parameters
119  ----------
120  coord : `lsst.geom.SpherePoint`
121  ICRS sky coordinate to search for.
122 
123  Returns
124  -------
125  result : `TractInfo`
126  TractInfo of tract whose center is nearest the specified coord.
127 
128  Notes
129  -----
130  - If coord is equidistant between multiple sky tract centers then one
131  is arbitrarily chosen.
132 
133  - The default implementation is not very efficient; subclasses may wish
134  to override.
135 
136  **Warning:**
137  If tracts do not cover the whole sky then the returned tract may not
138  include the coord.
139  """
140  distTractInfoList = []
141  for i, tractInfo in enumerate(self):
142  angSep = coord.separation(tractInfo.getCtrCoord()).asDegrees()
143  # include index in order to disambiguate identical angSep values
144  distTractInfoList.append((angSep, i, tractInfo))
145  distTractInfoList.sort()
146  return distTractInfoList[0][2]
147 
148  def findTractPatchList(self, coordList):
149  """Find tracts and patches that overlap a region.
150 
151  Parameters
152  ----------
153  coordList : `list` of `lsst.geom.SpherePoint`
154  List of ICRS sky coordinates to search for.
155 
156  Returns
157  -------
158  reList : `list` of (`TractInfo`, `list` of `PatchInfo`)
159  For tracts and patches that contain, or may contain, the specified
160  region. The list will be empty if there is no overlap.
161 
162  Notes
163  -----
164  **warning:**
165  This uses a naive algorithm that may find some tracts and patches
166  that do not overlap the region (especially if the region is not a
167  rectangle aligned along patch x, y).
168  """
169  retList = []
170  for tractInfo in self:
171  patchList = tractInfo.findPatchList(coordList)
172  if patchList:
173  retList.append((tractInfo, patchList))
174  return retList
175 
176  def findClosestTractPatchList(self, coordList):
177  """Find closest tract and patches that overlap coordinates.
178 
179  Parameters
180  ----------
181  coordList : `lsst.geom.SpherePoint`
182  List of ICRS sky coordinates to search for.
183 
184  Returns
185  -------
186  retList : `list`
187  list of (TractInfo, list of PatchInfo) for tracts and patches
188  that contain, or may contain, the specified region.
189  The list will be empty if there is no overlap.
190  """
191  retList = []
192  for coord in coordList:
193  tractInfo = self.findTract(coord)
194  patchList = tractInfo.findPatchList(coordList)
195  if patchList and not (tractInfo, patchList) in retList:
196  retList.append((tractInfo, patchList))
197  return retList
198 
199  def __getitem__(self, ind):
200  return self._tractInfoList[ind]
201 
202  def __iter__(self):
203  return iter(self._tractInfoList)
204 
205  def __len__(self):
206  return len(self._tractInfoList)
207 
208  def __hash__(self):
209  return hash(self.getSha1())
210 
211  def __eq__(self, other):
212  try:
213  return self.getSha1() == other.getSha1()
214  except AttributeError:
215  return NotImplemented
216 
217  def __ne__(self, other):
218  return not (self == other)
219 
220  def getSha1(self):
221  """Return a SHA1 hash that uniquely identifies this SkyMap instance.
222 
223  Returns
224  -------
225  sha1 : `bytes`
226  A 20-byte hash that uniquely identifies this SkyMap instance.
227 
228  Notes
229  -----
230  Subclasses should almost always override ``updateSha1`` instead of
231  this function to add subclass-specific state to the hash.
232  """
233  if self._sha1 is None:
234  sha1 = hashlib.sha1()
235  sha1.update(type(self).__name__.encode('utf-8'))
236  configPacked = struct.pack(
237  "<iiidd3sd",
238  self.config.patchInnerDimensions[0],
239  self.config.patchInnerDimensions[1],
240  self.config.patchBorder,
241  self.config.tractOverlap,
242  self.config.pixelScale,
243  self.config.projection.encode('ascii'),
244  self.config.rotation
245  )
246  sha1.update(configPacked)
247  self.updateSha1(sha1)
248  self._sha1 = sha1.digest()
249  return self._sha1
250 
251  def updateSha1(self, sha1):
252  """Add subclass-specific state or configuration options to the SHA1.
253 
254  Parameters
255  ----------
256  sha1 : `hashlib.sha1`
257  A hashlib object on which `update` can be called to add
258  additional state to the hash.
259 
260  Notes
261  -----
262  This method is conceptually "protected" : it should be reimplemented by
263  all subclasses, but called only by the base class implementation of
264  `getSha1` .
265  """
266  raise NotImplementedError()
267 
268  def register(self, name, registry):
269  """Add SkyMap, Tract, and Patch Dimension entries to the given Gen3
270  Butler Registry.
271 
272  Parameters
273  ----------
274  name : `str`
275  The name of the skymap.
276  registry : `lsst.daf.butler.Registry`
277  The registry to add to.
278  """
279  nxMax = 0
280  nyMax = 0
281  for tractInfo in self:
282  nx, ny = tractInfo.getNumPatches()
283  nxMax = max(nxMax, nx)
284  nyMax = max(nyMax, ny)
285  registry.addDimensionEntry(
286  "SkyMap",
287  {"skymap": name,
288  "hash": self.getSha1(),
289  "tract_max": len(self),
290  "patch_nx_max": nxMax,
291  "patch_ny_max": nyMax}
292  )
293  for tractInfo in self:
294  region = tractInfo.getOuterSkyPolygon()
295  centroid = SpherePoint(region.getCentroid())
296  registry.addDimensionEntry(
297  "Tract",
298  {"skymap": name, "tract": tractInfo.getId(),
299  "region": region,
300  "ra": centroid.getRa().asDegrees(),
301  "dec": centroid.getDec().asDegrees()}
302  )
303  for patchInfo in tractInfo:
304  cellX, cellY = patchInfo.getIndex()
305  registry.addDimensionEntry(
306  "Patch",
307  {"skymap": name, "tract": tractInfo.getId(),
308  "patch": tractInfo.getSequentialPatchIndex(patchInfo),
309  "cell_x": cellX, "cell_y": cellY,
310  "region": patchInfo.getOuterSkyPolygon(tractInfo.getWcs())}
311  )
def findTractPatchList(self, coordList)
Definition: baseSkyMap.py:148
A class representing an angle.
Definition: Angle.h:127
def findClosestTractPatchList(self, coordList)
Definition: baseSkyMap.py:176
table::Key< int > type
Definition: Detector.cc:164
int max
Point in an unspecified spherical coordinate system.
Definition: SpherePoint.h:57
def register(self, name, registry)
Definition: baseSkyMap.py:268
def __init__(self, config=None)
Definition: baseSkyMap.py:102