Skip to content

Commit

Permalink
Add Transform<FromEndpoint, ToEndpoint>
Browse files Browse the repository at this point in the history
In Python these classes are named Transform_fromPrefix_To_toPrefix_
where _fromPrefix_ and _toPrefix_ are endpoint prefixes
such as "Generic", "Point3" or "SpherePoint".
  • Loading branch information
r-owen committed Mar 10, 2017
1 parent 117b1b2 commit 7d60ddd
Show file tree
Hide file tree
Showing 6 changed files with 622 additions and 0 deletions.
158 changes: 158 additions & 0 deletions include/lsst/afw/geom/Transform.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,158 @@
// -*- lsst-c++ -*-
/*
* LSST Data Management System
* Copyright 2008-2017 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/>.
*/

#ifndef LSST_AFW_GEOM_TRANSFORM_H
#define LSST_AFW_GEOM_TRANSFORM_H

#include <memory>
#include <vector>

#include "astshim.h"
#include "ndarray.h"

#include "lsst/afw/geom/Endpoint.h"

namespace lsst {
namespace afw {
namespace geom {

/**
Transform LSST spatial data, such as Point2D and SpherePoint, using an AST transform.
This class contains two Endpoints, to specify the "from" and "to" LSST data type,
and an astshim::FrameSet or astshim::Mapping to specify the transformation.
In the case of a FrameSet the transformation is from the `BASE` frame to the `CURRENT` frame.
The endpoints convert the data between the LSST Form (e.g. Point2D) and the form used by astshim.
@note You gain some safety by constructing a Transform from an astshim::FrameSet,
since the base and current frames in the FrameSet can be checked against by the appropriate endpoint.
@note "In place" versions of `tranForward` and `tranInverse` are not available
because data must be copied when converting from LSST data types to the type used by astshim,
so it didn't seem worth the bother.
*/
template <typename FromEndpoint, typename ToEndpoint>
class Transform {
public:
using FromArray = typename FromEndpoint::Array;
using FromPoint = typename FromEndpoint::Point;
using ToArray = typename ToEndpoint::Array;
using ToPoint = typename ToEndpoint::Point;

Transform(Transform const &) = delete;
Transform(Transform &&) = default;
Transform & operator=(Transform const &) = delete;
Transform & operator=(Transform &&) = default;

/**
Construct a Transform from a deep copy of an ast::Mapping
The internal FrameSet consists of a frame constructed by each endpoint
connected by the mapping.
@param[in] mapping ast::Mapping describing the desired transformation
@param[in] simplify Simplify the mapping? This combines component mappings
and removes redundant components where possible.
*/
explicit Transform(ast::Mapping const &mapping, bool simplify=true);

/**
Constructor a Transform from a deep copy of a FrameSet.
The result transforms from the "base" frame to the "current" frame.
The "from" endpoint is used to normalize the "base" frame
and the "to" endpoint is used to normalize the "current" frame.
This is pickier than the constructor that takes an ast::Mapping in that:
- SphereEndpoint must be associated with an ast::SkyFrame and the SkyFrame axes
are swapped if necessary to the standard order: longitude, latitude.
- Point2Endpoint must be associated with an ast::Frame (not a subclass),
because Frame is the only kind of Frame that is sure to be Cartesian.
@param[in] frameSet ast::FrameSet describing the desired transformation in the usual way:
from "base" frame to "current" frame
@param[in] simplify Simplify the frame set? This simplifies each mapping
in the frame set by combining component mappings and removing
redundant components where possible. However it
does not remove any frames.
*/
explicit Transform(ast::FrameSet const & frameSet, bool simplify=true);

~Transform(){};

/**
Get the "from" endpoint
*/
FromEndpoint getFromEndpoint() const { return _fromEndpoint; }

/**
Get the contained frameset
*/
std::shared_ptr<const ast::FrameSet> getFrameSet() const { return _frameSet; }

/**
Get the "to" endpoint
*/
ToEndpoint getToEndpoint() const { return _toEndpoint; }

/**
Transform one point in the forward direction ("from" to "to")
*/
ToPoint tranForward(FromPoint const & point) const;

/**
Transform an array of points in the forward direction ("from" to "to")
*/
ToArray tranForward(FromArray const &array) const;

/**
Transform one point in the inverse direction ("to" to "from")
*/
FromPoint tranInverse(ToPoint const & point) const;

/**
Transform an array of points in the inverse direction ("to" to "from")
*/
FromArray tranInverse(ToArray const & array) const;

private:
FromEndpoint const _fromEndpoint;
std::shared_ptr<const ast::FrameSet> _frameSet;
ToEndpoint const _toEndpoint;
};

/**
Print a Transform to an ostream
The format is "Transform<_fromEndpoint_, _toEndpoint_>"
where _fromEndpoint_ and _toEndpoint_ are the appropriate endpoint printed to the ostream;
for example "Transform<GenericEndpoint(4), Point3Endpoint()>"
*/
template <typename FromEndpoint, typename ToEndpoint>
std::ostream & operator<<(std::ostream & os, Transform<FromEndpoint, ToEndpoint> const & transform);

} // geom
} // afw
} // lsst

#endif
1 change: 1 addition & 0 deletions python/lsst/afw/geom/SConscript
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ scripts.BasicSConscript.pybind11([
'linearTransform',
'xyTransform',
'endpoint',
'transform',
],
addUnderscore=False
)
1 change: 1 addition & 0 deletions python/lsst/afw/geom/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,4 @@
from .transformMap import *
from .utils import *
from .endpoint import *
from .transform import *
124 changes: 124 additions & 0 deletions python/lsst/afw/geom/transform.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/*
* LSST Data Management System
* See COPYRIGHT file at the top of the source tree.
*
* 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/>.
*/
#include <memory>

