File indexing completed on 2024-04-28 03:44:40
0001 /* 0002 SPDX-FileCopyrightText: 2021 Valentin Boettcher <hiro at protagon.space; @hiro98:tchncs.de> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include <pybind11/pybind11.h> 0008 #include <pybind11/chrono.h> 0009 #include "skyobjects/skypoint.h" 0010 #include "skymesh.h" 0011 #include "cachingdms.h" 0012 #include "sqlstatements.cpp" 0013 #include "catalogobject.h" 0014 #include "catalogsdb.h" 0015 #include <iostream> 0016 0017 using namespace pybind11::literals; 0018 namespace py = pybind11; 0019 namespace pybind11 0020 { 0021 namespace detail 0022 { 0023 template <> 0024 struct type_caster<QString> 0025 { 0026 public: 0027 PYBIND11_TYPE_CASTER(QString, _("QString")); 0028 0029 bool load(handle src, bool) 0030 { 0031 try 0032 { 0033 value = QString::fromStdString(src.cast<std::string>()); 0034 } 0035 catch (const py::cast_error &) 0036 { 0037 return false; 0038 } 0039 0040 return !PyErr_Occurred(); 0041 } 0042 0043 static handle cast(QString src, return_value_policy /* policy */, handle /* parent */) 0044 { 0045 const handle *obj = new py::object(py::cast(src.toUtf8().constData())); 0046 return *obj; 0047 } 0048 }; 0049 0050 template <> 0051 struct type_caster<QDateTime> 0052 { 0053 public: 0054 PYBIND11_TYPE_CASTER(QDateTime, _("QDateTime")); 0055 0056 bool load(handle src, bool) 0057 { 0058 try 0059 { 0060 value = QDateTime::fromMSecsSinceEpoch( 0061 std::chrono::duration_cast<std::chrono::milliseconds>( 0062 src.cast<std::chrono::system_clock::time_point>().time_since_epoch()) 0063 .count()); 0064 } 0065 catch (const py::cast_error &) 0066 { 0067 return false; 0068 } 0069 0070 return !PyErr_Occurred(); 0071 } 0072 0073 static handle cast(QDateTime src, return_value_policy /* policy */, 0074 handle /* parent */) 0075 { 0076 const handle *obj = new py::object(py::cast(std::chrono::system_clock::time_point( 0077 std::chrono::milliseconds(src.currentMSecsSinceEpoch())))); 0078 return *obj; 0079 } 0080 }; 0081 } // namespace detail 0082 } // namespace pybind11 0083 0084 /** 0085 * @struct Indexer 0086 * Provides a simple wrapper to generate trixel ids from python code. 0087 */ 0088 struct Indexer 0089 { 0090 Indexer(int level) : m_mesh{ SkyMesh::Create(level) } {}; 0091 0092 int getLevel() const { return m_mesh->level(); }; 0093 void setLevel(int level) { m_mesh = SkyMesh::Create(level); }; 0094 0095 int getTrixel(double ra, double dec, bool convert_epoch = false) const 0096 { 0097 SkyPoint p{ dms(ra), dms(dec) }; 0098 if (convert_epoch) 0099 { 0100 p.B1950ToJ2000(); 0101 p = SkyPoint{ p.ra(), p.dec() }; // resetting ra0, dec0 0102 } 0103 0104 return m_mesh->index(&p); 0105 }; 0106 0107 SkyMesh *m_mesh; 0108 }; 0109 0110 /////////////////////////////////////////////////////////////////////////////// 0111 // PYBIND // 0112 /////////////////////////////////////////////////////////////////////////////// 0113 0114 const CatalogObject DEFAULT_CATALOG_OBJECT{}; 0115 0116 template <typename T> 0117 T cast_default(const py::object &value, const T &default_value) 0118 { 0119 try 0120 { 0121 return py::cast<T>(value); 0122 } 0123 catch (const py::cast_error &) 0124 { 0125 return default_value; 0126 } 0127 } 0128 0129 PYBIND11_MODULE(pykstars, m) 0130 { 0131 m.doc() = "Thin bindings for KStars to facilitate trixel indexation from python."; 0132 0133 py::class_<Indexer>(m, "Indexer") 0134 .def(py::init<int>(), "level"_a, 0135 "Initializes an `Indexer` with the given `level`.\n" 0136 "If the level is greater then approx. 10 the initialization can take some " 0137 "time.") 0138 .def_property("level", &Indexer::getLevel, &Indexer::setLevel, 0139 "Sets the level of the HTMesh/SkyMesh used to index points.") 0140 .def( 0141 "get_trixel", &Indexer::getTrixel, "ra"_a, "dec"_a, "convert_epoch"_a = false, 0142 "Calculates the trixel number from the right ascention and the declination.\n" 0143 "The epoch of coordinates is assumed to be J2000.\n\n" 0144 "If the epoch is B1950, `convert_epoch` has to be set to `True`.") 0145 .def("__repr__", [](const Indexer &indexer) { 0146 std::ostringstream lvl; 0147 lvl << indexer.getLevel(); 0148 return "<Indexer level=" + lvl.str() + ">"; 0149 }); 0150 0151 { 0152 using namespace CatalogsDB; 0153 py::class_<DBManager>(m, "DBManager") 0154 .def(py::init([](const std::string &filename) { 0155 return new DBManager(QString::fromStdString(filename)); 0156 }), 0157 "filename"_a) 0158 .def( 0159 "register_catalog", 0160 [](DBManager &self, const py::dict &cat) { 0161 return self.register_catalog( 0162 py::cast<int>(cat["id"]), py::cast<QString>(cat["name"]), 0163 py::cast<bool>(cat["mut"]), py::cast<bool>(cat["enabled"]), 0164 py::cast<double>(cat["precedence"]), 0165 py::cast<QString>(cat["author"]), 0166 py::cast<QString>(cat["source"]), 0167 py::cast<QString>(cat["description"]), 0168 py::cast<int>(cat["version"]), py::cast<QString>(cat["color"]), 0169 py::cast<QString>(cat["license"]), 0170 py::cast<QString>(cat["maintainer"]), 0171 py::cast<QDateTime>(cat["timestamp"])); 0172 }, 0173 "catalog"_a) 0174 .def( 0175 "update_catalog_meta", 0176 [](DBManager &self, const py::dict &cat) { 0177 return self.update_catalog_meta( 0178 { py::cast<int>(cat["id"]), py::cast<QString>(cat["name"]), 0179 py::cast<double>(cat["precedence"]), 0180 py::cast<QString>(cat["author"]), 0181 py::cast<QString>(cat["source"]), 0182 py::cast<QString>(cat["description"]), 0183 py::cast<bool>(cat["mut"]), py::cast<bool>(cat["enabled"]), 0184 py::cast<int>(cat["version"]), py::cast<QString>(cat["color"]), 0185 py::cast<QString>(cat["license"]), 0186 py::cast<QString>(cat["maintainer"]), 0187 py::cast<QDateTime>(cat["timestamp"]) }); 0188 }, 0189 "catalog"_a) 0190 .def("__repr__", 0191 [](const DBManager &manager) { 0192 return QString("<DBManager filename=\"" + manager.db_file_name() + 0193 "\">"); 0194 }) 0195 .def("update_catalog_views", &DBManager::update_catalog_views) 0196 .def("compile_master_catalog", &DBManager::compile_master_catalog) 0197 .def("dump_catalog", &DBManager::dump_catalog, "catalog_id"_a, "file_path"_a) 0198 .def("import_catalog", &DBManager::import_catalog, "file_path"_a, 0199 "overwrite"_a) 0200 .def("remove_catalog", &DBManager::remove_catalog, "catalog_id"_a); 0201 0202 py::register_exception<DatabaseError>(m, "DatabaseError"); 0203 } 0204 0205 py::enum_<SkyObject::TYPE>(m, "ObjectType", "The types of CatalogObjects", 0206 py::arithmetic()) 0207 .value("STAR", SkyObject::STAR) 0208 .value("CATALOG_STAR", SkyObject::CATALOG_STAR) 0209 .value("PLANET", SkyObject::TYPE::PLANET) 0210 .value("OPEN_CLUSTER", SkyObject::TYPE::OPEN_CLUSTER) 0211 .value("GLOBULAR_CLUSTER", SkyObject::TYPE::GLOBULAR_CLUSTER) 0212 .value("GASEOUS_NEBULA", SkyObject::TYPE::GASEOUS_NEBULA) 0213 .value("PLANETARY_NEBULA", SkyObject::TYPE::PLANETARY_NEBULA) 0214 .value("SUPERNOVA_REMNANT", SkyObject::TYPE::SUPERNOVA_REMNANT) 0215 .value("GALAXY", SkyObject::TYPE::GALAXY) 0216 .value("COMET", SkyObject::TYPE::COMET) 0217 .value("ASTEROID", SkyObject::TYPE::ASTEROID) 0218 .value("CONSTELLATION", SkyObject::TYPE::CONSTELLATION) 0219 .value("MOON", SkyObject::TYPE::MOON) 0220 .value("ASTERISM", SkyObject::TYPE::ASTERISM) 0221 .value("GALAXY_CLUSTER", SkyObject::TYPE::GALAXY_CLUSTER) 0222 .value("DARK_NEBULA", SkyObject::TYPE::DARK_NEBULA) 0223 .value("QUASAR", SkyObject::TYPE::QUASAR) 0224 .value("MULT_STAR", SkyObject::TYPE::MULT_STAR) 0225 .value("RADIO_SOURCE", SkyObject::TYPE::RADIO_SOURCE) 0226 .value("SATELLITE", SkyObject::TYPE::SATELLITE) 0227 .value("SUPERNOVA", SkyObject::TYPE::SUPERNOVA) 0228 .value("NUMBER_OF_KNOWN_TYPES", SkyObject::TYPE::NUMBER_OF_KNOWN_TYPES) 0229 .value("TYPE_UNKNOWN", SkyObject::TYPE::TYPE_UNKNOWN) 0230 .export_values(); 0231 0232 m.def( 0233 "get_id", 0234 [](const py::dict &obj) -> py::bytes { 0235 return CatalogObject::getId( 0236 static_cast<SkyObject::TYPE>(py::cast<int>(obj["type"])), 0237 py::cast<double>(obj["ra"]), py::cast<double>(obj["dec"]), 0238 py::cast<QString>(obj["name"]), 0239 py::cast<QString>(obj["catalog_identifier"])) 0240 .toStdString(); 0241 }, 0242 "object"_a, 0243 R"( 0244 Calculate the id of an object. 0245 0246 Parameters 0247 ----------)"); 0248 0249 /////////////////////////////////////////////////////////////////////////// 0250 // Sql Statements // 0251 /////////////////////////////////////////////////////////////////////////// 0252 auto s = m.def_submodule("sqlstatements"); 0253 { 0254 using namespace CatalogsDB::SqlStatements; 0255 0256 s.doc() = "Assorted sql statements to modify the catalog database."; 0257 0258 s.def("insert_dso", &insert_dso, "catalog_id"_a); 0259 s.def("create_catalog_table", &create_catalog_table, "catalog_id"_a); 0260 0261 #define ATTR(name) \ 0262 { \ 0263 s.attr(#name) = name; \ 0264 } 0265 ATTR(create_catalog_list_table); 0266 ATTR(insert_catalog); 0267 ATTR(get_catalog_by_id); 0268 ATTR(all_catalog_view); 0269 ATTR(master_catalog); 0270 ATTR(dso_by_name); 0271 #undef ATTR 0272 } 0273 }