23#ifndef AFW_TABLE_PYTHON_CATALOG_H_INCLUDED
24#define AFW_TABLE_PYTHON_CATALOG_H_INCLUDED
26#include "pybind11/pybind11.h"
28#include "ndarray/pybind11.h"
38template <
typename Record>
42template <
typename T,
typename Record>
47 ndarray::Array<typename Field<T>::Value, 1, 1> out = ndarray::allocate(catalog.
size());
48 auto outIter = out.begin();
49 auto inIter = catalog.
begin();
50 for (; inIter != catalog.
end(); ++inIter, ++outIter) {
51 *outIter = inIter->get(key);
58template <
typename Record>
63 ndarray::Array<double, 1, 1> out = ndarray::allocate(catalog.
size());
64 auto outIter = out.begin();
65 auto inIter = catalog.
begin();
66 for (; inIter != catalog.
end(); ++inIter, ++outIter) {
67 *outIter = inIter->get(key).asRadians();
74template <
typename T,
typename Record>
79 ndarray::Array<typename Field<T>::Value, 2, 2> out = ndarray::allocate(catalog.
size(), key.getSize());
80 auto outIter = out.begin();
81 auto inIter = catalog.
begin();
82 for (; inIter != catalog.
end(); ++inIter, ++outIter) {
83 *outIter = inIter->get(key);
88template <
typename Record>
92 ndarray::Array<bool const, 1>
const & array
94 if (array.size() != catalog.
size()) {
97 (boost::format(
"Catalog has %d rows, while array has %d elements.")
98 % catalog.
size() % array.size()).str()
101 auto catIter = catalog.
begin();
102 auto arrayIter = array.begin();
103 for (; catIter != catalog.
end(); ++catIter, ++arrayIter) {
104 catIter->set(key, *arrayIter);
108template <
typename Record>
114 for (
auto catIter = catalog.
begin(); catIter != catalog.
end(); ++catIter) {
115 catIter->set(key, value);
121template <
typename Record>
133 if (_index < _catalog->size()) {
134 return _catalog->
get(_index);
137 "Catalog shrunk during iteration, invalidating this iterator."
147 return _catalog == other._catalog && _index == other._index;
151 return !(*
this == other);
167template <
typename T,
typename Record>
170 using namespace pybind11::literals;
174 using ColumnView =
typename Record::ColumnView;
176 cls.def(
"isSorted", (
bool (
Catalog::*)(
Key<T> const &)
const) & Catalog::isSorted);
177 cls.def(
"sort", (
void (
Catalog::*)(
Key<T> const &)) & Catalog::sort);
179 auto iter = self.find(value, key);
180 if (
iter == self.end()) {
186 return self.upper_bound(value, key) - self.begin();
189 return self.lower_bound(value, key) - self.begin();
191 cls.def(
"equal_range", [](
Catalog &self, Value
const &value,
Key<T> const &key) {
192 auto p = self.equal_range(value, key);
193 return py::slice(p.first - self.begin(), p.second - self.begin(), 1);
195 cls.def(
"between", [](
Catalog &self, Value
const &lower, Value
const &upper,
Key<T> const &key) {
198 return py::slice(
a,
b, 1);
201 cls.def(
"_get_column_from_key",
202 [](
Catalog const &self,
Key<T> const &key, pybind11::object py_column_view) {
204 if (!column_view && self.isContiguous()) {
208 column_view = std::make_shared<ColumnView>(self.
getColumnView());
209 py_column_view = pybind11::cast(column_view);
213 if constexpr (std::is_same_v<T, Angle>) {
216 return pybind11::make_tuple(column_view->get_radians_array(key), column_view);
218 return pybind11::make_tuple((*column_view)[key].shallow(), column_view);
234template <
typename T,
typename Record>
237 using namespace pybind11::literals;
241 using ColumnView =
typename Record::ColumnView;
243 cls.def(
"_get_column_from_key",
246 if (!column_view && self.isContiguous()) {
250 column_view = std::make_shared<ColumnView>(self.
getColumnView());
251 py_column_view = pybind11::cast(column_view);
255 return pybind11::make_tuple((*column_view)[key].shallow(), column_view);
275template <
typename Record>
277 bool isBase =
false) {
279 using namespace pybind11::literals;
282 using Table =
typename Record::Table;
283 using ColumnView =
typename Record::ColumnView;
287 fullName =
"_" +
name +
"CatalogBase";
289 fullName =
name +
"Catalog";
294 return wrappers.wrapType(
296 [](
auto &mod,
auto &cls) {
298 cls.def(py::init<Schema const &>(),
"schema"_a);
299 cls.def(py::init<std::shared_ptr<Table> const &>(),
"table"_a);
300 cls.def(py::init<Catalog const &>(),
"other"_a);
303 cls.def_static(
"readFits", (Catalog(*)(std::string const &, int, int)) & Catalog::readFits,
304 "filename"_a,
"hdu"_a = fits::DEFAULT_HDU,
"flags"_a = 0);
305 cls.def_static(
"readFits", (Catalog(*)(fits::MemFileManager &, int, int)) & Catalog::readFits,
306 "manager"_a,
"hdu"_a = fits::DEFAULT_HDU,
"flags"_a = 0);
310 cls.def(
"getTable", &Catalog::getTable);
311 cls.def_property_readonly(
"table", &Catalog::getTable);
312 cls.def(
"getSchema", &Catalog::getSchema);
313 cls.def_property_readonly(
"schema", &Catalog::getSchema);
314 cls.def(
"capacity", &Catalog::capacity);
315 cls.def(
"__len__", &Catalog::size);
316 cls.def(
"resize", &Catalog::resize);
320 cls.def(
"_getColumnView", &Catalog::getColumnView);
321 cls.def(
"_addNew", &Catalog::addNew);
322 cls.def(
"_extend", [](Catalog &self, Catalog const &other, bool deep) {
323 self.insert(self.end(), other.begin(), other.end(), deep);
326 self.
insert(
mapper, self.end(), other.begin(), other.end());
331 self.erase(self.begin() + utils::python::cppIndex(self.size(), i));
333 cls.def(
"_delslice_", [](
Catalog &self, py::slice
const &s) {
334 Py_ssize_t start = 0, stop = 0,
step = 0,
length = 0;
335 if (PySlice_GetIndicesEx(s.ptr(), self.size(), &start, &stop, &
step, &
length) != 0) {
336 throw py::error_already_set();
339 throw py::index_error(
"Slice step must not exactly 1");
341 self.erase(self.begin() + start, self.begin() + stop);
345 cls.def(
"set", &Catalog::set);
346 cls.def(
"_getitem_", [](
Catalog &self,
int i) {
347 return self.get(utils::python::cppIndex(self.size(), i));
349 cls.def(
"__iter__", [](
Catalog & self) {
376 return py::make_iterator(
380 }, py::keep_alive<0, 1>());
381 cls.def(
"isContiguous", &Catalog::isContiguous);
385 "filename"_a,
"mode"_a =
"w",
"flags"_a = 0);
389 "manager"_a,
"mode"_a =
"w",
"flags"_a = 0);
390 cls.def(
"reserve", &Catalog::reserve);
392 (
Catalog(
Catalog::*)(ndarray::Array<bool const, 1>
const &)
const) & Catalog::subset);
397 declareCatalogOverloads<std::int32_t>(cls);
398 declareCatalogOverloads<std::int64_t>(cls);
399 declareCatalogOverloads<float>(cls);
400 declareCatalogOverloads<double>(cls);
401 declareCatalogOverloads<lsst::geom::Angle>(cls);
402 declareCatalogArrayOverloads<std::uint8_t>(cls);
403 declareCatalogArrayOverloads<std::uint16_t>(cls);
404 declareCatalogArrayOverloads<int>(cls);
405 declareCatalogArrayOverloads<float>(cls);
406 declareCatalogArrayOverloads<double>(cls);
408 cls.def(
"_get_column_from_key",
409 [](
Catalog const &self,
Key<Flag> const &key, pybind11::object py_column_view) {
416 return pybind11::make_tuple(
423 [](
Catalog &self,
Key<Flag> const & key, ndarray::Array<bool const, 1>
const & array) {
table::Key< std::string > name
#define LSST_EXCEPT(type,...)
Lifetime-management for memory that goes into FITS memory files.
def insert(self, key, value)
Tag types used to declare specialized field types.
A custom container class for records, based on std::vector.
std::shared_ptr< RecordT > const get(size_type i) const
Return a pointer to the record at index i.
size_type size() const
Return the number of elements in the catalog.
iterator begin()
Iterator access.
A class used as a handle to a particular field in a table.
A mapping between the keys of two Schemas, used to copy data between them.
PyCatalogIndexIterator(CatalogT< Record > const *catalog, std::size_t index)
std::shared_ptr< Record > operator*() const
bool operator!=(PyCatalogIndexIterator const &other) const
PyCatalogIndexIterator & operator++()
bool operator==(PyCatalogIndexIterator const &other) const
void _setFlagColumnToArray(CatalogT< Record > &catalog, Key< Flag > const &key, ndarray::Array< bool const, 1 > const &array)
ndarray::Array< typename Field< T >::Value const, 1, 1 > _getArrayFromCatalog(CatalogT< Record > const &catalog, Key< T > const &key)
Extract a column from a potentially non-contiguous Catalog.
void declareCatalogArrayOverloads(PyCatalog< Record > &cls)
Declare field-type-specific overloaded catalog member functions for one array-valued field type.
void _setFlagColumnToScalar(CatalogT< Record > &catalog, Key< Flag > const &key, bool value)
pybind11::class_< CatalogT< Record >, std::shared_ptr< CatalogT< Record > > > PyCatalog
PyCatalog< Record > declareCatalog(utils::python::WrapperCollection &wrappers, std::string const &name, bool isBase=false)
Wrap an instantiation of lsst::afw::table::CatalogT<Record>.
void declareCatalogOverloads(PyCatalog< Record > &cls)
Declare field-type-specific overloaded catalog member functions for one field type.
T Value
the type returned by BaseRecord::get