LSSTApplications  17.0+50,17.0+84,17.0+9,18.0.0+14,18.0.0+2,18.0.0+30,18.0.0+4,18.0.0+9,18.0.0-2-ge43143a+4,18.1.0-1-g0001055,18.1.0-1-g0896a44+6,18.1.0-1-g1349e88+4,18.1.0-1-g2505f39+3,18.1.0-1-g380d4d4+4,18.1.0-1-g5e4b7ea,18.1.0-1-g85f8cd4+3,18.1.0-1-g9a6769a+2,18.1.0-1-ga1a4c1a+2,18.1.0-1-gc037db8,18.1.0-1-gd55f500+1,18.1.0-1-ge10677a+3,18.1.0-10-g73b8679e+7,18.1.0-11-g311e899+3,18.1.0-12-g0d73a3591,18.1.0-12-gc95f69a+3,18.1.0-2-g000ad9a+3,18.1.0-2-g31c43f9+3,18.1.0-2-g9c63283+4,18.1.0-2-gdf0b915+4,18.1.0-2-gf03bb23,18.1.0-3-g2e29e3d+6,18.1.0-3-g52aa583+2,18.1.0-3-g9cb968e+3,18.1.0-4-gd2e8982+6,18.1.0-5-g510c42a+3,18.1.0-5-gaeab27e+4,18.1.0-6-gdda7f3e+6,18.1.0-7-g89824ecc+4,w.2019.32
LSSTDataManagementBasePackage
testUtils.py
Go to the documentation of this file.
1 #
2 # LSST Data Management System
3 # Copyright 2016 LSST Corporation.
4 #
5 # This product includes software developed by the
6 # LSST Project (http://www.lsst.org/).
7 #
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU General Public License as published by
10 # the Free Software Foundation, either version 3 of the License, or
11 # (at your option) any later version.
12 #
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
17 #
18 # You should have received a copy of the LSST License Statement and
19 # the GNU General Public License along with this program. If not,
20 # see <http://www.lsstcorp.org/LegalNotices/>.
21 #
22 
23 __all__ = ["BoxGrid", "makeSipIwcToPixel", "makeSipPixelToIwc"]
24 
25 import itertools
26 import math
27 import os
28 import pickle
29 
30 import astshim as ast
31 import numpy as np
32 from numpy.testing import assert_allclose, assert_array_equal
33 from astshim.test import makeForwardPolyMap, makeTwoWayPolyMap
34 from lsst.afw.geom.wcsUtils import getCdMatrixFromMetadata
35 
36 import lsst.geom
37 import lsst.afw.geom as afwGeom
38 from lsst.pex.exceptions import InvalidParameterError
39 import lsst.utils
40 import lsst.utils.tests
41 
42 
43 class BoxGrid:
44  """Divide a box into nx by ny sub-boxes that tile the region
45 
46  The sub-boxes will be of the same type as `box` and will exactly tile `box`;
47  they will also all be the same size, to the extent possible (some variation
48  is inevitable for integer boxes that cannot be evenly divided.
49 
50  Parameters
51  ----------
52  box : `lsst.geom.Box2I` or `lsst.geom.Box2D`
53  the box to subdivide; the boxes in the grid will be of the same type
54  numColRow : pair of `int`
55  number of columns and rows
56  """
57 
58  def __init__(self, box, numColRow):
59  if len(numColRow) != 2:
60  raise RuntimeError(
61  "numColRow=%r; must be a sequence of two integers" % (numColRow,))
62  self._numColRow = tuple(int(val) for val in numColRow)
63 
64  if isinstance(box, lsst.geom.Box2I):
65  stopDelta = 1
66  elif isinstance(box, lsst.geom.Box2D):
67  stopDelta = 0
68  else:
69  raise RuntimeError("Unknown class %s of box %s" % (type(box), box))
70  self.boxClass = type(box)
71  self.stopDelta = stopDelta
72 
73  minPoint = box.getMin()
74  self.pointClass = type(minPoint)
75  dtype = np.array(minPoint).dtype
76 
77  self._divList = [np.linspace(start=box.getMin()[i],
78  stop=box.getMax()[i] + self.stopDelta,
79  num=self._numColRow[i] + 1,
80  endpoint=True,
81  dtype=dtype) for i in range(2)]
82 
83  @property
84  def numColRow(self):
85  return self._numColRow
86 
87  def __getitem__(self, indXY):
88  """Return the box at the specified x,y index
89 
90  Parameters
91  ----------
92  indXY : pair of `ints`
93  the x,y index to return
94 
95  Returns
96  -------
97  subBox : `lsst.geom.Box2I` or `lsst.geom.Box2D`
98  """
99  beg = self.pointClass(*[self._divList[i][indXY[i]] for i in range(2)])
100  end = self.pointClass(
101  *[self._divList[i][indXY[i] + 1] - self.stopDelta for i in range(2)])
102  return self.boxClass(beg, end)
103 
104  def __len__(self):
105  return self.shape[0]*self.shape[1]
106 
107  def __iter__(self):
108  """Return an iterator over all boxes, where column varies most quickly
109  """
110  for row in range(self.numColRow[1]):
111  for col in range(self.numColRow[0]):
112  yield self[col, row]
113 
114 
116  """Information about a FrameSet
117 
118  Parameters
119  ----------
120  frameSet : `ast.FrameSet`
121  The FrameSet about which you want information
122 
123  Notes
124  -----
125  **Fields**
126 
127  baseInd : `int`
128  Index of base frame
129  currInd : `int`
130  Index of current frame
131  isBaseSkyFrame : `bool`
132  Is the base frame an `ast.SkyFrame`?
133  isCurrSkyFrame : `bool`
134  Is the current frame an `ast.SkyFrame`?
135  """
136  def __init__(self, frameSet):
137  self.baseInd = frameSet.base
138  self.currInd = frameSet.current
139  self.isBaseSkyFrame = frameSet.getFrame(self.baseInd).className == "SkyFrame"
140  self.isCurrSkyFrame = frameSet.getFrame(self.currInd).className == "SkyFrame"
141 
142 
143 def makeSipPolyMapCoeffs(metadata, name):
144  """Return a list of ast.PolyMap coefficients for the specified SIP matrix
145 
146  The returned list of coefficients for an ast.PolyMap
147  that computes the following function:
148 
149  f(dxy) = dxy + sipPolynomial(dxy))
150  where dxy = pixelPosition - pixelOrigin
151  and sipPolynomial is a polynomial with terms `<name>n_m for x^n y^m`
152  (e.g. A2_0 is the coefficient for x^2 y^0)
153 
154  Parameters
155  ----------
156  metadata : lsst.daf.base.PropertySet
157  FITS metadata describing a WCS with the specified SIP coefficients
158  name : str
159  The desired SIP terms: one of A, B, AP, BP
160 
161  Returns
162  -------
163  list
164  A list of coefficients for an ast.PolyMap that computes
165  the specified SIP polynomial, including a term for out = in
166 
167  Note
168  ----
169  This is an internal function for use by makeSipIwcToPixel
170  and makeSipPixelToIwc
171  """
172  outAxisDict = dict(A=1, B=2, AP=1, BP=2)
173  outAxis = outAxisDict.get(name)
174  if outAxis is None:
175  raise RuntimeError("%s not a supported SIP name" % (name,))
176  width = metadata.getAsInt("%s_ORDER" % (name,)) + 1
177  found = False
178  # start with a term for out = in
179  coeffs = []
180  if outAxis == 1:
181  coeffs.append([1.0, outAxis, 1, 0])
182  else:
183  coeffs.append([1.0, outAxis, 0, 1])
184  # add SIP distortion terms
185  for xPower in range(width):
186  for yPower in range(width):
187  coeffName = "%s_%s_%s" % (name, xPower, yPower)
188  if not metadata.exists(coeffName):
189  continue
190  found = True
191  coeff = metadata.getAsDouble(coeffName)
192  coeffs.append([coeff, outAxis, xPower, yPower])
193  if not found:
194  raise RuntimeError("No %s coefficients found" % (name,))
195  return coeffs
196 
197 
198 def makeSipIwcToPixel(metadata):
199  """Make an IWC to pixel transform with SIP distortion from FITS-WCS metadata
200 
201  This function is primarily intended for unit tests.
202  IWC is intermediate world coordinates, as described in the FITS papers.
203 
204  Parameters
205  ----------
206  metadata : lsst.daf.base.PropertySet
207  FITS metadata describing a WCS with inverse SIP coefficients
208 
209  Returns
210  -------
211  lsst.afw.geom.TransformPoint2ToPoint2
212  Transform from IWC position to pixel position (zero-based)
213  in the forward direction. The inverse direction is not defined.
214 
215  Notes
216  -----
217 
218  The inverse SIP terms APn_m, BPn_m are polynomial coefficients x^n y^m
219  for computing transformed x, y respectively. If we call the resulting
220  polynomial inverseSipPolynomial, the returned transformation is:
221 
222  pixelPosition = pixel origin + uv + inverseSipPolynomial(uv)
223  where uv = inverseCdMatrix * iwcPosition
224  """
225  crpix = (metadata.getScalar("CRPIX1") - 1, metadata.getScalar("CRPIX2") - 1)
226  pixelRelativeToAbsoluteMap = ast.ShiftMap(crpix)
227  cdMatrix = getCdMatrixFromMetadata(metadata)
228  cdMatrixMap = ast.MatrixMap(cdMatrix.copy())
229  coeffList = makeSipPolyMapCoeffs(metadata, "AP") + makeSipPolyMapCoeffs(metadata, "BP")
230  coeffArr = np.array(coeffList, dtype=float)
231  sipPolyMap = ast.PolyMap(coeffArr, 2, "IterInverse=0")
232 
233  iwcToPixelMap = cdMatrixMap.inverted().then(sipPolyMap).then(pixelRelativeToAbsoluteMap)
234  return afwGeom.TransformPoint2ToPoint2(iwcToPixelMap)
235 
236 
237 def makeSipPixelToIwc(metadata):
238  """Make a pixel to IWC transform with SIP distortion from FITS-WCS metadata
239 
240  This function is primarily intended for unit tests.
241  IWC is intermediate world coordinates, as described in the FITS papers.
242 
243  Parameters
244  ----------
245  metadata : lsst.daf.base.PropertySet
246  FITS metadata describing a WCS with forward SIP coefficients
247 
248  Returns
249  -------
250  lsst.afw.geom.TransformPoint2ToPoint2
251  Transform from pixel position (zero-based) to IWC position
252  in the forward direction. The inverse direction is not defined.
253 
254  Notes
255  -----
256 
257  The forward SIP terms An_m, Bn_m are polynomial coefficients x^n y^m
258  for computing transformed x, y respectively. If we call the resulting
259  polynomial sipPolynomial, the returned transformation is:
260 
261  iwcPosition = cdMatrix * (dxy + sipPolynomial(dxy))
262  where dxy = pixelPosition - pixelOrigin
263  """
264  crpix = (metadata.getScalar("CRPIX1") - 1, metadata.getScalar("CRPIX2") - 1)
265  pixelAbsoluteToRelativeMap = ast.ShiftMap(crpix).inverted()
266  cdMatrix = getCdMatrixFromMetadata(metadata)
267  cdMatrixMap = ast.MatrixMap(cdMatrix.copy())
268  coeffList = makeSipPolyMapCoeffs(metadata, "A") + makeSipPolyMapCoeffs(metadata, "B")
269  coeffArr = np.array(coeffList, dtype=float)
270  sipPolyMap = ast.PolyMap(coeffArr, 2, "IterInverse=0")
271  pixelToIwcMap = pixelAbsoluteToRelativeMap.then(sipPolyMap).then(cdMatrixMap)
272  return afwGeom.TransformPoint2ToPoint2(pixelToIwcMap)
273 
274 
276  """A FrameSet with base or current frame possibly permuted, with associated
277  information
278 
279  Only two-axis frames will be permuted.
280 
281  Parameters
282  ----------
283  frameSet : `ast.FrameSet`
284  The FrameSet you wish to permute. A deep copy is made.
285  permuteBase : `bool`
286  Permute the base frame's axes?
287  permuteCurr : `bool`
288  Permute the current frame's axes?
289 
290  Raises
291  ------
292  RuntimeError
293  If you try to permute a frame that does not have 2 axes
294 
295  Notes
296  -----
297  **Fields**
298 
299  frameSet : `ast.FrameSet`
300  The FrameSet that may be permuted. A local copy is made.
301  isBaseSkyFrame : `bool`
302  Is the base frame an `ast.SkyFrame`?
303  isCurrSkyFrame : `bool`
304  Is the current frame an `ast.SkyFrame`?
305  isBasePermuted : `bool`
306  Are the base frame axes permuted?
307  isCurrPermuted : `bool`
308  Are the current frame axes permuted?
309  """
310  def __init__(self, frameSet, permuteBase, permuteCurr):
311  self.frameSet = frameSet.copy()
312  fsInfo = FrameSetInfo(self.frameSet)
313  self.isBaseSkyFrame = fsInfo.isBaseSkyFrame
314  self.isCurrSkyFrame = fsInfo.isCurrSkyFrame
315  if permuteBase:
316  baseNAxes = self.frameSet.getFrame(fsInfo.baseInd).nAxes
317  if baseNAxes != 2:
318  raise RuntimeError("Base frame has {} axes; 2 required to permute".format(baseNAxes))
319  self.frameSet.current = fsInfo.baseInd
320  self.frameSet.permAxes([2, 1])
321  self.frameSet.current = fsInfo.currInd
322  if permuteCurr:
323  currNAxes = self.frameSet.getFrame(fsInfo.currInd).nAxes
324  if currNAxes != 2:
325  raise RuntimeError("Current frame has {} axes; 2 required to permute".format(currNAxes))
326  assert self.frameSet.getFrame(fsInfo.currInd).nAxes == 2
327  self.frameSet.permAxes([2, 1])
328  self.isBasePermuted = permuteBase
329  self.isCurrPermuted = permuteCurr
330 
331 
333  """Base class for unit tests of Transform<X>To<Y>
334 
335  Subclasses must call `TransformTestBaseClass.setUp(self)`
336  if they provide their own version.
337 
338  If a package other than afw uses this class then it must
339  override the `getTestDir` method to avoid writing into
340  afw's test directory.
341  """
342 
343  def getTestDir(self):
344  """Return a directory where temporary test files can be written
345 
346  The default implementation returns the test directory of the `afw`
347  package.
348 
349  If this class is used by a test in a package other than `afw`
350  then the subclass must override this method.
351  """
352  return os.path.join(lsst.utils.getPackageDir("afw"), "tests")
353 
354  def setUp(self):
355  """Set up a test
356 
357  Subclasses should call this method if they override setUp.
358  """
359  # tell unittest to use the msg argument of asserts as a supplement
360  # to the error message, rather than as the whole error message
361  self.longMessage = True
362 
363  # list of endpoint class name prefixes; the full name is prefix + "Endpoint"
364  self.endpointPrefixes = ("Generic", "Point2", "SpherePoint")
365 
366  # GoodNAxes is dict of endpoint class name prefix:
367  # tuple containing 0 or more valid numbers of axes
368  self.goodNAxes = {
369  "Generic": (1, 2, 3, 4), # all numbers of axes are valid for GenericEndpoint
370  "Point2": (2,),
371  "SpherePoint": (2,),
372  }
373 
374  # BadAxes is dict of endpoint class name prefix:
375  # tuple containing 0 or more invalid numbers of axes
376  self.badNAxes = {
377  "Generic": (), # all numbers of axes are valid for GenericEndpoint
378  "Point2": (1, 3, 4),
379  "SpherePoint": (1, 3, 4),
380  }
381 
382  # Dict of frame index: identity name for frames created by makeFrameSet
383  self.frameIdentDict = {
384  1: "baseFrame",
385  2: "frame2",
386  3: "frame3",
387  4: "currFrame",
388  }
389 
390  @staticmethod
391  def makeRawArrayData(nPoints, nAxes, delta=0.123):
392  """Make an array of generic point data
393 
394  The data will be suitable for spherical points
395 
396  Parameters
397  ----------
398  nPoints : `int`
399  Number of points in the array
400  nAxes : `int`
401  Number of axes in the point
402 
403  Returns
404  -------
405  np.array of floats with shape (nAxes, nPoints)
406  The values are as follows; if nAxes != 2:
407  The first point has values `[0, delta, 2*delta, ..., (nAxes-1)*delta]`
408  The Nth point has those values + N
409  if nAxes == 2 then the data is scaled so that the max value of axis 1
410  is a bit less than pi/2
411  """
412  delta = 0.123
413  # oneAxis = [0, 1, 2, ...nPoints-1]
414  oneAxis = np.arange(nPoints, dtype=float) # [0, 1, 2...]
415  # rawData = [oneAxis, oneAxis + delta, oneAxis + 2 delta, ...]
416  rawData = np.array([j * delta + oneAxis for j in range(nAxes)], dtype=float)
417  if nAxes == 2:
418  # scale rawData so that max value of 2nd axis is a bit less than pi/2,
419  # thus making the data safe for SpherePoint
420  maxLatitude = np.max(rawData[1])
421  rawData *= math.pi * 0.4999 / maxLatitude
422  return rawData
423 
424  @staticmethod
425  def makeRawPointData(nAxes, delta=0.123):
426  """Make one generic point
427 
428  Parameters
429  ----------
430  nAxes : `int`
431  Number of axes in the point
432  delta : `float`
433  Increment between axis values
434 
435  Returns
436  -------
437  A list of `nAxes` floats with values `[0, delta, ..., (nAxes-1)*delta]
438  """
439  return [i*delta for i in range(nAxes)]
440 
441  @staticmethod
442  def makeEndpoint(name, nAxes=None):
443  """Make an endpoint
444 
445  Parameters
446  ----------
447  name : `str`
448  Endpoint class name prefix; the full class name is name + "Endpoint"
449  nAxes : `int` or `None`, optional
450  number of axes; an int is required if `name` == "Generic";
451  otherwise ignored
452 
453  Returns
454  -------
455  subclass of `lsst.afw.geom.BaseEndpoint`
456  The constructed endpoint
457 
458  Raises
459  ------
460  TypeError
461  If `name` == "Generic" and `nAxes` is None or <= 0
462  """
463  EndpointClassName = name + "Endpoint"
464  EndpointClass = getattr(afwGeom, EndpointClassName)
465  if name == "Generic":
466  if nAxes is None:
467  raise TypeError("nAxes must be an integer for GenericEndpoint")
468  return EndpointClass(nAxes)
469  return EndpointClass()
470 
471  @classmethod
472  def makeGoodFrame(cls, name, nAxes=None):
473  """Return the appropriate frame for the given name and nAxes
474 
475  Parameters
476  ----------
477  name : `str`
478  Endpoint class name prefix; the full class name is name + "Endpoint"
479  nAxes : `int` or `None`, optional
480  number of axes; an int is required if `name` == "Generic";
481  otherwise ignored
482 
483  Returns
484  -------
485  `ast.Frame`
486  The constructed frame
487 
488  Raises
489  ------
490  TypeError
491  If `name` == "Generic" and `nAxes` is `None` or <= 0
492  """
493  return cls.makeEndpoint(name, nAxes).makeFrame()
494 
495  @staticmethod
496  def makeBadFrames(name):
497  """Return a list of 0 or more frames that are not a valid match for the
498  named endpoint
499 
500  Parameters
501  ----------
502  name : `str`
503  Endpoint class name prefix; the full class name is name + "Endpoint"
504 
505  Returns
506  -------
507  Collection of `ast.Frame`
508  A collection of 0 or more frames
509  """
510  return {
511  "Generic": [],
512  "Point2": [
513  ast.SkyFrame(),
514  ast.Frame(1),
515  ast.Frame(3),
516  ],
517  "SpherePoint": [
518  ast.Frame(1),
519  ast.Frame(2),
520  ast.Frame(3),
521  ],
522  }[name]
523 
524  def makeFrameSet(self, baseFrame, currFrame):
525  """Make a FrameSet
526 
527  The FrameSet will contain 4 frames and three transforms connecting them.
528  The idenity of each frame is provided by self.frameIdentDict
529 
530  Frame Index Mapping from this frame to the next
531  `baseFrame` 1 `ast.UnitMap(nIn)`
532  Frame(nIn) 2 `polyMap`
533  Frame(nOut) 3 `ast.UnitMap(nOut)`
534  `currFrame` 4
535 
536  where:
537  - `nIn` = `baseFrame.nAxes`
538  - `nOut` = `currFrame.nAxes`
539  - `polyMap` = `makeTwoWayPolyMap(nIn, nOut)`
540 
541  Returns
542  ------
543  `ast.FrameSet`
544  The FrameSet as described above
545 
546  Parameters
547  ----------
548  baseFrame : `ast.Frame`
549  base frame
550  currFrame : `ast.Frame`
551  current frame
552  """
553  nIn = baseFrame.nAxes
554  nOut = currFrame.nAxes
555  polyMap = makeTwoWayPolyMap(nIn, nOut)
556 
557  # The only way to set the Ident of a frame in a FrameSet is to set it in advance,
558  # and I don't want to modify the inputs, so replace the input frames with copies
559  baseFrame = baseFrame.copy()
560  baseFrame.ident = self.frameIdentDict[1]
561  currFrame = currFrame.copy()
562  currFrame.ident = self.frameIdentDict[4]
563 
564  frameSet = ast.FrameSet(baseFrame)
565  frame2 = ast.Frame(nIn)
566  frame2.ident = self.frameIdentDict[2]
567  frameSet.addFrame(ast.FrameSet.CURRENT, ast.UnitMap(nIn), frame2)
568  frame3 = ast.Frame(nOut)
569  frame3.ident = self.frameIdentDict[3]
570  frameSet.addFrame(ast.FrameSet.CURRENT, polyMap, frame3)
571  frameSet.addFrame(ast.FrameSet.CURRENT, ast.UnitMap(nOut), currFrame)
572  return frameSet
573 
574  @staticmethod
575  def permuteFrameSetIter(frameSet):
576  """Iterator over 0 or more frameSets with SkyFrames axes permuted
577 
578  Only base and current SkyFrames are permuted. If neither the base nor
579  the current frame is a SkyFrame then no frames are returned.
580 
581  Returns
582  -------
583  iterator over `PermutedFrameSet`
584  """
585 
586  fsInfo = FrameSetInfo(frameSet)
587  if not (fsInfo.isBaseSkyFrame or fsInfo.isCurrSkyFrame):
588  return
589 
590  permuteBaseList = [False, True] if fsInfo.isBaseSkyFrame else [False]
591  permuteCurrList = [False, True] if fsInfo.isCurrSkyFrame else [False]
592  for permuteBase in permuteBaseList:
593  for permuteCurr in permuteCurrList:
594  yield PermutedFrameSet(frameSet, permuteBase, permuteCurr)
595 
596  @staticmethod
597  def makeJacobian(nIn, nOut, inPoint):
598  """Make a Jacobian matrix for the equation described by
599  `makeTwoWayPolyMap`.
600 
601  Parameters
602  ----------
603  nIn, nOut : `int`
604  the dimensions of the input and output data; see makeTwoWayPolyMap
605  inPoint : `numpy.ndarray`
606  an array of size `nIn` representing the point at which the Jacobian
607  is measured
608 
609  Returns
610  -------
611  J : `numpy.ndarray`
612  an `nOut` x `nIn` array of first derivatives
613  """
614  basePolyMapCoeff = 0.001 # see makeTwoWayPolyMap
615  baseCoeff = 2.0 * basePolyMapCoeff
616  coeffs = np.empty((nOut, nIn))
617  for iOut in range(nOut):
618  coeffOffset = baseCoeff * iOut
619  for iIn in range(nIn):
620  coeffs[iOut, iIn] = baseCoeff * (iIn + 1) + coeffOffset
621  coeffs[iOut, iIn] *= inPoint[iIn]
622  assert coeffs.ndim == 2
623  # Avoid spurious errors when comparing to a simplified array
624  assert coeffs.shape == (nOut, nIn)
625  return coeffs
626 
627  def checkTransformation(self, transform, mapping, msg=""):
628  """Check applyForward and applyInverse for a transform
629 
630  Parameters
631  ----------
632  transform : `lsst.afw.geom.Transform`
633  The transform to check
634  mapping : `ast.Mapping`
635  The mapping the transform should use. This mapping
636  must contain valid forward or inverse transformations,
637  but they need not match if both present. Hence the
638  mappings returned by make*PolyMap are acceptable.
639  msg : `str`
640  Error message suffix describing test parameters
641  """
642  fromEndpoint = transform.fromEndpoint
643  toEndpoint = transform.toEndpoint
644  mappingFromTransform = transform.getMapping()
645 
646  nIn = mapping.nIn
647  nOut = mapping.nOut
648  self.assertEqual(nIn, fromEndpoint.nAxes, msg=msg)
649  self.assertEqual(nOut, toEndpoint.nAxes, msg=msg)
650 
651  # forward transformation of one point
652  rawInPoint = self.makeRawPointData(nIn)
653  inPoint = fromEndpoint.pointFromData(rawInPoint)
654 
655  # forward transformation of an array of points
656  nPoints = 7 # arbitrary
657  rawInArray = self.makeRawArrayData(nPoints, nIn)
658  inArray = fromEndpoint.arrayFromData(rawInArray)
659 
660  if mapping.hasForward:
661  self.assertTrue(transform.hasForward)
662  outPoint = transform.applyForward(inPoint)
663  rawOutPoint = toEndpoint.dataFromPoint(outPoint)
664  assert_allclose(rawOutPoint, mapping.applyForward(rawInPoint), err_msg=msg)
665  assert_allclose(rawOutPoint, mappingFromTransform.applyForward(rawInPoint), err_msg=msg)
666 
667  outArray = transform.applyForward(inArray)
668  rawOutArray = toEndpoint.dataFromArray(outArray)
669  self.assertFloatsAlmostEqual(rawOutArray, mapping.applyForward(rawInArray), msg=msg)
670  self.assertFloatsAlmostEqual(rawOutArray, mappingFromTransform.applyForward(rawInArray), msg=msg)
671  else:
672  # Need outPoint, but don't need it to be consistent with inPoint
673  rawOutPoint = self.makeRawPointData(nOut)
674  outPoint = toEndpoint.pointFromData(rawOutPoint)
675  rawOutArray = self.makeRawArrayData(nPoints, nOut)
676  outArray = toEndpoint.arrayFromData(rawOutArray)
677 
678  self.assertFalse(transform.hasForward)
679 
680  if mapping.hasInverse:
681  self.assertTrue(transform.hasInverse)
682  # inverse transformation of one point;
683  # remember that the inverse need not give the original values
684  # (see the description of the `mapping` parameter)
685  inversePoint = transform.applyInverse(outPoint)
686  rawInversePoint = fromEndpoint.dataFromPoint(inversePoint)
687  assert_allclose(rawInversePoint, mapping.applyInverse(rawOutPoint), err_msg=msg)
688  assert_allclose(rawInversePoint, mappingFromTransform.applyInverse(rawOutPoint), err_msg=msg)
689 
690  # inverse transformation of an array of points;
691  # remember that the inverse will not give the original values
692  # (see the description of the `mapping` parameter)
693  inverseArray = transform.applyInverse(outArray)
694  rawInverseArray = fromEndpoint.dataFromArray(inverseArray)
695  self.assertFloatsAlmostEqual(rawInverseArray, mapping.applyInverse(rawOutArray), msg=msg)
696  self.assertFloatsAlmostEqual(rawInverseArray, mappingFromTransform.applyInverse(rawOutArray),
697  msg=msg)
698  else:
699  self.assertFalse(transform.hasInverse)
700 
701  def checkInverseTransformation(self, forward, inverse, msg=""):
702  """Check that two Transforms are each others' inverses.
703 
704  Parameters
705  ----------
706  forward : `lsst.afw.geom.Transform`
707  the reference Transform to test
708  inverse : `lsst.afw.geom.Transform`
709  the transform that should be the inverse of `forward`
710  msg : `str`
711  error message suffix describing test parameters
712  """
713  fromEndpoint = forward.fromEndpoint
714  toEndpoint = forward.toEndpoint
715  forwardMapping = forward.getMapping()
716  inverseMapping = inverse.getMapping()
717 
718  # properties
719  self.assertEqual(forward.fromEndpoint,
720  inverse.toEndpoint, msg=msg)
721  self.assertEqual(forward.toEndpoint,
722  inverse.fromEndpoint, msg=msg)
723  self.assertEqual(forward.hasForward, inverse.hasInverse, msg=msg)
724  self.assertEqual(forward.hasInverse, inverse.hasForward, msg=msg)
725 
726  # transformations of one point
727  # we don't care about whether the transformation itself is correct
728  # (see checkTransformation), so inPoint/outPoint need not be related
729  rawInPoint = self.makeRawPointData(fromEndpoint.nAxes)
730  inPoint = fromEndpoint.pointFromData(rawInPoint)
731  rawOutPoint = self.makeRawPointData(toEndpoint.nAxes)
732  outPoint = toEndpoint.pointFromData(rawOutPoint)
733 
734  # transformations of arrays of points
735  nPoints = 7 # arbitrary
736  rawInArray = self.makeRawArrayData(nPoints, fromEndpoint.nAxes)
737  inArray = fromEndpoint.arrayFromData(rawInArray)
738  rawOutArray = self.makeRawArrayData(nPoints, toEndpoint.nAxes)
739  outArray = toEndpoint.arrayFromData(rawOutArray)
740 
741  if forward.hasForward:
742  self.assertEqual(forward.applyForward(inPoint),
743  inverse.applyInverse(inPoint), msg=msg)
744  self.assertEqual(forwardMapping.applyForward(rawInPoint),
745  inverseMapping.applyInverse(rawInPoint), msg=msg)
746  # Assertions must work with both lists and numpy arrays
747  assert_array_equal(forward.applyForward(inArray),
748  inverse.applyInverse(inArray),
749  err_msg=msg)
750  assert_array_equal(forwardMapping.applyForward(rawInArray),
751  inverseMapping.applyInverse(rawInArray),
752  err_msg=msg)
753 
754  if forward.hasInverse:
755  self.assertEqual(forward.applyInverse(outPoint),
756  inverse.applyForward(outPoint), msg=msg)
757  self.assertEqual(forwardMapping.applyInverse(rawOutPoint),
758  inverseMapping.applyForward(rawOutPoint), msg=msg)
759  assert_array_equal(forward.applyInverse(outArray),
760  inverse.applyForward(outArray),
761  err_msg=msg)
762  assert_array_equal(forwardMapping.applyInverse(rawOutArray),
763  inverseMapping.applyForward(rawOutArray),
764  err_msg=msg)
765 
766  def checkTransformFromMapping(self, fromName, toName):
767  """Check Transform_<fromName>_<toName> using the Mapping constructor
768 
769  Parameters
770  ----------
771  fromName, toName : `str`
772  Endpoint name prefix for "from" and "to" endpoints, respectively,
773  e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
774  fromAxes, toAxes : `int`
775  number of axes in fromFrame and toFrame, respectively
776  """
777  transformClassName = "Transform{}To{}".format(fromName, toName)
778  TransformClass = getattr(afwGeom, transformClassName)
779  baseMsg = "TransformClass={}".format(TransformClass.__name__)
780 
781  # check valid numbers of inputs and outputs
782  for nIn, nOut in itertools.product(self.goodNAxes[fromName],
783  self.goodNAxes[toName]):
784  msg = "{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
785  polyMap = makeTwoWayPolyMap(nIn, nOut)
786  transform = TransformClass(polyMap)
787 
788  # desired output from `str(transform)`
789  desStr = "{}[{}->{}]".format(transformClassName, nIn, nOut)
790  self.assertEqual("{}".format(transform), desStr)
791  self.assertEqual(repr(transform), "lsst.afw.geom." + desStr)
792 
793  self.checkTransformation(transform, polyMap, msg=msg)
794 
795  # Forward transform but no inverse
796  polyMap = makeForwardPolyMap(nIn, nOut)
797  transform = TransformClass(polyMap)
798  self.checkTransformation(transform, polyMap, msg=msg)
799 
800  # Inverse transform but no forward
801  polyMap = makeForwardPolyMap(nOut, nIn).inverted()
802  transform = TransformClass(polyMap)
803  self.checkTransformation(transform, polyMap, msg=msg)
804 
805  # check invalid # of output against valid # of inputs
806  for nIn, badNOut in itertools.product(self.goodNAxes[fromName],
807  self.badNAxes[toName]):
808  badPolyMap = makeTwoWayPolyMap(nIn, badNOut)
809  msg = "{}, nIn={}, badNOut={}".format(baseMsg, nIn, badNOut)
810  with self.assertRaises(InvalidParameterError, msg=msg):
811  TransformClass(badPolyMap)
812 
813  # check invalid # of inputs against valid and invalid # of outputs
814  for badNIn, nOut in itertools.product(self.badNAxes[fromName],
815  self.goodNAxes[toName] + self.badNAxes[toName]):
816  badPolyMap = makeTwoWayPolyMap(badNIn, nOut)
817  msg = "{}, badNIn={}, nOut={}".format(baseMsg, nIn, nOut)
818  with self.assertRaises(InvalidParameterError, msg=msg):
819  TransformClass(badPolyMap)
820 
821  def checkTransformFromFrameSet(self, fromName, toName):
822  """Check Transform_<fromName>_<toName> using the FrameSet constructor
823 
824  Parameters
825  ----------
826  fromName, toName : `str`
827  Endpoint name prefix for "from" and "to" endpoints, respectively,
828  e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
829  """
830  transformClassName = "Transform{}To{}".format(fromName, toName)
831  TransformClass = getattr(afwGeom, transformClassName)
832  baseMsg = "TransformClass={}".format(TransformClass.__name__)
833  for nIn, nOut in itertools.product(self.goodNAxes[fromName],
834  self.goodNAxes[toName]):
835  msg = "{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
836 
837  baseFrame = self.makeGoodFrame(fromName, nIn)
838  currFrame = self.makeGoodFrame(toName, nOut)
839  frameSet = self.makeFrameSet(baseFrame, currFrame)
840  self.assertEqual(frameSet.nFrame, 4)
841 
842  # construct 0 or more frame sets that are invalid for this transform class
843  for badBaseFrame in self.makeBadFrames(fromName):
844  badFrameSet = self.makeFrameSet(badBaseFrame, currFrame)
845  with self.assertRaises(InvalidParameterError):
846  TransformClass(badFrameSet)
847  for badCurrFrame in self.makeBadFrames(toName):
848  reallyBadFrameSet = self.makeFrameSet(badBaseFrame, badCurrFrame)
849  with self.assertRaises(InvalidParameterError):
850  TransformClass(reallyBadFrameSet)
851  for badCurrFrame in self.makeBadFrames(toName):
852  badFrameSet = self.makeFrameSet(baseFrame, badCurrFrame)
853  with self.assertRaises(InvalidParameterError):
854  TransformClass(badFrameSet)
855 
856  transform = TransformClass(frameSet)
857 
858  desStr = "{}[{}->{}]".format(transformClassName, nIn, nOut)
859  self.assertEqual("{}".format(transform), desStr)
860  self.assertEqual(repr(transform), "lsst.afw.geom." + desStr)
861 
862  self.checkPersistence(transform)
863 
864  mappingFromTransform = transform.getMapping()
865  transformCopy = TransformClass(mappingFromTransform)
866  self.assertEqual(type(transform), type(transformCopy))
867  self.assertEqual(transform.getMapping(), mappingFromTransform)
868 
869  polyMap = makeTwoWayPolyMap(nIn, nOut)
870 
871  self.checkTransformation(transform, mapping=polyMap, msg=msg)
872 
873  # If the base and/or current frame of frameSet is a SkyFrame,
874  # try permuting that frame (in place, so the connected mappings are
875  # correctly updated). The Transform constructor should undo the permutation,
876  # (via SpherePointEndpoint.normalizeFrame) in its internal copy of frameSet,
877  # forcing the axes of the SkyFrame into standard (longitude, latitude) order
878  for permutedFS in self.permuteFrameSetIter(frameSet):
879  if permutedFS.isBaseSkyFrame:
880  baseFrame = permutedFS.frameSet.getFrame(ast.FrameSet.BASE)
881  # desired base longitude axis
882  desBaseLonAxis = 2 if permutedFS.isBasePermuted else 1
883  self.assertEqual(baseFrame.lonAxis, desBaseLonAxis)
884  if permutedFS.isCurrSkyFrame:
885  currFrame = permutedFS.frameSet.getFrame(ast.FrameSet.CURRENT)
886  # desired current base longitude axis
887  desCurrLonAxis = 2 if permutedFS.isCurrPermuted else 1
888  self.assertEqual(currFrame.lonAxis, desCurrLonAxis)
889 
890  permTransform = TransformClass(permutedFS.frameSet)
891  self.checkTransformation(permTransform, mapping=polyMap, msg=msg)
892 
893  def checkInverted(self, fromName, toName):
894  """Test Transform<fromName>To<toName>.inverted
895 
896  Parameters
897  ----------
898  fromName, toName : `str`
899  Endpoint name prefix for "from" and "to" endpoints, respectively,
900  e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
901  """
902  transformClassName = "Transform{}To{}".format(fromName, toName)
903  TransformClass = getattr(afwGeom, transformClassName)
904  baseMsg = "TransformClass={}".format(TransformClass.__name__)
905  for nIn, nOut in itertools.product(self.goodNAxes[fromName],
906  self.goodNAxes[toName]):
907  msg = "{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
908  self.checkInverseMapping(
909  TransformClass,
910  makeTwoWayPolyMap(nIn, nOut),
911  "{}, Map={}".format(msg, "TwoWay"))
912  self.checkInverseMapping(
913  TransformClass,
914  makeForwardPolyMap(nIn, nOut),
915  "{}, Map={}".format(msg, "Forward"))
916  self.checkInverseMapping(
917  TransformClass,
918  makeForwardPolyMap(nOut, nIn).inverted(),
919  "{}, Map={}".format(msg, "Inverse"))
920 
921  def checkInverseMapping(self, TransformClass, mapping, msg):
922  """Test Transform<fromName>To<toName>.inverted for a specific
923  mapping.
924 
925  Also check that inverted() and getInverted() return the same
926  transform.
927 
928  Parameters
929  ----------
930  TransformClass : `type`
931  The class of transform to test, such as TransformPoint2ToPoint2
932  mapping : `ast.Mapping`
933  The mapping to use for the transform
934  msg : `str`
935  Error message suffix
936  """
937  transform = TransformClass(mapping)
938  inverse = transform.inverted()
939  inverseInverse = inverse.inverted()
940 
941  self.checkInverseTransformation(transform, inverse, msg=msg)
942  self.checkInverseTransformation(inverse, inverseInverse, msg=msg)
943  self.checkTransformation(inverseInverse, mapping, msg=msg)
944 
945  def checkGetJacobian(self, fromName, toName):
946  """Test Transform<fromName>To<toName>.getJacobian
947 
948  Parameters
949  ----------
950  fromName, toName : `str`
951  Endpoint name prefix for "from" and "to" endpoints, respectively,
952  e.g. "Point2" for `lsst.afw.geom.Point2Endpoint`
953  """
954  transformClassName = "Transform{}To{}".format(fromName, toName)
955  TransformClass = getattr(afwGeom, transformClassName)
956  baseMsg = "TransformClass={}".format(TransformClass.__name__)
957  for nIn, nOut in itertools.product(self.goodNAxes[fromName],
958  self.goodNAxes[toName]):
959  msg = "{}, nIn={}, nOut={}".format(baseMsg, nIn, nOut)
960  polyMap = makeForwardPolyMap(nIn, nOut)
961  transform = TransformClass(polyMap)
962  fromEndpoint = transform.fromEndpoint
963 
964  # Test multiple points to ensure correct functional form
965  rawInPoint = self.makeRawPointData(nIn)
966  inPoint = fromEndpoint.pointFromData(rawInPoint)
967  jacobian = transform.getJacobian(inPoint)
968  assert_allclose(jacobian, self.makeJacobian(nIn, nOut, rawInPoint),
969  err_msg=msg)
970 
971  rawInPoint = self.makeRawPointData(nIn, 0.111)
972  inPoint = fromEndpoint.pointFromData(rawInPoint)
973  jacobian = transform.getJacobian(inPoint)
974  assert_allclose(jacobian, self.makeJacobian(nIn, nOut, rawInPoint),
975  err_msg=msg)
976 
977  def checkThen(self, fromName, midName, toName):
978  """Test Transform<fromName>To<midName>.then(Transform<midName>To<toName>)
979 
980  Parameters
981  ----------
982  fromName : `str`
983  the prefix of the starting endpoint (e.g., "Point2" for a
984  Point2Endpoint) for the final, concatenated Transform
985  midName : `str`
986  the prefix for the shared endpoint where two Transforms will be
987  concatenated
988  toName : `str`
989  the prefix of the ending endpoint for the final, concatenated
990  Transform
991  """
992  TransformClass1 = getattr(afwGeom,
993  "Transform{}To{}".format(fromName, midName))
994  TransformClass2 = getattr(afwGeom,
995  "Transform{}To{}".format(midName, toName))
996  baseMsg = "{}.then({})".format(TransformClass1.__name__,
997  TransformClass2.__name__)
998  for nIn, nMid, nOut in itertools.product(self.goodNAxes[fromName],
999  self.goodNAxes[midName],
1000  self.goodNAxes[toName]):
1001  msg = "{}, nIn={}, nMid={}, nOut={}".format(
1002  baseMsg, nIn, nMid, nOut)
1003  polyMap1 = makeTwoWayPolyMap(nIn, nMid)
1004  transform1 = TransformClass1(polyMap1)
1005  polyMap2 = makeTwoWayPolyMap(nMid, nOut)
1006  transform2 = TransformClass2(polyMap2)
1007  transform = transform1.then(transform2)
1008 
1009  fromEndpoint = transform1.fromEndpoint
1010  toEndpoint = transform2.toEndpoint
1011 
1012  inPoint = fromEndpoint.pointFromData(self.makeRawPointData(nIn))
1013  outPointMerged = transform.applyForward(inPoint)
1014  outPointSeparate = transform2.applyForward(
1015  transform1.applyForward(inPoint))
1016  assert_allclose(toEndpoint.dataFromPoint(outPointMerged),
1017  toEndpoint.dataFromPoint(outPointSeparate),
1018  err_msg=msg)
1019 
1020  outPoint = toEndpoint.pointFromData(self.makeRawPointData(nOut))
1021  inPointMerged = transform.applyInverse(outPoint)
1022  inPointSeparate = transform1.applyInverse(
1023  transform2.applyInverse(outPoint))
1024  assert_allclose(
1025  fromEndpoint.dataFromPoint(inPointMerged),
1026  fromEndpoint.dataFromPoint(inPointSeparate),
1027  err_msg=msg)
1028 
1029  # Mismatched number of axes should fail
1030  if midName == "Generic":
1031  nIn = self.goodNAxes[fromName][0]
1032  nOut = self.goodNAxes[toName][0]
1033  polyMap = makeTwoWayPolyMap(nIn, 3)
1034  transform1 = TransformClass1(polyMap)
1035  polyMap = makeTwoWayPolyMap(2, nOut)
1036  transform2 = TransformClass2(polyMap)
1037  with self.assertRaises(InvalidParameterError):
1038  transform = transform1.then(transform2)
1039 
1040  # Mismatched types of endpoints should fail
1041  if fromName != midName:
1042  # Use TransformClass1 for both args to keep test logic simple
1043  outName = midName
1044  joinNAxes = set(self.goodNAxes[fromName]).intersection(
1045  self.goodNAxes[outName])
1046 
1047  for nIn, nMid, nOut in itertools.product(self.goodNAxes[fromName],
1048  joinNAxes,
1049  self.goodNAxes[outName]):
1050  polyMap = makeTwoWayPolyMap(nIn, nMid)
1051  transform1 = TransformClass1(polyMap)
1052  polyMap = makeTwoWayPolyMap(nMid, nOut)
1053  transform2 = TransformClass1(polyMap)
1054  with self.assertRaises(InvalidParameterError):
1055  transform = transform1.then(transform2)
1056 
1057  def assertTransformsEqual(self, transform1, transform2):
1058  """Assert that two transforms are equal"""
1059  self.assertEqual(type(transform1), type(transform2))
1060  self.assertEqual(transform1.fromEndpoint, transform2.fromEndpoint)
1061  self.assertEqual(transform1.toEndpoint, transform2.toEndpoint)
1062  self.assertEqual(transform1.getMapping(), transform2.getMapping())
1063 
1064  fromEndpoint = transform1.fromEndpoint
1065  toEndpoint = transform1.toEndpoint
1066  mapping = transform1.getMapping()
1067  nIn = mapping.nIn
1068  nOut = mapping.nOut
1069 
1070  if mapping.hasForward:
1071  nPoints = 7 # arbitrary
1072  rawInArray = self.makeRawArrayData(nPoints, nIn)
1073  inArray = fromEndpoint.arrayFromData(rawInArray)
1074  outArray = transform1.applyForward(inArray)
1075  outData = toEndpoint.dataFromArray(outArray)
1076  outArrayRoundTrip = transform2.applyForward(inArray)
1077  outDataRoundTrip = toEndpoint.dataFromArray(outArrayRoundTrip)
1078  assert_allclose(outData, outDataRoundTrip)
1079 
1080  if mapping.hasInverse:
1081  nPoints = 7 # arbitrary
1082  rawOutArray = self.makeRawArrayData(nPoints, nOut)
1083  outArray = toEndpoint.arrayFromData(rawOutArray)
1084  inArray = transform1.applyInverse(outArray)
1085  inData = fromEndpoint.dataFromArray(inArray)
1086  inArrayRoundTrip = transform2.applyInverse(outArray)
1087  inDataRoundTrip = fromEndpoint.dataFromArray(inArrayRoundTrip)
1088  assert_allclose(inData, inDataRoundTrip)
1089 
1090  def checkPersistence(self, transform):
1091  """Check persistence of a transform
1092  """
1093  className = type(transform).__name__
1094 
1095  # check writeString and readString
1096  transformStr = transform.writeString()
1097  serialVersion, serialClassName, serialRest = transformStr.split(" ", 2)
1098  self.assertEqual(int(serialVersion), 1)
1099  self.assertEqual(serialClassName, className)
1100  badStr1 = " ".join(["2", serialClassName, serialRest])
1101  with self.assertRaises(lsst.pex.exceptions.InvalidParameterError):
1102  transform.readString(badStr1)
1103  badClassName = "x" + serialClassName
1104  badStr2 = " ".join(["1", badClassName, serialRest])
1105  with self.assertRaises(lsst.pex.exceptions.InvalidParameterError):
1106  transform.readString(badStr2)
1107  transformFromStr1 = transform.readString(transformStr)
1108  self.assertTransformsEqual(transform, transformFromStr1)
1109 
1110  # check transformFromString
1111  transformFromStr2 = afwGeom.transformFromString(transformStr)
1112  self.assertTransformsEqual(transform, transformFromStr2)
1113 
1114  # Check pickling
1115  self.assertTransformsEqual(transform, pickle.loads(pickle.dumps(transform)))
1116 
1117  # Check afw::table::io persistence round-trip
1118  with lsst.utils.tests.getTempFilePath(".fits") as filename:
1119  transform.writeFits(filename)
1120  self.assertTransformsEqual(transform, type(transform).readFits(filename))
def __init__(self, frameSet, permuteBase, permuteCurr)
Definition: testUtils.py:310
Eigen::Matrix2d getCdMatrixFromMetadata(daf::base::PropertySet &metadata)
Read a CD matrix from FITS WCS metadata.
Definition: wcsUtils.cc:78
def makeGoodFrame(cls, name, nAxes=None)
Definition: testUtils.py:472
def checkThen(self, fromName, midName, toName)
Definition: testUtils.py:977
MatrixMap is a form of Mapping which performs a general linear transformation.
Definition: MatrixMap.h:42
A floating-point coordinate rectangle geometry.
Definition: Box.h:305
A UnitMap is a unit (null) Mapping that has no effect on the coordinates supplied to it...
Definition: UnitMap.h:44
def checkTransformFromFrameSet(self, fromName, toName)
Definition: testUtils.py:821
def makeRawArrayData(nPoints, nAxes, delta=0.123)
Definition: testUtils.py:391
def checkTransformation(self, transform, mapping, msg="")
Definition: testUtils.py:627
daf::base::PropertySet * set
Definition: fits.cc:884
def checkInverseTransformation(self, forward, inverse, msg="")
Definition: testUtils.py:701
std::string getPackageDir(std::string const &packageName)
return the root directory of a setup package
Definition: packaging.cc:33
def checkGetJacobian(self, fromName, toName)
Definition: testUtils.py:945
SkyFrame is a specialised form of Frame which describes celestial longitude/latitude coordinate syste...
Definition: SkyFrame.h:66
table::Key< int > type
Definition: Detector.cc:167
def makeTwoWayPolyMap(nIn, nOut)
Definition: test.py:283
Frame is used to represent a coordinate system.
Definition: Frame.h:157
def format(config, name=None, writeSourceLine=True, prefix="", verbose=False)
Definition: history.py:168
def makeSipPixelToIwc(metadata)
Definition: testUtils.py:237
def checkInverted(self, fromName, toName)
Definition: testUtils.py:893
ShiftMap is a linear Mapping which shifts each axis by a specified constant value.
Definition: ShiftMap.h:40
def __getitem__(self, indXY)
Definition: testUtils.py:87
def getTempFilePath(ext, expectOutput=True)
Definition: tests.py:317
def __init__(self, box, numColRow)
Definition: testUtils.py:58
Reports invalid arguments.
Definition: Runtime.h:66
PolyMap is a Mapping which performs a general polynomial transformation.
Definition: PolyMap.h:49
def assertTransformsEqual(self, transform1, transform2)
Definition: testUtils.py:1057
def makeSipPolyMapCoeffs(metadata, name)
Definition: testUtils.py:143
An integer coordinate rectangle.
Definition: Box.h:54
def makeForwardPolyMap(nIn, nOut)
Definition: test.py:307
A FrameSet consists of a set of one or more Frames (which describe coordinate systems), connected together by Mappings (which describe how the coordinate systems are inter-related).
Definition: FrameSet.h:99
def makeSipIwcToPixel(metadata)
Definition: testUtils.py:198
def checkInverseMapping(self, TransformClass, mapping, msg)
Definition: testUtils.py:921
Transform LSST spatial data, such as lsst::geom::Point2D and lsst::geom::SpherePoint, using an AST mapping.
Definition: Transform.h:67
def makeFrameSet(self, baseFrame, currFrame)
Definition: testUtils.py:524
def checkTransformFromMapping(self, fromName, toName)
Definition: testUtils.py:766