#include "astshim.h"
#include "pybind11/pybind11.h"
#include "pybind11/stl.h"
#include "numpy/arrayobject.h"
#include "ndarray/pybind11.h"

#include "lsst/afw/geom/Endpoint.h"
#include "lsst/afw/geom/Point.h"
#include "lsst/afw/geom/SpherePoint.h"
#include "lsst/afw/geom/Transform.h"

namespace py = pybind11;
using namespace py::literals;

namespace lsst {
namespace afw {
namespace geom {
namespace {

// Return a string consisting of "_pythonClassName_[_fromNAxes_->_toNAxes_]",
// for example "TransformGenericToPoint3[4->3]"
template<typename Class>
std::string formatStr(Class const & self, std::string const & pyClassName) {
std::ostringstream os;
os << pyClassName;
auto const frameSet = self.getFrameSet();
os << "[" << frameSet->getNin() << "->" << frameSet->getNout() << "]";
return os.str();
}

// Declare Transform<FromEndpoint, ToEndpoint> using python class name TransformFrom<X>To<Y>
// where <X> and <Y> are the name of the from endpoint and to endpoint class, respectively,
// for example TransformFromGenericToPoint3
template <typename FromEndpoint, typename ToEndpoint>
void declareTransform(py::module& mod, std::string const & fromName, std::string const & toName) {
using Class = Transform<FromEndpoint, ToEndpoint>;
using ToPoint = typename ToEndpoint::Point;
using ToArray = typename ToEndpoint::Array;
using FromPoint = typename FromEndpoint::Point;
using FromArray = typename FromEndpoint::Array;

std::string const pyClassName = "Transform" + fromName + "To" + toName;

py::class_<Class, std::shared_ptr<Class>> cls(mod, pyClassName.c_str());

cls.def(py::init<ast::Mapping const &, bool>(), "mapping"_a, "simplify"_a=true);
cls.def(py::init<ast::FrameSet const &, bool>(), "frameSet"_a, "simplify"_a=true);

cls.def("getFromEndpoint", &Class::getFromEndpoint);
cls.def("getFrameSet", &Class::getFrameSet);
cls.def("getToEndpoint", &Class::getToEndpoint);

cls.def("tranForward", (ToArray (Class::*)(FromArray const &) const) &Class::tranForward, "array"_a);
cls.def("tranForward", (ToPoint (Class::*)(FromPoint const &) const) &Class::tranForward, "point"_a);
cls.def("tranInverse", (FromArray (Class::*)(ToArray const &) const) &Class::tranInverse, "array"_a);
cls.def("tranInverse", (FromPoint (Class::*)(ToPoint const &) const) &Class::tranInverse, "point"_a);
// str(self) = "<Python class name>[<nIn>-><nOut>]"
cls.def("__str__", [pyClassName](Class const & self) {
return formatStr(self, pyClassName);
});
// repr(self) = "lsst.afw.geom.<Python class name>[<nIn>-><nOut>]"
cls.def("__repr__", [pyClassName](Class const & self) {
return "lsst.afw.geom." + formatStr(self, pyClassName);
});
}

PYBIND11_PLUGIN(transform) {
py::module mod("transform");

py::module::import("lsst.afw.geom.endpoint");

// Need to import numpy for ndarray and eigen conversions
if (_import_array() < 0) {
PyErr_SetString(PyExc_ImportError, "numpy.core.multiarray failed to import");
return nullptr;
}

declareTransform<GenericEndpoint, GenericEndpoint>(mod, "Generic", "Generic");
declareTransform<GenericEndpoint, Point2Endpoint>(mod, "Generic", "Point2");
declareTransform<GenericEndpoint, Point3Endpoint>(mod, "Generic", "Point3");
declareTransform<GenericEndpoint, SpherePointEndpoint>(mod, "Generic", "SpherePoint");
declareTransform<Point2Endpoint, GenericEndpoint>(mod, "Point2", "Generic");
declareTransform<Point2Endpoint, Point2Endpoint>(mod, "Point2", "Point2");
declareTransform<Point2Endpoint, Point3Endpoint>(mod, "Point2", "Point3");
declareTransform<Point2Endpoint, SpherePointEndpoint>(mod, "Point2", "SpherePoint");
declareTransform<Point3Endpoint, GenericEndpoint>(mod, "Point3", "Generic");
declareTransform<Point3Endpoint, Point2Endpoint>(mod, "Point3", "Point2");
declareTransform<Point3Endpoint, Point3Endpoint>(mod, "Point3", "Point3");
declareTransform<Point3Endpoint, SpherePointEndpoint>(mod, "Point3", "SpherePoint");
declareTransform<SpherePointEndpoint, GenericEndpoint>(mod, "SpherePoint", "Generic");
declareTransform<SpherePointEndpoint, Point2Endpoint>(mod, "SpherePoint", "Point2");
declareTransform<SpherePointEndpoint, Point3Endpoint>(mod, "SpherePoint", "Point3");
declareTransform<SpherePointEndpoint, SpherePointEndpoint>(mod, "SpherePoint", "SpherePoint");

return mod.ptr();
}

} // <anonymous>
} // geom
} // afw
} // lsst

0 comments on commit 7d60ddd

Please sign in to comment.