Skip to content

Commit

Permalink
Add updateRefCentroids and updateSourceCoords
Browse files Browse the repository at this point in the history
Add two utility functions to lsst.afw.table:
updateRefCentroids and updateSourceCoords.
Add a unit test for these functions testTableUtils.py
(since the functions are defined in new module lsst.afw.table.utils)
  • Loading branch information
r-owen committed Jun 22, 2016
1 parent 52f1810 commit efee653
Show file tree
Hide file tree
Showing 3 changed files with 251 additions and 0 deletions.
1 change: 1 addition & 0 deletions python/lsst/afw/table/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@
from .tableEnumDicts import *
from .multiMatch import *
from .catalogMatches import *
from .utils import *
69 changes: 69 additions & 0 deletions python/lsst/afw/table/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#
# LSST Data Management System
# Copyright 2016 AURA/LSST.
#
# This product includes software developed by the
# LSST Project (http://www.lsst.org/).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the LSST License Statement and
# the GNU General Public License along with this program. If not,
# see <https://www.lsstcorp.org/LegalNotices/>.
#
from __future__ import absolute_import, division, print_function

from .tableLib import CoordKey, Point2DKey

__all__ = ["updateRefCentroids", "updateSourceCoords"]


def updateRefCentroids(wcs, refList):
"""Update centroids in a collection of reference objects
This code supports any kind of collection, instead of requiring a catalog,
to make it easy to use with match lists. For example:
updateRefCentroids(wcs, refList=[match.first for match in matches])
@param[in] wcs WCS to map from sky to pixels; an lsst.afw.image.Wcs
@param[in,out] refList collection of reference objects (lsst.afw.table.SimpleRecords) with fields:
- "coord", a field of lsst.afw.coord.Coord, is read
- "centroid", a field of lsst.afw.geom.Point2D, is written
"""
if len(refList) < 1:
return
schema = refList[0].schema
coordKey = CoordKey(schema["coord"])
centroidKey = Point2DKey(schema["centroid"])
for refObj in refList:
refObj.set(centroidKey, wcs.skyToPixel(refObj.get(coordKey)))


def updateSourceCoords(wcs, sourceList):
"""Update coords in a collection of sources
This code supports any kind of collection, instead of requiring a catalog,
to make it easy to use with match lists. For example:
updateSourceCoords(wcs, sourceList=[match.second for match in matches])
@param[in] wcs WCS to map from pixels to sky; an lsst.afw.image.Wcs
@param[in,out] sourceList collection of sources (lsst.afw.table.SourceRecords) with fields:
- "slot_Centroid" (as returned by getCentroid) is read
- "coord", a field of lsst.afw.coord.Coord, is written
"""
if len(sourceList) < 1:
return
schema = sourceList[0].schema
srcCoordKey = CoordKey(schema["coord"])
for src in sourceList:
src.set(srcCoordKey, wcs.pixelToSky(src.getCentroid()))
181 changes: 181 additions & 0 deletions tests/testTableUtils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,181 @@
#!/usr/bin/env python

# LSST Data Management System
# Copyright 2016 LSST Corporation.
#
# This product includes software developed by the
# LSST Project (http://www.lsst.org/).
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the LSST License Statement and
# the GNU General Public License along with this program. If not,
# see <http://www.lsstcorp.org/LegalNotices/>.
#
# The classes in this test are a little non-standard to reduce code
# duplication and support automated unittest discovery.
# A base class includes all the code that implements the testing and
# itself inherits from unittest.TestCase. unittest automated discovery
# will scan all classes that inherit from unittest.TestCase and invoke
# any test methods found. To prevent this base class from being executed
# the test methods are placed in a different class that does not inherit
# from unittest.TestCase. The actual test classes then inherit from
# both the testing class and the implementation class allowing test
# discovery to only run tests found in the subclasses.

from __future__ import absolute_import, division, print_function
import math
import unittest
from itertools import izip

import numpy as np

import lsst.utils.tests
import lsst.afw.coord as afwCoord
import lsst.afw.geom as afwGeom
import lsst.afw.image as afwImage
import lsst.afw.table as afwTable


def setup_module(module):
lsst.utils.tests.init()


def setCentroids(cat, key, maxPix, numPoints):
"""Add a square grid of points to an empty catalog
The catalog must be empty to start with
@param[in,out] cat a catalog, e.g. lsst.afw.table.SimpleCatalog
@paramp[in] key key for field to be set
@param[in] maxPix maximum pixel position; used for both x and y;
the min is the negative of maxPix
@param[in] numPoints number of points in x or y; total points = numPoints*numPoints
"""
if len(cat) != 0:
raise RuntimeError("cat must be empty")
for i in np.linspace(-maxPix, maxPix, numPoints):
for j in np.linspace(-maxPix, maxPix, numPoints):
rec = cat.addNew()
rec.set(key, afwGeom.Point2D(i, j))


