diff --git a/kstars/auxiliary/geolocation.cpp b/kstars/auxiliary/geolocation.cpp index a42ac7acf..a50515f10 100644 --- a/kstars/auxiliary/geolocation.cpp +++ b/kstars/auxiliary/geolocation.cpp @@ -1,223 +1,239 @@ /*************************************************************************** geolocation.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001-2005 by Jason Harris email : jharris@30doradus.org copyright : (C) 2003-2005 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "geolocation.h" #include "timezonerule.h" GeoLocation::GeoLocation(const dms &lng, const dms &lat, const QString &name, const QString &province, const QString &country, double tz, TimeZoneRule *tzrule, double elevation, bool readOnly, int iEllips) : Longitude(lng), Latitude(lat) { Name = name; Province = province; Country = country; TimeZone = tz; TZrule = tzrule; Elevation = elevation; indexEllipsoid = iEllips; ReadOnly = readOnly; setEllipsoid(indexEllipsoid); geodToCart(); } GeoLocation::GeoLocation(double x, double y, double z, const QString &name, const QString &province, const QString &country, double TZ, TimeZoneRule *tzrule, double elevation, bool readOnly, int iEllips) { PosCartX = x; PosCartY = y; PosCartZ = z; Name = name; Province = province; Country = country; TimeZone = TZ; TZrule = tzrule; Elevation = elevation; indexEllipsoid = iEllips; ReadOnly = readOnly; setEllipsoid(indexEllipsoid); cartToGeod(); } QString GeoLocation::fullName() const { if (country().isEmpty()) return translatedName(); else if (province().isEmpty()) { return QString("%1, %2").arg(translatedName(), translatedCountry()); } else { return QString("%1, %2, %3").arg(translatedName(), translatedProvince(), translatedCountry()); } } void GeoLocation::setEllipsoid(int i) { static const double A[] = { 6378140.0, 6378137.0, 6378137.0, 6378137.0, 6378136.0 }; static const double F[] = { 0.0033528131779, 0.0033528106812, 0.0033528131779, 0.00335281066474, 0.0033528131779 }; Q_ASSERT(i >= 0 && (unsigned int)i < sizeof(A) / sizeof(A[0]) && "Index must be in bounds"); axis = A[i]; flattening = F[i]; } void GeoLocation::changeEllipsoid(int index) { setEllipsoid(index); cartToGeod(); } QString GeoLocation::translatedName() const { QString context; if (province().isEmpty()) { context = QString("City in %1").arg(country()); } else { context = QString("City in %1 %2").arg(province(), country()); } return Name.isEmpty() ? QString() : i18nc(context.toUtf8().data(), Name.toUtf8().data()); } QString GeoLocation::translatedProvince() const { return Province.isEmpty() ? QString() : i18nc(QString("Region/state in " + country()).toUtf8().data(), Province.toUtf8().data()); } QString GeoLocation::translatedCountry() const { return Country.isEmpty() ? QString() : i18nc("Country name", Country.toUtf8().data()); } void GeoLocation::cartToGeod() { static const double RIT = 2.7778e-6; double e2, rpro, lat1, xn, sqrtP2, latd, sinl; e2 = 2 * flattening - flattening * flattening; sqrtP2 = sqrt(PosCartX * PosCartX + PosCartY * PosCartY); rpro = PosCartZ / sqrtP2; latd = atan2(rpro, (1 - e2)); lat1 = 0.; while (fabs(latd - lat1) > RIT) { double s1 = sin(latd); lat1 = latd; xn = axis / (sqrt(1 - e2 * s1 * s1)); latd = atan2(static_cast(rpro) * (1 + e2 * xn * s1), PosCartZ); } sinl = sin(latd); xn = axis / (sqrt(1 - e2 * sinl * sinl)); Elevation = sqrtP2 / cos(latd) - xn; Longitude.setRadians(atan2(PosCartY, PosCartX)); Latitude.setRadians(latd); } void GeoLocation::geodToCart() { double e2, xn; double sinLong, cosLong, sinLat, cosLat; e2 = 2 * flattening - flattening * flattening; Longitude.SinCos(sinLong, cosLong); Latitude.SinCos(sinLat, cosLat); xn = axis / (sqrt(1 - e2 * sinLat * sinLat)); PosCartX = (xn + Elevation) * cosLat * cosLong; PosCartY = (xn + Elevation) * cosLat * sinLong; PosCartZ = (xn * (1 - e2) + Elevation) * sinLat; } void GeoLocation::TopocentricVelocity(double vtopo[], const dms &gst) { double Wearth = 7.29211510e-5; // rads/s dms angularVEarth; dms time = GSTtoLST(gst); // angularVEarth.setRadians(time.Hours()*Wearth*3600.); double se, ce; // angularVEarth.SinCos(se,ce); time.SinCos(se, ce); double d0 = sqrt(PosCartX * PosCartX + PosCartY * PosCartY); // km/s vtopo[0] = -d0 * Wearth * se / 1000.; vtopo[1] = d0 * Wearth * ce / 1000.; vtopo[2] = 0.; } double GeoLocation::LMST(double jd) { int divresult; double ut, tu, gmst, theta; ut = (jd + 0.5) - floor(jd + 0.5); jd -= ut; tu = (jd - 2451545.) / 36525.; gmst = 24110.54841 + tu * (8640184.812866 + tu * (0.093104 - tu * 6.2e-6)); divresult = (int)((gmst + 8.6400e4 * 1.00273790934 * ut) / 8.6400e4); gmst = (gmst + 8.6400e4 * 1.00273790934 * ut) - (double)divresult * 8.6400e4; theta = 2. * dms::PI * gmst / (24. * 60. * 60.); divresult = (int)((theta + Longitude.radians()) / (2. * dms::PI)); return ((theta + Longitude.radians()) - (double)divresult * 2. * dms::PI); } bool GeoLocation::isReadOnly() const { return ReadOnly; } void GeoLocation::setReadOnly(bool value) { ReadOnly = value; } KStarsDateTime GeoLocation::UTtoLT(const KStarsDateTime &ut) const { KStarsDateTime lt = ut.addSecs(int(3600. * TZ())); // 2017-08-11 (Jasem): setUtcOffset is deprecated //lt.setUtcOffset(int(3600. * TZ())); lt.setTimeSpec(Qt::LocalTime); return lt; } KStarsDateTime GeoLocation::LTtoUT(const KStarsDateTime <) const { KStarsDateTime ut = lt.addSecs(int(-3600. * TZ())); ut.setTimeSpec(Qt::UTC); // 2017-08-11 (Jasem): setUtcOffset is deprecated //ut.setUtcOffset(0); return ut; } + +double GeoLocation::distanceTo(const dms &longitude, const dms &latitude) +{ + const double R = 6378.135; + + double diffLongitude = (Longitude - longitude).radians(); + double diffLatitude = (Latitude - latitude).radians(); + + double a = sin(diffLongitude / 2) * sin(diffLongitude / 2) + cos(Longitude.radians()) * cos(longitude.radians()) * + sin(diffLatitude / 2) * sin(diffLatitude / 2); + double c = 2 * atan2(sqrt(a), sqrt(1 - a)); + + double distance = R * c; + + return distance; +} diff --git a/kstars/auxiliary/geolocation.h b/kstars/auxiliary/geolocation.h index a01299a66..2d6421779 100644 --- a/kstars/auxiliary/geolocation.h +++ b/kstars/auxiliary/geolocation.h @@ -1,283 +1,348 @@ /*************************************************************************** geolocation.h - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001-2005 by Jason Harris email : jharris@30doradus.org copyright : (C) 2003-2005 by Pablo de Vicente email : p.devicente@wanadoo.es ***************************************************************************/ /*************************************************************************** * * * 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 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #pragma once #include #include "cachingdms.h" #include "timezonerule.h" #include "kstarsdatetime.h" /** * @class GeoLocation * Contains all relevant information for specifying a location * on Earth: City Name, State/Province name, Country Name, * Longitude, Latitude, Elevation, Time Zone, and Daylight Savings * Time rule. * * @short Relevant data about an observing location on Earth. * @author Jason Harris * @version 1.0 */ class GeoLocation { - public: - /** Constructor using dms objects to specify longitude and latitude. - * @param lng the longitude - * @param lat the latitude - * @param name the name of the city/town/location - * @param province the name of the province/US state - * @param country the name of the country - * @param TZ the base time zone offset from Greenwich, UK - * @param TZrule pointer to the daylight savings time rule - * @param elevation the elevation above sea level (in meters) - * @param readOnly whether the location is read only or updatable. - * @param iEllips type of geodetic ellipsoid model + public: + /** Constructor using dms objects to specify longitude and latitude. + * @param lng the longitude + * @param lat the latitude + * @param name the name of the city/town/location + * @param province the name of the province/US state + * @param country the name of the country + * @param TZ the base time zone offset from Greenwich, UK + * @param TZrule pointer to the daylight savings time rule + * @param elevation the elevation above sea level (in meters) + * @param readOnly whether the location is read only or updatable. + * @param iEllips type of geodetic ellipsoid model + */ + GeoLocation(const dms &lng, const dms &lat, const QString &name = "Nowhere", const QString &province = "Nowhere", + const QString &country = "Nowhere", double TZ = 0, TimeZoneRule *TZrule = nullptr, + double elevation = -10, bool readOnly = false, int iEllips = 4); + + /** Constructor using doubles to specify X, Y and Z referred to the center of the Earth. + * @param x the x-position, in m + * @param y the y-position, in m + * @param z the z-position, in m + * @param name the name of the city/town/location + * @param province the name of the province/US state + * @param country the name of the country + * @param TZ the base time zone offset from Greenwich, UK + * @param TZrule pointer to the daylight savings time rule + * @param elevation the elevation above sea level (in meters) + * @param readOnly whether the location is read only or updatable. + * @param iEllips type of geodetic ellipsoid model + */ + GeoLocation(double x, double y, double z, const QString &name = "Nowhere", const QString &province = "Nowhere", + const QString &country = "Nowhere", double TZ = 0, TimeZoneRule *TZrule = nullptr, + double elevation = -10, bool readOnly = false, int iEllips = 4); + + /** @return pointer to the longitude dms object */ + const CachingDms *lng() const + { + return &Longitude; + } + + /** @return pointer to the latitude dms object */ + const CachingDms *lat() const + { + return &Latitude; + } + + /** @return elevation above seal level (meters) */ + double elevation() const + { + return Elevation; + } + + /** @return X position in m */ + double xPos() const + { + return PosCartX; + } + + /** @return Y position in m */ + double yPos() const + { + return PosCartY; + } + + /** @return Z position in m */ + double zPos() const + { + return PosCartZ; + } + + /** @return index identifying the geodetic ellipsoid model */ + int ellipsoid() const + { + return indexEllipsoid; + } + + /** @return untranslated City name */ + QString name() const + { + return Name; + } + + /** @return translated City name */ + QString translatedName() const; + + /** @return untranslated Province name */ + QString province() const + { + return Province; + } + + /** @return translated Province name */ + QString translatedProvince() const; + + /** @return untranslated Country name */ + QString country() const + { + return Country; + } + + /** @return translated Country name */ + QString translatedCountry() const; + + /** @return comma-separated city, province, country names (each localized) */ + QString fullName() const; + + /** @return time zone without DST correction */ + double TZ0() const + { + return TimeZone; + } + + /** @return time zone, including any DST correction. */ + double TZ() const + { + if (TZrule) + return TimeZone + TZrule->deltaTZ(); + return TimeZone; + } + + /** @return pointer to time zone rule object */ + TimeZoneRule *tzrule() + { + return TZrule; + } + + /** Set Time zone. + * @param value the new time zone */ + void setTZ0(double value) + { + TimeZone = value; + } + + /** Set Time zone rule. + * @param value pointer to the new time zone rule */ + void setTZRule(TimeZoneRule *value) + { + TZrule = value; + } + + /** Set longitude according to dms argument. + * @param l the new longitude */ + void setLong(const dms &l) + { + Longitude = l; + geodToCart(); + } + + /** Set latitude according to dms argument. + * @param l the new latitude + */ + void setLat(const dms &l) + { + Latitude = l; + geodToCart(); + } + + /** Set elevation above sea level + * @param hg the new elevation (meters) + */ + void setElevation(double hg) + { + Elevation = hg; + geodToCart(); + } + + /** Set X + * @param x the new x-position (meters) + */ + void setXPos(double x) + { + PosCartX = x; + cartToGeod(); + } + /** Set Y + * @param y the new y-position (meters) + */ + void setYPos(double y) + { + PosCartY = y; + cartToGeod(); + } + /** Set Z + * @param z the new z-position (meters) + */ + void setZPos(double z) + { + PosCartZ = z; + cartToGeod(); + } + + /** Update Latitude, Longitude and Height according to new ellipsoid. These are + * computed from XYZ which do NOT change on changing the ellipsoid. + * @param i index to identify the ellipsoid + */ + void changeEllipsoid(int i); + + /** Set City name according to argument. + * @param n new city name + */ + void setName(const QString &n) + { + Name = n; + } + + /** Set Province name according to argument. + * @param n new province name + */ + void setProvince(const QString &n) + { + Province = n; + } + + /** Set Country name according to argument. + * @param n new country name + */ + void setCountry(const QString &n) + { + Country = n; + } + + /** Converts from cartesian coordinates in meters to longitude, + * latitude and height on a standard geoid for the Earth. The + * geoid is characterized by two parameters: the semimajor axis + * and the flattening. + * + * @note The astronomical zenith is defined as the perpendicular to + * the real geoid. The geodetic zenith is the perpendicular to the + * standard geoid. Both zeniths differ due to local gravitational + * anomalies. + * + * Algorithm is from "GPS Satellite Surveying", A. Leick, page 184. + */ + void cartToGeod(); + + /** Converts from longitude, latitude and height on a standard + * geoid of the Earth to cartesian coordinates in meters. The geoid + * is characterized by two parameters: the semimajor axis and the + * flattening. + * + * @note The astronomical zenith is defined as the perpendicular to + * the real geoid. The geodetic zenith is the perpendicular to the + * standard geoid. Both zeniths differ due to local gravitational + * anomalies. + * + * Algorithm is from "GPS Satellite Surveying", A. Leick, page 184. + */ + void geodToCart(); + + /** The geoid is an elliposid which fits the shape of the Earth. It is + * characterized by two parameters: the semimajor axis and the + * flattening. + * + * @param i is the index which allows to identify the parameters for the + * chosen elliposid. 1="IAU76", 2="GRS80", 3="MERIT83", 4="WGS84", + * 5="IERS89". + */ + void setEllipsoid(int i); + + /** + * @brief distanceTo Return the distance in km from this location to the given longitude and latitude + * @param longitude Target site longitude + * @param latitude Target site latitude + * @return distance in kilometers between this site and the target site. */ - GeoLocation(const dms &lng, const dms &lat, const QString &name = "Nowhere", const QString &province = "Nowhere", - const QString &country = "Nowhere", double TZ = 0, TimeZoneRule *TZrule = nullptr, - double elevation = -10, bool readOnly = false, int iEllips = 4); - - /** Constructor using doubles to specify X, Y and Z referred to the center of the Earth. - * @param x the x-position, in m - * @param y the y-position, in m - * @param z the z-position, in m - * @param name the name of the city/town/location - * @param province the name of the province/US state - * @param country the name of the country - * @param TZ the base time zone offset from Greenwich, UK - * @param TZrule pointer to the daylight savings time rule - * @param elevation the elevation above sea level (in meters) - * @param readOnly whether the location is read only or updatable. - * @param iEllips type of geodetic ellipsoid model - */ - GeoLocation(double x, double y, double z, const QString &name = "Nowhere", const QString &province = "Nowhere", - const QString &country = "Nowhere", double TZ = 0, TimeZoneRule *TZrule = nullptr, - double elevation = -10, bool readOnly = false, int iEllips = 4); - - /** @return pointer to the longitude dms object */ - const CachingDms *lng() const { return &Longitude; } - - /** @return pointer to the latitude dms object */ - const CachingDms *lat() const { return &Latitude; } - - /** @return elevation above seal level (meters) */ - double elevation() const { return Elevation; } - - /** @return X position in m */ - double xPos() const { return PosCartX; } - - /** @return Y position in m */ - double yPos() const { return PosCartY; } - - /** @return Z position in m */ - double zPos() const { return PosCartZ; } - - /** @return index identifying the geodetic ellipsoid model */ - int ellipsoid() const { return indexEllipsoid; } - - /** @return untranslated City name */ - QString name() const { return Name; } - - /** @return translated City name */ - QString translatedName() const; - - /** @return untranslated Province name */ - QString province() const { return Province; } - - /** @return translated Province name */ - QString translatedProvince() const; - - /** @return untranslated Country name */ - QString country() const { return Country; } - - /** @return translated Country name */ - QString translatedCountry() const; - - /** @return comma-separated city, province, country names (each localized) */ - QString fullName() const; - - /** @return time zone without DST correction */ - double TZ0() const { return TimeZone; } - - /** @return time zone, including any DST correction. */ - double TZ() const - { - if (TZrule) - return TimeZone + TZrule->deltaTZ(); - return TimeZone; - } - - /** @return pointer to time zone rule object */ - TimeZoneRule *tzrule() { return TZrule; } - - /** Set Time zone. - * @param value the new time zone */ - void setTZ0(double value) { TimeZone = value; } - - /** Set Time zone rule. - * @param value pointer to the new time zone rule */ - void setTZRule(TimeZoneRule *value) { TZrule = value; } - - /** Set longitude according to dms argument. - * @param l the new longitude */ - void setLong(const dms &l) - { - Longitude = l; - geodToCart(); - } - - /** Set latitude according to dms argument. - * @param l the new latitude - */ - void setLat(const dms &l) - { - Latitude = l; - geodToCart(); - } - - /** Set elevation above sea level - * @param hg the new elevation (meters) - */ - void setElevation(double hg) - { - Elevation = hg; - geodToCart(); - } - - /** Set X - * @param x the new x-position (meters) - */ - void setXPos(double x) - { - PosCartX = x; - cartToGeod(); - } - /** Set Y - * @param y the new y-position (meters) - */ - void setYPos(double y) - { - PosCartY = y; - cartToGeod(); - } - /** Set Z - * @param z the new z-position (meters) - */ - void setZPos(double z) - { - PosCartZ = z; - cartToGeod(); - } - - /** Update Latitude, Longitude and Height according to new ellipsoid. These are - * computed from XYZ which do NOT change on changing the ellipsoid. - * @param i index to identify the ellipsoid - */ - void changeEllipsoid(int i); - - /** Set City name according to argument. - * @param n new city name - */ - void setName(const QString &n) { Name = n; } - - /** Set Province name according to argument. - * @param n new province name - */ - void setProvince(const QString &n) { Province = n; } - - /** Set Country name according to argument. - * @param n new country name - */ - void setCountry(const QString &n) { Country = n; } - - /** Converts from cartesian coordinates in meters to longitude, - * latitude and height on a standard geoid for the Earth. The - * geoid is characterized by two parameters: the semimajor axis - * and the flattening. - * - * @note The astronomical zenith is defined as the perpendicular to - * the real geoid. The geodetic zenith is the perpendicular to the - * standard geoid. Both zeniths differ due to local gravitational - * anomalies. - * - * Algorithm is from "GPS Satellite Surveying", A. Leick, page 184. - */ - void cartToGeod(); - - /** Converts from longitude, latitude and height on a standard - * geoid of the Earth to cartesian coordinates in meters. The geoid - * is characterized by two parameters: the semimajor axis and the - * flattening. - * - * @note The astronomical zenith is defined as the perpendicular to - * the real geoid. The geodetic zenith is the perpendicular to the - * standard geoid. Both zeniths differ due to local gravitational - * anomalies. - * - * Algorithm is from "GPS Satellite Surveying", A. Leick, page 184. - */ - void geodToCart(); - - /** The geoid is an elliposid which fits the shape of the Earth. It is - * characterized by two parameters: the semimajor axis and the - * flattening. - * - * @param i is the index which allows to identify the parameters for the - * chosen elliposid. 1="IAU76", 2="GRS80", 3="MERIT83", 4="WGS84", - * 5="IERS89". - */ - void setEllipsoid(int i); - - dms GSTtoLST(const dms &gst) const { return dms(gst.Degrees() + Longitude.Degrees()); } - dms LSTtoGST(const dms &lst) const { return dms(lst.Degrees() - Longitude.Degrees()); } - - KStarsDateTime UTtoLT(const KStarsDateTime &ut) const; - KStarsDateTime LTtoUT(const KStarsDateTime <) const; - - /** Computes the velocity in km/s of an observer on the surface of the Earth - * referred to a system whose origin is the center of the Earth. The X and - * Y axis are contained in the equator and the X axis is towards the nodes - * line. The Z axis is along the poles. - * - * @param vtopo[] Topocentric velocity. The resultant velocity is available - * in this array. - * @param gt Greenwich sideral time for which we want to compute the topocentric velocity. - */ - void TopocentricVelocity(double vtopo[], const dms >); - - /** @return Local Mean Sidereal Time. - * @param jd Julian date - */ - double LMST(double jd); - - bool isReadOnly() const; - void setReadOnly(bool value); - - private: - CachingDms Longitude, Latitude; - QString Name, Province, Country; - TimeZoneRule *TZrule; - double TimeZone, Elevation; - double axis, flattening; - long double PosCartX, PosCartY, PosCartZ; - int indexEllipsoid; - bool ReadOnly; + double distanceTo(const dms &longitude, const dms &latitude); + + dms GSTtoLST(const dms &gst) const + { + return dms(gst.Degrees() + Longitude.Degrees()); + } + dms LSTtoGST(const dms &lst) const + { + return dms(lst.Degrees() - Longitude.Degrees()); + } + + KStarsDateTime UTtoLT(const KStarsDateTime &ut) const; + KStarsDateTime LTtoUT(const KStarsDateTime <) const; + + /** Computes the velocity in km/s of an observer on the surface of the Earth + * referred to a system whose origin is the center of the Earth. The X and + * Y axis are contained in the equator and the X axis is towards the nodes + * line. The Z axis is along the poles. + * + * @param vtopo[] Topocentric velocity. The resultant velocity is available + * in this array. + * @param gt Greenwich sideral time for which we want to compute the topocentric velocity. + */ + void TopocentricVelocity(double vtopo[], const dms >); + + /** @return Local Mean Sidereal Time. + * @param jd Julian date + */ + double LMST(double jd); + + bool isReadOnly() const; + void setReadOnly(bool value); + + private: + CachingDms Longitude, Latitude; + QString Name, Province, Country; + TimeZoneRule *TZrule; + double TimeZone, Elevation; + double axis, flattening; + long double PosCartX, PosCartY, PosCartZ; + int indexEllipsoid; + bool ReadOnly; }; diff --git a/kstars/kstarsdata.cpp b/kstars/kstarsdata.cpp index d55940cc3..08ab27251 100644 --- a/kstars/kstarsdata.cpp +++ b/kstars/kstarsdata.cpp @@ -1,1507 +1,1526 @@ /*************************************************************************** kstarsdata.cpp - K Desktop Planetarium ------------------- begin : Sun Jul 29 2001 copyright : (C) 2001 by Heiko Evermann email : heiko@evermann.de ***************************************************************************/ /*************************************************************************** * * * 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 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "kstarsdata.h" #include "ksutils.h" #include "Options.h" #include "auxiliary/kspaths.h" #include "skycomponents/supernovaecomponent.h" #include "skycomponents/skymapcomposite.h" #ifndef KSTARS_LITE #include "fov.h" #include "imageexporter.h" #include "kstars.h" #include "observinglist.h" #include "skymap.h" #include "dialogs/detaildialog.h" #include "oal/execute.h" #endif #ifndef KSTARS_LITE #include #endif #include #include #include #include "kstars_debug.h" namespace { // Report fatal error during data loading to user // Calls QApplication::exit void fatalErrorMessage(QString fname) { #ifndef KSTARS_LITE KMessageBox::sorry(nullptr, i18n("The file %1 could not be found. " "KStars cannot run properly without this file. " "KStars searches for this file in following locations:\n\n\t" "%2\n\n" "It appears that your setup is broken.", fname, QStandardPaths::standardLocations(QStandardPaths::DataLocation).join("\n\t")), i18n("Critical File Not Found: %1", fname)); // FIXME: Must list locations depending on file type #endif qDebug() << i18n("Critical File Not Found: %1", fname); qApp->exit(1); } // Report non-fatal error during data loading to user and ask // whether he wants to continue. // Calls QApplication::exit if he don't #if 1 bool nonFatalErrorMessage(QString fname) { #ifdef KSTARS_LITE Q_UNUSED(fname) #else int res = KMessageBox::warningContinueCancel(0, i18n("The file %1 could not be found. " "KStars can still run without this file. " "KStars search for this file in following locations:\n\n\t" "%2\n\n" "It appears that you setup is broken. Press Continue to run KStars without this file ", fname, QStandardPaths::standardLocations( QStandardPaths::DataLocation ).join("\n\t") ), i18n( "Non-Critical File Not Found: %1", fname )); // FIXME: Must list locations depending on file type if( res != KMessageBox::Continue ) qApp->exit(1); return res == KMessageBox::Continue; #endif return true; } #endif } KStarsData *KStarsData::pinstance = nullptr; KStarsData *KStarsData::Create() { // This method should never be called twice within a run, since a // lot of the code assumes that KStarsData, once created, is never // destroyed. They maintain local copies of KStarsData::Instance() // for efficiency (maybe this should change, but it is not // required to delete and reinstantiate KStarsData). Thus, when we // call this method, pinstance MUST be zero, i.e. this must be the // first (and last) time we are calling it. -- asimha Q_ASSERT(!pinstance); delete pinstance; pinstance = new KStarsData(); return pinstance; } KStarsData::KStarsData() : m_Geo(dms(0), dms(0)), m_ksuserdb(), m_catalogdb(), temporaryTrail(false), //locale( new KLocale( "kstars" ) ), m_preUpdateID(0), m_updateID(0), m_preUpdateNumID(0), m_updateNumID(0), m_preUpdateNum(J2000), m_updateNum(J2000) { #ifndef KSTARS_LITE m_LogObject.reset(new OAL::Log); #endif // at startup times run forward setTimeDirection(0.0); } KStarsData::~KStarsData() { Q_ASSERT(pinstance); -//delete locale; + //delete locale; qDeleteAll(geoList); geoList.clear(); qDeleteAll(ADVtreeList); ADVtreeList.clear(); pinstance = nullptr; } bool KStarsData::initialize() { //Initialize CatalogDB// catalogdb()->Initialize(); //Load Time Zone Rules// emit progressText(i18n("Reading time zone rules")); if (!readTimeZoneRulebook()) { fatalErrorMessage("TZrules.dat"); return false; } emit progressText(i18n("Upgrade existing user city db to support geographic elevation.")); QString dbfile = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + "mycitydb.sqlite"; /// This code to add Height column to table city in mycitydb.sqlite is a transitional measure to support a meaningful /// geographic elevation. if (QFile::exists(dbfile)) { - QSqlDatabase fixcitydb = QSqlDatabase::addDatabase("QSQLITE", "fixcitydb"); + QSqlDatabase fixcitydb = QSqlDatabase::addDatabase("QSQLITE", "fixcitydb"); fixcitydb.setDatabaseName(dbfile); fixcitydb.open(); - if (fixcitydb.tables().contains("city",Qt::CaseInsensitive)) + if (fixcitydb.tables().contains("city", Qt::CaseInsensitive)) { QSqlRecord r = fixcitydb.record("city"); if (!r.contains("Elevation")) { emit progressText(i18n("Adding \"Elevation\" column to city table.")); QSqlQuery query(fixcitydb); if (query.exec("alter table city add column Elevation real default -10;") == false) { emit progressText(QString("failed to add Elevation column to city table in mycitydb.sqlite: &1").arg(query.lastError().text())); } } else { emit progressText(i18n("City table already contains \"Elevation\".")); } } else { emit progressText(i18n("City table missing from database.")); } fixcitydb.close(); } //Load Cities// emit progressText(i18n("Loading city data")); if (!readCityData()) { fatalErrorMessage("citydb.sqlite"); return false; } //Initialize User Database// emit progressText(i18n("Loading User Information")); m_ksuserdb.Initialize(); //Initialize SkyMapComposite// emit progressText(i18n("Loading sky objects")); m_SkyComposite.reset(new SkyMapComposite()); //Load Image URLs// //#ifndef Q_OS_ANDROID //On Android these 2 calls produce segfault. WARNING emit progressText(i18n("Loading Image URLs")); if( !readURLData( "image_url.dat", 0 ) && !nonFatalErrorMessage( "image_url.dat" ) ) return false; //QtConcurrent::run(this, &KStarsData::readURLData, QString("image_url.dat"), 0, false); //Load Information URLs// //emit progressText(i18n("Loading Information URLs")); if( !readURLData( "info_url.dat", 1 ) && !nonFatalErrorMessage( "info_url.dat" ) ) return false; QtConcurrent::run(this, &KStarsData::readURLData, QString("info_url.dat"), 1, false); -//#endif -//emit progressText( i18n("Loading Variable Stars" ) ); + //#endif + //emit progressText( i18n("Loading Variable Stars" ) ); #ifndef KSTARS_LITE //Initialize Observing List m_ObservingList = new ObservingList(); #endif readUserLog(); #ifndef KSTARS_LITE readADVTreeData(); #endif return true; } void KStarsData::updateTime(GeoLocation *geo, const bool automaticDSTchange) { // sync LTime with the simulation clock LTime = geo->UTtoLT(ut()); syncLST(); //Only check DST if (1) TZrule is not the empty rule, and (2) if we have crossed //the DST change date/time. if (!geo->tzrule()->isEmptyRule()) { if (TimeRunsForward) { // timedirection is forward // DST change happens if current date is bigger than next calculated dst change if (ut() > NextDSTChange) resetToNewDST(geo, automaticDSTchange); } else { // timedirection is backward // DST change happens if current date is smaller than next calculated dst change if (ut() < NextDSTChange) resetToNewDST(geo, automaticDSTchange); } } KSNumbers num(ut().djd()); if (std::abs(ut().djd() - LastNumUpdate.djd()) > 1.0) { LastNumUpdate = KStarsDateTime(ut().djd()); m_preUpdateNumID++; m_preUpdateNum = KSNumbers(num); skyComposite()->update(&num); } if (std::abs(ut().djd() - LastPlanetUpdate.djd()) > 0.01) { LastPlanetUpdate = KStarsDateTime(ut().djd()); skyComposite()->updateSolarSystemBodies(&num); } // Moon moves ~30 arcmin/hr, so update its position every minute. if (std::abs(ut().djd() - LastMoonUpdate.djd()) > 0.00069444) { LastMoonUpdate = ut(); skyComposite()->updateMoons(&num); } //Update Alt/Az coordinates. Timescale varies with zoom level //If Clock is in Manual Mode, always update. (?) if (std::abs(ut().djd() - LastSkyUpdate.djd()) > 0.1 / Options::zoomFactor() || clock()->isManualMode()) { LastSkyUpdate = ut(); m_preUpdateID++; //omit KSNumbers arg == just update Alt/Az coords // <-- Eh? -- asimha. Looks like this behavior / ideology has changed drastically. skyComposite()->update(&num); emit skyUpdate(clock()->isManualMode()); } } void KStarsData::syncUpdateIDs() { m_updateID = m_preUpdateID; if (m_updateNumID == m_preUpdateNumID) return; m_updateNumID = m_preUpdateNumID; m_updateNum = KSNumbers(m_preUpdateNum); } unsigned int KStarsData::incUpdateID() { m_preUpdateID++; m_preUpdateNumID++; syncUpdateIDs(); return m_updateID; } void KStarsData::setFullTimeUpdate() { //Set the update markers to invalid dates to trigger updates in each category LastSkyUpdate = KStarsDateTime(QDateTime()); LastPlanetUpdate = KStarsDateTime(QDateTime()); LastMoonUpdate = KStarsDateTime(QDateTime()); LastNumUpdate = KStarsDateTime(QDateTime()); } void KStarsData::syncLST() { LST = geo()->GSTtoLST(ut().gst()); } void KStarsData::changeDateTime(const KStarsDateTime &newDate) { //Turn off animated slews for the next time step. setSnapNextFocus(); clock()->setUTC(newDate); LTime = geo()->UTtoLT(ut()); //set local sideral time syncLST(); //Make sure Numbers, Moon, planets, and sky objects are updated immediately setFullTimeUpdate(); // reset tzrules data with new local time and time direction (forward or backward) geo()->tzrule()->reset_with_ltime(LTime, geo()->TZ0(), isTimeRunningForward()); // reset next dst change time setNextDSTChange(geo()->tzrule()->nextDSTChange()); } void KStarsData::resetToNewDST(GeoLocation *geo, const bool automaticDSTchange) { // reset tzrules data with local time, timezone offset and time direction (forward or backward) // force a DST change with option true for 3. parameter geo->tzrule()->reset_with_ltime(LTime, geo->TZ0(), TimeRunsForward, automaticDSTchange); // reset next DST change time setNextDSTChange(geo->tzrule()->nextDSTChange()); //reset LTime, because TZoffset has changed LTime = geo->UTtoLT(ut()); } void KStarsData::setTimeDirection(float scale) { TimeRunsForward = scale >= 0; } GeoLocation *KStarsData::locationNamed(const QString &city, const QString &province, const QString &country) { foreach (GeoLocation *loc, geoList) { if (loc->translatedName() == city && (province.isEmpty() || loc->translatedProvince() == province) && - (country.isEmpty() || loc->translatedCountry() == country)) + (country.isEmpty() || loc->translatedCountry() == country)) { return loc; } } return nullptr; } +GeoLocation *KStarsData::nearestLocation(double longitude, double latitude) +{ + GeoLocation *nearest = nullptr; + double distance = 1e6; + + dms lng(longitude), lat(latitude); + for (auto oneCity : geoList) + { + double newDistance = oneCity->distanceTo(lng, lat); + if (newDistance < distance) + { + distance = newDistance; + nearest = oneCity; + } + } + + return nearest; +} + void KStarsData::setLocationFromOptions() { setLocation(GeoLocation(dms(Options::longitude()), dms(Options::latitude()), Options::cityName(), Options::provinceName(), Options::countryName(), Options::timeZone(), &(Rulebook[Options::dST()]), Options::elevation(), false, 4)); } void KStarsData::setLocation(const GeoLocation &l) { m_Geo = GeoLocation(l); if (m_Geo.lat()->Degrees() >= 90.0) m_Geo.setLat(dms(89.99)); if (m_Geo.lat()->Degrees() <= -90.0) m_Geo.setLat(dms(-89.99)); //store data in the Options objects Options::setCityName(m_Geo.name()); Options::setProvinceName(m_Geo.province()); Options::setCountryName(m_Geo.country()); Options::setTimeZone(m_Geo.TZ0()); Options::setElevation(m_Geo.elevation()); Options::setLongitude(m_Geo.lng()->Degrees()); Options::setLatitude(m_Geo.lat()->Degrees()); // set the rule from rulebook foreach (const QString &key, Rulebook.keys()) { if (!key.isEmpty() && m_Geo.tzrule()->equals(&Rulebook[key])) Options::setDST(key); } emit geoChanged(); } SkyObject *KStarsData::objectNamed(const QString &name) { if ((name == "star") || (name == "nothing") || name.isEmpty()) return nullptr; return skyComposite()->findByName(name); } bool KStarsData::readCityData() { QSqlDatabase citydb = QSqlDatabase::addDatabase("QSQLITE", "citydb"); QString dbfile = KSPaths::locate(QStandardPaths::GenericDataLocation, "citydb.sqlite"); citydb.setDatabaseName(dbfile); if (citydb.open() == false) { qCCritical(KSTARS) << "Unable to open city database file " << dbfile << citydb.lastError().text(); return false; } QSqlQuery get_query(citydb); //get_query.prepare("SELECT * FROM city"); if (!get_query.exec("SELECT * FROM city")) { qCCritical(KSTARS) << get_query.lastError(); return false; } bool citiesFound = false; // get_query.size() always returns -1 so we set citiesFound if at least one city is found while (get_query.next()) { citiesFound = true; QString name = get_query.value(1).toString(); QString province = get_query.value(2).toString(); QString country = get_query.value(3).toString(); dms lat = dms(get_query.value(4).toString()); dms lng = dms(get_query.value(5).toString()); double TZ = get_query.value(6).toDouble(); TimeZoneRule *TZrule = &(Rulebook[get_query.value(7).toString()]); double elevation = get_query.value(8).toDouble(); // appends city names to list - geoList.append(new GeoLocation(lng, lat, name, province, country, TZ, TZrule, elevation, true,4)); + geoList.append(new GeoLocation(lng, lat, name, province, country, TZ, TZrule, elevation, true, 4)); } citydb.close(); // Reading local database QSqlDatabase mycitydb = QSqlDatabase::addDatabase("QSQLITE", "mycitydb"); dbfile = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + QDir::separator() + "mycitydb.sqlite"; if (QFile::exists(dbfile)) { mycitydb.setDatabaseName(dbfile); if (mycitydb.open()) { QSqlQuery get_query(mycitydb); if (!get_query.exec("SELECT * FROM city")) { qDebug() << get_query.lastError(); return false; } while (get_query.next()) { QString name = get_query.value(1).toString(); QString province = get_query.value(2).toString(); QString country = get_query.value(3).toString(); dms lat = dms(get_query.value(4).toString()); dms lng = dms(get_query.value(5).toString()); double TZ = get_query.value(6).toDouble(); TimeZoneRule *TZrule = &(Rulebook[get_query.value(7).toString()]); double elevation = get_query.value(8).toDouble(); // appends city names to list - geoList.append(new GeoLocation(lng, lat, name, province, country, TZ, TZrule, elevation, false,4)); + geoList.append(new GeoLocation(lng, lat, name, province, country, TZ, TZrule, elevation, false, 4)); } mycitydb.close(); } } return citiesFound; } bool KStarsData::readTimeZoneRulebook() { QFile file; if (KSUtils::openDataFile(file, "TZrules.dat")) { QTextStream stream(&file); while (!stream.atEnd()) { QString line = stream.readLine().trimmed(); if (line.length() && !line.startsWith('#')) //ignore commented and blank lines { QStringList fields = line.split(' ', QString::SkipEmptyParts); QString id = fields[0]; QTime stime = QTime(fields[3].leftRef(fields[3].indexOf(':')).toInt(), - fields[3].midRef(fields[3].indexOf(':') + 1, fields[3].length()).toInt()); + fields[3].midRef(fields[3].indexOf(':') + 1, fields[3].length()).toInt()); QTime rtime = QTime(fields[6].leftRef(fields[6].indexOf(':')).toInt(), - fields[6].midRef(fields[6].indexOf(':') + 1, fields[6].length()).toInt()); + fields[6].midRef(fields[6].indexOf(':') + 1, fields[6].length()).toInt()); Rulebook[id] = TimeZoneRule(fields[1], fields[2], stime, fields[4], fields[5], rtime); } } return true; } else { return false; } } bool KStarsData::openUrlFile(const QString &urlfile, QFile &file) { //QFile file; QString localFile; bool fileFound = false; QFile localeFile; //if ( locale->language() != "en_US" ) if (QLocale().language() != QLocale::English) //localFile = locale->language() + '/' + urlfile; localFile = QLocale().languageToString(QLocale().language()) + '/' + urlfile; if (!localFile.isEmpty() && KSUtils::openDataFile(file, localFile)) { fileFound = true; } else { // Try to load locale file, if not successful, load regular urlfile and then copy it to locale. file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + urlfile); if (file.open(QIODevice::ReadOnly)) { //local file found. Now, if global file has newer timestamp, then merge the two files. //First load local file into QStringList bool newDataFound(false); QStringList urlData; QTextStream lStream(&file); while (!lStream.atEnd()) urlData.append(lStream.readLine()); //Find global file(s) in findAllResources() list. QFileInfo fi_local(file.fileName()); QStringList flist = KSPaths::locateAll(QStandardPaths::DataLocation, urlfile); for (int i = 0; i < flist.size(); i++) { if (flist[i] != file.fileName()) { QFileInfo fi_global(flist[i]); //Is this global file newer than the local file? if (fi_global.lastModified() > fi_local.lastModified()) { //Global file has newer timestamp than local. Add lines in global file that don't already exist in local file. //be smart about this; in some cases the URL is updated but the image is probably the same if its //label string is the same. So only check strings up to last ":" QFile globalFile(flist[i]); if (globalFile.open(QIODevice::ReadOnly)) { QTextStream gStream(&globalFile); while (!gStream.atEnd()) { QString line = gStream.readLine(); //If global-file line begins with "XXX:" then this line should be removed from the local file. if (line.startsWith(QLatin1String("XXX:")) && urlData.contains(line.mid(4))) { urlData.removeAt(urlData.indexOf(line.mid(4))); } else { //does local file contain the current global file line, up to second ':' ? bool linefound(false); for (int j = 0; j < urlData.size(); ++j) { if (urlData[j].contains(line.left(line.indexOf(':', line.indexOf(':') + 1)))) { //replace line in urlData with its equivalent in the newer global file. urlData.replace(j, line); if (!newDataFound) newDataFound = true; linefound = true; break; } } if (!linefound) { urlData.append(line); if (!newDataFound) newDataFound = true; } } } } } } } file.close(); //(possibly) write appended local file if (newDataFound) { if (file.open(QIODevice::WriteOnly)) { QTextStream outStream(&file); for (int i = 0; i < urlData.size(); i++) { outStream << urlData[i] << endl; } file.close(); } } if (file.open(QIODevice::ReadOnly)) fileFound = true; } else { if (KSUtils::openDataFile(file, urlfile)) { if (QLocale().language() != QLocale::English) qDebug() << "No localized URL file; using default English file."; // we found urlfile, we need to copy it to locale localeFile.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + urlfile); if (localeFile.open(QIODevice::WriteOnly)) { QTextStream readStream(&file); QTextStream writeStream(&localeFile); while (!readStream.atEnd()) { QString line = readStream.readLine(); if (!line.startsWith(QLatin1String("XXX:"))) //do not write "deleted" lines writeStream << line << endl; } localeFile.close(); file.reset(); } else { qDebug() << "Failed to copy default URL file to locale folder, modifying default object links is " - "not possible"; + "not possible"; } fileFound = true; } } } return fileFound; } // FIXME: This is a significant contributor to KStars start-up time bool KStarsData::readURLData(const QString &urlfile, int type, bool deepOnly) { #ifndef KSTARS_LITE if (KStars::Closing) return true; #endif QFile file; if (!openUrlFile(urlfile, file)) return false; QTextStream stream(&file); while (!stream.atEnd()) { QString line = stream.readLine(); //ignore comment lines if (!line.startsWith('#')) { #ifndef KSTARS_LITE if (KStars::Closing) { file.close(); return true; } #endif int idx = line.indexOf(':'); QString name = line.left(idx); if (name == "XXX") continue; QString sub = line.mid(idx + 1); idx = sub.indexOf(':'); QString title = sub.left(idx); QString url = sub.mid(idx + 1); // Dirty hack to fix things up for planets SkyObject *o; if (name == "Mercury" || name == "Venus" || name == "Mars" || name == "Jupiter" || name == "Saturn" || - name == "Uranus" || name == "Neptune" /* || name == "Pluto" */) + name == "Uranus" || name == "Neptune" /* || name == "Pluto" */) o = skyComposite()->findByName(i18n(name.toLocal8Bit().data())); else o = skyComposite()->findByName(name); if (!o) { qCWarning(KSTARS) << i18n("Object named %1 not found", name); } else { if (!deepOnly || (o->type() > 2 && o->type() < 9)) { if (type == 0) //image URL { o->ImageList().append(url); o->ImageTitle().append(title); } else if (type == 1) //info URL { o->InfoList().append(url); o->InfoTitle().append(title); } } } } } file.close(); return true; } // FIXME: Improve the user log system // Note: It might be very nice to keep the log in plaintext files, for // portability, human-readability, and greppability. However, it takes // a lot of time to parse and look up, is very messy from the // reliability and programming point of view, needs to be parsed at // start, can become corrupt easily because of a missing bracket... // An SQLite database is a good compromise. A user can easily view it // using an SQLite browser. There is no need to read at start-up, one // can read the log when required. Easy to edit logs / update logs // etc. Will not become corrupt. Needn't be parsed. // However, IMHO, it is best to put these kinds of things in separate // databases, instead of unifying them as a table under the user // database. This ensures portability and a certain robustness that if // a user opens it, they cannot incorrectly edit a part of the DB they // did not intend to edit. // --asimha 2016 Aug 17 // FIXME: This is a significant contributor to KStars startup time. bool KStarsData::readUserLog() { QFile file; QString buffer; QString sub, name, data; if (!KSUtils::openDataFile(file, "userlog.dat")) return false; QTextStream stream(&file); if (!stream.atEnd()) buffer = stream.readAll(); while (!buffer.isEmpty()) { int startIndex, endIndex; startIndex = buffer.indexOf(QLatin1String("[KSLABEL:")); sub = buffer.mid(startIndex); // FIXME: This is inefficient because we are making a copy of a huge string! endIndex = sub.indexOf(QLatin1String("[KSLogEnd]")); // Read name after KSLABEL identifier name = sub.mid(startIndex + 9, sub.indexOf(']') - (startIndex + 9)); // Read data and skip new line data = sub.mid(sub.indexOf(']') + 2, endIndex - (sub.indexOf(']') + 2)); buffer = buffer.mid(endIndex + 11); //Find the sky object named 'name'. //Note that ObjectNameList::find() looks for the ascii representation //of star genetive names, so stars are identified that way in the user log. SkyObject *o = skyComposite()->findByName(name); if (!o) { qWarning() << name << " not found"; } else { o->userLog() = data; } } // end while file.close(); return true; } bool KStarsData::readADVTreeData() { QFile file; QString Interface; QString Name, Link, subName; if (!KSUtils::openDataFile(file, "advinterface.dat")) return false; QTextStream stream(&file); QString Line; while (!stream.atEnd()) { int Type, interfaceIndex; Line = stream.readLine(); if (Line.startsWith(QLatin1String("[KSLABEL]"))) { Name = Line.mid(9); Type = 0; } else if (Line.startsWith(QLatin1String("[END]"))) Type = 1; else if (Line.startsWith(QLatin1String("[KSINTERFACE]"))) { Interface = Line.mid(13); continue; } else { int idx = Line.indexOf(':'); Name = Line.left(idx); Link = Line.mid(idx + 1); // Link is empty, using Interface instead if (Link.isEmpty()) { Link = Interface; subName = Name; interfaceIndex = Link.indexOf(QLatin1String("KSINTERFACE")); Link.remove(interfaceIndex, 11); Link = Link.insert(interfaceIndex, subName.replace(' ', '+')); } Type = 2; } ADVTreeData *ADVData = new ADVTreeData; ADVData->Name = Name; ADVData->Link = Link; ADVData->Type = Type; ADVtreeList.append(ADVData); } return true; } //There's a lot of code duplication here, but it's not avoidable because //this function is only called from main.cpp when the user is using //"dump" mode to produce an image from the command line. In this mode, //there is no KStars object, so none of the DBus functions can be called //directly. bool KStarsData::executeScript(const QString &scriptname, SkyMap *map) { #ifndef KSTARS_LITE int cmdCount(0); QFile f(scriptname); if (!f.open(QIODevice::ReadOnly)) { qDebug() << "Could not open file " << f.fileName(); return false; } QTextStream istream(&f); while (!istream.atEnd()) { QString line = istream.readLine(); line.remove("string:"); line.remove("int:"); line.remove("double:"); line.remove("bool:"); //find a dbus line and extract the function name and its arguments //The function name starts after the last occurrence of "org.kde.kstars." //or perhaps "org.kde.kstars.SimClock.". if (line.startsWith(QString("dbus-send"))) { QString funcprefix = "org.kde.kstars.SimClock."; int i = line.lastIndexOf(funcprefix); if (i >= 0) { i += funcprefix.length(); } else { funcprefix = "org.kde.kstars."; i = line.lastIndexOf(funcprefix); if (i >= 0) { i += funcprefix.length(); } } if (i < 0) { qWarning() << "Could not parse line: " << line; return false; } QStringList fn = line.mid(i).split(' '); //DEBUG //qDebug() << fn << endl; if (fn[0] == "lookTowards" && fn.size() >= 2) { double az(-1.0); QString arg = fn[1].toLower(); if (arg == "n" || arg == "north") az = 0.0; if (arg == "ne" || arg == "northeast") az = 45.0; if (arg == "e" || arg == "east") az = 90.0; if (arg == "se" || arg == "southeast") az = 135.0; if (arg == "s" || arg == "south") az = 180.0; if (arg == "sw" || arg == "southwest") az = 225.0; if (arg == "w" || arg == "west") az = 270.0; if (arg == "nw" || arg == "northwest") az = 335.0; if (az >= 0.0) { map->setFocusAltAz(dms(90.0), map->focus()->az()); map->focus()->HorizontalToEquatorial(&LST, geo()->lat()); map->setDestination(*map->focus()); cmdCount++; } if (arg == "z" || arg == "zenith") { map->setFocusAltAz(dms(90.0), map->focus()->az()); map->focus()->HorizontalToEquatorial(&LST, geo()->lat()); map->setDestination(*map->focus()); cmdCount++; } //try a named object. The name is everything after fn[0], //concatenated with spaces. fn.removeAll(fn.first()); QString objname = fn.join(" "); SkyObject *target = objectNamed(objname); if (target) { map->setFocus(target); map->focus()->EquatorialToHorizontal(&LST, geo()->lat()); map->setDestination(*map->focus()); cmdCount++; } } else if (fn[0] == "setRaDec" && fn.size() == 3) { bool ok(false); dms r(0.0), d(0.0); ok = r.setFromString(fn[1], false); //assume angle in hours if (ok) ok = d.setFromString(fn[2], true); //assume angle in degrees if (ok) { map->setFocus(r, d); map->focus()->EquatorialToHorizontal(&LST, geo()->lat()); cmdCount++; } } else if (fn[0] == "setAltAz" && fn.size() == 3) { bool ok(false); dms az(0.0), alt(0.0); ok = alt.setFromString(fn[1]); if (ok) ok = az.setFromString(fn[2]); if (ok) { map->setFocusAltAz(alt, az); map->focus()->HorizontalToEquatorial(&LST, geo()->lat()); cmdCount++; } } else if (fn[0] == "loadColorScheme") { fn.removeAll(fn.first()); QString csName = fn.join(" ").remove('\"'); qCDebug(KSTARS) << "Loading Color scheme: " << csName; QString filename = csName.toLower().trimmed(); bool ok(false); //Parse default names which don't follow the regular file-naming scheme if (csName == i18nc("use default color scheme", "Default Colors")) filename = "classic.colors"; if (csName == i18nc("use 'star chart' color scheme", "Star Chart")) filename = "chart.colors"; if (csName == i18nc("use 'night vision' color scheme", "Night Vision")) filename = "night.colors"; //Try the filename if it ends with ".colors" if (filename.endsWith(QLatin1String(".colors"))) ok = colorScheme()->load(filename); //If that didn't work, try assuming that 'name' is the color scheme name //convert it to a filename exactly as ColorScheme::save() does if (!ok) { if (!filename.isEmpty()) { for (int i = 0; i < filename.length(); ++i) if (filename.at(i) == ' ') filename.replace(i, 1, "-"); filename = filename.append(".colors"); ok = colorScheme()->load(filename); } if (!ok) qDebug() << QString("Unable to load color scheme named %1. Also tried %2.") - .arg(csName, filename); + .arg(csName, filename); } } else if (fn[0] == "zoom" && fn.size() == 2) { bool ok(false); double z = fn[1].toDouble(&ok); if (ok) { if (z > MAXZOOM) z = MAXZOOM; if (z < MINZOOM) z = MINZOOM; Options::setZoomFactor(z); cmdCount++; } } else if (fn[0] == "zoomIn") { if (Options::zoomFactor() < MAXZOOM) { Options::setZoomFactor(Options::zoomFactor() * DZOOM); cmdCount++; } } else if (fn[0] == "zoomOut") { if (Options::zoomFactor() > MINZOOM) { Options::setZoomFactor(Options::zoomFactor() / DZOOM); cmdCount++; } } else if (fn[0] == "defaultZoom") { Options::setZoomFactor(DEFAULTZOOM); cmdCount++; } else if (fn[0] == "setLocalTime" && fn.size() == 7) { bool ok(false); // min is a macro - use mnt int yr(0), mth(0), day(0), hr(0), mnt(0), sec(0); yr = fn[1].toInt(&ok); if (ok) mth = fn[2].toInt(&ok); if (ok) day = fn[3].toInt(&ok); if (ok) hr = fn[4].toInt(&ok); if (ok) mnt = fn[5].toInt(&ok); if (ok) sec = fn[6].toInt(&ok); if (ok) { changeDateTime(geo()->LTtoUT(KStarsDateTime(QDate(yr, mth, day), QTime(hr, mnt, sec)))); cmdCount++; } else { qWarning() << ki18n("Could not set time: %1 / %2 / %3 ; %4:%5:%6") - .subs(day) - .subs(mth) - .subs(yr) - .subs(hr) - .subs(mnt) - .subs(sec) - .toString() + .subs(day) + .subs(mth) + .subs(yr) + .subs(hr) + .subs(mnt) + .subs(sec) + .toString() << endl; } } else if (fn[0] == "changeViewOption" && fn.size() == 3) { bool bOk(false), dOk(false); //parse bool value bool bVal(false); if (fn[2].toLower() == "true") { bOk = true; bVal = true; } if (fn[2].toLower() == "false") { bOk = true; bVal = false; } if (fn[2] == "1") { bOk = true; bVal = true; } if (fn[2] == "0") { bOk = true; bVal = false; } //parse double value double dVal = fn[2].toDouble(&dOk); // FIXME: REGRESSION // if ( fn[1] == "FOVName" ) { Options::setFOVName( fn[2] ); cmdCount++; } // if ( fn[1] == "FOVSizeX" && dOk ) { Options::setFOVSizeX( (float)dVal ); cmdCount++; } // if ( fn[1] == "FOVSizeY" && dOk ) { Options::setFOVSizeY( (float)dVal ); cmdCount++; } // if ( fn[1] == "FOVShape" && nOk ) { Options::setFOVShape( nVal ); cmdCount++; } // if ( fn[1] == "FOVColor" ) { Options::setFOVColor( fn[2] ); cmdCount++; } if (fn[1] == "ShowStars" && bOk) { Options::setShowStars(bVal); cmdCount++; } if (fn[1] == "ShowMessier" && bOk) { Options::setShowMessier(bVal); cmdCount++; } if (fn[1] == "ShowMessierImages" && bOk) { Options::setShowMessierImages(bVal); cmdCount++; } if (fn[1] == "ShowCLines" && bOk) { Options::setShowCLines(bVal); cmdCount++; } if (fn[1] == "ShowCNames" && bOk) { Options::setShowCNames(bVal); cmdCount++; } if (fn[1] == "ShowNGC" && bOk) { Options::setShowNGC(bVal); cmdCount++; } if (fn[1] == "ShowIC" && bOk) { Options::setShowIC(bVal); cmdCount++; } if (fn[1] == "ShowMilkyWay" && bOk) { Options::setShowMilkyWay(bVal); cmdCount++; } if (fn[1] == "ShowEquatorialGrid" && bOk) { Options::setShowEquatorialGrid(bVal); cmdCount++; } if (fn[1] == "ShowHorizontalGrid" && bOk) { Options::setShowHorizontalGrid(bVal); cmdCount++; } if (fn[1] == "ShowEquator" && bOk) { Options::setShowEquator(bVal); cmdCount++; } if (fn[1] == "ShowEcliptic" && bOk) { Options::setShowEcliptic(bVal); cmdCount++; } if (fn[1] == "ShowHorizon" && bOk) { Options::setShowHorizon(bVal); cmdCount++; } if (fn[1] == "ShowGround" && bOk) { Options::setShowGround(bVal); cmdCount++; } if (fn[1] == "ShowSun" && bOk) { Options::setShowSun(bVal); cmdCount++; } if (fn[1] == "ShowMoon" && bOk) { Options::setShowMoon(bVal); cmdCount++; } if (fn[1] == "ShowMercury" && bOk) { Options::setShowMercury(bVal); cmdCount++; } if (fn[1] == "ShowVenus" && bOk) { Options::setShowVenus(bVal); cmdCount++; } if (fn[1] == "ShowMars" && bOk) { Options::setShowMars(bVal); cmdCount++; } if (fn[1] == "ShowJupiter" && bOk) { Options::setShowJupiter(bVal); cmdCount++; } if (fn[1] == "ShowSaturn" && bOk) { Options::setShowSaturn(bVal); cmdCount++; } if (fn[1] == "ShowUranus" && bOk) { Options::setShowUranus(bVal); cmdCount++; } if (fn[1] == "ShowNeptune" && bOk) { Options::setShowNeptune(bVal); cmdCount++; } //if ( fn[1] == "ShowPluto" && bOk ) { Options::setShowPluto( bVal ); cmdCount++; } if (fn[1] == "ShowAsteroids" && bOk) { Options::setShowAsteroids(bVal); cmdCount++; } if (fn[1] == "ShowComets" && bOk) { Options::setShowComets(bVal); cmdCount++; } if (fn[1] == "ShowSolarSystem" && bOk) { Options::setShowSolarSystem(bVal); cmdCount++; } if (fn[1] == "ShowDeepSky" && bOk) { Options::setShowDeepSky(bVal); cmdCount++; } if (fn[1] == "ShowSupernovae" && bOk) { Options::setShowSupernovae(bVal); cmdCount++; } if (fn[1] == "ShowStarNames" && bOk) { Options::setShowStarNames(bVal); cmdCount++; } if (fn[1] == "ShowStarMagnitudes" && bOk) { Options::setShowStarMagnitudes(bVal); cmdCount++; } if (fn[1] == "ShowAsteroidNames" && bOk) { Options::setShowAsteroidNames(bVal); cmdCount++; } if (fn[1] == "ShowCometNames" && bOk) { Options::setShowCometNames(bVal); cmdCount++; } if (fn[1] == "ShowPlanetNames" && bOk) { Options::setShowPlanetNames(bVal); cmdCount++; } if (fn[1] == "ShowPlanetImages" && bOk) { Options::setShowPlanetImages(bVal); cmdCount++; } if (fn[1] == "UseAltAz" && bOk) { Options::setUseAltAz(bVal); cmdCount++; } if (fn[1] == "UseRefraction" && bOk) { Options::setUseRefraction(bVal); cmdCount++; } if (fn[1] == "UseAutoLabel" && bOk) { Options::setUseAutoLabel(bVal); cmdCount++; } if (fn[1] == "UseAutoTrail" && bOk) { Options::setUseAutoTrail(bVal); cmdCount++; } if (fn[1] == "UseAnimatedSlewing" && bOk) { Options::setUseAnimatedSlewing(bVal); cmdCount++; } if (fn[1] == "FadePlanetTrails" && bOk) { Options::setFadePlanetTrails(bVal); cmdCount++; } if (fn[1] == "SlewTimeScale" && dOk) { Options::setSlewTimeScale(dVal); cmdCount++; } if (fn[1] == "ZoomFactor" && dOk) { Options::setZoomFactor(dVal); cmdCount++; } // if ( fn[1] == "MagLimitDrawStar" && dOk ) { Options::setMagLimitDrawStar( dVal ); cmdCount++; } if (fn[1] == "StarDensity" && dOk) { Options::setStarDensity(dVal); cmdCount++; } // if ( fn[1] == "MagLimitDrawStarZoomOut" && dOk ) { Options::setMagLimitDrawStarZoomOut( dVal ); cmdCount++; } if (fn[1] == "MagLimitDrawDeepSky" && dOk) { Options::setMagLimitDrawDeepSky(dVal); cmdCount++; } if (fn[1] == "MagLimitDrawDeepSkyZoomOut" && dOk) { Options::setMagLimitDrawDeepSkyZoomOut(dVal); cmdCount++; } if (fn[1] == "StarLabelDensity" && dOk) { Options::setStarLabelDensity(dVal); cmdCount++; } if (fn[1] == "MagLimitHideStar" && dOk) { Options::setMagLimitHideStar(dVal); cmdCount++; } if (fn[1] == "MagLimitAsteroid" && dOk) { Options::setMagLimitAsteroid(dVal); cmdCount++; } if (fn[1] == "AsteroidLabelDensity" && dOk) { Options::setAsteroidLabelDensity(dVal); cmdCount++; } if (fn[1] == "MaxRadCometName" && dOk) { Options::setMaxRadCometName(dVal); cmdCount++; } //these three are a "radio group" if (fn[1] == "UseLatinConstellationNames" && bOk) { Options::setUseLatinConstellNames(true); Options::setUseLocalConstellNames(false); Options::setUseAbbrevConstellNames(false); cmdCount++; } if (fn[1] == "UseLocalConstellationNames" && bOk) { Options::setUseLatinConstellNames(false); Options::setUseLocalConstellNames(true); Options::setUseAbbrevConstellNames(false); cmdCount++; } if (fn[1] == "UseAbbrevConstellationNames" && bOk) { Options::setUseLatinConstellNames(false); Options::setUseLocalConstellNames(false); Options::setUseAbbrevConstellNames(true); cmdCount++; } } else if (fn[0] == "setGeoLocation" && (fn.size() == 3 || fn.size() == 4)) { QString city(fn[1]), province, country(fn[2]); province.clear(); if (fn.size() == 4) { province = fn[2]; country = fn[3]; } bool cityFound(false); foreach (GeoLocation *loc, geoList) { if (loc->translatedName() == city && - (province.isEmpty() || loc->translatedProvince() == province) && - loc->translatedCountry() == country) + (province.isEmpty() || loc->translatedProvince() == province) && + loc->translatedCountry() == country) { cityFound = true; setLocation(*loc); cmdCount++; break; } } if (!cityFound) qWarning() << i18n("Could not set location named %1, %2, %3", city, province, country); } } } //end while if (cmdCount) return true; #else Q_UNUSED(map) Q_UNUSED(scriptname) #endif return false; } #ifndef KSTARS_LITE void KStarsData::syncFOV() { visibleFOVs.clear(); // Add visible FOVs foreach (FOV *fov, availFOVs) { if (Options::fOVNames().contains(fov->name())) visibleFOVs.append(fov); } // Remove unavailable FOVs QSet names = QSet::fromList(Options::fOVNames()); QSet all; foreach (FOV *fov, visibleFOVs) { all.insert(fov->name()); } Options::setFOVNames(all.intersect(names).toList()); } // FIXME: Why does KStarsData store the Execute instance??? -- asimha Execute *KStarsData::executeSession() { if (!m_Execute.get()) m_Execute.reset(new Execute()); return m_Execute.get(); } // FIXME: Why does KStarsData store the ImageExporer instance??? KStarsData is supposed to work with no reference to KStars -- asimha ImageExporter *KStarsData::imageExporter() { if (!m_ImageExporter.get()) m_ImageExporter.reset(new ImageExporter(KStars::Instance())); return m_ImageExporter.get(); } #endif diff --git a/kstars/kstarsdata.h b/kstars/kstarsdata.h index 2940e28b1..6703a8a05 100644 --- a/kstars/kstarsdata.h +++ b/kstars/kstarsdata.h @@ -1,413 +1,499 @@ /*************************************************************************** kstarsdata.h - K Desktop Planetarium ------------------- begin : Sun Jul 29 2001 copyright : (C) 2001 by Heiko Evermann email : heiko@evermann.de ***************************************************************************/ /*************************************************************************** * * * 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 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #pragma once #include "catalogdb.h" #include "colorscheme.h" #include "geolocation.h" #include "ksnumbers.h" #include "kstarsdatetime.h" #include "ksuserdb.h" #include "simclock.h" #ifndef KSTARS_LITE #include "oal/oal.h" #include "oal/log.h" #endif #include #include #include #include #include #define MINZOOM 250. #define MAXZOOM 5000000. #define DEFAULTZOOM 2000. #define DZOOM 1.189207115 // 2^(1/4) #define AU_KM 1.49605e8 //km in one AU class QFile; class Execute; class FOV; class ImageExporter; class SkyMap; class SkyMapComposite; class SkyObject; class ObservingList; class TimeZoneRule; #ifdef KSTARS_LITE //Will go away when details window will be implemented in KStars Lite struct ADVTreeData { QString Name; QString Link; int Type; }; #else struct ADVTreeData; #endif /** * @class KStarsData * KStarsData is the backbone of KStars. It contains all the data used by KStars, * including the SkyMapComposite that contains all items in the skymap * (stars, deep-sky objects, planets, constellations, etc). Other kinds of data * are stored here as well: the geographic locations, the timezone rules, etc. * * @author Heiko Evermann * @version 1.0 */ class KStarsData : public QObject { - Q_OBJECT - - protected: - /** Constructor. */ - KStarsData(); - - public: - // FIXME: It uses temporary trail. There must be way to - // this better. And resumeKey in DBUS code - friend class KStars; - // FIXME: it uses temporary trail and resumeKey - friend class SkyMap; - // FIXME: uses geoList and changes it. - friend class LocationDialog; - friend class LocationDialogLite; - - static KStarsData *Create(); - - static inline KStarsData *Instance() { return pinstance; } - - /** - * Initialize KStarsData while running splash screen. - * @return true on success. - */ - bool initialize(); - - /** Destructor. Delete data objects. */ - ~KStarsData() override; - - /** - * Set the NextDSTChange member. - * Need this accessor because I could not make KStars::privatedata a friend - * class for some reason...:/ - */ - void setNextDSTChange(const KStarsDateTime &dt) { NextDSTChange = dt; } - - /** - * Returns true if time is running forward else false. Used by KStars to prevent - * double calculations of daylight saving change time. - */ - bool isTimeRunningForward() const { return TimeRunsForward; } - - /** @return pointer to the localization (KLocale) object */ - //KLocale *getLocale() { return locale; } - - /** - * @short Find object by name. - * @param name Object name to find - * @return pointer to SkyObject matching this name - */ - SkyObject *objectNamed(const QString &name); - - /** - * The Sky is updated more frequently than the moon, which is updated more frequently - * than the planets. The date of the last update for each category is recorded so we - * know when we need to do it again (see KStars::updateTime()). - * Initializing these to -1000000.0 ensures they will be updated immediately - * on the first call to KStars::updateTime(). - */ - void setFullTimeUpdate(); - - /** - * Change the current simulation date/time to the KStarsDateTime argument. - * Specified DateTime is always universal time. - * @param newDate the DateTime to set. - */ - void changeDateTime(const KStarsDateTime &newDate); - - /** @return pointer to the current simulation local time */ - const KStarsDateTime <() const { return LTime; } - - /** @return reference to the current simulation universal time */ - const KStarsDateTime &ut() const { return Clock.utc(); } - - /** Sync the LST with the simulation clock. */ - void syncLST(); - - /** @return pointer to SkyComposite */ - SkyMapComposite *skyComposite() { return m_SkyComposite.get(); } - - /** @return pointer to the ColorScheme object */ - ColorScheme *colorScheme() { return &CScheme; } - - /** @return name of current color scheme **/ - Q_INVOKABLE QString colorSchemeName() { return CScheme.fileName(); } - - /** @return pointer to the KSUserDB object */ - KSUserDB *userdb() { return &m_ksuserdb; } - - /** @return pointer to the Catalog DB object */ - CatalogDB *catalogdb() { return &m_catalogdb; } - - /** @return pointer to the simulation Clock object */ - Q_INVOKABLE SimClock *clock() { return &Clock; } - - /** @return pointer to the local sidereal time: a dms object */ - CachingDms *lst() { return &LST; } - - /** @return pointer to the GeoLocation object*/ - GeoLocation *geo() { return &m_Geo; } - - /** @return list of all geographic locations */ - QList &getGeoList() { return geoList; } - - GeoLocation *locationNamed(const QString &city, const QString &province = QString(), - const QString &country = QString()); - - /** - * Set the GeoLocation according to the argument. - * @param l reference to the new GeoLocation - */ - void setLocation(const GeoLocation &l); - - /** Set the GeoLocation according to the values stored in the configuration file. */ - void setLocationFromOptions(); - - /** Return map for daylight saving rules. */ - const QMap &getRulebook() const { return Rulebook; } - - /** @return whether the next Focus change will omit the slewing animation. */ - bool snapNextFocus() const { return snapToFocus; } - - /** - * Disable or re-enable the slewing animation for the next Focus change. - * @note If the user has turned off all animated slewing, setSnapNextFocus(false) - * will *NOT* enable animation on the next slew. A false argument would only - * be used if you have previously called setSnapNextFocus(true), but then decided - * you didn't want that after all. In other words, it's extremely unlikely you'd - * ever want to use setSnapNextFocus(false). - * @param b when true (the default), the next Focus change will omit the slewing - * animation. - */ - void setSnapNextFocus(bool b = true) { snapToFocus = b; } - - /** - * Execute a script. This function actually duplicates the DCOP functionality - * for those cases when invoking DCOP is not practical (i.e., when preparing - * a sky image in command-line dump mode). - * @param name the filename of the script to "execute". - * @param map pointer to the SkyMap object. - * @return true if the script was successfully parsed. - */ - bool executeScript(const QString &name, SkyMap *map); - - /** Synchronize list of visible FOVs and list of selected FOVs in Options */ - #ifndef KSTARS_LITE - void syncFOV(); - #endif - - /** - * @return the list of visible FOVs - */ - inline const QList getVisibleFOVs() const { return visibleFOVs; } - - /** - * @return the list of available FOVs - */ - inline const QList getAvailableFOVs() const { return availFOVs; } + Q_OBJECT + + protected: + /** Constructor. */ + KStarsData(); + + public: + // FIXME: It uses temporary trail. There must be way to + // this better. And resumeKey in DBUS code + friend class KStars; + // FIXME: it uses temporary trail and resumeKey + friend class SkyMap; + // FIXME: uses geoList and changes it. + friend class LocationDialog; + friend class LocationDialogLite; + + static KStarsData *Create(); + + static inline KStarsData *Instance() + { + return pinstance; + } + + /** + * Initialize KStarsData while running splash screen. + * @return true on success. + */ + bool initialize(); + + /** Destructor. Delete data objects. */ + ~KStarsData() override; + + /** + * Set the NextDSTChange member. + * Need this accessor because I could not make KStars::privatedata a friend + * class for some reason...:/ + */ + void setNextDSTChange(const KStarsDateTime &dt) + { + NextDSTChange = dt; + } + + /** + * Returns true if time is running forward else false. Used by KStars to prevent + * double calculations of daylight saving change time. + */ + bool isTimeRunningForward() const + { + return TimeRunsForward; + } + + /** @return pointer to the localization (KLocale) object */ + //KLocale *getLocale() { return locale; } + + /** + * @short Find object by name. + * @param name Object name to find + * @return pointer to SkyObject matching this name + */ + SkyObject *objectNamed(const QString &name); + + /** + * The Sky is updated more frequently than the moon, which is updated more frequently + * than the planets. The date of the last update for each category is recorded so we + * know when we need to do it again (see KStars::updateTime()). + * Initializing these to -1000000.0 ensures they will be updated immediately + * on the first call to KStars::updateTime(). + */ + void setFullTimeUpdate(); + + /** + * Change the current simulation date/time to the KStarsDateTime argument. + * Specified DateTime is always universal time. + * @param newDate the DateTime to set. + */ + void changeDateTime(const KStarsDateTime &newDate); + + /** @return pointer to the current simulation local time */ + const KStarsDateTime <() const + { + return LTime; + } + + /** @return reference to the current simulation universal time */ + const KStarsDateTime &ut() const + { + return Clock.utc(); + } + + /** Sync the LST with the simulation clock. */ + void syncLST(); + + /** @return pointer to SkyComposite */ + SkyMapComposite *skyComposite() + { + return m_SkyComposite.get(); + } + + /** @return pointer to the ColorScheme object */ + ColorScheme *colorScheme() + { + return &CScheme; + } + + /** @return name of current color scheme **/ + Q_INVOKABLE QString colorSchemeName() + { + return CScheme.fileName(); + } + + /** @return pointer to the KSUserDB object */ + KSUserDB *userdb() + { + return &m_ksuserdb; + } + + /** @return pointer to the Catalog DB object */ + CatalogDB *catalogdb() + { + return &m_catalogdb; + } + + /** @return pointer to the simulation Clock object */ + Q_INVOKABLE SimClock *clock() + { + return &Clock; + } + + /** @return pointer to the local sidereal time: a dms object */ + CachingDms *lst() + { + return &LST; + } + + /** @return pointer to the GeoLocation object*/ + GeoLocation *geo() + { + return &m_Geo; + } + + /** @return list of all geographic locations */ + QList &getGeoList() + { + return geoList; + } + + GeoLocation *locationNamed(const QString &city, const QString &province = QString(), + const QString &country = QString()); + + /** + * @brief nearestLocation Return nearest location to the given logntidue and latitude coordiantes + * @param longitude Longitude (-180 to +180) + * @param latitude Latitude (-90 to +90) + * @return nearest geographical location to the parameters above. + */ + GeoLocation *nearestLocation(double longitude, double latitude); + + /** + * Set the GeoLocation according to the argument. + * @param l reference to the new GeoLocation + */ + void setLocation(const GeoLocation &l); + + /** Set the GeoLocation according to the values stored in the configuration file. */ + void setLocationFromOptions(); + + /** Return map for daylight saving rules. */ + const QMap &getRulebook() const + { + return Rulebook; + } + + /** @return whether the next Focus change will omit the slewing animation. */ + bool snapNextFocus() const + { + return snapToFocus; + } + + /** + * Disable or re-enable the slewing animation for the next Focus change. + * @note If the user has turned off all animated slewing, setSnapNextFocus(false) + * will *NOT* enable animation on the next slew. A false argument would only + * be used if you have previously called setSnapNextFocus(true), but then decided + * you didn't want that after all. In other words, it's extremely unlikely you'd + * ever want to use setSnapNextFocus(false). + * @param b when true (the default), the next Focus change will omit the slewing + * animation. + */ + void setSnapNextFocus(bool b = true) + { + snapToFocus = b; + } + + /** + * Execute a script. This function actually duplicates the DCOP functionality + * for those cases when invoking DCOP is not practical (i.e., when preparing + * a sky image in command-line dump mode). + * @param name the filename of the script to "execute". + * @param map pointer to the SkyMap object. + * @return true if the script was successfully parsed. + */ + bool executeScript(const QString &name, SkyMap *map); + + /** Synchronize list of visible FOVs and list of selected FOVs in Options */ #ifndef KSTARS_LITE - /** Return log object */ - OAL::Log *logObject() { return m_LogObject.get(); } - - /** Return ADV Tree */ - QList avdTree() { return ADVtreeList; } - - inline ObservingList *observingList() const { return m_ObservingList; } - - ImageExporter *imageExporter(); + void syncFOV(); +#endif - Execute *executeSession(); + /** + * @return the list of visible FOVs + */ + inline const QList getVisibleFOVs() const + { + return visibleFOVs; + } + + /** + * @return the list of available FOVs + */ + inline const QList getAvailableFOVs() const + { + return availFOVs; + } +#ifndef KSTARS_LITE + /** Return log object */ + OAL::Log *logObject() + { + return m_LogObject.get(); + } + + /** Return ADV Tree */ + QList avdTree() + { + return ADVtreeList; + } + + inline ObservingList *observingList() const + { + return m_ObservingList; + } + + ImageExporter *imageExporter(); + + Execute *executeSession(); #endif - /*@short Increments the updateID, forcing a recomputation of star positions as well */ - unsigned int incUpdateID(); - - unsigned int updateID() const { return m_updateID; } - unsigned int updateNumID() const { return m_updateNumID; } - KSNumbers *updateNum() { return &m_updateNum; } - void syncUpdateIDs(); - - signals: - /** Signal that specifies the text that should be drawn in the KStarsSplash window. */ - void progressText(const QString &text); - - /** Should be used to refresh skymap. */ - void skyUpdate(bool); - - /** If data changed, emit clearCache signal. */ - void clearCache(); - - /** Emitted when geo location changed */ - void geoChanged(); - - public slots: - /** @short send a message to the console*/ - void slotConsoleMessage(QString s) { std::cout << (const char *)(s.toLocal8Bit()) << std::endl; } - - /** - * Update the Simulation Clock. Update positions of Planets. Update - * Alt/Az coordinates of objects. Update precession. - * emit the skyUpdate() signal so that SkyMap / whatever draws the sky can update itself - * - * This is ugly. - * It _will_ change! - * (JH:)hey, it's much less ugly now...can we lose the comment yet? :p - */ - void updateTime(GeoLocation *geo, const bool automaticDSTchange = true); - - /** - * Sets the direction of time and stores it in bool TimeRunForwards. If scale >= 0 - * time is running forward else time runs backward. We need this to calculate just - * one daylight saving change time (previous or next DST change). - */ - void setTimeDirection(float scale); - - private: - /** - * Populate list of geographic locations from "citydb.sqlite" database. Also check for custom - * locations file "mycitydb.sqlite" database, but don't require it. Each line in the file - * provides the information required to create one GeoLocation object. - * @short Fill list of geographic locations from file(s) - * @return true if at least one city read successfully. - * @see KStarsData::processCity() - */ - bool readCityData(); - - /** Read the data file that contains daylight savings time rules. */ - bool readTimeZoneRulebook(); - - //TODO JM: ADV tree should use XML instead - /** - * Read Advanced interface structure to be used later to construct the list view in - * the advanced tab in the Detail Dialog. - * @li KSLABEL designates a top-level parent label - * @li KSINTERFACE designates a common URL interface for several objects - * @li END designates the end of a sub tree structure - * @short read online database lookup structure. - * @return true if data is successfully read. - */ - bool readADVTreeData(); - - /** Read INDI hosts from an XML file */ - bool readINDIHosts(); - - //TODO JM: Use XML instead; The logger should have more features - // that allow users to enter details about their observation logs - // objects observed, eye pieces, telescope, conditions, mag..etc - /** - * @short read user logs. - * - * Read user logs. The log file is formatted as following: - * @li KSLABEL designates the beginning of a log - * @li KSLogEnd designates the end of a log. - * - * @return true if data is successfully read. - */ - bool readUserLog(); - - /** - * Read in URLs to be attached to a named object's right-click popup menu. At this - * point, there is no way to attach URLs to unnamed objects. There are two - * kinds of URLs, each with its own data file: image links and webpage links. In addition, - * there may be user-specific versions with custom URLs. Each line contains 3 fields - * separated by colons (":"). Note that the last field is the URL, and as such it will - * generally contain a colon itself. Only the first two colons encountered are treated - * as field separators. The fields are: - * - * @li Object name. This must be the "primary" name of the object (the name at the top of the popup menu). - * @li Menu text. The string that should appear in the popup menu to activate the link. - * @li URL. - * @short Read in image and information URLs. - * @return true if data files were successfully read. - */ - bool readURLData(const QString &url, int type = 0, bool deepOnly = false); - - /** - * @short open a file containing URL links. - * @param urlfile string representation of the filename to open - * @param file reference to the QFile object which will be opened to this file. - * @return true if file successfully opened. - */ - bool openUrlFile(const QString &urlfile, QFile &file); - - /** - * Reset local time to new daylight saving time. Use this function if DST has changed. - * Used by updateTime(). - */ - void resetToNewDST(GeoLocation *geo, const bool automaticDSTchange); - - QList ADVtreeList; - std::unique_ptr m_SkyComposite; - - GeoLocation m_Geo; - SimClock Clock; - KStarsDateTime LTime; - KSUserDB m_ksuserdb; - CatalogDB m_catalogdb; - ColorScheme CScheme; + /*@short Increments the updateID, forcing a recomputation of star positions as well */ + unsigned int incUpdateID(); + + unsigned int updateID() const + { + return m_updateID; + } + unsigned int updateNumID() const + { + return m_updateNumID; + } + KSNumbers *updateNum() + { + return &m_updateNum; + } + void syncUpdateIDs(); + + signals: + /** Signal that specifies the text that should be drawn in the KStarsSplash window. */ + void progressText(const QString &text); + + /** Should be used to refresh skymap. */ + void skyUpdate(bool); + + /** If data changed, emit clearCache signal. */ + void clearCache(); + + /** Emitted when geo location changed */ + void geoChanged(); + + public slots: + /** @short send a message to the console*/ + void slotConsoleMessage(QString s) + { + std::cout << (const char *)(s.toLocal8Bit()) << std::endl; + } + + /** + * Update the Simulation Clock. Update positions of Planets. Update + * Alt/Az coordinates of objects. Update precession. + * emit the skyUpdate() signal so that SkyMap / whatever draws the sky can update itself + * + * This is ugly. + * It _will_ change! + * (JH:)hey, it's much less ugly now...can we lose the comment yet? :p + */ + void updateTime(GeoLocation *geo, const bool automaticDSTchange = true); + + /** + * Sets the direction of time and stores it in bool TimeRunForwards. If scale >= 0 + * time is running forward else time runs backward. We need this to calculate just + * one daylight saving change time (previous or next DST change). + */ + void setTimeDirection(float scale); + + private: + /** + * Populate list of geographic locations from "citydb.sqlite" database. Also check for custom + * locations file "mycitydb.sqlite" database, but don't require it. Each line in the file + * provides the information required to create one GeoLocation object. + * @short Fill list of geographic locations from file(s) + * @return true if at least one city read successfully. + * @see KStarsData::processCity() + */ + bool readCityData(); + + /** Read the data file that contains daylight savings time rules. */ + bool readTimeZoneRulebook(); + + //TODO JM: ADV tree should use XML instead + /** + * Read Advanced interface structure to be used later to construct the list view in + * the advanced tab in the Detail Dialog. + * @li KSLABEL designates a top-level parent label + * @li KSINTERFACE designates a common URL interface for several objects + * @li END designates the end of a sub tree structure + * @short read online database lookup structure. + * @return true if data is successfully read. + */ + bool readADVTreeData(); + + /** Read INDI hosts from an XML file */ + bool readINDIHosts(); + + //TODO JM: Use XML instead; The logger should have more features + // that allow users to enter details about their observation logs + // objects observed, eye pieces, telescope, conditions, mag..etc + /** + * @short read user logs. + * + * Read user logs. The log file is formatted as following: + * @li KSLABEL designates the beginning of a log + * @li KSLogEnd designates the end of a log. + * + * @return true if data is successfully read. + */ + bool readUserLog(); + + /** + * Read in URLs to be attached to a named object's right-click popup menu. At this + * point, there is no way to attach URLs to unnamed objects. There are two + * kinds of URLs, each with its own data file: image links and webpage links. In addition, + * there may be user-specific versions with custom URLs. Each line contains 3 fields + * separated by colons (":"). Note that the last field is the URL, and as such it will + * generally contain a colon itself. Only the first two colons encountered are treated + * as field separators. The fields are: + * + * @li Object name. This must be the "primary" name of the object (the name at the top of the popup menu). + * @li Menu text. The string that should appear in the popup menu to activate the link. + * @li URL. + * @short Read in image and information URLs. + * @return true if data files were successfully read. + */ + bool readURLData(const QString &url, int type = 0, bool deepOnly = false); + + /** + * @short open a file containing URL links. + * @param urlfile string representation of the filename to open + * @param file reference to the QFile object which will be opened to this file. + * @return true if file successfully opened. + */ + bool openUrlFile(const QString &urlfile, QFile &file); + + /** + * Reset local time to new daylight saving time. Use this function if DST has changed. + * Used by updateTime(). + */ + void resetToNewDST(GeoLocation *geo, const bool automaticDSTchange); + + QList ADVtreeList; + std::unique_ptr m_SkyComposite; + + GeoLocation m_Geo; + SimClock Clock; + KStarsDateTime LTime; + KSUserDB m_ksuserdb; + CatalogDB m_catalogdb; + ColorScheme CScheme; #ifndef KSTARS_LITE - ObservingList* m_ObservingList { nullptr }; - std::unique_ptr m_LogObject; - std::unique_ptr m_Execute; - std::unique_ptr m_ImageExporter; + ObservingList* m_ObservingList { nullptr }; + std::unique_ptr m_LogObject; + std::unique_ptr m_Execute; + std::unique_ptr m_ImageExporter; #endif - //EquipmentWriter *m_equipmentWriter; + //EquipmentWriter *m_equipmentWriter; - bool TimeRunsForward { false }; - bool temporaryTrail { false }; - // FIXME: Used in SkyMap only. Check! - bool snapToFocus { false }; + bool TimeRunsForward { false }; + bool temporaryTrail { false }; + // FIXME: Used in SkyMap only. Check! + bool snapToFocus { false }; - //KLocale *locale; + //KLocale *locale; - CachingDms LST; + CachingDms LST; - QKeySequence resumeKey; + QKeySequence resumeKey; - QList availFOVs; // List of all available FOVs - QList visibleFOVs; // List of visible FOVs. Cached from Options::FOVNames + QList availFOVs; // List of all available FOVs + QList visibleFOVs; // List of visible FOVs. Cached from Options::FOVNames - KStarsDateTime LastNumUpdate, LastSkyUpdate, LastPlanetUpdate, LastMoonUpdate; - KStarsDateTime NextDSTChange; - // FIXME: Used in kstarsdcop.cpp only - KStarsDateTime StoredDate; + KStarsDateTime LastNumUpdate, LastSkyUpdate, LastPlanetUpdate, LastMoonUpdate; + KStarsDateTime NextDSTChange; + // FIXME: Used in kstarsdcop.cpp only + KStarsDateTime StoredDate; - QList geoList; - QMap Rulebook; + QList geoList; + QMap Rulebook; - quint32 m_preUpdateID, m_updateID; - quint32 m_preUpdateNumID, m_updateNumID; - KSNumbers m_preUpdateNum, m_updateNum; + quint32 m_preUpdateID, m_updateID; + quint32 m_preUpdateNumID, m_updateNumID; + KSNumbers m_preUpdateNum, m_updateNum; - static KStarsData *pinstance; + static KStarsData *pinstance; }; diff --git a/kstars/kstarsdbus.cpp b/kstars/kstarsdbus.cpp index bb707433c..6a7d2954a 100644 --- a/kstars/kstarsdbus.cpp +++ b/kstars/kstarsdbus.cpp @@ -1,1029 +1,1026 @@ /*************************************************************************** kstarsdbus.cpp - description ------------------- begin : Son Apr 7 2002 copyright : (C) 2002 by Thomas Kabelmann email : tk78@gmx.de ***************************************************************************/ /*************************************************************************** * * * 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 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ //KStars DBUS functions #include "kstars.h" #include "colorscheme.h" #include "eyepiecefield.h" #include "imageexporter.h" #include "ksdssdownloader.h" #include "kstarsdata.h" #include "observinglist.h" #include "Options.h" #include "skymap.h" #include "skycomponents/constellationboundarylines.h" #include "skycomponents/skymapcomposite.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/ksplanetbase.h" #include "skyobjects/starobject.h" #include "tools/whatsinteresting/wiview.h" #ifdef HAVE_CFITSIO #include "fitsviewer/fitsviewer.h" #ifdef HAVE_INDI #include "ekos/manager.h" #endif #endif #include #include #include #include "kstars_debug.h" void KStars::setRaDec(double ra, double dec) { SkyPoint p(ra, dec); map()->setDestination(p); } void KStars::setAltAz(double alt, double az) { map()->setDestinationAltAz(dms(alt), dms(az)); } void KStars::lookTowards(const QString &direction) { QString dir = direction.toLower(); if (dir == i18n("zenith") || dir == "z") { actionCollection()->action("zenith")->trigger(); } else if (dir == i18n("north") || dir == "n") { actionCollection()->action("north")->trigger(); } else if (dir == i18n("east") || dir == "e") { actionCollection()->action("east")->trigger(); } else if (dir == i18n("south") || dir == "s") { actionCollection()->action("south")->trigger(); } else if (dir == i18n("west") || dir == "w") { actionCollection()->action("west")->trigger(); } else if (dir == i18n("northeast") || dir == "ne") { map()->stopTracking(); map()->clickedPoint()->setAlt(15.0); map()->clickedPoint()->setAz(45.0); map()->clickedPoint()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat()); map()->slotCenter(); } else if (dir == i18n("southeast") || dir == "se") { map()->stopTracking(); map()->clickedPoint()->setAlt(15.0); map()->clickedPoint()->setAz(135.0); map()->clickedPoint()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat()); map()->slotCenter(); } else if (dir == i18n("southwest") || dir == "sw") { map()->stopTracking(); map()->clickedPoint()->setAlt(15.0); map()->clickedPoint()->setAz(225.0); map()->clickedPoint()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat()); map()->slotCenter(); } else if (dir == i18n("northwest") || dir == "nw") { map()->stopTracking(); map()->clickedPoint()->setAlt(15.0); map()->clickedPoint()->setAz(315.0); map()->clickedPoint()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat()); map()->slotCenter(); } else { SkyObject *target = data()->objectNamed(direction); if (target != nullptr) { map()->setClickedObject(target); map()->setClickedPoint(target); map()->slotCenter(); } } } void KStars::addLabel(const QString &name) { SkyObject *target = data()->objectNamed(name); if (target != nullptr) { data()->skyComposite()->addNameLabel(target); map()->forceUpdate(); } } void KStars::removeLabel(const QString &name) { SkyObject *target = data()->objectNamed(name); if (target != nullptr) { data()->skyComposite()->removeNameLabel(target); map()->forceUpdate(); } } void KStars::addTrail(const QString &name) { TrailObject *target = dynamic_cast(data()->objectNamed(name)); if (target) { target->addToTrail(); map()->forceUpdate(); } } void KStars::removeTrail(const QString &name) { TrailObject *target = dynamic_cast(data()->objectNamed(name)); if (target) { target->clearTrail(); map()->forceUpdate(); } } void KStars::zoom(double z) { map()->setZoomFactor(z); } void KStars::zoomIn() { map()->slotZoomIn(); } void KStars::zoomOut() { map()->slotZoomOut(); } void KStars::defaultZoom() { map()->slotZoomDefault(); } void KStars::setLocalTime(int yr, int mth, int day, int hr, int min, int sec) { data()->changeDateTime(data()->geo()->LTtoUT(KStarsDateTime(QDate(yr, mth, day), QTime(hr, min, sec)))); } void KStars::setTimeToNow() { slotSetTimeToNow(); } void KStars::waitFor(double sec) { QTime tm; tm.start(); while (tm.elapsed() < int(1000. * sec)) { qApp->processEvents(); } } void KStars::waitForKey(const QString &k) { data()->resumeKey = QKeySequence::fromString(k); if (!data()->resumeKey.isEmpty()) { //When the resumeKey is pressed, resumeKey is set to empty while (!data()->resumeKey.isEmpty()) qApp->processEvents(); } else { qDebug() << "Error [D-Bus waitForKey()]: Invalid key requested."; } } void KStars::setTracking(bool track) { if (track != Options::isTracking()) slotTrack(); } void KStars::popupMessage(int /*x*/, int /*y*/, const QString & /*message*/) { //Show a small popup window at (x,y) with a text message } void KStars::drawLine(int /*x1*/, int /*y1*/, int /*x2*/, int /*y2*/, int /*speed*/) { //Draw a line on the skymap display } bool KStars::setGeoLocation(const QString &city, const QString &province, const QString &country) { //Set the geographic location bool cityFound(false); foreach (GeoLocation *loc, data()->geoList) { if (loc->translatedName() == city && (province.isEmpty() || loc->translatedProvince() == province) && loc->translatedCountry() == country) { cityFound = true; data()->setLocation(*loc); //configure time zone rule KStarsDateTime ltime = loc->UTtoLT(data()->ut()); loc->tzrule()->reset_with_ltime(ltime, loc->TZ0(), data()->isTimeRunningForward()); data()->setNextDSTChange(loc->tzrule()->nextDSTChange()); //reset LST data()->syncLST(); //make sure planets, etc. are updated immediately data()->setFullTimeUpdate(); // If the sky is in Horizontal mode and not tracking, reset focus such that // Alt/Az remain constant. if (!Options::isTracking() && Options::useAltAz()) { map()->focus()->HorizontalToEquatorial(data()->lst(), data()->geo()->lat()); } // recalculate new times and objects data()->setSnapNextFocus(); updateTime(); //no need to keep looking, we're done. break; } } if (!cityFound) { if (province.isEmpty()) qDebug() << QString("Error [D-Bus setGeoLocation]: city %1, %2 not found in database.").arg(city, country); else qDebug() << QString("Error [D-Bus setGeoLocation]: city %1, %2, %3 not found in database.") .arg(city, province, country); } return cityFound; } bool KStars::setGPSLocation(double longitude, double latitude, double elevation, double tz0) { GeoLocation *geo = data()->geo(); std::unique_ptr tempGeo; QString newLocationName("GPS Location"); dms lng(longitude), lat(latitude); - if (geo->name() != newLocationName) - { - TimeZoneRule *rule = geo->tzrule(); - tempGeo.reset(new GeoLocation(lng, lat, newLocationName, "", "", tz0, rule, elevation)); - geo = tempGeo.get(); - } + GeoLocation *nearest = data()->nearestLocation(longitude, latitude); + + if (nearest) + tempGeo.reset(new GeoLocation(lng, lat, newLocationName, "", "", nearest->TZ0(), nearest->tzrule(), elevation)); else - { - geo->setLong(lng); - geo->setLat(lat); - } + tempGeo.reset(new GeoLocation(lng, lat, newLocationName, "", "", tz0, new TimeZoneRule(), elevation)); + + geo = tempGeo.get(); qCInfo(KSTARS) << "Setting location from DBus. Longitude:" << longitude << "Latitude:" << latitude; data()->setLocation(*geo); return true; } void KStars::readConfig() { //Load config file values into Options object Options::self()->load(); applyConfig(); //Reset date, if one was stored if (data()->StoredDate.isValid()) { data()->changeDateTime(data()->geo()->LTtoUT(data()->StoredDate)); data()->StoredDate = KStarsDateTime(QDateTime()); //invalidate StoredDate } map()->forceUpdate(); } void KStars::writeConfig() { Options::self()->save(); //Store current simulation time data()->StoredDate = data()->lt(); } QString KStars::getOption(const QString &name) { //Some config items are not stored in the Options object while //the program is running; catch these here and returntheir current value. if (name == "FocusRA") { return QString::number(map()->focus()->ra().Hours(), 'f', 6); } if (name == "FocusDec") { return QString::number(map()->focus()->dec().Degrees(), 'f', 6); } KConfigSkeletonItem *it = Options::self()->findItem(name); if (it) return it->property().toString(); else return QString(); } void KStars::changeViewOption(const QString &op, const QString &val) { bool bOk(false), dOk(false); //parse bool value bool bVal(false); if (val.toLower() == "true") { bOk = true; bVal = true; } if (val.toLower() == "false") { bOk = true; bVal = false; } if (val == "1") { bOk = true; bVal = true; } if (val == "0") { bOk = true; bVal = false; } //parse double value double dVal = val.toDouble(&dOk); //[GUI] if (op == "ShowInfoBoxes" && bOk) Options::setShowInfoBoxes(bVal); if (op == "ShowTimeBox" && bOk) Options::setShowTimeBox(bVal); if (op == "ShowGeoBox" && bOk) Options::setShowGeoBox(bVal); if (op == "ShowFocusBox" && bOk) Options::setShowFocusBox(bVal); if (op == "ShadeTimeBox" && bOk) Options::setShadeTimeBox(bVal); if (op == "ShadeGeoBox" && bOk) Options::setShadeGeoBox(bVal); if (op == "ShadeFocusBox" && bOk) Options::setShadeFocusBox(bVal); //[View] // FIXME: REGRESSION // if ( op == "FOVName" ) Options::setFOVName( val ); // if ( op == "FOVSizeX" && dOk ) Options::setFOVSizeX( (float)dVal ); // if ( op == "FOVSizeY" && dOk ) Options::setFOVSizeY( (float)dVal ); // if ( op == "FOVShape" && nOk ) Options::setFOVShape( nVal ); // if ( op == "FOVColor" ) Options::setFOVColor( val ); if (op == "ShowStars" && bOk) Options::setShowStars(bVal); if (op == "ShowMessier" && bOk) Options::setShowMessier(bVal); if (op == "ShowMessierImages" && bOk) Options::setShowMessierImages(bVal); if (op == "ShowNGC" && bOk) Options::setShowNGC(bVal); if (op == "ShowIC" && bOk) Options::setShowIC(bVal); if (op == "ShowCLines" && bOk) Options::setShowCLines(bVal); if (op == "ShowCBounds" && bOk) Options::setShowCBounds(bVal); if (op == "ShowCNames" && bOk) Options::setShowCNames(bVal); if (op == "ShowMilkyWay" && bOk) Options::setShowMilkyWay(bVal); if (op == "AutoSelectGrid" && bOk) Options::setAutoSelectGrid(bVal); if (op == "ShowEquatorialGrid" && bOk) Options::setShowEquatorialGrid(bVal); if (op == "ShowHorizontalGrid" && bOk) Options::setShowHorizontalGrid(bVal); if (op == "ShowEquator" && bOk) Options::setShowEquator(bVal); if (op == "ShowEcliptic" && bOk) Options::setShowEcliptic(bVal); if (op == "ShowHorizon" && bOk) Options::setShowHorizon(bVal); if (op == "ShowGround" && bOk) Options::setShowGround(bVal); if (op == "ShowSun" && bOk) Options::setShowSun(bVal); if (op == "ShowMoon" && bOk) Options::setShowMoon(bVal); if (op == "ShowMercury" && bOk) Options::setShowMercury(bVal); if (op == "ShowVenus" && bOk) Options::setShowVenus(bVal); if (op == "ShowMars" && bOk) Options::setShowMars(bVal); if (op == "ShowJupiter" && bOk) Options::setShowJupiter(bVal); if (op == "ShowSaturn" && bOk) Options::setShowSaturn(bVal); if (op == "ShowUranus" && bOk) Options::setShowUranus(bVal); if (op == "ShowNeptune" && bOk) Options::setShowNeptune(bVal); //if ( op == "ShowPluto" && bOk ) Options::setShowPluto( bVal ); if (op == "ShowAsteroids" && bOk) Options::setShowAsteroids(bVal); if (op == "ShowComets" && bOk) Options::setShowComets(bVal); if (op == "ShowSolarSystem" && bOk) Options::setShowSolarSystem(bVal); if (op == "ShowDeepSky" && bOk) Options::setShowDeepSky(bVal); if (op == "ShowSupernovae" && bOk) Options::setShowSupernovae(bVal); if (op == "ShowStarNames" && bOk) Options::setShowStarNames(bVal); if (op == "ShowStarMagnitudes" && bOk) Options::setShowStarMagnitudes(bVal); if (op == "ShowAsteroidNames" && bOk) Options::setShowAsteroidNames(bVal); if (op == "ShowCometNames" && bOk) Options::setShowCometNames(bVal); if (op == "ShowPlanetNames" && bOk) Options::setShowPlanetNames(bVal); if (op == "ShowPlanetImages" && bOk) Options::setShowPlanetImages(bVal); if (op == "HideOnSlew" && bOk) Options::setHideOnSlew(bVal); if (op == "HideStars" && bOk) Options::setHideStars(bVal); if (op == "HidePlanets" && bOk) Options::setHidePlanets(bVal); if (op == "HideMessier" && bOk) Options::setHideMessier(bVal); if (op == "HideNGC" && bOk) Options::setHideNGC(bVal); if (op == "HideIC" && bOk) Options::setHideIC(bVal); if (op == "HideMilkyWay" && bOk) Options::setHideMilkyWay(bVal); if (op == "HideCNames" && bOk) Options::setHideCNames(bVal); if (op == "HideCLines" && bOk) Options::setHideCLines(bVal); if (op == "HideCBounds" && bOk) Options::setHideCBounds(bVal); if (op == "HideGrids" && bOk) Options::setHideGrids(bVal); if (op == "HideLabels" && bOk) Options::setHideLabels(bVal); if (op == "UseAltAz" && bOk) Options::setUseAltAz(bVal); if (op == "UseRefraction" && bOk) Options::setUseRefraction(bVal); if (op == "UseAutoLabel" && bOk) Options::setUseAutoLabel(bVal); if (op == "UseHoverLabel" && bOk) Options::setUseHoverLabel(bVal); if (op == "UseAutoTrail" && bOk) Options::setUseAutoTrail(bVal); if (op == "UseAnimatedSlewing" && bOk) Options::setUseAnimatedSlewing(bVal); if (op == "FadePlanetTrails" && bOk) Options::setFadePlanetTrails(bVal); if (op == "SlewTimeScale" && dOk) Options::setSlewTimeScale(dVal); if (op == "ZoomFactor" && dOk) Options::setZoomFactor(dVal); // if ( op == "MagLimitDrawStar" && dOk ) Options::setMagLimitDrawStar( dVal ); if (op == "MagLimitDrawDeepSky" && dOk) Options::setMagLimitDrawDeepSky(dVal); if (op == "StarDensity" && dOk) Options::setStarDensity(dVal); // if ( op == "MagLimitDrawStarZoomOut" && dOk ) Options::setMagLimitDrawStarZoomOut( dVal ); if (op == "MagLimitDrawDeepSkyZoomOut" && dOk) Options::setMagLimitDrawDeepSkyZoomOut(dVal); if (op == "StarLabelDensity" && dOk) Options::setStarLabelDensity(dVal); if (op == "MagLimitHideStar" && dOk) Options::setMagLimitHideStar(dVal); if (op == "MagLimitAsteroid" && dOk) Options::setMagLimitAsteroid(dVal); if (op == "AsteroidLabelDensity" && dOk) Options::setAsteroidLabelDensity(dVal); if (op == "MaxRadCometName" && dOk) Options::setMaxRadCometName(dVal); //these three are a "radio group" if (op == "UseLatinConstellationNames" && bOk) { Options::setUseLatinConstellNames(true); Options::setUseLocalConstellNames(false); Options::setUseAbbrevConstellNames(false); } if (op == "UseLocalConstellationNames" && bOk) { Options::setUseLatinConstellNames(false); Options::setUseLocalConstellNames(true); Options::setUseAbbrevConstellNames(false); } if (op == "UseAbbrevConstellationNames" && bOk) { Options::setUseLatinConstellNames(false); Options::setUseLocalConstellNames(false); Options::setUseAbbrevConstellNames(true); } map()->forceUpdate(); } void KStars::setColor(const QString &name, const QString &value) { ColorScheme *cs = data()->colorScheme(); if (cs->hasColorNamed(name)) { cs->setColor(name, value); map()->forceUpdate(); } } void KStars::loadColorScheme(const QString &name) { data()->colorScheme()->load(name); #if 0 if (ok) { //set the application colors for the Night Vision scheme if (Options::darkAppColors()) { //OriginalPalette = QApplication::palette(); QApplication::setPalette(DarkPalette); if (KStars::Instance()->wiView()) KStars::Instance()->wiView()->setNightVisionOn(true); //Note: This uses style sheets to set the dark colors, this is cross platform. Palettes have a different behavior on OS X and Windows as opposed to Linux. //It might be a good idea to use stylesheets in the future instead of palettes but this will work for now for OS X. //This is also in KStars.cpp. If you change it, change it in BOTH places. #ifdef Q_OS_OSX qDebug() << "setting dark stylesheet"; qApp->setStyleSheet( "QWidget { background-color: black; color:red; " "selection-background-color:rgb(30,30,30);selection-color:white}" "QToolBar { border:none }" "QTabBar::tab:selected { background-color:rgb(50,50,50) }" "QTabBar::tab:!selected { background-color:rgb(30,30,30) }" "QPushButton { background-color:rgb(50,50,50);border-width:1px; border-style:solid;border-color:black}" "QPushButton::disabled { background-color:rgb(10,10,10);border-width:1px; " "border-style:solid;border-color:black }" "QToolButton:Checked { background-color:rgb(30,30,30); border:none }" "QComboBox { background-color:rgb(30,30,30); }" "QComboBox::disabled { background-color:rgb(10,10,10) }" "QScrollBar::handle { background: rgb(30,30,30) }" "QSpinBox { border-width: 1px; border-style:solid; border-color:rgb(30,30,30) }" "QDoubleSpinBox { border-width:1px; border-style:solid; border-color:rgb(30,30,30) }" "QLineEdit { border-width: 1px; border-style: solid; border-color:rgb(30,30,30) }" "QCheckBox::indicator:unchecked { background-color:rgb(30,30,30);border-width:1px; " "border-style:solid;border-color:black }" "QCheckBox::indicator:checked { background-color:red;border-width:1px; " "border-style:solid;border-color:black }" "QRadioButton::indicator:unchecked { background-color:rgb(30,30,30) }" "QRadioButton::indicator:checked { background-color:red }" "QRoundProgressBar { alternate-background-color:black }" "QDateTimeEdit {background-color:rgb(30,30,30); border-width: 1px; border-style:solid; " "border-color:rgb(30,30,30) }" "QHeaderView { color:red;background-color:black }" "QHeaderView::Section { background-color:rgb(30,30,30) }" "QTableCornerButton::section{ background-color:rgb(30,30,30) }" ""); qDebug() << "stylesheet set"; #endif } else { if (KStars::Instance()->wiView()) KStars::Instance()->wiView()->setNightVisionOn(false); QApplication::setPalette(OriginalPalette); #ifdef Q_OS_OSX qDebug() << "setting light stylesheet"; qApp->setStyleSheet(""); qDebug() << "stylesheet set"; #endif } } #endif #ifdef HAVE_INDI if (KStars::Instance()->ekosManager()) { if (KStars::Instance()->ekosManager()->guideModule()) { KStars::Instance()->ekosManager()->guideModule()->refreshColorScheme(); } } #endif Options::setColorSchemeFile(name); map()->forceUpdate(); } void KStars::exportImage(const QString &url, int w, int h, bool includeLegend) { ImageExporter *m_ImageExporter = m_KStarsData->imageExporter(); if (w <= 0) w = map()->width(); if (h <= 0) h = map()->height(); QSize size(w, h); m_ImageExporter->includeLegend(includeLegend); m_ImageExporter->setRasterOutputSize(&size); m_ImageExporter->exportImage(url); } QString KStars::getDSSURL(const QString &objectName) { SkyObject *target = data()->objectNamed(objectName); if (!target) { return QString("ERROR"); } else { return KSDssDownloader::getDSSURL(target); } } QString KStars::getDSSURL(double RA_J2000, double Dec_J2000, float width, float height) { dms ra(RA_J2000), dec(Dec_J2000); return KSDssDownloader::getDSSURL(ra, dec, width, height); } QString KStars::getObjectDataXML(const QString &objectName) { SkyObject *target = data()->objectNamed(objectName); if (!target) { return QString(""); } QString output; QXmlStreamWriter stream(&output); stream.setAutoFormatting(true); stream.writeStartDocument(); stream.writeStartElement("object"); stream.writeTextElement("Name", target->name()); stream.writeTextElement("Alt_Name", target->name2()); stream.writeTextElement("Long_Name", target->longname()); stream.writeTextElement("Constellation", KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(target)); stream.writeTextElement("RA_Dec_Epoch_JD", QString::number(target->getLastPrecessJD(), 'f', 3)); stream.writeTextElement("RA_HMS", target->ra().toHMSString()); stream.writeTextElement("Dec_DMS", target->dec().toDMSString()); stream.writeTextElement("RA_J2000_HMS", target->ra0().toHMSString()); stream.writeTextElement("Dec_J2000_DMS", target->dec0().toDMSString()); stream.writeTextElement("RA_Degrees", QString::number(target->ra().Degrees())); stream.writeTextElement("Dec_Degrees", QString::number(target->dec().Degrees())); stream.writeTextElement("RA_J2000_Degrees", QString::number(target->ra0().Degrees())); stream.writeTextElement("Dec_J2000_Degrees", QString::number(target->dec0().Degrees())); stream.writeTextElement("Type", target->typeName()); stream.writeTextElement("Magnitude", QString::number(target->mag(), 'g', 2)); stream.writeTextElement("Position_Angle", QString::number(target->pa(), 'g', 3)); StarObject *star = dynamic_cast(target); DeepSkyObject *dso = dynamic_cast(target); if (star) { stream.writeTextElement("Spectral_Type", star->sptype()); stream.writeTextElement("Genetive_Name", star->gname()); stream.writeTextElement("Greek_Letter", star->greekLetter()); stream.writeTextElement("Proper_Motion", QString::number(star->pmMagnitude())); stream.writeTextElement("Proper_Motion_RA", QString::number(star->pmRA())); stream.writeTextElement("Proper_Motion_Dec", QString::number(star->pmDec())); stream.writeTextElement("Parallax_mas", QString::number(star->parallax())); stream.writeTextElement("Distance_pc", QString::number(star->distance())); stream.writeTextElement("Henry_Draper", QString::number(star->getHDIndex())); stream.writeTextElement("BV_Index", QString::number(star->getBVIndex())); } else if (dso) { stream.writeTextElement("Catalog", dso->catalog()); stream.writeTextElement("Major_Axis", QString::number(dso->a())); stream.writeTextElement("Minor_Axis", QString::number(dso->a() * dso->e())); } stream.writeEndElement(); // object stream.writeEndDocument(); return output; } QString KStars::getObjectPositionInfo(const QString &objectName) { Q_ASSERT(data()); const SkyObject *obj = data()->objectNamed(objectName); // make sure we work with a clone if (!obj) { return QString(""); } SkyObject *target = obj->clone(); if (!target) // should not happen { qWarning() << "ERROR: Could not clone SkyObject " << objectName << "!"; return QString(""); } const KSNumbers *updateNum = data()->updateNum(); const KStarsDateTime ut = data()->ut(); const GeoLocation *geo = data()->geo(); QString riseTimeString, setTimeString, transitTimeString; QString riseAzString, setAzString, transitAltString; // Make sure the coordinates of the SkyObject are updated target->updateCoords(updateNum, true, geo->lat(), data()->lst(), true); target->EquatorialToHorizontal(data()->lst(), geo->lat()); // Compute rise, set and transit times and parameters -- Code pulled from DetailDialog QTime riseTime = target->riseSetTime(ut, geo, true); //true = use rise time dms riseAz = target->riseSetTimeAz(ut, geo, true); //true = use rise time QTime transitTime = target->transitTime(ut, geo); dms transitAlt = target->transitAltitude(ut, geo); if (transitTime < riseTime) { transitTime = target->transitTime(ut.addDays(1), geo); transitAlt = target->transitAltitude(ut.addDays(1), geo); } //If set time is before rise time, use set time for tomorrow QTime setTime = target->riseSetTime(ut, geo, false); //false = use set time dms setAz = target->riseSetTimeAz(ut, geo, false); //false = use set time if (setTime < riseTime) { setTime = target->riseSetTime(ut.addDays(1), geo, false); //false = use set time setAz = target->riseSetTimeAz(ut.addDays(1), geo, false); //false = use set time } if (riseTime.isValid()) { riseTimeString = QString().sprintf("%02d:%02d", riseTime.hour(), riseTime.minute()); setTimeString = QString().sprintf("%02d:%02d", setTime.hour(), setTime.minute()); riseAzString = riseAz.toDMSString(true, true); setAzString = setAz.toDMSString(true, true); } else { if (target->alt().Degrees() > 0.0) { riseTimeString = setTimeString = QString("Circumpolar"); } else { riseTimeString = setTimeString = QString("Never Rises"); } riseAzString = setAzString = QString("N/A"); } transitTimeString = QString().sprintf("%02d:%02d", transitTime.hour(), transitTime.minute()); transitAltString = transitAlt.toDMSString(true, true); QString output; QXmlStreamWriter stream(&output); stream.setAutoFormatting(true); stream.writeStartDocument(); stream.writeStartElement("object"); stream.writeTextElement("Name", target->name()); stream.writeTextElement("RA_Dec_Epoch_JD", QString::number(target->getLastPrecessJD(), 'f', 3)); stream.writeTextElement("AltAz_JD", QString::number(data()->ut().djd(), 'f', 3)); stream.writeTextElement("RA_HMS", target->ra().toHMSString(true)); stream.writeTextElement("Dec_DMS", target->dec().toDMSString(true, true)); stream.writeTextElement("RA_J2000_HMS", target->ra0().toHMSString(true)); stream.writeTextElement("Dec_J2000_DMS", target->dec0().toDMSString(true, true)); stream.writeTextElement("RA_Degrees", QString::number(target->ra().Degrees())); stream.writeTextElement("Dec_Degrees", QString::number(target->dec().Degrees())); stream.writeTextElement("RA_J2000_Degrees", QString::number(target->ra0().Degrees())); stream.writeTextElement("Dec_J2000_Degrees", QString::number(target->dec0().Degrees())); stream.writeTextElement("Altitude_DMS", target->alt().toDMSString(true, true)); stream.writeTextElement("Azimuth_DMS", target->az().toDMSString(true, true)); stream.writeTextElement("Altitude_Degrees", QString::number(target->alt().Degrees())); stream.writeTextElement("Azimuth_Degrees", QString::number(target->az().Degrees())); stream.writeTextElement("Rise", riseTimeString); stream.writeTextElement("Rise_Az_DMS", riseAzString); stream.writeTextElement("Set", setTimeString); stream.writeTextElement("Set_Az_DMS", setAzString); stream.writeTextElement("Transit", transitTimeString); stream.writeTextElement("Transit_Alt_DMS", transitAltString); stream.writeTextElement("Time_Zone_Offset", QString().sprintf("%02.2f", geo->TZ())); stream.writeEndElement(); // object stream.writeEndDocument(); return output; } void KStars::renderEyepieceView(const QString &objectName, const QString &destPathChart, const double fovWidth, const double fovHeight, const double rotation, const double scale, const bool flip, const bool invert, QString imagePath, const QString &destPathImage, const bool overlay, const bool invertColors) { const SkyObject *obj = data()->objectNamed(objectName); if (!obj) { qCWarning(KSTARS) << "Object named " << objectName << " was not found!"; return; } SkyObject *target = obj->clone(); const KSNumbers *updateNum = data()->updateNum(); const KStarsDateTime ut = data()->ut(); const GeoLocation *geo = data()->geo(); QPixmap *renderChart = new QPixmap(); QPixmap *renderImage = nullptr; QTemporaryFile tempFile; if (overlay || (!destPathImage.isEmpty())) { if (!QFile::exists(imagePath)) { // We must download a DSS image tempFile.open(); QEventLoop loop; std::function slot = [&loop](bool unused) { Q_UNUSED(unused); loop.quit(); }; new KSDssDownloader(target, tempFile.fileName(), slot, this); qDebug() << "DSS download requested. Waiting for download to complete..."; loop.exec(); // wait for download to complete imagePath = tempFile.fileName(); } if (QFile::exists(imagePath)) // required because DSS Download may fail renderImage = new QPixmap(); } // Make sure the coordinates of the SkyObject are updated target->updateCoords(updateNum, true, geo->lat(), data()->lst(), true); target->EquatorialToHorizontal(data()->lst(), geo->lat()); EyepieceField::renderEyepieceView(target, renderChart, fovWidth, fovHeight, rotation, scale, flip, invert, imagePath, renderImage, overlay, invertColors); renderChart->save(destPathChart); delete renderChart; if (renderImage) { renderImage->save(destPathImage); delete renderImage; } } QString KStars::getObservingWishListObjectNames() { QString output; for (auto &object : KStarsData::Instance()->observingList()->obsList()) { output.append(object->name() + '\n'); } return output; } QString KStars::getObservingSessionPlanObjectNames() { QString output; for (auto &object : KStarsData::Instance()->observingList()->sessionList()) { output.append(object->name() + '\n'); } return output; } void KStars::setApproxFOV(double FOV_Degrees) { zoom(map()->width() / (FOV_Degrees * dms::DegToRad)); } QString KStars::getSkyMapDimensions() { return (QString::number(map()->width()) + 'x' + QString::number(map()->height())); } void KStars::printImage(bool usePrintDialog, bool useChartColors) { //QPRINTER_FOR_NOW // KPrinter printer( true, QPrinter::HighResolution ); QPrinter printer(QPrinter::HighResolution); printer.setFullPage(false); //Set up the printer (either with the Print Dialog, //or using the default settings) bool ok(false); if (usePrintDialog) { //QPRINTER_FOR_NOW // ok = printer.setup( this, i18n("Print Sky") ); //QPrintDialog *dialog = KdePrint::createPrintDialog(&printer, this); QPrintDialog *dialog = new QPrintDialog(&printer, this); dialog->setWindowTitle(i18n("Print Sky")); if (dialog->exec() == QDialog::Accepted) ok = true; delete dialog; } else { //QPRINTER_FOR_NOW // ok = printer.autoConfigure(); ok = true; } if (ok) { QApplication::setOverrideCursor(Qt::WaitCursor); //Save current ColorScheme file name and switch to Star Chart //scheme (if requested) QString schemeName = data()->colorScheme()->fileName(); if (useChartColors) { loadColorScheme("chart.colors"); } map()->setupProjector(); map()->exportSkyImage(&printer, true); //Restore old color scheme if necessary //(if printing was aborted, the ColorScheme is still restored) if (useChartColors) { loadColorScheme(schemeName); map()->forceUpdate(); } QApplication::restoreOverrideCursor(); } } void KStars::openFITS(const QUrl &imageURL) { #ifndef HAVE_CFITSIO qWarning() << "KStars does not support loading FITS. Please recompile KStars with FITS support."; #else QPointer fv; if (Options::singleWindowOpenedFITS()) fv = genericFITSViewer(); else { fv = new FITSViewer((Options::independentWindowFITS()) ? nullptr : this); KStars::Instance()->addFITSViewer(fv); } auto m_Loaded = std::make_shared(); *m_Loaded = connect(fv, &FITSViewer::loaded, [fv, m_Loaded]() { fv->show(); QObject::disconnect(*m_Loaded); }); auto m_Failed = std::make_shared(); *m_Failed = connect(fv, &FITSViewer::failed, [fv, m_Failed]() { delete (fv); QObject::disconnect(*m_Failed); }); fv->addFITS(imageURL); #endif }