Kstars

pykstars.cpp
1 /*
2  SPDX-FileCopyrightText: 2021 Valentin Boettcher <hiro at protagon.space; @hiro98:tchncs.de>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include <pybind11/pybind11.h>
8 #include <pybind11/chrono.h>
9 #include "skyobjects/skypoint.h"
10 #include "skymesh.h"
11 #include "cachingdms.h"
12 #include "sqlstatements.cpp"
13 #include "catalogobject.h"
14 #include "catalogsdb.h"
15 #include <iostream>
16 
17 using namespace pybind11::literals;
18 namespace py = pybind11;
19 namespace pybind11
20 {
21 namespace detail
22 {
23 template <>
24 struct type_caster<QString>
25 {
26  public:
27  PYBIND11_TYPE_CASTER(QString, _("QString"));
28 
29  bool load(handle src, bool)
30  {
31  try
32  {
33  value = QString::fromStdString(src.cast<std::string>());
34  }
35  catch (const py::cast_error &)
36  {
37  return false;
38  }
39 
40  return !PyErr_Occurred();
41  }
42 
43  static handle cast(QString src, return_value_policy /* policy */, handle /* parent */)
44  {
45  const handle *obj = new py::object(py::cast(src.toUtf8().constData()));
46  return *obj;
47  }
48 };
49 
50 template <>
51 struct type_caster<QDateTime>
52 {
53  public:
54  PYBIND11_TYPE_CASTER(QDateTime, _("QDateTime"));
55 
56  bool load(handle src, bool)
57  {
58  try
59  {
61  std::chrono::duration_cast<std::chrono::milliseconds>(
62  src.cast<std::chrono::system_clock::time_point>().time_since_epoch())
63  .count());
64  }
65  catch (const py::cast_error &)
66  {
67  return false;
68  }
69 
70  return !PyErr_Occurred();
71  }
72 
73  static handle cast(QDateTime src, return_value_policy /* policy */,
74  handle /* parent */)
75  {
76  const handle *obj = new py::object(py::cast(std::chrono::system_clock::time_point(
77  std::chrono::milliseconds(src.currentMSecsSinceEpoch()))));
78  return *obj;
79  }
80 };
81 } // namespace detail
82 } // namespace pybind11
83 
84 /**
85  * @struct Indexer
86  * Provides a simple wrapper to generate trixel ids from python code.
87  */
88 struct Indexer
89 {
90  Indexer(int level) : m_mesh{ SkyMesh::Create(level) } {};
91 
92  int getLevel() const { return m_mesh->level(); };
93  void setLevel(int level) { m_mesh = SkyMesh::Create(level); };
94 
95  int getTrixel(double ra, double dec, bool convert_epoch = false) const
96  {
97  SkyPoint p{ dms(ra), dms(dec) };
98  if (convert_epoch)
99  {
100  p.B1950ToJ2000();
101  p = SkyPoint{ p.ra(), p.dec() }; // resetting ra0, dec0
102  }
103 
104  return m_mesh->index(&p);
105  };
106 
107  SkyMesh *m_mesh;
108 };
109 
110 ///////////////////////////////////////////////////////////////////////////////
111 // PYBIND //
112 ///////////////////////////////////////////////////////////////////////////////
113 
114 const CatalogObject DEFAULT_CATALOG_OBJECT{};
115 
116 template <typename T>
117 T cast_default(const py::object &value, const T &default_value)
118 {
119  try
120  {
121  return py::cast<T>(value);
122  }
123  catch (const py::cast_error &)
124  {
125  return default_value;
126  }
127 }
128 
129 PYBIND11_MODULE(pykstars, m)
130 {
131  m.doc() = "Thin bindings for KStars to facilitate trixel indexation from python.";
132 
133  py::class_<Indexer>(m, "Indexer")
134  .def(py::init<int>(), "level"_a,
135  "Initializes an `Indexer` with the given `level`.\n"
136  "If the level is greater then approx. 10 the initialization can take some "
137  "time.")
138  .def_property("level", &Indexer::getLevel, &Indexer::setLevel,
139  "Sets the level of the HTMesh/SkyMesh used to index points.")
140  .def(
141  "get_trixel", &Indexer::getTrixel, "ra"_a, "dec"_a, "convert_epoch"_a = false,
142  "Calculates the trixel number from the right ascention and the declination.\n"
143  "The epoch of coordinates is assumed to be J2000.\n\n"
144  "If the epoch is B1950, `convert_epoch` has to be set to `True`.")
145  .def("__repr__", [](const Indexer &indexer) {
146  std::ostringstream lvl;
147  lvl << indexer.getLevel();
148  return "<Indexer level=" + lvl.str() + ">";
149  });
150 
151  {
152  using namespace CatalogsDB;
153  py::class_<DBManager>(m, "DBManager")
154  .def(py::init([](const std::string &filename) {
155  return new DBManager(QString::fromStdString(filename));
156  }),
157  "filename"_a)
158  .def(
159  "register_catalog",
160  [](DBManager &self, const py::dict &cat) {
161  return self.register_catalog(
162  py::cast<int>(cat["id"]), py::cast<QString>(cat["name"]),
163  py::cast<bool>(cat["mut"]), py::cast<bool>(cat["enabled"]),
164  py::cast<double>(cat["precedence"]),
165  py::cast<QString>(cat["author"]),
166  py::cast<QString>(cat["source"]),
167  py::cast<QString>(cat["description"]),
168  py::cast<int>(cat["version"]), py::cast<QString>(cat["color"]),
169  py::cast<QString>(cat["license"]),
170  py::cast<QString>(cat["maintainer"]),
171  py::cast<QDateTime>(cat["timestamp"]));
172  },
173  "catalog"_a)
174  .def(
175  "update_catalog_meta",
176  [](DBManager &self, const py::dict &cat) {
177  return self.update_catalog_meta(
178  { py::cast<int>(cat["id"]), py::cast<QString>(cat["name"]),
179  py::cast<double>(cat["precedence"]),
180  py::cast<QString>(cat["author"]),
181  py::cast<QString>(cat["source"]),
182  py::cast<QString>(cat["description"]),
183  py::cast<bool>(cat["mut"]), py::cast<bool>(cat["enabled"]),
184  py::cast<int>(cat["version"]), py::cast<QString>(cat["color"]),
185  py::cast<QString>(cat["license"]),
186  py::cast<QString>(cat["maintainer"]),
187  py::cast<QDateTime>(cat["timestamp"]) });
188  },
189  "catalog"_a)
190  .def("__repr__",
191  [](const DBManager &manager) {
192  return QString("<DBManager filename=\"" + manager.db_file_name() +
193  "\">");
194  })
195  .def("update_catalog_views", &DBManager::update_catalog_views)
196  .def("compile_master_catalog", &DBManager::compile_master_catalog)
197  .def("dump_catalog", &DBManager::dump_catalog, "catalog_id"_a, "file_path"_a)
198  .def("import_catalog", &DBManager::import_catalog, "file_path"_a,
199  "overwrite"_a)
200  .def("remove_catalog", &DBManager::remove_catalog, "catalog_id"_a);
201 
202  py::register_exception<DatabaseError>(m, "DatabaseError");
203  }
204 
205  py::enum_<SkyObject::TYPE>(m, "ObjectType", "The types of CatalogObjects",
206  py::arithmetic())
207  .value("STAR", SkyObject::STAR)
208  .value("CATALOG_STAR", SkyObject::CATALOG_STAR)
209  .value("PLANET", SkyObject::TYPE::PLANET)
210  .value("OPEN_CLUSTER", SkyObject::TYPE::OPEN_CLUSTER)
211  .value("GLOBULAR_CLUSTER", SkyObject::TYPE::GLOBULAR_CLUSTER)
212  .value("GASEOUS_NEBULA", SkyObject::TYPE::GASEOUS_NEBULA)
213  .value("PLANETARY_NEBULA", SkyObject::TYPE::PLANETARY_NEBULA)
214  .value("SUPERNOVA_REMNANT", SkyObject::TYPE::SUPERNOVA_REMNANT)
215  .value("GALAXY", SkyObject::TYPE::GALAXY)
216  .value("COMET", SkyObject::TYPE::COMET)
217  .value("ASTEROID", SkyObject::TYPE::ASTEROID)
218  .value("CONSTELLATION", SkyObject::TYPE::CONSTELLATION)
219  .value("MOON", SkyObject::TYPE::MOON)
220  .value("ASTERISM", SkyObject::TYPE::ASTERISM)
221  .value("GALAXY_CLUSTER", SkyObject::TYPE::GALAXY_CLUSTER)
222  .value("DARK_NEBULA", SkyObject::TYPE::DARK_NEBULA)
223  .value("QUASAR", SkyObject::TYPE::QUASAR)
224  .value("MULT_STAR", SkyObject::TYPE::MULT_STAR)
225  .value("RADIO_SOURCE", SkyObject::TYPE::RADIO_SOURCE)
226  .value("SATELLITE", SkyObject::TYPE::SATELLITE)
227  .value("SUPERNOVA", SkyObject::TYPE::SUPERNOVA)
228  .value("NUMBER_OF_KNOWN_TYPES", SkyObject::TYPE::NUMBER_OF_KNOWN_TYPES)
229  .value("TYPE_UNKNOWN", SkyObject::TYPE::TYPE_UNKNOWN)
230  .export_values();
231 
232  m.def(
233  "get_id",
234  [](const py::dict &obj) -> py::bytes {
235  return CatalogObject::getId(
236  static_cast<SkyObject::TYPE>(py::cast<int>(obj["type"])),
237  py::cast<double>(obj["ra"]), py::cast<double>(obj["dec"]),
238  py::cast<QString>(obj["name"]),
239  py::cast<QString>(obj["catalog_identifier"]))
240  .toStdString();
241  },
242  "object"_a,
243  R"(
244  Calculate the id of an object.
245 
246  Parameters
247  ----------)");
248 
249  ///////////////////////////////////////////////////////////////////////////
250  // Sql Statements //
251  ///////////////////////////////////////////////////////////////////////////
252  auto s = m.def_submodule("sqlstatements");
253  {
254  using namespace CatalogsDB::SqlStatements;
255 
256  s.doc() = "Assorted sql statements to modify the catalog database.";
257 
258  s.def("insert_dso", &insert_dso, "catalog_id"_a);
259  s.def("create_catalog_table", &create_catalog_table, "catalog_id"_a);
260 
261 #define ATTR(name) \
262  { \
263  s.attr(#name) = name; \
264  }
265  ATTR(create_catalog_list_table);
266  ATTR(insert_catalog);
267  ATTR(get_catalog_by_id);
268  ATTR(all_catalog_view);
269  ATTR(master_catalog);
270  ATTR(dso_by_name);
271 #undef ATTR
272  }
273 }
const QString & db_file_name() const
Definition: catalogsdb.h:225
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
QDateTime fromMSecsSinceEpoch(qint64 msecs)
qint64 currentMSecsSinceEpoch()
QAction * load(const QObject *recvr, const char *slot, QObject *parent)
Manages the catalog database and provides an interface to provide an interface to query and modify th...
Definition: catalogsdb.h:181
std::string toStdString() const const
Holds a collection of hardcoded sql statements.
QString fromStdString(const std::string &str)
QByteArray toUtf8() const const
int level() const
returns the mesh level.
Definition: HTMesh.h:122
const oid getId() const
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
const CachingDms & ra() const
Definition: skypoint.h:263
const char * constData() const const
A simple container object to hold the minimum information for a Deeb Sky Object to be drawn on the sk...
Definition: catalogobject.h:40
static SkyMesh * Create(int level)
creates the single instance of SkyMesh.
Definition: skymesh.cpp:25
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sun Aug 14 2022 04:13:59 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.