class UpdateTestCase(lsst.utils.tests.TestCase):
"""A test case for the lsst.afw.table.updateRefCentroids and updateSourceCoords
"""

def setUp(self):
self.crval = afwCoord.IcrsCoord(afwGeom.PointD(44., 45.))
self.crpix = afwGeom.Point2D(15000, 4000)

arcsecPerPixel = 1/3600.0
CD11 = arcsecPerPixel
CD12 = 0
CD21 = 0
CD22 = arcsecPerPixel

self.wcs = afwImage.makeWcs(self.crval, self.crpix, CD11, CD12, CD21, CD22)

refSchema = afwTable.SimpleTable.makeMinimalSchema()
self.refCentroidKey = afwTable.Point2DKey.addFields(refSchema, "centroid", "centroid", "pixels")
self.refCoordKey = afwTable.CoordKey(refSchema["coord"])
self.refCat = afwTable.SimpleCatalog(refSchema)

# an alias is required to make src.getCentroid() work;
# simply defining a field named "slot_Centroid" doesn't suffice
srcSchema = afwTable.SourceTable.makeMinimalSchema()
self.srcCentroidKey = afwTable.Point2DKey.addFields(srcSchema, "base_SdssCentroid",
"centroid", "pixels")
srcAliases = srcSchema.getAliasMap()
srcAliases.set("slot_Centroid", "base_SdssCentroid")
self.srcCoordKey = afwTable.CoordKey(srcSchema["coord"])
self.sourceCat = afwTable.SourceCatalog(srcSchema)

def tearDown(self):
del self.wcs
del self.refCat
del self.sourceCat

def testNull(self):
"""Check that an empty list causes no problems for either function"""
afwTable.updateRefCentroids(self.wcs, [])
afwTable.updateSourceCoords(self.wcs, [])

def testRefCenter(self):
"""Check that a ref obj at the center is handled as expected"""
refObj = self.refCat.addNew()
refObj.set(self.refCoordKey, self.crval)

# initial centroid should be nan
nanRefCentroid = self.refCat[0].get(self.refCentroidKey)
for val in nanRefCentroid:
self.assertTrue(math.isnan(val))

# computed centroid should be crpix
afwTable.updateRefCentroids(self.wcs, self.refCat)
refCentroid = self.refCat[0].get(self.refCentroidKey)
self.assertPairsNearlyEqual(refCentroid, self.crpix)

# coord should not be changed
self.assertPairsNearlyEqual(self.refCat[0].get(self.refCoordKey), self.crval, maxDiff=0)

def testSourceCenter(self):
"""Check that a source at the center is handled as expected"""
src = self.sourceCat.addNew()
src.set(self.srcCentroidKey, self.crpix)

# initial coord should be nan; as a sanity-check
nanSourceCoord = self.sourceCat[0].get(self.srcCoordKey)
for val in nanSourceCoord:
self.assertTrue(math.isnan(val))

# compute coord should be crval
afwTable.updateSourceCoords(self.wcs, self.sourceCat)
srcCoord = self.sourceCat[0].get(self.srcCoordKey)
self.assertPairsNearlyEqual(srcCoord, self.crval)

# centroid should not be changed; also make sure that getCentroid words
self.assertPairsNearlyEqual(self.sourceCat[0].getCentroid(), self.crpix, maxDiff=0)

def testList(self):
"""Check lists of reference objects and sources"""
maxPix = 1000
numPoints = 10
setCentroids(cat=self.sourceCat, key=self.srcCentroidKey, maxPix=maxPix, numPoints=numPoints)
for src in self.sourceCat:
refObj = self.refCat.addNew()
centroid = src.get(self.srcCentroidKey)
coord = self.wcs.pixelToSky(centroid)
refObj.setCoord(coord)
self.assertEqual(len(self.sourceCat), len(self.refCat))

# update the catalogs, but this time use a list instead of an actual catalog
# to make sure that feature works
afwTable.updateSourceCoords(self.wcs, [s for s in self.sourceCat])
afwTable.updateRefCentroids(self.wcs, [r for r in self.refCat])

# check that centroids and coords match
for src, refObj in izip(self.sourceCat, self.refCat):
srcCentroid = src.get(self.srcCentroidKey)
refCentroid = refObj.get(self.refCentroidKey)
self.assertPairsNearlyEqual(srcCentroid, refCentroid)

srcCoord = src.get(self.srcCoordKey)
refCoord = refObj.get(self.refCoordKey)
self.assertCoordsNearlyEqual(srcCoord, refCoord)


class MyMemoryTestCase(lsst.utils.tests.MemoryTestCase):
pass

if __name__ == "__main__":
lsst.utils.tests.init()
unittest.main()

0 comments on commit efee653

Please sign in to comment.