diff --git a/kstars/dialogs/detaildialog.cpp b/kstars/dialogs/detaildialog.cpp index d7ac0f44e..6cb1983cd 100644 --- a/kstars/dialogs/detaildialog.cpp +++ b/kstars/dialogs/detaildialog.cpp @@ -1,1332 +1,1332 @@ /*************************************************************************** detaildialog.cpp - description ------------------- begin : Sun May 5 2002 copyright : (C) 2002 by Jason Harris and Jasem Mutlaq email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "detaildialog.h" #include "config-kstars.h" #include "addlinkdialog.h" #include "kspaths.h" #include "kstars.h" #include "kstarsdata.h" #include "ksutils.h" #include "observinglist.h" #include "skymap.h" #include "thumbnailpicker.h" #include "skycomponents/constellationboundarylines.h" #include "skycomponents/skymapcomposite.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/ksasteroid.h" #include "skyobjects/kscomet.h" #include "skyobjects/ksmoon.h" #include "skyobjects/starobject.h" #include "skyobjects/supernova.h" #include "skycomponents/catalogcomponent.h" #ifdef HAVE_INDI #include #include "indi/indilistener.h" #endif #include #include DetailDialog::DetailDialog(SkyObject *o, const KStarsDateTime &ut, GeoLocation *geo, QWidget *parent) : KPageDialog(parent), selectedObject(o), Data(nullptr), DataComet(nullptr), Pos(nullptr), Links(nullptr), Adv(nullptr), Log(nullptr) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif setFaceType(Tabbed); setBackgroundRole(QPalette::Base); titlePalette = palette(); titlePalette.setColor(backgroundRole(), palette().color(QPalette::Active, QPalette::Highlight)); titlePalette.setColor(foregroundRole(), palette().color(QPalette::Active, QPalette::HighlightedText)); //Create thumbnail image Thumbnail.reset(new QPixmap(200, 200)); setWindowTitle(i18n("Object Details")); // JM 2016-11-22: Do we really need a close button? //setStandardButtons(QDialogButtonBox::Close); setStandardButtons(QDialogButtonBox::NoButton); createGeneralTab(); createPositionTab(ut, geo); createLinksTab(); createAdvancedTab(); createLogTab(); } void DetailDialog::createGeneralTab() { Data = new DataWidget(this); addPage(Data, i18n("General")); Data->Names->setPalette(titlePalette); //Connections connect(Data->ObsListButton, SIGNAL(clicked()), this, SLOT(addToObservingList())); connect(Data->CenterButton, SIGNAL(clicked()), this, SLOT(centerMap())); #ifdef HAVE_INDI connect(Data->ScopeButton, SIGNAL(clicked()), this, SLOT(centerTelescope())); #else Data->ScopeButton->setEnabled(false); #endif connect(Data->Image, SIGNAL(clicked()), this, SLOT(updateThumbnail())); // Stuff that should be visible only for specific types of objects Data->IllumLabel->setVisible(false); // Only shown for the moon Data->Illumination->setVisible(false); Data->BVIndex->setVisible(false); // Only shown for stars Data->BVLabel->setVisible(false); //Show object thumbnail image showThumbnail(); //Fill in the data fields //Contents depend on type of object QString objecttyp; switch (selectedObject->type()) { case SkyObject::STAR: { StarObject *s = (StarObject *)selectedObject; if (s->getHDIndex()) { Data->Names->setText(QString("%1, HD %2").arg(s->longname()).arg(s->getHDIndex())); } else { Data->Names->setText(s->longname()); } objecttyp = i18n("%1 star", s->sptype()); Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag", QLocale().toString(s->mag(), 'f', 2))); //show to hundredth place Data->BVLabel->setVisible(true); Data->BVIndex->setVisible(true); if (s->getBVIndex() < 30.) { Data->BVIndex->setText(QString::number(s->getBVIndex(), 'f', 2)); } //The thumbnail image is empty, and isn't clickable for stars //Also, don't show the border around the Image QFrame. Data->Image->setFrameStyle(QFrame::NoFrame); disconnect(Data->Image, SIGNAL(clicked()), this, SLOT(updateThumbnail())); //distance if (s->distance() > 2000. || s->distance() < 0.) // parallax < 0.5 mas { Data->Distance->setText(QString(i18nc("larger than 2000 parsecs", "> 2000 pc"))); } else if (s->distance() > 50.) //show to nearest integer { Data->Distance->setText(i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 0))); } else if (s->distance() > 10.0) //show to tenths place { Data->Distance->setText(i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 1))); } else //show to hundredths place { Data->Distance->setText(i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 2))); } //Note multiplicity/variability in angular size label Data->AngSizeLabel->setText(QString()); Data->AngSize->setText(QString()); Data->AngSizeLabel->setFont(Data->AngSize->font()); if (s->isMultiple() && s->isVariable()) { Data->AngSizeLabel->setText(i18nc("the star is a multiple star", "multiple") + ','); Data->AngSize->setText(i18nc("the star is a variable star", "variable")); } else if (s->isMultiple()) { Data->AngSizeLabel->setText(i18nc("the star is a multiple star", "multiple")); } else if (s->isVariable()) { Data->AngSizeLabel->setText(i18nc("the star is a variable star", "variable")); } break; //end of stars case } case SkyObject::ASTEROID: //[fall through to planets] case SkyObject::COMET: //[fall through to planets] case SkyObject::MOON: //[fall through to planets] case SkyObject::PLANET: { - KSPlanetBase *ps = (KSPlanetBase *)selectedObject; + KSPlanetBase *ps = dynamic_cast(selectedObject); Data->Names->setText(ps->longname()); //Type is "G5 star" for Sun - if (ps->name() == "Sun") + if (ps->name() == i18n("Sun")) { objecttyp = i18n("G5 star"); } - else if (ps->name() == "Moon") + else if (ps->name() == i18n("Moon")) { objecttyp = ps->translatedName(); } else if (ps->name() == i18nc("Asteroid name (optional)", "Pluto") || ps->name() == i18nc("Asteroid name (optional)", "Ceres") || ps->name() == i18nc("Asteroid name (optional)", "Eris")) { objecttyp = i18n("Dwarf planet"); } else { objecttyp = ps->typeName(); } //The moon displays illumination fraction and updateMag is called to calculate moon's current magnitude - if (selectedObject->name() == "Moon") + if (selectedObject->name() == i18n("Moon")) { Data->IllumLabel->setVisible(true); Data->Illumination->setVisible(true); Data->Illumination->setText( QString("%1 %").arg(QLocale().toString(((KSMoon *)selectedObject)->illum() * 100., 'f', 0))); ((KSMoon *)selectedObject)->updateMag(); } // JM: Shouldn't we use the calculated magnitude? Disabling the following /* if(selectedObject->type() == SkyObject::COMET){ Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag", QLocale().toString( ((KSComet *)selectedObject)->getTotalMagnitudeParameter(), 'f', 2))); //show to hundredth place } else{*/ Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag", QLocale().toString(ps->mag(), 'f', 2))); //show to hundredth place //} //Distance from Earth. The moon requires a unit conversion - if (ps->name() == "Moon") + if (ps->name() == i18n("Moon")) { Data->Distance->setText( i18nc("distance in kilometers", "%1 km", QLocale().toString(ps->rearth() * AU_KM, 'f', 2))); } else { Data->Distance->setText( i18nc("distance in Astronomical Units", "%1 AU", QLocale().toString(ps->rearth(), 'f', 3))); } //Angular size; moon and sun in arcmin, others in arcsec if (ps->angSize()) { - if (ps->name() == "Sun" || ps->name() == "Moon") + if (ps->name() == i18n("Sun") || ps->name() == i18n("Moon")) { Data->AngSize->setText(i18nc( - "angular size in arcminutes", "%1 arcmin", - QLocale().toString( - ps->angSize(), 'f', - 1))); // Needn't be a plural form because sun / moon will never contract to 1 arcminute + "angular size in arcminutes", "%1 arcmin", + QLocale().toString( + ps->angSize(), 'f', + 1))); // Needn't be a plural form because sun / moon will never contract to 1 arcminute } else { Data->AngSize->setText(i18nc("angular size in arcseconds", "%1 arcsec", QLocale().toString(ps->angSize() * 60.0, 'f', 1))); } } else { Data->AngSize->setText("--"); } break; //end of planets/comets/asteroids case } case SkyObject::SUPERNOVA: { - Supernova *sup = (Supernova *)selectedObject; + Supernova *sup = dynamic_cast(selectedObject); objecttyp = i18n("Supernova"); Data->Names->setText(sup->name()); if (sup->mag() < 99) Data->Magnitude->setText( i18nc("number in magnitudes", "%1 mag", QLocale().toString(sup->mag(), 'f', 2))); else Data->Magnitude->setText("--"); Data->DistanceLabel->setVisible(false); Data->Distance->setVisible(false); Data->AngSizeLabel->setVisible(false); Data->AngSize->setVisible(false); QLabel *discoveryDateLabel = new QLabel(i18n("Discovery Date:"), this); QLabel *discoveryDate = new QLabel(sup->getDate(), this); Data->dataGridLayout->addWidget(discoveryDateLabel, 1, 0); Data->dataGridLayout->addWidget(discoveryDate, 1, 1); QLabel *typeLabel = new QLabel(i18n("Type:"), this); QLabel *type = new QLabel(sup->getType(), this); Data->dataGridLayout->addWidget(typeLabel, 2, 0); Data->dataGridLayout->addWidget(type, 2, 1); QLabel *hostGalaxyLabel = new QLabel(i18n("Host Galaxy:"), this); QLabel *hostGalaxy = new QLabel(sup->getHostGalaxy().isEmpty() ? "--" : sup->getHostGalaxy(), this); Data->dataGridLayout->addWidget(hostGalaxyLabel, 3, 0); Data->dataGridLayout->addWidget(hostGalaxy, 3, 1); QLabel *redShiftLabel = new QLabel(i18n("Red Shift:"), this); QLabel *redShift = new QLabel( (sup->getRedShift() < 99) ? QString::number(sup->getRedShift(), 'f', 2) : QString("--"), this); Data->dataGridLayout->addWidget(redShiftLabel, 4, 0); Data->dataGridLayout->addWidget(redShift, 4, 1); break; } default: //deep-sky objects { - DeepSkyObject *dso = (DeepSkyObject *)selectedObject; + DeepSkyObject *dso = dynamic_cast(selectedObject); //Show all names recorded for the object QStringList nameList; if (!dso->longname().isEmpty() && dso->longname() != dso->name()) { nameList.append(dso->translatedLongName()); nameList.append(dso->translatedName()); } else { nameList.append(dso->translatedName()); } if (!dso->translatedName2().isEmpty()) { nameList.append(dso->translatedName2()); } if (dso->ugc() != 0) { nameList.append(QString("UGC %1").arg(dso->ugc())); } if (dso->pgc() != 0) { nameList.append(QString("PGC %1").arg(dso->pgc())); } Data->Names->setText(nameList.join(",")); objecttyp = dso->typeName(); if (dso->type() == SkyObject::RADIO_SOURCE) { Q_ASSERT(dso->customCatalog()); // the in-built catalogs don't have radio sources Data->MagLabel->setText( i18nc("integrated flux at a frequency", "Flux(%1):", dso->customCatalog()->fluxFrequency())); Data->Magnitude->setText(i18nc("integrated flux value", "%1 %2", QLocale().toString(dso->flux(), 'f', 1), dso->customCatalog()->fluxUnit())); //show to tenths place } else if (dso->mag() > 90.0) { Data->Magnitude->setText("--"); } else { Data->Magnitude->setText(i18nc("number in magnitudes", "%1 mag", QLocale().toString(dso->mag(), 'f', 1))); //show to tenths place } //No distances at this point... Data->Distance->setText("--"); //Only show decimal place for small angular sizes if (dso->a() > 10.0) { Data->AngSize->setText( i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 'f', 0))); } else if (dso->a()) { Data->AngSize->setText( i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 'f', 1))); } else { Data->AngSize->setText("--"); } break; } } // Add specifics data switch (selectedObject->type()) { case SkyObject::ASTEROID: { KSAsteroid *ast = dynamic_cast(selectedObject); // Show same specifics data as comets DataComet = new DataCometWidget(this); Data->IncludeData->layout()->addWidget(DataComet); // Perihelion DataComet->Perihelion->setText(i18nc("Distance in astronomical units", "%1 AU", QString::number(ast->getPerihelion()))); // Earth MOID if (ast->getEarthMOID() == 0) DataComet->EarthMOID->setText("--"); else DataComet->EarthMOID->setText(i18nc("Distance in astronomical units", "%1 AU", QString::number(ast->getEarthMOID()))); // Orbit ID DataComet->OrbitID->setText(ast->getOrbitID()); // Orbit Class DataComet->OrbitClass->setText(ast->getOrbitClass()); // NEO if (ast->isNEO()) DataComet->NEO->setText(i18n("Yes")); else DataComet->NEO->setText(i18n("No")); // Albedo if (ast->getAlbedo() == 0.0) DataComet->Albedo->setText("--"); else DataComet->Albedo->setText(QString::number(ast->getAlbedo())); // Diameter if (ast->getDiameter() == 0.0) DataComet->Diameter->setText("--"); else DataComet->Diameter->setText(i18nc("Diameter in kilometers", "%1 km", QString::number(ast->getDiameter()))); // Dimensions if (ast->getDimensions().isEmpty()) DataComet->Dimensions->setText("--"); else DataComet->Dimensions->setText(i18nc("Dimension in kilometers", "%1 km", ast->getDimensions())); // Rotation period if (ast->getRotationPeriod() == 0.0) DataComet->Rotation->setText("--"); else DataComet->Rotation->setText(i18nc("Rotation period in hours", "%1 h", QString::number(ast->getRotationPeriod()))); // Period if (ast->getPeriod() == 0.0) - DataComet->Period->setText("--"); + DataComet->Period->setText("--"); else DataComet->Period->setText(i18nc("Orbit period in years", "%1 y", QString::number(ast->getPeriod()))); break; } case SkyObject::COMET: { KSComet *com = dynamic_cast(selectedObject); DataComet = new DataCometWidget(this); Data->IncludeData->layout()->addWidget(DataComet); // Perihelion DataComet->Perihelion->setText(i18nc("Distance in astronomical units", "%1 AU", QString::number(com->getPerihelion()))); // Earth MOID if (com->getEarthMOID() == 0) DataComet->EarthMOID->setText("--"); else DataComet->EarthMOID->setText(i18nc("Distance in astronomical units", "%1 AU", QString::number(com->getEarthMOID()))); // Orbit ID DataComet->OrbitID->setText(com->getOrbitID()); // Orbit Class DataComet->OrbitClass->setText(com->getOrbitClass()); // NEO if (com->isNEO()) DataComet->NEO->setText(i18n("Yes")); else DataComet->NEO->setText(i18n("No")); // Albedo if (com->getAlbedo() == 0.0) DataComet->Albedo->setText("--"); else DataComet->Albedo->setText(QString::number(com->getAlbedo())); // Diameter if (com->getDiameter() == 0.0) DataComet->Diameter->setText("--"); else DataComet->Diameter->setText(i18nc("Diameter in kilometers", "%1 km", QString::number(com->getDiameter()))); // Dimensions if (com->getDimensions().isEmpty()) DataComet->Dimensions->setText("--"); else DataComet->Dimensions->setText(i18nc("Dimension in kilometers", "%1 km", com->getDimensions())); // Rotation period if (com->getRotationPeriod() == 0.0) DataComet->Rotation->setText("--"); else DataComet->Rotation->setText(i18nc("Rotation period in hours", "%1 h", QString::number(com->getRotationPeriod()))); // Period if (com->getPeriod() == 0.0) - DataComet->Period->setText("--"); + DataComet->Period->setText("--"); else DataComet->Period->setText(i18nc("Orbit period in years", "%1 y", QString::number(com->getPeriod()))); break; } } //Common to all types: QString cname = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(selectedObject); if (selectedObject->type() != SkyObject::CONSTELLATION) { cname = i18nc("%1 type of sky object (planet, asteroid etc), %2 name of a constellation", "%1 in %2", objecttyp, cname); } Data->ObjectTypeInConstellation->setText(cname); } void DetailDialog::createPositionTab(const KStarsDateTime &ut, GeoLocation *geo) { Pos = new PositionWidget(this); addPage(Pos, i18n("Position")); Pos->CoordTitle->setPalette(titlePalette); Pos->RSTTitle->setPalette(titlePalette); KStarsData *data = KStarsData::Instance(); //Coordinates Section: //Don't use KLocale::formatNumber() for the epoch string, //because we don't want a thousands-place separator! selectedObject->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false); QString sEpoch = QString::number(KStarsDateTime::jdToEpoch(selectedObject->getLastPrecessJD()), 'f', 1); //Replace the decimal point with localized decimal symbol sEpoch.replace('.', QLocale().decimalPoint()); // Is this necessary? -- asimha Oct 2016 /*qDebug() << (selectedObject->deprecess(data->updateNum())).ra0().toHMSString() << (selectedObject->deprecess(data->updateNum())).dec0().toDMSString() << endl;*/ //qDebug() << selectedObject->ra().toHMSString() << selectedObject->dec().toDMSString() << endl; Pos->RALabel->setText(i18n("RA (%1):", sEpoch)); Pos->DecLabel->setText(i18n("DE (%1):", sEpoch)); Pos->RA->setText(selectedObject->ra().toHMSString(false, true)); Pos->Dec->setText(selectedObject->dec().toDMSString(false, false, true)); selectedObject->EquatorialToHorizontal(data->lst(), data->geo()->lat()); Pos->Az->setText(selectedObject->az().toDMSString()); dms a; if (Options::useAltAz()) a = selectedObject->alt(); else a = selectedObject->altRefracted(); Pos->Alt->setText(a.toDMSString()); // Display the RA0 and Dec0 for objects that are outside the solar system // 2017-09-10 JM: Exception added for asteroids and comets since we have J2000 for them. // Maybe others? Pos->RA0->setText(selectedObject->ra0().toHMSString(false, true)); Pos->Dec0->setText(selectedObject->dec0().toDMSString(false, false, true)); #if 0 if (!selectedObject->isSolarSystem() || selectedObject->type() == SkyObject::COMET || selectedObject->type() == SkyObject::ASTEROID) { Pos->RA0->setText(selectedObject->ra0().toHMSString()); Pos->Dec0->setText(selectedObject->dec0().toDMSString()); } else { Pos->RA0->setText("--"); Pos->Dec0->setText("--"); } #endif //Hour Angle can be negative, but dms HMS expressions cannot. //Here's a kludgy workaround: dms lst = geo->GSTtoLST(ut.gst()); dms ha(lst.Degrees() - selectedObject->ra().Degrees()); QChar sgn('+'); if (ha.Hours() > 12.0) { ha.setH(24.0 - ha.Hours()); sgn = '-'; } Pos->HA->setText(QString("%1%2").arg(sgn).arg(ha.toHMSString())); //Airmass is approximated as the secant of the zenith distance, //equivalent to 1./sin(Alt). Beware of Inf at Alt=0! if (selectedObject->alt().Degrees() > 0.0) Pos->Airmass->setText(QLocale().toString(selectedObject->airmass(), 'f', 2)); else Pos->Airmass->setText("--"); //Rise/Set/Transit Section: //Prepare time/position variables QTime rt = selectedObject->riseSetTime(ut, geo, true); //true = use rise time dms raz = selectedObject->riseSetTimeAz(ut, geo, true); //true = use rise time //If transit time is before rise time, use transit time for tomorrow QTime tt = selectedObject->transitTime(ut, geo); dms talt = selectedObject->transitAltitude(ut, geo); if (tt < rt) { tt = selectedObject->transitTime(ut.addDays(1), geo); talt = selectedObject->transitAltitude(ut.addDays(1), geo); } //If set time is before rise time, use set time for tomorrow QTime st = selectedObject->riseSetTime(ut, geo, false); //false = use set time dms saz = selectedObject->riseSetTimeAz(ut, geo, false); //false = use set time if (st < rt) { st = selectedObject->riseSetTime(ut.addDays(1), geo, false); //false = use set time saz = selectedObject->riseSetTimeAz(ut.addDays(1), geo, false); //false = use set time } if (rt.isValid()) { Pos->TimeRise->setText(QString().sprintf("%02d:%02d", rt.hour(), rt.minute())); Pos->TimeSet->setText(QString().sprintf("%02d:%02d", st.hour(), st.minute())); Pos->AzRise->setText(raz.toDMSString()); Pos->AzSet->setText(saz.toDMSString()); } else { if (selectedObject->alt().Degrees() > 0.0) { Pos->TimeRise->setText(i18n("Circumpolar")); Pos->TimeSet->setText(i18n("Circumpolar")); } else { Pos->TimeRise->setText(i18n("Never rises")); Pos->TimeSet->setText(i18n("Never rises")); } Pos->AzRise->setText(i18nc("Not Applicable", "N/A")); Pos->AzSet->setText(i18nc("Not Applicable", "N/A")); } Pos->TimeTransit->setText(QString().sprintf("%02d:%02d", tt.hour(), tt.minute())); Pos->AltTransit->setText(talt.toDMSString()); // Restore the position and other time-dependent parameters selectedObject->recomputeCoords(ut, geo); } void DetailDialog::createLinksTab() { // don't create a link tab for an unnamed star if (selectedObject->name() == QString("star")) return; Links = new LinksWidget(this); addPage(Links, i18n("Links")); Links->InfoTitle->setPalette(titlePalette); Links->ImagesTitle->setPalette(titlePalette); foreach (const QString &s, selectedObject->InfoTitle()) Links->InfoTitleList->addItem(i18nc("Image/info menu item (should be translated)", s.toLocal8Bit())); //Links->InfoTitleList->setCurrentRow(0); foreach (const QString &s, selectedObject->ImageTitle()) Links->ImageTitleList->addItem(i18nc("Image/info menu item (should be translated)", s.toLocal8Bit())); // Signals/Slots connect(Links->ViewButton, SIGNAL(clicked()), this, SLOT(viewLink())); connect(Links->AddLinkButton, SIGNAL(clicked()), this, SLOT(addLink())); connect(Links->EditLinkButton, SIGNAL(clicked()), this, SLOT(editLinkDialog())); connect(Links->RemoveLinkButton, SIGNAL(clicked()), this, SLOT(removeLinkDialog())); // When an item is selected in info list, selected items are cleared image list. - connect(Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, + connect(Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(setCurrentLink(QListWidgetItem*))); - connect(Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + connect(Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), Links->ImageTitleList, SLOT(clearSelection())); // vice versa - connect(Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), this, + connect(Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT(setCurrentLink(QListWidgetItem*))); - connect(Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*,QListWidgetItem*)), + connect(Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), Links->InfoTitleList, SLOT(clearSelection())); connect(Links->InfoTitleList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(viewLink())); connect(Links->ImageTitleList, SIGNAL(itemDoubleClicked(QListWidgetItem*)), this, SLOT(viewLink())); connect(Links->InfoTitleList, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons())); connect(Links->ImageTitleList, SIGNAL(itemSelectionChanged()), this, SLOT(updateButtons())); updateLists(); } void DetailDialog::addLink() { if (!selectedObject) return; QPointer adialog = new AddLinkDialog(this, selectedObject->name()); QString entry; QFile file; if (adialog->exec() == QDialog::Accepted) { if (adialog->isImageLink()) { //Add link to object's ImageList, and descriptive text to its ImageTitle list selectedObject->ImageList().append(adialog->url()); selectedObject->ImageTitle().append(adialog->desc()); //Also, update the user's custom image links database //check for user's image-links database. If it doesn't exist, create it. file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "image_url.dat"); //determine filename in local user KDE directory tree. if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) { QString message = i18n("Custom image-links file could not be opened.\nLink cannot be recorded for future sessions."); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); delete adialog; return; } else { entry = selectedObject->name() + ':' + adialog->desc() + ':' + adialog->url(); QTextStream stream(&file); stream << entry << endl; file.close(); updateLists(); } } else { selectedObject->InfoList().append(adialog->url()); selectedObject->InfoTitle().append(adialog->desc()); //check for user's image-links database. If it doesn't exist, create it. file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "info_url.dat"); //determine filename in local user KDE directory tree. if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) { QString message = i18n( - "Custom information-links file could not be opened.\nLink cannot be recorded for future sessions."); + "Custom information-links file could not be opened.\nLink cannot be recorded for future sessions."); KMessageBox::sorry(nullptr, message, i18n("Could not Open File")); delete adialog; return; } else { entry = selectedObject->name() + ':' + adialog->desc() + ':' + adialog->url(); QTextStream stream(&file); stream << entry << endl; file.close(); updateLists(); } } } delete adialog; } void DetailDialog::createAdvancedTab() { // Don't create an adv tab for an unnamed star or if advinterface file failed loading // We also don't need adv dialog for solar system objects. if (selectedObject->name() == QString("star") || KStarsData::Instance()->avdTree().isEmpty() || - selectedObject->type() == SkyObject::PLANET || selectedObject->type() == SkyObject::COMET || - selectedObject->type() == SkyObject::ASTEROID) + selectedObject->type() == SkyObject::PLANET || selectedObject->type() == SkyObject::COMET || + selectedObject->type() == SkyObject::ASTEROID) return; Adv = new DatabaseWidget(this); addPage(Adv, i18n("Advanced")); - connect(Adv->ADVTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(viewADVData())); + connect(Adv->ADVTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(viewADVData())); populateADVTree(); } void DetailDialog::createLogTab() { //Don't create a log tab for an unnamed star if (selectedObject->name() == QString("star")) return; // Log Tab Log = new LogWidget(this); addPage(Log, i18n("Log")); Log->LogTitle->setPalette(titlePalette); if (selectedObject->userLog().isEmpty()) Log->UserLog->setText( i18n("Record here observation logs and/or data on %1.", selectedObject->translatedName())); else Log->UserLog->setText(selectedObject->userLog()); //Automatically save the log contents when the widget loses focus connect(Log->UserLog, SIGNAL(focusOut()), this, SLOT(saveLogData())); } void DetailDialog::setCurrentLink(QListWidgetItem *it) { m_CurrentLink = it; } void DetailDialog::viewLink() { QString URL; if (m_CurrentLink == nullptr) return; if (m_CurrentLink->listWidget() == Links->InfoTitleList) { URL = QString(selectedObject->InfoList().at(Links->InfoTitleList->row(m_CurrentLink))); } else if (m_CurrentLink->listWidget() == Links->ImageTitleList) { URL = QString(selectedObject->ImageList().at(Links->ImageTitleList->row(m_CurrentLink))); } if (!URL.isEmpty()) QDesktopServices::openUrl(QUrl(URL)); } void DetailDialog::updateLists() { Links->InfoTitleList->clear(); Links->ImageTitleList->clear(); foreach (const QString &s, selectedObject->InfoTitle()) Links->InfoTitleList->addItem(s); foreach (const QString &s, selectedObject->ImageTitle()) Links->ImageTitleList->addItem(s); updateButtons(); } void DetailDialog::updateButtons() { bool anyLink = false; if (!Links->InfoTitleList->selectedItems().isEmpty() || !Links->ImageTitleList->selectedItems().isEmpty()) anyLink = true; // Buttons could be disabled if lists are initially empty, we enable and disable them here // depending on the current status of the list. Links->ViewButton->setEnabled(anyLink); Links->EditLinkButton->setEnabled(anyLink); Links->RemoveLinkButton->setEnabled(anyLink); } void DetailDialog::editLinkDialog() { int type = 0, row = 0; QString search_line, replace_line, currentItemTitle, currentItemURL; if (m_CurrentLink == nullptr) return; QDialog editDialog(this); editDialog.setWindowTitle(i18n("Edit Link")); QVBoxLayout *mainLayout = new QVBoxLayout; QFrame editFrame(&editDialog); mainLayout->addWidget(&editFrame); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(accepted()), &editDialog, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), &editDialog, SLOT(reject())); editDialog.setLayout(mainLayout); if (m_CurrentLink->listWidget() == Links->InfoTitleList) { row = Links->InfoTitleList->row(m_CurrentLink); currentItemTitle = m_CurrentLink->text(); currentItemURL = selectedObject->InfoList().at(row); search_line = selectedObject->name(); search_line += ':'; search_line += currentItemTitle; search_line += ':'; search_line += currentItemURL; type = 0; } else if (m_CurrentLink->listWidget() == Links->ImageTitleList) { row = Links->ImageTitleList->row(m_CurrentLink); currentItemTitle = m_CurrentLink->text(); currentItemURL = selectedObject->ImageList().at(row); search_line = selectedObject->name(); search_line += ':'; search_line += currentItemTitle; search_line += ':'; search_line += currentItemURL; type = 1; } else return; QLineEdit editNameField(&editFrame); editNameField.setObjectName("nameedit"); editNameField.home(false); editNameField.setText(currentItemTitle); QLabel editLinkURL(i18n("URL:"), &editFrame); QLineEdit editLinkField(&editFrame); editLinkField.setObjectName("urledit"); editLinkField.home(false); editLinkField.setText(currentItemURL); QVBoxLayout vlay(&editFrame); vlay.setObjectName("vlay"); QHBoxLayout editLinkLayout(&editFrame); editLinkLayout.setObjectName("editlinklayout"); editLinkLayout.addWidget(&editLinkURL); editLinkLayout.addWidget(&editLinkField); vlay.addWidget(&editNameField); vlay.addLayout(&editLinkLayout); bool go(true); // If user presses cancel then skip the action if (editDialog.exec() != QDialog::Accepted) go = false; // If nothing changed, skip th action if (editLinkField.text() == currentItemURL && editNameField.text() == currentItemTitle) go = false; if (go) { replace_line = selectedObject->name() + ':' + editNameField.text() + ':' + editLinkField.text(); // Info Link if (type == 0) { selectedObject->InfoTitle().replace(row, editNameField.text()); selectedObject->InfoList().replace(row, editLinkField.text()); // Image Links } else { selectedObject->ImageTitle().replace(row, editNameField.text()); selectedObject->ImageList().replace(row, editLinkField.text()); } // Update local files updateLocalDatabase(type, search_line, replace_line); // Set focus to the same item again if (type == 0) Links->InfoTitleList->setCurrentRow(row); else Links->ImageTitleList->setCurrentRow(row); } } void DetailDialog::removeLinkDialog() { int type = 0, row = 0; QString currentItemURL, currentItemTitle, LineEntry, TempFileName; QFile URLFile; QTemporaryFile TempFile; TempFile.setAutoRemove(false); TempFile.open(); TempFileName = TempFile.fileName(); if (m_CurrentLink == nullptr) return; if (m_CurrentLink->listWidget() == Links->InfoTitleList) { row = Links->InfoTitleList->row(m_CurrentLink); currentItemTitle = m_CurrentLink->text(); currentItemURL = selectedObject->InfoList()[row]; LineEntry = selectedObject->name(); LineEntry += ':'; LineEntry += currentItemTitle; LineEntry += ':'; LineEntry += currentItemURL; type = 0; } else if (m_CurrentLink->listWidget() == Links->ImageTitleList) { row = Links->ImageTitleList->row(m_CurrentLink); currentItemTitle = m_CurrentLink->text(); currentItemURL = selectedObject->ImageList()[row]; LineEntry = selectedObject->name(); LineEntry += ':'; LineEntry += currentItemTitle; LineEntry += ':'; LineEntry += currentItemURL; type = 1; } else return; if (KMessageBox::warningContinueCancel(nullptr, i18n("Are you sure you want to remove the %1 link?", currentItemTitle), i18n("Delete Confirmation"), KStandardGuiItem::del()) != KMessageBox::Continue) return; if (type == 0) { selectedObject->InfoTitle().removeAt(row); selectedObject->InfoList().removeAt(row); } else { selectedObject->ImageTitle().removeAt(row); selectedObject->ImageList().removeAt(row); } // Remove link from file updateLocalDatabase(type, LineEntry); // Set focus to the 1st item in the list if (type == 0) Links->InfoTitleList->clearSelection(); else Links->ImageTitleList->clearSelection(); } void DetailDialog::updateLocalDatabase(int type, const QString &search_line, const QString &replace_line) { QString TempFileName, file_line; QFile URLFile; QTemporaryFile TempFile; TempFile.setAutoRemove(false); TempFile.open(); QTextStream *temp_stream = nullptr; QTextStream *out_stream = nullptr; bool replace = !replace_line.isEmpty(); if (search_line.isEmpty()) return; TempFileName = TempFile.fileName(); switch (type) { // Info Links case 0: // Get name for our local info_url file URLFile.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "info_url.dat"); break; // Image Links case 1: // Get name for our local info_url file URLFile.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "image_url.dat"); break; } // Copy URL file to temp file KIO::file_copy(QUrl::fromLocalFile(URLFile.fileName()), QUrl::fromLocalFile(TempFileName), -1, KIO::Overwrite | KIO::HideProgressInfo); if (!URLFile.open(QIODevice::WriteOnly)) { qDebug() << "DetailDialog: Failed to open " << URLFile.fileName(); qDebug() << "KStars cannot save to user database"; return; } // Get streams; temp_stream = new QTextStream(&TempFile); out_stream = new QTextStream(&URLFile); while (!temp_stream->atEnd()) { file_line = temp_stream->readLine(); // If we find a match, either replace, or remove (by skipping). if (file_line == search_line) { if (replace) (*out_stream) << replace_line << endl; else continue; } else (*out_stream) << file_line << endl; } URLFile.close(); delete (temp_stream); delete (out_stream); updateLists(); } void DetailDialog::populateADVTree() { QTreeWidgetItem *parent = nullptr; QTreeWidgetItem *temp = nullptr; // We populate the tree iteratively, keeping track of parents as we go // This solution is more efficient than the previous recursion algorithm. foreach (ADVTreeData *item, KStarsData::Instance()->avdTree()) { switch (item->Type) { // Top Level case 0: temp = new QTreeWidgetItem(parent, QStringList(i18nc("Advanced URLs: description or category", item->Name.toLocal8Bit().data()))); if (parent == nullptr) Adv->ADVTree->addTopLevelItem(temp); parent = temp; break; // End of top level case 1: if (parent != nullptr) parent = parent->parent(); break; // Leaf case 2: new QTreeWidgetItem(parent, QStringList(i18nc("Advanced URLs: description or category", item->Name.toLocal8Bit().data()))); break; } } } void DetailDialog::viewADVData() { QString link; QTreeWidgetItem *current = Adv->ADVTree->currentItem(); //If the item has children or is invalid, do nothing if (!current || current->childCount() > 0) return; foreach (ADVTreeData *item, KStarsData::Instance()->avdTree()) { if (item->Name == current->text(0)) { link = item->Link; link = parseADVData(link); QDesktopServices::openUrl(QUrl(link)); return; } } } QString DetailDialog::parseADVData(const QString &inlink) { QString link = inlink; QString subLink; int index; if ((index = link.indexOf("KSOBJ")) != -1) { link.remove(index, 5); link = link.insert(index, selectedObject->name()); } if ((index = link.indexOf("KSRA")) != -1) { link.remove(index, 4); subLink.sprintf("%02d%02d%02d", selectedObject->ra0().hour(), selectedObject->ra0().minute(), selectedObject->ra0().second()); subLink = subLink.insert(2, "%20"); subLink = subLink.insert(7, "%20"); link = link.insert(index, subLink); } if ((index = link.indexOf("KSDEC")) != -1) { link.remove(index, 5); if (selectedObject->dec().degree() < 0) { subLink.sprintf("%03d%02d%02d", selectedObject->dec0().degree(), selectedObject->dec0().arcmin(), selectedObject->dec0().arcsec()); subLink = subLink.insert(3, "%20"); subLink = subLink.insert(8, "%20"); } else { subLink.sprintf("%02d%02d%02d", selectedObject->dec0().degree(), selectedObject->dec0().arcmin(), selectedObject->dec0().arcsec()); subLink = subLink.insert(0, "%2B"); subLink = subLink.insert(5, "%20"); subLink = subLink.insert(10, "%20"); } link = link.insert(index, subLink); } return link; } void DetailDialog::saveLogData() { selectedObject->saveUserLog(Log->UserLog->toPlainText()); } void DetailDialog::addToObservingList() { KStarsData::Instance()->observingList()->slotAddObject(selectedObject); } void DetailDialog::centerMap() { SkyMap::Instance()->setClickedObject(selectedObject); SkyMap::Instance()->slotCenter(); } void DetailDialog::centerTelescope() { #ifdef HAVE_INDI if (INDIListener::Instance()->size() == 0) { KMessageBox::sorry(0, i18n("KStars did not find any active telescopes.")); return; } foreach (ISD::GDInterface *gd, INDIListener::Instance()->getDevices()) { INDI::BaseDevice *bd = gd->getBaseDevice(); if (gd->getType() != KSTARS_TELESCOPE) continue; if (bd == nullptr) continue; if (bd->isConnected() == false) { KMessageBox::error(0, i18n("Telescope %1 is offline. Please connect and retry again.", gd->getDeviceName())); return; } // Display Sun warning on slew - if (selectedObject && selectedObject->name() == "Sun") + if (selectedObject && selectedObject->name() == i18n("Sun")) { if (KMessageBox::warningContinueCancel(nullptr, i18n("Danger! Viewing the Sun without adequate solar filters is dangerous and will result in permanent eye damage!")) - ==KMessageBox::Cancel) + == KMessageBox::Cancel) return; } ISD::GDSetCommand SlewCMD(INDI_SWITCH, "ON_COORD_SET", "TRACK", ISS_ON, this); gd->setProperty(&SlewCMD); gd->runCommand(INDI_SEND_COORDS, selectedObject); return; } KMessageBox::sorry(0, i18n("KStars did not find any active telescopes.")); #endif } void DetailDialog::showThumbnail() { //No image if object is a star if (selectedObject->type() == SkyObject::STAR || selectedObject->type() == SkyObject::CATALOG_STAR) { Thumbnail->scaled(Data->Image->width(), Data->Image->height()); Thumbnail->fill(Data->DataFrame->palette().color(QPalette::Window)); Data->Image->setPixmap(*Thumbnail); return; } //Try to load the object's image from disk //If no image found, load "no image" image QFile file; QString fname = "thumb-" + selectedObject->name().toLower().remove(' ').remove('/') + ".png"; if (KSUtils::openDataFile(file, fname)) { file.close(); Thumbnail->load(file.fileName(), "PNG"); } else Thumbnail->load(":/images/noimage.png"); *Thumbnail = Thumbnail->scaled(Data->Image->width(), Data->Image->height(), Qt::KeepAspectRatio, Qt::FastTransformation); Data->Image->setPixmap(*Thumbnail); } void DetailDialog::updateThumbnail() { QPointer tp = new ThumbnailPicker(selectedObject, *Thumbnail, this); if (tp->exec() == QDialog::Accepted) { QString fname = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "thumb-" + selectedObject->name().toLower().remove(' ').remove('/') + ".png"; Data->Image->setPixmap(*(tp->image())); //If a real image was set, save it. //If the image was unset, delete the old image on disk. if (tp->imageFound()) { bool rc = Data->Image->pixmap()->save(fname, "PNG"); if (rc == false) { KMessageBox::error(nullptr, i18n("Unable to save image to %1", fname), i18n("Save Thumbnail")); } else *Thumbnail = *(Data->Image->pixmap()); } else { QFile f; f.setFileName(fname); f.remove(); } } delete tp; } DataWidget::DataWidget(QWidget *p) : QFrame(p) { setupUi(this); DataFrame->setBackgroundRole(QPalette::Base); } DataCometWidget::DataCometWidget(QWidget *p) : QFrame(p) { setupUi(this); } PositionWidget::PositionWidget(QWidget *p) : QFrame(p) { setupUi(this); CoordFrame->setBackgroundRole(QPalette::Base); RSTFrame->setBackgroundRole(QPalette::Base); } LinksWidget::LinksWidget(QWidget *p) : QFrame(p) { setupUi(this); } DatabaseWidget::DatabaseWidget(QWidget *p) : QFrame(p) { setupUi(this); } LogWidget::LogWidget(QWidget *p) : QFrame(p) { setupUi(this); } diff --git a/kstars/ekos/scheduler/schedulerjob.cpp b/kstars/ekos/scheduler/schedulerjob.cpp index c5a977e24..6820ee245 100644 --- a/kstars/ekos/scheduler/schedulerjob.cpp +++ b/kstars/ekos/scheduler/schedulerjob.cpp @@ -1,1054 +1,1056 @@ /* Ekos Scheduler Job Copyright (C) Jasem Mutlaq This application 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 "schedulerjob.h" #include "dms.h" #include "kstarsdata.h" #include "skymapcomposite.h" #include "Options.h" #include "scheduler.h" #include #include #include #define BAD_SCORE -1000 #define MIN_ALTITUDE 15.0 SchedulerJob::SchedulerJob() { - moon = dynamic_cast(KStarsData::Instance()->skyComposite()->findByName("Moon")); + moon = dynamic_cast(KStarsData::Instance()->skyComposite()->findByName(i18n("Moon"))); } void SchedulerJob::setName(const QString &value) { name = value; updateJobCells(); } void SchedulerJob::setStartupCondition(const StartupCondition &value) { startupCondition = value; /* Keep startup time and condition valid */ if (value == START_ASAP) startupTime = QDateTime(); /* Refresh estimated time - which update job cells */ setEstimatedTime(estimatedTime); } void SchedulerJob::setStartupTime(const QDateTime &value) { startupTime = value; /* Keep startup time and condition valid */ if (value.isValid()) startupCondition = START_AT; else startupCondition = fileStartupCondition; // Refresh altitude - invalid date/time is taken care of when rendering altitudeAtStartup = findAltitude(targetCoords, startupTime, &isSettingAtStartup); /* Refresh estimated time - which update job cells */ setEstimatedTime(estimatedTime); } void SchedulerJob::setSequenceFile(const QUrl &value) { sequenceFile = value; } void SchedulerJob::setFITSFile(const QUrl &value) { fitsFile = value; } void SchedulerJob::setMinAltitude(const double &value) { minAltitude = value; } void SchedulerJob::setMinMoonSeparation(const double &value) { minMoonSeparation = value; } void SchedulerJob::setEnforceWeather(bool value) { enforceWeather = value; } void SchedulerJob::setCompletionTime(const QDateTime &value) { /* If completion time is valid, automatically switch condition to FINISH_AT */ if (value.isValid()) { setCompletionCondition(FINISH_AT); completionTime = value; altitudeAtCompletion = findAltitude(targetCoords, completionTime, &isSettingAtCompletion); setEstimatedTime(-1); } /* If completion time is invalid, and job is looping, keep completion time undefined */ else if (FINISH_LOOP == completionCondition) { completionTime = QDateTime(); altitudeAtCompletion = findAltitude(targetCoords, completionTime, &isSettingAtCompletion); setEstimatedTime(-1); } /* If completion time is invalid, deduce completion from startup and duration */ else if (startupTime.isValid()) { completionTime = startupTime.addSecs(estimatedTime); altitudeAtCompletion = findAltitude(targetCoords, completionTime, &isSettingAtCompletion); updateJobCells(); } /* Else just refresh estimated time - which update job cells */ else setEstimatedTime(estimatedTime); /* Invariants */ Q_ASSERT_X(completionTime.isValid() ? - (FINISH_AT == completionCondition || FINISH_REPEAT == completionCondition || FINISH_SEQUENCE == completionCondition) : - FINISH_LOOP == completionCondition, - __FUNCTION__, "Valid completion time implies job is FINISH_AT/REPEAT/SEQUENCE, else job is FINISH_LOOP."); + (FINISH_AT == completionCondition || FINISH_REPEAT == completionCondition || FINISH_SEQUENCE == completionCondition) : + FINISH_LOOP == completionCondition, + __FUNCTION__, "Valid completion time implies job is FINISH_AT/REPEAT/SEQUENCE, else job is FINISH_LOOP."); } void SchedulerJob::setCompletionCondition(const CompletionCondition &value) { completionCondition = value; // Update repeats requirement, looping jobs have none switch (completionCondition) { case FINISH_LOOP: setCompletionTime(QDateTime()); - /* Fall through */ + /* Fall through */ case FINISH_AT: if (0 < getRepeatsRequired()) setRepeatsRequired(0); break; case FINISH_SEQUENCE: if (1 != getRepeatsRequired()) setRepeatsRequired(1); break; case FINISH_REPEAT: if (0 == getRepeatsRequired()) setRepeatsRequired(1); break; - default: break; + default: + break; } updateJobCells(); } void SchedulerJob::setStepPipeline(const StepPipeline &value) { stepPipeline = value; } void SchedulerJob::setState(const JOBStatus &value) { state = value; /* FIXME: move this to Scheduler, SchedulerJob is mostly a model */ if (JOB_ERROR == state) KNotification::event(QLatin1String("EkosSchedulerJobFail"), i18n("Ekos job failed (%1)", getName())); /* If job becomes invalid, automatically reset its startup characteristics, and force its duration to be reestimated */ if (JOB_INVALID == value) { setStartupCondition(fileStartupCondition); setStartupTime(fileStartupTime); setEstimatedTime(-1); } /* If job is aborted, automatically reset its startup characteristics */ if (JOB_ABORTED == value) { setStartupCondition(fileStartupCondition); /* setStartupTime(fileStartupTime); */ } updateJobCells(); } void SchedulerJob::setLeadTime(const int64_t &value) { leadTime = value; updateJobCells(); } void SchedulerJob::setScore(int value) { score = value; updateJobCells(); } void SchedulerJob::setCulminationOffset(const int16_t &value) { culminationOffset = value; } void SchedulerJob::setSequenceCount(const int count) { sequenceCount = count; updateJobCells(); } void SchedulerJob::setNameCell(QTableWidgetItem *value) { nameCell = value; } void SchedulerJob::setCompletedCount(const int count) { completedCount = count; updateJobCells(); } void SchedulerJob::setStatusCell(QTableWidgetItem *value) { statusCell = value; if (nullptr != statusCell) statusCell->setToolTip(i18n("Current status of job '%1', managed by the Scheduler.\n" "If invalid, the Scheduler was not able to find a proper observation time for the target.\n" "If aborted, the Scheduler missed the scheduled time or encountered transitory issues and will reschedule the job.\n" "If complete, the Scheduler verified that all sequence captures requested were stored, including repeats.", name)); } void SchedulerJob::setAltitudeCell(QTableWidgetItem *value) { altitudeCell = value; if (nullptr != altitudeCell) altitudeCell->setToolTip(i18n("Current altitude of the target of job '%1'.\n" - "The altitude at startup, if available, is displayed between parentheses.\n" - "A rising target is indicated with an arrow going up.\n" - "A setting target is indicated with an arrow going down.", - name)); + "The altitude at startup, if available, is displayed between parentheses.\n" + "A rising target is indicated with an arrow going up.\n" + "A setting target is indicated with an arrow going down.", + name)); } void SchedulerJob::setStartupCell(QTableWidgetItem *value) { startupCell = value; if (nullptr != startupCell) startupCell->setToolTip(i18n("Startup time for job '%1', as estimated by the Scheduler.\n" "Fixed time from user or culmination time is marked with a chronometer symbol. ", name)); } void SchedulerJob::setCompletionCell(QTableWidgetItem *value) { completionCell = value; if (nullptr != completionCell) completionCell->setToolTip(i18n("Completion time for job '%1', as estimated by the Scheduler.\n" "You may specify a fixed time to limit duration of looping jobs. " "A warning symbol indicates the altitude at completion may cause the job to abort before completion.\n", name)); } void SchedulerJob::setCaptureCountCell(QTableWidgetItem *value) { captureCountCell = value; if (nullptr != captureCountCell) captureCountCell->setToolTip(i18n("Count of captures stored for job '%1', based on its sequence job.\n" "This is a summary, additional specific frame types may be required to complete the job.", name)); } void SchedulerJob::setScoreCell(QTableWidgetItem *value) { scoreCell = value; if (nullptr != scoreCell) scoreCell->setToolTip(i18n("Current score for job '%1', from its altitude, moon separation and sky darkness.\n" "Negative if adequate altitude is not achieved yet or if there is no proper observation time today.\n" "The Scheduler will refresh scores when picking a new candidate job.", name)); } void SchedulerJob::setLeadTimeCell(QTableWidgetItem *value) { leadTimeCell = value; if (nullptr != leadTimeCell) leadTimeCell->setToolTip(i18n("Time interval from the job which precedes job '%1'.\n" - "Adjust the Lead Time in Ekos options to increase that duration and leave time for jobs to complete.\n" - "Rearrange jobs to minimize that duration and optimize your imaging time.", - name)); + "Adjust the Lead Time in Ekos options to increase that duration and leave time for jobs to complete.\n" + "Rearrange jobs to minimize that duration and optimize your imaging time.", + name)); } void SchedulerJob::setDateTimeDisplayFormat(const QString &value) { dateTimeDisplayFormat = value; updateJobCells(); } void SchedulerJob::setStage(const JOBStage &value) { stage = value; updateJobCells(); } void SchedulerJob::setStageCell(QTableWidgetItem *cell) { stageCell = cell; // FIXME: Add a tool tip if cell is used } void SchedulerJob::setStageLabel(QLabel *label) { stageLabel = label; } void SchedulerJob::setFileStartupCondition(const StartupCondition &value) { fileStartupCondition = value; } void SchedulerJob::setFileStartupTime(const QDateTime &value) { fileStartupTime = value; } void SchedulerJob::setEstimatedTime(const int64_t &value) { /* Estimated time is generally the difference between startup and completion times: * - It is fixed when startup and completion times are fixed, that is, we disregard the argument * - Else mostly it pushes completion time from startup time - * + * * However it cannot advance startup time when completion time is fixed because of the way jobs are scheduled. * This situation requires a warning in the user interface when there is not enough time for the job to process. */ /* If startup and completion times are fixed, estimated time cannot change - disregard the argument */ if (START_ASAP != fileStartupCondition && FINISH_AT == completionCondition) { estimatedTime = startupTime.secsTo(completionTime); } /* If completion time isn't fixed, estimated time adjusts completion time */ else if (FINISH_AT != completionCondition && FINISH_LOOP != completionCondition) { estimatedTime = value; completionTime = startupTime.addSecs(value); altitudeAtCompletion = findAltitude(targetCoords, completionTime, &isSettingAtCompletion); } /* Else estimated time is simply stored as is - covers FINISH_LOOP from setCompletionTime */ else estimatedTime = value; updateJobCells(); } void SchedulerJob::setInSequenceFocus(bool value) { inSequenceFocus = value; } void SchedulerJob::setPriority(const uint8_t &value) { priority = value; } void SchedulerJob::setEnforceTwilight(bool value) { enforceTwilight = value; } void SchedulerJob::setEstimatedTimeCell(QTableWidgetItem *value) { estimatedTimeCell = value; if (estimatedTimeCell) estimatedTimeCell->setToolTip(i18n("Duration job '%1' will take to complete when started, as estimated by the Scheduler.\n" - "Depends on the actions to be run, and the sequence job to be processed.", - name)); + "Depends on the actions to be run, and the sequence job to be processed.", + name)); } void SchedulerJob::setLightFramesRequired(bool value) { lightFramesRequired = value; } void SchedulerJob::setRepeatsRequired(const uint16_t &value) { repeatsRequired = value; // Update completion condition to be compatible if (1 < repeatsRequired) { if (FINISH_REPEAT != completionCondition) setCompletionCondition(FINISH_REPEAT); } else if (0 < repeatsRequired) { if (FINISH_SEQUENCE != completionCondition) setCompletionCondition(FINISH_SEQUENCE); } else { if (FINISH_LOOP != completionCondition) setCompletionCondition(FINISH_LOOP); } updateJobCells(); } void SchedulerJob::setRepeatsRemaining(const uint16_t &value) { repeatsRemaining = value; updateJobCells(); } void SchedulerJob::setCapturedFramesMap(const CapturedFramesMap &value) { capturedFramesMap = value; } -void SchedulerJob::setTargetCoords(dms& ra, dms& dec) +void SchedulerJob::setTargetCoords(dms &ra, dms &dec) { targetCoords.setRA0(ra); targetCoords.setDec0(dec); targetCoords.apparentCoord(static_cast(J2000), KStarsData::Instance()->ut().djd()); } void SchedulerJob::updateJobCells() { if (nullptr != nameCell) { nameCell->setText(name); if (nullptr != nameCell) nameCell->tableWidget()->resizeColumnToContents(nameCell->column()); } if (nullptr != nameLabel) { nameLabel->setText(name + QString(":")); } if (nullptr != statusCell) { static QMap stateStrings; static QString stateStringUnknown; if (stateStrings.isEmpty()) { stateStrings[JOB_IDLE] = i18n("Idle"); stateStrings[JOB_EVALUATION] = i18n("Evaluating"); stateStrings[JOB_SCHEDULED] = i18n("Scheduled"); stateStrings[JOB_BUSY] = i18n("Running"); stateStrings[JOB_INVALID] = i18n("Invalid"); stateStrings[JOB_COMPLETE] = i18n("Complete"); stateStrings[JOB_ABORTED] = i18n("Aborted"); stateStrings[JOB_ERROR] = i18n("Error"); stateStringUnknown = i18n("Unknown"); } statusCell->setText(stateStrings.value(state, stateStringUnknown)); if (nullptr != statusCell->tableWidget()) statusCell->tableWidget()->resizeColumnToContents(statusCell->column()); } if (nullptr != stageCell || nullptr != stageLabel) { /* Translated string cache - overkill, probably, and doesn't warn about missing enums like switch/case should ; also, not thread-safe */ /* FIXME: this should work with a static initializer in C++11, but QT versions are touchy on this, and perhaps i18n can't be used? */ static QMap stageStrings; static QString stageStringUnknown; if (stageStrings.isEmpty()) { stageStrings[STAGE_IDLE] = i18n("Idle"); stageStrings[STAGE_SLEWING] = i18n("Slewing"); stageStrings[STAGE_SLEW_COMPLETE] = i18n("Slew complete"); stageStrings[STAGE_FOCUSING] = - stageStrings[STAGE_POSTALIGN_FOCUSING] = i18n("Focusing"); + stageStrings[STAGE_POSTALIGN_FOCUSING] = i18n("Focusing"); stageStrings[STAGE_FOCUS_COMPLETE] = - stageStrings[STAGE_POSTALIGN_FOCUSING_COMPLETE ] = i18n("Focus complete"); + stageStrings[STAGE_POSTALIGN_FOCUSING_COMPLETE ] = i18n("Focus complete"); stageStrings[STAGE_ALIGNING] = i18n("Aligning"); stageStrings[STAGE_ALIGN_COMPLETE] = i18n("Align complete"); stageStrings[STAGE_RESLEWING] = i18n("Repositioning"); stageStrings[STAGE_RESLEWING_COMPLETE] = i18n("Repositioning complete"); /*stageStrings[STAGE_CALIBRATING] = i18n("Calibrating");*/ stageStrings[STAGE_GUIDING] = i18n("Guiding"); stageStrings[STAGE_GUIDING_COMPLETE] = i18n("Guiding complete"); stageStrings[STAGE_CAPTURING] = i18n("Capturing"); stageStringUnknown = i18n("Unknown"); } if (nullptr != stageCell) { stageCell->setText(stageStrings.value(stage, stageStringUnknown)); if (nullptr != stageCell->tableWidget()) stageCell->tableWidget()->resizeColumnToContents(stageCell->column()); } if (nullptr != stageLabel) { stageLabel->setText(QString("%1: %2").arg(name, stageStrings.value(stage, stageStringUnknown))); } } if (nullptr != startupCell) { /* Display startup time if it is valid */ if (startupTime.isValid()) { startupCell->setText(QString("%1%2%L3° %4") - .arg(altitudeAtStartup < minAltitude ? QString(QChar(0x26A0)) : "") - .arg(QChar(isSettingAtStartup ? 0x2193 : 0x2191)) - .arg(altitudeAtStartup, 0, 'f', 1) - .arg(startupTime.toString(dateTimeDisplayFormat))); + .arg(altitudeAtStartup < minAltitude ? QString(QChar(0x26A0)) : "") + .arg(QChar(isSettingAtStartup ? 0x2193 : 0x2191)) + .arg(altitudeAtStartup, 0, 'f', 1) + .arg(startupTime.toString(dateTimeDisplayFormat))); switch (fileStartupCondition) { /* If the original condition is START_AT/START_CULMINATION, startup time is fixed */ case START_AT: case START_CULMINATION: startupCell->setIcon(QIcon::fromTheme("chronometer")); break; /* If the original condition is START_ASAP, startup time is informational */ case START_ASAP: startupCell->setIcon(QIcon()); break; - default: break; + default: + break; } } /* Else do not display any startup time */ else { startupCell->setText("-"); startupCell->setIcon(QIcon()); } if (nullptr != startupCell->tableWidget()) startupCell->tableWidget()->resizeColumnToContents(startupCell->column()); } if (nullptr != altitudeCell) { // FIXME: Cache altitude calculations bool is_setting = false; double const alt = findAltitude(targetCoords, QDateTime(), &is_setting); altitudeCell->setText(QString("%1%L2°") - .arg(QChar(is_setting ? 0x2193 : 0x2191)) - .arg(alt, 0, 'f', 1)); + .arg(QChar(is_setting ? 0x2193 : 0x2191)) + .arg(alt, 0, 'f', 1)); if (nullptr != altitudeCell->tableWidget()) altitudeCell->tableWidget()->resizeColumnToContents(altitudeCell->column()); } if (nullptr != completionCell) { /* Display completion time if it is valid and job is not looping */ if (FINISH_LOOP != completionCondition && completionTime.isValid()) { completionCell->setText(QString("%1%2%L3° %4") - .arg(altitudeAtCompletion < minAltitude ? QString(QChar(0x26A0)) : "") - .arg(QChar(isSettingAtCompletion ? 0x2193 : 0x2191)) - .arg(altitudeAtCompletion, 0, 'f', 1) - .arg(completionTime.toString(dateTimeDisplayFormat))); + .arg(altitudeAtCompletion < minAltitude ? QString(QChar(0x26A0)) : "") + .arg(QChar(isSettingAtCompletion ? 0x2193 : 0x2191)) + .arg(altitudeAtCompletion, 0, 'f', 1) + .arg(completionTime.toString(dateTimeDisplayFormat))); switch (completionCondition) { case FINISH_AT: completionCell->setIcon(QIcon::fromTheme("chronometer")); break; case FINISH_SEQUENCE: case FINISH_REPEAT: default: completionCell->setIcon(QIcon()); break; } } /* Else do not display any completion time */ else { completionCell->setText("-"); completionCell->setIcon(QIcon()); } if (nullptr != completionCell->tableWidget()) completionCell->tableWidget()->resizeColumnToContents(completionCell->column()); } if (nullptr != estimatedTimeCell) { if (0 < estimatedTime) /* Seconds to ms - this doesn't follow dateTimeDisplayFormat, which renders YMD too */ - estimatedTimeCell->setText(QTime::fromMSecsSinceStartOfDay(estimatedTime*1000).toString("HH:mm:ss")); + estimatedTimeCell->setText(QTime::fromMSecsSinceStartOfDay(estimatedTime * 1000).toString("HH:mm:ss")); #if 0 else if(0 == estimatedTime) /* FIXME: this special case could be merged with the previous, kept for future to indicate actual duration */ estimatedTimeCell->setText("00:00:00"); #endif else /* Invalid marker */ estimatedTimeCell->setText("-"); /* Warn the end-user if estimated time doesn't fit in the startup/completion interval */ if (estimatedTime < startupTime.secsTo(completionTime)) estimatedTimeCell->setIcon(QIcon::fromTheme("document-find")); else estimatedTimeCell->setIcon(QIcon()); if (nullptr != estimatedTimeCell->tableWidget()) estimatedTimeCell->tableWidget()->resizeColumnToContents(estimatedTimeCell->column()); } if (nullptr != captureCountCell) { switch (completionCondition) { case FINISH_AT: - // FIXME: Attempt to calculate the number of frames until end - requires detailed imaging time + // FIXME: Attempt to calculate the number of frames until end - requires detailed imaging time case FINISH_LOOP: // If looping, display the count of completed frames captureCountCell->setText(QString("%L1/-").arg(completedCount)); break; case FINISH_SEQUENCE: case FINISH_REPEAT: default: // If repeating, display the count of completed frames to the count of requested frames captureCountCell->setText(QString("%L1/%L2").arg(completedCount).arg(sequenceCount)); break; } if (nullptr != captureCountCell->tableWidget()) captureCountCell->tableWidget()->resizeColumnToContents(captureCountCell->column()); } if (nullptr != scoreCell) { if (0 <= score) scoreCell->setText(QString("%L1").arg(score)); else /* FIXME: negative scores are just weird for the end-user */ scoreCell->setText("<0"); if (nullptr != scoreCell->tableWidget()) scoreCell->tableWidget()->resizeColumnToContents(scoreCell->column()); } if (nullptr != leadTimeCell) { // Display lead time, plus a warning if lead time is more than twice the lead time of the Ekos options switch (state) { case JOB_INVALID: case JOB_ERROR: case JOB_COMPLETE: leadTimeCell->setText("-"); break; default: leadTimeCell->setText(QString("%1%2") - .arg(Options::leadTime() * 60 * 2 < leadTime ? QString(QChar(0x26A0)) : "") - .arg(QTime::fromMSecsSinceStartOfDay(leadTime*1000).toString("HH:mm:ss"))); + .arg(Options::leadTime() * 60 * 2 < leadTime ? QString(QChar(0x26A0)) : "") + .arg(QTime::fromMSecsSinceStartOfDay(leadTime * 1000).toString("HH:mm:ss"))); break; } if (nullptr != leadTimeCell->tableWidget()) leadTimeCell->tableWidget()->resizeColumnToContents(leadTimeCell->column()); } } void SchedulerJob::reset() { state = JOB_IDLE; stage = STAGE_IDLE; estimatedTime = -1; leadTime = 0; startupCondition = fileStartupCondition; startupTime = fileStartupCondition == START_AT ? fileStartupTime : QDateTime(); /* No change to culmination offset */ repeatsRemaining = repeatsRequired; updateJobCells(); } bool SchedulerJob::decreasingScoreOrder(SchedulerJob const *job1, SchedulerJob const *job2) { return job1->getScore() > job2->getScore(); } bool SchedulerJob::increasingPriorityOrder(SchedulerJob const *job1, SchedulerJob const *job2) { return job1->getPriority() < job2->getPriority(); } bool SchedulerJob::decreasingAltitudeOrder(SchedulerJob const *job1, SchedulerJob const *job2, QDateTime const &when) { bool A_is_setting = job1->isSettingAtStartup; double const altA = when.isValid() ? - findAltitude(job1->getTargetCoords(), when, &A_is_setting) : - job1->altitudeAtStartup; + findAltitude(job1->getTargetCoords(), when, &A_is_setting) : + job1->altitudeAtStartup; bool B_is_setting = job2->isSettingAtStartup; double const altB = when.isValid() ? - findAltitude(job2->getTargetCoords(), when, &B_is_setting) : - job2->altitudeAtStartup; + findAltitude(job2->getTargetCoords(), when, &B_is_setting) : + job2->altitudeAtStartup; // Sort with the setting target first if (A_is_setting && !B_is_setting) return true; else if (!A_is_setting && B_is_setting) return false; // If both targets rise or set, sort by decreasing altitude, considering a setting target is prioritary return (A_is_setting && B_is_setting) ? altA < altB : altB < altA; } bool SchedulerJob::increasingStartupTimeOrder(SchedulerJob const *job1, SchedulerJob const *job2) { return job1->getStartupTime() < job2->getStartupTime(); } int16_t SchedulerJob::getAltitudeScore(QDateTime const &when) const { // FIXME: block calculating target coordinates at a particular time is duplicated in several places GeoLocation *geo = KStarsData::Instance()->geo(); // Retrieve the argument date/time, or fall back to current time - don't use QDateTime's timezone! KStarsDateTime ltWhen(when.isValid() ? Qt::UTC == when.timeSpec() ? geo->UTtoLT(KStarsDateTime(when)) : when : KStarsData::Instance()->lt()); // Create a sky object with the target catalog coordinates SkyPoint const target = getTargetCoords(); SkyObject o; o.setRA0(target.ra0()); o.setDec0(target.dec0()); // Update RA/DEC of the target for the current fraction of the day KSNumbers numbers(ltWhen.djd()); o.updateCoordsNow(&numbers); // Compute local sidereal time for the current fraction of the day, calculate altitude CachingDms const LST = geo->GSTtoLST(geo->LTtoUT(ltWhen).gst()); o.EquatorialToHorizontal(&LST, geo->lat()); double const altitude = o.alt().Degrees(); double const SETTING_ALTITUDE_CUTOFF = Options::settingAltitudeCutoff(); int16_t score = BAD_SCORE - 1; // If altitude is negative, bad score // FIXME: some locations may allow negative altitudes if (altitude < 0) { score = BAD_SCORE; } else if (-90 < getMinAltitude()) { // If under altitude constraint, bad score if (altitude < getMinAltitude()) score = BAD_SCORE; // Else if setting and under altitude cutoff, job would end soon after starting, bad score // FIXME: half bad score when under altitude cutoff risk getting positive again else { double offset = LST.Hours() - o.ra().Hours(); if (24.0 <= offset) offset -= 24.0; else if (offset < 0.0) offset += 24.0; if (0.0 <= offset && offset < 12.0) if (altitude - SETTING_ALTITUDE_CUTOFF < getMinAltitude()) score = BAD_SCORE / 2; } } // If not constrained but below minimum hard altitude, set score to 10% of altitude value else if (altitude < MIN_ALTITUDE) { score = static_cast (altitude / 10.0); } // Else default score calculation without altitude constraint if (score < BAD_SCORE) score = static_cast ((1.5 * pow(1.06, altitude)) - (MIN_ALTITUDE / 10.0)); //qCDebug(KSTARS_EKOS_SCHEDULER) << QString("Job '%1' target altitude is %3 degrees at %2 (score %4).") // .arg(getName()) // .arg(when.toString(getDateTimeDisplayFormat())) // .arg(currentAlt, 0, 'f', minAltitude->decimals()) // .arg(QString::asprintf("%+d", score)); return score; } int16_t SchedulerJob::getMoonSeparationScore(QDateTime const &when) const { // FIXME: block calculating target coordinates at a particular time is duplicated in several places GeoLocation *geo = KStarsData::Instance()->geo(); // Retrieve the argument date/time, or fall back to current time - don't use QDateTime's timezone! KStarsDateTime ltWhen(when.isValid() ? Qt::UTC == when.timeSpec() ? geo->UTtoLT(KStarsDateTime(when)) : when : KStarsData::Instance()->lt()); // Create a sky object with the target catalog coordinates SkyPoint const target = getTargetCoords(); SkyObject o; o.setRA0(target.ra0()); o.setDec0(target.dec0()); // Update RA/DEC of the target for the current fraction of the day KSNumbers numbers(ltWhen.djd()); o.updateCoordsNow(&numbers); // Update moon //ut = geo->LTtoUT(ltWhen); //KSNumbers ksnum(ut.djd()); // BUG: possibly LT.djd() != UT.djd() because of translation //LST = geo->GSTtoLST(ut.gst()); CachingDms LST = geo->GSTtoLST(geo->LTtoUT(ltWhen).gst()); moon->updateCoords(&numbers, true, geo->lat(), &LST, true); double const moonAltitude = moon->alt().Degrees(); // Lunar illumination % double const illum = moon->illum() * 100.0; // Moon/Sky separation p double const separation = moon->angularDistanceTo(&o).Degrees(); // Zenith distance of the moon double const zMoon = (90 - moonAltitude); // Zenith distance of target double const zTarget = (90 - o.alt().Degrees()); int16_t score = 0; // If target = Moon, or no illuminiation, or moon below horizon, return static score. if (zMoon == zTarget || illum == 0 || zMoon >= 90) score = 100; else { // JM: Some magic voodoo formula I came up with! double moonEffect = (pow(separation, 1.7) * pow(zMoon, 0.5)) / (pow(zTarget, 1.1) * pow(illum, 0.5)); // Limit to 0 to 100 range. moonEffect = KSUtils::clamp(moonEffect, 0.0, 100.0); if (getMinMoonSeparation() > 0) { if (separation < getMinMoonSeparation()) score = BAD_SCORE * 5; else score = moonEffect; } else score = moonEffect; } // Limit to 0 to 20 score /= 5.0; //qCDebug(KSTARS_EKOS_SCHEDULER) << QString("Job '%1' target is %L3 degrees from Moon (score %2).") // .arg(getName()) // .arg(separation, 0, 'f', 3) // .arg(QString::asprintf("%+d", score)); return score; } double SchedulerJob::getCurrentMoonSeparation() const { // FIXME: block calculating target coordinates at a particular time is duplicated in several places GeoLocation *geo = KStarsData::Instance()->geo(); // Retrieve the argument date/time, or fall back to current time - don't use QDateTime's timezone! KStarsDateTime ltWhen(KStarsData::Instance()->lt()); // Create a sky object with the target catalog coordinates SkyPoint const target = getTargetCoords(); SkyObject o; o.setRA0(target.ra0()); o.setDec0(target.dec0()); // Update RA/DEC of the target for the current fraction of the day KSNumbers numbers(ltWhen.djd()); o.updateCoordsNow(&numbers); // Update moon //ut = geo->LTtoUT(ltWhen); //KSNumbers ksnum(ut.djd()); // BUG: possibly LT.djd() != UT.djd() because of translation //LST = geo->GSTtoLST(ut.gst()); CachingDms LST = geo->GSTtoLST(geo->LTtoUT(ltWhen).gst()); moon->updateCoords(&numbers, true, geo->lat(), &LST, true); // Moon/Sky separation p return moon->angularDistanceTo(&o).Degrees(); } QDateTime SchedulerJob::calculateAltitudeTime(QDateTime const &when) const { // FIXME: block calculating target coordinates at a particular time is duplicated in several places GeoLocation *geo = KStarsData::Instance()->geo(); // Retrieve the argument date/time, or fall back to current time - don't use QDateTime's timezone! KStarsDateTime ltWhen(when.isValid() ? Qt::UTC == when.timeSpec() ? geo->UTtoLT(KStarsDateTime(when)) : when : KStarsData::Instance()->lt()); // Create a sky object with the target catalog coordinates SkyPoint const target = getTargetCoords(); SkyObject o; o.setRA0(target.ra0()); o.setDec0(target.dec0()); // Calculate the UT at the argument time KStarsDateTime const ut = geo->LTtoUT(ltWhen); double const SETTING_ALTITUDE_CUTOFF = Options::settingAltitudeCutoff(); // Within the next 24 hours, search when the job target matches the altitude and moon constraints for (unsigned int minute = 0; minute < 24 * 60; minute++) { KStarsDateTime const ltOffset(ltWhen.addSecs(minute * 60)); // Update RA/DEC of the target for the current fraction of the day KSNumbers numbers(ltOffset.djd()); o.updateCoordsNow(&numbers); // Compute local sidereal time for the current fraction of the day, calculate altitude CachingDms const LST = geo->GSTtoLST(geo->LTtoUT(ltOffset).gst()); o.EquatorialToHorizontal(&LST, geo->lat()); double const altitude = o.alt().Degrees(); if (getMinAltitude() <= altitude) { // Don't test proximity to dawn in this situation, we only cater for altitude here // Continue searching if Moon separation is not good enough if (0 < getMinMoonSeparation() && getMoonSeparationScore(ltOffset) < 0) continue; // Continue searching if target is setting and under the cutoff double offset = LST.Hours() - o.ra().Hours(); if (24.0 <= offset) offset -= 24.0; else if (offset < 0.0) offset += 24.0; if (0.0 <= offset && offset < 12.0) if (altitude - SETTING_ALTITUDE_CUTOFF < getMinAltitude()) continue; return ltOffset; } } return QDateTime(); } QDateTime SchedulerJob::calculateCulmination(QDateTime const &when) const { // FIXME: culmination calculation is a min altitude requirement, should be an interval altitude requirement GeoLocation *geo = KStarsData::Instance()->geo(); // FIXME: block calculating target coordinates at a particular time is duplicated in calculateCulmination // Retrieve the argument date/time, or fall back to current time - don't use QDateTime's timezone! KStarsDateTime ltWhen(when.isValid() ? Qt::UTC == when.timeSpec() ? geo->UTtoLT(KStarsDateTime(when)) : when : KStarsData::Instance()->lt()); // Create a sky object with the target catalog coordinates SkyPoint const target = getTargetCoords(); SkyObject o; o.setRA0(target.ra0()); o.setDec0(target.dec0()); // Update RA/DEC for the argument date/time KSNumbers numbers(ltWhen.djd()); o.updateCoordsNow(&numbers); // Calculate transit date/time at the argument date - transitTime requires UT and returns LocalTime KStarsDateTime transitDateTime(ltWhen.date(), o.transitTime(geo->LTtoUT(ltWhen), geo), Qt::LocalTime); // Shift transit date/time by the argument offset KStarsDateTime observationDateTime = transitDateTime.addSecs(getCulminationOffset() * 60); // Relax observation time, culmination calculation is stable at minute only KStarsDateTime relaxedDateTime = observationDateTime.addSecs(Options::leadTime() * 60); // Verify resulting observation time is under lead time vs. argument time // If sooner, delay by 8 hours to get to the next transit - perhaps in a third call if (relaxedDateTime < ltWhen) { qCDebug(KSTARS_EKOS_SCHEDULER) << QString("Job '%1' startup %2 is posterior to transit %3, shifting by 8 hours.") .arg(getName()) .arg(ltWhen.toString(getDateTimeDisplayFormat())) .arg(relaxedDateTime.toString(getDateTimeDisplayFormat())); return calculateCulmination(when.addSecs(8 * 60 * 60)); } // Guarantees - culmination calculation is stable at minute level, so relax by lead time Q_ASSERT_X(observationDateTime.isValid(), __FUNCTION__, "Observation time for target culmination is valid."); Q_ASSERT_X(ltWhen <= relaxedDateTime, __FUNCTION__, "Observation time for target culmination is at or after than argument time"); // Return consolidated culmination time return Qt::UTC == observationDateTime.timeSpec() ? geo->UTtoLT(observationDateTime) : observationDateTime; } double SchedulerJob::findAltitude(const SkyPoint &target, const QDateTime &when, bool * is_setting, bool debug) { // FIXME: block calculating target coordinates at a particular time is duplicated in several places GeoLocation * const geo = KStarsData::Instance()->geo(); // Retrieve the argument date/time, or fall back to current time - don't use QDateTime's timezone! KStarsDateTime ltWhen(when.isValid() ? Qt::UTC == when.timeSpec() ? geo->UTtoLT(KStarsDateTime(when)) : when : KStarsData::Instance()->lt()); // Create a sky object with the target catalog coordinates SkyObject o; o.setRA0(target.ra0()); o.setDec0(target.dec0()); // Update RA/DEC of the target for the current fraction of the day KSNumbers numbers(ltWhen.djd()); o.updateCoordsNow(&numbers); // Calculate alt/az coordinates using KStars instance's geolocation CachingDms const LST = geo->GSTtoLST(geo->LTtoUT(ltWhen).gst()); o.EquatorialToHorizontal(&LST, geo->lat()); // Hours are reduced to [0,24[, meridian being at 0 double offset = LST.Hours() - o.ra().Hours(); if (24.0 <= offset) offset -= 24.0; else if (offset < 0.0) offset += 24.0; bool const passed_meridian = 0.0 <= offset && offset < 12.0; if (debug) qCDebug(KSTARS_EKOS_SCHEDULER) << QString("When:%9 LST:%8 RA:%1 RA0:%2 DEC:%3 DEC0:%4 alt:%5 setting:%6 HA:%7") .arg(o.ra().toHMSString()) .arg(o.ra0().toHMSString()) .arg(o.dec().toHMSString()) .arg(o.dec0().toHMSString()) .arg(o.alt().Degrees()) .arg(passed_meridian ? "yes" : "no") .arg(o.ra().Hours()) .arg(LST.toHMSString()) .arg(ltWhen.toString("HH:mm:ss")); if (is_setting) *is_setting = passed_meridian; return o.alt().Degrees(); } diff --git a/kstars/kstarsdata.cpp b/kstars/kstarsdata.cpp index 08ab27251..df1368195 100644 --- a/kstars/kstarsdata.cpp +++ b/kstars/kstarsdata.cpp @@ -1,1526 +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; 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"); fixcitydb.setDatabaseName(dbfile); fixcitydb.open(); 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" ) ); #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)) { 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)); } 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)); } 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()); QTime rtime = QTime(fields[6].leftRef(fields[6].indexOf(':')).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"; } 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" */) - o = skyComposite()->findByName(i18n(name.toLocal8Bit().data())); - else - o = skyComposite()->findByName(name); + + // if (name == "Mercury" || name == "Venus" || name == "Mars" || name == "Jupiter" || name == "Saturn" || + // name == "Uranus" || name == "Neptune" /* || name == "Pluto" */) + // o = skyComposite()->findByName(i18n(name.toLocal8Bit().data())); + // else + SkyObject *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); } } 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() << 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) { 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/printing/detailstable.cpp b/kstars/printing/detailstable.cpp index edc56d0b3..45bdc93d6 100644 --- a/kstars/printing/detailstable.cpp +++ b/kstars/printing/detailstable.cpp @@ -1,729 +1,729 @@ /*************************************************************************** detailstable.cpp - K Desktop Planetarium ------------------- begin : Fri Jul 29 2011 copyright : (C) 2011 by Rafał Kułaga email : rl.kulaga@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "detailstable.h" #include #include #include "kstars.h" #include "kstarsdata.h" #include "ksplanetbase.h" #include "starobject.h" #include "skymapcomposite.h" #include "constellationboundarylines.h" #include "deepskyobject.h" #include "catalogcomponent.h" #include "ksmoon.h" #include "ksasteroid.h" #include "kscomet.h" DetailsTable::DetailsTable() { m_Document = new QTextDocument(KStars::Instance()); setDefaultFormatting(); } DetailsTable::~DetailsTable() { if (m_Document) { delete m_Document; } } void DetailsTable::createGeneralTable(SkyObject *obj) { clearContents(); QTextCursor cursor = m_Document->rootFrame()->firstCursorPosition(); //Fill in the data fields //Contents depend on type of object StarObject *s = nullptr; DeepSkyObject *dso = nullptr; KSPlanetBase *ps = nullptr; QString pname, oname; QString objNamesVal, objTypeVal, objDistVal, objSizeVal, objMagVal, objBvVal, objIllumVal; QString objSizeLabel, objMagLabel; switch (obj->type()) { case SkyObject::STAR: { s = (StarObject *)obj; objNamesVal = s->longname(); if (s->getHDIndex() != 0) { if (!s->longname().isEmpty()) { objNamesVal = s->longname() + QString(", HD%1").arg(QString::number(s->getHDIndex())); } else { objNamesVal = QString(", HD%1").arg(QString::number(s->getHDIndex())); } } objTypeVal = s->sptype() + ' ' + i18n("star"); objMagVal = i18nc("number in magnitudes", "%1 mag", QLocale().toString(s->mag(), 1)); //show to tenths place if (s->getBVIndex() < 30.0) { objBvVal = QString::number(s->getBVIndex(), 'g', 2); } //distance if (s->distance() > 2000. || s->distance() < 0.) // parallax < 0.5 mas { objDistVal = i18nc("larger than 2000 parsecs", "> 2000 pc"); } else if (s->distance() > 50.0) //show to nearest integer { objDistVal = i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 0)); } else if (s->distance() > 10.0) //show to tenths place { objDistVal = i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 1)); } else //show to hundredths place { objDistVal = i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 2)); } //Note multiplicity/variability in angular size label if (s->isMultiple() && s->isVariable()) { objSizeLabel = i18nc("the star is a multiple star", "multiple") + ','; objSizeVal = i18nc("the star is a variable star", "variable"); } else if (s->isMultiple()) { objSizeLabel = i18nc("the star is a multiple star", "multiple"); } else if (s->isVariable()) { objSizeLabel = i18nc("the star is a variable star", "variable"); } objIllumVal = "--"; break; //End of stars case } case SkyObject::ASTEROID: //[fall through to planets] case SkyObject::COMET: //[fall through to planets] case SkyObject::MOON: //[fall through to planets] case SkyObject::PLANET: { - ps = (KSPlanetBase *)obj; + ps = dynamic_cast(obj); objNamesVal = ps->longname(); //Type is "G5 star" for Sun - if (ps->name() == "Sun") + if (ps->name() == i18n("Sun")) { objTypeVal = i18n("G5 star"); } - else if (ps->name() == "Moon") + else if (ps->name() == i18n("Moon")) { objTypeVal = ps->translatedName(); } else if (ps->name() == i18nc("Asteroid name (optional)", "Pluto") || ps->name() == i18nc("Asteroid name (optional)", "Ceres") || ps->name() == i18nc("Asteroid name (optional)", "Eris")) // TODO: Check if Ceres / Eris have translations and i18n() them { objTypeVal = i18n("Dwarf planet"); } else { objTypeVal = ps->typeName(); } //Magnitude: The moon displays illumination fraction instead - if (obj->name() == "Moon") + if (obj->name() == i18n("Moon")) { - objIllumVal = QString("%1 %").arg(QLocale().toString(((KSMoon *)obj)->illum() * 100., 0)); + objIllumVal = QString("%1 %").arg(QLocale().toString((dynamic_cast(obj))->illum() * 100., 0)); } objMagVal = i18nc("number in magnitudes", "%1 mag", QLocale().toString(ps->mag(), 1)); //show to tenths place //Distance from Earth. The moon requires a unit conversion - if (ps->name() == "Moon") + if (ps->name() == i18n("Moon")) { objDistVal = i18nc("distance in kilometers", "%1 km", QLocale().toString(ps->rearth() * AU_KM)); } else { objDistVal = i18nc("distance in Astronomical Units", "%1 AU", QLocale().toString(ps->rearth())); } //Angular size; moon and sun in arcmin, others in arcsec if (ps->angSize()) { - if (ps->name() == "Sun" || ps->name() == "Moon") + if (ps->name() == i18n("Sun") || ps->name() == i18n("Moon")) { // Needn't be a plural form because sun / moon will never contract to 1 arcminute objSizeVal = i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(ps->angSize())); } else { objSizeVal = i18nc("angular size in arcseconds", "%1 arcsec", QLocale().toString(ps->angSize() * 60.0)); } } else { objSizeVal = "--"; } break; //End of planets/comets/asteroids case } default: //Deep-sky objects { dso = (DeepSkyObject *)obj; //Show all names recorded for the object if (!dso->longname().isEmpty() && dso->longname() != dso->name()) { pname = dso->translatedLongName(); oname = dso->translatedName(); } else { pname = dso->translatedName(); } if (!dso->translatedName2().isEmpty()) { if (oname.isEmpty()) { oname = dso->translatedName2(); } else { oname += ", " + dso->translatedName2(); } } if (dso->ugc() != 0) { if (!oname.isEmpty()) { oname += ", "; } oname += "UGC " + QString::number(dso->ugc()); } if (dso->pgc() != 0) { if (!oname.isEmpty()) { oname += ", "; } oname += "PGC " + QString::number(dso->pgc()); } if (!oname.isEmpty()) { pname += ", " + oname; } objNamesVal = pname; objTypeVal = dso->typeName(); if (dso->type() == SkyObject::RADIO_SOURCE) { Q_ASSERT(dso->customCatalog()); objMagLabel = i18nc("integrated flux at a frequency", "Flux(%1):", dso->customCatalog()->fluxFrequency()); objMagVal = i18nc("integrated flux value", "%1 %2", QLocale().toString(dso->flux(), 1), dso->customCatalog()->fluxUnit()); //show to tenths place } else if (dso->mag() > 90.0) { objMagVal = "--"; } else { objMagVal = i18nc("number in magnitudes", "%1 mag", QLocale().toString(dso->mag(), 1)); //show to tenths place } //No distances at this point... objDistVal = "--"; //Only show decimal place for small angular sizes if (dso->a() > 10.0) { objSizeVal = i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 0)); } else if (dso->a()) { objSizeVal = i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 1)); } else { objSizeVal = "--"; } break; //End of deep-space objects case } } //Common to all types: if (obj->type() == SkyObject::CONSTELLATION) { objTypeVal = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(obj); } else { objTypeVal = i18nc("%1 type of sky object (planet, asteroid etc), %2 name of a constellation", "%1 in %2", objTypeVal, KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(obj)); } QVector constraints; constraints << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25); m_TableFormat.setColumnWidthConstraints(constraints); QTextTable *table = cursor.insertTable(5, 4, m_TableFormat); table->mergeCells(0, 0, 1, 4); QTextBlockFormat centered; centered.setAlignment(Qt::AlignCenter); table->cellAt(0, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(0, 0).firstCursorPosition().insertText(i18n("General"), m_TableTitleCharFormat); table->mergeCells(1, 1, 1, 3); table->cellAt(1, 0).firstCursorPosition().insertText(i18n("Names:"), m_ItemNameCharFormat); table->cellAt(1, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(1, 1).firstCursorPosition().insertText(objNamesVal, m_ItemValueCharFormat); table->cellAt(2, 0).firstCursorPosition().insertText(i18n("Type:"), m_ItemNameCharFormat); table->cellAt(2, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(2, 1).firstCursorPosition().insertText(objTypeVal, m_ItemValueCharFormat); table->cellAt(3, 0).firstCursorPosition().insertText(i18n("Distance:"), m_ItemNameCharFormat); table->cellAt(3, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(3, 1).firstCursorPosition().insertText(objDistVal, m_ItemValueCharFormat); table->cellAt(4, 0).firstCursorPosition().insertText(i18n("Size:"), m_ItemNameCharFormat); table->cellAt(4, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(4, 1).firstCursorPosition().insertText(objSizeVal, m_ItemValueCharFormat); table->cellAt(2, 2).firstCursorPosition().insertText(i18n("Magnitude:"), m_ItemNameCharFormat); table->cellAt(2, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(2, 3).firstCursorPosition().insertText(objMagVal, m_ItemValueCharFormat); table->cellAt(3, 2).firstCursorPosition().insertText(i18n("B-V index:"), m_ItemNameCharFormat); table->cellAt(3, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(3, 3).firstCursorPosition().insertText(objBvVal, m_ItemValueCharFormat); table->cellAt(4, 2).firstCursorPosition().insertText(i18n("Illumination:"), m_ItemNameCharFormat); table->cellAt(4, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(4, 3).firstCursorPosition().insertText(objIllumVal, m_ItemValueCharFormat); } void DetailsTable::createAsteroidCometTable(SkyObject *obj) { clearContents(); QTextCursor cursor = m_Document->rootFrame()->firstCursorPosition(); QString perihelionVal, orbitIdVal, neoVal, diamVal, rotPeriodVal, moidVal; QString orbitClassVal, albedoVal, dimVal, periodVal; // Add specifics data switch (obj->type()) { case SkyObject::ASTEROID: { KSAsteroid *ast = (KSAsteroid *)obj; // Perihelion perihelionVal = QString::number(ast->getPerihelion()) + " AU"; // Earth MOID moidVal = ast->getEarthMOID() == 0 ? QString("--") : QString::number(ast->getEarthMOID()) + QString(" AU"); // Orbit ID orbitIdVal = ast->getOrbitID(); // Orbit Class orbitClassVal = ast->getOrbitClass(); // NEO neoVal = ast->isNEO() ? i18n("Yes") : i18n("No"); // Albedo albedoVal = ast->getAlbedo() == 0 ? QString("--") : QString::number(ast->getAlbedo()); // Diameter diamVal = ast->getDiameter() == 0 ? QString("--") : QString::number(ast->getDiameter()) + QString(" km"); // Dimensions dimVal = ast->getDimensions().isEmpty() ? QString("--") : ast->getDimensions() + QString(" km"); // Rotation period rotPeriodVal = ast->getRotationPeriod() == 0 ? QString("--") : - QString::number(ast->getRotationPeriod()) + QString(" h"); + QString::number(ast->getRotationPeriod()) + QString(" h"); // Period periodVal = ast->getPeriod() == 0 ? QString("--") : QString::number(ast->getPeriod()) + QString(" y"); break; } case SkyObject::COMET: { KSComet *com = (KSComet *)obj; // Perihelion perihelionVal = QString::number(com->getPerihelion()) + " AU"; // Earth MOID moidVal = com->getEarthMOID() == 0 ? QString("--") : QString::number(com->getEarthMOID()) + QString(" AU"); // Orbit ID orbitIdVal = com->getOrbitID(); // Orbit Class orbitClassVal = com->getOrbitClass(); // NEO neoVal = com->isNEO() ? i18n("Yes") : i18n("No"); // Albedo albedoVal = com->getAlbedo() == 0 ? QString("--") : QString::number(com->getAlbedo()); // Diameter diamVal = com->getDiameter() == 0 ? QString("--") : QString::number(com->getDiameter()) + QString(" km"); // Dimensions dimVal = com->getDimensions().isEmpty() ? QString("--") : com->getDimensions() + QString(" km"); // Rotation period rotPeriodVal = com->getRotationPeriod() == 0 ? QString("--") : - QString::number(com->getRotationPeriod()) + QString(" h"); + QString::number(com->getRotationPeriod()) + QString(" h"); // Period periodVal = com->getPeriod() == 0 ? QString("--") : QString::number(com->getPeriod()) + QString(" y"); break; } default: { return; } } // Set column width constraints QVector constraints; constraints << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25); m_TableFormat.setColumnWidthConstraints(constraints); QTextTable *table = cursor.insertTable(6, 4, m_TableFormat); table->mergeCells(0, 0, 1, 4); QTextBlockFormat centered; centered.setAlignment(Qt::AlignCenter); table->cellAt(0, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(0, 0).firstCursorPosition().insertText(i18n("Asteroid/Comet details"), m_TableTitleCharFormat); table->cellAt(1, 0).firstCursorPosition().insertText(i18n("Perihelion:"), m_ItemNameCharFormat); table->cellAt(1, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(1, 1).firstCursorPosition().insertText(perihelionVal, m_ItemValueCharFormat); table->cellAt(2, 0).firstCursorPosition().insertText(i18n("Orbit ID:"), m_ItemNameCharFormat); table->cellAt(2, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(2, 1).firstCursorPosition().insertText(orbitIdVal, m_ItemValueCharFormat); table->cellAt(3, 0).firstCursorPosition().insertText(i18n("NEO:"), m_ItemNameCharFormat); table->cellAt(3, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(3, 1).firstCursorPosition().insertText(neoVal, m_ItemValueCharFormat); table->cellAt(4, 0).firstCursorPosition().insertText(i18n("Diameter:"), m_ItemNameCharFormat); table->cellAt(4, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(4, 1).firstCursorPosition().insertText(diamVal, m_ItemValueCharFormat); table->cellAt(5, 0).firstCursorPosition().insertText(i18n("Rotation period:"), m_ItemNameCharFormat); table->cellAt(5, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(5, 1).firstCursorPosition().insertText(rotPeriodVal, m_ItemValueCharFormat); table->cellAt(1, 2).firstCursorPosition().insertText(i18n("Earth MOID:"), m_ItemNameCharFormat); table->cellAt(1, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(1, 3).firstCursorPosition().insertText(moidVal, m_ItemValueCharFormat); table->cellAt(2, 2).firstCursorPosition().insertText(i18n("Orbit class:"), m_ItemNameCharFormat); table->cellAt(2, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(2, 3).firstCursorPosition().insertText(orbitClassVal, m_ItemValueCharFormat); table->cellAt(3, 2).firstCursorPosition().insertText(i18n("Albedo:"), m_ItemNameCharFormat); table->cellAt(3, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(3, 3).firstCursorPosition().insertText(albedoVal, m_ItemValueCharFormat); table->cellAt(4, 2).firstCursorPosition().insertText(i18n("Dimensions:"), m_ItemNameCharFormat); table->cellAt(4, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(4, 3).firstCursorPosition().insertText(dimVal, m_ItemValueCharFormat); table->cellAt(5, 2).firstCursorPosition().insertText(i18n("Period:"), m_ItemNameCharFormat); table->cellAt(5, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(5, 3).firstCursorPosition().insertText(periodVal, m_ItemValueCharFormat); } void DetailsTable::createCoordinatesTable(SkyObject *obj, const KStarsDateTime &ut, GeoLocation *geo) { clearContents(); QTextCursor cursor = m_Document->rootFrame()->firstCursorPosition(); // Set column width constraints QVector constraints; constraints << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25); m_TableFormat.setColumnWidthConstraints(constraints); // Insert table & row containing table name QTextTable *table = cursor.insertTable(4, 4, m_TableFormat); table->mergeCells(0, 0, 1, 4); QTextBlockFormat centered; centered.setAlignment(Qt::AlignCenter); table->cellAt(0, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(0, 0).firstCursorPosition().insertText(i18n("Coordinates"), m_TableTitleCharFormat); //Coordinates Section: //Don't use KLocale::formatNumber() for the epoch string, //because we don't want a thousands-place separator! QString sEpoch = QString::number(ut.epoch(), 'f', 1); //Replace the decimal point with localized decimal symbol sEpoch.replace('.', QLocale().decimalPoint()); table->cellAt(1, 0).firstCursorPosition().insertText(i18n("RA (%1):", sEpoch), m_ItemNameCharFormat); table->cellAt(1, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(1, 1).firstCursorPosition().insertText(obj->ra().toHMSString(), m_ItemValueCharFormat); table->cellAt(2, 0).firstCursorPosition().insertText(i18n("Dec (%1):", sEpoch), m_ItemNameCharFormat); table->cellAt(2, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(2, 1).firstCursorPosition().insertText(obj->dec().toDMSString(), m_ItemValueCharFormat); table->cellAt(3, 0).firstCursorPosition().insertText(i18n("Hour angle:"), m_ItemNameCharFormat); table->cellAt(3, 0).firstCursorPosition().setBlockFormat(centered); //Hour Angle can be negative, but dms HMS expressions cannot. //Here's a kludgy workaround: dms lst = geo->GSTtoLST(ut.gst()); dms ha(lst.Degrees() - obj->ra().Degrees()); QChar sgn('+'); if (ha.Hours() > 12.0) { ha.setH(24.0 - ha.Hours()); sgn = '-'; } table->cellAt(3, 1).firstCursorPosition().insertText(QString("%1%2").arg(sgn).arg(ha.toHMSString()), - m_ItemValueCharFormat); + m_ItemValueCharFormat); table->cellAt(1, 2).firstCursorPosition().insertText(i18n("Azimuth:"), m_ItemNameCharFormat); table->cellAt(1, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(1, 3).firstCursorPosition().insertText(obj->az().toDMSString(), m_ItemValueCharFormat); table->cellAt(2, 2).firstCursorPosition().insertText(i18n("Altitude:"), m_ItemNameCharFormat); table->cellAt(2, 2).firstCursorPosition().setBlockFormat(centered); dms a; if (Options::useAltAz()) { a = obj->alt(); } else { a = obj->altRefracted(); } table->cellAt(2, 3).firstCursorPosition().insertText(a.toDMSString(), m_ItemValueCharFormat); table->cellAt(3, 2).firstCursorPosition().insertText(i18n("Airmass:"), m_ItemNameCharFormat); table->cellAt(3, 2).firstCursorPosition().setBlockFormat(centered); //Airmass is approximated as the secant of the zenith distance, //equivalent to 1./sin(Alt). Beware of Inf at Alt=0! QString aMassStr; if (obj->alt().Degrees() > 0.0) { aMassStr = QLocale().toString(1. / sin(obj->alt().radians()), 2); } else { aMassStr = "--"; } table->cellAt(3, 3).firstCursorPosition().insertText(aMassStr, m_ItemValueCharFormat); // Restore the position and other time-dependent parameters obj->recomputeCoords(ut, geo); } void DetailsTable::createRSTTAble(SkyObject *obj, const KStarsDateTime &ut, GeoLocation *geo) { clearContents(); QTextCursor cursor = m_Document->rootFrame()->firstCursorPosition(); QString rtValue, stValue; // Rise/Set time values QString azRValue, azSValue; // Rise/Set azimuth values //Prepare time/position variables QTime rt = obj->riseSetTime(ut, geo, true); //true = use rise time dms raz = obj->riseSetTimeAz(ut, geo, true); //true = use rise time //If transit time is before rise time, use transit time for tomorrow QTime tt = obj->transitTime(ut, geo); dms talt = obj->transitAltitude(ut, geo); if (tt < rt) { tt = obj->transitTime(ut.addDays(1), geo); talt = obj->transitAltitude(ut.addDays(1), geo); } //If set time is before rise time, use set time for tomorrow QTime st = obj->riseSetTime(ut, geo, false); //false = use set time dms saz = obj->riseSetTimeAz(ut, geo, false); //false = use set time if (st < rt) { st = obj->riseSetTime(ut.addDays(1), geo, false); //false = use set time saz = obj->riseSetTimeAz(ut.addDays(1), geo, false); //false = use set time } if (rt.isValid()) { rtValue = QString().sprintf("%02d:%02d", rt.hour(), rt.minute()); stValue = QString().sprintf("%02d:%02d", st.hour(), st.minute()); azRValue = raz.toDMSString(); azSValue = saz.toDMSString(); } else { if (obj->alt().Degrees() > 0.0) { rtValue = i18n("Circumpolar"); stValue = i18n("Circumpolar"); } else { rtValue = i18n("Never rises"); stValue = i18n("Never rises"); } azRValue = i18nc("Not Applicable", "N/A"); azSValue = i18nc("Not Applicable", "N/A"); } // Set column width constraints QVector constraints; constraints << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25) << QTextLength(QTextLength::PercentageLength, 25); m_TableFormat.setColumnWidthConstraints(constraints); // Insert table & row containing table name QTextTable *table = cursor.insertTable(4, 4, m_TableFormat); table->mergeCells(0, 0, 1, 4); QTextBlockFormat centered; centered.setAlignment(Qt::AlignCenter); table->cellAt(0, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(0, 0).firstCursorPosition().insertText(i18n("Rise/Set/Transit"), m_TableTitleCharFormat); // Insert cell names & values table->cellAt(1, 0).firstCursorPosition().insertText(i18n("Rise time:"), m_ItemNameCharFormat); table->cellAt(1, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(1, 1).firstCursorPosition().insertText(rtValue, m_ItemValueCharFormat); table->cellAt(2, 0).firstCursorPosition().insertText(i18n("Transit time:"), m_ItemNameCharFormat); table->cellAt(2, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(2, 1).firstCursorPosition().insertText(QString().sprintf("%02d:%02d", tt.hour(), tt.minute()), - m_ItemValueCharFormat); + m_ItemValueCharFormat); table->cellAt(3, 0).firstCursorPosition().insertText(i18n("Set time:"), m_ItemNameCharFormat); table->cellAt(3, 0).firstCursorPosition().setBlockFormat(centered); table->cellAt(3, 1).firstCursorPosition().insertText(stValue, m_ItemValueCharFormat); table->cellAt(1, 2).firstCursorPosition().insertText(i18n("Azimuth at rise:"), m_ItemNameCharFormat); table->cellAt(1, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(1, 3).firstCursorPosition().insertText(azRValue, m_ItemValueCharFormat); table->cellAt(2, 2).firstCursorPosition().insertText(i18n("Altitude at transit:"), m_ItemNameCharFormat); table->cellAt(2, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(2, 3).firstCursorPosition().insertText(talt.toDMSString(), m_ItemValueCharFormat); table->cellAt(3, 2).firstCursorPosition().insertText(i18n("Azimuth at set:"), m_ItemNameCharFormat); table->cellAt(3, 2).firstCursorPosition().setBlockFormat(centered); table->cellAt(3, 3).firstCursorPosition().insertText(azSValue, m_ItemValueCharFormat); // Restore the position and other time-dependent parameters obj->recomputeCoords(ut, geo); } void DetailsTable::clearContents() { m_Document->clear(); } void DetailsTable::setDefaultFormatting() { // Set default table format m_TableFormat.setAlignment(Qt::AlignCenter); m_TableFormat.setBorder(4); m_TableFormat.setCellPadding(2); m_TableFormat.setCellSpacing(2); // Set default table title character format m_TableTitleCharFormat.setFont(QFont("Times", 12, QFont::Bold)); m_TableTitleCharFormat.setFontCapitalization(QFont::Capitalize); // Set default table item name character format m_ItemNameCharFormat.setFont(QFont("Times", 10, QFont::Bold)); // Set default table item value character format m_ItemValueCharFormat.setFont(QFont("Times", 10)); } diff --git a/kstars/printing/pwizobjectselection.cpp b/kstars/printing/pwizobjectselection.cpp index 2b2a80fce..29b5c4ef0 100644 --- a/kstars/printing/pwizobjectselection.cpp +++ b/kstars/printing/pwizobjectselection.cpp @@ -1,216 +1,216 @@ /*************************************************************************** pwizobjectselection.cpp - K Desktop Planetarium ------------------- begin : Wed Aug 3 2011 copyright : (C) 2011 by Rafał Kułaga email : rl.kulaga@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "pwizobjectselection.h" #include "printingwizard.h" #include "kstars.h" #include "skymap.h" #include "dialogs/finddialog.h" #include "dialogs/detaildialog.h" #include "starobject.h" #include "ksplanetbase.h" #include "deepskyobject.h" #include #include PWizObjectSelectionUI::PWizObjectSelectionUI(PrintingWizard *wizard, QWidget *parent) : QFrame(parent), m_ParentWizard(wizard) { setupUi(this); detailsButton->setVisible(false); selectedObjLabel->setVisible(false); objInfoLabel->setVisible(false); connect(fromListButton, SIGNAL(clicked()), this, SLOT(slotSelectFromList())); connect(pointButton, SIGNAL(clicked()), this, SLOT(slotPointObject())); connect(detailsButton, SIGNAL(clicked()), this, SLOT(slotShowDetails())); } void PWizObjectSelectionUI::setSkyObject(SkyObject *obj) { m_ParentWizard->setSkyObject(obj); m_ParentWizard->updateStepButtons(); QString infoStr = objectInfoString(obj); objInfoLabel->setText(infoStr); detailsButton->setVisible(true); selectedObjLabel->setVisible(true); objInfoLabel->setVisible(true); } void PWizObjectSelectionUI::slotSelectFromList() { if (FindDialog::Instance()->exec() == QDialog::Accepted) { SkyObject *obj = FindDialog::Instance()->targetObject(); if (obj) { setSkyObject(obj); m_ParentWizard->updateStepButtons(); } } } void PWizObjectSelectionUI::slotPointObject() { m_ParentWizard->beginPointing(); } void PWizObjectSelectionUI::slotShowDetails() { if (m_ParentWizard->getSkyObject()) { QPointer detailDlg(new DetailDialog( - m_ParentWizard->getSkyObject(), KStars::Instance()->data()->ut(), KStars::Instance()->data()->geo(), this)); + m_ParentWizard->getSkyObject(), KStars::Instance()->data()->ut(), KStars::Instance()->data()->geo(), this)); detailDlg->exec(); delete detailDlg; } } QString PWizObjectSelectionUI::objectInfoString(SkyObject *obj) { QString retVal; switch (obj->type()) { case SkyObject::STAR: { StarObject *s = (StarObject *)obj; retVal = s->longname(); if (s->getHDIndex() != 0) { if (!s->longname().isEmpty()) { retVal += QString(" ● HD%1").arg(QString::number(s->getHDIndex())); } else { retVal += QString(" ● HD%1").arg(QString::number(s->getHDIndex())); } } retVal += " ● " + s->sptype() + ' ' + i18n("star"); retVal += " ● " + i18nc("number in magnitudes", "%1 mag", QLocale().toString(s->mag(), 1)); break; } case SkyObject::ASTEROID: //[fall through to planets] case SkyObject::COMET: //[fall through to planets] case SkyObject::MOON: //[fall through to planets] case SkyObject::PLANET: { KSPlanetBase *ps = (KSPlanetBase *)obj; retVal = ps->longname(); //Type is "G5 star" for Sun QString type; - if (ps->name() == "Sun") + if (ps->name() == i18n("Sun")) { type = i18n("G5 star"); } - else if (ps->name() == "Moon") + else if (ps->name() == i18n("Moon")) { type = ps->translatedName(); } else if (ps->name() == i18nc("Asteroid name (optional)", "Pluto") || ps->name() == i18nc("Asteroid name (optional)", "Ceres") || ps->name() == i18nc("Asteroid name (optional)", "Eris")) { type = i18n("Dwarf planet"); } else { type = ps->typeName(); } retVal += " ● " + type; retVal += " ● " + i18nc("number in magnitudes", "%1 mag", QLocale().toString(ps->mag(), 1)); break; } default: // deep-sky object { DeepSkyObject *dso = (DeepSkyObject *)obj; QString oname, pname; //Show all names recorded for the object if (!dso->longname().isEmpty() && dso->longname() != dso->name()) { pname = dso->translatedLongName(); oname = dso->translatedName(); } else { pname = dso->translatedName(); } if (!dso->translatedName2().isEmpty()) { if (oname.isEmpty()) { oname = dso->translatedName2(); } else { oname += " ● " + dso->translatedName2(); } } if (dso->ugc()) { if (!oname.isEmpty()) { oname += " ● "; } oname += "UGC " + QString::number(dso->ugc()); } if (dso->pgc()) { if (!oname.isEmpty()) { oname += " ● "; } oname += "PGC " + QString::number(dso->pgc()); } if (!oname.isEmpty()) pname += " ● " + oname; retVal = pname; retVal += " ● " + dso->typeName(); break; } } return retVal; } diff --git a/kstars/skycomponents/milkyway.cpp b/kstars/skycomponents/milkyway.cpp index d16d66547..0dd4f309a 100644 --- a/kstars/skycomponents/milkyway.cpp +++ b/kstars/skycomponents/milkyway.cpp @@ -1,140 +1,137 @@ /*************************************************************************** milkyway.cpp - K Desktop Planetarium ------------------- begin : 12 Nov. 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "milkyway.h" #include "ksfilereader.h" #include "kstarsdata.h" #ifdef KSTARS_LITE #include "skymaplite.h" #else #include "skymap.h" #endif #include "Options.h" #include "skypainter.h" #include "skycomponents/skiphashlist.h" #include MilkyWay::MilkyWay(SkyComposite *parent) : LineListIndex(parent, i18n("Milky Way")) { intro(); // Milky way //loadContours("milkyway.dat", i18n("Loading Milky Way")); // Magellanic clouds //loadContours("lmc.dat", i18n("Loading Large Magellanic Clouds")); //loadContours("smc.dat", i18n("Loading Small Magellanic Clouds")); //summary(); QtConcurrent::run(this, &MilkyWay::loadContours, QString("milkyway.dat"), i18n("Loading Milky Way")); QtConcurrent::run(this, &MilkyWay::loadContours, QString("lmc.dat"), i18n("Loading Large Magellanic Clouds")); QtConcurrent::run(this, &MilkyWay::loadContours, QString("smc.dat"), i18n("Loading Small Magellanic Clouds")); } const IndexHash &MilkyWay::getIndexHash(LineList *lineList) { - // FIXME: EVIL! - SkipHashList *skipList = (SkipHashList *)lineList; + SkipHashList *skipList = dynamic_cast(lineList); return skyMesh()->indexLine(skipList->points(), skipList->skipHash()); } SkipHashList *MilkyWay::skipList(LineList *lineList) { - // FIXME: EVIL! - SkipHashList *skipList = (SkipHashList *)lineList; - return skipList; + return dynamic_cast(lineList); } bool MilkyWay::selected() { #ifndef KSTARS_LITE return Options::showMilkyWay() && !(Options::hideOnSlew() && Options::hideMilkyWay() && SkyMap::IsSlewing()); #else return Options::showMilkyWay() && !(Options::hideOnSlew() && Options::hideMilkyWay() && SkyMapLite::IsSlewing()); #endif } void MilkyWay::draw(SkyPainter *skyp) { if (!selected()) return; QColor color = KStarsData::Instance()->colorScheme()->colorNamed("MWColor"); skyp->setPen(QPen(color, 3, Qt::SolidLine)); skyp->setBrush(QBrush(color)); if (Options::fillMilkyWay()) { drawFilled(skyp); } else { drawLines(skyp); } } void MilkyWay::loadContours(QString fname, QString greeting) { KSFileReader fileReader; std::shared_ptr skipList; int iSkip = 0; if (!fileReader.open(fname)) return; fileReader.setProgress(greeting, 2136, 5); while (fileReader.hasMoreLines()) { QString line = fileReader.readLine(); QChar firstChar = line.at(0); fileReader.showProgress(); if (firstChar == '#') continue; bool okRA = false, okDec = false; double ra = line.midRef(2, 8).toDouble(&okRA); double dec = line.midRef(11, 8).toDouble(&okDec); if (!okRA || !okDec) { qDebug() << QString("%1: conversion error on line: %2\n").arg(fname).arg(fileReader.lineNumber()); continue; } if (firstChar == 'M') { if (skipList.get()) appendBoth(skipList); skipList.reset(); iSkip = 0; } if (!skipList.get()) skipList.reset(new SkipHashList()); std::shared_ptr point(new SkyPoint(ra, dec)); skipList->append(std::move(point)); if (firstChar == 'S') static_cast(skipList.get())->setSkip(iSkip); iSkip++; } if (skipList.get()) appendBoth(skipList); } diff --git a/kstars/skycomponents/planetmoonscomponent.cpp b/kstars/skycomponents/planetmoonscomponent.cpp index e78e5e8d4..85c20c57f 100644 --- a/kstars/skycomponents/planetmoonscomponent.cpp +++ b/kstars/skycomponents/planetmoonscomponent.cpp @@ -1,170 +1,171 @@ /*************************************************************************** planetmoonscomponent.cpp - K Desktop Planetarium ------------------- begin : Sat Mar 13 2009 copyright : (C) 2009 by Vipul Kumar Singh, Médéric Boquien email : vipulkrsingh@gmail.com, mboquien@free.fr ***************************************************************************/ /*************************************************************************** * * * 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 "planetmoonscomponent.h" #include "kstarsdata.h" #include "Options.h" #include "skylabeler.h" #include "skypainter.h" +#include "kssun.h" #include "solarsystemcomposite.h" #include "solarsystemsinglecomponent.h" #include "projections/projector.h" #include "skyobjects/jupitermoons.h" PlanetMoonsComponent::PlanetMoonsComponent(SkyComposite *p, SolarSystemSingleComponent *planetComponent, - KSPlanetBase::Planets& _planet) + KSPlanetBase::Planets &_planet) : SkyComponent(p), planet(_planet), m_Planet(planetComponent) { /* if (planet == KSPlanetBase::JUPITER) pmoons = new JupiterMoons(); else pmoons = new SaturnMoons(); */ Q_ASSERT(planet == KSPlanetBase::JUPITER); -// delete pmoons; + // delete pmoons; // pmoons = new JupiterMoons(); -// int nmoons = pmoons->nMoons(); + // int nmoons = pmoons->nMoons(); -// for (int i = 0; i < nmoons; ++i) -// { - // objectNames(SkyObject::MOON).append( pmoons->name(i) ); - // objectLists(SkyObject::MOON).append( QPair(pmoons->name(i),pmoons->moon(i)) ); -// } + // for (int i = 0; i < nmoons; ++i) + // { + // objectNames(SkyObject::MOON).append( pmoons->name(i) ); + // objectLists(SkyObject::MOON).append( QPair(pmoons->name(i),pmoons->moon(i)) ); + // } } bool PlanetMoonsComponent::selected() { return m_Planet->selected(); } #ifndef KSTARS_LITE void PlanetMoonsComponent::update(KSNumbers *) { KStarsData *data = KStarsData::Instance(); if (selected()) pmoons->EquatorialToHorizontal(data->lst(), data->geo()->lat()); } #endif void PlanetMoonsComponent::updateMoons(KSNumbers *num) { //FIXME: evil cast if (selected()) - pmoons->findPosition(num, m_Planet->planet(), (KSSun *)(parent()->findByName("Sun"))); + pmoons->findPosition(num, m_Planet->planet(), dynamic_cast(parent()->findByName(i18n("Sun")))); } SkyObject *PlanetMoonsComponent::findByName(const QString &name) { int nmoons = pmoons->nMoons(); for (int i = 0; i < nmoons; ++i) { TrailObject *moon = pmoons->moon(i); if (QString::compare(moon->name(), name, Qt::CaseInsensitive) == 0 || - QString::compare(moon->longname(), name, Qt::CaseInsensitive) == 0 || - QString::compare(moon->name2(), name, Qt::CaseInsensitive) == 0) + QString::compare(moon->longname(), name, Qt::CaseInsensitive) == 0 || + QString::compare(moon->name2(), name, Qt::CaseInsensitive) == 0) return moon; } return nullptr; } #ifdef KSTARS_LITE KSPlanetBase *PlanetMoonsComponent::getPlanet() const { return m_Planet->planet(); } #endif SkyObject *PlanetMoonsComponent::objectNearest(SkyPoint *p, double &maxrad) { SkyObject *oBest = nullptr; int nmoons = pmoons->nMoons(); if (Options::zoomFactor() < 3000) return nullptr; for (int i = 0; i < nmoons; ++i) { SkyObject *moon = pmoons->moon(i); double r = moon->angularDistanceTo(p).Degrees(); if (r < maxrad) { maxrad = r; oBest = moon; } } return oBest; } void PlanetMoonsComponent::draw(SkyPainter *skyp) { if (!(planet == KSPlanetBase::JUPITER && Options::showJupiter())) return; //In order to get the z-order right for the moons and the planet, //we need to first draw the moons that are further away than the planet, //then re-draw the planet, then draw the moons nearer than the planet. QList frontMoons; int nmoons = pmoons->nMoons(); for (int i = 0; i < nmoons; ++i) { if (pmoons->z(i) < 0.0) //Moon is nearer than the planet { frontMoons.append(pmoons->moon(i)); } else { //Draw Moons that are further than the planet skyp->drawPointSource(pmoons->moon(i), pmoons->moon(i)->mag()); } } //Now redraw the planet m_Planet->draw(skyp); //Now draw the remaining moons, as stored in frontMoons foreach (TrailObject *moon, frontMoons) { skyp->drawPointSource(moon, moon->mag()); } //Draw Moon name labels if at high zoom if (!(Options::showPlanetNames() && Options::zoomFactor() > 50. * MINZOOM)) return; for (int i = 0; i < nmoons; ++i) { /* if (planet ==KSPlanetBase::SATURN) SkyLabeler::AddLabel( pmoons->moon(i), SkyLabeler::SATURN_MOON_LABEL ); else */ SkyLabeler::AddLabel(pmoons->moon(i), SkyLabeler::JUPITER_MOON_LABEL); } } void PlanetMoonsComponent::drawTrails(SkyPainter *skyp) { if (!selected()) return; int nmoons = pmoons->nMoons(); for (int i = 0; i < nmoons; ++i) pmoons->moon(i)->drawTrail(skyp); } diff --git a/kstars/skycomponents/skymapcomposite.cpp b/kstars/skycomponents/skymapcomposite.cpp index 067595bae..7d0274a0b 100644 --- a/kstars/skycomponents/skymapcomposite.cpp +++ b/kstars/skycomponents/skymapcomposite.cpp @@ -1,925 +1,925 @@ /*************************************************************************** skymapcomposite.cpp - K Desktop Planetarium ------------------- begin : 2005/07/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@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. * * * ***************************************************************************/ #include "skymapcomposite.h" #include "artificialhorizoncomponent.h" #include "catalogcomponent.h" #include "constellationartcomponent.h" #include "constellationboundarylines.h" #include "constellationlines.h" #include "constellationnamescomponent.h" #include "culturelist.h" #include "deepskycomponent.h" #include "deepstarcomponent.h" #include "ecliptic.h" #include "equator.h" #include "equatorialcoordinategrid.h" #include "horizoncomponent.h" #include "horizontalcoordinategrid.h" #include "localmeridiancomponent.h" #include "ksasteroid.h" #include "kscomet.h" #ifndef KSTARS_LITE #include "kstars.h" #endif #include "kstarsdata.h" #include "milkyway.h" #include "satellitescomponent.h" #include "skylabeler.h" #include "skypainter.h" #include "solarsystemcomposite.h" #include "starcomponent.h" #include "supernovaecomponent.h" #include "syncedcatalogcomponent.h" #include "targetlistcomponent.h" #include "projections/projector.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/ksplanet.h" #include "skyobjects/constellationsart.h" #ifndef KSTARS_LITE #include "flagcomponent.h" #include "ksutils.h" #include "observinglist.h" #include "skymap.h" #include "hipscomponent.h" #endif #include #include SkyMapComposite::SkyMapComposite(SkyComposite *parent) : SkyComposite(parent), m_reindexNum(J2000) { m_skyLabeler.reset(SkyLabeler::Instance()); m_skyMesh.reset(SkyMesh::Create(3)); // level 5 mesh = 8192 trixels m_skyMesh->debug(0); // 1 => print "indexing ..." // 2 => prints totals too // 10 => prints detailed lists // You can also set the debug level of individual // appendLine() and appendPoly() calls. //Add all components //Stars must come before constellation lines #ifdef KSTARS_LITE addComponent(m_MilkyWay = new MilkyWay(this), 50); addComponent(m_Stars = StarComponent::Create(this), 10); addComponent(m_EquatorialCoordinateGrid = new EquatorialCoordinateGrid(this)); addComponent(m_HorizontalCoordinateGrid = new HorizontalCoordinateGrid(this)); // Do add to components. addComponent(m_CBoundLines = new ConstellationBoundaryLines(this), 80); m_Cultures.reset(new CultureList()); addComponent(m_CLines = new ConstellationLines(this, m_Cultures.get()), 85); addComponent(m_CNames = new ConstellationNamesComponent(this, m_Cultures.get()), 90); addComponent(m_Equator = new Equator(this), 95); addComponent(m_Ecliptic = new Ecliptic(this), 95); addComponent(m_Horizon = new HorizonComponent(this), 100); addComponent(m_DeepSky = new DeepSkyComponent(this), 5); addComponent(m_ConstellationArt = new ConstellationArtComponent(this, m_Cultures.get()), 100); addComponent(m_ArtificialHorizon = new ArtificialHorizonComponent(this), 110); m_internetResolvedCat = "_Internet_Resolved"; m_manualAdditionsCat = "_Manual_Additions"; addComponent(m_internetResolvedComponent = new SyncedCatalogComponent(this, m_internetResolvedCat, true, 0), 6); addComponent(m_manualAdditionsComponent = new SyncedCatalogComponent(this, m_manualAdditionsCat, true, 0), 6); m_CustomCatalogs.reset(new SkyComposite(this)); QStringList allcatalogs = Options::showCatalogNames(); if (!allcatalogs.contains(m_internetResolvedCat)) { allcatalogs.append(m_internetResolvedCat); } if (!allcatalogs.contains(m_manualAdditionsCat)) { allcatalogs.append(m_manualAdditionsCat); } Options::setShowCatalogNames(allcatalogs); for (int i = 0; i < allcatalogs.size(); ++i) { if (allcatalogs.at(i) == m_internetResolvedCat || allcatalogs.at(i) == m_manualAdditionsCat) // This is a special catalog continue; m_CustomCatalogs->addComponent(new CatalogComponent(this, allcatalogs.at(i), false, i), 6); // FIXME: Should this be 6 or 5? See SkyMapComposite::reloadDeepSky() } addComponent(m_SolarSystem = new SolarSystemComposite(this), 2); //addComponent( m_ObservingList = new TargetListComponent( this , 0, QPen(), // &Options::obsListSymbol, &Options::obsListText ), 120 ); addComponent(m_StarHopRouteList = new TargetListComponent(this, 0, QPen()), 130); addComponent(m_Satellites = new SatellitesComponent(this), 7); addComponent(m_Supernovae = new SupernovaeComponent(this), 7); SkyMapLite::Instance()->loadingFinished(); #else addComponent(m_MilkyWay = new MilkyWay(this), 50); addComponent(m_Stars = StarComponent::Create(this), 10); addComponent(m_EquatorialCoordinateGrid = new EquatorialCoordinateGrid(this)); addComponent(m_HorizontalCoordinateGrid = new HorizontalCoordinateGrid(this)); addComponent(m_LocalMeridianComponent = new LocalMeridianComponent(this)); // Do add to components. addComponent(m_CBoundLines = new ConstellationBoundaryLines(this), 80); m_Cultures.reset(new CultureList()); addComponent(m_CLines = new ConstellationLines(this, m_Cultures.get()), 85); addComponent(m_CNames = new ConstellationNamesComponent(this, m_Cultures.get()), 90); addComponent(m_Equator = new Equator(this), 95); addComponent(m_Ecliptic = new Ecliptic(this), 95); addComponent(m_Horizon = new HorizonComponent(this), 100); addComponent(m_DeepSky = new DeepSkyComponent(this), 5); addComponent(m_ConstellationArt = new ConstellationArtComponent(this, m_Cultures.get()), 100); // Hips addComponent(m_HiPS = new HIPSComponent(this)); addComponent(m_ArtificialHorizon = new ArtificialHorizonComponent(this), 110); m_internetResolvedCat = "_Internet_Resolved"; m_manualAdditionsCat = "_Manual_Additions"; addComponent(m_internetResolvedComponent = new SyncedCatalogComponent(this, m_internetResolvedCat, true, 0), 6); addComponent(m_manualAdditionsComponent = new SyncedCatalogComponent(this, m_manualAdditionsCat, true, 0), 6); m_CustomCatalogs.reset(new SkyComposite(this)); QStringList allcatalogs = Options::showCatalogNames(); for (int i = 0; i < allcatalogs.size(); ++i) { if (allcatalogs.at(i) == m_internetResolvedCat || allcatalogs.at(i) == m_manualAdditionsCat) // This is a special catalog continue; m_CustomCatalogs->addComponent(new CatalogComponent(this, allcatalogs.at(i), false, i), 6); // FIXME: Should this be 6 or 5? See SkyMapComposite::reloadDeepSky() } addComponent(m_SolarSystem = new SolarSystemComposite(this), 2); addComponent(m_Flags = new FlagComponent(this), 4); addComponent(m_ObservingList = new TargetListComponent(this, nullptr, QPen(), &Options::obsListSymbol, &Options::obsListText), 120); addComponent(m_StarHopRouteList = new TargetListComponent(this, nullptr, QPen()), 130); addComponent(m_Satellites = new SatellitesComponent(this), 7); addComponent(m_Supernovae = new SupernovaeComponent(this), 7); #endif connect(this, SIGNAL(progressText(QString)), KStarsData::Instance(), SIGNAL(progressText(QString))); } void SkyMapComposite::update(KSNumbers *num) { //printf("updating SkyMapComposite\n"); //1. Milky Way //m_MilkyWay->update( data, num ); //2. Coordinate grid //m_EquatorialCoordinateGrid->update( num ); m_HorizontalCoordinateGrid->update(num); #ifndef KSTARS_LITE m_LocalMeridianComponent->update(num); #endif //3. Constellation boundaries //m_CBounds->update( data, num ); //4. Constellation lines //m_CLines->update( data, num ); //5. Constellation names if (m_CNames) m_CNames->update(num); //6. Equator //m_Equator->update( data, num ); //7. Ecliptic //m_Ecliptic->update( data, num ); //8. Deep sky //m_DeepSky->update( data, num ); //9. Custom catalogs m_CustomCatalogs->update(num); m_internetResolvedComponent->update(num); m_manualAdditionsComponent->update(num); //10. Stars //m_Stars->update( data, num ); //m_CLines->update( data, num ); // MUST follow stars. //12. Solar system m_SolarSystem->update(num); //13. Satellites m_Satellites->update(num); //14. Supernovae m_Supernovae->update(num); //15. Horizon m_Horizon->update(num); #ifndef KSTARS_LITE //16. Flags m_Flags->update(num); #endif } void SkyMapComposite::updateSolarSystemBodies(KSNumbers *num) { m_SolarSystem->updateSolarSystemBodies(num); } void SkyMapComposite::updateMoons(KSNumbers *num ) { m_SolarSystem->updateMoons( num ); } //Reimplement draw function so that we have control over the order of //elements, and we can add object labels // //The order in which components are drawn naturally determines the //z-ordering (the layering) of the components. Objects which //should appear "behind" others should be drawn first. void SkyMapComposite::draw(SkyPainter *skyp) { Q_UNUSED(skyp) #ifndef KSTARS_LITE SkyMap *map = SkyMap::Instance(); KStarsData *data = KStarsData::Instance(); // We delay one draw cycle before re-indexing // we MUST ensure CLines do not get re-indexed while we use DRAW_BUF // so we do it here. m_CLines->reindex(&m_reindexNum); // This queues re-indexing for the next draw cycle m_reindexNum = KSNumbers(data->updateNum()->julianDay()); // This ensures that the JIT updates are synchronized for the entire draw // cycle so the sky moves as a single sheet. May not be needed. data->syncUpdateIDs(); // prepare the aperture // FIXME_FOV: We may want to rejigger this to allow // wide-angle views --hdevalence float radius = map->projector()->fov(); if (radius > 180.0) radius = 180.0; if (m_skyMesh->inDraw()) { printf("Warning: aborting concurrent SkyMapComposite::draw()\n"); return; } m_skyMesh->inDraw(true); SkyPoint *focus = map->focus(); m_skyMesh->aperture(focus, radius + 1.0, DRAW_BUF); // divide by 2 for testing // create the no-precess aperture if needed if (Options::showEquatorialGrid() || Options::showHorizontalGrid() || Options::showCBounds() || Options::showEquator()) { m_skyMesh->index(focus, radius + 1.0, NO_PRECESS_BUF); } // clear marks from old labels and prep fonts m_skyLabeler->reset(map); m_skyLabeler->useStdFont(); // info boxes have highest label priority // FIXME: REGRESSION. Labeler now know nothing about infoboxes // map->infoBoxes()->reserveBoxes( psky ); // JM 2016-12-01: Why is this done this way?!! It's too inefficient if (KStars::Instance()) { auto &obsList = KStarsData::Instance()->observingList()->sessionList(); if (Options::obsListText()) for (auto &obj_clone : obsList) { // Find the "original" obj SkyObject *o = findByName(obj_clone->name()); // FIXME: This is slow.... and can also fail!!! if (!o) continue; SkyLabeler::AddLabel(o, SkyLabeler::RUDE_LABEL); } } m_MilkyWay->draw(skyp); // Draw HIPS after milky way but before everything else m_HiPS->draw(skyp); m_EquatorialCoordinateGrid->draw(skyp); m_HorizontalCoordinateGrid->draw(skyp); m_LocalMeridianComponent->draw(skyp); //Draw constellation boundary lines only if we draw western constellations if (m_Cultures->current() == "Western") { m_CBoundLines->draw(skyp); m_ConstellationArt->draw(skyp); } else if (m_Cultures->current() == "Inuit") { m_ConstellationArt->draw(skyp); } m_CLines->draw(skyp); m_Equator->draw(skyp); m_Ecliptic->draw(skyp); m_DeepSky->draw(skyp); m_CustomCatalogs->draw(skyp); m_internetResolvedComponent->draw(skyp); m_manualAdditionsComponent->draw(skyp); m_Stars->draw(skyp); m_SolarSystem->drawTrails(skyp); m_SolarSystem->draw(skyp); m_Satellites->draw(skyp); m_Supernovae->draw(skyp); map->drawObjectLabels(labelObjects()); m_skyLabeler->drawQueuedLabels(); m_CNames->draw(skyp); m_Stars->drawLabels(); m_DeepSky->drawLabels(); m_ObservingList->pen = QPen(QColor(data->colorScheme()->colorNamed("ObsListColor")), 1.); m_ObservingList->list2 = KStarsData::Instance()->observingList()->sessionList(); m_ObservingList->draw(skyp); m_Flags->draw(skyp); m_StarHopRouteList->pen = QPen(QColor(data->colorScheme()->colorNamed("StarHopRouteColor")), 1.); m_StarHopRouteList->draw(skyp); m_ArtificialHorizon->draw(skyp); m_Horizon->draw(skyp); m_skyMesh->inDraw(false); // DEBUG Edit. Keywords: Trixel boundaries. Currently works only in QPainter mode // -jbb uncomment these to see trixel outlines: /* QPainter *psky = dynamic_cast< QPainter *>( skyp ); if( psky ) { qCDebug(KSTARS) << "Drawing trixel boundaries for debugging."; psky->setPen( QPen( QBrush( QColor( "yellow" ) ), 1, Qt::SolidLine ) ); m_skyMesh->draw( *psky, OBJ_NEAREST_BUF ); SkyMesh *p; if( p = SkyMesh::Instance( 6 ) ) { qCDebug(KSTARS) << "We have a deep sky mesh to draw"; p->draw( *psky, OBJ_NEAREST_BUF ); } psky->setPen( QPen( QBrush( QColor( "green" ) ), 1, Qt::SolidLine ) ); m_skyMesh->draw( *psky, NO_PRECESS_BUF ); if( p ) p->draw( *psky, NO_PRECESS_BUF ); } */ #endif } //Select nearest object to the given skypoint, but give preference //to certain object types. //we multiply each object type's smallest angular distance by the //following factors before selecting the final nearest object: // faint stars > 12th mag = 1.75 // stars > 4 and < 12 = 1.5 // stars brighter than 4th mag = 0.75 // IC catalog = 0.8 // NGC catalog = 0.6 // "other" catalog = 0.6 // Messier object = 0.5 // custom object = 0.5 // Solar system = 0.25 SkyObject *SkyMapComposite::objectNearest(SkyPoint *p, double &maxrad) { double rTry = maxrad; double rBest = maxrad; SkyObject *oTry = nullptr; SkyObject *oBest = nullptr; //printf("%.1f %.1f\n", p->ra().Degrees(), p->dec().Degrees() ); m_skyMesh->aperture(p, maxrad + 1.0, OBJ_NEAREST_BUF); oBest = m_Stars->objectNearest(p, rBest); //reduce rBest by 0.75 for stars brighter than 4th mag if (oBest && oBest->mag() < 4.0) rBest *= 0.75; // For stars fainter than 12th mag else if (oBest && oBest->mag() > 12.0) rBest *= 1.75; // For stars between 4th and 12th mag else if (oBest) rBest *= 1.5; oTry = m_Satellites->objectNearest(p, rTry); if (rTry < rBest) { rBest = rTry; oBest = oTry; } for (auto &star : m_DeepStars) { rTry = maxrad; oTry = star->objectNearest(p, rTry); if (rTry < rBest) { rBest = rTry; oBest = oTry; } } // TODO: Add support for deep star catalogs //m_DeepSky internally discriminates among deepsky catalogs //and renormalizes rTry oTry = m_DeepSky->objectNearest(p, rTry); if (oTry && rTry < rBest) { rBest = rTry; oBest = oTry; } rTry = maxrad; oTry = m_CustomCatalogs->objectNearest(p, rTry); rTry *= 0.5; if (oTry && rTry < rBest) { rBest = rTry; oBest = oTry; } rTry = maxrad; oTry = m_internetResolvedComponent->objectNearest(p, rTry); rTry *= 0.5; if (oTry && rTry < rBest) { rBest = rTry; oBest = oTry; } rTry = maxrad; oTry = m_manualAdditionsComponent->objectNearest(p, rTry); rTry *= 0.5; if (oTry && rTry < rBest) { rBest = rTry; oBest = oTry; } rTry = maxrad; oTry = m_Supernovae->objectNearest(p, rTry); //qCDebug(KSTARS)<objectNearest(p, rTry); if (!dynamic_cast(oTry) && !dynamic_cast( oTry)) // There are gazillions of faint asteroids and comets; we want to prevent them from getting precedence { rTry *= 0.25; // this is either sun, moon, or one of the major planets or their moons. } else { if (std::isfinite(oTry->mag()) && oTry->mag() < 12.0) { rTry *= 0.75; // Bright comets / asteroids get some precedence. } } if (rTry < rBest) { rBest = rTry; oBest = oTry; } //if ( oBest && Options::verboseLogging()) //qCDebug(KSTARS) << "OBEST=" << oBest->name() << " - " << oBest->name2(); maxrad = rBest; return oBest; //will be 0 if no object nearer than maxrad was found } SkyObject *SkyMapComposite::starNearest(SkyPoint *p, double &maxrad) { double rtry = maxrad; SkyObject *star = nullptr; m_skyMesh->aperture(p, maxrad + 1.0, OBJ_NEAREST_BUF); star = m_Stars->objectNearest(p, rtry); //reduce rBest by 0.75 for stars brighter than 4th mag if (star && star->mag() < 4.0) rtry *= 0.75; // TODO: Add Deep Star Catalog support maxrad = rtry; return star; } bool SkyMapComposite::addNameLabel(SkyObject *o) { if (!o) return false; labelObjects().append(o); return true; } bool SkyMapComposite::removeNameLabel(SkyObject *o) { if (!o) return false; int index = labelObjects().indexOf(o); if (index < 0) return false; labelObjects().removeAt(index); return true; } QHash &SkyMapComposite::getObjectNames() { return m_ObjectNames; } QHash>> &SkyMapComposite::getObjectLists() { return m_ObjectLists; } QList SkyMapComposite::findObjectsInArea(const SkyPoint &p1, const SkyPoint &p2) { const SkyRegion ®ion = m_skyMesh->skyRegion(p1, p2); QList list; // call objectsInArea( QList&, const SkyRegion& ) for each of the // components of the SkyMapComposite if (m_Stars->selected()) m_Stars->objectsInArea(list, region); if (m_DeepSky->selected()) m_DeepSky->objectsInArea(list, region); return list; } SkyObject *SkyMapComposite::findByName(const QString &name) { #ifndef KSTARS_LITE if (KStars::Closing) return nullptr; #endif //We search the children in an "intelligent" order (most-used //object types first), in order to avoid wasting too much time //looking for a match. The most important part of this ordering //is that stars should be last (because the stars list is so long) SkyObject *o = nullptr; o = m_SolarSystem->findByName(name); if (o) return o; o = m_DeepSky->findByName(name); if (o) return o; o = m_CustomCatalogs->findByName(name); if (o) return o; o = m_internetResolvedComponent->findByName(name); if (o) return o; o = m_manualAdditionsComponent->findByName(name); if (o) return o; o = m_CNames->findByName(name); if (o) return o; o = m_Stars->findByName(name); if (o) return o; o = m_Supernovae->findByName(name); if (o) return o; o = m_Satellites->findByName(name); if (o) return o; return nullptr; } SkyObject *SkyMapComposite::findStarByGenetiveName(const QString name) { return m_Stars->findStarByGenetiveName(name); } KSPlanetBase *SkyMapComposite::planet(int n) { if (n == KSPlanetBase::SUN) - return dynamic_cast((m_SolarSystem->findByName("Sun"))); + return dynamic_cast((m_SolarSystem->findByName(i18n("Sun")))); if (n == KSPlanetBase::MERCURY) return dynamic_cast((m_SolarSystem->findByName(i18n("Mercury")))); if (n == KSPlanetBase::VENUS) return dynamic_cast((m_SolarSystem->findByName(i18n("Venus")))); if (n == KSPlanetBase::MOON) return dynamic_cast((m_SolarSystem->findByName(i18n("Moon")))); if (n == KSPlanetBase::MARS) return dynamic_cast((m_SolarSystem->findByName(i18n("Mars")))); if (n == KSPlanetBase::JUPITER) return dynamic_cast((m_SolarSystem->findByName(i18n("Jupiter")))); if (n == KSPlanetBase::SATURN) return dynamic_cast((m_SolarSystem->findByName(i18n("Saturn")))); if (n == KSPlanetBase::URANUS) return dynamic_cast((m_SolarSystem->findByName(i18n("Uranus")))); if (n == KSPlanetBase::NEPTUNE) return dynamic_cast((m_SolarSystem->findByName(i18n("Neptune")))); //if ( n == KSPlanetBase::PLUTO ) return (KSPlanetBase*)(m_SolarSystem->findByName( i18n( "Pluto" ) ) ); return nullptr; } void SkyMapComposite::addCustomCatalog(const QString &filename, int index) { CatalogComponent *cc = new CatalogComponent(this, filename, false, index); if (cc->objectList().size()) { m_CustomCatalogs->addComponent(cc); } else { delete cc; } } void SkyMapComposite::removeCustomCatalog(const QString &name) { for (auto sc : m_CustomCatalogs->components()) { CatalogComponent *ccc = dynamic_cast(sc); if (ccc->name() == name) { m_CustomCatalogs->removeComponent(ccc); return; } } qCWarning(KSTARS) << i18n("Could not find custom catalog component named %1.", name); } void SkyMapComposite::reloadCLines() { #ifndef KSTARS_LITE Q_ASSERT(!SkyMapDrawAbstract::drawLock()); SkyMapDrawAbstract::setDrawLock( true); // This is not (yet) multithreaded, so I think we don't have to worry about overwriting the state of an existing lock --asimha removeComponent(m_CLines); delete m_CLines; addComponent(m_CLines = new ConstellationLines(this, m_Cultures.get())); SkyMapDrawAbstract::setDrawLock(false); #endif } void SkyMapComposite::reloadCNames() { // Q_ASSERT( !SkyMapDrawAbstract::drawLock() ); // SkyMapDrawAbstract::setDrawLock( true ); // This is not (yet) multithreaded, so I think we don't have to worry about overwriting the state of an existing lock --asimha // objectNames(SkyObject::CONSTELLATION).clear(); // delete m_CNames; // m_CNames = 0; // m_CNames = new ConstellationNamesComponent( this, m_Cultures.get() ); // SkyMapDrawAbstract::setDrawLock( false ); objectNames(SkyObject::CONSTELLATION).clear(); removeComponent(m_CNames); delete m_CNames; addComponent(m_CNames = new ConstellationNamesComponent(this, m_Cultures.get())); } void SkyMapComposite::reloadConstellationArt() { #ifndef KSTARS_LITE Q_ASSERT(!SkyMapDrawAbstract::drawLock()); SkyMapDrawAbstract::setDrawLock(true); removeComponent(m_ConstellationArt); delete m_ConstellationArt; addComponent(m_ConstellationArt = new ConstellationArtComponent(this, m_Cultures.get())); SkyMapDrawAbstract::setDrawLock(false); #endif } void SkyMapComposite::reloadDeepSky() { #ifndef KSTARS_LITE Q_ASSERT(!SkyMapDrawAbstract::drawLock()); // Deselect object if selected! If not deselected then InfoBox tries to // get the name of an object which may not exist (getLongName) // FIXME (spacetime): Is there a better way? // Current Solution: Look for the nearest star in the region and select it. SkyMap *current_map = KStars::Instance()->map(); double maxrad = 30.0; SkyPoint center_point = current_map->getCenterPoint(); current_map->setClickedObject(KStars::Instance()->data()->skyComposite()->starNearest(¢er_point, maxrad)); current_map->setClickedPoint(current_map->clickedObject()); current_map->slotCenter(); // Remove and Regenerate set of catalog objects // // FIXME: Why should we do this? Because it messes up observing // list really bad to delete and regenerate SkyObjects. SkyMapDrawAbstract::setDrawLock(true); m_CustomCatalogs.reset(new SkyComposite(this)); removeComponent(m_internetResolvedComponent); delete m_internetResolvedComponent; addComponent(m_internetResolvedComponent = new SyncedCatalogComponent(this, m_internetResolvedCat, true, 0), 6); removeComponent(m_manualAdditionsComponent); delete m_manualAdditionsComponent; addComponent(m_manualAdditionsComponent = new SyncedCatalogComponent(this, m_manualAdditionsCat, true, 0), 6); QStringList allcatalogs = Options::showCatalogNames(); for (int i = 0; i < allcatalogs.size(); ++i) { if (allcatalogs.at(i) == m_internetResolvedCat || allcatalogs.at(i) == m_manualAdditionsCat) // These are special catalogs continue; m_CustomCatalogs->addComponent(new CatalogComponent(this, allcatalogs.at(i), false, i), 5); // FIXME: Should this be 6 or 5? See SkyMapComposite::SkyMapComposite() } // FIXME: We should also reload anything that refers to SkyObject // * in memory, because all the old SkyObjects are now gone! This // includes the observing list. Otherwise, expect a bad, bad crash // that is hard to debug! -- AS SkyMapDrawAbstract::setDrawLock(false); #endif } bool SkyMapComposite::isLocalCNames() { return m_CNames->isLocalCNames(); } void SkyMapComposite::emitProgressText(const QString &message) { emit progressText(message); #ifndef Q_OS_ANDROID //Can cause crashes on Android, investigate it qApp->processEvents(); // -jbb: this seemed to make it work. #endif //qCDebug(KSTARS) << QString("PROGRESS TEXT: %1\n").arg( message ); } const QList &SkyMapComposite::deepSkyObjects() const { return m_DeepSky->objectList(); } const QList &SkyMapComposite::constellationNames() const { return m_CNames->objectList(); } // Returns only named stars, and should not be used const QList &SkyMapComposite::stars() const { return m_Stars->objectList(); } const QList &SkyMapComposite::asteroids() const { return m_SolarSystem->asteroids(); } const QList &SkyMapComposite::comets() const { return m_SolarSystem->comets(); } const QList &SkyMapComposite::supernovae() const { return m_Supernovae->objectList(); } QList SkyMapComposite::planets() { return solarSystemComposite()->planetObjects(); } //Store it permanently /* QList SkyMapComposite::moons() { QList skyObjects; foreach(PlanetMoonsComponent *pMoons, m_SolarSystem->planetMoonsComponent()) { PlanetMoons *moons = pMoons->getMoons(); for(int i = 0; i < moons->nMoons(); ++i) { skyObjects.append(moons->moon(i)); } } return skyObjects; } */ const QList *SkyMapComposite::getSkyObjectsList(SkyObject::TYPE t) { switch (t) { case SkyObject::STAR: return &m_Stars->objectList(); case SkyObject::CATALOG_STAR: return nullptr; case SkyObject::PLANET: return &m_SolarSystem->planetObjects(); case SkyObject::COMET: return &comets(); case SkyObject::ASTEROID: return &asteroids(); case SkyObject::MOON: return &m_SolarSystem->moons(); case SkyObject::GALAXY: case SkyObject::PLANETARY_NEBULA: case SkyObject::GASEOUS_NEBULA: case SkyObject::GLOBULAR_CLUSTER: case SkyObject::OPEN_CLUSTER: return nullptr; case SkyObject::CONSTELLATION: return &constellationNames(); case SkyObject::SUPERNOVA: return &supernovae(); default: return nullptr; } //return nullptr; } KSPlanet *SkyMapComposite::earth() { return m_SolarSystem->earth(); } QList SkyMapComposite::customCatalogs() { return m_CustomCatalogs->components(); } QStringList SkyMapComposite::getCultureNames() { return m_Cultures->getNames(); } QString SkyMapComposite::getCultureName(int index) { return m_Cultures->getName(index); } void SkyMapComposite::setCurrentCulture(QString culture) { m_Cultures->setCurrent(culture); } QString SkyMapComposite::currentCulture() { return m_Cultures->current(); } #ifndef KSTARS_LITE FlagComponent *SkyMapComposite::flags() { return m_Flags; } #endif SatellitesComponent *SkyMapComposite::satellites() { return m_Satellites; } SupernovaeComponent *SkyMapComposite::supernovaeComponent() { return m_Supernovae; } ArtificialHorizonComponent *SkyMapComposite::artificialHorizon() { return m_ArtificialHorizon; } diff --git a/kstars/skycomponents/solarsystemsinglecomponent.cpp b/kstars/skycomponents/solarsystemsinglecomponent.cpp index 527c92ab4..f85e437bb 100644 --- a/kstars/skycomponents/solarsystemsinglecomponent.cpp +++ b/kstars/skycomponents/solarsystemsinglecomponent.cpp @@ -1,136 +1,137 @@ /*************************************************************************** solarsystemsinglecomponent.cpp - K Desktop Planetarium ------------------- begin : 2005/30/08 copyright : (C) 2005 by Thomas Kabelmann email : thomas.kabelmann@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. * * * ***************************************************************************/ #include "solarsystemsinglecomponent.h" #include "solarsystemcomposite.h" #include "skycomponent.h" +#include #include "dms.h" #include "kstarsdata.h" #include "skyobjects/starobject.h" #include "skyobjects/ksplanetbase.h" #include "skyobjects/ksplanet.h" #ifdef KSTARS_LITE #include "skymaplite.h" #else #include "skymap.h" #endif #include "Options.h" #include "skylabeler.h" #include "skypainter.h" #include "projections/projector.h" SolarSystemSingleComponent::SolarSystemSingleComponent(SolarSystemComposite *parent, KSPlanetBase *kspb, - bool (*visibleMethod)(), bool isMoon) + bool (*visibleMethod)(), bool isMoon) : SkyComponent(parent), visible(visibleMethod), m_isMoon(isMoon), m_Earth(parent->earth()), m_Planet(kspb) { m_Planet->loadData(); if (!m_Planet->name().isEmpty()) { objectNames(m_Planet->type()).append(m_Planet->name()); objectLists(m_Planet->type()).append(QPair(m_Planet->name(), m_Planet)); } if (!m_Planet->longname().isEmpty() && m_Planet->longname() != m_Planet->name()) { objectNames(m_Planet->type()).append(m_Planet->longname()); objectLists(m_Planet->type()).append(QPair(m_Planet->longname(), m_Planet)); } } SolarSystemSingleComponent::~SolarSystemSingleComponent() { removeFromNames(m_Planet); removeFromLists(m_Planet); delete m_Planet; } bool SolarSystemSingleComponent::selected() { return visible(); } SkyObject *SolarSystemSingleComponent::findByName(const QString &name) { if (QString::compare(m_Planet->name(), name, Qt::CaseInsensitive) == 0 || - QString::compare(m_Planet->longname(), name, Qt::CaseInsensitive) == 0 || - QString::compare(m_Planet->name2(), name, Qt::CaseInsensitive) == 0) + QString::compare(m_Planet->longname(), name, Qt::CaseInsensitive) == 0 || + QString::compare(m_Planet->name2(), name, Qt::CaseInsensitive) == 0) return m_Planet; return nullptr; } SkyObject *SolarSystemSingleComponent::objectNearest(SkyPoint *p, double &maxrad) { double r = m_Planet->angularDistanceTo(p).Degrees(); if (r < maxrad) { maxrad = r; return m_Planet; } return nullptr; } void SolarSystemSingleComponent::update(KSNumbers *) { KStarsData *data = KStarsData::Instance(); if (selected()) m_Planet->EquatorialToHorizontal(data->lst(), data->geo()->lat()); } void SolarSystemSingleComponent::updateSolarSystemBodies(KSNumbers *num) { if (!m_isMoon && selected()) { KStarsData *data = KStarsData::Instance(); m_Planet->findPosition(num, data->geo()->lat(), data->lst(), m_Earth); m_Planet->EquatorialToHorizontal(data->lst(), data->geo()->lat()); if (m_Planet->hasTrail()) m_Planet->updateTrail(data->lst(), data->geo()->lat()); } } // NOTE: This seems like code duplication, and yes IT IS. But there may be some // NOTE: changes to be made to it later on, and calling `updateSolarSystemBodies` // NOTE: is ugly. void SolarSystemSingleComponent::updateMoons(KSNumbers *num) { KStarsData *data = KStarsData::Instance(); m_Planet->findPosition(num, data->geo()->lat(), data->lst(), m_Earth); m_Planet->EquatorialToHorizontal(data->lst(), data->geo()->lat()); if (m_Planet->hasTrail()) m_Planet->updateTrail(data->lst(), data->geo()->lat()); } void SolarSystemSingleComponent::draw(SkyPainter *skyp) { if (!selected()) return; skyp->setPen(m_Planet->color()); skyp->setBrush(m_Planet->color()); bool drawn = skyp->drawPlanet(m_Planet); if (drawn && Options::showPlanetNames()) SkyLabeler::AddLabel(m_Planet, SkyLabeler::PLANET_LABEL); } void SolarSystemSingleComponent::drawTrails(SkyPainter *skyp) { if (selected()) m_Planet->drawTrail(skyp); } diff --git a/kstars/skymapdrawabstract.cpp b/kstars/skymapdrawabstract.cpp index 63c0431c8..322395ff7 100644 --- a/kstars/skymapdrawabstract.cpp +++ b/kstars/skymapdrawabstract.cpp @@ -1,473 +1,473 @@ /*************************************************************************** skymapdrawabstract.cpp - K Desktop Planetarium ------------------- begin : Sun Mar 2 2003 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ // This file implements the class SkyMapDrawAbstract, and is almost // identical to the older skymapdraw.cpp file, written by Jason // Harris. Essentially, skymapdraw.cpp was renamed and modified. // -- asimha (2011) #include #include #include "skymapdrawabstract.h" #include "skymap.h" #include "Options.h" #include "fov.h" #include "kstars.h" #include "kstarsdata.h" #include "ksnumbers.h" #include "ksutils.h" #include "skyobjects/skyobject.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/starobject.h" #include "skyobjects/ksplanetbase.h" #include "simclock.h" #include "observinglist.h" #include "skycomponents/constellationboundarylines.h" #include "skycomponents/skylabeler.h" #include "skycomponents/skymapcomposite.h" #include "skyqpainter.h" #include "projections/projector.h" #include "projections/lambertprojector.h" #include #ifdef HAVE_INDI #include #include "indi/indilistener.h" #include "indi/driverinfo.h" #include "indi/indistd.h" #include "ekos/manager.h" #include "ekos/align/align.h" #endif bool SkyMapDrawAbstract::m_DrawLock = false; SkyMapDrawAbstract::SkyMapDrawAbstract(SkyMap *sm) : m_KStarsData(KStarsData::Instance()), m_SkyMap(sm) { //m_fpstime.start(); //m_framecount = 0; } void SkyMapDrawAbstract::drawOverlays(QPainter &p, bool drawFov) { if (!KStars::Instance()) return; //draw labels SkyLabeler::Instance()->draw(p); if (drawFov) { //draw FOV symbol foreach (FOV *fov, m_KStarsData->getVisibleFOVs()) { if (fov->lockCelestialPole()) { SkyPoint centerSkyPoint = SkyMap::Instance()->projector()->fromScreen(p.viewport().center(), KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); QPointF screenSkyPoint = p.viewport().center(); double northRotation = SkyMap::Instance()->projector()->findNorthPA(¢erSkyPoint, screenSkyPoint.x(), screenSkyPoint.y()); fov->setCenter(centerSkyPoint); fov->setNorthPA(northRotation); } fov->draw(p, Options::zoomFactor()); } } drawSolverFOV(p); drawTelescopeSymbols(p); drawZoomBox(p); // FIXME: Maybe we should take care of this differently. Maybe // drawOverlays should remain in SkyMap, since it just calls // certain drawing functions which are implemented in // SkyMapDrawAbstract. Really, it doesn't draw anything on its // own. if (m_SkyMap->rulerMode) { m_SkyMap->updateAngleRuler(); drawAngleRuler(p); } } void SkyMapDrawAbstract::drawAngleRuler(QPainter &p) { //FIXME use sky painter. p.setPen(QPen(m_KStarsData->colorScheme()->colorNamed("AngularRuler"), 3.0, Qt::DotLine)); p.drawLine( m_SkyMap->m_proj->toScreen(m_SkyMap->AngularRuler.point( - 0)), // FIXME: More ugliness. m_proj should probably be a single-instance class, or we should have our own instance etc. + 0)), // FIXME: More ugliness. m_proj should probably be a single-instance class, or we should have our own instance etc. m_SkyMap->m_proj->toScreen(m_SkyMap->AngularRuler.point( - 1))); // FIXME: Again, AngularRuler should be something better -- maybe a class in itself. After all it's used for more than one thing after we integrate the StarHop feature. + 1))); // FIXME: Again, AngularRuler should be something better -- maybe a class in itself. After all it's used for more than one thing after we integrate the StarHop feature. } void SkyMapDrawAbstract::drawZoomBox(QPainter &p) { //draw the manual zoom-box, if it exists if (m_SkyMap->ZoomRect.isValid()) { p.setPen(QPen(Qt::white, 1.0, Qt::DotLine)); p.drawRect(m_SkyMap->ZoomRect.x(), m_SkyMap->ZoomRect.y(), m_SkyMap->ZoomRect.width(), m_SkyMap->ZoomRect.height()); } } void SkyMapDrawAbstract::drawObjectLabels(QList &labelObjects) { bool checkSlewing = (m_SkyMap->slewing || (m_SkyMap->clockSlewing && m_KStarsData->clock()->isActive())) && Options::hideOnSlew(); if (checkSlewing && Options::hideLabels()) return; SkyLabeler *skyLabeler = SkyLabeler::Instance(); skyLabeler->resetFont(); // use the zoom dependent font skyLabeler->setPen(m_KStarsData->colorScheme()->colorNamed("UserLabelColor")); bool drawPlanets = Options::showSolarSystem() && !(checkSlewing && Options::hidePlanets()); bool drawComets = drawPlanets && Options::showComets(); bool drawAsteroids = drawPlanets && Options::showAsteroids(); bool drawMessier = Options::showDeepSky() && (Options::showMessier() || Options::showMessierImages()) && - !(checkSlewing && Options::hideMessier()); + !(checkSlewing && Options::hideMessier()); bool drawNGC = Options::showDeepSky() && Options::showNGC() && !(checkSlewing && Options::hideNGC()); bool drawIC = Options::showDeepSky() && Options::showIC() && !(checkSlewing && Options::hideIC()); bool drawOther = Options::showDeepSky() && Options::showOther() && !(checkSlewing && Options::hideOther()); bool drawStars = Options::showStars(); bool hideFaintStars = checkSlewing && Options::hideStars(); //Attach a label to the centered object if (m_SkyMap->focusObject() != nullptr && Options::useAutoLabel()) { QPointF o = m_SkyMap->m_proj->toScreen(m_SkyMap->focusObject()); // FIXME: Same thing. m_proj should be accessible here. skyLabeler->drawNameLabel(m_SkyMap->focusObject(), o); } foreach (SkyObject *obj, labelObjects) { //Only draw an attached label if the object is being drawn to the map //reproducing logic from other draw funcs here...not an optimal solution if (obj->type() == SkyObject::STAR || obj->type() == SkyObject::CATALOG_STAR || - obj->type() == SkyObject::MULT_STAR) + obj->type() == SkyObject::MULT_STAR) { if (!drawStars) continue; // if ( obj->mag() > Options::magLimitDrawStar() ) continue; if (hideFaintStars && obj->mag() > Options::magLimitHideStar()) continue; } if (obj->type() == SkyObject::PLANET) { if (!drawPlanets) continue; - if (obj->name() == "Sun" && !Options::showSun()) + if (obj->name() == i18n("Sun") && !Options::showSun()) continue; if (obj->name() == i18n("Mercury") && !Options::showMercury()) continue; if (obj->name() == i18n("Venus") && !Options::showVenus()) continue; - if (obj->name() == "Moon" && !Options::showMoon()) + if (obj->name() == i18n("Moon") && !Options::showMoon()) continue; if (obj->name() == i18n("Mars") && !Options::showMars()) continue; if (obj->name() == i18n("Jupiter") && !Options::showJupiter()) continue; if (obj->name() == i18n("Saturn") && !Options::showSaturn()) continue; if (obj->name() == i18n("Uranus") && !Options::showUranus()) continue; if (obj->name() == i18n("Neptune") && !Options::showNeptune()) continue; //if ( obj->name() == i18n( "Pluto" ) && ! Options::showPluto() ) continue; } if ((obj->type() >= SkyObject::OPEN_CLUSTER && obj->type() <= SkyObject::GALAXY) || - (obj->type() >= SkyObject::ASTERISM && obj->type() <= SkyObject::QUASAR) || - (obj->type() == SkyObject::RADIO_SOURCE)) + (obj->type() >= SkyObject::ASTERISM && obj->type() <= SkyObject::QUASAR) || + (obj->type() == SkyObject::RADIO_SOURCE)) { if (((DeepSkyObject *)obj)->isCatalogM() && !drawMessier) continue; if (((DeepSkyObject *)obj)->isCatalogNGC() && !drawNGC) continue; if (((DeepSkyObject *)obj)->isCatalogIC() && !drawIC) continue; if (((DeepSkyObject *)obj)->isCatalogNone() && !drawOther) continue; } if (obj->type() == SkyObject::COMET && !drawComets) continue; if (obj->type() == SkyObject::ASTEROID && !drawAsteroids) continue; if (!m_SkyMap->m_proj->checkVisibility(obj)) continue; // FIXME: m_proj should be a member of this class. QPointF o = m_SkyMap->m_proj->toScreen(obj); if (!m_SkyMap->m_proj->onScreen(o)) continue; skyLabeler->drawNameLabel(obj, o); } skyLabeler->useStdFont(); // use the StdFont for the guides. } void SkyMapDrawAbstract::drawSolverFOV(QPainter &psky) { Q_UNUSED(psky); #ifdef HAVE_INDI Ekos::Align *align = KStars::Instance()->ekosManager()->alignModule(); //if (align && align->isSolverComplete()) if (align) { if (Options::showSensorFOV()) { FOV *fov = align->getSensorFOV(); if (fov) { fov->setColor(KStars::Instance()->data()->colorScheme()->colorNamed("SensorFOVColor").name()); SkyPoint centerSkyPoint = SkyMap::Instance()->projector()->fromScreen(psky.viewport().center(), KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); QPointF screenSkyPoint = psky.viewport().center(); double northRotation = SkyMap::Instance()->projector()->findNorthPA(¢erSkyPoint, screenSkyPoint.x(), screenSkyPoint.y()); fov->setCenter(centerSkyPoint); fov->setNorthPA(northRotation); fov->draw(psky, Options::zoomFactor()); } } if (align->status() == Ekos::ALIGN_COMPLETE || align->status() == Ekos::ALIGN_PROGRESS) { bool isVisible = false; FOV *fov = align->getSolverFOV(); if (fov == nullptr) return; SkyPoint p = fov->center(); if (std::isnan(p.ra().Degrees())) return; p.EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); QPointF point = SkyMap::Instance()->projector()->toScreen(&p, true, &isVisible); double northRotation = SkyMap::Instance()->projector()->findNorthPA(&p, point.x(), point.y()); fov->setNorthPA(northRotation); fov->draw(psky, Options::zoomFactor()); } } #endif } void SkyMapDrawAbstract::drawTelescopeSymbols(QPainter &psky) { Q_UNUSED(psky); #ifdef HAVE_INDI if (!Options::showTargetCrosshair()) return; if (INDIListener::Instance()->size() == 0) return; SkyPoint indi_sp; psky.setPen(QPen(QColor(m_KStarsData->colorScheme()->colorNamed("TargetColor")))); psky.setBrush(Qt::NoBrush); float pxperdegree = Options::zoomFactor() / 57.3; foreach (ISD::GDInterface *gd, INDIListener::Instance()->getDevices()) { if (gd->getType() != KSTARS_TELESCOPE) continue; INDI::BaseDevice *bd = gd->getBaseDevice(); if (bd == nullptr) continue; if (bd->isConnected() == false) continue; INumberVectorProperty *coordNP = bd->getNumber("EQUATORIAL_EOD_COORD"); if (coordNP == nullptr) { coordNP = bd->getNumber("EQUATORIAL_COORD"); if (coordNP) { INumber *np = IUFindNumber(coordNP, "RA"); if (np == nullptr) continue; indi_sp.setRA(np->value); indi_sp.setRA0(np->value); np = IUFindNumber(coordNP, "DEC"); if (np == nullptr) continue; indi_sp.setDec(np->value); indi_sp.setDec0(np->value); indi_sp.apparentCoord(static_cast(J2000), KStars::Instance()->data()->ut().djd()); } else { coordNP = bd->getNumber("HORIZONTAL_COORD"); if (coordNP == nullptr) continue; else { INumber *np = IUFindNumber(coordNP, "AZ"); if (np == nullptr) continue; indi_sp.setAz(np->value); np = IUFindNumber(coordNP, "ALT"); if (np == nullptr) continue; indi_sp.setAlt(np->value); indi_sp.HorizontalToEquatorial(m_KStarsData->lst(), m_KStarsData->geo()->lat()); } } } else { INumber *np = IUFindNumber(coordNP, "RA"); if (np == nullptr) continue; indi_sp.setRA(np->value); np = IUFindNumber(coordNP, "DEC"); if (np == nullptr) continue; indi_sp.setDec(np->value); } if (Options::useAltAz()) indi_sp.EquatorialToHorizontal(m_KStarsData->lst(), m_KStarsData->geo()->lat()); if (std::isnan(indi_sp.ra().Degrees()) || std::isnan(indi_sp.dec().Degrees())) continue; QPointF P = m_SkyMap->m_proj->toScreen(&indi_sp); if (Options::useAntialias()) { float s1 = 0.5 * pxperdegree; float s2 = pxperdegree; float s3 = 2.0 * pxperdegree; float x0 = P.x(); float y0 = P.y(); float x1 = x0 - 0.5 * s1; float y1 = y0 - 0.5 * s1; float x2 = x0 - 0.5 * s2; float y2 = y0 - 0.5 * s2; float x3 = x0 - 0.5 * s3; float y3 = y0 - 0.5 * s3; //Draw radial lines psky.drawLine(QPointF(x1, y0), QPointF(x3, y0)); psky.drawLine(QPointF(x0 + s2, y0), QPointF(x0 + 0.5 * s1, y0)); psky.drawLine(QPointF(x0, y1), QPointF(x0, y3)); psky.drawLine(QPointF(x0, y0 + 0.5 * s1), QPointF(x0, y0 + s2)); //Draw circles at 0.5 & 1 degrees psky.drawEllipse(QRectF(x1, y1, s1, s1)); psky.drawEllipse(QRectF(x2, y2, s2, s2)); psky.drawText(QPointF(x0 + s2 + 2., y0), bd->getDeviceName()); } else { int s1 = int(0.5 * pxperdegree); int s2 = int(pxperdegree); int s3 = int(2.0 * pxperdegree); int x0 = int(P.x()); int y0 = int(P.y()); int x1 = x0 - s1 / 2; int y1 = y0 - s1 / 2; int x2 = x0 - s2 / 2; int y2 = y0 - s2 / 2; int x3 = x0 - s3 / 2; int y3 = y0 - s3 / 2; //Draw radial lines psky.drawLine(QPoint(x1, y0), QPoint(x3, y0)); psky.drawLine(QPoint(x0 + s2, y0), QPoint(x0 + s1 / 2, y0)); psky.drawLine(QPoint(x0, y1), QPoint(x0, y3)); psky.drawLine(QPoint(x0, y0 + s1 / 2), QPoint(x0, y0 + s2)); //Draw circles at 0.5 & 1 degrees psky.drawEllipse(QRect(x1, y1, s1, s1)); psky.drawEllipse(QRect(x2, y2, s2, s2)); psky.drawText(QPoint(x0 + s2 + 2, y0), bd->getDeviceName()); } } #endif } void SkyMapDrawAbstract::exportSkyImage(QPaintDevice *pd, bool scale) { SkyQPainter p(m_SkyMap, pd); p.begin(); p.setRenderHint(QPainter::SmoothPixmapTransform, true); exportSkyImage(&p, scale); p.end(); } void SkyMapDrawAbstract::exportSkyImage(SkyQPainter *painter, bool scale) { bool vectorStarState; vectorStarState = painter->getVectorStars(); painter->setVectorStars( true); // Since we are exporting an image, we may use vector stars without worrying about time painter->setRenderHint(QPainter::Antialiasing, Options::useAntialias()); if (scale) { //scale sky image to fit paint device qDebug() << "Scaling true while exporting Sky Image"; double xscale = double(painter->device()->width()) / double(m_SkyMap->width()); double yscale = double(painter->device()->height()) / double(m_SkyMap->height()); double scale = qMin(xscale, yscale); qDebug() << "xscale: " << xscale << "yscale: " << yscale << "chosen scale: " << scale; painter->scale(scale, scale); } painter->drawSkyBackground(); m_KStarsData->skyComposite()->draw(painter); drawOverlays(*painter); painter->setVectorStars(vectorStarState); // Restore the state of the painter } /* JM 2016-05-03: Not needed since we're not using OpenGL for now * void SkyMapDrawAbstract::calculateFPS() { if(m_framecount == 25) { //float sec = m_fpstime.elapsed()/1000.; // qDebug() << "FPS " << m_framecount/sec; m_framecount = 0; m_fpstime.restart(); } ++m_framecount; }*/ void SkyMapDrawAbstract::setDrawLock(bool state) { m_DrawLock = state; } diff --git a/kstars/skyobjects/ksearthshadow.cpp b/kstars/skyobjects/ksearthshadow.cpp index 51a33715a..3701b2c6f 100644 --- a/kstars/skyobjects/ksearthshadow.cpp +++ b/kstars/skyobjects/ksearthshadow.cpp @@ -1,93 +1,96 @@ /*************************************************************************** ksearthshadow.cpp - K Desktop Planetarium ------------------- begin : Fri Aug 24 2018 copyright : (C) 2018 by Valentin Boettcher email : valentin@boettcher.cf ***************************************************************************/ /*************************************************************************** * * * 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 "ksearthshadow.h" #include "kssun.h" #include "ksmoon.h" KSEarthShadow::KSEarthShadow(const KSMoon *moon, const KSSun *sun, const KSPlanet * earth) - : KSPlanetBase ("Earth Shadow"), m_sun { sun }, m_moon { moon }, m_earth {earth} + : KSPlanetBase (i18n("Earth Shadow")), m_sun { sun }, m_moon { moon }, m_earth {earth} { } bool KSEarthShadow::shouldUpdate() { return (m_moon->illum() > 0.8); } bool KSEarthShadow::isInEclipse() { double dist = angularDistanceTo(m_moon).Degrees() * 60; return (dist - m_moon->angSize() / 2) <= m_penumbra_ang; } KSEarthShadow::ECLIPSE_TYPE KSEarthShadow::getEclipseType() { double half_a_moon = m_moon->angSize() / 2; double dist = angularDistanceTo(m_moon).Degrees() * 60; // arcminutes - if (dist <= (m_penumbra_ang - half_a_moon)) { + if (dist <= (m_penumbra_ang - half_a_moon)) + { if(dist <= (m_umbra_ang - half_a_moon)) return FULL_UMBRA; else return FULL_PENUMBRA; - } else if ((dist - half_a_moon) <= m_penumbra_ang) { - return PARTIAL; + } + else if ((dist - half_a_moon) <= m_penumbra_ang) + { + return PARTIAL; } return NONE; } bool KSEarthShadow::findGeocentricPosition(const KSNumbers *, const KSPlanetBase *) { updateCoords(); return true; //TODO: not nice! } void KSEarthShadow::updateCoords(const KSNumbers *, bool, const CachingDms *, const CachingDms *, bool ) { updateCoords(); } //TODO: Abort if Null void KSEarthShadow::updateCoords() { // flip the sun around to get the 'shadow coordinates' dms t_ra(m_sun->ra().Degrees() + 180); t_ra.reduceToRange(dms::ZERO_TO_2PI); - dms t_dec(-1*(m_sun->dec().Degrees())); + dms t_dec(-1 * (m_sun->dec().Degrees())); set(t_ra, t_dec); Rearth = m_moon->rearth(); } //NOTE: This can easily be generalized to any three bodies. void KSEarthShadow::calculateShadowRadius() { double d_sun = m_sun->rearth() * AU_KM; double d_moon = m_moon->rearth() * AU_KM; double r_sun = m_sun->physicalSize() / 2; double r_earth = m_earth->physicalSize() / 2; - double umbraRad = 1.01 * r_earth + (r_earth-r_sun)/d_sun * d_moon; - double penumbraRad = 1.01 * r_earth+(r_sun+r_earth)/d_sun*d_moon; + double umbraRad = 1.01 * r_earth + (r_earth - r_sun) / d_sun * d_moon; + double penumbraRad = 1.01 * r_earth + (r_sun + r_earth) / d_sun * d_moon; m_umbra_ang = asin(umbraRad / d_moon) * 60. * 180. / dms::PI; m_penumbra_ang = asin(penumbraRad / d_moon) * 60. * 180. / dms::PI; return; } diff --git a/kstars/skyobjects/ksplanet.cpp b/kstars/skyobjects/ksplanet.cpp index 3abeef816..fc92570cc 100644 --- a/kstars/skyobjects/ksplanet.cpp +++ b/kstars/skyobjects/ksplanet.cpp @@ -1,442 +1,450 @@ /*************************************************************************** ksplanet.cpp - K Desktop Planetarium ------------------- begin : Sun Jul 22 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "ksplanet.h" #include "ksnumbers.h" #include "ksutils.h" #include "ksfilereader.h" #include #include #include "kstars_debug.h" KSPlanet::OrbitDataManager KSPlanet::odm; KSPlanet::OrbitDataManager::OrbitDataManager() { //EMPTY } bool KSPlanet::OrbitDataManager::readOrbitData(const QString &fname, QVector *vector) { QFile f; if (KSUtils::openDataFile(f, fname)) { KSFileReader fileReader(f); // close file is included QStringList fields; while (fileReader.hasMoreLines()) { fields = fileReader.readLine().split(' ', QString::SkipEmptyParts); if (fields.size() == 3) { double A = fields[0].toDouble(); double B = fields[1].toDouble(); double C = fields[2].toDouble(); vector->append(OrbitData(A, B, C)); } } } else { return false; } return true; } bool KSPlanet::OrbitDataManager::loadData(KSPlanet::OrbitDataColl &odc, const QString &n) { QString fname, snum; QFile f; int nCount = 0; QString nl = n.toLower(); if (hash.contains(nl)) { odc = hash[nl]; return true; //orbit data already loaded } //Create a new OrbitDataColl OrbitDataColl ret; //Ecliptic Longitude for (int i = 0; i < 6; ++i) { snum.setNum(i); fname = nl + ".L" + snum + ".vsop"; if (readOrbitData(fname, &ret.Lon[i])) nCount++; } if (nCount == 0) return false; //Ecliptic Latitude for (int i = 0; i < 6; ++i) { snum.setNum(i); fname = nl + ".B" + snum + ".vsop"; if (readOrbitData(fname, &ret.Lat[i])) nCount++; } if (nCount == 0) return false; //Heliocentric Distance for (int i = 0; i < 6; ++i) { snum.setNum(i); fname = nl + ".R" + snum + ".vsop"; if (readOrbitData(fname, &ret.Dst[i])) nCount++; } if (nCount == 0) return false; hash[nl] = ret; odc = hash[nl]; return true; } KSPlanet::KSPlanet(const QString &s, const QString &imfile, const QColor &c, double pSize) : KSPlanetBase(s, imfile, c, pSize) { } KSPlanet::KSPlanet(int n) : KSPlanetBase() { switch (n) { case MERCURY: KSPlanetBase::init(i18n("Mercury"), "mercury", KSPlanetBase::planetColor[KSPlanetBase::MERCURY], 4879.4); break; case VENUS: KSPlanetBase::init(i18n("Venus"), "venus", KSPlanetBase::planetColor[KSPlanetBase::VENUS], 12103.6); break; case MARS: KSPlanetBase::init(i18n("Mars"), "mars", KSPlanetBase::planetColor[KSPlanetBase::MARS], 6792.4); break; case JUPITER: KSPlanetBase::init(i18n("Jupiter"), "jupiter", KSPlanetBase::planetColor[KSPlanetBase::JUPITER], 142984.); break; case SATURN: KSPlanetBase::init(i18n("Saturn"), "saturn", KSPlanetBase::planetColor[KSPlanetBase::SATURN], 120536.); break; case URANUS: KSPlanetBase::init(i18n("Uranus"), "uranus", KSPlanetBase::planetColor[KSPlanetBase::URANUS], 51118.); break; case NEPTUNE: KSPlanetBase::init(i18n("Neptune"), "neptune", KSPlanetBase::planetColor[KSPlanetBase::NEPTUNE], 49572.); break; default: qDebug() << "Error: Illegal identifier in KSPlanet constructor: " << n; break; } } KSPlanet *KSPlanet::clone() const { Q_ASSERT(typeid(this) == typeid(static_cast(this))); // Ensure we are not slicing a derived class return new KSPlanet(*this); } // TODO: Get rid of this dirty hack post KDE 4.2 release QString KSPlanet::untranslatedName() const { if (name() == i18n("Mercury")) return "Mercury"; else if (name() == i18n("Venus")) return "Venus"; else if (name() == i18n("Mars")) return "Mars"; else if (name() == i18n("Jupiter")) return "Jupiter"; else if (name() == i18n("Saturn")) return "Saturn"; else if (name() == i18n("Uranus")) return "Uranus"; else if (name() == i18n("Neptune")) return "Neptune"; + else if (name() == i18n("Earth")) + return "Earth"; + else if (name() == i18n("Earth Shadow")) + return "Earth Shadow"; + else if (name() == i18n("Moon")) + return "Moon"; + else if (name() == i18n("Sun")) + return "Sun"; else return name(); } //we don't need the reference to the ODC, so just give it a junk variable bool KSPlanet::loadData() { OrbitDataColl odc; return odm.loadData(odc, untranslatedName()); } void KSPlanet::calcEcliptic(double Tau, EclipticPosition &epret) const { double sum[6]; OrbitDataColl odc; double Tpow[6]; Tpow[0] = 1.0; for (int i = 1; i < 6; ++i) { Tpow[i] = Tpow[i - 1] * Tau; } if (!odm.loadData(odc, untranslatedName())) { epret.longitude = dms(0.0); epret.latitude = dms(0.0); epret.radius = 0.0; qCWarning(KSTARS) << "Could not get data for name:" << name() << "(" << untranslatedName() << ")" << endl; return; } //Ecliptic Longitude for (int i = 0; i < 6; ++i) { sum[i] = 0.0; for (int j = 0; j < odc.Lon[i].size(); ++j) { sum[i] += odc.Lon[i][j].A * cos(odc.Lon[i][j].B + odc.Lon[i][j].C * Tau); /* qDebug() << "sum[" << i <<"] =" << sum[i] << " A = " << odc.Lon[i][j].A << " B = " << odc.Lon[i][j].B << " C = " << odc.Lon[i][j].C << endl; */ } sum[i] *= Tpow[i]; //qDebug() << name() << " : sum[" << i << "] = " << sum[i]; } epret.longitude.setRadians(sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]); epret.longitude.setD(epret.longitude.reduce().Degrees()); //Compute Ecliptic Latitude for (uint i = 0; i < 6; ++i) { sum[i] = 0.0; for (int j = 0; j < odc.Lat[i].size(); ++j) { sum[i] += odc.Lat[i][j].A * cos(odc.Lat[i][j].B + odc.Lat[i][j].C * Tau); } sum[i] *= Tpow[i]; } epret.latitude.setRadians(sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]); //Compute Heliocentric Distance for (uint i = 0; i < 6; ++i) { sum[i] = 0.0; for (int j = 0; j < odc.Dst[i].size(); ++j) { sum[i] += odc.Dst[i][j].A * cos(odc.Dst[i][j].B + odc.Dst[i][j].C * Tau); } sum[i] *= Tpow[i]; } epret.radius = sum[0] + sum[1] + sum[2] + sum[3] + sum[4] + sum[5]; /* qDebug() << name() << " pre: Lat = " << epret.latitude.toDMSString() << " Long = " << epret.longitude.toDMSString() << " Dist = " << epret.radius << endl; */ } bool KSPlanet::findGeocentricPosition(const KSNumbers *num, const KSPlanetBase *Earth) { if (Earth != nullptr) { double sinL, sinL0, sinB, sinB0; double cosL, cosL0, cosB, cosB0; double x = 0.0, y = 0.0, z = 0.0; double olddst = -1000; double dst = 0; EclipticPosition trialpos; double jm = num->julianMillenia(); Earth->ecLong().SinCos(sinL0, cosL0); Earth->ecLat().SinCos(sinB0, cosB0); double eX = Earth->rsun() * cosB0 * cosL0; double eY = Earth->rsun() * cosB0 * sinL0; double eZ = Earth->rsun() * sinB0; bool once = true; while (fabs(dst - olddst) > .001) { calcEcliptic(jm, trialpos); // We store the heliocentric ecliptic coordinates the first time they are computed. if (once) { helEcPos = trialpos; once = false; } olddst = dst; trialpos.longitude.SinCos(sinL, cosL); trialpos.latitude.SinCos(sinB, cosB); x = trialpos.radius * cosB * cosL - eX; y = trialpos.radius * cosB * sinL - eY; z = trialpos.radius * sinB - eZ; //distance from Earth dst = sqrt(x * x + y * y + z * z); //The light-travel time delay, in millenia //0.0057755183 is the inverse speed of light, //in days/AU double delay = (.0057755183 * dst) / 365250.0; jm = num->julianMillenia() - delay; } ep.longitude.setRadians(atan2(y, x)); ep.longitude.reduce(); ep.latitude.setRadians(atan2(z, sqrt(x * x + y * y))); setRsun(trialpos.radius); setRearth(dst); EclipticToEquatorial(num->obliquity()); setRA0(ra()); setDec0(dec()); apparentCoord(J2000, lastPrecessJD); //nutate(num); //aberrate(num); } else { calcEcliptic(num->julianMillenia(), ep); helEcPos = ep; } //determine the position angle findPA(num); return true; } void KSPlanet::findMagnitude(const KSNumbers *num) { double cosDec, sinDec; dec().SinCos(cosDec, sinDec); /* Computation of the visual magnitude (V band) of the planet. * Algorithm provided by Pere Planesas (Observatorio Astronomico Nacional) * It has some similarity to J. Meeus algorithm in Astronomical Algorithms, Chapter 40. * */ // Initialized to the faintest magnitude observable with the HST float magnitude = 30; double param = 5 * log10(rsun() * rearth()); double phase = this->phase().Degrees(); double f1 = phase / 100.; if (name() == i18n("Mercury")) { if (phase > 150.) f1 = 1.5; magnitude = -0.36 + param + 3.8 * f1 - 2.73 * f1 * f1 + 2 * f1 * f1 * f1; } else if (name() == i18n("Venus")) { magnitude = -4.29 + param + 0.09 * f1 + 2.39 * f1 * f1 - 0.65 * f1 * f1 * f1; } else if (name() == i18n("Mars")) { magnitude = -1.52 + param + 0.016 * phase; } else if (name() == i18n("Jupiter")) { magnitude = -9.25 + param + 0.005 * phase; } else if (name() == i18n("Saturn")) { double T = num->julianCenturies(); double a0 = (40.66 - 4.695 * T) * dms::PI / 180.; double d0 = (83.52 + 0.403 * T) * dms::PI / 180.; double sinx = -cos(d0) * cosDec * cos(a0 - ra().radians()); sinx = fabs(sinx - sin(d0) * sinDec); double rings = -2.6 * sinx + 1.25 * sinx * sinx; magnitude = -8.88 + param + 0.044 * phase + rings; } else if (name() == i18n("Uranus")) { magnitude = -7.19 + param + 0.0028 * phase; } else if (name() == i18n("Neptune")) { magnitude = -6.87 + param; } setMag(magnitude); } SkyObject::UID KSPlanet::getUID() const { SkyObject::UID n; if (name() == i18n("Mercury")) { n = 1; } else if (name() == i18n("Venus")) { n = 2; } else if (name() == i18n("Earth")) { n = 3; } else if (name() == i18n("Mars")) { n = 4; } else if (name() == i18n("Jupiter")) { n = 5; } else if (name() == i18n("Saturn")) { n = 6; } else if (name() == i18n("Uranus")) { n = 7; } else if (name() == i18n("Neptune")) { n = 8; } else { return SkyObject::invalidUID; } return solarsysUID(UID_SOL_BIGOBJ) | n; } diff --git a/kstars/skyobjects/ksplanetbase.cpp b/kstars/skyobjects/ksplanetbase.cpp index 465cc3f40..a984ad065 100644 --- a/kstars/skyobjects/ksplanetbase.cpp +++ b/kstars/skyobjects/ksplanetbase.cpp @@ -1,308 +1,308 @@ /*************************************************************************** ksplanetbase.cpp - K Desktop Planetarium ------------------- begin : Sun Jul 22 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "ksplanetbase.h" #include "ksnumbers.h" #include "kstarsdata.h" #include "ksutils.h" #include "Options.h" #include "skymap.h" #include "ksasteroid.h" #include "kscomet.h" #include "ksmoon.h" #include "ksplanet.h" #include "kssun.h" #include "texturemanager.h" #include "skycomponents/skymapcomposite.h" QVector KSPlanetBase::planetColor = QVector() << QColor("slateblue") << //Mercury - QColor("lightgreen") << //Venus - QColor("red") << //Mars - QColor("goldenrod") << //Jupiter - QColor("khaki") << //Saturn - QColor("lightseagreen") << //Uranus - QColor("skyblue") << //Neptune - QColor("grey") << //Pluto - QColor("yellow") << //Sun - QColor("white"); //Moon + QColor("lightgreen") << //Venus + QColor("red") << //Mars + QColor("goldenrod") << //Jupiter + QColor("khaki") << //Saturn + QColor("lightseagreen") << //Uranus + QColor("skyblue") << //Neptune + QColor("grey") << //Pluto + QColor("yellow") << //Sun + QColor("white"); //Moon const SkyObject::UID KSPlanetBase::UID_SOL_BIGOBJ = 0; const SkyObject::UID KSPlanetBase::UID_SOL_ASTEROID = 1; const SkyObject::UID KSPlanetBase::UID_SOL_COMET = 2; KSPlanetBase::KSPlanetBase(const QString &s, const QString &image_file, const QColor &c, double pSize) : TrailObject(2, 0.0, 0.0, 0.0, s) { init(s, image_file, c, pSize); } void KSPlanetBase::init(const QString &s, const QString &image_file, const QColor &c, double pSize) { m_image = TextureManager::getImage(image_file); PositionAngle = 0.0; PhysicalSize = pSize; m_Color = c; setName(s); setLongName(s); } KSPlanetBase *KSPlanetBase::createPlanet(int n) { switch (n) { case KSPlanetBase::MERCURY: case KSPlanetBase::VENUS: case KSPlanetBase::MARS: case KSPlanetBase::JUPITER: case KSPlanetBase::SATURN: case KSPlanetBase::URANUS: case KSPlanetBase::NEPTUNE: return new KSPlanet(n); /*case KSPlanetBase::PLUTO: return new KSPluto(); break;*/ case KSPlanetBase::SUN: return new KSSun(); case KSPlanetBase::MOON: - return new KSMoon(); + return new KSMoon(); } return nullptr; } void KSPlanetBase::EquatorialToEcliptic(const CachingDms *Obliquity) { findEcliptic(Obliquity, ep.longitude, ep.latitude); } void KSPlanetBase::EclipticToEquatorial(const CachingDms *Obliquity) { setFromEcliptic(Obliquity, ep.longitude, ep.latitude); } void KSPlanetBase::updateCoords(const KSNumbers *num, bool includePlanets, const CachingDms *lat, const CachingDms *LST, bool) { KStarsData *kd = KStarsData::Instance(); if (kd == nullptr || !includePlanets) return; kd->skyComposite()->earth()->findPosition(num); //since we don't pass lat & LST, localizeCoords will be skipped if (lat && LST) { findPosition(num, lat, LST, kd->skyComposite()->earth()); // Don't add to the trail this time if (hasTrail()) Trail.takeLast(); } else { findGeocentricPosition(num, kd->skyComposite()->earth()); } } void KSPlanetBase::findPosition(const KSNumbers *num, const CachingDms *lat, const CachingDms *LST, const KSPlanetBase *Earth) { lastPrecessJD = num->julianDay(); findGeocentricPosition(num, Earth); //private function, reimplemented in each subclass findPhase(); setAngularSize(findAngularSize()); //angular size in arcmin if (lat && LST) localizeCoords(num, lat, LST); //correct for figure-of-the-Earth if (hasTrail()) { addToTrail(KStarsDateTime(num->getJD()).toString("yyyy.MM.dd hh:mm") + i18nc("Universal time", "UT")); // TODO: Localize date/time format? if (Trail.size() > TrailObject::MaxTrail) clipTrail(); } findMagnitude(num); if (type() == SkyObject::COMET) { // Compute tail size KSComet *me = static_cast(this); double comaAngSize; // Convert the tail size in km to angular tail size (degrees) comaAngSize = asin(physicalSize() / Rearth / AU_KM) * 60.0 * 180.0 / dms::PI; // Find the apparent length as projected on the celestial sphere (the comet's tail points away from the sun) me->setComaAngSize(comaAngSize * fabs(sin(phase().radians()))); } } bool KSPlanetBase::isMajorPlanet() const { if (name() == i18n("Mercury") || name() == i18n("Venus") || name() == i18n("Mars") || name() == i18n("Jupiter") || - name() == i18n("Saturn") || name() == i18n("Uranus") || name() == i18n("Neptune")) + name() == i18n("Saturn") || name() == i18n("Uranus") || name() == i18n("Neptune")) return true; return false; } void KSPlanetBase::localizeCoords(const KSNumbers *num, const CachingDms *lat, const CachingDms *LST) { //convert geocentric coordinates to local apparent coordinates (topocentric coordinates) dms HA, HA2; //Hour Angle, before and after correction double rsinp, rcosp, u, sinHA, cosHA, sinDec, cosDec, D; double cosHA2; double r = Rearth * AU_KM; //distance from Earth, in km u = atan(0.996647 * tan(lat->radians())); rsinp = 0.996647 * sin(u); rcosp = cos(u); HA.setD(LST->Degrees() - ra().Degrees()); HA.SinCos(sinHA, cosHA); dec().SinCos(sinDec, cosDec); D = atan2(rcosp * sinHA, r * cosDec / 6378.14 - rcosp * cosHA); dms temp; temp.setRadians(ra().radians() - D); setRA(temp); HA2.setD(LST->Degrees() - ra().Degrees()); cosHA2 = cos(HA2.radians()); //temp.setRadians( atan2( cosHA2*( r*sinDec/6378.14 - rsinp ), r*cosDec*cosHA/6378.14 - rcosp ) ); // The atan2() version above makes the planets move crazy in the htm branch -jbb temp.setRadians(atan(cosHA2 * (r * sinDec / 6378.14 - rsinp) / (r * cosDec * cosHA / 6378.14 - rcosp))); setDec(temp); //Make sure Dec is between -90 and +90 if (dec().Degrees() > 90.0) { setDec(180.0 - dec().Degrees()); setRA(ra().Hours() + 12.0); ra().reduce(); } if (dec().Degrees() < -90.0) { setDec(180.0 + dec().Degrees()); setRA(ra().Hours() + 12.0); ra().reduce(); } EquatorialToEcliptic(num->obliquity()); } void KSPlanetBase::setRearth(const KSPlanetBase *Earth) { double sinL, sinB, sinL0, sinB0; double cosL, cosB, cosL0, cosB0; double x, y, z; //The Moon's Rearth is set in its findGeocentricPosition()... - if (name() == "Moon") + if (name() == i18n("Moon")) { return; } - if (name() == "Earth") + if (name() == i18n("Earth")) { Rearth = 0.0; return; } if (!Earth) { qDebug() << "KSPlanetBase::setRearth(): Error: Need an Earth pointer. (" << name() << ")"; Rearth = 1.0; return; } Earth->ecLong().SinCos(sinL0, cosL0); Earth->ecLat().SinCos(sinB0, cosB0); double eX = Earth->rsun() * cosB0 * cosL0; double eY = Earth->rsun() * cosB0 * sinL0; double eZ = Earth->rsun() * sinB0; helEcLong().SinCos(sinL, cosL); helEcLat().SinCos(sinB, cosB); x = rsun() * cosB * cosL - eX; y = rsun() * cosB * sinL - eY; z = rsun() * sinB - eZ; Rearth = sqrt(x * x + y * y + z * z); //Set angular size, in arcmin AngularSize = asin(PhysicalSize / Rearth / AU_KM) * 60. * 180. / dms::PI; } void KSPlanetBase::findPA(const KSNumbers *num) { //Determine position angle of planet (assuming that it is aligned with //the Ecliptic, which is only roughly correct). //Displace a point along +Ecliptic Latitude by 1 degree SkyPoint test; dms newELat(ecLat().Degrees() + 1.0); test.setFromEcliptic(num->obliquity(), ecLong(), newELat); double dx = ra().Degrees() - test.ra().Degrees(); double dy = test.dec().Degrees() - dec().Degrees(); double pa; if (dy) { pa = atan2(dx, dy) * 180.0 / dms::PI; } else { pa = dx < 0 ? 90.0 : -90.0; } setPA(pa); } double KSPlanetBase::labelOffset() const { double size = angSize() * dms::PI * Options::zoomFactor() / 10800.0; //Determine minimum size for offset double minsize = 4.; if (type() == SkyObject::ASTEROID || type() == SkyObject::COMET) minsize = 2.; - if (name() == "Sun" || name() == "Moon") + if (name() == i18n("Sun") || name() == i18n("Moon")) minsize = 8.; if (size < minsize) size = minsize; //Inflate offset for Saturn if (name() == i18n("Saturn")) size = int(2.5 * size); return 0.5 * size + 4.; } void KSPlanetBase::findPhase() { - if (2*rsun()*rearth() == 0) + if (2 * rsun()*rearth() == 0) { Phase = std::numeric_limits::quiet_NaN(); return; } /* Compute the phase of the planet in degrees */ double earthSun = KStarsData::Instance()->skyComposite()->earth()->rsun(); double cosPhase = (rsun() * rsun() + rearth() * rearth() - earthSun * earthSun) / (2 * rsun() * rearth()); Phase = acos(cosPhase) * 180.0 / dms::PI; /* More elegant way of doing it, but requires the Sun. TODO: Switch to this if and when we make KSSun a singleton */ // Phase = ecLong()->Degrees() - Sun->ecLong()->Degrees(); } diff --git a/kstars/skyobjects/satellite.cpp b/kstars/skyobjects/satellite.cpp index f7a0a9559..67a197917 100644 --- a/kstars/skyobjects/satellite.cpp +++ b/kstars/skyobjects/satellite.cpp @@ -1,1346 +1,1346 @@ /*************************************************************************** satellite.cpp - K Desktop Planetarium ------------------- begin : Tue 02 Mar 2011 copyright : (C) 2009 by Jerome SONRIER email : jsid@emor3j.fr.eu.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "satellite.h" #include "ksplanetbase.h" #ifndef KSTARS_LITE #include "kspopupmenu.h" #endif #include "kstarsdata.h" #include "kssun.h" #include "Options.h" #include "skymapcomposite.h" #include #include #include // Define some constants // WGS-72 constants #define RADIUSEARTHKM 6378.135 // Earth radius (km) #define XKE 0.07436691613317 // 60.0 / sqrt(RADIUSEARTHKM^3/MU) #define J2 0.001082616 // The second gravitational zonal harmonic of the Earth #define J4 -0.00000165597 // The fourth gravitational zonal harmonic of the Earth #define J3OJ2 -2.34506972e-3 // J3 / J2 // Mathematical constants #define TWOPI 6.2831853071795864769 // 2*PI #define PIO2 1.5707963267948966192 // PI/2 #define X2O3 .66666666666666666667 // 2/3 #define DEG2RAD 1.745329251994330e-2 // Deg -> Rad // Other constants #define MINPD 1440 // Minutes per day #define MEANALT 0.84 // Mean altitude (km) #define SR 6.96000e5 // Solar radius - km (IAU 76) #define AU 1.49597870691e8 // Astronomical unit - km (IAU 76) #define XPDOTP 229.1831180523293 // 1440.0 / (2.0 * pi) #define SS 1.0122292801892716288 // Parameter for the SGP4 density function #define QZMS2T 1.8802791590152706439e-9 // (( 120.0 - 78.0) / RADIUSEARTHKM )^4 #define F 3.35281066474748e-3 // Flattening factor #define MFACTOR 7.292115e-5 Satellite::Satellite(const QString &name, const QString &line1, const QString &line2) { //m_name = name; m_number = line1.midRef(2, 5).toInt(); m_class = line1.at(7); m_id = line1.mid(9, 8); m_epoch = line1.midRef(18, 14).toDouble(); m_first_deriv = line1.midRef(33, 10).toDouble() / (XPDOTP * MINPD); m_second_deriv = line1.midRef(44, 6).toDouble() * (1.0e-5 / pow(10.0, line1.midRef(51, 1).toDouble())) / (XPDOTP * MINPD * MINPD); m_bstar = line1.midRef(53, 6).toDouble() * 1.0e-5 / pow(10.0, line1.midRef(60, 1).toDouble()); m_ephem_type = line1.midRef(62, 1).toInt(); m_elem_number = line1.midRef(64, 4).toInt(); m_inclination = line2.midRef(8, 8).toDouble() * DEG2RAD; m_ra = line2.midRef(17, 8).toDouble() * DEG2RAD; m_eccentricity = line2.midRef(26, 7).toDouble() * 1.0e-7; m_arg_perigee = line2.midRef(34, 8).toDouble() * DEG2RAD; m_mean_anomaly = line2.midRef(43, 8).toDouble() * DEG2RAD; m_mean_motion = line2.midRef(52, 11).toDouble() * TWOPI / MINPD; m_nb_revolution = line2.midRef(63, 5).toInt(); setName(name); setName2(name); setLongName(name + " (" + m_id + ')'); setType(SkyObject::SATELLITE); setMag(0.0); m_is_selected = Options::selectedSatellites().contains(name); // Convert TLE epoch to Julian date double day = modf(m_epoch * 1.e-3, &m_epoch_year) * 1.e3; if (m_epoch_year < 57.) m_epoch_year += 2000.; else m_epoch_year += 1900.; double year = m_epoch_year - 1.; long i = year / 100; long A = i; i = A / 4; long B = 2 - A + i; i = 365.25 * year; i += 30.6001 * 14; m_tle_jd = i + 1720994.5 + B + day; init(); } Satellite *Satellite::clone() const { Q_ASSERT(typeid(this) == typeid(static_cast(this))); // Ensure we are not slicing a derived class return new Satellite(*this); } void Satellite::init() { double ao, cosio, sinio, cosio2, omeosq, posq, rp, rteosq, eccsq, con42, cnodm, snodm, cosim, sinim, cosomm, sinomm, - cc1sq, cc2, cc3, coef, coef1, cosio4, day, em, emsq, eeta, etasq, gam, inclm, nm, perige, pinvsq, psisq, - qzms24, rtemsq, s1, s2, s3, s4, s5, s6, s7, sfour, ss1(0), ss2(0), ss3(0), ss4(0), ss5(0), ss6(0), ss7(0), - sz1(0), sz2(0), sz3(0), sz11(0), sz12(0), sz13(0), sz21(0), sz22(0), sz23(0), sz31(0), sz32(0), sz33(0), tc, - temp, temp1, temp2, temp3, tsi, xpidot, xhdot1, z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33, ak, d1, - del, adel, po, ds70, ts70, tfrac, c1, thgr70, fk5r, c1p2p; -// double dndt; + cc1sq, cc2, cc3, coef, coef1, cosio4, day, em, emsq, eeta, etasq, gam, inclm, nm, perige, pinvsq, psisq, + qzms24, rtemsq, s1, s2, s3, s4, s5, s6, s7, sfour, ss1(0), ss2(0), ss3(0), ss4(0), ss5(0), ss6(0), ss7(0), + sz1(0), sz2(0), sz3(0), sz11(0), sz12(0), sz13(0), sz21(0), sz22(0), sz23(0), sz31(0), sz32(0), sz33(0), tc, + temp, temp1, temp2, temp3, tsi, xpidot, xhdot1, z1, z2, z3, z11, z12, z13, z21, z22, z23, z31, z32, z33, ak, d1, + del, adel, po, ds70, ts70, tfrac, c1, thgr70, fk5r, c1p2p; + // double dndt; // Init near earth variables isimp = false; aycof = 0.; con41 = 0.; cc1 = 0.; cc4 = 0.; cc5 = 0.; d2 = 0.; d3 = 0.; d4 = 0.; delmo = 0.; eta = 0.; argpdot = 0.; omgcof = 0.; sinmao = 0.; t = 0.; t2cof = 0.; t3cof = 0.; t4cof = 0.; t5cof = 0.; x1mth2 = 0.; x7thm1 = 0.; mdot = 0.; nodedot = 0.; xlcof = 0.; xmcof = 0.; nodecf = 0.; // Init deep space variables irez = 0; d2201 = 0.; d2211 = 0.; d3210 = 0.; d3222 = 0.; d4410 = 0.; d4422 = 0.; d5220 = 0.; d5232 = 0.; d5421 = 0.; d5433 = 0.; dedt = 0.; del1 = 0.; del2 = 0.; del3 = 0.; didt = 0.; dmdt = 0.; dnodt = 0.; domdt = 0.; e3 = 0.; ee2 = 0.; peo = 0.; pgho = 0.; pho = 0.; pinco = 0.; plo = 0.; se2 = 0.; se3 = 0.; sgh2 = 0.; sgh3 = 0.; sgh4 = 0.; sh2 = 0.; sh3 = 0.; si2 = 0.; si3 = 0.; sl2 = 0.; sl3 = 0.; sl4 = 0.; gsto = 0.; xfact = 0.; xgh2 = 0.; xgh3 = 0.; xgh4 = 0.; xh2 = 0.; xh3 = 0.; xi2 = 0.; xi3 = 0.; xl2 = 0.; xl3 = 0.; xl4 = 0.; xlamo = 0.; zmol = 0.; zmos = 0.; atime = 0.; xli = 0.; xni = 0.; method = 'n'; m_is_visible = false; // Divisor for divide by zero check on inclination const double temp4 = 1.5e-12; /*----- Initializes variables for sgp4 -----*/ // Calculate auxiliary epoch quantities eccsq = m_eccentricity * m_eccentricity; omeosq = 1.0 - eccsq; rteosq = sqrt(omeosq); cosio = cos(m_inclination); cosio2 = cosio * cosio; // Un-kozai the mean motion ak = pow(XKE / m_mean_motion, X2O3); d1 = 0.75 * J2 * (3.0 * cosio2 - 1.0) / (rteosq * omeosq); del = d1 / (ak * ak); adel = ak * (1.0 - del * del - del * (1.0 / 3.0 + 134.0 * del * del / 81.0)); del = d1 / (adel * adel); m_mean_motion = m_mean_motion / (1.0 + del); ao = pow(XKE / m_mean_motion, X2O3); sinio = sin(m_inclination); po = ao * omeosq; con42 = 1.0 - 5.0 * cosio2; con41 = -con42 - (2.0 * cosio2); posq = po * po; rp = ao * (1.0 - m_eccentricity); method = 'n'; // Find sidereal time ts70 = m_tle_jd - 2433281.5 - 7305.0; ds70 = floor(ts70 + 1.0e-8); tfrac = ts70 - ds70; // find greenwich location at epoch c1 = 1.72027916940703639e-2; thgr70 = 1.7321343856509374; fk5r = 5.07551419432269442e-15; c1p2p = c1 + TWOPI; gsto = fmod(thgr70 + c1 * ds70 + c1p2p * tfrac + ts70 * ts70 * fk5r, TWOPI); if (gsto < 0.0) gsto = gsto + TWOPI; if ((omeosq >= 0.0) || (m_mean_motion >= 0.0)) { if (rp < (220.0 / RADIUSEARTHKM + 1.0)) isimp = true; sfour = SS; qzms24 = QZMS2T; perige = (rp - 1.0) * RADIUSEARTHKM; // For perigees below 156 km, s and qoms2t are altered if (perige < 156.0) { sfour = perige - 78.0; if (perige < 98.0) sfour = 20.0; qzms24 = pow(((120.0 - sfour) / RADIUSEARTHKM), 4.0); sfour = sfour / RADIUSEARTHKM + 1.0; } pinvsq = 1.0 / posq; tsi = 1.0 / (ao - sfour); eta = ao * m_eccentricity * tsi; etasq = eta * eta; eeta = m_eccentricity * eta; psisq = fabs(1.0 - etasq); coef = qzms24 * pow(tsi, 4.0); coef1 = coef / pow(psisq, 3.5); cc2 = coef1 * m_mean_motion * - (ao * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq)) + - 0.375 * J2 * tsi / psisq * con41 * (8.0 + 3.0 * etasq * (8.0 + etasq))); + (ao * (1.0 + 1.5 * etasq + eeta * (4.0 + etasq)) + + 0.375 * J2 * tsi / psisq * con41 * (8.0 + 3.0 * etasq * (8.0 + etasq))); cc1 = m_bstar * cc2; cc3 = 0.0; if (m_eccentricity > 1.0e-4) cc3 = -2.0 * coef * tsi * J3OJ2 * m_mean_motion * sinio / m_eccentricity; x1mth2 = 1.0 - cosio2; cc4 = 2.0 * m_mean_motion * coef1 * ao * omeosq * - (eta * (2.0 + 0.5 * etasq) + m_eccentricity * (0.5 + 2.0 * etasq) - - J2 * tsi / (ao * psisq) * - (-3.0 * con41 * (1.0 - 2.0 * eeta + etasq * (1.5 - 0.5 * eeta)) + - 0.75 * x1mth2 * (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * m_arg_perigee))); + (eta * (2.0 + 0.5 * etasq) + m_eccentricity * (0.5 + 2.0 * etasq) - + J2 * tsi / (ao * psisq) * + (-3.0 * con41 * (1.0 - 2.0 * eeta + etasq * (1.5 - 0.5 * eeta)) + + 0.75 * x1mth2 * (2.0 * etasq - eeta * (1.0 + etasq)) * cos(2.0 * m_arg_perigee))); cc5 = 2.0 * coef1 * ao * omeosq * (1.0 + 2.75 * (etasq + eeta) + eeta * etasq); cosio4 = cosio2 * cosio2; temp1 = 1.5 * J2 * pinvsq * m_mean_motion; temp2 = 0.5 * temp1 * J2 * pinvsq; temp3 = -0.46875 * J4 * pinvsq * pinvsq * m_mean_motion; mdot = m_mean_motion + 0.5 * temp1 * rteosq * con41 + - 0.0625 * temp2 * rteosq * (13.0 - 78.0 * cosio2 + 137.0 * cosio4); + 0.0625 * temp2 * rteosq * (13.0 - 78.0 * cosio2 + 137.0 * cosio4); argpdot = -0.5 * temp1 * con42 + 0.0625 * temp2 * (7.0 - 114.0 * cosio2 + 395.0 * cosio4) + temp3 * (3.0 - 36.0 * cosio2 + 49.0 * cosio4); xhdot1 = -temp1 * cosio; nodedot = xhdot1 + (0.5 * temp2 * (4.0 - 19.0 * cosio2) + 2.0 * temp3 * (3.0 - 7.0 * cosio2)) * cosio; xpidot = argpdot + nodedot; omgcof = m_bstar * cc3 * cos(m_arg_perigee); xmcof = 0.0; if (m_eccentricity > 1.0e-4) xmcof = -X2O3 * coef * m_bstar / eeta; nodecf = 3.5 * omeosq * xhdot1 * cc1; t2cof = 1.5 * cc1; // Do not divide by zero if (fabs(1.0 + cosio) > 1.5e-12) xlcof = -0.25 * J3OJ2 * sinio * (3.0 + 5.0 * cosio) / (1.0 + cosio); else xlcof = -0.25 * J3OJ2 * sinio * (3.0 + 5.0 * cosio) / temp4; aycof = -0.5 * J3OJ2 * sinio; delmo = pow((1.0 + eta * cos(m_mean_anomaly)), 3); sinmao = sin(m_mean_anomaly); x7thm1 = 7.0 * cosio2 - 1.0; // Deep space initialization if ((TWOPI / m_mean_motion) >= 225.0) { method = 'd'; isimp = true; tc = 0.0; inclm = m_inclination; // Init deep space common variables // Define some constants const double zes = 0.01675; const double zel = 0.05490; const double c1ss = 2.9864797e-6; const double c1l = 4.7968065e-7; const double zsinis = 0.39785416; const double zcosis = 0.91744867; const double zcosgs = 0.1945905; const double zsings = -0.98088458; int lsflg; double a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, betasq, cc, ctem, stem, x1, x2, x3, x4, x5, x6, x7, x8, - xnodce, xnoi, zcosg, zcosgl, zcosh, zcoshl, zcosi, zcosil, zsing, zsingl, zsinh, zsinhl, zsini, zsinil, - zx, zy; + xnodce, xnoi, zcosg, zcosgl, zcosh, zcoshl, zcosi, zcosil, zsing, zsingl, zsinh, zsinhl, zsini, zsinil, + zx, zy; nm = m_mean_motion; em = m_eccentricity; snodm = sin(m_ra); cnodm = cos(m_ra); sinomm = sin(m_arg_perigee); cosomm = cos(m_arg_perigee); sinim = sin(m_inclination); cosim = cos(m_inclination); emsq = em * em; betasq = 1.0 - emsq; rtemsq = sqrt(betasq); // Initialize lunar solar terms peo = 0.0; pinco = 0.0; plo = 0.0; pgho = 0.0; pho = 0.0; day = m_tle_jd - 2433281.5 + 18261.5 + tc / 1440.0; xnodce = fmod(4.5236020 - 9.2422029e-4 * day, TWOPI); stem = sin(xnodce); ctem = cos(xnodce); zcosil = 0.91375164 - 0.03568096 * ctem; zsinil = sqrt(1.0 - zcosil * zcosil); zsinhl = 0.089683511 * stem / zsinil; zcoshl = sqrt(1.0 - zsinhl * zsinhl); gam = 5.8351514 + 0.0019443680 * day; zx = 0.39785416 * stem / zsinil; zy = zcoshl * ctem + 0.91744867 * zsinhl * stem; zx = atan2(zx, zy); zx = gam + zx - xnodce; zcosgl = cos(zx); zsingl = sin(zx); // Solar terms zcosg = zcosgs; zsing = zsings; zcosi = zcosis; zsini = zsinis; zcosh = cnodm; zsinh = snodm; cc = c1ss; xnoi = 1.0 / nm; for (lsflg = 1; lsflg <= 2; ++lsflg) { a1 = zcosg * zcosh + zsing * zcosi * zsinh; a3 = -zsing * zcosh + zcosg * zcosi * zsinh; a7 = -zcosg * zsinh + zsing * zcosi * zcosh; a8 = zsing * zsini; a9 = zsing * zsinh + zcosg * zcosi * zcosh; a10 = zcosg * zsini; a2 = cosim * a7 + sinim * a8; a4 = cosim * a9 + sinim * a10; a5 = -sinim * a7 + cosim * a8; a6 = -sinim * a9 + cosim * a10; x1 = a1 * cosomm + a2 * sinomm; x2 = a3 * cosomm + a4 * sinomm; x3 = -a1 * sinomm + a2 * cosomm; x4 = -a3 * sinomm + a4 * cosomm; x5 = a5 * sinomm; x6 = a6 * sinomm; x7 = a5 * cosomm; x8 = a6 * cosomm; z31 = 12.0 * x1 * x1 - 3.0 * x3 * x3; z32 = 24.0 * x1 * x2 - 6.0 * x3 * x4; z33 = 12.0 * x2 * x2 - 3.0 * x4 * x4; z1 = 3.0 * (a1 * a1 + a2 * a2) + z31 * emsq; z2 = 6.0 * (a1 * a3 + a2 * a4) + z32 * emsq; z3 = 3.0 * (a3 * a3 + a4 * a4) + z33 * emsq; z11 = -6.0 * a1 * a5 + emsq * (-24.0 * x1 * x7 - 6.0 * x3 * x5); z12 = -6.0 * (a1 * a6 + a3 * a5) + emsq * (-24.0 * (x2 * x7 + x1 * x8) - 6.0 * (x3 * x6 + x4 * x5)); z13 = -6.0 * a3 * a6 + emsq * (-24.0 * x2 * x8 - 6.0 * x4 * x6); z21 = 6.0 * a2 * a5 + emsq * (24.0 * x1 * x5 - 6.0 * x3 * x7); z22 = 6.0 * (a4 * a5 + a2 * a6) + emsq * (24.0 * (x2 * x5 + x1 * x6) - 6.0 * (x4 * x7 + x3 * x8)); z23 = 6.0 * a4 * a6 + emsq * (24.0 * x2 * x6 - 6.0 * x4 * x8); z1 = z1 + z1 + betasq * z31; z2 = z2 + z2 + betasq * z32; z3 = z3 + z3 + betasq * z33; s3 = cc * xnoi; s2 = -0.5 * s3 / rtemsq; s4 = s3 * rtemsq; s1 = -15.0 * em * s4; s5 = x1 * x3 + x2 * x4; s6 = x2 * x3 + x1 * x4; s7 = x2 * x4 - x1 * x3; // Lunar terms if (lsflg == 1) { ss1 = s1; ss2 = s2; ss3 = s3; ss4 = s4; ss5 = s5; ss6 = s6; ss7 = s7; sz1 = z1; sz2 = z2; sz3 = z3; sz11 = z11; sz12 = z12; sz13 = z13; sz21 = z21; sz22 = z22; sz23 = z23; sz31 = z31; sz32 = z32; sz33 = z33; zcosg = zcosgl; zsing = zsingl; zcosi = zcosil; zsini = zsinil; zcosh = zcoshl * cnodm + zsinhl * snodm; zsinh = snodm * zcoshl - cnodm * zsinhl; cc = c1l; } } zmol = fmod(4.7199672 + 0.22997150 * day - gam, TWOPI); zmos = fmod(6.2565837 + 0.017201977 * day, TWOPI); // Solar terms se2 = 2.0 * ss1 * ss6; se3 = 2.0 * ss1 * ss7; si2 = 2.0 * ss2 * sz12; si3 = 2.0 * ss2 * (sz13 - sz11); sl2 = -2.0 * ss3 * sz2; sl3 = -2.0 * ss3 * (sz3 - sz1); sl4 = -2.0 * ss3 * (-21.0 - 9.0 * emsq) * zes; sgh2 = 2.0 * ss4 * sz32; sgh3 = 2.0 * ss4 * (sz33 - sz31); sgh4 = -18.0 * ss4 * zes; sh2 = -2.0 * ss2 * sz22; sh3 = -2.0 * ss2 * (sz23 - sz21); // Lunar terms ee2 = 2.0 * s1 * s6; e3 = 2.0 * s1 * s7; xi2 = 2.0 * s2 * z12; xi3 = 2.0 * s2 * (z13 - z11); xl2 = -2.0 * s3 * z2; xl3 = -2.0 * s3 * (z3 - z1); xl4 = -2.0 * s3 * (-21.0 - 9.0 * emsq) * zel; xgh2 = 2.0 * s4 * z32; xgh3 = 2.0 * s4 * (z33 - z31); xgh4 = -18.0 * s4 * zel; xh2 = -2.0 * s2 * z22; xh3 = -2.0 * s2 * (z23 - z21); // Apply deep space long period periodic contributions to the mean elements -// double f2, f3, sinzf, zf, zm; + // double f2, f3, sinzf, zf, zm; double ses, sghl, sghs, shll, shs, sis, sls; // Define some constants const double zns = 1.19459e-5; const double znl = 1.5835218e-4; // Calculate time varying periodics // These results are never used, should we remove them? -// zm = zmos; -// zf = zm + 2.0 * zes * sin(zm); -// sinzf = sin(zf); -// f2 = 0.5 * sinzf * sinzf - 0.25; -// f3 = -0.5 * sinzf * cos(zf); -// ses = se2 * f2 + se3 * f3; -// sis = si2 * f2 + si3 * f3; -// sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf; -// sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf; -// shs = sh2 * f2 + sh3 * f3; -// zm = zmol; - -// zf = zm + 2.0 * zel * sin(zm); -// sinzf = sin(zf); -// f2 = 0.5 * sinzf * sinzf - 0.25; -// f3 = -0.5 * sinzf * cos(zf); -// sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf; -// shll = xh2 * f2 + xh3 * f3; + // zm = zmos; + // zf = zm + 2.0 * zes * sin(zm); + // sinzf = sin(zf); + // f2 = 0.5 * sinzf * sinzf - 0.25; + // f3 = -0.5 * sinzf * cos(zf); + // ses = se2 * f2 + se3 * f3; + // sis = si2 * f2 + si3 * f3; + // sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf; + // sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf; + // shs = sh2 * f2 + sh3 * f3; + // zm = zmol; + + // zf = zm + 2.0 * zel * sin(zm); + // sinzf = sin(zf); + // f2 = 0.5 * sinzf * sinzf - 0.25; + // f3 = -0.5 * sinzf * cos(zf); + // sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf; + // shll = xh2 * f2 + xh3 * f3; // Deep space contributions to mean motion dot due to geopotential resonance with half day and one day orbits double ainv2, aonv = 0.0, cosisq, eoc, f220, f221, f311, f321, f322, f330, f441, f442, f522, f523, f542, f543, g200, g201, g211, g300, g310, g322, g410, g422, g520, g521, g532, g533, sgs, sini2, temp, temp1, theta, xno2, emsqo; -// double emo; + // double emo; // Define some constant const double q22 = 1.7891679e-6; const double q31 = 2.1460748e-6; const double q33 = 2.2123015e-7; const double root22 = 1.7891679e-6; const double root44 = 7.3636953e-9; const double root54 = 2.1765803e-9; const double rptim = 4.37526908801129966e-3; // this equates to 7.29211514668855e-5 rad/sec const double root32 = 3.7393792e-7; const double root52 = 1.1428639e-7; // Deep space initialization irez = 0; if ((nm < 0.0052359877) && (nm > 0.0034906585)) irez = 1; if ((nm >= 8.26e-3) && (nm <= 9.24e-3) && (em >= 0.5)) irez = 2; // Solar terms ses = ss1 * zns * ss5; sis = ss2 * zns * (sz11 + sz13); sls = -zns * ss3 * (sz1 + sz3 - 14.0 - 6.0 * emsq); sghs = ss4 * zns * (sz31 + sz33 - 6.0); shs = -zns * ss2 * (sz21 + sz23); if ((inclm < 5.2359877e-2) || (inclm > M_PI - 5.2359877e-2)) shs = 0.0; if (sinim != 0.0) shs = shs / sinim; sgs = sghs - cosim * shs; // Lunar terms dedt = ses + s1 * znl * s5; didt = sis + s2 * znl * (z11 + z13); dmdt = sls - znl * s3 * (z1 + z3 - 14.0 - 6.0 * emsq); sghl = s4 * znl * (z31 + z33 - 6.0); shll = -znl * s2 * (z21 + z23); if ((inclm < 5.2359877e-2) || (inclm > M_PI - 5.2359877e-2)) shll = 0.0; domdt = sgs + sghl; dnodt = shs; if (sinim != 0.0) { domdt = domdt - cosim / sinim * shll; dnodt = dnodt + shll / sinim; } // Calculate deep space resonance effects // Value never used -// dndt = 0.0; + // dndt = 0.0; theta = fmod(gsto + tc * rptim, TWOPI); // Initialize the resonance terms if (irez != 0) { aonv = pow(nm / XKE, X2O3); // Geopotential resonance for 12 hour orbits if (irez == 2) { cosisq = cosim * cosim; // Value never used -// emo = em; + // emo = em; em = m_eccentricity; emsqo = emsq; emsq = eccsq; eoc = em * emsq; g201 = -0.306 - (em - 0.64) * 0.440; if (em <= 0.65) { g211 = 3.616 - 13.2470 * em + 16.2900 * emsq; g310 = -19.302 + 117.3900 * em - 228.4190 * emsq + 156.5910 * eoc; g322 = -18.9068 + 109.7927 * em - 214.6334 * emsq + 146.5816 * eoc; g410 = -41.122 + 242.6940 * em - 471.0940 * emsq + 313.9530 * eoc; g422 = -146.407 + 841.8800 * em - 1629.014 * emsq + 1083.4350 * eoc; g520 = -532.114 + 3017.977 * em - 5740.032 * emsq + 3708.2760 * eoc; } else { g211 = -72.099 + 331.819 * em - 508.738 * emsq + 266.724 * eoc; g310 = -346.844 + 1582.851 * em - 2415.925 * emsq + 1246.113 * eoc; g322 = -342.585 + 1554.908 * em - 2366.899 * emsq + 1215.972 * eoc; g410 = -1052.797 + 4758.686 * em - 7193.992 * emsq + 3651.957 * eoc; g422 = -3581.690 + 16178.110 * em - 24462.770 * emsq + 12422.520 * eoc; if (em > 0.715) g520 = -5149.66 + 29936.92 * em - 54087.36 * emsq + 31324.56 * eoc; else g520 = 1464.74 - 4664.75 * em + 3763.64 * emsq; } if (em < 0.7) { g533 = -919.22770 + 4988.6100 * em - 9064.7700 * emsq + 5542.21 * eoc; g521 = -822.71072 + 4568.6173 * em - 8491.4146 * emsq + 5337.524 * eoc; g532 = -853.66600 + 4690.2500 * em - 8624.7700 * emsq + 5341.4 * eoc; } else { g533 = -37995.780 + 161616.52 * em - 229838.20 * emsq + 109377.94 * eoc; g521 = -51752.104 + 218913.95 * em - 309468.16 * emsq + 146349.42 * eoc; g532 = -40023.880 + 170470.89 * em - 242699.48 * emsq + 115605.82 * eoc; } sini2 = sinim * sinim; f220 = 0.75 * (1.0 + 2.0 * cosim + cosisq); f221 = 1.5 * sini2; f321 = 1.875 * sinim * (1.0 - 2.0 * cosim - 3.0 * cosisq); f322 = -1.875 * sinim * (1.0 + 2.0 * cosim - 3.0 * cosisq); f441 = 35.0 * sini2 * f220; f442 = 39.3750 * sini2 * sini2; f522 = 9.84375 * sinim * (sini2 * (1.0 - 2.0 * cosim - 5.0 * cosisq) + 0.33333333 * (-2.0 + 4.0 * cosim + 6.0 * cosisq)); f523 = sinim * (4.92187512 * sini2 * (-2.0 - 4.0 * cosim + 10.0 * cosisq) + - 6.56250012 * (1.0 + 2.0 * cosim - 3.0 * cosisq)); + 6.56250012 * (1.0 + 2.0 * cosim - 3.0 * cosisq)); f542 = 29.53125 * sinim * (2.0 - 8.0 * cosim + cosisq * (-12.0 + 8.0 * cosim + 10.0 * cosisq)); f543 = 29.53125 * sinim * (-2.0 - 8.0 * cosim + cosisq * (12.0 + 8.0 * cosim - 10.0 * cosisq)); xno2 = nm * nm; ainv2 = aonv * aonv; temp1 = 3.0 * xno2 * ainv2; temp = temp1 * root22; d2201 = temp * f220 * g201; d2211 = temp * f221 * g211; temp1 = temp1 * aonv; temp = temp1 * root32; d3210 = temp * f321 * g310; d3222 = temp * f322 * g322; temp1 = temp1 * aonv; temp = 2.0 * temp1 * root44; d4410 = temp * f441 * g410; d4422 = temp * f442 * g422; temp1 = temp1 * aonv; temp = temp1 * root52; d5220 = temp * f522 * g520; d5232 = temp * f523 * g532; temp = 2.0 * temp1 * root54; d5421 = temp * f542 * g521; d5433 = temp * f543 * g533; xlamo = fmod(m_mean_anomaly + m_ra + m_ra - theta - theta, TWOPI); xfact = mdot + dmdt + 2.0 * (nodedot + dnodt - rptim) - m_mean_motion; // Value never used -// em = emo; + // em = emo; emsq = emsqo; } if (irez == 1) { g200 = 1.0 + emsq * (-2.5 + 0.8125 * emsq); g310 = 1.0 + 2.0 * emsq; g300 = 1.0 + emsq * (-6.0 + 6.60937 * emsq); f220 = 0.75 * (1.0 + cosim) * (1.0 + cosim); f311 = 0.9375 * sinim * sinim * (1.0 + 3.0 * cosim) - 0.75 * (1.0 + cosim); f330 = 1.0 + cosim; f330 = 1.875 * f330 * f330 * f330; del1 = 3.0 * nm * nm * aonv * aonv; del2 = 2.0 * del1 * f220 * g200 * q22; del3 = 3.0 * del1 * f330 * g300 * q33 * aonv; del1 = del1 * f311 * g310 * q31 * aonv; xlamo = fmod(m_mean_anomaly + m_ra + m_arg_perigee - theta, TWOPI); xfact = mdot + xpidot - rptim + dmdt + domdt + dnodt - m_mean_motion; } xli = xlamo; xni = m_mean_motion; atime = 0.0; // Value never used -// nm = m_mean_motion + dndt; + // nm = m_mean_motion + dndt; } } // Set variables if not deep space if (!isimp) { cc1sq = cc1 * cc1; d2 = 4.0 * ao * tsi * cc1sq; temp = d2 * tsi * cc1 / 3.0; d3 = (17.0 * ao + sfour) * temp; d4 = 0.5 * temp * ao * tsi * (221.0 * ao + 31.0 * sfour) * cc1; t3cof = d2 + 2.0 * cc1sq; t4cof = 0.25 * (3.0 * d3 + cc1 * (12.0 * d2 + 10.0 * cc1sq)); t5cof = 0.2 * (3.0 * d4 + 12.0 * cc1 * d3 + 6.0 * d2 * d2 + 15.0 * cc1sq * (2.0 * d2 + cc1sq)); } } } int Satellite::updatePos() { KStarsData *data = KStarsData::Instance(); return sgp4((data->clock()->utc().djd() - m_tle_jd) * MINPD); } int Satellite::sgp4(double tsince) { KStarsData *data = KStarsData::Instance(); int ktr; double am, axnl, aynl, betal, cosim, cnod, cos2u, coseo1 = 0, cosi, cosip, cosisq, cossu, cosu, delm, delomg, em, - ecose, el2, eo1, ep, esine, argpm, argpp, argpdf, pl, - mrt = 0.0, mvt, rdotl, rl, rvdot, rvdotl, sinim, dndt, sin2u, sineo1 = 0, sini, sinip, sinsu, sinu, snod, su, t2, - t3, t4, tem5, temp, temp1, temp2, tempa, tempe, templ, u, ux, uy, uz, vx, vy, vz, inclm, mm, nm, nodem, xinc, - xincp, xl, xlm, mp, xmdf, xmx, xmy, nodedf, xnode, nodep, tc, sat_posx, sat_posy, sat_posz, sat_posw, sat_velx, - sat_vely, sat_velz, sinlat, obs_posx, obs_posy, obs_posz, obs_posw, /*obs_velx, obs_vely, obs_velz,*/ - coslat, thetageo, sintheta, costheta, c, sq, achcp, vkmpersec; -// double emsq; + ecose, el2, eo1, ep, esine, argpm, argpp, argpdf, pl, + mrt = 0.0, mvt, rdotl, rl, rvdot, rvdotl, sinim, dndt, sin2u, sineo1 = 0, sini, sinip, sinsu, sinu, snod, su, t2, + t3, t4, tem5, temp, temp1, temp2, tempa, tempe, templ, u, ux, uy, uz, vx, vy, vz, inclm, mm, nm, nodem, xinc, + xincp, xl, xlm, mp, xmdf, xmx, xmy, nodedf, xnode, nodep, tc, sat_posx, sat_posy, sat_posz, sat_posw, sat_velx, + sat_vely, sat_velz, sinlat, obs_posx, obs_posy, obs_posz, obs_posw, /*obs_velx, obs_vely, obs_velz,*/ + coslat, thetageo, sintheta, costheta, c, sq, achcp, vkmpersec; + // double emsq; const double temp4 = 1.5e-12; double jul_utc = data->clock()->utc().djd(); vkmpersec = RADIUSEARTHKM * XKE / 60.0; // Update for secular gravity and atmospheric drag xmdf = m_mean_anomaly + mdot * tsince; argpdf = m_arg_perigee + argpdot * tsince; nodedf = m_ra + nodedot * tsince; argpm = argpdf; mm = xmdf; t2 = tsince * tsince; nodem = nodedf + nodecf * t2; tempa = 1.0 - cc1 * tsince; tempe = m_bstar * cc4 * tsince; templ = t2cof * t2; if (!isimp) { delomg = omgcof * tsince; delm = xmcof * (pow((1.0 + eta * cos(xmdf)), 3) - delmo); temp = delomg + delm; mm = xmdf + temp; argpm = argpdf - temp; t3 = t2 * tsince; t4 = t3 * tsince; tempa = tempa - d2 * t2 - d3 * t3 - d4 * t4; tempe = tempe + m_bstar * cc5 * (sin(mm) - sinmao); templ = templ + t3cof * t3 + t4 * (t4cof + tsince * t5cof); } nm = m_mean_motion; em = m_eccentricity; inclm = m_inclination; if (method == 'd') { tc = tsince; // Deep space contributions to mean elements for perturbing third body int iretn; double delt, ft, theta, x2li, x2omi, xl, xldot, xnddt, xndt, xomi; // Define some constants const double fasx2 = 0.13130908; const double fasx4 = 2.8843198; const double fasx6 = 0.37448087; const double g22 = 5.7686396; const double g32 = 0.95240898; const double g44 = 1.8014998; const double g52 = 1.0508330; const double g54 = 4.4108898; const double rptim = 4.37526908801129966e-3; // this equates to 7.29211514668855e-5 rad/sec const double step = 720.0; const double step2 = step * step / 2; // Calculate deep space resonance effects // Value never used -// dndt = 0.0; + // dndt = 0.0; theta = fmod(gsto + tc * rptim, TWOPI); em = em + dedt * tsince; inclm = inclm + didt * tsince; argpm = argpm + domdt * tsince; nodem = nodem + dnodt * tsince; mm = mm + dmdt * tsince; // Update resonances : numerical (euler-maclaurin) integration ft = 0.0; if (irez != 0) { if ((atime == 0.0) || (tsince * atime <= 0.0) || (fabs(tsince) < fabs(atime))) { atime = 0.0; xni = m_mean_motion; xli = xlamo; } if (tsince > 0.0) delt = step; else delt = -step; iretn = 381; // added for do loop while (iretn == 381) { // Near - synchronous resonance terms if (irez != 2) { xndt = del1 * sin(xli - fasx2) + del2 * sin(2.0 * (xli - fasx4)) + del3 * sin(3.0 * (xli - fasx6)); xldot = xni + xfact; xnddt = del1 * cos(xli - fasx2) + 2.0 * del2 * cos(2.0 * (xli - fasx4)) + 3.0 * del3 * cos(3.0 * (xli - fasx6)); xnddt = xnddt * xldot; } else { // Near - half-day resonance terms xomi = m_arg_perigee + argpdot * atime; x2omi = xomi + xomi; x2li = xli + xli; xndt = d2201 * sin(x2omi + xli - g22) + d2211 * sin(xli - g22) + d3210 * sin(xomi + xli - g32) + - d3222 * sin(-xomi + xli - g32) + d4410 * sin(x2omi + x2li - g44) + d4422 * sin(x2li - g44) + - d5220 * sin(xomi + xli - g52) + d5232 * sin(-xomi + xli - g52) + - d5421 * sin(xomi + x2li - g54) + d5433 * sin(-xomi + x2li - g54); + d3222 * sin(-xomi + xli - g32) + d4410 * sin(x2omi + x2li - g44) + d4422 * sin(x2li - g44) + + d5220 * sin(xomi + xli - g52) + d5232 * sin(-xomi + xli - g52) + + d5421 * sin(xomi + x2li - g54) + d5433 * sin(-xomi + x2li - g54); xldot = xni + xfact; xnddt = d2201 * cos(x2omi + xli - g22) + d2211 * cos(xli - g22) + d3210 * cos(xomi + xli - g32) + d3222 * cos(-xomi + xli - g32) + d5220 * cos(xomi + xli - g52) + d5232 * cos(-xomi + xli - g52) + 2.0 * (d4410 * cos(x2omi + x2li - g44) + d4422 * cos(x2li - g44) + d5421 * cos(xomi + x2li - g54) + d5433 * cos(-xomi + x2li - g54)); xnddt = xnddt * xldot; } if (fabs(tsince - atime) >= step) { iretn = 381; } else { ft = tsince - atime; iretn = 0; } if (iretn == 381) { xli = xli + xldot * delt + xndt * step2; xni = xni + xndt * delt + xnddt * step2; atime = atime + delt; } } nm = xni + xndt * ft + xnddt * ft * ft * 0.5; xl = xli + xldot * ft + xndt * ft * ft * 0.5; if (irez != 1) { mm = xl - 2.0 * nodem + 2.0 * theta; dndt = nm - m_mean_motion; } else { mm = xl - nodem - argpm + theta; dndt = nm - m_mean_motion; } nm = m_mean_motion + dndt; } } if (nm <= 0.0) { qDebug() << "Mean motion less than 0.0"; return (2); } am = pow((XKE / nm), X2O3) * tempa * tempa; nm = XKE / pow(am, 1.5); em = em - tempe; if ((em >= 1.0) || (em < -0.001)) { qDebug() << "Eccentricity >= 1.0 or < -0.001"; return (1); } if (em < 1.0e-6) em = 1.0e-6; mm = mm + m_mean_motion * templ; xlm = mm + argpm + nodem; // Value never used -// emsq = em * em; + // emsq = em * em; // Value never used -// temp = 1.0 - emsq; + // temp = 1.0 - emsq; nodem = fmod(nodem, TWOPI); argpm = fmod(argpm, TWOPI); xlm = fmod(xlm, TWOPI); mm = fmod(xlm - argpm - nodem, TWOPI); // Compute extra mean quantities sinim = sin(inclm); cosim = cos(inclm); // Add lunar-solar periodics ep = em; xincp = inclm; argpp = argpm; nodep = nodem; mp = mm; sinip = sinim; cosip = cosim; if (method == 'd') { double alfdp, betdp, cosip, cosop, dalf, dbet, dls, f2, f3, pe, pgh, ph, pinc, pl, sel, ses, sghl, sghs, shll, - shs, sil, sinip, sinop, sinzf, sis, sll, sls, xls, xnoh, zf, zm; + shs, sil, sinip, sinop, sinzf, sis, sll, sls, xls, xnoh, zf, zm; // Define some constants const double zns = 1.19459e-5; const double zes = 0.01675; const double znl = 1.5835218e-4; const double zel = 0.05490; // Calculate time varying periodics zm = zmos + zns * tsince; zf = zm + 2.0 * zes * sin(zm); sinzf = sin(zf); f2 = 0.5 * sinzf * sinzf - 0.25; f3 = -0.5 * sinzf * cos(zf); ses = se2 * f2 + se3 * f3; sis = si2 * f2 + si3 * f3; sls = sl2 * f2 + sl3 * f3 + sl4 * sinzf; sghs = sgh2 * f2 + sgh3 * f3 + sgh4 * sinzf; shs = sh2 * f2 + sh3 * f3; zm = zmol + znl * tsince; zf = zm + 2.0 * zel * sin(zm); sinzf = sin(zf); f2 = 0.5 * sinzf * sinzf - 0.25; f3 = -0.5 * sinzf * cos(zf); sel = ee2 * f2 + e3 * f3; sil = xi2 * f2 + xi3 * f3; sll = xl2 * f2 + xl3 * f3 + xl4 * sinzf; sghl = xgh2 * f2 + xgh3 * f3 + xgh4 * sinzf; shll = xh2 * f2 + xh3 * f3; pe = ses + sel; pinc = sis + sil; pl = sls + sll; pgh = sghs + sghl; ph = shs + shll; pe = pe - peo; pinc = pinc - pinco; pl = pl - plo; pgh = pgh - pgho; ph = ph - pho; xincp = xincp + pinc; ep = ep + pe; sinip = sin(xincp); cosip = cos(xincp); // Apply periodics directly if (xincp >= 0.2) { ph = ph / sinip; pgh = pgh - cosip * ph; argpp = argpp + pgh; nodep = nodep + ph; mp = mp + pl; } else { // Apply periodics with lyddane modification sinop = sin(nodep); cosop = cos(nodep); alfdp = sinip * sinop; betdp = sinip * cosop; dalf = ph * cosop + pinc * cosip * sinop; dbet = -ph * sinop + pinc * cosip * cosop; alfdp = alfdp + dalf; betdp = betdp + dbet; nodep = fmod(nodep, TWOPI); if (nodep < 0.0) nodep += TWOPI; xls = mp + argpp + cosip * nodep; dls = pl + pgh - pinc * nodep * sinip; xls = xls + dls; xnoh = nodep; nodep = atan2(alfdp, betdp); if ((nodep < 0.0)) nodep += TWOPI; if (fabs(xnoh - nodep) > M_PI) { if (nodep < xnoh) nodep += TWOPI; else nodep -= TWOPI; } mp = mp + pl; argpp = xls - mp - cosip * nodep; } if (xincp < 0.0) { xincp = -xincp; nodep = nodep + M_PI; argpp = argpp - M_PI; } if ((ep < 0.0) || (ep > 1.0)) { qDebug() << "Eccentricity < 0.0 or > 1.0"; return (3); } } // Long period periodics if (method == 'd') { sinip = sin(xincp); cosip = cos(xincp); aycof = -0.5 * J3OJ2 * sinip; if (fabs(cosip + 1.0) > 1.5e-12) xlcof = -0.25 * J3OJ2 * sinip * (3.0 + 5.0 * cosip) / (1.0 + cosip); else xlcof = -0.25 * J3OJ2 * sinip * (3.0 + 5.0 * cosip) / temp4; } axnl = ep * cos(argpp); temp = 1.0 / (am * (1.0 - ep * ep)); aynl = ep * sin(argpp) + temp * aycof; xl = mp + argpp + nodep + temp * xlcof * axnl; // Solve kepler's equation u = fmod(xl - nodep, TWOPI); eo1 = u; tem5 = 9999.9; ktr = 1; while ((fabs(tem5) >= 1.0e-12) && (ktr <= 10)) { sineo1 = sin(eo1); coseo1 = cos(eo1); tem5 = 1.0 - coseo1 * axnl - sineo1 * aynl; tem5 = (u - aynl * coseo1 + axnl * sineo1 - eo1) / tem5; if (fabs(tem5) >= 0.95) tem5 = tem5 > 0.0 ? 0.95 : -0.95; eo1 = eo1 + tem5; ktr = ktr + 1; } // Short period preliminary quantities ecose = axnl * coseo1 + aynl * sineo1; esine = axnl * sineo1 - aynl * coseo1; el2 = axnl * axnl + aynl * aynl; pl = am * (1.0 - el2); if (pl < 0.0) { qDebug() << "Semi-latus rectum < 0.0"; return (4); } rl = am * (1.0 - ecose); rdotl = sqrt(am) * esine / rl; rvdotl = sqrt(pl) / rl; betal = sqrt(1.0 - el2); temp = esine / (1.0 + betal); sinu = am / rl * (sineo1 - aynl - axnl * temp); cosu = am / rl * (coseo1 - axnl + aynl * temp); su = atan2(sinu, cosu); sin2u = (cosu + cosu) * sinu; cos2u = 1.0 - 2.0 * sinu * sinu; temp = 1.0 / pl; temp1 = 0.5 * J2 * temp; temp2 = temp1 * temp; // Update for short period periodics if (method == 'd') { cosisq = cosip * cosip; con41 = 3.0 * cosisq - 1.0; x1mth2 = 1.0 - cosisq; x7thm1 = 7.0 * cosisq - 1.0; } mrt = rl * (1.0 - 1.5 * temp2 * betal * con41) + 0.5 * temp1 * x1mth2 * cos2u; su = su - 0.25 * temp2 * x7thm1 * sin2u; xnode = nodep + 1.5 * temp2 * cosip * sin2u; xinc = xincp + 1.5 * temp2 * cosip * sinip * cos2u; mvt = rdotl - nm * temp1 * x1mth2 * sin2u / XKE; rvdot = rvdotl + nm * temp1 * (x1mth2 * cos2u + 1.5 * con41) / XKE; // Orientation vectors sinsu = sin(su); cossu = cos(su); snod = sin(xnode); cnod = cos(xnode); sini = sin(xinc); cosi = cos(xinc); xmx = -snod * cosi; xmy = cnod * cosi; ux = xmx * sinsu + cnod * cossu; uy = xmy * sinsu + snod * cossu; uz = sini * sinsu; vx = xmx * cossu - cnod * sinsu; vy = xmy * cossu - snod * sinsu; vz = sini * cossu; // Position and velocity (in km and km/sec) sat_posx = (mrt * ux) * RADIUSEARTHKM; sat_posy = (mrt * uy) * RADIUSEARTHKM; sat_posz = (mrt * uz) * RADIUSEARTHKM; sat_posw = sqrt(sat_posx * sat_posx + sat_posy * sat_posy + sat_posz * sat_posz); sat_velx = (mvt * ux + rvdot * vx) * vkmpersec; sat_vely = (mvt * uy + rvdot * vy) * vkmpersec; sat_velz = (mvt * uz + rvdot * vz) * vkmpersec; m_velocity = sqrt(sat_velx * sat_velx + sat_vely * sat_vely + sat_velz * sat_velz); // printf("tsince=%.15f\n", tsince); // printf("sat_posx=%.15f\n", sat_posx); // printf("sat_posy=%.15f\n", sat_posy); // printf("sat_posz=%.15f\n", sat_posz); // printf("sat_velx=%.15f\n", sat_velx); // printf("sat_vely=%.15f\n", sat_vely); // printf("sat_velz=%.15f\n", sat_velz); if (mrt < 1.0) { qDebug() << "Satellite has decayed"; return (6); } // Observer ECI position and velocity sinlat = sin(data->geo()->lat()->radians()); coslat = cos(data->geo()->lat()->radians()); thetageo = data->geo()->LMST(jul_utc); sintheta = sin(thetageo); costheta = cos(thetageo); c = 1.0 / sqrt(1.0 + F * (F - 2.0) * sinlat * sinlat); sq = (1.0 - F) * (1.0 - F) * c; achcp = (RADIUSEARTHKM * c + MEANALT) * coslat; obs_posx = achcp * costheta; obs_posy = achcp * sintheta; obs_posz = (RADIUSEARTHKM * sq + MEANALT) * sinlat; obs_posw = sqrt(obs_posx * obs_posx + obs_posy * sat_posy + obs_posz * obs_posz); /*obs_velx = -MFACTOR * obs_posy; obs_vely = MFACTOR * obs_posx; obs_velz = 0.;*/ m_altitude = sat_posw - obs_posw + MEANALT; // Az and Dec double range_posx = sat_posx - obs_posx; double range_posy = sat_posy - obs_posy; double range_posz = sat_posz - obs_posz; m_range = sqrt(range_posx * range_posx + range_posy * range_posy + range_posz * range_posz); // double range_velx = sat_velx - obs_velx; // double range_vely = sat_velx - obs_vely; // double range_velz = sat_velx - obs_velz; double top_s = sinlat * costheta * range_posx + sinlat * sintheta * range_posy - coslat * range_posz; double top_e = -sintheta * range_posx + costheta * range_posy; double top_z = coslat * costheta * range_posx + coslat * sintheta * range_posy + sinlat * range_posz; double azimuth = atan(-top_e / top_s); if (top_s > 0.) azimuth += M_PI; if (azimuth < 0.) azimuth += TWOPI; double elevation = arcSin(top_z / m_range); // printf("azimuth=%.15f\n\r", azimuth / DEG2RAD); // printf("elevation=%.15f\n\r", elevation / DEG2RAD); setAz(azimuth / DEG2RAD); setAlt(elevation / DEG2RAD); HorizontalToEquatorial(data->lst(), data->geo()->lat()); // is the satellite visible ? // Find ECI coordinates of the sun double mjd, year, T, M, L, e, C, O, Lsa, nu, R, eps; mjd = jul_utc - 2415020.0; year = 1900.0 + mjd / 365.25; T = (mjd + deltaET(year) / (MINPD * 60.0)) / 36525.0; M = DEG2RAD * (Modulus(358.47583 + Modulus(35999.04975 * T, 360.0) - (0.000150 + 0.0000033 * T) * T * T, 360.0)); L = DEG2RAD * (Modulus(279.69668 + Modulus(36000.76892 * T, 360.0) + 0.0003025 * T * T, 360.0)); e = 0.01675104 - (0.0000418 + 0.000000126 * T) * T; C = DEG2RAD * ((1.919460 - (0.004789 + 0.000014 * T) * T) * sin(M) + (0.020094 - 0.000100 * T) * sin(2 * M) + - 0.000293 * sin(3 * M)); + 0.000293 * sin(3 * M)); O = DEG2RAD * (Modulus(259.18 - 1934.142 * T, 360.0)); Lsa = Modulus(L + C - DEG2RAD * (0.00569 - 0.00479 * sin(O)), TWOPI); nu = Modulus(M + C, TWOPI); R = 1.0000002 * (1.0 - e * e) / (1.0 + e * cos(nu)); eps = DEG2RAD * (23.452294 - (0.0130125 + (0.00000164 - 0.000000503 * T) * T) * T + 0.00256 * cos(O)); R = AU * R; double sun_posx = R * cos(Lsa); double sun_posy = R * sin(Lsa) * cos(eps); double sun_posz = R * sin(Lsa) * sin(eps); double sun_posw = R; // Calculates satellite's eclipse status and depth double sd_sun, sd_earth, delta, depth; // Determine partial eclipse sd_earth = arcSin(RADIUSEARTHKM / sat_posw); double rho_x = sun_posx - sat_posx; double rho_y = sun_posy - sat_posy; double rho_z = sun_posz - sat_posz; double rho_w = sqrt(rho_x * rho_x + rho_y * rho_y + rho_z * rho_z); sd_sun = arcSin(SR / rho_w); double earth_x = -1.0 * sat_posx; double earth_y = -1.0 * sat_posy; double earth_z = -1.0 * sat_posz; double earth_w = sat_posw; delta = PIO2 - arcSin((sun_posx * earth_x + sun_posy * earth_y + sun_posz * earth_z) / (sun_posw * earth_w)); depth = sd_earth - sd_sun - delta; - KSSun *sun = (KSSun *)data->skyComposite()->findByName("Sun"); + KSSun *sun = dynamic_cast(data->skyComposite()->findByName(i18n("Sun"))); m_is_eclipsed = sd_earth >= sd_sun && depth >= 0; m_is_visible = !m_is_eclipsed && sun->alt().Degrees() <= -12.0 && elevation >= 0.0; return (0); } QString Satellite::sgp4ErrorString(int code) { switch (code) { case 0: return i18n("Success"); case 1: case 3: return i18n("Eccentricity >= 1.0 or < -0.001"); case 2: return i18n("Mean motion less than 0.0"); case 4: return i18n("Semi-latus rectum < 0.0"); case 6: return i18n("Satellite has decayed"); default: return i18n("Unknown error"); } } double Satellite::arcSin(double arg) { if (fabs(arg) >= 1.) if (arg > 0.) return PIO2; else if (arg < 0.) return -PIO2; else return 0.; else return (atan(arg / sqrt(1. - arg * arg))); } double Satellite::deltaET(double year) { double delta_et; delta_et = 26.465 + 0.747622 * (year - 1950) + 1.886913 * sin(TWOPI * (year - 1975) / 33); return delta_et; } double Satellite::Modulus(double arg1, double arg2) { int i; double ret_val; ret_val = arg1; i = ret_val / arg2; ret_val -= i * arg2; if (ret_val < 0.0) ret_val += arg2; return ret_val; } bool Satellite::isVisible() { return m_is_visible; } bool Satellite::selected() { return m_is_selected; } void Satellite::setSelected(bool selected) { m_is_selected = selected; } void Satellite::initPopupMenu(KSPopupMenu *pmenu) { #ifndef KSTARS_LITE pmenu->createSatelliteMenu(this); #else Q_UNUSED(pmenu); #endif } double Satellite::velocity() { return m_velocity; } double Satellite::altitude() { return m_altitude; } double Satellite::range() { return m_range; } QString Satellite::id() { return m_id; } diff --git a/kstars/skyobjects/skyobject.cpp b/kstars/skyobjects/skyobject.cpp index 809d68f5f..e45757a82 100644 --- a/kstars/skyobjects/skyobject.cpp +++ b/kstars/skyobjects/skyobject.cpp @@ -1,554 +1,554 @@ /*************************************************************************** skyobject.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "skyobject.h" #include "geolocation.h" #include "ksnumbers.h" #include "kspaths.h" #ifdef KSTARS_LITE #include "skymaplite.h" #else #include "kspopupmenu.h" #include "skymap.h" #endif #include "kstarsdata.h" #include "Options.h" #include "starobject.h" #include "skycomponents/skylabeler.h" QString SkyObject::emptyString; QString SkyObject::unnamedString = QString(I18N_NOOP("unnamed")); QString SkyObject::unnamedObjectString = QString(I18N_NOOP("unnamed object")); QString SkyObject::starString = QString("star"); const SkyObject::UID SkyObject::invalidUID = ~0; const SkyObject::UID SkyObject::UID_STAR = 0; const SkyObject::UID SkyObject::UID_GALAXY = 1; const SkyObject::UID SkyObject::UID_DEEPSKY = 2; const SkyObject::UID SkyObject::UID_SOLARSYS = 3; SkyObject::SkyObject(int t, dms r, dms d, float m, const QString &n, const QString &n2, const QString &lname) : SkyPoint(r, d), info() { setType(t); sortMagnitude = m; setName(n); setName2(n2); setLongName(lname); } SkyObject::SkyObject(int t, double r, double d, float m, const QString &n, const QString &n2, const QString &lname) : SkyPoint(r, d), info() { setType(t); sortMagnitude = m; setName(n); setName2(n2); setLongName(lname); } SkyObject *SkyObject::clone() const { Q_ASSERT(typeid(this) == typeid(static_cast(this))); // Ensure we are not slicing a derived class return new SkyObject(*this); } void SkyObject::showPopupMenu(KSPopupMenu *pmenu, const QPoint &pos) { #if defined(KSTARS_LITE) Q_UNUSED(pos) Q_UNUSED(pmenu); #else initPopupMenu(pmenu); pmenu->popup(pos); #endif } void SkyObject::initPopupMenu(KSPopupMenu *pmenu) { #ifdef KSTARS_LITE Q_UNUSED(pmenu) #else pmenu->createEmptyMenu(this); #endif } void SkyObject::setLongName(const QString &longname) { if (longname.isEmpty()) { if (hasName()) LongName = name(); else if (hasName2()) LongName = name2(); else LongName.clear(); } else { LongName = longname; } } QTime SkyObject::riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact) const { // If this object does not rise or set, return an invalid time SkyPoint p = recomputeCoords(dt, geo); if (p.checkCircumpolar(geo->lat())) return QTime(); //First of all, if the object is below the horizon at date/time dt, adjust the time //to bring it above the horizon KStarsDateTime dt2 = dt; dms lst(geo->GSTtoLST(dt.gst())); p.EquatorialToHorizontal(&lst, geo->lat()); if (p.alt().Degrees() < 0.0) { if (p.az().Degrees() < 180.0) //object has not risen yet { dt2 = dt.addSecs(12. * 3600.); // Move forward 12 hours, to a time when it has already risen } else //object has already set { dt2 = dt.addSecs(-12. * 3600.); // Move backward 12 hours, to a time when it has not yet set } } // The addition / subtraction of 12 hours ensures that we always // compute the _closest_ rise time and the _closest_ set time to // the current time. QTime rstUt = riseSetTimeUT(dt2, geo, rst, exact); if (!rstUt.isValid()) return QTime(); return geo->UTtoLT(KStarsDateTime(dt2.date(), rstUt)).time(); } QTime SkyObject::riseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, bool riseT, bool exact) const { // First trial to calculate UT QTime UT = auxRiseSetTimeUT(dt, geo, &ra(), &dec(), riseT); // We iterate once more using the calculated UT to compute again // the ra and dec for that time and hence the rise/set time. // Also, adjust the date by +/- 1 day, if necessary // By adding this +/- 1 day, we are double-checking that the // reported rise-time is the _already_ (last) risen time, and that // the reported set-time is the _future_ (next) set time // // However, issues with this are taken care of in // SkyObject::riseSetTime() KStarsDateTime dt0 = dt; dt0.setTime(UT); if (riseT && dt0 > dt) { dt0 = dt0.addDays(-1); } else if (!riseT && dt0 < dt) { dt0 = dt0.addDays(1); } SkyPoint sp = recomputeCoords(dt0, geo); UT = auxRiseSetTimeUT(dt0, geo, &sp.ra(), &sp.dec(), riseT); if (exact) { // We iterate a second time (For the Moon the second iteration changes // aprox. 1.5 arcmin the coordinates). dt0.setTime(UT); sp = recomputeCoords(dt0, geo); UT = auxRiseSetTimeUT(dt0, geo, &sp.ra(), &sp.dec(), riseT); } return UT; } QTime SkyObject::auxRiseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, const dms *righta, const dms *decl, bool riseT) const { dms LST = auxRiseSetTimeLST(geo->lat(), righta, decl, riseT); return dt.GSTtoUT(geo->LSTtoGST(LST)); } dms SkyObject::auxRiseSetTimeLST(const dms *gLat, const dms *righta, const dms *decl, bool riseT) const { dms h0 = elevationCorrection(); double H = approxHourAngle(&h0, gLat, decl); dms LST; if (riseT) LST.setH(24.0 + righta->Hours() - H / 15.0); else LST.setH(righta->Hours() + H / 15.0); return LST.reduce(); } dms SkyObject::riseSetTimeAz(const KStarsDateTime &dt, const GeoLocation *geo, bool riseT) const { dms Azimuth; double AltRad, AzRad; double sindec, cosdec, sinlat, coslat, sinHA, cosHA; double sinAlt, cosAlt; QTime UT = riseSetTimeUT(dt, geo, riseT); KStarsDateTime dt0 = dt; dt0.setTime(UT); SkyPoint sp = recomputeCoords(dt0, geo); dms LST = auxRiseSetTimeLST(geo->lat(), &sp.ra0(), &sp.dec0(), riseT); dms HourAngle = dms(LST.Degrees() - sp.ra0().Degrees()); geo->lat()->SinCos(sinlat, coslat); dec().SinCos(sindec, cosdec); HourAngle.SinCos(sinHA, cosHA); sinAlt = sindec * sinlat + cosdec * coslat * cosHA; AltRad = asin(sinAlt); cosAlt = cos(AltRad); AzRad = acos((sindec - sinlat * sinAlt) / (coslat * cosAlt)); if (sinHA > 0.0) AzRad = 2.0 * dms::PI - AzRad; // resolve acos() ambiguity Azimuth.setRadians(AzRad); return Azimuth; } QTime SkyObject::transitTimeUT(const KStarsDateTime &dt, const GeoLocation *geo) const { dms LST = geo->GSTtoLST(dt.gst()); //dSec is the number of seconds until the object transits. dms HourAngle = dms(LST.Degrees() - ra().Degrees()); int dSec = int(-3600. * HourAngle.Hours()); //dt0 is the first guess at the transit time. KStarsDateTime dt0 = dt.addSecs(dSec); //recompute object's position at UT0 and then find //transit time of this refined position SkyPoint sp = recomputeCoords(dt0, geo); HourAngle = dms(LST.Degrees() - sp.ra().Degrees()); dSec = int(-3600. * HourAngle.Hours()); return dt.addSecs(dSec).time(); } QTime SkyObject::transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const { return geo->UTtoLT(KStarsDateTime(dt.date(), transitTimeUT(dt, geo))).time(); } dms SkyObject::transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const { KStarsDateTime dt0 = dt; dt0.setTime(transitTimeUT(dt, geo)); SkyPoint sp = recomputeCoords(dt0, geo); double delta = 90 - geo->lat()->Degrees() + sp.dec().Degrees(); if (delta > 90) delta = 180 - delta; return dms(delta); } double SkyObject::approxHourAngle(const dms *h0, const dms *gLat, const dms *dec) const { double sh0 = sin(h0->radians()); double r = (sh0 - sin(gLat->radians()) * sin(dec->radians())) / (cos(gLat->radians()) * cos(dec->radians())); double H = acos(r) / dms::DegToRad; return H; } dms SkyObject::elevationCorrection(void) const { /* The atmospheric refraction at the horizon shifts altitude by * - 34 arcmin = 0.5667 degrees. This value changes if the observer * is above the horizon, or if the weather conditions change much. * * For the sun we have to add half the angular sie of the body, since * the sunset is the time the upper limb of the sun disappears below * the horizon, and dawn, when the upper part of the limb appears * over the horizon. The angular size of the sun = angular size of the * moon = 31' 59''. * * So for the sun the correction is = -34 - 16 = 50 arcmin = -0.8333 * * This same correction should be applied to the moon however parallax * is important here. Meeus states that the correction should be * 0.7275 P - 34 arcmin, where P is the moon's horizontal parallax. * He proposes a mean value of 0.125 degrees if no great accuracy * is needed. */ - if (name() == "Sun" || name() == "Moon" || name() == "Earth Shadow") + if (name() == i18n("Sun") || name() == i18n("Moon") || name() == i18n("Earth Shadow")) return dms(-0.8333); // else if ( name() == "Moon" ) // return dms(0.125); else // All sources point-like. return dms(-0.5667); } SkyPoint SkyObject::recomputeCoords(const KStarsDateTime &dt, const GeoLocation *geo) const { // Create a clone SkyObject *c = this->clone(); // compute coords of the copy for new time jd KSNumbers num(dt.djd()); // Note: isSolarSystem() below should give the same result on this // and c. The only very minor reason to prefer this is so that we // have an additional layer of warnings about subclasses of // KSPlanetBase that do not implement SkyObject::clone() due to // the passing of lat and LST if (isSolarSystem() && geo) { CachingDms LST = geo->GSTtoLST(dt.gst()); c->updateCoords(&num, true, geo->lat(), &LST); } else { c->updateCoords(&num); } // Transfer the coordinates into a SkyPoint SkyPoint p = *c; // Delete the clone delete c; // Return the SkyPoint return p; } SkyPoint SkyObject::recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const { Q_ASSERT(geo); SkyPoint ret = recomputeCoords(dt, geo); CachingDms LST = geo->GSTtoLST(dt.gst()); ret.EquatorialToHorizontal(&LST, geo->lat()); return ret; } QString SkyObject::typeName(int t) { switch (t) { case STAR: return i18n("Star"); case CATALOG_STAR: return i18n("Catalog Star"); case PLANET: return i18n("Planet"); case OPEN_CLUSTER: return i18n("Open Cluster"); case GLOBULAR_CLUSTER: return i18n("Globular Cluster"); case GASEOUS_NEBULA: return i18n("Gaseous Nebula"); case PLANETARY_NEBULA: return i18n("Planetary Nebula"); case SUPERNOVA_REMNANT: return i18n("Supernova Remnant"); case GALAXY: return i18n("Galaxy"); case COMET: return i18n("Comet"); case ASTEROID: return i18n("Asteroid"); case CONSTELLATION: return i18n("Constellation"); case MOON: return i18n("Moon"); case GALAXY_CLUSTER: return i18n("Galaxy Cluster"); case SATELLITE: return i18n("Satellite"); case SUPERNOVA: return i18n("Supernova"); case RADIO_SOURCE: return i18n("Radio Source"); case ASTERISM: return i18n("Asterism"); case DARK_NEBULA: return i18n("Dark Nebula"); case QUASAR: return i18n("Quasar"); case MULT_STAR: return i18n("Multiple Star"); default: return i18n("Unknown Type"); } } QString SkyObject::typeName() const { return typeName(Type); } QString SkyObject::messageFromTitle(const QString &imageTitle) const { QString message = imageTitle; //HST Image if (imageTitle == i18n("Show HST Image") || imageTitle.contains("HST")) { message = i18n("%1: Hubble Space Telescope, operated by STScI for NASA [public domain]", longname()); //Spitzer Image } else if (imageTitle.contains(i18n("Show Spitzer Image"))) { message = i18n("%1: Spitzer Space Telescope, courtesy NASA/JPL-Caltech [public domain]", longname()); //SEDS Image } else if (imageTitle == i18n("Show SEDS Image")) { message = i18n("%1: SEDS, http://www.seds.org [free for non-commercial use]", longname()); //Kitt Peak AOP Image } else if (imageTitle == i18n("Show KPNO AOP Image")) { message = i18n("%1: Advanced Observing Program at Kitt Peak National Observatory [free for non-commercial use; " "no physical reproductions]", longname()); //NOAO Image } else if (imageTitle.contains(i18n("Show NOAO Image"))) { message = i18n("%1: National Optical Astronomy Observatories and AURA [free for non-commercial use]", longname()); //VLT Image } else if (imageTitle.contains("VLT")) { message = i18n("%1: Very Large Telescope, operated by the European Southern Observatory [free for " "non-commercial use; no reproductions]", longname()); //All others } else if (imageTitle.startsWith(i18n("Show"))) { message = imageTitle.mid(imageTitle.indexOf(" ") + 1); //eat first word, "Show" message = longname() + ": " + message; } return message; } //TODO: Should create a special UserLog widget that encapsulates the "default" //message in the widget when no log exists (much like we do with dmsBox now) void SkyObject::saveUserLog(const QString &newLog) { QFile file; QString logs; //existing logs //Do nothing if: //+ new log is the "default" message //+ new log is empty if (newLog == (i18n("Record here observation logs and/or data on %1.", name())) || newLog.isEmpty()) return; // header label QString KSLabel = "[KSLABEL:" + name() + ']'; //However, we can't accept a star name if it has a greek letter in it: if (type() == STAR) { StarObject *star = (StarObject *)this; if (name() == star->gname()) KSLabel = "[KSLABEL:" + star->gname(false) + ']'; //"false": spell out greek letter } file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "userlog.dat"); //determine filename in local user KDE directory tree. if (file.open(QIODevice::ReadOnly)) { QTextStream instream(&file); // read all data into memory logs = instream.readAll(); file.close(); } //Remove old log entry from the logs text if (!userLog().isEmpty()) { int startIndex, endIndex; QString sub; startIndex = logs.indexOf(KSLabel); sub = logs.mid(startIndex); endIndex = sub.indexOf("[KSLogEnd]"); logs.remove(startIndex, endIndex + 11); } //append the new log entry to the end of the logs text logs.append(KSLabel + '\n' + newLog + "\n[KSLogEnd]\n"); //Open file for writing if (!file.open(QIODevice::WriteOnly)) { qDebug() << "Cannot write to user log file"; return; } //Write new logs text QTextStream outstream(&file); outstream << logs; //Set the log text in the object itself. userLog() = newLog; file.close(); } QString SkyObject::labelString() const { return translatedName(); } double SkyObject::labelOffset() const { return SkyLabeler::ZoomOffset(); } AuxInfo *SkyObject::getAuxInfo() { if (!info) info = new AuxInfo; return &(*info); } SkyObject::UID SkyObject::getUID() const { return invalidUID; } diff --git a/kstars/skyobjects/skypoint.cpp b/kstars/skyobjects/skypoint.cpp index 400847c8a..a658f95e2 100644 --- a/kstars/skyobjects/skypoint.cpp +++ b/kstars/skyobjects/skypoint.cpp @@ -1,959 +1,959 @@ /*************************************************************************** skypoint.cpp - K Desktop Planetarium ------------------- begin : Sun Feb 11 2001 copyright : (C) 2001-2005 by Jason Harris email : jharris@30doradus.org copyright : (C) 2004-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 "skypoint.h" #include "dms.h" #include "ksnumbers.h" #include "kstarsdatetime.h" #include "kssun.h" #include "kstarsdata.h" #include "Options.h" #include "skyobject.h" #include "skycomponents/skymapcomposite.h" #include #include #include #ifdef PROFILE_COORDINATE_CONVERSION #include // For profiling, remove if not profiling. long unsigned SkyPoint::eqToHzCalls = 0; double SkyPoint::cpuTime_EqToHz = 0.; #endif KSSun *SkyPoint::m_Sun = nullptr; const double SkyPoint::altCrit = -1.0; SkyPoint::SkyPoint() { // Default constructor. Set nonsense values RA0.setD(-1); // RA >= 0 always :-) Dec0.setD(180); // Dec is between -90 and 90 Degrees :-) RA = RA0; Dec = Dec0; lastPrecessJD = J2000; // By convention, we use J2000 coordinates } void SkyPoint::set(const dms &r, const dms &d) { RA0 = RA = r; Dec0 = Dec = d; lastPrecessJD = J2000; // By convention, we use J2000 coordinates } void SkyPoint::EquatorialToHorizontal(const dms *LST, const dms *lat) { // qDebug() << "NOTE: This EquatorialToHorizontal overload (using dms pointers instead of CachingDms pointers) is deprecated and should be replaced with CachingDms prototype wherever speed is desirable!"; CachingDms _LST(*LST), _lat(*lat); EquatorialToHorizontal(&_LST, &_lat); } void SkyPoint::EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat) { #ifdef PROFILE_COORDINATE_CONVERSION std::clock_t start = std::clock(); #endif //Uncomment for spherical trig version double AltRad, AzRad; double sindec, cosdec, sinlat, coslat, sinHA, cosHA; double sinAlt, cosAlt; CachingDms HourAngle = (*LST) - ra(); // Using CachingDms subtraction operator to find cos/sin of HourAngle without calling sincos() lat->SinCos(sinlat, coslat); dec().SinCos(sindec, cosdec); HourAngle.SinCos(sinHA, cosHA); sinAlt = sindec * sinlat + cosdec * coslat * cosHA; AltRad = asin(sinAlt); cosAlt = sqrt( - 1 - - sinAlt * - sinAlt); // Avoid trigonometric function. Return value of asin is always in [-pi/2, pi/2] and in this domain cosine is always non-negative, so we can use this. + 1 - + sinAlt * + sinAlt); // Avoid trigonometric function. Return value of asin is always in [-pi/2, pi/2] and in this domain cosine is always non-negative, so we can use this. if (cosAlt == 0.) cosAlt = cos(AltRad); double arg = (sindec - sinlat * sinAlt) / (coslat * cosAlt); if (arg <= -1.0) AzRad = dms::PI; else if (arg >= 1.0) AzRad = 0.0; else AzRad = acos(arg); if (sinHA > 0.0) AzRad = 2.0 * dms::PI - AzRad; // resolve acos() ambiguity Alt.setRadians(AltRad); Az.setRadians(AzRad); #ifdef PROFILE_COORDINATE_CONVERSION std::clock_t stop = std::clock(); cpuTime_EqToHz += double(stop - start) / double(CLOCKS_PER_SEC); // Accumulate time in seconds ++eqToHzCalls; #endif // //Uncomment for XYZ version // double xr, yr, zr, xr1, zr1, sa, ca; // //Z-axis rotation by -LST // dms a = dms( -1.0*LST->Degrees() ); // a.SinCos( sa, ca ); // xr1 = m_X*ca + m_Y*sa; // yr = -1.0*m_X*sa + m_Y*ca; // zr1 = m_Z; // // //Y-axis rotation by lat - 90. // a = dms( lat->Degrees() - 90.0 ); // a.SinCos( sa, ca ); // xr = xr1*ca - zr1*sa; // zr = xr1*sa + zr1*ca; // // //FIXME: eventually, we will work with XYZ directly // Alt.setRadians( asin( zr ) ); // Az.setRadians( atan2( yr, xr ) ); } void SkyPoint::HorizontalToEquatorial(const dms *LST, const dms *lat) { double HARad, DecRad; double sinlat, coslat, sinAlt, cosAlt, sinAz, cosAz; double sinDec, cosDec; lat->SinCos(sinlat, coslat); alt().SinCos(sinAlt, cosAlt); Az.SinCos(sinAz, cosAz); sinDec = sinAlt * sinlat + cosAlt * coslat * cosAz; DecRad = asin(sinDec); cosDec = cos(DecRad); Dec.setRadians(DecRad); double x = (sinAlt - sinlat * sinDec) / (coslat * cosDec); //Under certain circumstances, x can be very slightly less than -1.0000, or slightly //greater than 1.0000, leading to a crash on acos(x). However, the value isn't //*really* out of range; it's a kind of roundoff error. if (x < -1.0 && x > -1.000001) HARad = dms::PI; else if (x > 1.0 && x < 1.000001) HARad = 0.0; else if (x < -1.0) { //qWarning() << "Coordinate out of range."; HARad = dms::PI; } else if (x > 1.0) { //qWarning() << "Coordinate out of range."; HARad = 0.0; } else HARad = acos(x); if (sinAz > 0.0) HARad = 2.0 * dms::PI - HARad; // resolve acos() ambiguity RA.setRadians(LST->radians() - HARad); RA.reduceToRange(dms::ZERO_TO_2PI); } void SkyPoint::findEcliptic(const CachingDms *Obliquity, dms &EcLong, dms &EcLat) { double sinRA, cosRA, sinOb, cosOb, sinDec, cosDec, tanDec; ra().SinCos(sinRA, cosRA); dec().SinCos(sinDec, cosDec); Obliquity->SinCos(sinOb, cosOb); tanDec = sinDec / cosDec; // FIXME: -jbb div by zero? double y = sinRA * cosOb + tanDec * sinOb; double ELongRad = atan2(y, cosRA); EcLong.setRadians(ELongRad); EcLong.reduceToRange(dms::ZERO_TO_2PI); EcLat.setRadians(asin(sinDec * cosOb - cosDec * sinOb * sinRA)); } void SkyPoint::setFromEcliptic(const CachingDms *Obliquity, const dms &EcLong, const dms &EcLat) { double sinLong, cosLong, sinLat, cosLat, sinObliq, cosObliq; EcLong.SinCos(sinLong, cosLong); EcLat.SinCos(sinLat, cosLat); Obliquity->SinCos(sinObliq, cosObliq); double sinDec = sinLat * cosObliq + cosLat * sinObliq * sinLong; double y = sinLong * cosObliq - (sinLat / cosLat) * sinObliq; // double RARad = atan2( y, cosLong ); RA.setUsing_atan2(y, cosLong); RA.reduceToRange(dms::ZERO_TO_2PI); Dec.setUsing_asin(sinDec); } void SkyPoint::precess(const KSNumbers *num) { double cosRA0, sinRA0, cosDec0, sinDec0; const Eigen::Matrix3d &precessionMatrix = num->p2(); Eigen::Vector3d v, s; RA0.SinCos(sinRA0, cosRA0); Dec0.SinCos(sinDec0, cosDec0); s[0] = cosRA0 * cosDec0; s[1] = sinRA0 * cosDec0; s[2] = sinDec0; // NOTE: Rotation matrices are the fastest way to do rotations on // a vector. Quaternions need more multiplications. The rotation // matrix compensates in some sense by having more 'precomputed' // multiplications. The matrix elements seem to cache nicely, so // there isn't much overhead in accessing them. //Multiply P2 and s to get v, the vector representing the new coords. // for ( unsigned int i=0; i<3; ++i ) { // v[i] = 0.0; // for (uint j=0; j< 3; ++j) { // v[i] += num->p2( j, i )*s[j]; // } // } v.noalias() = precessionMatrix * s; //Extract RA, Dec from the vector: RA.setUsing_atan2(v[1], v[0]); RA.reduceToRange(dms::ZERO_TO_2PI); Dec.setUsing_asin(v[2]); } SkyPoint SkyPoint::deprecess(const KSNumbers *num, long double epoch) { SkyPoint p1(RA, Dec); long double now = num->julianDay(); p1.precessFromAnyEpoch(now, epoch); if ((std::isnan(RA0.Degrees()) || std::isnan(Dec0.Degrees())) || - (!std::isnan(Dec0.Degrees()) && fabs(Dec0.Degrees()) > 90.0)) + (!std::isnan(Dec0.Degrees()) && fabs(Dec0.Degrees()) > 90.0)) { // We have invalid RA0 and Dec0, so set them if epoch = J2000. Otherwise, do not touch. if (epoch == J2000) { RA0 = p1.ra(); Dec0 = p1.dec(); } } return p1; } void SkyPoint::nutate(const KSNumbers *num) { double cosRA, sinRA, cosDec, sinDec, tanDec; double cosOb, sinOb; RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); num->obliquity()->SinCos(sinOb, cosOb); //Step 2: Nutation if (fabs(Dec.Degrees()) < 80.0) //approximate method { tanDec = sinDec / cosDec; double dRA = num->dEcLong() * (cosOb + sinOb * sinRA * tanDec) - num->dObliq() * cosRA * tanDec; double dDec = num->dEcLong() * (sinOb * cosRA) + num->dObliq() * sinRA; RA.setD(RA.Degrees() + dRA); Dec.setD(Dec.Degrees() + dDec); } else //exact method { dms EcLong, EcLat; findEcliptic(num->obliquity(), EcLong, EcLat); //Add dEcLong to the Ecliptic Longitude dms newLong(EcLong.Degrees() + num->dEcLong()); setFromEcliptic(num->obliquity(), newLong, EcLat); } } SkyPoint SkyPoint::moveAway(const SkyPoint &from, double dist) const { CachingDms lat1, dtheta; if (dist == 0.0) { qDebug() << "moveAway called with zero distance!"; return *this; } double dst = fabs(dist * dms::DegToRad / 3600.0); // In radian // Compute the bearing angle w.r.t. the RA axis ("latitude") CachingDms dRA(ra() - from.ra()); CachingDms dDec(dec() - from.dec()); double bearing = atan2(dRA.sin() / dRA.cos(), dDec.sin()); // Do not use dRA = PI / 2!! //double bearing = atan2( dDec.radians() , dRA.radians() ); // double dir0 = (dist >= 0 ) ? bearing : bearing + dms::PI; // in radian double dir0 = bearing + std::signbit(dist) * dms::PI; // might be faster? double sinDst = sin(dst), cosDst = cos(dst); lat1.setUsing_asin(dec().sin() * cosDst + dec().cos() * sinDst * cos(dir0)); dtheta.setUsing_atan2(sin(dir0) * sinDst * dec().cos(), cosDst - dec().sin() * lat1.sin()); return SkyPoint(ra() + dtheta, lat1); } bool SkyPoint::checkBendLight() { // First see if we are close enough to the sun to bother about the // gravitational lensing effect. We correct for the effect at // least till b = 10 solar radii, where the effect is only about // 0.06". Assuming min. sun-earth distance is 200 solar radii. static const dms maxAngle(1.75 * (30.0 / 200.0) / dms::DegToRad); if (!m_Sun) { SkyComposite *skycomopsite = KStarsData::Instance()->skyComposite(); if (skycomopsite == nullptr) return false; - m_Sun = (KSSun *)skycomopsite->findByName("Sun"); + m_Sun = dynamic_cast(skycomopsite->findByName(i18n("Sun"))); if (m_Sun == nullptr) return false; } // TODO: This can be optimized further. We only need a ballpark estimate of the distance to the sun to start with. return (fabs(angularDistanceTo(static_cast(m_Sun)).Degrees()) <= maxAngle.Degrees()); // NOTE: dynamic_cast is slow and not important here. } bool SkyPoint::bendlight() { // NOTE: This should be applied before aberration // NOTE: One must call checkBendLight() before unnecessarily calling this. // We correct for GR effects // NOTE: This code is buggy. The sun needs to be initialized to // the current epoch -- but we are not certain that this is the // case. We have, as of now, no way of telling if the sun is // initialized or not. If we initialize the sun here, we will be // slowing down the program rather substantially and potentially // introducing bugs. Therefore, we just ignore this problem, and // hope that whenever the user is interested in seeing the effects // of GR, we have the sun initialized correctly. This is usually // the case. When the sun is not correctly initialized, rearth() // is not computed, so we just assume it is nominally equal to 1 // AU to get a reasonable estimate. Q_ASSERT(m_Sun); double corr_sec = 1.75 * m_Sun->physicalSize() / ((std::isfinite(m_Sun->rearth()) ? m_Sun->rearth() : 1) * AU_KM * angularDistanceTo(static_cast(m_Sun)).sin()); Q_ASSERT(corr_sec > 0); SkyPoint sp = moveAway(*m_Sun, corr_sec); setRA(sp.ra()); setDec(sp.dec()); return true; } void SkyPoint::aberrate(const KSNumbers *num) { double cosRA, sinRA, cosDec, sinDec; double cosOb, sinOb, cosL, sinL, cosP, sinP; double K = num->constAberr().Degrees(); //constant of aberration double e = num->earthEccentricity(); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); num->obliquity()->SinCos(sinOb, cosOb); // double tanOb = sinOb/cosOb; num->sunTrueLongitude().SinCos(sinL, cosL); num->earthPerihelionLongitude().SinCos(sinP, cosP); //Step 3: Aberration // double dRA = -1.0 * K * ( cosRA * cosL * cosOb + sinRA * sinL )/cosDec // + e * K * ( cosRA * cosP * cosOb + sinRA * sinP )/cosDec; // double dDec = -1.0 * K * ( cosL * cosOb * ( tanOb * cosDec - sinRA * sinDec ) + cosRA * sinDec * sinL ) // + e * K * ( cosP * cosOb * ( tanOb * cosDec - sinRA * sinDec ) + cosRA * sinDec * sinP ); double dRA = K * (cosRA * cosOb / cosDec) * (e * cosP - cosL); double dDec = K * (sinRA * (sinOb * cosDec - cosOb * sinDec) * (e * cosP - cosL) + cosRA * sinDec * (e * sinP - sinL)); RA.setD(RA.Degrees() + dRA); Dec.setD(Dec.Degrees() + dDec); } // Note: This method is one of the major rate determining factors in how fast the map pans / zooms in or out void SkyPoint::updateCoords(const KSNumbers *num, bool /*includePlanets*/, const CachingDms *lat, const CachingDms *LST, bool forceRecompute) { //Correct the catalog coordinates for the time-dependent effects //of precession, nutation and aberration bool recompute, lens; // NOTE: The same short-circuiting checks are also implemented in // StarObject::JITUpdate(), even before calling // updateCoords(). While this is code-duplication, these bits of // code need to be really optimized, at least for stars. For // optimization purposes, the code is left duplicated in two // places. Please be wary of changing one without changing the // other. Q_ASSERT(std::isfinite(lastPrecessJD)); if (Options::useRelativistic() && checkBendLight()) { recompute = true; lens = true; } else { recompute = (Options::alwaysRecomputeCoordinates() || forceRecompute || std::abs(lastPrecessJD - num->getJD()) >= 0.00069444); // Update once per solar minute lens = false; } if (recompute) { precess(num); nutate(num); if (lens) bendlight(); // FIXME: Shouldn't we apply this on the horizontal coordinates? aberrate(num); lastPrecessJD = num->getJD(); Q_ASSERT(std::isfinite(RA.Degrees()) && std::isfinite(Dec.Degrees())); } if (lat || LST) qWarning() << i18n("lat and LST parameters should only be used in KSPlanetBase objects."); } void SkyPoint::precessFromAnyEpoch(long double jd0, long double jdf) { double cosRA, sinRA, cosDec, sinDec; double v[3], s[3]; RA = RA0; Dec = Dec0; // Is this necessary? if (jd0 == jdf) return; RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); if (jd0 == B1950) { B1950ToJ2000(); jd0 = J2000; RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); } if (jd0 != jdf) { // The original coordinate is referred to the FK5 system and // is NOT J2000. if (jd0 != J2000) { //v is a column vector representing input coordinates. v[0] = cosRA * cosDec; v[1] = sinRA * cosDec; v[2] = sinDec; //Need to first precess to J2000.0 coords //s is the product of P1 and v; s represents the //coordinates precessed to J2000 KSNumbers num(jd0); for (unsigned int i = 0; i < 3; ++i) { s[i] = num.p1(0, i) * v[0] + num.p1(1, i) * v[1] + num.p1(2, i) * v[2]; } //Input coords already in J2000, set s accordingly. } else { s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; } if (jdf == B1950) { RA.setRadians(atan2(s[1], s[0])); Dec.setRadians(asin(s[2])); J2000ToB1950(); return; } KSNumbers num(jdf); for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p2(0, i) * s[0] + num.p2(1, i) * s[1] + num.p2(2, i) * s[2]; } RA.setUsing_atan2(v[1], v[0]); Dec.setUsing_asin(v[2]); RA.reduceToRange(dms::ZERO_TO_2PI); return; } } void SkyPoint::apparentCoord(long double jd0, long double jdf) { precessFromAnyEpoch(jd0, jdf); KSNumbers num(jdf); nutate(&num); if (Options::useRelativistic() && checkBendLight()) bendlight(); aberrate(&num); } void SkyPoint::Equatorial1950ToGalactic(dms &galLong, dms &galLat) { double a = 192.25; double sinb, cosb, sina_RA, cosa_RA, sinDEC, cosDEC, tanDEC; dms c(303.0); dms b(27.4); tanDEC = tan(Dec.radians()); b.SinCos(sinb, cosb); dms(a - RA.Degrees()).SinCos(sina_RA, cosa_RA); Dec.SinCos(sinDEC, cosDEC); galLong.setRadians(c.radians() - atan2(sina_RA, cosa_RA * sinb - tanDEC * cosb)); galLong.reduceToRange(dms::ZERO_TO_2PI); galLat.setRadians(asin(sinDEC * sinb + cosDEC * cosb * cosa_RA)); } void SkyPoint::GalacticToEquatorial1950(const dms *galLong, const dms *galLat) { double a = 123.0; double sinb, cosb, singLat, cosgLat, tangLat, singLong_a, cosgLong_a; dms c(12.25); dms b(27.4); tangLat = tan(galLat->radians()); galLat->SinCos(singLat, cosgLat); dms(galLong->Degrees() - a).SinCos(singLong_a, cosgLong_a); b.SinCos(sinb, cosb); RA.setRadians(c.radians() + atan2(singLong_a, cosgLong_a * sinb - tangLat * cosb)); RA.reduceToRange(dms::ZERO_TO_2PI); Dec.setRadians(asin(singLat * sinb + cosgLat * cosb * cosgLong_a)); } void SkyPoint::B1950ToJ2000(void) { double cosRA, sinRA, cosDec, sinDec; // double cosRA0, sinRA0, cosDec0, sinDec0; double v[3], s[3]; // 1984 January 1 0h KSNumbers num(2445700.5); // Eterms due to aberration addEterms(); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); // Precession from B1950 to J1984 s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p2b(0, i) * s[0] + num.p2b(1, i) * s[1] + num.p2b(2, i) * s[2]; } // RA zero-point correction at 1984 day 1, 0h. RA.setRadians(atan2(v[1], v[0])); Dec.setRadians(asin(v[2])); RA.setH(RA.Hours() + 0.06390 / 3600.); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; // Precession from 1984 to J2000. for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p1(0, i) * s[0] + num.p1(1, i) * s[1] + num.p1(2, i) * s[2]; } RA.setRadians(atan2(v[1], v[0])); Dec.setRadians(asin(v[2])); } void SkyPoint::J2000ToB1950(void) { double cosRA, sinRA, cosDec, sinDec; // double cosRA0, sinRA0, cosDec0, sinDec0; double v[3], s[3]; // 1984 January 1 0h KSNumbers num(2445700.5); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; // Precession from J2000 to 1984 day, 0h. for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p2(0, i) * s[0] + num.p2(1, i) * s[1] + num.p2(2, i) * s[2]; } RA.setRadians(atan2(v[1], v[0])); Dec.setRadians(asin(v[2])); // RA zero-point correction at 1984 day 1, 0h. RA.setH(RA.Hours() - 0.06390 / 3600.); RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); // Precession from B1950 to J1984 s[0] = cosRA * cosDec; s[1] = sinRA * cosDec; s[2] = sinDec; for (unsigned int i = 0; i < 3; ++i) { v[i] = num.p1b(0, i) * s[0] + num.p1b(1, i) * s[1] + num.p1b(2, i) * s[2]; } RA.setRadians(atan2(v[1], v[0])); Dec.setRadians(asin(v[2])); // Eterms due to aberration subtractEterms(); } SkyPoint SkyPoint::Eterms(void) { double sd, cd, sinEterm, cosEterm; dms raTemp, raDelta, decDelta; Dec.SinCos(sd, cd); raTemp.setH(RA.Hours() + 11.25); raTemp.SinCos(sinEterm, cosEterm); raDelta.setH(0.0227 * sinEterm / (3600. * cd)); decDelta.setD(0.341 * cosEterm * sd / 3600. + 0.029 * cd / 3600.); return SkyPoint(raDelta, decDelta); } void SkyPoint::addEterms(void) { SkyPoint spd = Eterms(); RA = RA + spd.ra(); Dec = Dec + spd.dec(); } void SkyPoint::subtractEterms(void) { SkyPoint spd = Eterms(); RA = RA - spd.ra(); Dec = Dec - spd.dec(); } dms SkyPoint::angularDistanceTo(const SkyPoint *sp, double *const positionAngle) const { // double dalpha = sp->ra().radians() - ra().radians() ; // double ddelta = sp->dec().radians() - dec().radians(); CachingDms dalpha = sp->ra() - ra(); CachingDms ddelta = sp->dec() - dec(); // double sa = sin(dalpha/2.); // double sd = sin(ddelta/2.); // double hava = sa*sa; // double havd = sd*sd; // Compute the haversin directly: double hava = (1 - dalpha.cos()) / 2.; double havd = (1 - ddelta.cos()) / 2.; // Haversine law double aux = havd + (sp->dec().cos()) * dec().cos() * hava; dms angDist; angDist.setRadians(2. * fabs(asin(sqrt(aux)))); if (positionAngle) { // Also compute the position angle of the line from this SkyPoint to sp //*positionAngle = acos( tan(-ddelta)/tan( angDist.radians() ) ); // FIXME: Might fail for large ddelta / zero angDist //if( -dalpha < 0 ) // *positionAngle = 2*M_PI - *positionAngle; *positionAngle = atan2f(dalpha.sin(), (dec().cos()) * tan(sp->dec().radians()) - (dec().sin()) * dalpha.cos()) * 180 / M_PI; } return angDist; } double SkyPoint::vRSun(long double jd0) { double ca, sa, cd, sd, vsun; double cosRA, sinRA, cosDec, sinDec; /* Sun apex (where the sun goes) coordinates */ dms asun(270.9592); // Right ascention: 18h 3m 50.2s [J2000] dms dsun(30.00467); // Declination: 30^o 0' 16.8'' [J2000] vsun = 20.; // [km/s] asun.SinCos(sa, ca); dsun.SinCos(sd, cd); /* We need an auxiliary SkyPoint since we need the * source referred to the J2000 equinox and we do not want to overwrite * the current values */ SkyPoint aux; aux.set(RA0, Dec0); aux.precessFromAnyEpoch(jd0, J2000); aux.ra().SinCos(sinRA, cosRA); aux.dec().SinCos(sinDec, cosDec); /* Computation is done performing the scalar product of a unitary vector in the direction of the source with the vector velocity of Sun, both being in the LSR reference system: Vlsr = Vhel + Vsun.u_radial => Vlsr = Vhel + vsun(cos D cos A,cos D sen A,sen D).(cos d cos a,cos d sen a,sen d) Vhel = Vlsr - Vsun.u_radial */ return vsun * (cd * cosDec * (cosRA * ca + sa * sinRA) + sd * sinDec); } double SkyPoint::vHeliocentric(double vlsr, long double jd0) { return vlsr - vRSun(jd0); } double SkyPoint::vHelioToVlsr(double vhelio, long double jd0) { return vhelio + vRSun(jd0); } double SkyPoint::vREarth(long double jd0) { double sinRA, sinDec, cosRA, cosDec; /* u_radial = unitary vector in the direction of the source Vlsr = Vhel + Vsun.u_radial = Vgeo + VEarth.u_radial + Vsun.u_radial => Vgeo = (Vlsr -Vsun.u_radial) - VEarth.u_radial = Vhel - VEarth.u_radial = Vhel - (vx, vy, vz).(cos d cos a,cos d sen a,sen d) */ /* We need an auxiliary SkyPoint since we need the * source referred to the J2000 equinox and we do not want to overwrite * the current values */ SkyPoint aux(RA0, Dec0); aux.precessFromAnyEpoch(jd0, J2000); aux.ra().SinCos(sinRA, cosRA); aux.dec().SinCos(sinDec, cosDec); /* vEarth is referred to the J2000 equinox, hence we need that the source coordinates are also in the same reference system. */ KSNumbers num(jd0); return num.vEarth(0) * cosDec * cosRA + num.vEarth(1) * cosDec * sinRA + num.vEarth(2) * sinDec; } double SkyPoint::vGeocentric(double vhelio, long double jd0) { return vhelio - vREarth(jd0); } double SkyPoint::vGeoToVHelio(double vgeo, long double jd0) { return vgeo + vREarth(jd0); } double SkyPoint::vRSite(double vsite[3]) { double sinRA, sinDec, cosRA, cosDec; RA.SinCos(sinRA, cosRA); Dec.SinCos(sinDec, cosDec); return vsite[0] * cosDec * cosRA + vsite[1] * cosDec * sinRA + vsite[2] * sinDec; } double SkyPoint::vTopoToVGeo(double vtopo, double vsite[3]) { return vtopo + vRSite(vsite); } double SkyPoint::vTopocentric(double vgeo, double vsite[3]) { return vgeo - vRSite(vsite); } bool SkyPoint::checkCircumpolar(const dms *gLat) const { return fabs(dec().Degrees()) > (90 - fabs(gLat->Degrees())); } dms SkyPoint::altRefracted() const { if (Options::useRefraction()) return refract(Alt); else return Alt; } double SkyPoint::refractionCorr(double alt) { return 1.02 / tan(dms::DegToRad * (alt + 10.3 / (alt + 5.11))) / 60; } double SkyPoint::refract(const double alt) { static double corrCrit = SkyPoint::refractionCorr(SkyPoint::altCrit); if (alt > SkyPoint::altCrit) return (alt + SkyPoint::refractionCorr(alt)); else return (alt + corrCrit * (alt + 90) / - (SkyPoint::altCrit + 90)); // Linear extrapolation from corrCrit at altCrit to 0 at -90 degrees + (SkyPoint::altCrit + 90)); // Linear extrapolation from corrCrit at altCrit to 0 at -90 degrees } // Found uncorrected value by solving equation. This is OK since // unrefract is never called in loops. // // Convergence is quite fast just a few iterations. double SkyPoint::unrefract(const double alt) { double h0 = alt; double h1 = alt - (refract(h0) - h0); // It's probably okay to add h0 in refract() and subtract it here, since refract() is called way more frequently. while (fabs(h1 - h0) > 1e-4) { h0 = h1; h1 = alt - (refract(h0) - h0); } return h1; } dms SkyPoint::findAltitude(const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour) { Q_ASSERT(p); if (!p) return dms(NaN::d); // Jasem 2015-08-24 Using correct procedure to find altitude return SkyPoint::timeTransformed(p, dt, geo, hour).alt(); } SkyPoint SkyPoint::timeTransformed(const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour) { Q_ASSERT(p); if (!p) return SkyPoint(NaN::d, NaN::d); // Jasem 2015-08-24 Using correct procedure to find altitude SkyPoint sp = *p; // make a copy KStarsDateTime targetDateTime = dt.addSecs(hour * 3600.0); dms LST = geo->GSTtoLST(targetDateTime.gst()); sp.EquatorialToHorizontal(&LST, geo->lat()); return sp; } double SkyPoint::maxAlt(const dms &lat) const { double retval = (lat.Degrees() + 90. - dec().Degrees()); if (retval > 90.) retval = 180. - retval; return retval; } double SkyPoint::minAlt(const dms &lat) const { double retval = (lat.Degrees() - 90. + dec().Degrees()); if (retval < -90.) retval = 180. + retval; return retval; } #ifndef KSTARS_LITE QDBusArgument &operator<<(QDBusArgument &argument, const SkyPoint &source) { argument.beginStructure(); argument << source.ra().Hours() << source.dec().Degrees(); argument.endStructure(); return argument; } const QDBusArgument &operator>>(const QDBusArgument &argument, SkyPoint &dest) { double ra, dec; argument.beginStructure(); argument >> ra >> dec; argument.endStructure(); dest = SkyPoint(ra, dec); return argument; } #endif diff --git a/kstars/skyqpainter.cpp b/kstars/skyqpainter.cpp index c965a3357..c79da2664 100644 --- a/kstars/skyqpainter.cpp +++ b/kstars/skyqpainter.cpp @@ -1,1054 +1,1066 @@ /* (C) 2010 Henry de Valence 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. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more decomas. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "skyqpainter.h" #include #include "kstarsdata.h" #include "Options.h" #include "skymap.h" #include "projections/projector.h" #include "skycomponents/flagcomponent.h" #include "skycomponents/linelist.h" #include "skycomponents/linelistlabel.h" #include "skycomponents/satellitescomponent.h" #include "skycomponents/skiphashlist.h" #include "skycomponents/skymapcomposite.h" #include "skycomponents/solarsystemcomposite.h" #include "skycomponents/earthshadowcomponent.h" #include "skyobjects/constellationsart.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/ksasteroid.h" #include "skyobjects/kscomet.h" #include "skyobjects/kssun.h" #include "skyobjects/satellite.h" #include "skyobjects/supernova.h" #include "skyobjects/ksearthshadow.h" #include "hips/hipsrenderer.h" namespace { // Convert spectral class to numerical index. // If spectral class is invalid return index for white star (A class) int harvardToIndex(char c) { switch (c) { case 'o': case 'O': return 0; case 'b': case 'B': return 1; case 'a': case 'A': return 2; case 'f': case 'F': return 3; case 'g': case 'G': return 4; case 'k': case 'K': return 5; case 'm': case 'M': return 6; // For unknown spectral class assume A class (white star) default: return 2; } } // Total number of sizes of stars. const int nStarSizes = 15; // Total number of spectral classes // N.B. Must be in sync with harvardToIndex const int nSPclasses = 7; // Cache for star images. // // These pixmaps are never deallocated. Not really good... QPixmap *imageCache[nSPclasses][nStarSizes] = { { nullptr } }; std::unique_ptr visibleSatPixmap, invisibleSatPixmap; } int SkyQPainter::starColorMode = 0; QColor SkyQPainter::m_starColor = QColor(); QMap SkyQPainter::ColorMap = QMap(); void SkyQPainter::releaseImageCache() { for (char &color : ColorMap.keys()) { QPixmap **pmap = imageCache[harvardToIndex(color)]; for (int size = 1; size < nStarSizes; size++) { if (pmap[size]) delete pmap[size]; pmap[size] = nullptr; } } } SkyQPainter::SkyQPainter(QPaintDevice *pd) : SkyPainter(), QPainter() { Q_ASSERT(pd); m_pd = pd; m_size = QSize(pd->width(), pd->height()); m_hipsRender = new HIPSRenderer(); } SkyQPainter::SkyQPainter(QPaintDevice *pd, const QSize &size) : SkyPainter(), QPainter() { Q_ASSERT(pd); m_pd = pd; m_size = size; m_hipsRender = new HIPSRenderer(); } SkyQPainter::SkyQPainter(QWidget *widget, QPaintDevice *pd) : SkyPainter(), QPainter() { Q_ASSERT(widget); // Set paint device pointer to pd or to the widget if pd = 0 m_pd = (pd ? pd : widget); m_size = widget->size(); m_hipsRender = new HIPSRenderer(); } SkyQPainter::~SkyQPainter() { delete (m_hipsRender); } void SkyQPainter::begin() { QPainter::begin(m_pd); bool aa = !m_sm->isSlewing() && Options::useAntialias(); setRenderHint(QPainter::Antialiasing, aa); setRenderHint(QPainter::HighQualityAntialiasing, aa); m_proj = m_sm->projector(); } void SkyQPainter::end() { QPainter::end(); } void SkyQPainter::drawSkyBackground() { //FIXME use projector fillRect(0, 0, m_size.width(), m_size.height(), KStarsData::Instance()->colorScheme()->colorNamed("SkyColor")); } void SkyQPainter::setPen(const QPen &pen) { QPainter::setPen(pen); } void SkyQPainter::setBrush(const QBrush &brush) { QPainter::setBrush(brush); } void SkyQPainter::initStarImages() { const int starColorIntensity = Options::starColorIntensity(); ColorMap.clear(); switch (Options::starColorMode()) { case 1: // Red stars. m_starColor = Qt::red; break; case 2: // Black stars. m_starColor = Qt::black; break; case 3: // White stars m_starColor = Qt::white; break; case 0: // Real color default: // And use real color for everything else m_starColor = QColor(); ColorMap.insert('O', QColor::fromRgb(0, 0, 255)); ColorMap.insert('B', QColor::fromRgb(0, 200, 255)); ColorMap.insert('A', QColor::fromRgb(0, 255, 255)); ColorMap.insert('F', QColor::fromRgb(200, 255, 100)); ColorMap.insert('G', QColor::fromRgb(255, 255, 0)); ColorMap.insert('K', QColor::fromRgb(255, 100, 0)); ColorMap.insert('M', QColor::fromRgb(255, 0, 0)); break; } if (ColorMap.isEmpty()) { ColorMap.insert('O', m_starColor); ColorMap.insert('B', m_starColor); ColorMap.insert('A', m_starColor); ColorMap.insert('F', m_starColor); ColorMap.insert('G', m_starColor); ColorMap.insert('K', m_starColor); ColorMap.insert('M', m_starColor); } for (char &color : ColorMap.keys()) { QPixmap BigImage(15, 15); BigImage.fill(Qt::transparent); QPainter p; p.begin(&BigImage); if (Options::starColorMode() == 0) { qreal h, s, v, a; p.setRenderHint(QPainter::Antialiasing, false); QColor starColor = ColorMap[color]; starColor.getHsvF(&h, &s, &v, &a); for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) { qreal x = i - 7; qreal y = j - 7; qreal dist = sqrt(x * x + y * y) / 7.0; starColor.setHsvF(h, qMin(qreal(1), dist < (10 - starColorIntensity) / 10.0 ? 0 : dist), v, qMax(qreal(0), dist < (10 - starColorIntensity) / 20.0 ? 1 : 1 - dist)); p.setPen(starColor); p.drawPoint(i, j); p.drawPoint(14 - i, j); p.drawPoint(i, 14 - j); p.drawPoint(14 - i, 14 - j); } } } else { p.setRenderHint(QPainter::Antialiasing, true); p.setPen(QPen(ColorMap[color], 2.0)); p.setBrush(p.pen().color()); p.drawEllipse(QRectF(2, 2, 10, 10)); } p.end(); // Cache array slice QPixmap **pmap = imageCache[harvardToIndex(color)]; for (int size = 1; size < nStarSizes; size++) { if (!pmap[size]) pmap[size] = new QPixmap(); *pmap[size] = BigImage.scaled(size, size, Qt::KeepAspectRatio, Qt::SmoothTransformation); } } starColorMode = Options::starColorMode(); if (!visibleSatPixmap.get()) visibleSatPixmap.reset(new QPixmap(":/icons/kstars_satellites_visible.svg")); if (!invisibleSatPixmap.get()) invisibleSatPixmap.reset(new QPixmap(":/icons/kstars_satellites_invisible.svg")); } void SkyQPainter::drawSkyLine(SkyPoint *a, SkyPoint *b) { bool aVisible, bVisible; QPointF aScreen = m_proj->toScreen(a, true, &aVisible); QPointF bScreen = m_proj->toScreen(b, true, &bVisible); drawLine(aScreen, bScreen); //THREE CASES: -// if (aVisible && bVisible) -// { -// //Both a,b visible, so paint the line normally: -// drawLine(aScreen, bScreen); -// } -// else if (aVisible) -// { -// //a is visible but b isn't: -// drawLine(aScreen, m_proj->clipLine(a, b)); -// } -// else if (bVisible) -// { -// //b is visible but a isn't: -// drawLine(bScreen, m_proj->clipLine(b, a)); -// } //FIXME: what if both are offscreen but the line isn't? + // if (aVisible && bVisible) + // { + // //Both a,b visible, so paint the line normally: + // drawLine(aScreen, bScreen); + // } + // else if (aVisible) + // { + // //a is visible but b isn't: + // drawLine(aScreen, m_proj->clipLine(a, b)); + // } + // else if (bVisible) + // { + // //b is visible but a isn't: + // drawLine(bScreen, m_proj->clipLine(b, a)); + // } //FIXME: what if both are offscreen but the line isn't? } void SkyQPainter::drawSkyPolyline(LineList *list, SkipHashList *skipList, LineListLabel *label) { SkyList *points = list->points(); bool isVisible, isVisibleLast; QPointF oLast = m_proj->toScreen(points->first().get(), true, &isVisibleLast); // & with the result of checkVisibility to clip away things below horizon isVisibleLast &= m_proj->checkVisibility(points->first().get()); QPointF oThis, oThis2; for (int j = 1; j < points->size(); j++) { SkyPoint *pThis = points->at(j).get(); oThis2 = oThis = m_proj->toScreen(pThis, true, &isVisible); // & with the result of checkVisibility to clip away things below horizon isVisible &= m_proj->checkVisibility(pThis); bool doSkip = false; if (skipList) { doSkip = skipList->skip(j); } bool pointsVisible = false; //Temporary solution to avoid random lines in Gnomonic projection and draw lines up to horizon if (SkyMap::Instance()->projector()->type() == Projector::Gnomonic) { if (isVisible && isVisibleLast) pointsVisible = true; } else { if (isVisible || isVisibleLast) pointsVisible = true; } if (!doSkip) { if (pointsVisible) { drawLine(oLast, oThis); if (label) label->updateLabelCandidates(oThis.x(), oThis.y(), list, j); } } oLast = oThis2; isVisibleLast = isVisible; } } void SkyQPainter::drawSkyPolygon(LineList *list, bool forceClip) { bool isVisible = false, isVisibleLast; SkyList *points = list->points(); QPolygonF polygon; if (forceClip == false) { for (const auto &point : *points) { polygon << m_proj->toScreen(point.get(), false, &isVisibleLast); isVisible |= isVisibleLast; } // If 1+ points are visible, draw it if (polygon.size() && isVisible) drawPolygon(polygon); return; } SkyPoint *pLast = points->last().get(); QPointF oLast = m_proj->toScreen(pLast, true, &isVisibleLast); // & with the result of checkVisibility to clip away things below horizon isVisibleLast &= m_proj->checkVisibility(pLast); for (const auto &point : *points) { SkyPoint *pThis = point.get(); QPointF oThis = m_proj->toScreen(pThis, true, &isVisible); // & with the result of checkVisibility to clip away things below horizon isVisible &= m_proj->checkVisibility(pThis); if (isVisible && isVisibleLast) { polygon << oThis; } else if (isVisibleLast) { QPointF oMid = m_proj->clipLine(pLast, pThis); polygon << oMid; } else if (isVisible) { QPointF oMid = m_proj->clipLine(pThis, pLast); polygon << oMid; polygon << oThis; } pLast = pThis; oLast = oThis; isVisibleLast = isVisible; } if (polygon.size()) drawPolygon(polygon); } bool SkyQPainter::drawPlanet(KSPlanetBase *planet) { if (!m_proj->checkVisibility(planet)) return false; bool visible = false; QPointF pos = m_proj->toScreen(planet, true, &visible); if (!visible || !m_proj->onScreen(pos)) return false; float fakeStarSize = (10.0 + log10(Options::zoomFactor()) - log10(MINZOOM)) * (10 - planet->mag()) / 10; if (fakeStarSize > 15.0) fakeStarSize = 15.0; double size = planet->angSize() * dms::PI * Options::zoomFactor() / 10800.0; - if (size < fakeStarSize && planet->name() != "Sun" && planet->name() != "Moon") + if (size < fakeStarSize && planet->name() != i18n("Sun") && planet->name() != i18n("Moon")) { // Draw them as bright stars of appropriate color instead of images char spType; //FIXME: do these need i18n? if (planet->name() == i18n("Mars")) { spType = 'K'; } else if (planet->name() == i18n("Jupiter") || planet->name() == i18n("Mercury") || planet->name() == i18n("Saturn")) { spType = 'F'; } else { spType = 'B'; } drawPointSource(pos, fakeStarSize, spType); } else { float sizemin = 1.0; - if (planet->name() == "Sun" || planet->name() == "Moon") + if (planet->name() == i18n("Sun") || planet->name() == i18n("Moon")) sizemin = 8.0; if (size < sizemin) size = sizemin; if (Options::showPlanetImages() && !planet->image().isNull()) { //Because Saturn has rings, we inflate its image size by a factor 2.5 if (planet->name() == "Saturn") size = int(2.5 * size); // Scale size exponentially so it is visible at large zooms else if (planet->name() == "Pluto") size = int(size * exp(1.5 * size)); save(); translate(pos); rotate(m_proj->findPA(planet, pos.x(), pos.y())); drawImage(QRectF(-0.5 * size, -0.5 * size, size, size), planet->image()); restore(); } else //Otherwise, draw a simple circle. { drawEllipse(pos, size * .5, size * .5); } } return true; } bool SkyQPainter::drawEarthShadow(KSEarthShadow *shadow) { if (!m_proj->checkVisibility(shadow)) - return false; + return false; bool visible = false; QPointF pos = m_proj->toScreen(shadow, true, &visible); if(!visible) return false; double umbra_size = shadow->getUmbraAngSize() * dms::PI * Options::zoomFactor() / 10800.0; double penumbra_size = shadow->getPenumbraAngSize() * dms::PI * Options::zoomFactor() / 10800.0; save(); setBrush(QBrush(QColor(255, 96, 38, 128))); drawEllipse(pos, umbra_size, umbra_size); setBrush(QBrush(QColor(255, 96, 38, 90))); drawEllipse(pos, penumbra_size, penumbra_size); restore(); return true; } bool SkyQPainter::drawComet(KSComet *com) { if (!m_proj->checkVisibility(com)) return false; double size = com->angSize() * dms::PI * Options::zoomFactor() / 10800.0 / 2; // Radius if (size < 1) size = 1; bool visible = false; QPointF pos = m_proj->toScreen(com, true, &visible); // Draw the coma. FIXME: Another Check?? if (visible && m_proj->onScreen(pos)) { // Draw the comet. drawEllipse(pos, size, size); double comaLength = (com->getComaAngSize().arcmin() * dms::PI * Options::zoomFactor() / 10800.0); // If coma is visible and long enough. if (Options::showCometComas() && comaLength > size) { KSSun *sun = KStarsData::Instance()->skyComposite()->solarSystemComposite()->sun(); // Find the angle to the sun. double comaAngle = m_proj->findPA(sun, pos.x(), pos.y()); const QVector coma = { QPoint(pos.x() - size, pos.y()), QPoint(pos.x() + size, pos.y()), - QPoint(pos.x(), pos.y() + comaLength) }; + QPoint(pos.x(), pos.y() + comaLength) + }; QPolygon comaPoly(coma); comaPoly = QTransform() - .translate(pos.x(), pos.y()) - .rotate(comaAngle) // Already + 180 Deg, because rotated from south, not north. - .translate(-pos.x(), -pos.y()) - .map(comaPoly); + .translate(pos.x(), pos.y()) + .rotate(comaAngle) // Already + 180 Deg, because rotated from south, not north. + .translate(-pos.x(), -pos.y()) + .map(comaPoly); save(); // Nice fade for the Coma. QLinearGradient linearGrad(pos, comaPoly.point(2)); linearGrad.setColorAt(0, QColor("white")); linearGrad.setColorAt(size / comaLength, QColor("white")); linearGrad.setColorAt(0.9, QColor("transparent")); setBrush(linearGrad); // Render Coma. drawConvexPolygon(comaPoly); restore(); } return true; } else { return false; } } bool SkyQPainter::drawPointSource(SkyPoint *loc, float mag, char sp) { //Check if it's even visible before doing anything if (!m_proj->checkVisibility(loc)) return false; bool visible = false; QPointF pos = m_proj->toScreen(loc, true, &visible); if (visible && - m_proj->onScreen( - pos)) // FIXME: onScreen here should use canvas size rather than SkyMap size, especially while printing in portrait mode! + m_proj->onScreen( + pos)) // FIXME: onScreen here should use canvas size rather than SkyMap size, especially while printing in portrait mode! { drawPointSource(pos, starWidth(mag), sp); return true; } else { return false; } } void SkyQPainter::drawPointSource(const QPointF &pos, float size, char sp) { int isize = qMin(static_cast(size), 14); if (!m_vectorStars || starColorMode == 0) { // Draw stars as bitmaps, either because we were asked to, or because we're painting real colors QPixmap *im = imageCache[harvardToIndex(sp)][isize]; float offset = 0.5 * im->width(); drawPixmap(QPointF(pos.x() - offset, pos.y() - offset), *im); } else { // Draw stars as vectors, for better printing / SVG export etc. if (starColorMode != 4) { setPen(m_starColor); setBrush(m_starColor); } else { // Note: This is not efficient, but we use vector stars only when plotting SVG, not when drawing the skymap, so speed is not very important. QColor c = ColorMap.value(sp, Qt::white); setPen(c); setBrush(c); } // Be consistent with old raster representation if (size > 14) size = 14; if (size >= 2) drawEllipse(pos.x() - 0.5 * size, pos.y() - 0.5 * size, int(size), int(size)); else if (size >= 1) drawPoint(pos.x(), pos.y()); } } bool SkyQPainter::drawConstellationArtImage(ConstellationsArt *obj) { double zoom = Options::zoomFactor(); bool visible = false; obj->EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat()); QPointF constellationmidpoint = m_proj->toScreen(obj, true, &visible); if (!visible || !m_proj->onScreen(constellationmidpoint)) return false; //qDebug() << "o->pa() " << obj->pa(); float positionangle = m_proj->findPA(obj, constellationmidpoint.x(), constellationmidpoint.y()); //qDebug() << " final PA " << positionangle; float w = obj->getWidth() * 60 * dms::PI * zoom / 10800; float h = obj->getHeight() * 60 * dms::PI * zoom / 10800; save(); setRenderHint(QPainter::SmoothPixmapTransform); translate(constellationmidpoint); rotate(positionangle); setOpacity(0.7); drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), obj->image()); setOpacity(1); setRenderHint(QPainter::SmoothPixmapTransform, false); restore(); return true; } bool SkyQPainter::drawHips() { int w = viewport().width(); int h = viewport().height(); QImage *hipsImage = new QImage(w, h, QImage::Format_ARGB32_Premultiplied); bool rendered = m_hipsRender->render(w, h, hipsImage, m_proj); if (rendered) drawImage(viewport(), *hipsImage); delete (hipsImage); return rendered; } bool SkyQPainter::drawDeepSkyObject(DeepSkyObject *obj, bool drawImage) { if (!m_proj->checkVisibility(obj)) return false; bool visible = false; QPointF pos = m_proj->toScreen(obj, true, &visible); if (!visible || !m_proj->onScreen(pos)) return false; // if size is 0.0 set it to 1.0, this are normally stars (type 0 and 1) // if we use size 0.0 the star wouldn't be drawn float majorAxis = obj->a(); if (majorAxis == 0.0) { majorAxis = 1.0; } float size = majorAxis * dms::PI * Options::zoomFactor() / 10800.0; //FIXME: this is probably incorrect float positionAngle = m_proj->findPA(obj, pos.x(), pos.y()); //Draw Image if (drawImage && Options::zoomFactor() > 5. * MINZOOM) drawDeepSkyImage(pos, obj, positionAngle); //Draw Symbol drawDeepSkySymbol(pos, obj->type(), size, obj->e(), positionAngle); return true; } bool SkyQPainter::drawDeepSkyImage(const QPointF &pos, DeepSkyObject *obj, float positionAngle) { double zoom = Options::zoomFactor(); double w = obj->a() * dms::PI * zoom / 10800.0; double h = obj->e() * w; save(); translate(pos); rotate(positionAngle); drawImage(QRectF(-0.5 * w, -0.5 * h, w, h), obj->image()); restore(); return true; } void SkyQPainter::drawDeepSkySymbol(const QPointF &pos, int type, float size, float e, float positionAngle) { float x = pos.x(); float y = pos.y(); float zoom = Options::zoomFactor(); int isize = int(size); float dx1 = -0.5 * size; float dx2 = 0.5 * size; float dy1 = -1.0 * e * size / 2.; float dy2 = e * size / 2.; float x1 = x + dx1; float x2 = x + dx2; float y1 = y + dy1; float y2 = y + dy2; float dxa = -size / 4.; float dxb = size / 4.; float dya = -1.0 * e * size / 4.; float dyb = e * size / 4.; float xa = x + dxa; float xb = x + dxb; float ya = y + dya; float yb = y + dyb; QString color; float psize; QBrush tempBrush; std::function lambdaDrawEllipse; std::function lambdaDrawLine; std::function lambdaDrawCross; if (Options::useAntialias()) { - lambdaDrawEllipse = [this](float x, float y, float width, float height) { + lambdaDrawEllipse = [this](float x, float y, float width, float height) + { drawEllipse(QRectF(x, y, width, height)); }; - lambdaDrawLine = [this](float x1, float y1, float x2, float y2) { drawLine(QLineF(x1, y1, x2, y2)); }; - lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY) { + lambdaDrawLine = [this](float x1, float y1, float x2, float y2) + { + drawLine(QLineF(x1, y1, x2, y2)); + }; + lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY) + { drawLine(QLineF(centerX - sizeX / 2., centerY, centerX + sizeX / 2., centerY)); drawLine(QLineF(centerX, centerY - sizeY / 2., centerX, centerY + sizeY / 2.)); }; } else { - lambdaDrawEllipse = [this](float x, float y, float width, float height) { + lambdaDrawEllipse = [this](float x, float y, float width, float height) + { drawEllipse(QRect(x, y, width, height)); }; - lambdaDrawLine = [this](float x1, float y1, float x2, float y2) { drawLine(QLine(x1, y1, x2, y2)); }; - lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY) { + lambdaDrawLine = [this](float x1, float y1, float x2, float y2) + { + drawLine(QLine(x1, y1, x2, y2)); + }; + lambdaDrawCross = [this](float centerX, float centerY, float sizeX, float sizeY) + { drawLine(QLine(centerX - sizeX / 2., centerY, centerX + sizeX / 2., centerY)); drawLine(QLine(centerX, centerY - sizeY / 2., centerX, centerY + sizeY / 2.)); }; } switch (type) { case 0: case 1: //catalog star //Some NGC/IC objects are stars...changed their type to 1 (was double star) if (size < 2.) size = 2.; lambdaDrawEllipse(x - size / 2., y - size / 2., size, size); break; case 2: //Planet break; case 3: //Open cluster; draw circle of points case 13: // Asterism { tempBrush = brush(); color = pen().color().name(); setBrush(pen().color()); psize = 2.; if (size > 50.) psize *= 2.; if (size > 100.) psize *= 2.; - auto putDot = [psize, &lambdaDrawEllipse](float x, float y) { + auto putDot = [psize, &lambdaDrawEllipse](float x, float y) + { lambdaDrawEllipse(x - psize / 2., y - psize / 2., psize, psize); }; putDot(xa, y1); putDot(xb, y1); putDot(xa, y2); putDot(xb, y2); putDot(x1, ya); putDot(x1, yb); putDot(x2, ya); putDot(x2, yb); setBrush(tempBrush); break; } case 4: //Globular Cluster if (size < 2.) size = 2.; save(); translate(x, y); color = pen().color().name(); rotate(positionAngle); //rotate the coordinate system lambdaDrawEllipse(dx1, dy1, size, e * size); lambdaDrawCross(0, 0, size, e * size); restore(); //reset coordinate system break; case 5: //Gaseous Nebula case 15: // Dark Nebula save(); translate(x, y); rotate(positionAngle); //rotate the coordinate system color = pen().color().name(); lambdaDrawLine(dx1, dy1, dx2, dy1); lambdaDrawLine(dx2, dy1, dx2, dy2); lambdaDrawLine(dx2, dy2, dx1, dy2); lambdaDrawLine(dx1, dy2, dx1, dy1); restore(); //reset coordinate system break; case 6: //Planetary Nebula if (size < 2.) size = 2.; save(); translate(x, y); rotate(positionAngle); //rotate the coordinate system color = pen().color().name(); lambdaDrawEllipse(dx1, dy1, size, e * size); lambdaDrawLine(0., dy1, 0., dy1 - e * size / 2.); lambdaDrawLine(0., dy2, 0., dy2 + e * size / 2.); lambdaDrawLine(dx1, 0., dx1 - size / 2., 0.); lambdaDrawLine(dx2, 0., dx2 + size / 2., 0.); restore(); //reset coordinate system break; case 7: //Supernova remnant // FIXME: Why is SNR drawn different from a gaseous nebula? save(); translate(x, y); rotate(positionAngle); //rotate the coordinate system color = pen().color().name(); lambdaDrawLine(0., dy1, dx2, 0.); lambdaDrawLine(dx2, 0., 0., dy2); lambdaDrawLine(0., dy2, dx1, 0.); lambdaDrawLine(dx1, 0., 0., dy1); restore(); //reset coordinate system break; case 8: //Galaxy case 16: // Quasar color = pen().color().name(); if (size < 1. && zoom > 20 * MINZOOM) size = 3.; //force ellipse above zoomFactor 20 if (size < 1. && zoom > 5 * MINZOOM) size = 1.; //force points above zoomFactor 5 if (size > 2.) { save(); translate(x, y); rotate(positionAngle); //rotate the coordinate system lambdaDrawEllipse(dx1, dy1, size, e * size); restore(); //reset coordinate system } else if (size > 0.) { drawPoint(QPointF(x, y)); } break; case 14: // Galaxy cluster - draw a dashed circle { tempBrush = brush(); setBrush(QBrush()); color = pen().color().name(); save(); translate(x, y); rotate(positionAngle); //rotate the coordinate system QPen newPen = pen(); newPen.setStyle(Qt::DashLine); setPen(newPen); lambdaDrawEllipse(dx1, dy1, size, e * size); restore(); setBrush(tempBrush); break; } default: // Unknown object or something we don't know how to draw. Just draw an ellipse with a ?-mark color = pen().color().name(); if (size < 1. && zoom > 20 * MINZOOM) size = 3.; //force ellipse above zoomFactor 20 if (size < 1. && zoom > 5 * MINZOOM) size = 1.; //force points above zoomFactor 5 if (size > 2.) { save(); QFont f = font(); const QString qMark = " ? "; double scaleFactor = 0.8 * size / fontMetrics().width(qMark); f.setPointSizeF(f.pointSizeF() * scaleFactor); setFont(f); translate(x, y); rotate(positionAngle); //rotate the coordinate system lambdaDrawEllipse(dx1, dy1, size, e * size); if (Options::useAntialias()) drawText(QRectF(dx1, dy1, size, e * size), Qt::AlignCenter, qMark); else { int idx1 = int(dx1); int idy1 = int(dy1); drawText(QRect(idx1, idy1, isize, int(e * size)), Qt::AlignCenter, qMark); } restore(); //reset coordinate system (and font?) } else if (size > 0.) { if (Options::useAntialias()) drawPoint(QPointF(x, y)); else drawPoint(QPoint(x, y)); } } } void SkyQPainter::drawObservingList(const QList &obs) { foreach (SkyObject *obj, obs) { bool visible = false; QPointF o = m_proj->toScreen(obj, true, &visible); if (!visible || !m_proj->onScreen(o)) continue; float size = 20.; float x1 = o.x() - 0.5 * size; float y1 = o.y() - 0.5 * size; drawArc(QRectF(x1, y1, size, size), -60 * 16, 120 * 16); drawArc(QRectF(x1, y1, size, size), 120 * 16, 120 * 16); } } void SkyQPainter::drawFlags() { KStarsData *data = KStarsData::Instance(); std::shared_ptr point; QImage image; bool visible = false; QPointF pos; for (int i = 0; i < data->skyComposite()->flags()->size(); i++) { point = data->skyComposite()->flags()->pointList().at(i); image = data->skyComposite()->flags()->image(i); // Set Horizontal coordinates point->EquatorialToHorizontal(data->lst(), data->geo()->lat()); // Get flag position on screen pos = m_proj->toScreen(point.get(), true, &visible); // Return if flag is not visible if (!visible || !m_proj->onScreen(pos)) continue; // Draw flag image drawImage(pos.x() - 0.5 * image.width(), pos.y() - 0.5 * image.height(), image); // Draw flag label setPen(data->skyComposite()->flags()->labelColor(i)); setFont(QFont("Helvetica", 10, QFont::Bold)); drawText(pos.x() + 10, pos.y() - 10, data->skyComposite()->flags()->label(i)); } } void SkyQPainter::drawHorizon(bool filled, SkyPoint *labelPoint, bool *drawLabel) { QVector ground = m_proj->groundPoly(labelPoint, drawLabel); if (ground.size()) { QPolygonF groundPoly(ground.size()); for (int i = 0; i < ground.size(); ++i) groundPoly[i] = KSUtils::vecToPoint(ground[i]); if (filled) drawPolygon(groundPoly); else { groundPoly.append(groundPoly.first()); drawPolyline(groundPoly); } } } bool SkyQPainter::drawSatellite(Satellite *sat) { if (!m_proj->checkVisibility(sat)) return false; QPointF pos; bool visible = false; //sat->HorizontalToEquatorial( data->lst(), data->geo()->lat() ); pos = m_proj->toScreen(sat, true, &visible); if (!visible || !m_proj->onScreen(pos)) return false; if (Options::drawSatellitesLikeStars()) { drawPointSource(pos, 3.5, 'B'); } else { if (sat->isVisible()) drawPixmap(QPoint(pos.x() - 15, pos.y() - 11), *visibleSatPixmap); else drawPixmap(QPoint(pos.x() - 15, pos.y() - 11), *invisibleSatPixmap); //drawPixmap(pos, *genericSatPixmap); /*drawLine( QPoint( pos.x() - 0.5, pos.y() - 0.5 ), QPoint( pos.x() + 0.5, pos.y() - 0.5 ) ); drawLine( QPoint( pos.x() + 0.5, pos.y() - 0.5 ), QPoint( pos.x() + 0.5, pos.y() + 0.5 ) ); drawLine( QPoint( pos.x() + 0.5, pos.y() + 0.5 ), QPoint( pos.x() - 0.5, pos.y() + 0.5 ) ); drawLine( QPoint( pos.x() - 0.5, pos.y() + 0.5 ), QPoint( pos.x() - 0.5, pos.y() - 0.5 ) );*/ } return true; //if ( Options::showSatellitesLabels() ) //data->skyComposite()->satellites()->drawLabel( sat, pos ); } bool SkyQPainter::drawSupernova(Supernova *sup) { KStarsData *data = KStarsData::Instance(); if (!m_proj->checkVisibility(sup)) { return false; } bool visible = false; QPointF pos = m_proj->toScreen(sup, true, &visible); //qDebug()<<"sup->ra() = "<<(sup->ra()).toHMSString()<<"sup->dec() = "<dec().toDMSString(); //qDebug()<<"pos = "<onScreen(pos) = "<onScreen(pos); if (!visible || !m_proj->onScreen(pos)) return false; setPen(data->colorScheme()->colorNamed("SupernovaColor")); //qDebug()<<"Here"< ApproachSolver::ApproachSolver(QObject *parent) : QObject(parent) { m_geoPlace = KStarsData::Instance()->geo(); - m_Earth = KSPlanet(I18N_NOOP("Earth"), QString(), QColor("white"), 12756.28 /*diameter in km*/); + m_Earth = KSPlanet(i18n("Earth"), QString(), QColor("white"), 12756.28 /*diameter in km*/); } void ApproachSolver::setGeoLocation(GeoLocation *geo) { if (geo != nullptr) m_geoPlace = geo; else m_geoPlace = KStarsData::Instance()->geo(); } // FIXME: We need a better algo for finding approaches! QMap ApproachSolver::findClosestApproach(long double startJD, - long double stopJD, std::function const & callback) + long double stopJD, std::function const &callback) { QMap Separations; QPair extremum; dms Dist; dms prevDist; double step, step0; int Sign, prevSign; // qCDebug(KSTARS) << "Entered KSConjunct::findClosestApproach() with startJD = " << (double)startJD; // qCDebug(KSTARS) << "Initial Positional Information: \n"; // qCDebug(KSTARS) << m_object1->name() << ": RA = " << m_object1->ra() -> toHMSString() << "; Dec = " << m_object1->dec() -> toDMSString() << "\n"; // qCDebug(KSTARS) << m_object2->name() << ": RA = " << m_object2->ra() -> toHMSString() << "; Dec = " << m_object2->dec() -> toDMSString() << "\n"; prevSign = 0; step0 = findInitialStep(startJD, stopJD); step = step0; // qCDebug(KSTARS) << "Initial Separation between " << m_object1->name() << " and " << m_object2->name() << " = " << (prevDist.toDMSString()); long double jd = startJD; prevDist = updateAndFindDistance(jd); jd += step; while (jd <= stopJD) { int progress = int(100.0 * (jd - startJD) / (stopJD - startJD)); emit solverMadeProgress(progress); Dist = updateAndFindDistance(jd); Sign = sgn(Dist - prevDist); // qCDebug(KSTARS) << "Dist = " << Dist.toDMSString() << "; prevDist = " << prevDist.toDMSString() << "; Difference = " << (Dist.Degrees() - prevDist.Degrees()) << "; Step = " << step; //How close are we to a conjunction, and how fast are we approaching one? double factor = fabs((Dist.Degrees() - prevDist.Degrees()) / Dist.Degrees()); if (factor > 10.0) //let's go faster! { step = step0 * factor / 10.0; } else //slow down, we're getting close! { step = step0; } if (Sign != prevSign && prevSign == -1) //all right, we may have just passed a conjunction { if (step > step0) //mini-loop to back up and make sure we're close enough { // qCDebug(KSTARS) << "Entering slow loop: " << endl; jd -= step; step = step0; Sign = prevSign; while (jd <= stopJD) { Dist = updateAndFindDistance(jd); Sign = sgn(Dist - prevDist); // qCDebug(KSTARS) << "Dist=" << Dist.toDMSString() << "; prevDist=" << prevDist.toDMSString() << "; Diff=" << (Dist.Degrees() - prevDist.Degrees()) << "djd=" << (int)(jd - startJD); if (Sign != prevSign) break; prevDist = Dist; prevSign = Sign; jd += step; } } // qCDebug(KSTARS) << "Sign = " << Sign << " and " << "prevSign = " << prevSign << ": Entering findPrecise()\n"; - if (findPrecise(&extremum, jd, step, Sign)){ - if (extremum.second.radians() < getMaxSeparation()) { + if (findPrecise(&extremum, jd, step, Sign)) + { + if (extremum.second.radians() < getMaxSeparation()) + { Separations.insert(extremum.first, extremum.second); if(callback) callback(extremum.first, extremum.second); } } } prevDist = Dist; prevSign = Sign; jd += step; } return Separations; } bool ApproachSolver::findPrecise(QPair *out, long double jd, - double step, int prevSign) + double step, int prevSign) { dms prevDist; int Sign; dms Dist; if (out == nullptr) { qCDebug(KSTARS) << "ERROR: Argument out to KSConjunct::findPrecise(...) was nullptr!"; return false; } prevDist = updateAndFindDistance(jd); step = -step / 2.0; prevSign = -prevSign; while (true) { jd += step; Dist = updateAndFindDistance(jd); // qCDebug(KSTARS) << "Dist=" << Dist.toDMSString() << "; prevDist=" << prevDist.toDMSString() << "; Diff=" << (Dist.Degrees() - prevDist.Degrees()) << "step=" << step; if (fabs(step) < 1.0 / (24.0 * 60.0)) { out->first = jd - step / 2.0; out->second = updateAndFindDistance(jd - step / 2.0); if (out->second.radians() < updateAndFindDistance(jd - 5.0).radians()) return true; else return false; } Sign = sgn(Dist - prevDist); if (Sign != prevSign) { step = -step / 2.0; Sign = -Sign; } prevSign = Sign; prevDist = Dist; } } dms ApproachSolver::findSkyPointDistance(SkyPoint * obj1, SkyPoint * obj2) { dms dist; dist.setRadians(obj1->angularDistanceTo(obj2).radians()); return dist; } int ApproachSolver::sgn(dms a) { // Auxiliary function used by the KSConjunct::findClosestApproach(...) // method and the KSConjunct::findPrecise(...) method return ((a.radians() > 0) ? 1 : ((a.radians() < 0) ? -1 : 0)); } diff --git a/kstars/tools/ksconjunct.cpp b/kstars/tools/ksconjunct.cpp index c380376a3..ffac6c90d 100644 --- a/kstars/tools/ksconjunct.cpp +++ b/kstars/tools/ksconjunct.cpp @@ -1,91 +1,91 @@ /*************************************************************************** ksconjunct.cpp - K Desktop Planetarium ------------------- begin : Sat Mar 22 2008 copyright : (C) 2008 by Akarsh Simha email : kstar@bas.org.in ***************************************************************************/ /*************************************************************************** * * * 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 "ksconjunct.h" #include "ksnumbers.h" #include "kstarsdata.h" #include "skyobjects/skyobject.h" #include "skyobjects/ksplanetbase.h" #include KSConjunct::KSConjunct() : ApproachSolver () { connect(this, &ApproachSolver::solverMadeProgress, this, &KSConjunct::madeProgress); } dms KSConjunct::findDistance() { dms dist = findSkyPointDistance(m_object1.get(), m_object2.get()); if (m_opposition) { dist.setD(180 - dist.Degrees()); } return dist; } void KSConjunct::updatePositions(long double jd) { KStarsDateTime t(jd); KSNumbers num(jd); m_Earth.findPosition(&num); CachingDms LST(getGeoLocation()->GSTtoLST(t.gst())); KSPlanetBase *p = dynamic_cast(m_object1.get()); if (p) p->findPosition(&num, getGeoLocation()->lat(), &LST, &m_Earth); else m_object1->updateCoordsNow(&num); m_object2->findPosition(&num, getGeoLocation()->lat(), &LST, &m_Earth); } double KSConjunct::findInitialStep(long double startJD, long double stopJD) { double step0 = double(stopJD - startJD) / 4.0; // I'm an idiot for having done this without having the lines that follow -- asimha // TODO: Work out a solid footing on which one can decide step0. -- asimha if (step0 > 24.8 * 365.25) // Sample pluto's orbit (248.09 years) at least 10 times. step0 = 24.8 * 365.25; // FIXME: This can be done better, but for now, I'm doing it the dumb way -- asimha if (m_object1->name() == i18n("Neptune") || m_object2->name() == i18n("Neptune") || m_object1->name() == i18n("Uranus") || - m_object2->name() == i18n("Uranus")) + m_object2->name() == i18n("Uranus")) if (step0 > 3652.5) step0 = 3652.5; if (m_object1->name() == i18n("Jupiter") || m_object2->name() == i18n("Jupiter") || m_object1->name() == i18n("Saturn") || - m_object2->name() == i18n("Saturn")) + m_object2->name() == i18n("Saturn")) if (step0 > 365.25) step0 = 365; if (m_object1->name() == i18n("Mars") || m_object2->name() == i18n("Mars")) if (step0 > 10.0) step0 = 10.0; if (m_object1->name() == i18n("Venus") || m_object1->name() == i18n("Mercury") || m_object2->name() == i18n("Mercury") || - m_object2->name() == i18n("Venus")) + m_object2->name() == i18n("Venus")) if (step0 > 5.0) step0 = 5.0; - if (m_object1->name() == "Moon" || m_object2->name() == "Moon") + if (m_object1->name() == i18n("Moon") || m_object2->name() == i18n("Moon")) if (step0 > 0.25) step0 = 0.25; return step0; } diff --git a/kstars/tools/modcalcplanets.cpp b/kstars/tools/modcalcplanets.cpp index 23a48f238..bc89ec63c 100644 --- a/kstars/tools/modcalcplanets.cpp +++ b/kstars/tools/modcalcplanets.cpp @@ -1,453 +1,453 @@ /*************************************************************************** modcalcequinox.cpp - description ------------------- begin : dom may 2 2004 copyright : (C) 2004-2005 by Pablo de Vicente email : p.devicentea@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 "modcalcplanets.h" #include "geolocation.h" #include "kstarsdata.h" #include "dialogs/locationdialog.h" #include "skyobjects/ksmoon.h" #include "skyobjects/kssun.h" modCalcPlanets::modCalcPlanets(QWidget *parentSplit) : QFrame(parentSplit) { setupUi(this); KStarsDateTime dt(KStarsDateTime::currentDateTime()); DateTimeBox->setDateTime(dt); DateBoxBatch->setDate(dt.date()); UTBoxBatch->setTime(dt.time()); geoPlace = KStarsData::Instance()->geo(); LocationButton->setText(geoPlace->fullName()); RABox->setDegType(false); // signals and slots connections connect(PlanetComboBox, SIGNAL(activated(int)), this, SLOT(slotComputePosition())); connect(DateTimeBox, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(slotComputePosition())); connect(LocationButton, SIGNAL(clicked()), this, SLOT(slotLocation())); connect(UTCheckBatch, SIGNAL(clicked()), this, SLOT(slotUtCheckedBatch())); connect(DateCheckBatch, SIGNAL(clicked()), this, SLOT(slotDateCheckedBatch())); connect(LatCheckBatch, SIGNAL(clicked()), this, SLOT(slotLatCheckedBatch())); connect(LongCheckBatch, SIGNAL(clicked()), this, SLOT(slotLongCheckedBatch())); connect(PlanetCheckBatch, SIGNAL(clicked()), this, SLOT(slotPlanetsCheckedBatch())); slotComputePosition(); show(); } void modCalcPlanets::slotLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { geoPlace = ld->selectedCity(); LocationButton->setText(geoPlace->fullName()); slotComputePosition(); } delete ld; } void modCalcPlanets::slotComputePosition() { KStarsDateTime dt(DateTimeBox->dateTime()); long double julianDay = dt.djd(); KSNumbers num(julianDay); CachingDms LST(geoPlace->GSTtoLST(dt.gst())); // Earth - KSPlanet Earth(I18N_NOOP("Earth")); + KSPlanet Earth(i18n("Earth")); Earth.findPosition(&num); // Earth is special case! if (PlanetComboBox->currentIndex() == 2) { showCoordinates(Earth); return; } // Pointer to hold planet data. Pointer is used since it has to // hold objects of different type. It's safe to use new/delete // because exceptions are disallowed. std::unique_ptr p; switch (PlanetComboBox->currentIndex()) { case 0: p.reset(new KSPlanet(KSPlanetBase::MERCURY)); break; case 1: p.reset(new KSPlanet(KSPlanetBase::VENUS)); break; case 3: p.reset(new KSPlanet(KSPlanetBase::MARS)); break; case 4: p.reset(new KSPlanet(KSPlanetBase::JUPITER)); break; case 5: p.reset(new KSPlanet(KSPlanetBase::SATURN)); break; case 6: p.reset(new KSPlanet(KSPlanetBase::URANUS)); break; case 7: p.reset(new KSPlanet(KSPlanetBase::NEPTUNE)); break; /*case 8: p.reset(new KSPluto(); break;*/ case 8: p.reset(new KSMoon()); break; case 9: p.reset(new KSSun()); p->setRsun(0.0); break; } if (p.get() == nullptr) return; // Show data. p->findPosition(&num, geoPlace->lat(), &LST, &Earth); p->EquatorialToHorizontal(&LST, geoPlace->lat()); showCoordinates(*p); } void modCalcPlanets::showCoordinates(const KSPlanetBase &ksp) { showHeliocentricEclipticCoords(ksp.helEcLong(), ksp.helEcLat(), ksp.rsun()); showGeocentricEclipticCoords(ksp.ecLong(), ksp.ecLat(), ksp.rearth()); showEquatorialCoords(ksp.ra(), ksp.dec()); showTopocentricCoords(ksp.az(), ksp.alt()); } void modCalcPlanets::showHeliocentricEclipticCoords(const dms &hLong, const dms &hLat, double dist) { HelioLongBox->show(hLong); HelioLatBox->show(hLat); HelioDistBox->setText(QLocale().toString(dist, 6)); } void modCalcPlanets::showGeocentricEclipticCoords(const dms &eLong, const dms &eLat, double dist) { GeoLongBox->show(eLong); GeoLatBox->show(eLat); GeoDistBox->setText(QLocale().toString(dist, 6)); } void modCalcPlanets::showEquatorialCoords(const dms &ra, const dms &dec) { RABox->show(ra, false); DecBox->show(dec); } void modCalcPlanets::showTopocentricCoords(const dms &az, const dms &el) { AzBox->show(az); AltBox->show(el); } void modCalcPlanets::slotPlanetsCheckedBatch() { PlanetComboBoxBatch->setEnabled(!PlanetCheckBatch->isChecked()); } void modCalcPlanets::slotUtCheckedBatch() { UTBoxBatch->setEnabled(!UTCheckBatch->isChecked()); } void modCalcPlanets::slotDateCheckedBatch() { DateBoxBatch->setEnabled(!DateCheckBatch->isChecked()); } void modCalcPlanets::slotLongCheckedBatch() { LongBoxBatch->setEnabled(!LongCheckBatch->isChecked()); } void modCalcPlanets::slotLatCheckedBatch() { LatBoxBatch->setEnabled(!LatCheckBatch->isChecked()); } void modCalcPlanets::slotRunBatch() { const QString inputFileName = InputFileBoxBatch->url().toLocalFile(); // We open the input file and read its content if (QFile::exists(inputFileName)) { QFile f(inputFileName); if (!f.open(QIODevice::ReadOnly)) { QString message = i18n("Could not open file %1.", f.fileName()); KMessageBox::sorry(nullptr, message, i18n("Could Not Open File")); return; } QTextStream istream(&f); processLines(istream); f.close(); } else { QString message = i18n("Invalid file: %1", inputFileName); KMessageBox::sorry(nullptr, message, i18n("Invalid file")); InputFileBoxBatch->setUrl(QUrl()); } } unsigned int modCalcPlanets::requiredBatchFields() { unsigned int i = 0; if (PlanetCheckBatch->isChecked()) i++; if (UTCheckBatch->isChecked()) i++; if (DateCheckBatch->isChecked()) i++; if (LongCheckBatch->isChecked()) i++; if (LatCheckBatch->isChecked()) i++; return i; } void modCalcPlanets::processLines(QTextStream &istream) { // we open the output file const QString outputFileName = OutputFileBoxBatch->url().toLocalFile(); QFile fOut(outputFileName); fOut.open(QIODevice::WriteOnly); QTextStream ostream(&fOut); bool lineIsValid = true; QChar space = ' '; QString planetB; unsigned int i = 0, nline = 0; QTime utB; QDate dtB; CachingDms longB, latB, hlongB, hlatB, glongB, glatB, raB, decB, azmB, altB; double rSunB(0.0), rEarthB(0.0); //Initialize planet names QString pn; QStringList pNames, pNamesi18n; pNames << "Mercury" << "Venus" << "Earth" << "Mars" << "Jupiter" << "Saturn" << "Uranus" << "Neptune" /* << "Pluto" */ << "Sun" << "Moon"; pNamesi18n << i18n("Mercury") << i18n("Venus") << i18n("Earth") << i18n("Mars") << i18n("Jupiter") << i18n("Saturn") << i18n("Uranus") << i18n("Neptune") /* << i18nc("Asteroid name (optional)", "Pluto") */ << i18n("Sun") << i18n("Moon"); ///Parse the input file int numberOfRequiredFields = requiredBatchFields(); while (!istream.atEnd()) { QString lineToWrite; QString line = istream.readLine(); line = line.trimmed(); //Go through the line, looking for parameters QStringList fields = line.split(' '); if (fields.count() != numberOfRequiredFields) { lineIsValid = false; qWarning() << i18n("Incorrect number of fields in line %1: ", nline) << i18n("Present fields %1. ", fields.count()) << i18n("Required fields %1. ", numberOfRequiredFields) << endl; nline++; continue; } i = 0; if (PlanetCheckBatch->isChecked()) { planetB = fields[i]; int j = pNamesi18n.indexOf(planetB); if (j == -1) { qWarning() << i18n("Unknown planet ") << fields[i] << i18n(" in line %1: ", nline) << endl; continue; } pn = pNames.at(j); //untranslated planet name i++; } else { planetB = PlanetComboBoxBatch->currentText(); } if (AllRadioBatch->isChecked() || PlanetCheckBatch->isChecked()) { lineToWrite = planetB; lineToWrite += space; } // Read Ut and write in ostream if corresponds if (UTCheckBatch->isChecked()) { utB = QTime::fromString(fields[i]); if (!utB.isValid()) { qWarning() << i18n("Line %1 contains an invalid time", nline); lineIsValid = false; nline++; continue; } i++; } else { utB = UTBoxBatch->time(); } if (AllRadioBatch->isChecked() || UTCheckBatch->isChecked()) lineToWrite += QLocale().toString(utB).append(space); // Read date and write in ostream if corresponds if (DateCheckBatch->isChecked()) { dtB = QDate::fromString(fields[i], Qt::ISODate); if (!dtB.isValid()) { qWarning() << i18n("Line %1 contains an invalid date: ", nline) << fields[i] << endl; lineIsValid = false; nline++; continue; } i++; } else { dtB = DateBoxBatch->date(); } if (AllRadioBatch->isChecked() || DateCheckBatch->isChecked()) lineToWrite += QLocale().toString(dtB, QLocale::LongFormat).append(space); // Read Longitude and write in ostream if corresponds if (LongCheckBatch->isChecked()) { longB = CachingDms::fromString(fields[i], true); i++; } else { longB = LongBoxBatch->createDms(true); } if (AllRadioBatch->isChecked() || LongCheckBatch->isChecked()) lineToWrite += longB.toDMSString() + space; // Read Latitude if (LatCheckBatch->isChecked()) { latB = CachingDms::fromString(fields[i], true); i++; } else { latB = LatBoxBatch->createDms(true); } if (AllRadioBatch->isChecked() || LatCheckBatch->isChecked()) lineToWrite += latB.toDMSString() + space; KStarsDateTime edt(dtB, utB); CachingDms LST = edt.gst() + longB; KSNumbers num(edt.djd()); KSPlanet Earth(I18N_NOOP("Earth")); Earth.findPosition(&num); // FIXME: allocate new object for every iteration is probably not wisest idea. KSPlanetBase *kspb = nullptr; /*if ( pn == "Pluto" ) { kspb = new KSPluto(); } else*/ - if (pn == "Sun") + if (pn == i18n("Sun")) { kspb = new KSSun(); } - else if (pn == "Moon") + else if (pn == i18n("Moon")) { kspb = new KSMoon(); } else { kspb = new KSPlanet(i18n(pn.toLocal8Bit()), QString(), Qt::white, 1.0); } kspb->findPosition(&num, &latB, &LST, &Earth); kspb->EquatorialToHorizontal(&LST, &latB); // Heliocentric Ecl. coords. hlongB = kspb->helEcLong(); hlatB = kspb->helEcLat(); rSunB = kspb->rsun(); // Geocentric Ecl. coords. glongB = kspb->ecLong(); glatB = kspb->ecLat(); rEarthB = kspb->rearth(); // Equatorial coords. decB = kspb->dec(); raB = kspb->ra(); // Topocentric Coords. azmB = kspb->az(); altB = kspb->alt(); ostream << lineToWrite; if (HelioEclCheckBatch->isChecked()) ostream << hlongB.toDMSString() << space << hlatB.toDMSString() << space << rSunB << space; if (GeoEclCheckBatch->isChecked()) ostream << glongB.toDMSString() << space << glatB.toDMSString() << space << rEarthB << space; if (EquatorialCheckBatch->isChecked()) ostream << raB.toHMSString() << space << decB.toDMSString() << space; if (HorizontalCheckBatch->isChecked()) ostream << azmB.toDMSString() << space << altB.toDMSString() << space; ostream << endl; // Delete object delete kspb; nline++; } if (!lineIsValid) { QString message = i18n("Errors found while parsing some lines in the input file"); KMessageBox::sorry(nullptr, message, i18n("Errors in lines")); } fOut.close(); } diff --git a/kstars/tools/obslistwizard.cpp b/kstars/tools/obslistwizard.cpp index 681e6f0a0..6ba990a4c 100644 --- a/kstars/tools/obslistwizard.cpp +++ b/kstars/tools/obslistwizard.cpp @@ -1,983 +1,983 @@ /*************************************************************************** obslistwizard.cpp - Display overhead view of the solar system ------------------- begin : Thu 23 Jun 2005 copyright : (C) 2005 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "obslistwizard.h" #include "Options.h" #include "geolocation.h" #include "kstarsdata.h" #include "dialogs/locationdialog.h" #include "skycomponents/constellationboundarylines.h" #include "skycomponents/skymapcomposite.h" #include "skyobjects/deepskyobject.h" ObsListWizardUI::ObsListWizardUI(QWidget *p) : QFrame(p) { setupUi(this); } ObsListWizard::ObsListWizard(QWidget *ksparent) : QDialog(ksparent) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif olw = new ObsListWizardUI(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(olw); setLayout(mainLayout); setWindowTitle(i18n("Observing List Wizard")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, Qt::Horizontal); nextB = new QPushButton(i18n("&Next >")); nextB->setDefault(true); backB = new QPushButton(i18n("< &Back")); backB->setEnabled(false); buttonBox->addButton(backB, QDialogButtonBox::ActionRole); buttonBox->addButton(nextB, QDialogButtonBox::ActionRole); mainLayout->addWidget(buttonBox); connect(nextB, SIGNAL(clicked()), this, SLOT(slotNextPage())); connect(backB, SIGNAL(clicked()), this, SLOT(slotPrevPage())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(slotApplyFilters())); connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept())); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); connect(olw->AllButton, SIGNAL(clicked()), this, SLOT(slotAllButton())); connect(olw->NoneButton, SIGNAL(clicked()), this, SLOT(slotNoneButton())); connect(olw->DeepSkyButton, SIGNAL(clicked()), this, SLOT(slotDeepSkyButton())); connect(olw->SolarSystemButton, SIGNAL(clicked()), this, SLOT(slotSolarSystemButton())); connect(olw->LocationButton, SIGNAL(clicked()), this, SLOT(slotChangeLocation())); //Update the count of objects when the user asks for it connect(olw->updateButton, SIGNAL(clicked()), this, SLOT(slotUpdateObjectCount())); // Enable the update count button when certain elements are changed connect(olw->TypeList, &QListWidget::itemSelectionChanged, this, &ObsListWizard::slotObjectCountDirty); connect(olw->ConstellationList, &QListWidget::itemSelectionChanged, this, &ObsListWizard::slotObjectCountDirty); connect(olw->RAMin, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion); connect(olw->RAMax, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion); connect(olw->DecMin, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion); connect(olw->DecMax, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion); connect(olw->RA, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion); connect(olw->Dec, &QLineEdit::editingFinished, this, &ObsListWizard::slotParseRegion); connect(olw->Radius, &QLineEdit::editingFinished, this, &ObsListWizard::slotObjectCountDirty); connect(olw->Date, &QDateEdit::dateChanged, this, &ObsListWizard::slotObjectCountDirty); connect(olw->Mag, static_cast(&QDoubleSpinBox::valueChanged), this, &ObsListWizard::slotObjectCountDirty); connect(olw->IncludeNoMag, &QPushButton::clicked, this, &ObsListWizard::slotObjectCountDirty); connect(olw->timeTo, &QTimeEdit::timeChanged, this, &ObsListWizard::slotObjectCountDirty); connect(olw->timeFrom, &QTimeEdit::timeChanged, this, &ObsListWizard::slotObjectCountDirty); connect(olw->minAlt, static_cast(&QDoubleSpinBox::valueChanged), this, &ObsListWizard::slotObjectCountDirty); connect(olw->maxAlt, static_cast(&QDoubleSpinBox::valueChanged), this, &ObsListWizard::slotObjectCountDirty); olw->coverage->setValue(Options::obsListCoverage()); connect(olw->coverage, static_cast(&QDoubleSpinBox::valueChanged), [&](double value) { Options::setObsListCoverage(value); slotObjectCountDirty(); }); connect(olw->SelectByDate, SIGNAL(clicked()), this, SLOT(slotToggleDateWidgets())); connect(olw->SelectByMagnitude, SIGNAL(clicked()), this, SLOT(slotToggleMagWidgets())); geo = KStarsData::Instance()->geo(); olw->LocationButton->setText(geo->fullName()); olw->Date->setDate(KStarsDateTime::currentDateTime().date()); olw->timeFrom->setTime(QTime(18, 0)); olw->timeTo->setTime(QTime(23, 59)); initialize(); } void ObsListWizard::initialize() { KStarsData *data = KStarsData::Instance(); olw->olwStack->setCurrentIndex(0); //Populate the list of constellations foreach (SkyObject *p, data->skyComposite()->constellationNames()) olw->ConstellationList->addItem(p->name()); //unSelect all object types olw->TypeList->clearSelection(); olw->Mag->setMinimum(-5.0); olw->Mag->setMaximum(20.0); olw->Mag->setValue(6.0); olw->RA->setDegType(false); olw->RAMin->setDegType(false); olw->RAMax->setDegType(false); //Initialize object counts ObjectCount = 0; //number of objects in observing list StarCount = data->skyComposite()->stars().size(); //total number of stars PlanetCount = 10; //Sun, Moon, 8 planets AsteroidCount = data->skyComposite()->asteroids().size(); //total number of asteroids CometCount = data->skyComposite()->comets().size(); //total number of comets //DeepSkyObjects OpenClusterCount = 0; GlobClusterCount = 0; GasNebCount = 0; PlanNebCount = 0; GalaxyCount = 0; foreach (DeepSkyObject *o, data->skyComposite()->deepSkyObjects()) { if (o->type() == SkyObject::GALAXY) ++GalaxyCount; //most deepsky obj are galaxies, so check them first else if (o->type() == SkyObject::STAR || o->type() == SkyObject::CATALOG_STAR) ++StarCount; else if (o->type() == SkyObject::OPEN_CLUSTER) ++OpenClusterCount; else if (o->type() == SkyObject::GLOBULAR_CLUSTER) ++GlobClusterCount; else if (o->type() == SkyObject::GASEOUS_NEBULA || o->type() == SkyObject::SUPERNOVA_REMNANT) ++GasNebCount; else if (o->type() == SkyObject::PLANETARY_NEBULA) ++PlanNebCount; } } bool ObsListWizard::isItemSelected(const QString &name, QListWidget *listWidget, bool *ok) { /*QList items = listWidget->findItems(name, Qt::MatchContains); if (ok) *ok = items.size(); return items.size() && listWidget->isItemSelected(items[0]); ;*/ foreach(QListWidgetItem *item, listWidget->selectedItems()) { if (item->text().compare(name, Qt::CaseInsensitive) == 0) { if (ok) *ok = true; return true; } } if (ok) *ok = false; return false; } void ObsListWizard::setItemSelected(const QString &name, QListWidget *listWidget, bool value, bool *ok) { QList items = listWidget->findItems(name, Qt::MatchContains); if (ok) *ok = items.size(); if (items.size()) listWidget->setItemSelected(items[0], value); } //Advance to the next page in the stack. However, on page 2 the user //selects what regional filter they want to use, and this determines //what the page following page 2 should be: // + Constellation(s): the next page index is 3 // + Rectangular region: the next page index is 4 // + Circular region: the next page index is 5 // + No region selected (a.k.a. "all over the sky"): the next page index is 6 // //Also, if the current page index is 3 or 4, then the next page should be 6. // //NOTE: the page indexes are hard-coded here, which isn't ideal. However, //There's no easy way to access the pointers of widgets in the stack //if you didn't save them at the start. void ObsListWizard::slotNextPage() { int NextPage = olw->olwStack->currentIndex() + 1; if (olw->olwStack->currentIndex() == 2) { //On the Region select page. Determine what //the next page index should be. //No need to handle "by constellation, it's already currentIndex + 1. if (isItemSelected(i18n("in a rectangular region"), olw->RegionList)) NextPage = 4; if (isItemSelected(i18n("in a circular region"), olw->RegionList)) NextPage = 5; if (isItemSelected(i18n("all over the sky"), olw->RegionList)) NextPage = 6; } if (olw->olwStack->currentIndex() == 3 || olw->olwStack->currentIndex() == 4) NextPage = 6; olw->olwStack->setCurrentIndex(NextPage); if (olw->olwStack->currentIndex() == olw->olwStack->count() - 1) nextB->setEnabled(false); backB->setEnabled(true); } //Advance to the previous page in the stack. However, because the //path through the wizard branches depending on the user's choice of //Region filter, the previous page is not always currentPage-1. //Specifically, if the current page index is 4, 5, or 6, then the Previous //page index should be 2 rather than currentIndex-1. void ObsListWizard::slotPrevPage() { int PrevPage = olw->olwStack->currentIndex() - 1; if (olw->olwStack->currentIndex() == 4 || olw->olwStack->currentIndex() == 5 || olw->olwStack->currentIndex() == 6) PrevPage = 2; olw->olwStack->setCurrentIndex(PrevPage); if (olw->olwStack->currentIndex() == 0) backB->setEnabled(false); nextB->setEnabled(true); } void ObsListWizard::slotAllButton() { for (int i = 0; i < olw->TypeList->count(); ++i) olw->TypeList->setItemSelected(olw->TypeList->item(i), true); } void ObsListWizard::slotNoneButton() { olw->TypeList->clearSelection(); } void ObsListWizard::slotDeepSkyButton() { olw->TypeList->clearSelection(); setItemSelected(i18n("Open clusters"), olw->TypeList, true); setItemSelected(i18n("Globular clusters"), olw->TypeList, true); setItemSelected(i18n("Gaseous nebulae"), olw->TypeList, true); setItemSelected(i18n("Planetary nebulae"), olw->TypeList, true); setItemSelected(i18n("Galaxies"), olw->TypeList, true); } void ObsListWizard::slotSolarSystemButton() { olw->TypeList->clearSelection(); setItemSelected(i18n("Sun, moon, planets"), olw->TypeList, true); setItemSelected(i18n("Comets"), olw->TypeList, true); setItemSelected(i18n("Asteroids"), olw->TypeList, true); } void ObsListWizard::slotChangeLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { //set geographic location if (ld->selectedCity()) { geo = ld->selectedCity(); olw->LocationButton->setText(geo->fullName()); } } delete ld; } void ObsListWizard::slotToggleDateWidgets() { olw->Date->setEnabled(olw->SelectByDate->isChecked()); olw->LocationButton->setEnabled(olw->SelectByDate->isChecked()); olw->timeTo->setEnabled(olw->SelectByDate->isChecked()); olw->timeFrom->setEnabled(olw->SelectByDate->isChecked()); olw->minAlt->setEnabled(olw->SelectByDate->isChecked()); olw->maxAlt->setEnabled(olw->SelectByDate->isChecked()); // slotUpdateObjectCount(); slotObjectCountDirty(); } void ObsListWizard::slotToggleMagWidgets() { olw->Mag->setEnabled(olw->SelectByMagnitude->isChecked()); olw->IncludeNoMag->setEnabled(olw->SelectByMagnitude->isChecked()); slotObjectCountDirty(); // slotUpdateObjectCount(); } void ObsListWizard::slotParseRegion() { if (sender()->objectName() == "RAMin" || sender()->objectName() == "RAMax" || sender()->objectName() == "DecMin" || sender()->objectName() == "DecMax") { if (!olw->RAMin->isEmpty() && !olw->RAMax->isEmpty() && !olw->DecMin->isEmpty() && !olw->DecMax->isEmpty()) { bool rectOk = false; xRect1 = 0.0; xRect2 = 0.0; yRect1 = 0.0; yRect2 = 0.0; xRect1 = olw->RAMin->createDms(false, &rectOk).Hours(); if (rectOk) xRect2 = olw->RAMax->createDms(false, &rectOk).Hours(); if (rectOk) yRect1 = olw->DecMin->createDms(true, &rectOk).Degrees(); if (rectOk) yRect2 = olw->DecMax->createDms(true, &rectOk).Degrees(); if (xRect2 == 0.0) xRect2 = 24.0; if (!rectOk) { // qWarning() << i18n( "Illegal rectangle specified, no region selection possible." ) ; return; } //Make sure yRect1 < yRect2. if (yRect1 > yRect2) { double temp = yRect2; yRect2 = yRect1; yRect1 = temp; } //If xRect1 > xRect2, we may need to swap the two values, or subtract 24h from xRect1. if (xRect1 > xRect2) { if (xRect1 - xRect2 > 12.0) //the user probably wants a region that straddles 0h { xRect1 -= 24.0; } else //the user probably wants xRect2 to be the lower limit { double temp = xRect2; xRect2 = xRect1; xRect1 = temp; } } // slotUpdateObjectCount(); slotObjectCountDirty(); } } else if (!olw->RA->isEmpty() && !olw->Dec->isEmpty() && !olw->Radius->isEmpty()) { bool circOk; dms ra = olw->RA->createDms(false, &circOk); dms dc; if (circOk) dc = olw->Dec->createDms(true, &circOk); if (circOk) { pCirc.set(ra, dc); rCirc = olw->Radius->createDms(true, &circOk).Degrees(); } else { qWarning() << i18n("Illegal circle specified, no region selection possible."); return; } // slotUpdateObjectCount(); slotObjectCountDirty(); } } void ObsListWizard::slotObjectCountDirty() { olw->updateButton->setDisabled(false); } void ObsListWizard::slotUpdateObjectCount() { QApplication::setOverrideCursor(Qt::WaitCursor); ObjectCount = 0; if (isItemSelected(i18n("Stars"), olw->TypeList)) ObjectCount += StarCount; if (isItemSelected(i18n("Sun, moon, planets"), olw->TypeList)) ObjectCount += PlanetCount; if (isItemSelected(i18n("Comets"), olw->TypeList)) ObjectCount += CometCount; if (isItemSelected(i18n("Asteroids"), olw->TypeList)) ObjectCount += AsteroidCount; if (isItemSelected(i18n("Galaxies"), olw->TypeList)) ObjectCount += GalaxyCount; if (isItemSelected(i18n("Open clusters"), olw->TypeList)) ObjectCount += OpenClusterCount; if (isItemSelected(i18n("Globular clusters"), olw->TypeList)) ObjectCount += GlobClusterCount; if (isItemSelected(i18n("Gaseous nebulae"), olw->TypeList)) ObjectCount += GasNebCount; if (isItemSelected(i18n("Planetary nebulae"), olw->TypeList)) ObjectCount += PlanNebCount; applyFilters(false); //false = only adjust counts, do not build list QApplication::restoreOverrideCursor(); olw->updateButton->setDisabled(true); } void ObsListWizard::applyFilters(bool doBuildList) { bool filterPass = true; KStarsData *data = KStarsData::Instance(); if (doBuildList) obsList().clear(); //We don't need to call applyRegionFilter() if no region filter is selected, *and* //we are just counting items (i.e., doBuildList is false) bool needRegion = true; if (!doBuildList && isItemSelected(i18n("all over the sky"), olw->RegionList)) needRegion = false; double maglimit = 100.; if (olw->SelectByMagnitude->isChecked()) maglimit = olw->Mag->value(); //Stars if (isItemSelected(i18n("Stars"), olw->TypeList)) { const QList &starList = data->skyComposite()->stars(); int starIndex(starList.size()); if (maglimit < 100.) { //Stars are sorted by mag, so use binary search algo to find index of faintest mag int low(0), high(starList.size() - 1), middle(high); while (low < high) { middle = (low + high) / 2; if (maglimit == starList.at(middle)->mag()) break; if (maglimit < starList.at(middle)->mag()) high = middle - 1; if (maglimit > starList.at(middle)->mag()) low = middle + 1; } //now, the star at "middle" has the right mag, but we want the *last* star that has this mag. for (starIndex = middle + 1; starIndex < starList.size(); ++starIndex) { if (starList.at(starIndex)->mag() > maglimit) break; } } //DEBUG qDebug() << QString("starIndex for mag %1: %2").arg(maglimit).arg(starIndex) << endl; if (!doBuildList) { //reduce StarCount by appropriate amount ObjectCount -= StarCount; ObjectCount += starIndex; } for (int i = 0; i < starIndex; ++i) { SkyObject *o = (SkyObject *)(starList[i]); // JM 2012-10-22: Skip unnamed stars if (o->name() == "star") { if (!doBuildList) --ObjectCount; continue; } if (needRegion) filterPass = applyRegionFilter(o, doBuildList, !doBuildList); //Filter objects visible from geo at Date if region filter passes if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList, !doBuildList); } } //Sun, Moon, Planets if (isItemSelected(i18n("Sun, moon, planets"), olw->TypeList)) { - if (maglimit < data->skyComposite()->findByName("Sun")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Sun"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) - filterPass = applyRegionFilter(data->skyComposite()->findByName("Sun"), doBuildList); + filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Sun")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) - applyObservableFilter(data->skyComposite()->findByName("Sun"), doBuildList); + applyObservableFilter(data->skyComposite()->findByName(i18n("Sun")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Moon")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Moon"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) - filterPass = applyRegionFilter(data->skyComposite()->findByName("Moon"), doBuildList); + filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Moon")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) - applyObservableFilter(data->skyComposite()->findByName("Moon"), doBuildList); + applyObservableFilter(data->skyComposite()->findByName(i18n("Moon")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Mercury")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Mercury"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Mercury")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Mercury")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Venus")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Venus"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Venus")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Venus")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Mars")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Mars"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Mars")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Mars")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Jupiter")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Jupiter"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Jupiter")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Jupiter")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Saturn")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Saturn"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Saturn")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Saturn")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Uranus")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Uranus"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Uranus")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Uranus")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Neptune")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Neptune"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18n("Neptune")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18n("Neptune")), doBuildList); - if (maglimit < data->skyComposite()->findByName("Pluto")->mag()) + if (maglimit < data->skyComposite()->findByName(i18n("Pluto"))->mag()) { if (!doBuildList) --ObjectCount; filterPass = false; } else filterPass = true; if (needRegion && filterPass) filterPass = applyRegionFilter(data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto")), doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(data->skyComposite()->findByName(i18nc("Asteroid name (optional)", "Pluto")), doBuildList); } //Deep sky objects bool dso = (isItemSelected(i18n("Open clusters"), olw->TypeList) || isItemSelected(i18n("Globular clusters"), olw->TypeList) || isItemSelected(i18n("Gaseous nebulae"), olw->TypeList) || isItemSelected(i18n("Planetary nebulae"), olw->TypeList) || isItemSelected(i18n("Galaxies"), olw->TypeList)); if (dso) { //Don't need to do anything if we are just counting objects and not //filtering by region or magnitude if (needRegion || olw->SelectByMagnitude->isChecked() || olw->SelectByDate->isChecked()) { foreach (DeepSkyObject *o, data->skyComposite()->deepSkyObjects()) { //Skip unselected object types bool typeSelected = false; // if ( (o->type() == SkyObject::STAR || o->type() == SkyObject::CATALOG_STAR) && // isItemSelected( i18n( "Stars" ), olw->TypeList ) ) // typeSelected = true; switch (o->type()) { case SkyObject::OPEN_CLUSTER: if (isItemSelected(i18n("Open clusters"), olw->TypeList)) typeSelected = true; break; case SkyObject::GLOBULAR_CLUSTER: if (isItemSelected(i18n("Globular clusters"), olw->TypeList)) typeSelected = true; break; case SkyObject::GASEOUS_NEBULA: case SkyObject::SUPERNOVA_REMNANT: if (isItemSelected(i18n("Gaseous nebulae"), olw->TypeList)) typeSelected = true; break; case SkyObject::PLANETARY_NEBULA: if (isItemSelected(i18n("Planetary nebulae"), olw->TypeList)) typeSelected = true; break; case SkyObject::GALAXY: if (isItemSelected(i18n("Galaxies"), olw->TypeList)) typeSelected = true; break; } if (!typeSelected) continue; if (olw->SelectByMagnitude->isChecked()) { if (o->mag() > 90.) { if (olw->IncludeNoMag->isChecked()) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } else { if (o->mag() <= maglimit) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } } else { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } } } } //Comets if (isItemSelected(i18n("Comets"), olw->TypeList)) { foreach (SkyObject *o, data->skyComposite()->comets()) { if (olw->SelectByMagnitude->isChecked()) { if (o->mag() > 90.) { if (olw->IncludeNoMag->isChecked()) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } else { if (o->mag() <= maglimit) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } } else { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } } } //Asteroids if (isItemSelected(i18n("Asteroids"), olw->TypeList)) { foreach (SkyObject *o, data->skyComposite()->asteroids()) { if (olw->SelectByMagnitude->isChecked()) { if (o->mag() > 90.) { if (olw->IncludeNoMag->isChecked()) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } else { if (o->mag() <= maglimit) { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } else if (!doBuildList) --ObjectCount; } } else { if (needRegion) filterPass = applyRegionFilter(o, doBuildList); if (olw->SelectByDate->isChecked() && filterPass) applyObservableFilter(o, doBuildList); } } } //Update the object count label if (doBuildList) ObjectCount = obsList().size(); olw->CountLabel->setText(i18np("Your observing list currently has 1 object", "Your observing list currently has %1 objects", ObjectCount)); } bool ObsListWizard::applyRegionFilter(SkyObject *o, bool doBuildList, bool doAdjustCount) { //select by constellation if (isItemSelected(i18n("by constellation"), olw->RegionList)) { QString c = KStarsData::Instance()->skyComposite()->constellationBoundary()->constellationName(o); if (isItemSelected(c, olw->ConstellationList)) { if (doBuildList) obsList().append(o); return true; } else if (doAdjustCount) { --ObjectCount; return false; } else return false; } //select by rectangular region else if (isItemSelected(i18n("in a rectangular region"), olw->RegionList)) { double ra = o->ra().Hours(); double dec = o->dec().Degrees(); bool addObject = false; if (dec >= yRect1 && dec <= yRect2) { if (xRect1 < 0.0) { addObject = ra >= xRect1 + 24.0 || ra <= xRect2; } else { addObject = ra >= xRect1 && ra <= xRect2; } } if (addObject) { if (doBuildList) obsList().append(o); return true; } else { if (doAdjustCount) --ObjectCount; return false; } } //select by circular region //make sure circ region data are valid else if (isItemSelected(i18n("in a circular region"), olw->RegionList)) { if (o->angularDistanceTo(&pCirc).Degrees() < rCirc) { if (doBuildList) obsList().append(o); return true; } else if (doAdjustCount) { --ObjectCount; return false; } else return false; } //No region filter, just add the object else if (doBuildList) { obsList().append(o); } return true; } bool ObsListWizard::applyObservableFilter(SkyObject *o, bool doBuildList, bool doAdjustCount) { SkyPoint p = *o; //Check altitude of object every hour from 18:00 to midnight //If it's ever above 15 degrees, flag it as visible KStarsDateTime Evening(olw->Date->date(), QTime(18, 0, 0), Qt::LocalTime); KStarsDateTime Midnight(olw->Date->date().addDays(1), QTime(0, 0, 0), Qt::LocalTime); double minAlt = 15, maxAlt = 90; // Or use user-selected values, if they're valid if (olw->timeFrom->time().isValid() && olw->timeTo->time().isValid()) { Evening.setTime(olw->timeFrom->time()); Midnight.setTime(olw->timeTo->time()); // If time from < timeTo (e.g. 06:00 PM to 9:00 PM) // then we stay on the same day. if (olw->timeFrom->time() < olw->timeTo->time()) { Midnight.setDate(olw->Date->date()); } // Otherwise we advance by one day else { Midnight.setDate(olw->Date->date().addDays(1)); } } minAlt = olw->minAlt->value(); maxAlt = olw->maxAlt->value(); // This is the "relaxed" search mode // where if the object obeys the restrictions in 50% of the time of the range // then it qualifies as "visible" double totalCount = 0, visibleCount = 0; for (KStarsDateTime t = Evening; t < Midnight; t = t.addSecs(3600.0)) { dms LST = geo->GSTtoLST(t.gst()); p.EquatorialToHorizontal(&LST, geo->lat()); totalCount++; if (p.alt().Degrees() >= minAlt && p.alt().Degrees() <= maxAlt) visibleCount++; } // If the object is within the min/max alt at least coverage % of the time range // then consider it visible if (visibleCount / totalCount >= olw->coverage->value() / 100.0) return true; if (doAdjustCount) --ObjectCount; if (doBuildList) obsList().takeAt(obsList().indexOf(o)); return false; // This is the strict mode where ANY object that does not meet the min & max // altitude at ANY time would be removed from the list. #if 0 for (KStarsDateTime t = Evening; t < Midnight; t = t.addSecs(3600.0)) { dms LST = geo->GSTtoLST(t.gst()); p.EquatorialToHorizontal(&LST, geo->lat()); if (p.alt().Degrees() < minAlt || p.alt().Degrees() > maxAlt) { if (doAdjustCount) --ObjectCount; if (doBuildList) obsList().takeAt(obsList().indexOf(o)); return false; } } return true; #endif } diff --git a/kstars/tools/planetviewer.cpp b/kstars/tools/planetviewer.cpp index f4608d0d5..acbddcbb6 100644 --- a/kstars/tools/planetviewer.cpp +++ b/kstars/tools/planetviewer.cpp @@ -1,286 +1,286 @@ /*************************************************************************** planetviewer.cpp - Display overhead view of the solar system ------------------- begin : Sun May 25 2003 copyright : (C) 2003 by Jason Harris email : jharris@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "planetviewer.h" #include "ksfilereader.h" #include "ksnumbers.h" #include "kstarsdata.h" #include "ksutils.h" #include "skyobjects/ksplanet.h" #include "skyobjects/ksplanetbase.h" #include "widgets/timespinbox.h" #include #include #include #include #include #include #include #include #include PlanetViewerUI::PlanetViewerUI(QWidget *p) : QFrame(p) { setupUi(this); } PlanetViewer::PlanetViewer(QWidget *parent) : QDialog(parent), scale(1.0), isClockRunning(false), tmr(this) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif KStarsData *data = KStarsData::Instance(); pw = new PlanetViewerUI(this); QVBoxLayout *mainLayout = new QVBoxLayout; mainLayout->addWidget(pw); setLayout(mainLayout); setWindowTitle(i18n("Solar System Viewer")); //setMainWidget( pw ); //setButtons( QDialog::Close ); setModal(false); pw->map->setLimits(-48.0, 48.0, -48.0, 48.0); pw->map->axis(KPlotWidget::BottomAxis) - ->setLabel(i18nc("axis label for x-coordinate of solar system viewer. AU means astronomical unit.", - "X-position (AU)")); + ->setLabel(i18nc("axis label for x-coordinate of solar system viewer. AU means astronomical unit.", + "X-position (AU)")); pw->map->axis(KPlotWidget::LeftAxis) - ->setLabel(i18nc("axis label for y-coordinate of solar system viewer. AU means astronomical unit.", - "Y-position (AU)")); + ->setLabel(i18nc("axis label for y-coordinate of solar system viewer. AU means astronomical unit.", + "Y-position (AU)")); pw->TimeStep->setDaysOnly(true); pw->TimeStep->tsbox()->setValue(1); //start with 1-day timestep pw->RunButton->setIcon(QIcon::fromTheme("arrow-right")); pw->ZoomInButton->setIcon(QIcon::fromTheme("zoom-in")); pw->ZoomOutButton->setIcon(QIcon::fromTheme("zoom-out")); pw->DateBox->setDate(data->lt().date()); resize(500, 500); pw->map->QWidget::setFocus(); //give keyboard focus to the plot widget for key and mouse events setCenterPlanet(QString()); PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::MERCURY)); PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::VENUS)); - PlanetList.append(new KSPlanet("Earth")); + PlanetList.append(new KSPlanet(i18n("Earth"))); PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::MARS)); PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::JUPITER)); PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::SATURN)); PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::URANUS)); PlanetList.append(KSPlanetBase::createPlanet(KSPlanetBase::NEPTUNE)); //PlanetList.append( KSPlanetBase::createPlanet( KSPlanetBase::PLUTO ) ); ut = data->ut(); KSNumbers num(ut.djd()); for (int i = 0; i < PlanetList.count(); ++i) { PlanetList[i]->findPosition(&num, nullptr, nullptr); // nullptr args: don't need geocent. coords. LastUpdate[i] = int(ut.date().toJulianDay()); } //The planets' update intervals are 0.25% of one period: UpdateInterval[0] = 0; UpdateInterval[1] = 0; UpdateInterval[2] = 0; UpdateInterval[3] = 1; UpdateInterval[4] = 5; UpdateInterval[5] = 13; UpdateInterval[6] = 38; UpdateInterval[7] = 75; //UpdateInterval[8] = 113; QTimer::singleShot(0, this, SLOT(initPlotObjects())); connect(&tmr, SIGNAL(timeout()), SLOT(tick())); connect(pw->TimeStep, SIGNAL(scaleChanged(float)), SLOT(setTimeScale(float))); connect(pw->RunButton, SIGNAL(clicked()), SLOT(slotRunClock())); connect(pw->ZoomInButton, SIGNAL(clicked()), pw->map, SLOT(slotZoomIn())); connect(pw->ZoomOutButton, SIGNAL(clicked()), pw->map, SLOT(slotZoomOut())); connect(pw->DateBox, SIGNAL(dateChanged(QDate)), SLOT(slotChangeDate())); connect(pw->TodayButton, SIGNAL(clicked()), SLOT(slotToday())); connect(this, SIGNAL(closeClicked()), SLOT(slotCloseWindow())); } QString PlanetViewer::planetName(uint i) const { return PlanetList[i]->name(); } void PlanetViewer::tick() { //Update the time/date ut.setDJD(ut.djd() + scale * 0.1); pw->DateBox->setDate(ut.date()); updatePlanets(); } void PlanetViewer::setTimeScale(float f) { scale = f / 86400.; //convert seconds to days } void PlanetViewer::slotRunClock() { isClockRunning = !isClockRunning; if (isClockRunning) { pw->RunButton->setIcon( QIcon::fromTheme("media-playback-pause")); tmr.start(100); // pw->DateBox->setEnabled( false ); } else { pw->RunButton->setIcon(QIcon::fromTheme("arrow-right")); tmr.stop(); // pw->DateBox->setEnabled( true ); } } void PlanetViewer::slotChangeDate() { ut.setDate(pw->DateBox->date()); updatePlanets(); } void PlanetViewer::slotCloseWindow() { //Stop the clock if it's running if (isClockRunning) { tmr.stop(); isClockRunning = false; pw->RunButton->setIcon(QIcon::fromTheme("arrow-right")); } } void PlanetViewer::updatePlanets() { KSNumbers num(ut.djd()); bool changed(false); //Check each planet to see if it needs to be updated for (int i = 0; i < PlanetList.count(); ++i) { if (abs(int(ut.date().toJulianDay()) - LastUpdate[i]) > UpdateInterval[i]) { KSPlanetBase *p = PlanetList[i]; p->findPosition(&num); double s, c, s2, c2; p->helEcLong().SinCos(s, c); p->helEcLat().SinCos(s2, c2); QList points = planet[i]->points(); points.at(0)->setX(p->rsun() * c * c2); points.at(0)->setY(p->rsun() * s * c2); if (centerPlanet() == p->name()) { QRectF dataRect = pw->map->dataRect(); double xc = (dataRect.right() + dataRect.left()) * 0.5; double yc = (dataRect.bottom() + dataRect.top()) * 0.5; double dx = points.at(0)->x() - xc; double dy = points.at(0)->y() - yc; pw->map->setLimits(dataRect.x() + dx, dataRect.right() + dx, dataRect.y() + dy, dataRect.bottom() + dy); } LastUpdate[i] = int(ut.date().toJulianDay()); changed = true; } } if (changed) pw->map->update(); } void PlanetViewer::slotToday() { pw->DateBox->setDate(KStarsData::Instance()->lt().date()); } void PlanetViewer::paintEvent(QPaintEvent *) { pw->map->update(); } void PlanetViewer::initPlotObjects() { // Planets ksun = new KPlotObject(Qt::yellow, KPlotObject::Points, 12, KPlotObject::Circle); ksun->addPoint(0.0, 0.0); pw->map->addPlotObject(ksun); //Read in the orbit curves for (int i = 0; i < PlanetList.count(); ++i) { KSPlanetBase *p = PlanetList[i]; KPlotObject *orbit = new KPlotObject(Qt::white, KPlotObject::Lines, 1.0); QFile orbitFile; QString orbitFileName = - (p->isMajorPlanet() ? ((KSPlanet *)p)->untranslatedName().toLower() : p->name().toLower()) + ".orbit"; + (p->isMajorPlanet() ? (dynamic_cast(p))->untranslatedName().toLower() : p->name().toLower()) + ".orbit"; if (KSUtils::openDataFile(orbitFile, orbitFileName)) { KSFileReader fileReader(orbitFile); // close file is included double x, y; while (fileReader.hasMoreLines()) { QString line = fileReader.readLine(); QStringList fields = line.split(' ', QString::SkipEmptyParts); if (fields.size() == 3) { x = fields[0].toDouble(); y = fields[1].toDouble(); orbit->addPoint(x, y); } } } pw->map->addPlotObject(orbit); } for (int i = 0; i < PlanetList.count(); ++i) { KSPlanetBase *p = PlanetList[i]; planet[i] = new KPlotObject(p->color(), KPlotObject::Points, 6, KPlotObject::Circle); double s, c; p->helEcLong().SinCos(s, c); planet[i]->addPoint(p->rsun() * c, p->rsun() * s, p->translatedName()); pw->map->addPlotObject(planet[i]); } update(); } void PlanetViewer::keyPressEvent(QKeyEvent *e) { if (e->key() == Qt::Key_Escape) close(); else e->ignore(); } diff --git a/kstars/tools/pvplotwidget.cpp b/kstars/tools/pvplotwidget.cpp index 19840f58b..380ddaa69 100644 --- a/kstars/tools/pvplotwidget.cpp +++ b/kstars/tools/pvplotwidget.cpp @@ -1,299 +1,299 @@ /*************************************************************************** pvplotwidget.cpp ------------------- begin : Sat 17 Dec 2005 copyright : (C) 2005 by Jason Harris email : kstars@30doradus.org ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include "pvplotwidget.h" #include "planetviewer.h" #include #include #include #include #include #include PVPlotWidget::PVPlotWidget(QWidget *parent) : KPlotWidget(parent) { setFocusPolicy(Qt::StrongFocus); setMouseTracking(true); setAntialiasing(true); //FIXME: Evil cast! pv = (PlanetViewer *)topLevelWidget(); } void PVPlotWidget::keyPressEvent(QKeyEvent *e) { double xc = (dataRect().right() + dataRect().x()) * 0.5; double yc = (dataRect().bottom() + dataRect().y()) * 0.5; double xstep = 0.01 * (dataRect().right() - dataRect().x()); double ystep = 0.01 * (dataRect().bottom() - dataRect().y()); double dx = 0.5 * dataRect().width(); double dy = 0.5 * dataRect().height(); switch (e->key()) { case Qt::Key_Left: if (xc - xstep > -AUMAX) { setLimits(dataRect().x() - xstep, dataRect().right() - xstep, dataRect().y(), dataRect().bottom()); pv->setCenterPlanet(QString()); update(); } break; case Qt::Key_Right: if (xc + xstep < AUMAX) { setLimits(dataRect().x() + xstep, dataRect().right() + xstep, dataRect().y(), dataRect().bottom()); pv->setCenterPlanet(QString()); update(); } break; case Qt::Key_Down: if (yc - ystep > -AUMAX) { setLimits(dataRect().x(), dataRect().right(), dataRect().y() - ystep, dataRect().bottom() - ystep); pv->setCenterPlanet(QString()); update(); } break; case Qt::Key_Up: if (yc + ystep < AUMAX) { setLimits(dataRect().x(), dataRect().right(), dataRect().y() + ystep, dataRect().bottom() + ystep); pv->setCenterPlanet(QString()); update(); } break; case Qt::Key_Plus: case Qt::Key_Equal: { updateFactor(e->modifiers()); slotZoomIn(); break; } case Qt::Key_Minus: case Qt::Key_Underscore: { updateFactor(e->modifiers()); slotZoomOut(); break; } case Qt::Key_0: //Sun setLimits(-dx, dx, -dy, dy); - pv->setCenterPlanet("Sun"); + pv->setCenterPlanet(i18n("Sun")); update(); break; case Qt::Key_1: //Mercury { KPlotPoint *p = plotObjects().at(10)->points().at(0); setLimits(p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy); - pv->setCenterPlanet("Mercury"); + pv->setCenterPlanet(i18n("Mercury")); update(); break; } case Qt::Key_2: //Venus { KPlotPoint *p = plotObjects().at(11)->points().at(0); setLimits(p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy); - pv->setCenterPlanet("Venus"); + pv->setCenterPlanet(i18n("Venus")); update(); break; } case Qt::Key_3: //Earth { KPlotPoint *p = plotObjects().at(12)->points().at(0); setLimits(p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy); - pv->setCenterPlanet("Earth"); + pv->setCenterPlanet(i18n("Earth")); update(); break; } case Qt::Key_4: //Mars { KPlotPoint *p = plotObjects().at(13)->points().at(0); setLimits(p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy); - pv->setCenterPlanet("Mars"); + pv->setCenterPlanet(i18n("Mars")); update(); break; } case Qt::Key_5: //Jupiter { KPlotPoint *p = plotObjects().at(14)->points().at(0); setLimits(p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy); - pv->setCenterPlanet("Jupiter"); + pv->setCenterPlanet(i18n("Jupiter")); update(); break; } case Qt::Key_6: //Saturn { KPlotPoint *p = plotObjects().at(15)->points().at(0); setLimits(p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy); - pv->setCenterPlanet("Saturn"); + pv->setCenterPlanet(i18n("Saturn")); update(); break; } case Qt::Key_7: //Uranus { KPlotPoint *p = plotObjects().at(16)->points().at(0); setLimits(p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy); pv->setCenterPlanet("Uranus"); update(); break; } case Qt::Key_8: //Neptune { KPlotPoint *p = plotObjects().at(17)->points().at(0); setLimits(p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy); - pv->setCenterPlanet("Neptune"); + pv->setCenterPlanet(i18n("Neptune")); update(); break; } /*case Qt::Key_9: //Pluto { KPlotPoint *p = plotObjects().at(18)->points().at(0); setLimits( p->x() - dx, p->x() + dx, p->y() - dy, p->y() + dy ); pv->setCenterPlanet( "Pluto" ); update(); break; }*/ default: e->ignore(); break; } } void PVPlotWidget::mousePressEvent(QMouseEvent *e) { mouseButtonDown = true; oldx = e->x(); oldy = e->y(); } void PVPlotWidget::mouseReleaseEvent(QMouseEvent *) { mouseButtonDown = false; update(); } void PVPlotWidget::mouseMoveEvent(QMouseEvent *e) { if (mouseButtonDown) { //Determine how far we've moved double xc = (dataRect().right() + dataRect().x()) * 0.5; double yc = (dataRect().bottom() + dataRect().y()) * 0.5; double xscale = dataRect().width() / (width() - leftPadding() - rightPadding()); double yscale = dataRect().height() / (height() - topPadding() - bottomPadding()); xc += (oldx - e->x()) * xscale; yc -= (oldy - e->y()) * yscale; //Y data axis is reversed... if (xc > -AUMAX && xc < AUMAX && yc > -AUMAX && yc < AUMAX) { setLimits(xc - 0.5 * dataRect().width(), xc + 0.5 * dataRect().width(), yc - 0.5 * dataRect().height(), yc + 0.5 * dataRect().height()); update(); qApp->processEvents(); } oldx = e->x(); oldy = e->y(); } } void PVPlotWidget::mouseDoubleClickEvent(QMouseEvent *e) { double xscale = dataRect().width() / (width() - leftPadding() - rightPadding()); double yscale = dataRect().height() / (height() - topPadding() - bottomPadding()); double xc = dataRect().x() + xscale * (e->x() - leftPadding()); double yc = dataRect().bottom() - yscale * (e->y() - topPadding()); if (xc > -AUMAX && xc < AUMAX && yc > -AUMAX && yc < AUMAX) { setLimits(xc - 0.5 * dataRect().width(), xc + 0.5 * dataRect().width(), yc - 0.5 * dataRect().height(), yc + 0.5 * dataRect().height()); update(); } pv->setCenterPlanet(QString()); for (unsigned int i = 0; i < 9; ++i) { KPlotPoint *point = pv->planetObject(i)->points().at(0); double dx = (point->x() - xc) / xscale; if (dx < 4.0) { double dy = (point->y() - yc) / yscale; if (sqrt(dx * dx + dy * dy) < 4.0) { pv->setCenterPlanet(pv->planetName(i)); } } } } void PVPlotWidget::wheelEvent(QWheelEvent *e) { updateFactor(e->modifiers()); if (e->delta() > 0) slotZoomIn(); else slotZoomOut(); } void PVPlotWidget::slotZoomIn() { double size = dataRect().width(); if (size > 0.8) { setLimits(dataRect().x() + factor * 0.01 * size, dataRect().right() - factor * 0.01 * size, dataRect().y() + factor * 0.01 * size, dataRect().bottom() - factor * 0.01 * size); update(); } } void PVPlotWidget::slotZoomOut() { double size = dataRect().width(); if ((size) < 100.0) { setLimits(dataRect().x() - factor * 0.01 * size, dataRect().right() + factor * 0.01 * size, dataRect().y() - factor * 0.01 * size, dataRect().bottom() + factor * 0.01 * size); update(); } } void PVPlotWidget::updateFactor(const int modifier) { factor = (modifier & Qt::ControlModifier) ? 1 : 25; } diff --git a/kstars/tools/whatsinteresting/modelmanager.cpp b/kstars/tools/whatsinteresting/modelmanager.cpp index 1cc910b9f..d2e3e6873 100644 --- a/kstars/tools/whatsinteresting/modelmanager.cpp +++ b/kstars/tools/whatsinteresting/modelmanager.cpp @@ -1,344 +1,344 @@ /*************************************************************************** modelmanager.cpp - K Desktop Planetarium ------------------- begin : 2012/26/05 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "modelmanager.h" #include "ksfilereader.h" #include "kstars.h" #include "kstarsdata.h" #include "obsconditions.h" #include "skymapcomposite.h" #include "skyobjitem.h" #include "skyobjlistmodel.h" #include "starobject.h" #include ModelManager::ModelManager(ObsConditions *obs) { m_ObsConditions = obs; tempModel = new SkyObjListModel(); m_ModelList = QList(); m_ObjectList = QList>(); favoriteClusters = QList(); favoriteNebulas = QList(); favoriteGalaxies = QList(); for (int i = 0; i < NumberOfLists; i++) { m_ModelList.append(new SkyObjListModel()); m_ObjectList.append(QList()); } QtConcurrent::run(this, &ModelManager::loadLists); } ModelManager::~ModelManager() { qDeleteAll(m_ModelList); foreach (QList list, m_ObjectList) qDeleteAll(list); delete tempModel; } void ModelManager::loadLists() { if (KStars::Closing) return; emit loadProgressUpdated(0); KStarsData *data = KStarsData::Instance(); QVector> listStars; listStars.append(data->skyComposite()->objectLists(SkyObject::STAR)); for (int i = 0; i < listStars.size(); i++) { QPair pair = listStars.value(i); const StarObject *star = dynamic_cast(pair.second); if (star->hasLatinName()) m_ObjectList[Stars].append(new SkyObjItem((SkyObject *)(star))); } QString prevName; for (int i = 0; i < m_ObjectList[Stars].size(); i++) { SkyObjItem *star = m_ObjectList[Stars].at(i); if (prevName == star->getName()) { m_ObjectList[Stars].removeAt(i); i--; } prevName = star->getName(); } KSFileReader fileReader; if (!fileReader.open("Interesting.dat")) return; while (fileReader.hasMoreLines()) { if (KStars::Closing) return; QString line = fileReader.readLine(); if (line.length() == 0 || line[0] == '#') continue; SkyObject *o; if ((o = data->skyComposite()->findByName(line))) { //qDebug()<longname()<typeName(); switch (o->type()) { case SkyObject::OPEN_CLUSTER: case SkyObject::GLOBULAR_CLUSTER: case SkyObject::GALAXY_CLUSTER: favoriteClusters.append(new SkyObjItem(o)); break; case SkyObject::PLANETARY_NEBULA: case SkyObject::DARK_NEBULA: case SkyObject::GASEOUS_NEBULA: favoriteNebulas.append(new SkyObjItem(o)); break; case SkyObject::GALAXY: favoriteGalaxies.append(new SkyObjItem(o)); break; } } } emit loadProgressUpdated(0.20); loadObjectList(m_ObjectList[Asteroids], SkyObject::ASTEROID); emit loadProgressUpdated(0.30); loadObjectList(m_ObjectList[Comets], SkyObject::COMET); emit loadProgressUpdated(0.40); loadObjectList(m_ObjectList[Satellites], SkyObject::SATELLITE); loadObjectList(m_ObjectList[Supernovas], SkyObject::SUPERNOVA); emit loadProgressUpdated(0.50); loadObjectList(m_ObjectList[Constellations], SkyObject::CONSTELLATION); emit loadProgressUpdated(0.55); loadObjectList(m_ObjectList[Planets], SkyObject::PLANET); emit loadProgressUpdated(0.60); loadObjectList(m_ObjectList[Galaxies], SkyObject::GALAXY); emit loadProgressUpdated(0.70); loadObjectList(m_ObjectList[Clusters], SkyObject::OPEN_CLUSTER); loadObjectList(m_ObjectList[Clusters], SkyObject::GLOBULAR_CLUSTER); loadObjectList(m_ObjectList[Clusters], SkyObject::GALAXY_CLUSTER); emit loadProgressUpdated(0.80); loadObjectList(m_ObjectList[Nebulas], SkyObject::PLANETARY_NEBULA); loadObjectList(m_ObjectList[Nebulas], SkyObject::SUPERNOVA_REMNANT); loadObjectList(m_ObjectList[Nebulas], SkyObject::GASEOUS_NEBULA); loadObjectList(m_ObjectList[Nebulas], SkyObject::DARK_NEBULA); emit loadProgressUpdated(0.90); for (int i = 1; i <= 110; i++) { SkyObject *o; if ((o = data->skyComposite()->findByName("M " + QString::number(i)))) m_ObjectList[Messier].append(new SkyObjItem(o)); } emit loadProgressUpdated(1); } void ModelManager::loadNGCCatalog() { KStarsData *data = KStarsData::Instance(); if (!ngcLoaded) { for (int i = 1; i <= 7840; i++) { if (i % 100 == 0) emit loadProgressUpdated((double)i / 7840.0); SkyObject *o; if ((o = data->skyComposite()->findByName("NGC " + QString::number(i)))) m_ObjectList[NGC].append(new SkyObjItem(o)); } updateModel(m_ObsConditions, "ngc"); emit loadProgressUpdated(1); } ngcLoaded = true; } void ModelManager::loadICCatalog() { KStarsData *data = KStarsData::Instance(); if (!icLoaded) { for (int i = 1; i <= 3866; i++) { if (i % 100 == 0) emit loadProgressUpdated((double)i / 3866.0); SkyObject *o; if ((o = data->skyComposite()->findByName("IC " + QString::number(i)))) m_ObjectList[IC].append(new SkyObjItem(o)); } updateModel(m_ObsConditions, "ic"); emit loadProgressUpdated(1); } icLoaded = true; } void ModelManager::loadSharplessCatalog() { KStarsData *data = KStarsData::Instance(); if (!sharplessLoaded) { for (int i = 1; i <= 320; i++) { if (i % 100 == 0) emit loadProgressUpdated((double)i / 320.0); SkyObject *o; if ((o = data->skyComposite()->findByName("Sh2 " + QString::number(i)))) m_ObjectList[Sharpless].append(new SkyObjItem(o)); } updateModel(m_ObsConditions, "sharpless"); emit loadProgressUpdated(1); } sharplessLoaded = true; } void ModelManager::updateAllModels(ObsConditions *obs) { m_ObsConditions = obs; resetAllModels(); for (int i = 0; i < NumberOfLists; i++) loadObjectsIntoModel(*m_ModelList[i], m_ObjectList[i]); } void ModelManager::updateModel(ObsConditions *obs, QString modelName) { m_ObsConditions = obs; SkyObjListModel *model = returnModel(modelName); if (model) { model->resetModel(); if (showOnlyFavorites && modelName == "galaxies") loadObjectsIntoModel(*m_ModelList[getModelNumber(modelName)], favoriteGalaxies); else if (showOnlyFavorites && modelName == "nebulas") loadObjectsIntoModel(*m_ModelList[getModelNumber(modelName)], favoriteNebulas); else if (showOnlyFavorites && modelName == "clusters") loadObjectsIntoModel(*m_ModelList[getModelNumber(modelName)], favoriteClusters); else loadObjectsIntoModel(*m_ModelList[getModelNumber(modelName)], m_ObjectList[getModelNumber(modelName)]); emit modelUpdated(); } } void ModelManager::loadObjectList(QList &skyObjectList, int type) { if (KStars::Closing) return; KStarsData *data = KStarsData::Instance(); QVector> objects = data->skyComposite()->objectLists(type); for (int i = 0; i < objects.size(); i++) { if (KStars::Closing) return; QPair pair = objects.value(i); const SkyObject *listObject = pair.second; - if (listObject->name() != "Sun") - skyObjectList.append(new SkyObjItem((SkyObject *)(listObject))); + if (listObject->name() != i18n("Sun")) + skyObjectList.append(new SkyObjItem(const_cast(listObject))); } QString prevName; for (int i = 0; i < skyObjectList.size(); i++) { if (KStars::Closing) return; SkyObjItem *obj = skyObjectList.at(i); if (prevName == obj->getName()) { skyObjectList.removeAt(i); i--; } prevName = obj->getName(); } } void ModelManager::loadObjectsIntoModel(SkyObjListModel &model, QList &skyObjectList) { KStarsData *data = KStarsData::Instance(); foreach (SkyObjItem *soitem, skyObjectList) { bool isVisible = (showOnlyVisible) ? (m_ObsConditions->isVisible(data->geo(), data->lst(), soitem->getSkyObject())) : true; if (isVisible) model.addSkyObject(soitem); } } void ModelManager::resetAllModels() { foreach (SkyObjListModel *model, m_ModelList) model->resetModel(); } int ModelManager::getModelNumber(QString modelName) { if (modelName == "planets") return Planets; if (modelName == "stars") return Stars; if (modelName == "constellations") return Constellations; if (modelName == "galaxies") return Galaxies; if (modelName == "clusters") return Clusters; if (modelName == "nebulas") return Nebulas; if (modelName == "asteroids") return Asteroids; if (modelName == "comets") return Comets; if (modelName == "supernovas") return Supernovas; if (modelName == "satellites") return Satellites; if (modelName == "messier") return Messier; if (modelName == "ngc") return NGC; if (modelName == "ic") return IC; if (modelName == "sharpless") return Sharpless; else return -1; } SkyObjListModel *ModelManager::returnModel(QString modelName) { int modelNumber = getModelNumber(modelName); if (modelNumber > -1 && modelNumber < NumberOfLists) return m_ModelList[modelNumber]; else return tempModel; } diff --git a/kstars/tools/whatsinteresting/wiview.cpp b/kstars/tools/whatsinteresting/wiview.cpp index 454999fb9..c49653235 100644 --- a/kstars/tools/whatsinteresting/wiview.cpp +++ b/kstars/tools/whatsinteresting/wiview.cpp @@ -1,1027 +1,1034 @@ /*************************************************************************** wiview.cpp - K Desktop Planetarium ------------------- begin : 2012/26/05 copyright : (C) 2012 by Samikshan Bairagya email : samikshan@gmail.com ***************************************************************************/ /*************************************************************************** * * * 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 "wiview.h" #include "kspaths.h" #include "kstars.h" #include "modelmanager.h" #include "obsconditions.h" #include "Options.h" #include "skymap.h" #include "skymapcomposite.h" #include "skyobjitem.h" #include "skyobjlistmodel.h" #include "starobject.h" #include "wiequipsettings.h" #include "dialogs/detaildialog.h" #include #include #include #include #include #include #include #include #include #ifdef HAVE_INDI #include #include "indi/indilistener.h" #endif WIView::WIView(QWidget *parent) : QWidget(parent) { //These settings are like this just to get it started. int bortle = Options::bortleClass(); int aperture = 100; ObsConditions::Equipment equip = ObsConditions::Telescope; ObsConditions::TelescopeType telType = ObsConditions::Reflector; m_Obs = new ObsConditions(bortle, aperture, equip, telType); m_ModManager.reset(new ModelManager(m_Obs)); m_BaseView = new QQuickView(); ///To use i18n() instead of qsTr() in qml/wiview.qml for translation //KDeclarative kd; // kd.setDeclarativeEngine(m_BaseView->engine()); //kd.initialize(); //kd.setupBindings(); m_Ctxt = m_BaseView->rootContext(); m_Ctxt->setContextProperty("soListModel", m_ModManager->getTempModel()); // This is to avoid an error saying it doesn't exist. ///Use instead of KDeclarative m_Ctxt->setContextObject(new KLocalizedContext(m_BaseView)); #if 0 QString WI_Location; #if defined(Q_OS_OSX) WI_Location = QCoreApplication::applicationDirPath() + "/../Resources/data/tools/whatsinteresting/qml/wiview.qml"; if (!QFileInfo(WI_Location).exists()) WI_Location = KSPaths::locate(QStandardPaths::AppDataLocation, "tools/whatsinteresting/qml/wiview.qml"); #elif defined(Q_OS_WIN) WI_Location = KSPaths::locate(QStandardPaths::GenericDataLocation, "tools/whatsinteresting/qml/wiview.qml"); #else WI_Location = KSPaths::locate(QStandardPaths::AppDataLocation, "tools/whatsinteresting/qml/wiview.qml"); #endif m_BaseView->setSource(QUrl::fromLocalFile(WI_Location)); #endif m_BaseView->setSource(QUrl("qrc:/qml/whatisinteresting/wiview.qml")); m_BaseObj = m_BaseView->rootObject(); m_ProgressBar = m_BaseObj->findChild("progressBar"); m_loadingMessage = m_BaseObj->findChild("loadingMessage"); m_CategoryTitle = m_BaseObj->findChild(QString("categoryTitle")); m_ViewsRowObj = m_BaseObj->findChild(QString("viewsRowObj")); connect(m_ViewsRowObj, SIGNAL(categorySelected(QString)), this, SLOT(onCategorySelected(QString))); connect(m_ViewsRowObj, SIGNAL(inspectSkyObject(QString)), this, SLOT(inspectSkyObject(QString))); m_SoListObj = m_BaseObj->findChild("soListObj"); connect(m_SoListObj, SIGNAL(soListItemClicked(int)), this, SLOT(onSoListItemClicked(int))); m_DetailsViewObj = m_BaseObj->findChild("detailsViewObj"); descTextObj = m_DetailsViewObj->findChild("descTextObj"); infoBoxText = m_DetailsViewObj->findChild("infoBoxText"); m_NextObj = m_BaseObj->findChild("nextObj"); connect(m_NextObj, SIGNAL(nextObjClicked()), this, SLOT(onNextObjClicked())); m_PrevObj = m_BaseObj->findChild("prevObj"); connect(m_PrevObj, SIGNAL(prevObjClicked()), this, SLOT(onPrevObjClicked())); m_CenterButtonObj = m_BaseObj->findChild("centerButtonObj"); connect(m_CenterButtonObj, SIGNAL(centerButtonClicked()), this, SLOT(onCenterButtonClicked())); autoCenterCheckbox = m_DetailsViewObj->findChild("autoCenterCheckbox"); autoTrackCheckbox = m_DetailsViewObj->findChild("autoTrackCheckbox"); m_SlewTelescopeButtonObj = m_BaseObj->findChild("slewTelescopeButtonObj"); connect(m_SlewTelescopeButtonObj, SIGNAL(slewTelescopeButtonClicked()), this, SLOT(onSlewTelescopeButtonClicked())); m_DetailsButtonObj = m_BaseObj->findChild("detailsButtonObj"); connect(m_DetailsButtonObj, SIGNAL(detailsButtonClicked()), this, SLOT(onDetailsButtonClicked())); QObject *settingsIconObj = m_BaseObj->findChild("settingsIconObj"); connect(settingsIconObj, SIGNAL(settingsIconClicked()), this, SLOT(onSettingsIconClicked())); inspectIconObj = m_BaseObj->findChild("inspectIconObj"); connect(inspectIconObj, SIGNAL(inspectIconClicked(bool)), this, SLOT(onInspectIconClicked(bool))); visibleIconObj = m_BaseObj->findChild("visibleIconObj"); connect(visibleIconObj, SIGNAL(visibleIconClicked(bool)), this, SLOT(onVisibleIconClicked(bool))); favoriteIconObj = m_BaseObj->findChild("favoriteIconObj"); connect(favoriteIconObj, SIGNAL(favoriteIconClicked(bool)), this, SLOT(onFavoriteIconClicked(bool))); QObject *reloadIconObj = m_BaseObj->findChild("reloadIconObj"); connect(reloadIconObj, SIGNAL(reloadIconClicked()), this, SLOT(onReloadIconClicked())); QObject *downloadIconObj = m_BaseObj->findChild("downloadIconObj"); connect(downloadIconObj, SIGNAL(downloadIconClicked()), this, SLOT(onUpdateIconClicked())); m_BaseView->setResizeMode(QQuickView::SizeRootObjectToView); m_BaseView->show(); // Fix some weird issue with what's interesting panel view under Windows // In Qt 5.9 it content is messed up and there is no way to close the panel - #ifdef Q_OS_WIN - m_BaseView->setFlags(Qt::WindowCloseButtonHint); - #endif +#ifdef Q_OS_WIN + m_BaseView->setFlags(Qt::WindowCloseButtonHint); +#endif connect(KStars::Instance()->map(), SIGNAL(objectClicked(SkyObject*)), this, SLOT(inspectSkyObjectOnClick(SkyObject*))); manager.reset(new QNetworkAccessManager()); setProgressBarVisible(true); connect(m_ModManager.get(), SIGNAL(loadProgressUpdated(double)), this, SLOT(updateProgress(double))); connect(m_ModManager.get(), SIGNAL(modelUpdated()), this, SLOT(refreshListView())); m_ViewsRowObj->setProperty("enabled", false); inspectOnClick = false; nightVision = m_BaseObj->findChild("nightVision"); //if (Options::darkAppColors()) // nightVision->setProperty("state", "active"); } void WIView::setNightVisionOn(bool on) { if (on) nightVision->setProperty("state", "active"); else nightVision->setProperty("state", ""); if (m_CurSoItem != nullptr) loadDetailsView(m_CurSoItem, m_CurIndex); } void WIView::setProgressBarVisible(bool visible) { m_ProgressBar->setProperty("visible", visible); } void WIView::updateProgress(double value) { m_ProgressBar->setProperty("value", value); if (value == 1) { setProgressBarVisible(false); m_ViewsRowObj->setProperty("enabled", true); m_loadingMessage->setProperty("state", ""); } else { setProgressBarVisible(true); m_loadingMessage->setProperty("state", "loading"); } } void WIView::updateObservingConditions() { int bortle = Options::bortleClass(); /** NOTE This part of the code dealing with equipment type is presently not required as WI does not differentiate between Telescope and Binoculars. It only needs the aperture of the equipment whichever available. However this is kept as a part of the code as support to be utilised in the future. **/ ObsConditions::Equipment equip = ObsConditions::None; if (Options::telescopeCheck() && Options::binocularsCheck()) equip = ObsConditions::Both; else if (Options::telescopeCheck()) equip = ObsConditions::Telescope; else if (Options::binocularsCheck()) equip = ObsConditions::Binoculars; ObsConditions::TelescopeType telType; if (KStars::Instance()->getWIEquipSettings()) telType = (equip == ObsConditions::Telescope) ? KStars::Instance()->getWIEquipSettings()->getTelType() : - ObsConditions::Invalid; + ObsConditions::Invalid; else telType = ObsConditions::Invalid; int aperture = 100; //This doesn't work correctly, FIXME!! // if(KStars::Instance()->getWIEquipSettings()) // aperture = KStars::Instance()->getWIEquipSettings()->getAperture(); if (!m_Obs) m_Obs = new ObsConditions(bortle, aperture, equip, telType); else m_Obs->setObsConditions(bortle, aperture, equip, telType); } void WIView::onCategorySelected(QString model) { m_CurrentObjectListName = model; m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName)); m_CurIndex = -2; if (!m_ModManager->showOnlyVisibleObjects()) visibleIconObj->setProperty("state", "unchecked"); if (!m_ModManager->showOnlyFavoriteObjects()) favoriteIconObj->setProperty("state", "unchecked"); if (model == "ngc" && (!m_ModManager->isNGCLoaded())) { QtConcurrent::run(m_ModManager.get(), &ModelManager::loadNGCCatalog); return; } if (model == "ic" && (!m_ModManager->isICLoaded())) { QtConcurrent::run(m_ModManager.get(), &ModelManager::loadICCatalog); return; } if (model == "sharpless" && (!m_ModManager->isSharplessLoaded())) { QtConcurrent::run(m_ModManager.get(), &ModelManager::loadSharplessCatalog); return; } updateModel(*m_Obs); } void WIView::onSoListItemClicked(int index) { SkyObjItem *soitem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem(index); if (soitem) loadDetailsView(soitem, index); } void WIView::onNextObjClicked() { if (!m_CurrentObjectListName.isEmpty()) { int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); SkyObjItem *nextItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize); loadDetailsView(nextItem, (m_CurIndex + 1) % modelSize); } } void WIView::onPrevObjClicked() { if (!m_CurrentObjectListName.isEmpty()) { int modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); SkyObjItem *prevItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize); loadDetailsView(prevItem, (m_CurIndex - 1 + modelSize) % modelSize); } } void WIView::onCenterButtonClicked() { ///Center map on selected sky-object SkyObject *so = m_CurSoItem->getSkyObject(); KStars *kstars = KStars::Instance(); if (so) { kstars->map()->setFocusPoint(so); kstars->map()->setFocusObject(so); kstars->map()->setDestination(*kstars->map()->focusPoint()); Options::setIsTracking(autoTrackCheckbox->property("checked") == true); } } void WIView::onSlewTelescopeButtonClicked() { if (KMessageBox::Continue == - KMessageBox::warningContinueCancel(nullptr, "Are you sure you want your telescope to slew to this object?", - i18n("Continue Slew"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), - "continue_wi_slew_warning")) + KMessageBox::warningContinueCancel(nullptr, "Are you sure you want your telescope to slew to this object?", + i18n("Continue Slew"), KStandardGuiItem::cont(), KStandardGuiItem::cancel(), + "continue_wi_slew_warning")) { #ifdef HAVE_INDI if (INDIListener::Instance()->size() == 0) { KMessageBox::sorry(0, i18n("KStars did not find any active telescopes.")); return; } foreach (ISD::GDInterface *gd, INDIListener::Instance()->getDevices()) { INDI::BaseDevice *bd = gd->getBaseDevice(); if (gd->getType() != KSTARS_TELESCOPE) continue; if (bd == nullptr) continue; if (bd->isConnected() == false) { KMessageBox::error( 0, i18n("Telescope %1 is offline. Please connect and retry again.", gd->getDeviceName())); return; } ISD::GDSetCommand SlewCMD(INDI_SWITCH, "ON_COORD_SET", "TRACK", ISS_ON, this); gd->setProperty(&SlewCMD); gd->runCommand(INDI_SEND_COORDS, m_CurSoItem->getSkyObject()); /// Slew map to selected sky-object onCenterButtonClicked(); return; } KMessageBox::sorry(0, i18n("KStars did not find any active telescopes.")); #endif } } void WIView::onDetailsButtonClicked() { ///Code taken from WUTDialog::slotDetails() KStars *kstars = KStars::Instance(); SkyObject *so = m_CurSoItem->getSkyObject(); if (so) { DetailDialog *detail = new DetailDialog(so, kstars->data()->lt(), kstars->data()->geo(), kstars); detail->exec(); delete detail; } } void WIView::onSettingsIconClicked() { KStars *kstars = KStars::Instance(); kstars->showWISettingsUI(); } void WIView::onReloadIconClicked() { if (!m_CurrentObjectListName.isEmpty()) { updateModel(*m_Obs); m_CurIndex = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjIndex(m_CurSoItem); } loadDetailsView(m_CurSoItem, m_CurIndex); } void WIView::onVisibleIconClicked(bool visible) { m_ModManager->setShowOnlyVisibleObjects(visible); onReloadIconClicked(); } void WIView::onFavoriteIconClicked(bool favorites) { m_ModManager->setShowOnlyFavoriteObjects(favorites); onReloadIconClicked(); } void WIView::onUpdateIconClicked() { QMessageBox mbox; QPushButton *currentObject = mbox.addButton("Current Object", QMessageBox::AcceptRole); QPushButton *missingObjects = nullptr; QPushButton *allObjects = nullptr; mbox.setText("Please choose which object(s) to try to update with Wikipedia data."); if (!m_CurrentObjectListName.isEmpty()) { missingObjects = mbox.addButton("Objects with no data", QMessageBox::AcceptRole); allObjects = mbox.addButton("Entire List", QMessageBox::AcceptRole); } QPushButton *cancel = mbox.addButton("Cancel", QMessageBox::AcceptRole); mbox.setDefaultButton(cancel); mbox.exec(); if (mbox.clickedButton() == currentObject) { if (m_CurSoItem != nullptr) { tryToUpdateWikipediaInfo(m_CurSoItem, getWikipediaName(m_CurSoItem)); } } else if (mbox.clickedButton() == allObjects || mbox.clickedButton() == missingObjects) { SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName); if (model->rowCount() > 0) { tryToUpdateWikipediaInfoInModel(mbox.clickedButton() == missingObjects); } else { qDebug() << "No Objects in List!"; } } } void WIView::refreshListView() { m_Ctxt->setContextProperty("soListModel", nullptr); if (!m_CurrentObjectListName.isEmpty()) m_Ctxt->setContextProperty("soListModel", m_ModManager->returnModel(m_CurrentObjectListName)); if (m_CurIndex == -2) onSoListItemClicked(0); if (m_CurIndex != -1) m_SoListObj->setProperty("currentIndex", m_CurIndex); } -void WIView::updateModel(ObsConditions& obs) +void WIView::updateModel(ObsConditions &obs) { if (!m_CurrentObjectListName.isEmpty()) { m_Obs = &obs; m_ModManager->updateModel(m_Obs, m_CurrentObjectListName); } } -void WIView::inspectSkyObject(const QString& name) +void WIView::inspectSkyObject(const QString &name) { if (!name.isEmpty() && name != "star") { SkyObject *obj = KStarsData::Instance()->skyComposite()->findByName(name); if (obj) inspectSkyObject(obj); } } void WIView::inspectSkyObjectOnClick(SkyObject *obj) { if (inspectOnClick && KStars::Instance()->isWIVisible()) inspectSkyObject(obj); } void WIView::inspectSkyObject(SkyObject *obj) { if (!obj) return; if (obj->name() != "star") { m_CurrentObjectListName = ""; trackedItem.reset(new SkyObjItem(obj)); loadDetailsView(trackedItem.get(), -1); m_BaseObj->setProperty("state", "singleItemSelected"); m_CategoryTitle->setProperty("text", "Selected Object"); } } void WIView::loadDetailsView(SkyObjItem *soitem, int index) { if (soitem == nullptr) return; int modelSize = -1; if (index != -1) modelSize = m_ModManager->returnModel(m_CurrentObjectListName)->rowCount(); if (soitem != m_CurSoItem) m_CurSoItem = soitem; m_CurIndex = index; if (modelSize <= 1) { m_NextObj->setProperty("visible", "false"); m_PrevObj->setProperty("visible", "false"); } else { SkyObjItem *nextItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex + 1) % modelSize); SkyObjItem *prevItem = m_ModManager->returnModel(m_CurrentObjectListName)->getSkyObjItem((m_CurIndex - 1 + modelSize) % modelSize); m_NextObj->setProperty("visible", "true"); m_PrevObj->setProperty("visible", "true"); QObject *nextTextObj = m_NextObj->findChild("nextTextObj"); nextTextObj->setProperty("text", nextItem->getName()); QObject *prevTextObj = m_PrevObj->findChild("prevTextObj"); prevTextObj->setProperty("text", prevItem->getName()); } QObject *sonameObj = m_DetailsViewObj->findChild("sonameObj"); QObject *posTextObj = m_DetailsViewObj->findChild("posTextObj"); QObject *detailImage = m_DetailsViewObj->findChild("detailImage"); QObject *detailsTextObj = m_DetailsViewObj->findChild("detailsTextObj"); sonameObj->setProperty("text", soitem->getDescName()); posTextObj->setProperty("text", soitem->getPosition()); detailImage->setProperty("refreshableSource", soitem->getImageURL(false)); loadObjectDescription(soitem); infoBoxText->setProperty( "text", "

No Wikipedia information.
Please try to download it using the orange download button below."); loadObjectInfoBox(soitem); QString summary = soitem->getSummary(false); QString magText; if (soitem->getType() == SkyObjItem::Constellation) magText = xi18n("Magnitude: --"); else magText = xi18n("Magnitude: %1", QLocale().toString(soitem->getMagnitude(), 'f', 2)); QString sbText = xi18n("Surface Brightness: %1", soitem->getSurfaceBrightness()); QString sizeText = xi18n("Size: %1", soitem->getSize()); QString details = summary + "
" + sbText + "
" + magText + "
" + sizeText; detailsTextObj->setProperty("text", details); if (autoCenterCheckbox->property("checked") == true) { QTimer::singleShot(500, this, SLOT(onCenterButtonClicked())); } if (m_CurIndex != -1) m_SoListObj->setProperty("currentIndex", m_CurIndex); } QString WIView::getWikipediaName(SkyObjItem *soitem) { if (!soitem) return ""; QString name; if (soitem->getName().toLower().startsWith(QLatin1String("m "))) name = soitem->getName().replace("M ", "Messier_").remove(' '); else if (soitem->getName().toLower().startsWith(QLatin1String("ngc"))) name = soitem->getName().toLower().replace("ngc", "NGC_").remove(' '); else if (soitem->getName().toLower().startsWith(QLatin1String("ic"))) name = soitem->getName().toLower().replace("ic", "IC_").remove(' '); else if (soitem->getType() == SkyObjItem::Constellation) { QStringList words = soitem->getName().split(' '); for (int i = 0; i < words.size(); i++) { QString temp = words.at(i).toLower(); temp[0] = temp[0].toUpper(); words.replace(i, temp); } name = words.join("_") + "_(constellation)"; if (name.contains("Serpens")) name = "Serpens_(constellation)"; } - else if (soitem->getTypeName() == "Asteroid") + else if (soitem->getTypeName() == i18n("Asteroid")) name = soitem->getName().remove(' ') + "_(asteroid)"; - else if (soitem->getTypeName() == "Comet") + else if (soitem->getTypeName() == i18n("Comet")) name = soitem->getLongName(); - else if (soitem->getType() == SkyObjItem::Planet && soitem->getName() != "Sun" && soitem->getName() != "Moon") + else if (soitem->getType() == SkyObjItem::Planet && soitem->getName() != i18n("Sun") && soitem->getName() != i18n("Moon")) name = soitem->getName().remove(' ') + "_(planet)"; else if (soitem->getType() == SkyObjItem::Star) { StarObject *star = dynamic_cast(soitem->getSkyObject()); // The greek name seems to give the most consistent search results for opensearch. name = star->gname(false).replace(' ', '_'); if (name.isEmpty()) name = soitem->getName().replace(' ', '_') + "_(star)"; name.remove('[').remove(']'); } else name = soitem->getName().remove(' '); return name; } void WIView::updateWikipediaDescription(SkyObjItem *soitem) { if (!soitem) return; QString name = getWikipediaName(soitem); QUrl url("https://en.wikipedia.org/w/api.php?action=opensearch&search=" + name + "&format=xml"); QNetworkReply *response = manager->get(QNetworkRequest(url)); - QTimer::singleShot(30000, response, [response] { //Shut it down after 30 sec. + QTimer::singleShot(30000, response, [response] //Shut it down after 30 sec. + { response->abort(); response->deleteLater(); qDebug() << "Wikipedia Download Timed out."; }); - connect(response, &QNetworkReply::finished, this, [soitem, this, response] { + connect(response, &QNetworkReply::finished, this, [soitem, this, response] + { response->deleteLater(); if (response->error() != QNetworkReply::NoError) return; QString contentType = response->header(QNetworkRequest::ContentTypeHeader).toString(); if (!contentType.contains("charset=utf-8")) { qWarning() << "Content charsets other than utf-8 are not implemented yet."; return; } QString result = QString::fromUtf8(response->readAll()); int leftPos = result.indexOf("") - leftPos; int leftURL = result.indexOf("") + 26; int rightURL = result.indexOf("") - leftURL; QString srchtml = - "\n

Source: (" + - "Wikipedia)"; //Note the \n is so that the description is put on another line in the file. Doesn't affect the display but allows the source to be loaded in the details but not the list. + "\n

Source: (" + + "Wikipedia)"; //Note the \n is so that the description is put on another line in the file. Doesn't affect the display but allows the source to be loaded in the details but not the list. QString html = "" + result.mid(leftPos, rightPos) + srchtml + ""; saveObjectInfoBoxText(soitem, "description", html); -//TODO is this explicitly needed now with themes? + //TODO is this explicitly needed now with themes? #if 0 QString color = (Options::darkAppColors()) ? "red" : "white"; QString linkColor = (Options::darkAppColors()) ? "red" : "yellow"; html = "" + html + ""; + ";} a {text-decoration: none;color:" + linkColor + ";}" + html + ""; #endif if (soitem == m_CurSoItem) descTextObj->setProperty("text", html); refreshListView(); }); } void WIView::loadObjectDescription(SkyObjItem *soitem) { QFile file; QString fname = "description-" + soitem->getName().toLower().remove(' ') + ".html"; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname); //determine filename in local user KDE directory tree. if (file.exists()) { if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); bool isDarkTheme = (Options::currentTheme() == "Night Vision"); QString color = (isDarkTheme) ? "red" : "white"; QString linkColor = (isDarkTheme) ? "red" : "yellow"; QString line = "
" + - in.readAll() + ""; + ";} a {text-decoration: none;color:" + linkColor + ";}
" + + in.readAll() + ""; descTextObj->setProperty("text", line); file.close(); } } else { descTextObj->setProperty("text", soitem->getTypeName()); } } void WIView::loadObjectInfoBox(SkyObjItem *soitem) { if (!soitem) return; QFile file; QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html"; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname); //determine filename in local user KDE directory tree. if (file.exists()) { if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); QString infoBoxHTML; while (!in.atEnd()) { infoBoxHTML = in.readAll(); QString wikiImageName = QUrl::fromLocalFile( KSPaths::locate(QStandardPaths::GenericDataLocation, "descriptions/wikiImage-" + soitem->getName().toLower().remove(' ') + ".png")) - .url(); + .url(); if (!wikiImageName.isEmpty()) { int captionEnd = infoBoxHTML.indexOf( - ""); //Start looking for the image AFTER the caption. Planets have images in their caption. + ""); //Start looking for the image AFTER the caption. Planets have images in their caption. if (captionEnd == -1) captionEnd = 0; int leftImg = infoBoxHTML.indexOf("src=\"", captionEnd) + 5; int rightImg = infoBoxHTML.indexOf("\"", leftImg) - leftImg; QString imgURL = infoBoxHTML.mid(leftImg, rightImg); infoBoxHTML.replace(imgURL, wikiImageName); } bool isDarkTheme = (Options::currentTheme() == "Night Vision"); QString color = (isDarkTheme) ? "red" : "white"; QString linkColor = (isDarkTheme) ? "red" : "yellow"; if (isDarkTheme) infoBoxHTML.replace("color: white", "color: " + color); infoBoxHTML = "" + infoBoxHTML + ""; infoBoxText->setProperty("text", infoBoxHTML); } file.close(); } } } void WIView::tryToUpdateWikipediaInfoInModel(bool onlyMissing) { SkyObjListModel *model = m_ModManager->returnModel(m_CurrentObjectListName); int objectNum = model->rowCount(); for (int i = 0; i < objectNum; i++) { SkyObjItem *soitem = model->getSkyObjItem(i); QFile file; QString fname = "infoText-" + soitem->getName().toLower().remove(' ') + ".html"; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions/" + fname); //determine filename in local user KDE directory tree. if (file.exists() && onlyMissing) continue; tryToUpdateWikipediaInfo(soitem, getWikipediaName(soitem)); } } void WIView::tryToUpdateWikipediaInfo(SkyObjItem *soitem, QString name) { if (name.isEmpty() || !soitem) return; QUrl url("https://en.wikipedia.org/w/index.php?action=render&title=" + name + "&redirects"); QNetworkReply *response = manager->get(QNetworkRequest(url)); - QTimer::singleShot(30000, response, [response] { //Shut it down after 30 sec. + QTimer::singleShot(30000, response, [response] //Shut it down after 30 sec. + { response->abort(); response->deleteLater(); qDebug() << "Wikipedia Download Timed out."; }); - connect(response, &QNetworkReply::finished, this, [name, response, soitem, this] { + connect(response, &QNetworkReply::finished, this, [name, response, soitem, this] + { response->deleteLater(); if (response->error() == QNetworkReply::ContentNotFoundError) { QString html = "
Sorry, No Wikipedia article with this object name seems to exist. It is possible that " - "one does exist but does not match the namimg scheme."; + "one does exist but does not match the namimg scheme."; saveObjectInfoBoxText(soitem, "infoText", html); infoBoxText->setProperty("text", html); return; } if (response->error() != QNetworkReply::NoError) return; QString result = QString::fromUtf8(response->readAll()); int leftPos = result.indexOf("", leftPos) - leftPos; if (leftPos == -1) - { //No InfoBox is Found + { + //No InfoBox is Found if (soitem->getType() == SkyObjItem::Star && - name != soitem->getName().replace(' ', '_')) //For stars, the regular name rather than gname + name != soitem->getName().replace(' ', '_')) //For stars, the regular name rather than gname { tryToUpdateWikipediaInfo(soitem, soitem->getName().replace(' ', '_')); return; } QString html = "
Sorry, no Information Box in the object's Wikipedia article was found."; saveObjectInfoBoxText(soitem, "infoText", html); infoBoxText->setProperty("text", html); return; } updateWikipediaDescription(soitem); QString infoText = result.mid(leftPos, rightPos); //This if statement should correct for a situation like for the planets where there is a single internal table inside the infoText Box. if (infoText.indexOf("", leftPos + rightPos + 6) - leftPos; infoText = result.mid(leftPos, rightPos); } //This next section is for the headers in the colored boxes. It turns them black instead of white because they are more visible that way. infoText.replace("background: #", "color:black;background: #") - .replace("background-color: #", "color:black;background: #") - .replace("background:#", "color:black;background:#") - .replace("background-color:#", "color:black;background:#") - .replace("background: pink", "color:black;background: pink"); + .replace("background-color: #", "color:black;background: #") + .replace("background:#", "color:black;background:#") + .replace("background-color:#", "color:black;background:#") + .replace("background: pink", "color:black;background: pink"); infoText.replace("//", "http://"); //This is to fix links on wikipedia which are missing http from the url infoText.replace("https:http:", "https:") - .replace("http:http:", "http:"); //Just in case it was done to an actual complete url + .replace("http:http:", "http:"); //Just in case it was done to an actual complete url //This section is intended to remove links from the object name header at the top. The links break up the header. int thLeft = infoText.indexOf("
", thLeft); int firstA = infoText.indexOf("", firstA) - firstA + 1; infoText.remove(firstA, rightA); int endA = infoText.indexOf("", firstA); infoText.remove(endA, 4); } } int annotationLeft = infoText.indexOf("", annotationLeft) + 13 - annotationLeft; infoText.remove(annotationLeft, annotationRight); //This removes the annotation that does not render correctly for some DSOs. int mathLeft = infoText.indexOf("", mathLeft) + 1 - mathLeft; infoText.remove(mathLeft, mathRight); //This removes an image that doesn't render properly for some DSOs. infoText.replace("style=\"width:22em\"", "style=\"width:100%;background-color: black;color: white;\""); infoText = infoText + "
(Source: Wikipedia)"; saveInfoURL(soitem, "https://en.wikipedia.org/w/index.php?title=" + name + "&redirects"); int captionEnd = infoText.indexOf( - ""); //Start looking for the image AFTER the caption. Planets have images in their caption. + ""); //Start looking for the image AFTER the caption. Planets have images in their caption. if (captionEnd == -1) captionEnd = 0; int leftImg = infoText.indexOf("src=\"", captionEnd) + 5; if (leftImg > captionEnd + 5) { int rightImg = infoText.indexOf("\"", leftImg) - leftImg; QString imgURL = infoText.mid(leftImg, rightImg); imgURL.replace( "http://upload.wikimedia.org", "https://upload.wikimedia.org"); //Although they will display, the images apparently don't download properly unless they are https. saveImageURL(soitem, imgURL); downloadWikipediaImage(soitem, imgURL); } QString html = "
" + infoText + "
"; saveObjectInfoBoxText(soitem, "infoText", html); bool isDarkTheme = (Options::currentTheme() == "Night Vision"); QString color = (isDarkTheme) ? "red" : "white"; QString linkColor = (isDarkTheme) ? "red" : "yellow"; if (isDarkTheme) html.replace("color: white", "color: " + color); html = "" + html + ""; if (soitem == m_CurSoItem) infoBoxText->setProperty("text", html); }); } void WIView::saveObjectInfoBoxText(SkyObjItem *soitem, QString type, QString text) { QFile file; QString fname = type + '-' + soitem->getName().toLower().remove(' ') + ".html"; QDir writableDir; QString filePath = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions"; writableDir.mkpath(filePath); file.setFileName(filePath + '/' + fname); //determine filename in local user KDE directory tree. if (file.open(QIODevice::WriteOnly) == false) { qDebug() << "Image text cannot be saved for later. file save error"; return; } else { QTextStream stream(&file); stream << text; file.close(); } } void WIView::saveImageURL(SkyObjItem *soitem, QString imageURL) { QFile file; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "image_url.dat"); //determine filename in local user KDE directory tree. QString entry = soitem->getName() + ':' + "Show Wikipedia Image" + ':' + imageURL; if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); QString line; while (!in.atEnd()) { line = in.readLine(); if (line == entry) { file.close(); return; } } file.close(); } if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) { qDebug() << "Image URL cannot be saved for later. image_url.dat error"; return; } else { QTextStream stream(&file); stream << entry << endl; file.close(); } } void WIView::saveInfoURL(SkyObjItem *soitem, QString infoURL) { QFile file; file.setFileName(KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "info_url.dat"); //determine filename in local user KDE directory tree. QString entry = soitem->getName() + ':' + "Wikipedia Page" + ':' + infoURL; if (file.open(QIODevice::ReadOnly)) { QTextStream in(&file); QString line; while (!in.atEnd()) { line = in.readLine(); if (line == entry) { file.close(); return; } } file.close(); } if (!file.open(QIODevice::ReadWrite | QIODevice::Append)) { qDebug() << "Info URL cannot be saved for later. info_url.dat error"; return; } else { QTextStream stream(&file); stream << entry << endl; file.close(); } } void WIView::downloadWikipediaImage(SkyObjItem *soitem, QString imageURL) { QString fname = "wikiImage-" + soitem->getName().toLower().remove(' ') + ".png"; QDir writableDir; QString filePath = KSPaths::writableLocation(QStandardPaths::GenericDataLocation) + "descriptions"; writableDir.mkpath(filePath); QString fileN = filePath + '/' + fname; QNetworkReply *response = manager->get(QNetworkRequest(QUrl(imageURL))); - QTimer::singleShot(60000, response, [response] { //Shut it down after 60 sec. + QTimer::singleShot(60000, response, [response] //Shut it down after 60 sec. + { response->abort(); response->deleteLater(); qDebug() << "Image Download Timed out."; }); - connect(response, &QNetworkReply::finished, this, [fileN, response, this] { + connect(response, &QNetworkReply::finished, this, [fileN, response, this] + { response->deleteLater(); if (response->error() != QNetworkReply::NoError) return; QImage *image = new QImage(); QByteArray responseData = response->readAll(); if (image->loadFromData(responseData)) { image->save(fileN); refreshListView(); //This is to update the images displayed with the new image. } else qDebug() << "image not downloaded"; }); } diff --git a/kstars/tools/wutdialog.cpp b/kstars/tools/wutdialog.cpp index aa26b01bb..32fc5c266 100644 --- a/kstars/tools/wutdialog.cpp +++ b/kstars/tools/wutdialog.cpp @@ -1,627 +1,627 @@ /*************************************************************************** wutdialog.cpp - K Desktop Planetarium ------------------- begin : Die Feb 25 2003 copyright : (C) 2003 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. * * * ***************************************************************************/ #include "wutdialog.h" #include "kstars.h" #include "skymap.h" #include "dialogs/detaildialog.h" #include "dialogs/locationdialog.h" #include "dialogs/timedialog.h" #include "skyobjects/kssun.h" #include "skyobjects/ksmoon.h" #include "skycomponents/skymapcomposite.h" #include "tools/observinglist.h" WUTDialogUI::WUTDialogUI(QWidget *p) : QFrame(p) { setupUi(this); } WUTDialog::WUTDialog(QWidget *parent, bool _session, GeoLocation *_geo, KStarsDateTime _lt) : QDialog(parent), session(_session), T0(_lt), geo(_geo) { #ifdef Q_OS_OSX setWindowFlags(Qt::Tool | Qt::WindowStaysOnTopHint); #endif WUT = new WUTDialogUI(this); QVBoxLayout *mainLayout = new QVBoxLayout(this); mainLayout->addWidget(WUT); setWindowTitle(i18n("What's up Tonight")); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Close, this); mainLayout->addWidget(buttonBox); connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject())); setModal(false); //If the Time is earlier than 6:00 am, assume the user wants the night of the previous date if (T0.time().hour() < 6) T0 = T0.addDays(-1); //Now, set time T0 to midnight (of the following day) T0.setTime(QTime(0, 0, 0)); T0 = T0.addDays(1); UT0 = geo->LTtoUT(T0); //Set the Tomorrow date/time to Noon the following day Tomorrow = T0.addSecs(12 * 3600); TomorrowUT = geo->LTtoUT(Tomorrow); //Set the Evening date/time to 6:00 pm Evening = T0.addSecs(-6 * 3600); EveningUT = geo->LTtoUT(Evening); QString sGeo = geo->translatedName(); if (!geo->translatedProvince().isEmpty()) sGeo += ", " + geo->translatedProvince(); sGeo += ", " + geo->translatedCountry(); WUT->LocationLabel->setText(i18n("at %1", sGeo)); WUT->DateLabel->setText(i18n("The night of %1", QLocale().toString(Evening.date(), QLocale::LongFormat))); m_Mag = 10.0; WUT->MagnitudeEdit->setValue(m_Mag); //WUT->MagnitudeEdit->setSliderEnabled( true ); WUT->MagnitudeEdit->setSingleStep(0.100); initCategories(); makeConnections(); QTimer::singleShot(0, this, SLOT(init())); } void WUTDialog::makeConnections() { connect(WUT->DateButton, SIGNAL(clicked()), SLOT(slotChangeDate())); connect(WUT->LocationButton, SIGNAL(clicked()), SLOT(slotChangeLocation())); connect(WUT->CenterButton, SIGNAL(clicked()), SLOT(slotCenter())); connect(WUT->DetailButton, SIGNAL(clicked()), SLOT(slotDetails())); connect(WUT->ObslistButton, SIGNAL(clicked()), SLOT(slotObslist())); connect(WUT->CategoryListWidget, SIGNAL(currentTextChanged(QString)), SLOT(slotLoadList(QString))); connect(WUT->ObjectListWidget, SIGNAL(currentTextChanged(QString)), SLOT(slotDisplayObject(QString))); connect(WUT->EveningMorningBox, SIGNAL(activated(int)), SLOT(slotEveningMorning(int))); connect(WUT->MagnitudeEdit, SIGNAL(valueChanged(double)), SLOT(slotChangeMagnitude())); } void WUTDialog::initCategories() { m_Categories << i18n("Planets") << i18n("Stars") << i18n("Nebulae") << i18n("Galaxies") << i18n("Star Clusters") << i18n("Constellations") << i18n("Asteroids") << i18n("Comets"); foreach (const QString &c, m_Categories) WUT->CategoryListWidget->addItem(c); WUT->CategoryListWidget->setCurrentRow(0); } void WUTDialog::init() { QString sRise, sSet, sDuration; float Dur; int hDur, mDur; KStarsData *data = KStarsData::Instance(); // reset all lists foreach (const QString &c, m_Categories) { if (m_VisibleList.contains(c)) visibleObjects(c).clear(); else m_VisibleList[c] = QSet(); m_CategoryInitialized[c] = false; } // sun almanac information KSSun *oSun = dynamic_cast(data->objectNamed("Sun")); sunRiseTomorrow = oSun->riseSetTime(TomorrowUT, geo, true); sunSetToday = oSun->riseSetTime(EveningUT, geo, false); sunRiseToday = oSun->riseSetTime(EveningUT, geo, true); //check to see if Sun is circumpolar KSNumbers *num = new KSNumbers(UT0.djd()); KSNumbers *oldNum = new KSNumbers(data->ut().djd()); CachingDms LST = geo->GSTtoLST(T0.gst()); oSun->updateCoords(num, true, geo->lat(), &LST, true); if (oSun->checkCircumpolar(geo->lat())) { if (oSun->alt().Degrees() > 0.0) { sRise = i18n("circumpolar"); sSet = i18n("circumpolar"); sDuration = "00:00"; Dur = hDur = mDur = 0; } else { sRise = i18n("does not rise"); sSet = i18n("does not rise"); sDuration = "24:00"; Dur = hDur = 24; mDur = 0; } } else { //Round times to the nearest minute by adding 30 seconds to the time sRise = QLocale().toString(sunRiseTomorrow); sSet = QLocale().toString(sunSetToday); Dur = 24.0 + (float)sunRiseTomorrow.hour() + (float)sunRiseTomorrow.minute() / 60.0 + (float)sunRiseTomorrow.second() / 3600.0 - (float)sunSetToday.hour() - (float)sunSetToday.minute() / 60.0 - (float)sunSetToday.second() / 3600.0; hDur = int(Dur); mDur = int(60.0 * (Dur - (float)hDur)); QTime tDur(hDur, mDur); //sDuration = QLocale().toString(tDur); // Should always be in 24 hour format sDuration = tDur.toString("hh:mm"); } WUT->SunSetLabel->setText(i18nc("Sunset at time %1 on date %2", "Sunset: %1 on %2", sSet, QLocale().toString(Evening.date(), QLocale::LongFormat))); WUT->SunRiseLabel->setText(i18nc("Sunrise at time %1 on date %2", "Sunrise: %1 on %2", sRise, QLocale().toString(Tomorrow.date(), QLocale::LongFormat))); if (Dur == 0) WUT->NightDurationLabel->setText(i18n("Night duration: %1", sDuration)); else if (Dur > 1) WUT->NightDurationLabel->setText(i18n("Night duration: %1 hours", sDuration)); else if (Dur == 1) WUT->NightDurationLabel->setText(i18n("Night duration: %1 hour", sDuration)); else if (mDur > 1) WUT->NightDurationLabel->setText(i18n("Night duration: %1 minutes", sDuration)); else if (mDur == 1) WUT->NightDurationLabel->setText(i18n("Night duration: %1 minute", sDuration)); // moon almanac information - KSMoon *oMoon = dynamic_cast(data->objectNamed("Moon")); + KSMoon *oMoon = dynamic_cast(data->objectNamed(i18n("Moon"))); moonRise = oMoon->riseSetTime(UT0, geo, true); moonSet = oMoon->riseSetTime(UT0, geo, false); //check to see if Moon is circumpolar oMoon->updateCoords(num, true, geo->lat(), &LST, true); if (oMoon->checkCircumpolar(geo->lat())) { if (oMoon->alt().Degrees() > 0.0) { sRise = i18n("circumpolar"); sSet = i18n("circumpolar"); } else { sRise = i18n("does not rise"); sSet = i18n("does not rise"); } } else { //Round times to the nearest minute by adding 30 seconds to the time sRise = QLocale().toString(moonRise.addSecs(30)); sSet = QLocale().toString(moonSet.addSecs(30)); } WUT->MoonRiseLabel->setText( i18n("Moon rises at: %1 on %2", sRise, QLocale().toString(Evening.date(), QLocale::LongFormat))); // If the moon rises and sets on the same day, this will be valid [ Unless // the moon sets on the next day after staying on for over 24 hours ] if (moonSet > moonRise) WUT->MoonSetLabel->setText( i18n("Moon sets at: %1 on %2", sSet, QLocale().toString(Evening.date(), QLocale::LongFormat))); else WUT->MoonSetLabel->setText( i18n("Moon sets at: %1 on %2", sSet, QLocale().toString(Tomorrow.date(), QLocale::LongFormat))); oMoon->findPhase(nullptr); WUT->MoonIllumLabel->setText(oMoon->phaseName() + QString(" (%1%)").arg(int(100.0 * oMoon->illum()))); //Restore Sun's and Moon's coordinates, and recompute Moon's original Phase oMoon->updateCoords(oldNum, true, geo->lat(), data->lst(), true); oSun->updateCoords(oldNum, true, geo->lat(), data->lst(), true); oMoon->findPhase(nullptr); if (WUT->CategoryListWidget->currentItem()) slotLoadList(WUT->CategoryListWidget->currentItem()->text()); delete num; delete oldNum; } QSet &WUTDialog::visibleObjects(const QString &category) { return m_VisibleList[category]; } bool WUTDialog::isCategoryInitialized(const QString &category) { return m_CategoryInitialized[category]; } void WUTDialog::slotLoadList(const QString &c) { KStarsData *data = KStarsData::Instance(); if (!m_VisibleList.contains(c)) return; WUT->ObjectListWidget->clear(); setCursor(QCursor(Qt::WaitCursor)); if (!isCategoryInitialized(c)) { if (c == m_Categories[0]) //Planets { foreach (const QString &name, data->skyComposite()->objectNames(SkyObject::PLANET)) { SkyObject *o = data->skyComposite()->findByName(name); if (checkVisibility(o) && o->mag() <= m_Mag) visibleObjects(c).insert(o); } m_CategoryInitialized[c] = true; } else if (c == m_Categories[1]) //Stars { QVector> starObjects; starObjects.append(data->skyComposite()->objectLists(SkyObject::STAR)); starObjects.append(data->skyComposite()->objectLists(SkyObject::CATALOG_STAR)); for (const auto &object : starObjects) { - const SkyObject *o = object.second; + const SkyObject *o = object.second; - if (checkVisibility(o) && o->mag() <= m_Mag) - { - visibleObjects(c).insert(o); - } + if (checkVisibility(o) && o->mag() <= m_Mag) + { + visibleObjects(c).insert(o); + } } m_CategoryInitialized[c] = true; } else if (c == m_Categories[5]) //Constellations { foreach (SkyObject *o, data->skyComposite()->constellationNames()) if (checkVisibility(o)) visibleObjects(c).insert(o); m_CategoryInitialized[c] = true; } else if (c == m_Categories[6]) //Asteroids { foreach (SkyObject *o, data->skyComposite()->asteroids()) if (checkVisibility(o) && o->name() != i18nc("Asteroid name (optional)", "Pluto") && o->mag() <= m_Mag) visibleObjects(c).insert(o); m_CategoryInitialized[c] = true; } else if (c == m_Categories[7]) //Comets { foreach (SkyObject *o, data->skyComposite()->comets()) if (checkVisibility(o) && o->mag() <= m_Mag) visibleObjects(c).insert(o); m_CategoryInitialized[c] = true; } else //all deep-sky objects, need to split clusters, nebulae and galaxies { foreach (DeepSkyObject *dso, data->skyComposite()->deepSkyObjects()) { SkyObject *o = (SkyObject *)dso; if (checkVisibility(o) && o->mag() <= m_Mag) { switch (o->type()) { case SkyObject::OPEN_CLUSTER: //fall through case SkyObject::GLOBULAR_CLUSTER: visibleObjects(m_Categories[4]).insert(o); //star clusters break; case SkyObject::GASEOUS_NEBULA: //fall through case SkyObject::PLANETARY_NEBULA: //fall through case SkyObject::SUPERNOVA_REMNANT: visibleObjects(m_Categories[2]).insert(o); //nebulae break; case SkyObject::GALAXY: visibleObjects(m_Categories[3]).insert(o); //galaxies break; } } } m_CategoryInitialized[m_Categories[2]] = true; m_CategoryInitialized[m_Categories[3]] = true; m_CategoryInitialized[m_Categories[4]] = true; } } //Now the category has been initialized, we can populate the list widget foreach (const SkyObject *o, visibleObjects(c)) //WUT->ObjectListWidget->addItem(o->name()); WUT->ObjectListWidget->addItem(o->longname()); setCursor(QCursor(Qt::ArrowCursor)); // highlight first item if (WUT->ObjectListWidget->count()) { WUT->ObjectListWidget->setCurrentRow(0); WUT->ObjectListWidget->setFocus(); } } bool WUTDialog::checkVisibility(const SkyObject *o) { bool visible(false); double minAlt = 6.0; //An object is considered 'visible' if it is above horizon during civil twilight. // reject objects that never rise if (o->checkCircumpolar(geo->lat()) == true && o->alt().Degrees() <= 0) return false; //Initial values for T1, T2 assume all night option of EveningMorningBox KStarsDateTime T1 = Evening; T1.setTime(sunSetToday); KStarsDateTime T2 = Tomorrow; T2.setTime(sunRiseTomorrow); //Check Evening/Morning only state: if (EveningFlag == 0) //Evening only { T2 = T0; //midnight } else if (EveningFlag == 1) //Morning only { T1 = T0; //midnight } for (KStarsDateTime test = T1; test < T2; test = test.addSecs(3600)) { //Need LST of the test time, expressed as a dms object. KStarsDateTime ut = geo->LTtoUT(test); dms LST = geo->GSTtoLST(ut.gst()); SkyPoint sp = o->recomputeCoords(ut, geo); //check altitude of object at this time. sp.EquatorialToHorizontal(&LST, geo->lat()); if (sp.alt().Degrees() > minAlt) { visible = true; break; } } return visible; } void WUTDialog::slotDisplayObject(const QString &name) { QTime tRise, tSet, tTransit; QString sRise, sTransit, sSet; sRise = "--:--"; sTransit = "--:--"; sSet = "--:--"; WUT->DetailButton->setEnabled(false); SkyObject *o = nullptr; if (name.isEmpty()) { //no object selected WUT->ObjectBox->setTitle(i18n("No Object Selected")); o = nullptr; } else { o = KStarsData::Instance()->objectNamed(name); if (!o) //should never get here { WUT->ObjectBox->setTitle(i18n("Object Not Found")); } } if (o) { WUT->ObjectBox->setTitle(o->name()); if (o->checkCircumpolar(geo->lat())) { if (o->alt().Degrees() > 0.0) { sRise = i18n("circumpolar"); sSet = i18n("circumpolar"); } else { sRise = i18n("does not rise"); sSet = i18n("does not rise"); } } else { tRise = o->riseSetTime(T0, geo, true); tSet = o->riseSetTime(T0, geo, false); // if ( tSet < tRise ) // tSet = o->riseSetTime( JDTomorrow, geo, false ); sRise.clear(); sRise.sprintf("%02d:%02d", tRise.hour(), tRise.minute()); sSet.clear(); sSet.sprintf("%02d:%02d", tSet.hour(), tSet.minute()); } tTransit = o->transitTime(T0, geo); // if ( tTransit < tRise ) // tTransit = o->transitTime( JDTomorrow, geo ); sTransit.clear(); sTransit.sprintf("%02d:%02d", tTransit.hour(), tTransit.minute()); WUT->DetailButton->setEnabled(true); } WUT->ObjectRiseLabel->setText(i18n("Rises at: %1", sRise)); WUT->ObjectTransitLabel->setText(i18n("Transits at: %1", sTransit)); WUT->ObjectSetLabel->setText(i18n("Sets at: %1", sSet)); } void WUTDialog::slotCenter() { KStars *kstars = KStars::Instance(); SkyObject *o = nullptr; // get selected item if (WUT->ObjectListWidget->currentItem() != nullptr) { o = kstars->data()->objectNamed(WUT->ObjectListWidget->currentItem()->text()); } if (o != nullptr) { kstars->map()->setFocusPoint(o); kstars->map()->setFocusObject(o); kstars->map()->setDestination(*kstars->map()->focusPoint()); } } void WUTDialog::slotDetails() { KStars *kstars = KStars::Instance(); SkyObject *o = nullptr; // get selected item if (WUT->ObjectListWidget->currentItem() != nullptr) { o = kstars->data()->objectNamed(WUT->ObjectListWidget->currentItem()->text()); } if (o != nullptr) { QPointer detail = new DetailDialog(o, kstars->data()->ut(), geo, kstars); detail->exec(); delete detail; } } void WUTDialog::slotObslist() { SkyObject *o = nullptr; // get selected item if (WUT->ObjectListWidget->currentItem() != nullptr) { o = KStarsData::Instance()->objectNamed(WUT->ObjectListWidget->currentItem()->text()); } if (o != nullptr) { KStarsData::Instance()->observingList()->slotAddObject(o, session); } } void WUTDialog::slotChangeDate() { // Set the time T0 to the evening of today. This will make life easier for the user, who most probably // wants to see what's up on the night of some date, rather than the night of the previous day T0.setTime(QTime(18, 0, 0)); // 6 PM QPointer td = new TimeDialog(T0, KStarsData::Instance()->geo(), this); if (td->exec() == QDialog::Accepted) { T0 = td->selectedDateTime(); // If the time is set to 12 midnight, set it to 00:00:01, so that we don't have date interpretation problems if (T0.time() == QTime(0, 0, 0)) T0.setTime(QTime(0, 0, 1)); //If the Time is earlier than 6:00 am, assume the user wants the night of the previous date if (T0.time().hour() < 6) T0 = T0.addDays(-1); //Now, set time T0 to midnight (of the following day) T0.setTime(QTime(0, 0, 0)); T0 = T0.addDays(1); UT0 = geo->LTtoUT(T0); //Set the Tomorrow date/time to Noon the following day Tomorrow = T0.addSecs(12 * 3600); TomorrowUT = geo->LTtoUT(Tomorrow); //Set the Evening date/time to 6:00 pm Evening = T0.addSecs(-6 * 3600); EveningUT = geo->LTtoUT(Evening); WUT->DateLabel->setText(i18n("The night of %1", QLocale().toString(Evening.date(), QLocale::LongFormat))); init(); slotLoadList(WUT->CategoryListWidget->currentItem()->text()); } delete td; } void WUTDialog::slotChangeLocation() { QPointer ld = new LocationDialog(this); if (ld->exec() == QDialog::Accepted) { GeoLocation *newGeo = ld->selectedCity(); if (newGeo) { geo = newGeo; UT0 = geo->LTtoUT(T0); TomorrowUT = geo->LTtoUT(Tomorrow); EveningUT = geo->LTtoUT(Evening); WUT->LocationLabel->setText(i18n("at %1", geo->fullName())); init(); slotLoadList(WUT->CategoryListWidget->currentItem()->text()); } } delete ld; } void WUTDialog::slotEveningMorning(int index) { if (EveningFlag != index) { EveningFlag = index; init(); slotLoadList(WUT->CategoryListWidget->currentItem()->text()); } } void WUTDialog::updateMag() { m_Mag = WUT->MagnitudeEdit->value(); init(); slotLoadList(WUT->CategoryListWidget->currentItem()->text()); } void WUTDialog::slotChangeMagnitude() { if (timer) { timer->stop(); } else { timer = new QTimer(this); timer->setSingleShot(true); connect(timer, SIGNAL(timeout()), this, SLOT(updateMag())); } timer->start(500); }