diff --git a/kstars/dialogs/detaildialog.cpp b/kstars/dialogs/detaildialog.cpp index 294e09b06..c7ac56cac 100644 --- a/kstars/dialogs/detaildialog.cpp +++ b/kstars/dialogs/detaildialog.cpp @@ -1,1243 +1,1243 @@ /*************************************************************************** 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kstars.h" #include "kstarsdata.h" #include "kstarsdatetime.h" #include "ksnumbers.h" #include "geolocation.h" #include "ksutils.h" #include "skymap.h" #include "skyobjects/skyobject.h" #include "skyobjects/starobject.h" #include "skyobjects/deepskyobject.h" #include "skyobjects/ksplanetbase.h" #include "skyobjects/ksmoon.h" #include "skyobjects/kscomet.h" #include "skyobjects/ksasteroid.h" #include "skyobjects/supernova.h" #include "skycomponents/catalogcomponent.h" #include "thumbnailpicker.h" #include "Options.h" #include "widgets/kshelplabel.h" #include "addlinkdialog.h" #include "observinglist.h" #include #ifdef HAVE_INDI #include #include "indi/indilistener.h" #include "indi/indistd.h" #include "indi/driverinfo.h" #endif #include "skycomponents/constellationboundarylines.h" #include "skycomponents/skymapcomposite.h" #include "kspaths.h" DetailDialog::DetailDialog(SkyObject *o, const KStarsDateTime &ut, GeoLocation *geo, QWidget *parent ) : KPageDialog( parent ), selectedObject(o), Data(0), DataComet(0), Pos(0), Links(0), Adv(0), Log(0) { #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 = 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(); } DetailDialog::~DetailDialog() { delete Thumbnail; } 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, str; 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 = s->sptype() + ' ' + i18n("star"); 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/variablility 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; Data->Names->setText(ps->longname()); //Type is "G5 star" for Sun if (ps->name() == "Sun") { objecttyp = i18n("G5 star"); } else if (ps->name() == "Moon") { objecttyp = ps->translatedName(); } else if (ps->name() == i18n("Pluto") || ps->name() == "Ceres" || ps->name() == "Eris") { // TODO: Check if Ceres / Eris have translations and i18n() them 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") { 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") { 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") { 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 } 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; 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; //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 = (KSAsteroid *)selectedObject; DataComet = new DataCometWidget( this ); // Show same specifics data as comets Data->IncludeData->layout()->addWidget( DataComet ); // Perihelion str.setNum( ast->getPerihelion() ); DataComet->Perihelion->setText( str + " AU" ); // Earth MOID if ( ast->getEarthMOID() == 0 ) str = ""; else str.setNum( ast->getEarthMOID() ).append( " AU" ); DataComet->EarthMOID->setText( str ); // Orbit ID DataComet->OrbitID->setText( ast->getOrbitID() ); // Orbit Class DataComet->OrbitClass->setText( ast->getOrbitClass() ); // NEO if ( ast->isNEO() ) DataComet->NEO->setText( "Yes" ); else DataComet->NEO->setText( "No" ); // Albedo if ( ast->getAlbedo() == 0.0 ) str = ""; else str.setNum( ast->getAlbedo() ); DataComet->Albedo->setText( str ); // Diameter if( ast->getDiameter() == 0.0 ) str = ""; else str.setNum( ast->getDiameter() ).append( " km" ); DataComet->Diameter->setText( str ); // Dimensions if ( ast->getDimensions().isEmpty() ) DataComet->Dimensions->setText( "" ); else DataComet->Dimensions->setText( ast->getDimensions() + " km" ); // Rotation period if ( ast->getRotationPeriod() == 0.0 ) str = ""; else str.setNum( ast->getRotationPeriod() ).append( " h" ); DataComet->Rotation->setText( str ); // Period if ( ast->getPeriod() == 0.0 ) str = ""; else str.setNum( ast->getPeriod() ).append( " y" ); DataComet->Period->setText( str ); break; } case SkyObject::COMET: { KSComet* com = (KSComet *)selectedObject; DataComet = new DataCometWidget( this ); Data->IncludeData->layout()->addWidget( DataComet ); // Perihelion str.setNum( com->getPerihelion() ); DataComet->Perihelion->setText( str + " AU" ); // Earth MOID if ( com->getEarthMOID() == 0 ) str = ""; else str.setNum( com->getEarthMOID() ).append( " AU" ); DataComet->EarthMOID->setText( str ); // Orbit ID DataComet->OrbitID->setText( com->getOrbitID() ); // Orbit Class DataComet->OrbitClass->setText( com->getOrbitClass() ); // NEO if ( com->isNEO() ) DataComet->NEO->setText( "Yes" ); else DataComet->NEO->setText( "No" ); // Albedo if ( com->getAlbedo() == 0.0 ) str = ""; else str.setNum( com->getAlbedo() ); DataComet->Albedo->setText( str ); // Diameter if( com->getDiameter() == 0.0 ) str = ""; else str.setNum( com->getDiameter() ).append( " km" ); DataComet->Diameter->setText( str ); // Dimensions if ( com->getDimensions().isEmpty() ) DataComet->Dimensions->setText( "" ); else DataComet->Dimensions->setText( com->getDimensions() + " km" ); // Rotation period if ( com->getRotationPeriod() == 0.0 ) str = ""; else str.setNum( com->getRotationPeriod() ).append( " h" ); DataComet->Rotation->setText( str ); // Period if ( com->getPeriod() == 0.0 ) str = ""; else str.setNum( com->getPeriod() ).append( " y" ); DataComet->Period->setText( str ); 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() ); Pos->Dec->setText( selectedObject->dec().toDMSString() ); 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 if( !selectedObject->isSolarSystem() ) { Pos->RA0->setText( selectedObject->ra0().toHMSString() ); Pos->Dec0->setText( selectedObject->dec0().toDMSString() ); } else { Pos->RA0->setText( "--" ); Pos->Dec0->setText( "--" ); } //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, SLOT( setCurrentLink(QListWidgetItem*) ) ); connect( Links->InfoTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), Links->ImageTitleList, SLOT( clearSelection() ) ); // vice versa connect( Links->ImageTitleList, SIGNAL(currentItemChanged(QListWidgetItem*, QListWidgetItem*)), this, SLOT( setCurrentLink(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( 0, 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(); emit 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." ); KMessageBox::sorry( 0, 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(); emit 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 ) return; Adv = new DatabaseWidget( this ); addPage( Adv, i18n( "Advanced" ) ); connect( Adv->ADVTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem*, int)), this, SLOT(viewADVData())); populateADVTree(); } void DetailDialog::createLogTab() { - //Don't create a a log tab for an unnamed star + //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 == NULL) 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 == NULL) 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, FileLine; QFile URLFile; QTemporaryFile TempFile; TempFile.setAutoRemove(false); TempFile.open(); TempFileName = TempFile.fileName(); if (m_CurrentLink == NULL) 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( 0, 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=NULL, *out_stream=NULL; 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 = NULL, *temp = NULL; // We populate the tree iterativley, 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(item->Name)); if (parent == NULL) Adv->ADVTree->addTopLevelItem(temp); parent = temp; break; // End of top level case 1: if (parent != NULL) parent = parent->parent(); break; // Leaf case 2: new QTreeWidgetItem(parent, QStringList(item->Name)); 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 == NULL) 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, 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( ' ' ) + ".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( ' ' ) + ".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(0, i18n("Error: 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/align/align.ui b/kstars/ekos/align/align.ui index 03f97d438..dda090d80 100644 --- a/kstars/ekos/align/align.ui +++ b/kstars/ekos/align/align.ui @@ -1,1520 +1,1520 @@ Align 0 0 566 489 3 3 3 3 3 3 3 Solver Control Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter 3 3 3 3 3 true Capture && Solve Load a FITS image and solve. Slew mount to image central coordinates. Load && Slew... false Stop Select which action to perform after the captured image is solved Select what action to take once a solution is found. Solver Action 3 3 3 3 3 Synchronize the telescope to the solution coordinates S&ync true gotoModeButtonGroup Synchronize the telescope to the solution coordinates and then slew to the target coordinates S&lew to Target gotoModeButtonGroup Just solve &Nothing gotoModeButtonGroup Telescope Coordinates Accuracy threshold in arcseconds between solution and target coordinates. Plate solver shall be repeatedly executed until the solution coordinates are below the accuracy threshold 1 300 10 30 DE: true After telescope completes slewing, wait until it settles for this many milliseconds before capturing the next image. Settle true After telescope completes slewing, wait until it settles for this many milliseconds before capturing the next image. 15000 100 RA: Accuracy threshold in arcseconds between solution and target coordinates. Plate solver shall be repeatedly executed until the solution coordinates are below the accuracy threshold Accuracy Solution Coordinates 3 3 3 3 3 Field of View size in arcminutes FOV: Field of View size in arcminutes true RA: true DE: true Image rotation angle, East of North Rot: Image rotation angle, East of North true Difference between telescope coordinates and solution coordinates in Right Ascension dRA: Difference between telescope coordinates and solution coordinates in Right Ascension font-weight: bold; Difference between telescope coordinates and solution coordinates in Declination dDE Difference between telescope coordinates and solution coordinates in Declination font-weight: bold; Solver Options 3 3 3 3 3 1 1 Camera binning 22 22 22 22 Show in FITS Viewer Exp: 0 0 22 22 22 22 Clear RA & DEC values and reset solver options 22 22 estimated DEC of target center to limit the solver's search area DE: Bin: CCD: estimated RA of target center to limit the solver's search area RA: Radius: 0 0 The solver's search radius in degrees round the estimated RA and DEC 30 22 22 22 22 Toggle Full Screen 0 0 22 22 22 22 Fill RA &amp; DEC values from telescope coordinates and update options 22 22 Exposure duration in seconds 0.100000000000000 60.000000000000000 0.500000000000000 3.000000000000000 Subtract dark frame. If no suitable dark frame is available, a dark frame shall be captured. Dark Use Off-Axis Guide Telescope for FOV calculations OAGT 3 Options: Additional options to be the solver 3 Solver: Use online astrometry.net solver to solve the image. You must have an internet connection and a valid API key. Online true solverTypeGroup Use offline astrometry.net solver. You must install all the necessary index files for your field of view. Offline solverTypeGroup Use astrometry solver on remote machine running INDI server. Remote solverTypeGroup Qt::Horizontal 40 20 0 0 320 240 true 0 150 Polar Alignment Helper 3 3 3 3 3 false <p>Polar Alignment Helper tool requires the following:</p><p>1. German Equatorial Mount</p><p>2. Wide FOV &gt; 1 degrees</p><p>For small FOVs, use the Legacy Polar Alignment Tool.</p> QFrame::NoFrame 6 3 3 3 3 3 <html><head/><body><p>This tool provides a simple method to polar align a German equatorial mount. <span style=" font-weight:600;">Unpark</span> your mount, <span style=" font-weight:600;">Point</span> toward the celestial pole, and <span style=" font-weight:600;">Engage</span> sidereal tracking.</p><p>Click <span style=" font-weight:600;">Start</span> to begin the process.</p></body></html> true 3 Qt::Horizontal 40 20 Start 3 3 3 3 3 <html><head/><body><p>An image shall now be captured and solved. Click <span style=" font-weight:600;">Capture</span> to start.</p></body></html> true 3 Qt::Horizontal 40 20 Capture 3 3 3 3 3 <html><head/><body><p>Select direction and range of mount rotation, then click <span style=" font-weight:600;">Rotate</span> to continue.</p></body></html> true 3 West true East 15 60 15 30 ° Qt::Horizontal 40 20 Rotate 3 3 3 3 3 Capture a second image to measure polar alignment error. true 3 Qt::Horizontal 40 20 Capture 3 3 3 3 3 Error Occurred true 3 Qt::Horizontal 40 20 Restart 3 - <html><head/><body><p>Angle between expected perfectly aligned mount center and and actual center</p></body></html> + <html><head/><body><p>Angle between expected perfectly aligned mount center and the actual center</p></body></html> Displacement Angle 0 Qt::Horizontal 40 20 3 0 0 <html><head/><body><p>Correction vector is plotted above. Select a bright star to reposition the correction vector. Click <span style=" font-weight:600;">Next</span> when done.</p></body></html> true Next 3 3 3 3 3 <html><head/><body><p>Adjust mount's Altitude and Azimuth knobs until the selected star is centered within the crosshair. Click <span style=" font-weight:600;">Refresh</span> to begin continuous capture. Click <span style=" font-weight:600;">Done</span> when star is centered.</p></body></html> true 3 Refresh Exposure duration in seconds during refresh phase 1 30 Qt::Horizontal 40 20 Done Restart Legacy Polar Alignment Tool Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop 3 Az Error: true true Measure Az Error false Correct Az Error Qt::Vertical 3 Alt Error: true true Measure Alt Error false Correct Alt Error Qt::Vertical QSizePolicy::Preferred 20 10 dmsBox QLineEdit
widgets/dmsbox.h
1
solveB loadSlewB stopB syncR slewR nothingR ScopeRAOut ScopeDecOut accuracySpin delaySpin SolverRAOut SolverDecOut dRAOut dDEOut FOVOut RotOut CCDCaptureCombo showFITSViewerB exposureIN alignDarkFrameCheck toggleFullScreenB binningCombo kcfg_solverOTA raBox decBox clearBoxesB radiusBox syncBoxesB solverOptions onlineSolverR offlineSolverR remoteSolverR PAHStartB measureAzB correctAzB measureAltB correctAltB azError altError
diff --git a/kstars/kstarslite/dialogs/detaildialoglite.cpp b/kstars/kstarslite/dialogs/detaildialoglite.cpp index 142d41396..1b5842ea5 100644 --- a/kstars/kstarslite/dialogs/detaildialoglite.cpp +++ b/kstars/kstarslite/dialogs/detaildialoglite.cpp @@ -1,815 +1,815 @@ /*************************************************************************** finddialoglite.cpp - K Desktop Planetarium ------------------- begin : Wed Jul 29 2016 copyright : (C) 2016 by Artem Fedoskin email : afedoskin3@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 "detaildialoglite.h" #include "skymaplite.h" #include "kstarslite/skyobjectlite.h" #include "ksutils.h" #include "kspaths.h" #include "skyobjects/ksplanetbase.h" #include "skyobjects/ksmoon.h" #include "skyobjects/kscomet.h" #include "skyobjects/ksasteroid.h" #include "skyobjects/supernova.h" #include "deepskyobject.h" #include "starobject.h" #include #include #include "Options.h" #include "skymapcomposite.h" #include "constellationboundarylines.h" DetailDialogLite::DetailDialogLite( ) { setProperty("isLinksOn", true); setProperty("isLogOn", true); } void DetailDialogLite::initialize() { connect(SkyMapLite::Instance(), SIGNAL(objectLiteChanged()), this, SLOT(createGeneralTab())); connect(SkyMapLite::Instance(), SIGNAL(objectLiteChanged()), this, SLOT(createPositionTab())); connect(SkyMapLite::Instance(), SIGNAL(objectLiteChanged()), this, SLOT(createLogTab())); connect(SkyMapLite::Instance(), SIGNAL(objectLiteChanged()), this, SLOT(createLinksTab())); } void DetailDialogLite::createGeneralTab() { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); // Stuff that should be visible only for specific types of objects setProperty("illumination", "");// Only shown for the moon setProperty("BVindex",""); // Only shown for stars setupThumbnail(); //Fill in the data fields //Contents depend on type of object QString objecttyp, str; switch (selectedObject->type()) { case SkyObject::STAR: { StarObject* s = (StarObject*) selectedObject; if (s->getHDIndex()) { setProperty("name",(QString("%1, HD %2").arg(s->longname()).arg(s->getHDIndex()))); } else { setProperty("name",s->longname()); } objecttyp = s->sptype() + ' ' + i18n("star"); setProperty("magnitude",i18nc("number in magnitudes", "%1 mag", QLocale().toString(s->mag(), 'f', 2))); //show to hundredth place if(s->getBVIndex() < 30.) { setProperty("BVindex",QString::number(s->getBVIndex() , 'f', 2)); } //distance if (s->distance() > 2000. || s->distance() < 0.) { // parallax < 0.5 mas setProperty("distance",(QString(i18nc("larger than 2000 parsecs", "> 2000 pc")))); } else if (s->distance() > 50.) { //show to nearest integer setProperty("distance",(i18nc("number in parsecs", "%1 pc", QLocale().toString( s->distance(), 'f', 0)))); } else if (s->distance() > 10.0) { //show to tenths place setProperty("distance",(i18nc("number in parsecs", "%1 pc", QLocale().toString( s->distance(), 'f', 1)))); } else { //show to hundredths place setProperty("distance",(i18nc("number in parsecs", "%1 pc", QLocale().toString(s->distance(), 'f', 2 )))); } //Note multiplicity/variablility in angular size label setProperty("angSize",QString()); if (s->isMultiple() && s->isVariable()) { QString multiple = QString(i18nc( "the star is a multiple star", "multiple" ) + ','); setProperty("angSize", QString(multiple + "\n" + (i18nc( "the star is a variable star", "variable" )))); } else if (s->isMultiple()) { setProperty("angSize",i18nc("the star is a multiple star", "multiple")); } else if (s->isVariable()) { setProperty("angSize",(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; setProperty("name",ps->longname()); //Type is "G5 star" for Sun if (ps->name() == "Sun") { objecttyp = i18n("G5 star"); } else if (ps->name() == "Moon") { objecttyp = ps->translatedName(); } else if (ps->name() == i18n("Pluto") || ps->name() == "Ceres" || ps->name() == "Eris") { // TODO: Check if Ceres / Eris have translations and i18n() them 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") { setProperty("illumination",(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{*/ setProperty("magnitude",(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") { setProperty("distance",(i18nc("distance in kilometers", "%1 km", QLocale().toString(ps->rearth()*AU_KM, 'f', 2)))); } else { setProperty("distance",(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") { setProperty("angSize",(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 } else { setProperty("angSize",i18nc("angular size in arcseconds","%1 arcsec", QLocale().toString( ps->angSize()*60.0 , 'f', 1))); } } else { setProperty("angSize", "--" ); } break; //end of planets/comets/asteroids case } case SkyObject::SUPERNOVA: { Supernova* sup=(Supernova*) selectedObject; objecttyp = i18n("Supernova"); setProperty("name",sup->name()); setProperty("magnitude",(i18nc("number in magnitudes", "%1 mag", QLocale().toString(sup->mag(), 'f', 2)))); setProperty("distance", "---"); break; } default: { //deep-sky objects DeepSkyObject* dso = (DeepSkyObject*) 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())); } setProperty("name",nameList.join(",")); objecttyp = dso->typeName(); if (dso->type() == SkyObject::RADIO_SOURCE) { //ta->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) { setProperty("magnitude", "--"); } else { setProperty("magnitude",i18nc("number in magnitudes", "%1 mag", QLocale().toString( dso->mag(), 'f', 1))); //show to tenths place } //No distances at this point... setProperty("distance", "--"); //Only show decimal place for small angular sizes if (dso->a() > 10.0) { setProperty("angSize", i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString(dso->a(), 'f', 0))); } else if (dso->a()) { setProperty("angSize",i18nc("angular size in arcminutes", "%1 arcmin", QLocale().toString( dso->a(), 'f', 1 ))); } else { setProperty("angSize", "--"); } break; } } //Reset advanced properties setProperty("perihilion",""); setProperty("orbitID",""); setProperty("NEO",""); setProperty("diameter",""); setProperty("rotation",""); setProperty("earthMOID",""); setProperty("orbitClass",""); setProperty("albedo",""); setProperty("dimensions",""); setProperty("period",""); // Add specifics data switch (selectedObject->type()) { case SkyObject::ASTEROID: { KSAsteroid* ast = (KSAsteroid *)selectedObject; // Perihelion str.setNum( ast->getPerihelion() ); setProperty("perihelion", QString(str + " AU") ); // Earth MOID if ( ast->getEarthMOID() == 0 ) str = ""; else str.setNum( ast->getEarthMOID() ).append( " AU" ); setProperty("earthMOID", str ); // Orbit ID setProperty("orbitID", ast->getOrbitID() ); // Orbit Class setProperty("orbitClass", ast->getOrbitClass() ); // NEO if ( ast->isNEO() ) setProperty("NEO", "Yes" ); else setProperty("NEO", "No" ); // Albedo if ( ast->getAlbedo() == 0.0 ) str = ""; else str.setNum( ast->getAlbedo() ); setProperty("albedo", str ); // Diameter if( ast->getDiameter() == 0.0 ) str = ""; else str.setNum( ast->getDiameter() ).append( " km" ); setProperty("diameter", str ); // Dimensions if ( ast->getDimensions().isEmpty() ) setProperty("dimensions", "" ); else setProperty("dimensions", QString(ast->getDimensions() + " km") ); // Rotation period if ( ast->getRotationPeriod() == 0.0 ) str = ""; else str.setNum( ast->getRotationPeriod() ).append( " h" ); setProperty("rotation", str ); // Period if ( ast->getPeriod() == 0.0 ) str = ""; else str.setNum( ast->getPeriod() ).append( " y" ); setProperty("period", str ); break; } case SkyObject::COMET: { KSComet* com = (KSComet *)selectedObject; // Perihelion str.setNum( com->getPerihelion() ); setProperty("perihelion", QString(str + " AU" )); // Earth MOID if ( com->getEarthMOID() == 0 ) str = ""; else str.setNum( com->getEarthMOID() ).append( " AU" ); setProperty("earthMOID", str ); // Orbit ID setProperty("orbitID", com->getOrbitID() ); // Orbit Class setProperty("orbitClass", com->getOrbitClass() ); // NEO if ( com->isNEO() ) setProperty("NEO", "Yes" ); else setProperty("NEO", "No" ); // Albedo if ( com->getAlbedo() == 0.0 ) str = ""; else str.setNum( com->getAlbedo() ); setProperty("albedo", str ); // Diameter if( com->getDiameter() == 0.0 ) str = ""; else str.setNum( com->getDiameter() ).append( " km" ); setProperty("diameter", str ); // Dimensions if ( com->getDimensions().isEmpty() ) setProperty("dimensions", "" ); else setProperty("dimensions", QString(com->getDimensions() + " km") ); // Rotation period if ( com->getRotationPeriod() == 0.0 ) str = ""; else str.setNum( com->getRotationPeriod() ).append( " h" ); setProperty("rotation",str); // Period if ( com->getPeriod() == 0.0 ) str = ""; else str.setNum( com->getPeriod() ).append( " y" ); setProperty("period",str); 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); } setProperty("typeInConstellation", cname); } void DetailDialogLite::createPositionTab() { KStarsData *data = KStarsData::Instance(); KStarsDateTime ut = data->ut(); GeoLocation *geo = data->geo(); SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); //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() ); qDebug() << (selectedObject->deprecess(data->updateNum(),2451545.0l)).ra0().toHMSString() << (selectedObject->deprecess(data->updateNum(),2451545.0l)).dec0().toDMSString() << endl; //qDebug() << selectedObject->ra().toHMSString() << selectedObject->dec().toDMSString() << endl; setProperty("RALabel", i18n( "RA (%1):", sEpoch ) ); setProperty("decLabel", i18n( "Dec (%1):", sEpoch ) ); setProperty("RA", selectedObject->ra().toHMSString() ); setProperty("dec", selectedObject->dec().toDMSString() ); selectedObject->EquatorialToHorizontal(data->lst(), data->geo()->lat()); setProperty("az", selectedObject->az().toDMSString() ); dms a; if( Options::useAltAz() ) a = selectedObject->alt(); else a = selectedObject->altRefracted(); setProperty("alt", a.toDMSString() ); // Display the RA0 and Dec0 for objects that are outside the solar system if( !selectedObject->isSolarSystem() ) { setProperty("RA0", selectedObject->ra0().toHMSString() ); setProperty("dec0", selectedObject->dec0().toDMSString() ); } else { setProperty("RA0", "--" ); setProperty("dec0", "--"); } //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 = '-'; } setProperty("HA", 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 ) setProperty("airmass", QLocale().toString( selectedObject->airmass() , 'f', 2 ) ); else setProperty("airmass", "--" ); //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() ) { setProperty("timeRise", QString().sprintf( "%02d:%02d", rt.hour(), rt.minute() )); setProperty("timeSet", QString().sprintf( "%02d:%02d", st.hour(), st.minute() )); setProperty("azRise", raz.toDMSString() ); setProperty("azSet", saz.toDMSString()); } else { if ( selectedObject->alt().Degrees() > 0.0 ) { setProperty("timeRise", i18n( "Circumpolar" ) ); setProperty("timeSet", i18n( "Circumpolar" )); } else { setProperty("timeRise", i18n( "Never rises" ) ); setProperty("timeSet", i18n( "Never rises" ) ); } setProperty("azRise", i18nc( "Not Applicable", "N/A" )); setProperty("azSet", i18nc( "Not Applicable", "N/A" )); } setProperty("timeTransit", QString().sprintf( "%02d:%02d", tt.hour(), tt.minute() ) ); setProperty("altTransit", talt.toDMSString() ); // Restore the position and other time-dependent parameters selectedObject->recomputeCoords( ut, geo ); } void DetailDialogLite::createLogTab() { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); - //Don't create a a log tab for an unnamed star + //Don't create a log tab for an unnamed star if ( selectedObject->name() == QString("star") ) { setProperty("isLogOn",false); return; } setProperty("isLogOn",true); if ( selectedObject->userLog().isEmpty() ) { setProperty("userLog",i18n("Record here observation logs and/or data on %1.", selectedObject->translatedName())); } else { setProperty("userLog",selectedObject->userLog()); } /*//Automatically save the log contents when the widget loses focus connect( Log->UserLog, SIGNAL( focusOut() ), this, SLOT( saveLogData() ) );*/ } void DetailDialogLite::createLinksTab() { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); //No links for unnamed stars if ( selectedObject->name() == QString("star") ) { setProperty("isLinksOn",false); return; } setProperty("isLinksOn",true); QStringList newInfoList; foreach ( const QString &s, selectedObject->InfoTitle() ) newInfoList.append( i18nc( "Image/info menu item (should be translated)", s.toLocal8Bit() ) ); setProperty("infoTitleList", newInfoList); QStringList newImageList; foreach ( const QString &s, selectedObject->ImageTitle() ) newImageList.append( i18nc( "Image/info menu item (should be translated)", s.toLocal8Bit() ) ); setProperty("imageTitleList", newImageList); } void DetailDialogLite::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=NULL, *out_stream=NULL; 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; } if ( !URLFile.open( QIODevice::ReadWrite) ) { qDebug() << "DetailDialog: Failed to open " << URLFile.fileName(); qDebug() << "KStars cannot save to user database"; return; } // Copy URL file to temp file TempFile.write(URLFile.readAll()); //Return pointers to initial positions TempFile.seek(0); //Clear URLFile URLFile.resize(0); // 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); } void DetailDialogLite::addLink(QString url, QString desc, bool isImageLink) { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); if(url.isEmpty() || desc.isEmpty()) return; //Do nothing if empty url or desc were provided QString entry; QFile file; if ( isImageLink ) { //Add link to object's ImageList, and descriptive text to its ImageTitle list selectedObject->ImageList().append( url ); selectedObject->ImageTitle().append( 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." ); qDebug() << message; return; } else { entry = selectedObject->name() + ':' + desc + ':' + url; QTextStream stream( &file ); stream << entry << endl; file.close(); setProperty("imageTitleList", selectedObject->ImageTitle()); } } else { selectedObject->InfoList().append( url ); selectedObject->InfoTitle().append( 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." ); qDebug() << message; return; } else { entry = selectedObject->name() + ':' + desc + ':' + url; QTextStream stream( &file ); stream << entry << endl; file.close(); setProperty("infoTitleList", selectedObject->InfoTitle()); } } } void DetailDialogLite::removeLink(int itemIndex, bool isImage) { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); QString currentItemURL, currentItemTitle, LineEntry, TempFileName, FileLine; QFile URLFile; QTemporaryFile TempFile; TempFile.setAutoRemove(false); TempFile.open(); TempFileName = TempFile.fileName(); //Check if it is a valid index if(itemIndex < 0) { return; } else if(isImage && itemIndex >= selectedObject->ImageTitle().length()) { return; } else if (!isImage && itemIndex >= selectedObject->InfoTitle().length()) { return; } //if (title.isEmpty() || url.isEmpty()) return; if ( !isImage ) { //Information currentItemTitle = selectedObject->InfoTitle()[itemIndex]; currentItemURL = selectedObject->InfoList()[itemIndex]; LineEntry = selectedObject->name(); LineEntry += ':'; LineEntry += currentItemTitle; LineEntry += ':'; LineEntry += currentItemURL; } else { //Image currentItemTitle = selectedObject->ImageTitle()[itemIndex]; currentItemURL = selectedObject->ImageList()[itemIndex]; LineEntry = selectedObject->name(); LineEntry += ':'; LineEntry += currentItemTitle; LineEntry += ':'; LineEntry += currentItemURL; } /*if (KMessageBox::warningContinueCancel( 0, i18n("Are you sure you want to remove the %1 link?", currentItemTitle), i18n("Delete Confirmation"),KStandardGuiItem::del())!=KMessageBox::Continue) return;*/ if (isImage) { selectedObject->ImageTitle().removeAt(itemIndex); selectedObject->ImageList().removeAt(itemIndex); } else { selectedObject->InfoTitle().removeAt(itemIndex); selectedObject->InfoList().removeAt(itemIndex); } // Remove link from file updateLocalDatabase(isImage ? 1 : 0, LineEntry); setProperty("infoTitleList", selectedObject->InfoTitle()); setProperty("imageTitleList", selectedObject->ImageTitle()); } void DetailDialogLite::editLink(int itemIndex, bool isImage, QString desc, QString url) { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); if(url.isEmpty() || desc.isEmpty()) return; //Do nothing if empty url or desc were provided QString search_line, replace_line, currentItemTitle, currentItemURL; //Check if it is a valid index if(itemIndex < 0) { return; } else if(isImage && itemIndex >= selectedObject->ImageTitle().length()) { return; } else if (!isImage && itemIndex >= selectedObject->InfoTitle().length()) { return; } if ( !isImage ) { //Information currentItemTitle = selectedObject->InfoTitle()[itemIndex]; currentItemURL = selectedObject->InfoList()[itemIndex]; search_line = selectedObject->name(); search_line += ':'; search_line += currentItemTitle; search_line += ':'; search_line += currentItemURL; } else { //Image currentItemTitle = selectedObject->ImageTitle()[itemIndex]; currentItemURL = selectedObject->ImageList()[itemIndex]; search_line = selectedObject->name(); search_line += ':'; search_line += currentItemTitle; search_line += ':'; search_line += currentItemURL; } bool go( true ); // If nothing changed, skip th action if (url == currentItemURL && desc == currentItemTitle) go = false; if ( go ) { replace_line = selectedObject->name() + ':' + desc + ':' + url; // Info Link if (!isImage) { selectedObject->InfoTitle().replace(itemIndex, desc); selectedObject->InfoList().replace(itemIndex, url); // Image Links } else { selectedObject->ImageTitle().replace(itemIndex, desc); selectedObject->ImageList().replace(itemIndex, url); } // Update local files updateLocalDatabase(isImage ? 1 : 0, search_line, replace_line); setProperty("infoTitleList", selectedObject->InfoTitle()); setProperty("imageTitleList", selectedObject->ImageTitle()); } } QString DetailDialogLite::getInfoURL(int index) { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); QStringList urls = selectedObject->InfoList(); if(index >= 0 && index < urls.size()) { return urls[index]; } else { return ""; } } QString DetailDialogLite::getImageURL(int index) { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); QStringList urls = selectedObject->ImageList(); if(index >= 0 && index < urls.size()) { return urls[index]; } else { return ""; } } void DetailDialogLite::setupThumbnail() { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); //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 );*/ setProperty("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( ' ' ) + ".png"; if ( KSUtils::openDataFile( file, fname ) ) { file.close(); setProperty("thumbnail", file.fileName()); } else { setProperty("thumbnail", ""); } } /*void DetailDialogLite::viewResource(int itemIndex, bool isImage) { QString url; if(isImage) { url = getImageURL(itemIndex); } else { url = getInfoURL(itemIndex); } QDesktopServices::openUrl(QUrl(url, QUrl::TolerantMode)); }*/ void DetailDialogLite::saveLogData(QString userLog) { SkyObject *selectedObject = SkyMapLite::Instance()->getClickedObjectLite()->getObject(); selectedObject->saveUserLog( userLog ); } diff --git a/kstars/skycomponents/deepstarcomponent.cpp b/kstars/skycomponents/deepstarcomponent.cpp index c7ba9c7c6..d716c956e 100644 --- a/kstars/skycomponents/deepstarcomponent.cpp +++ b/kstars/skycomponents/deepstarcomponent.cpp @@ -1,610 +1,610 @@ /*************************************************************************** deepstarcomponent.cpp - K Desktop Planetarium ------------------- begin : Fri 1st Aug 2008 copyright : (C) 2008 Akarsh Simha, Thomas Kabelmann email : akarshsimha@gmail.com, 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. * * * ***************************************************************************/ #ifdef _WIN32 #include #endif #include "deepstarcomponent.h" #include #include #include #include //NOTE Added this for QT_FSEEK, should we be including another file? #include #include "Options.h" #include "kstarsdata.h" #ifndef KSTARS_LITE #include "skymap.h" #endif #include "skyobjects/starobject.h" #include "skymesh.h" #include "binfilehelper.h" #include "starblockfactory.h" #include "starcomponent.h" #include "projections/projector.h" #include "skypainter.h" #include "byteorder.h" DeepStarComponent::DeepStarComponent( SkyComposite *parent, QString fileName, float trigMag, bool staticstars ) : ListComponent(parent), m_reindexNum( J2000 ), triggerMag( trigMag ), m_FaintMagnitude(-5.0), staticStars( staticstars ), dataFileName( fileName ) { fileOpened = false; openDataFile(); if( staticStars ) loadStaticStars(); qDebug() << "Loaded DSO catalog file: " << dataFileName; } bool DeepStarComponent::loadStaticStars() { FILE *dataFile; if( !staticStars ) return true; if( !fileOpened ) return false; dataFile = starReader.getFileHandle(); rewind( dataFile ); if( !starReader.readHeader() ) { qDebug() << "Error reading header of catalog file " << dataFileName << ": " << starReader.getErrorNumber() << ": " << starReader.getError() << endl; return false; } quint8 recordSize = starReader.guessRecordSize(); if( recordSize != 16 && recordSize != 32 ) { qDebug() << "Cannot understand catalog file " << dataFileName << endl; return false; } //KDE_fseek(dataFile, starReader.getDataOffset(), SEEK_SET); QT_FSEEK(dataFile, starReader.getDataOffset(), SEEK_SET); qint16 faintmag; quint8 htm_level; quint16 t_MSpT; fread( &faintmag, 2, 1, dataFile ); if( starReader.getByteSwap() ) faintmag = bswap_16( faintmag ); fread( &htm_level, 1, 1, dataFile ); fread( &t_MSpT, 2, 1, dataFile ); // Unused if( starReader.getByteSwap() ) faintmag = bswap_16( faintmag ); // TODO: Read the multiplying factor from the dataFile m_FaintMagnitude = faintmag / 100.0; if( htm_level != m_skyMesh->level() ) qDebug() << "WARNING: HTM Level in shallow star data file and HTM Level in m_skyMesh do not match. EXPECT TROUBLE" << endl; - // JM 2012-12-05: Breaking into into 2 loops instead of one previously with multiple IF checks for recordSize + // JM 2012-12-05: Breaking into 2 loops instead of one previously with multiple IF checks for recordSize // While the CPU branch prediction might not suffer any penalities since the branch prediction after a few times // should always gets it right. It's better to do it this way to avoid any chances since the compiler might not optimize it. if (recordSize == 32) { for(Trixel i = 0; i < (unsigned int)m_skyMesh->size(); ++i) { Trixel trixel = i; quint64 records = starReader.getRecordCount( i ); StarBlock *SB = new StarBlock( records ); if( !SB ) qDebug() << "ERROR: Could not allocate new StarBlock to hold shallow unnamed stars for trixel " << trixel << endl; m_starBlockList.at( trixel )->setStaticBlock( SB ); for(quint64 j = 0; j < records; ++j) { bool fread_success = false; fread_success = fread( &stardata, sizeof( starData ), 1, dataFile ); if( !fread_success ) { qDebug() << "ERROR: Could not read starData structure for star #" << j << " under trixel #" << trixel << endl; } /* Swap Bytes when required */ if( starReader.getByteSwap() ) byteSwap( &stardata ); /* Initialize star with data just read. */ StarObject* star; #ifdef KSTARS_LITE star = &(SB->addStar( stardata )->star); #else star = SB->addStar( stardata ); #endif if( star ) { //KStarsData* data = KStarsData::Instance(); //star->EquatorialToHorizontal( data->lst(), data->geo()->lat() ); //if( star->getHDIndex() != 0 ) if (stardata.HD) m_CatalogNumber.insert( stardata.HD, star ); } else { qDebug() << "CODE ERROR: More unnamed static stars in trixel " << trixel << " than we allocated space for!" << endl; } } } } else { for(Trixel i = 0; i < (unsigned int)m_skyMesh->size(); ++i) { Trixel trixel = i; quint64 records = starReader.getRecordCount( i ); StarBlock *SB = new StarBlock( records ); if( !SB ) qDebug() << "ERROR: Could not allocate new StarBlock to hold shallow unnamed stars for trixel " << trixel << endl; m_starBlockList.at( trixel )->setStaticBlock( SB ); for(quint64 j = 0; j < records; ++j) { bool fread_success = false; fread_success = fread( &deepstardata, sizeof( deepStarData ), 1, dataFile ); if( !fread_success ) { qDebug() << "ERROR: Could not read starData structure for star #" << j << " under trixel #" << trixel << endl; } /* Swap Bytes when required */ if( starReader.getByteSwap() ) byteSwap( &deepstardata ); /* Initialize star with data just read. */ StarObject* star; #ifdef KSTARS_LITE star = &(SB->addStar( stardata )->star); #else star = SB->addStar( deepstardata ); #endif if( star ) { //KStarsData* data = KStarsData::Instance(); //star->EquatorialToHorizontal( data->lst(), data->geo()->lat() ); //if( star->getHDIndex() != 0 ) if (stardata.HD) m_CatalogNumber.insert( stardata.HD, star ); } else { qDebug() << "CODE ERROR: More unnamed static stars in trixel " << trixel << " than we allocated space for!" << endl; } } } } return true; } DeepStarComponent::~DeepStarComponent() { if( fileOpened ) starReader.closeFile(); fileOpened = false; } bool DeepStarComponent::selected() { return Options::showStars() && fileOpened; } bool openIndexFile( ) { // TODO: Work out the details /* if( hdidxReader.openFile( "Henry-Draper.idx" ) ) qDebug() << "Could not open HD Index file. Search by HD numbers for deep stars will not work." << endl; */ return 0; } //This function is empty for a reason; we override the normal //update function in favor of JiT updates for stars. void DeepStarComponent::update( KSNumbers * ) {} // TODO: Optimize draw, if it is worth it. void DeepStarComponent::draw( SkyPainter *skyp ) { #ifndef KSTARS_LITE if ( !fileOpened ) return; #ifdef PROFILE_SINCOS long trig_calls_here = - dms::trig_function_calls; long trig_redundancy_here = - dms::redundant_trig_function_calls; long cachingdms_bad_uses = -CachingDms::cachingdms_bad_uses; dms::seconds_in_trig = 0.; #endif #ifdef PROFILE_UPDATECOORDS StarObject::updateCoordsCpuTime = 0.; StarObject::starsUpdated = 0; #endif SkyMap *map = SkyMap::Instance(); KStarsData* data = KStarsData::Instance(); UpdateID updateID = data->updateID(); //FIXME_FOV -- maybe not clamp like that... float radius = map->projector()->fov(); if ( radius > 90.0 ) radius = 90.0; if ( m_skyMesh != SkyMesh::Instance() && m_skyMesh->inDraw() ) { printf("Warning: aborting concurrent DeepStarComponent::draw()"); } bool checkSlewing = ( map->isSlewing() && Options::hideOnSlew() ); //shortcuts to inform whether to draw different objects bool hideFaintStars( checkSlewing && Options::hideStars() ); double hideStarsMag = Options::magLimitHideStar(); //adjust maglimit for ZoomLevel // double lgmin = log10(MINZOOM); // double lgmax = log10(MAXZOOM); // double lgz = log10(Options::zoomFactor()); // TODO: Enable hiding of faint stars float maglim = StarComponent::zoomMagnitudeLimit(); if( maglim < triggerMag ) return; m_zoomMagLimit = maglim; m_skyMesh->inDraw( true ); SkyPoint* focus = map->focus(); m_skyMesh->aperture( focus, radius + 1.0, DRAW_BUF ); // divide by 2 for testing MeshIterator region(m_skyMesh, DRAW_BUF); magLim = maglim; // If we are to hide the fainter stars (eg: while slewing), we set the magnitude limit to hideStarsMag. if( hideFaintStars && maglim > hideStarsMag ) maglim = hideStarsMag; StarBlockFactory *m_StarBlockFactory = StarBlockFactory::Instance(); // m_StarBlockFactory->drawID = m_skyMesh->drawID(); // qDebug() << "Mesh size = " << m_skyMesh->size() << "; drawID = " << m_skyMesh->drawID(); QTime t; int nTrixels = 0; t_dynamicLoad = 0; t_updateCache = 0; t_drawUnnamed = 0; visibleStarCount = 0; t.start(); // Mark used blocks in the LRU Cache. Not required for static stars if( !staticStars ) { while( region.hasNext() ) { Trixel currentRegion = region.next(); for( int i = 0; i < m_starBlockList.at( currentRegion )->getBlockCount(); ++i ) { StarBlock *prevBlock = ( ( i >= 1 ) ? m_starBlockList.at( currentRegion )->block( i - 1 ) : NULL ); StarBlock *block = m_starBlockList.at( currentRegion )->block( i ); if( i == 0 && !m_StarBlockFactory->markFirst( block ) ) qDebug() << "markFirst failed in trixel" << currentRegion; if( i > 0 && !m_StarBlockFactory->markNext( prevBlock, block ) ) qDebug() << "markNext failed in trixel" << currentRegion << "while marking block" << i; if( i < m_starBlockList.at( currentRegion )->getBlockCount() && m_starBlockList.at( currentRegion )->block( i )->getFaintMag() < maglim ) break; } } t_updateCache = t.restart(); region.reset(); } while ( region.hasNext() ) { ++nTrixels; Trixel currentRegion = region.next(); // NOTE: We are guessing that the last 1.5/16 magnitudes in the catalog are just additions and the star catalog // is actually supposed to reach out continuously enough only to mag m_FaintMagnitude * ( 1 - 1.5/16 ) // TODO: Is there a better way? We may have to change the magnitude tolerance if the catalog changes // Static stars need not execute fillToMag if( !staticStars && !m_starBlockList.at( currentRegion )->fillToMag( maglim ) && maglim <= m_FaintMagnitude * ( 1 - 1.5/16 ) ) { qDebug() << "SBL::fillToMag( " << maglim << " ) failed for trixel " << currentRegion << " !"<< endl; } t_dynamicLoad += t.restart(); // qDebug() << "Drawing SBL for trixel " << currentRegion << ", SBL has " // << m_starBlockList[ currentRegion ]->getBlockCount() << " blocks" << endl; // REMARK: The following should never carry state, except for const parameters like updateID and maglim std::function mapFunction = [&updateID, &maglim]( StarBlock *myBlock ) { for ( StarObject &star : myBlock->contents() ) { if ( star.updateID != updateID ) star.JITupdate(); if ( star.mag() > maglim ) break; } }; QtConcurrent::blockingMap( m_starBlockList.at( currentRegion )->contents(), mapFunction ); for( int i = 0; i < m_starBlockList.at( currentRegion )->getBlockCount(); ++i ) { StarBlock *block = m_starBlockList.at( currentRegion )->block( i ); // qDebug() << "---> Drawing stars from block " << i << " of trixel " << // currentRegion << ". SB has " << block->getStarCount() << " stars" << endl; for( int j = 0; j < block->getStarCount(); j++ ) { StarObject *curStar = block->star( j ); // qDebug() << "We claim that he's from trixel " << currentRegion //<< ", and indexStar says he's from " << m_skyMesh->indexStar( curStar ); float mag = curStar->mag(); if ( mag > maglim ) break; if( skyp->drawPointSource(curStar, mag, curStar->spchar() ) ) visibleStarCount++; } } // DEBUG: Uncomment to identify problems with Star Block Factory / preservation of Magnitude Order in the LRU Cache // verifySBLIntegrity(); t_drawUnnamed += t.restart(); } m_skyMesh->inDraw( false ); #ifdef PROFILE_SINCOS trig_calls_here += dms::trig_function_calls; trig_redundancy_here += dms::redundant_trig_function_calls; cachingdms_bad_uses += CachingDms::cachingdms_bad_uses; qDebug() << "Spent " << dms::seconds_in_trig << " seconds doing " << trig_calls_here << " trigonometric function calls amounting to an average of " << 1000.0 * dms::seconds_in_trig/double( trig_calls_here ) << " ms per call"; qDebug() << "Redundancy of trig calls in this draw: " << double( trig_redundancy_here ) / double( trig_calls_here ) * 100. << "%"; qDebug() << "CachedDms constructor calls so far: " << CachingDms::cachingdms_constructor_calls; qDebug() << "Caching has prevented " << CachingDms::cachingdms_delta << " redundant trig function calls"; qDebug() << "Bad cache uses in this draw: " << cachingdms_bad_uses; #endif #ifdef PROFILE_UPDATECOORDS qDebug() << "Spent " << StarObject::updateCoordsCpuTime << " seconds updating " << StarObject::starsUpdated << " stars' coordinates (StarObject::updateCoords) for an average of " << double( StarObject::updateCoordsCpuTime )/double( StarObject::starsUpdated ) * 1.e6 << " us per star."; #endif #else Q_UNUSED(skyp) #endif } bool DeepStarComponent::openDataFile() { if( starReader.getFileHandle() ) return true; starReader.openFile( dataFileName ); fileOpened = false; if( !starReader.getFileHandle() ) qDebug() << "WARNING: Failed to open deep star catalog " << dataFileName << ". Disabling it." << endl; else if( !starReader.readHeader() ) qDebug() << "WARNING: Header read error for deep star catalog " << dataFileName << "!! Disabling it!" << endl; else { qint16 faintmag; quint8 htm_level; fread( &faintmag, 2, 1, starReader.getFileHandle() ); if( starReader.getByteSwap() ) faintmag = bswap_16( faintmag ); if( starReader.guessRecordSize() == 16 ) m_FaintMagnitude = faintmag / 1000.0; else m_FaintMagnitude = faintmag / 100.0; fread( &htm_level, 1, 1, starReader.getFileHandle() ); qDebug() << "Processing " << dataFileName << ", HTMesh Level" << htm_level; m_skyMesh = SkyMesh::Instance( htm_level ); if( !m_skyMesh ) { if( !( m_skyMesh = SkyMesh::Create( htm_level ) ) ) { qDebug() << "Could not create HTMesh of level " << htm_level << " for catalog " << dataFileName << ". Skipping it."; return false; } } meshLevel = htm_level; fread( &MSpT, 2, 1, starReader.getFileHandle() ); if( starReader.getByteSwap() ) MSpT = bswap_16( MSpT ); fileOpened = true; qDebug() << " Sky Mesh Size: " << m_skyMesh->size(); for (long int i = 0; i < m_skyMesh->size(); i++) { StarBlockList *sbl = new StarBlockList( i, this ); if( !sbl ) { qDebug() << "NULL starBlockList. Expect trouble!"; } m_starBlockList.append( sbl ); } m_zoomMagLimit = 0.06; } return fileOpened; } StarObject *DeepStarComponent::findByHDIndex( int HDnum ) { // Currently, we only handle HD catalog indexes return m_CatalogNumber.value( HDnum, NULL ); // TODO: Maybe, make this more general. } // This uses the main star index for looking up nearby stars but then // filters out objects with the generic name "star". We could easily // build an index for just the named stars which would make this go // much faster still. -jbb // SkyObject* DeepStarComponent::objectNearest( SkyPoint *p, double &maxrad ) { StarObject *oBest = 0; #ifdef KSTARS_LITE m_zoomMagLimit = StarComponent::zoomMagnitudeLimit(); #endif if( !fileOpened ) return NULL; m_skyMesh->index( p, maxrad + 1.0, OBJ_NEAREST_BUF); MeshIterator region( m_skyMesh, OBJ_NEAREST_BUF ); while ( region.hasNext() ) { Trixel currentRegion = region.next(); for( int i = 0; i < m_starBlockList.at( currentRegion )->getBlockCount(); ++i ) { StarBlock *block = m_starBlockList.at( currentRegion )->block( i ); for( int j = 0; j < block->getStarCount(); ++j ) { #ifdef KSTARS_LITE StarObject* star = &(block->star( j )->star); #else StarObject* star = block->star( j ); #endif if( !star ) continue; if ( star->mag() > m_zoomMagLimit ) continue; double r = star->angularDistanceTo( p ).Degrees(); if ( r < maxrad ) { oBest = star; maxrad = r; } } } } // TODO: What if we are looking around a point that's not on // screen? objectNearest() will need to keep on filling up all // trixels around the SkyPoint to find the best match in case it // has not been found. Ideally, this should be implemented in a // different method and should be called after all other // candidates (eg: DeepSkyObject::objectNearest()) have been // called. return oBest; } bool DeepStarComponent::starsInAperture( QList &list, const SkyPoint ¢er, float radius, float maglim ) { if( maglim < triggerMag ) return false; // For DeepStarComponents, whether we use ra0() and dec0(), or // ra() and dec(), should not matter, because the stars are // repeated in all trixels that they will pass through, although // the factuality of this statement needs to be verified // Ensure that we have deprecessed the (RA, Dec) to (RA0, Dec0) Q_ASSERT( center.ra0().Degrees() >= 0.0 ); Q_ASSERT( center.dec0().Degrees() <= 90.0 ); m_skyMesh->intersect( center.ra0().Degrees(), center.dec0().Degrees(), radius, (BufNum) OBJ_NEAREST_BUF ); MeshIterator region( m_skyMesh, OBJ_NEAREST_BUF ); if( maglim < -28 ) maglim = m_FaintMagnitude; while ( region.hasNext() ) { Trixel currentRegion = region.next(); // FIXME: Build a better way to iterate over all stars. // Ideally, StarBlockList should have such a facility. StarBlockList *sbl = m_starBlockList[ currentRegion ]; sbl->fillToMag( maglim ); for( int i = 0; i < sbl->getBlockCount(); ++i ) { StarBlock *block = sbl->block( i ); for( int j = 0; j < block->getStarCount(); ++j ) { #ifdef KSTARS_LITE StarObject *star = &(block->star( j )->star); #else StarObject *star = block->star( j ); #endif if( star->mag() > maglim ) break; // Stars are organized by magnitude, so this should work if( star->angularDistanceTo( ¢er ).Degrees() <= radius ) list.append( star ); } } } return true; } void DeepStarComponent::byteSwap( deepStarData *stardata ) { stardata->RA = bswap_32( stardata->RA ); stardata->Dec = bswap_32( stardata->Dec ); stardata->dRA = bswap_16( stardata->dRA ); stardata->dDec = bswap_16( stardata->dDec ); stardata->B = bswap_16( stardata->B ); stardata->V = bswap_16( stardata->V ); } void DeepStarComponent::byteSwap( starData *stardata ) { stardata->RA = bswap_32( stardata->RA ); stardata->Dec = bswap_32( stardata->Dec ); stardata->dRA = bswap_32( stardata->dRA ); stardata->dDec = bswap_32( stardata->dDec ); stardata->parallax = bswap_32( stardata->parallax ); stardata->HD = bswap_32( stardata->HD ); stardata->mag = bswap_16( stardata->mag ); stardata->bv_index = bswap_16( stardata->bv_index ); } bool DeepStarComponent::verifySBLIntegrity() { float faintMag = -5.0; bool integrity = true; for(Trixel trixel = 0; trixel < (unsigned int)m_skyMesh->size(); ++trixel) { for(int i = 0; i < m_starBlockList[ trixel ]->getBlockCount(); ++i) { StarBlock *block = m_starBlockList[ trixel ]->block( i ); if( i == 0 ) faintMag = block->getBrightMag(); // NOTE: Assumes 2 decimal places in magnitude field. TODO: Change if it ever does change if( block->getBrightMag() != faintMag && ( block->getBrightMag() - faintMag ) > 0.5) { qDebug() << "Trixel " << trixel << ": ERROR: faintMag of prev block = " << faintMag << ", brightMag of block #" << i << " = " << block->getBrightMag(); integrity = false; } if( i > 1 && ( !block->prev ) ) qDebug() << "Trixel " << trixel << ": ERROR: Block" << i << "is unlinked in LRU Cache"; if( block->prev && block->prev->parent == m_starBlockList[ trixel ] && block->prev != m_starBlockList[ trixel ]->block( i - 1 ) ) { qDebug() << "Trixel " << trixel << ": ERROR: SBF LRU Cache linked list seems to be broken at before block " << i << endl; integrity = false; } faintMag = block->getFaintMag(); } } return integrity; } diff --git a/kstars/skymapqdraw.cpp b/kstars/skymapqdraw.cpp index 496029d45..f6547985a 100644 --- a/kstars/skymapqdraw.cpp +++ b/kstars/skymapqdraw.cpp @@ -1,107 +1,107 @@ /*************************************************************************** skymapqdraw.cpp - K Desktop Planetarium ------------------- begin : Tue Dec 21 2010 08:36 AM UTC-6 copyright : (C) 2010 Akarsh Simha email : akarsh.simha@kdemail.net ***************************************************************************/ /*************************************************************************** * * * 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 "skymapqdraw.h" #include "skymapcomposite.h" #include "skyqpainter.h" #include "skymap.h" #include "projections/projector.h" #include "printing/legend.h" SkyMapQDraw::SkyMapQDraw( SkyMap *sm ) : QWidget( sm ), SkyMapDrawAbstract( sm ) { m_SkyPixmap = new QPixmap( width(), height() ); } SkyMapQDraw::~SkyMapQDraw() { delete m_SkyPixmap; } void SkyMapQDraw::paintEvent( QPaintEvent *event ) { Q_UNUSED(event); // This is machinery to prevent multiple concurrent paint events / recursive paint events if( m_DrawLock ) { qDebug() << "I just prevented a recursive / concurrent draw!"; return; } setDrawLock( true ); // JM 2016-05-03: Not needed since we're not using OpenGL for now //calculateFPS(); //If computeSkymap is false, then we just refresh the window using the stored sky pixmap //and draw the "overlays" on top. This lets us update the overlay information rapidly //without needing to recompute the entire skymap. //use update() to trigger this "short" paint event; to force a full "recompute" //of the skymap, use forceUpdate(). if (!m_SkyMap->computeSkymap) { QPainter p; p.begin( this ); p.drawLine(0,0,1,1); // Dummy operation to circumvent bug. TODO: Add details p.drawPixmap( 0, 0, *m_SkyPixmap ); drawOverlays(p); p.end(); setDrawLock( false ); return ; // exit because the pixmap is repainted and that's all what we want } - // FIXME: used to to notify infobox about possible change of object coordinates + // FIXME: used to notify infobox about possible change of object coordinates // Not elegant at all. Should find better option m_SkyMap->showFocusCoords(); m_SkyMap->setupProjector(); SkyQPainter psky(this, m_SkyPixmap); //FIXME: we may want to move this into the components. psky.begin(); //Draw all sky elements psky.drawSkyBackground(); // Set Clipping QPainterPath path; path.addPolygon(m_SkyMap->projector()->clipPoly()); psky.setClipPath(path); psky.setClipping(true); m_KStarsData->skyComposite()->draw( &psky ); //Finish up psky.end(); QPainter psky2; psky2.begin( this ); psky2.drawLine(0,0,1,1); // Dummy op. psky2.drawPixmap( 0, 0, *m_SkyPixmap ); drawOverlays(psky2); psky2.end(); if(m_SkyMap->m_previewLegend) { m_SkyMap->m_legend.paintLegend(m_SkyPixmap); } m_SkyMap->computeSkymap = false; // use forceUpdate() to compute new skymap else old pixmap will be shown setDrawLock( false ); } void SkyMapQDraw::resizeEvent( QResizeEvent *e ) { Q_UNUSED(e); delete m_SkyPixmap; m_SkyPixmap = new QPixmap( width(), height() ); } diff --git a/kstars/skyobjects/skypoint.h b/kstars/skyobjects/skypoint.h index 4a8331885..1b843d87b 100644 --- a/kstars/skyobjects/skypoint.h +++ b/kstars/skyobjects/skypoint.h @@ -1,599 +1,599 @@ /*************************************************************************** skypoint.h - 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. * * * ***************************************************************************/ #ifndef SKYPOINT_H_ #define SKYPOINT_H_ #include #include "cachingdms.h" #include "kstarsdatetime.h" //#define PROFILE_COORDINATE_CONVERSION class KSNumbers; class KSSun; class GeoLocation; /** @class SkyPoint * *The sky coordinates of a point in the sky. The *coordinates are stored in both Equatorial (Right Ascension, *Declination) and Horizontal (Azimuth, Altitude) coordinate systems. *Provides set/get functions for each coordinate angle, and functions *to convert between the Equatorial and Horizon coordinate systems. * *Because the coordinate values change slowly over time (due to *precession, nutation), the "catalog coordinates" are stored *(RA0, Dec0), which were the true coordinates on Jan 1, 2000. *The true coordinates (RA, Dec) at any other epoch can be found *from the catalog coordinates using updateCoords(). *@short Stores dms coordinates for a point in the sky. *for converting between coordinate systems. *@author Jason Harris *@version 1.0 */ class SkyPoint { public: /** Default constructor: Sets RA, Dec and RA0, Dec0 according *to arguments. Does not set Altitude or Azimuth. *@param r Right Ascension *@param d Declination */ SkyPoint( const dms& r, const dms& d ) : RA0(r) , Dec0(d) , RA(r) , Dec(d) , lastPrecessJD( J2000 ) {} SkyPoint( const CachingDms& r, const CachingDms& d ) : RA0(r) , Dec0(d) , RA(r) , Dec(d) , lastPrecessJD( J2000 ) {} /** Alternate constructor using double arguments, for convenience. *It behaves essentially like the default constructor. *@param r Right Ascension, expressed as a double *@param d Declination, expressed as a double *@note This also sets RA0 and Dec0 */ //FIXME: this (*15.0) thing is somewhat hacky. explicit SkyPoint( double r, double d ) : RA0(r*15.0) , Dec0(d) , RA(r*15.0) , Dec(d) , lastPrecessJD( J2000 ) {} /** *@short Default constructor. Sets nonsense values for RA, Dec etc */ SkyPoint(); /** Empty destructor. */ virtual ~SkyPoint(); //// //// 1. Setting Coordinates //// ======================= /** * @short Sets RA, Dec and RA0, Dec0 according to arguments. * Does not set Altitude or Azimuth. * @param r Right Ascension * @param d Declination * @note This function also sets RA0 and Dec0 to the same values, so call at your own peril! * @note FIXME: This method must be removed, or an epoch argument must be added. */ void set( const dms& r, const dms& d ); /** Sets RA0, the catalog Right Ascension. *@param r catalog Right Ascension. */ inline void setRA0( dms r ) { RA0 = r; } inline void setRA0( CachingDms r ) { RA0 = r; } /** Overloaded member function, provided for convenience. *It behaves essentially like the above function. *@param r Right Ascension, expressed as a double. */ inline void setRA0( double r ) { RA0.setH( r ); } /** Sets Dec0, the catalog Declination. *@param d catalog Declination. */ inline void setDec0( dms d ) { Dec0 = d; } inline void setDec0( const CachingDms &d ) { Dec0 = d; } /** Overloaded member function, provided for convenience. *It behaves essentially like the above function. *@param d Declination, expressed as a double. */ inline void setDec0( double d ) { Dec0.setD( d ); } /** Sets RA, the current Right Ascension. *@param r Right Ascension. */ inline void setRA( dms r ) { RA = r; } inline void setRA( const CachingDms &r ) { RA = r; } /** Overloaded member function, provided for convenience. *It behaves essentially like the above function. *@param r Right Ascension, expressed as a double. */ inline void setRA( double r ) { RA.setH( r ); } /** Sets Dec, the current Declination *@param d Declination. */ inline void setDec( dms d ) { Dec = d; } inline void setDec( const CachingDms &d ) { Dec = d; } /** Overloaded member function, provided for convenience. *It behaves essentially like the above function. *@param d Declination, expressed as a double. */ inline void setDec( double d ) { Dec.setD( d ); } /** Sets Alt, the Altitude. *@param alt Altitude. */ inline void setAlt( dms alt ) { Alt = alt; } /** Overloaded member function, provided for convenience. *It behaves essentially like the above function. *@param alt Altitude, expressed as a double. */ inline void setAlt( double alt ) { Alt.setD( alt ); } /** Sets Az, the Azimuth. *@param az Azimuth. */ inline void setAz( dms az ) { Az = az; } /** Overloaded member function, provided for convenience. *It behaves essentially like the above function. *@param az Azimuth, expressed as a double. */ inline void setAz( double az ) { Az.setD( az ); } //// //// 2. Returning coordinates. //// ========================= /** @return a pointer to the catalog Right Ascension. */ inline const CachingDms& ra0() const { return RA0; } /** @return a pointer to the catalog Declination. */ inline const CachingDms& dec0() const { return Dec0; } /** @returns a pointer to the current Right Ascension. */ inline const CachingDms& ra() const { return RA; } /** @return a pointer to the current Declination. */ inline const CachingDms& dec() const { return Dec; } /** @return a pointer to the current Azimuth. */ inline const dms& az() const { return Az; } /** @return a pointer to the current Altitude. */ inline const dms& alt() const { return Alt; } /** @return refracted altitude. This function uses * Options::useRefraction to determine whether refraction * correction should be applied */ dms altRefracted() const; /** * @return the JD for the precessed coordinates */ inline double getLastPrecessJD() const { return lastPrecessJD; } /** * @return the airmass of the point. Convenience method. * @note Question: is it better to use alt or refracted alt? Minor difference, probably doesn't matter. */ inline double airmass() const { return 1./sin( alt().radians() ); } //// //// 3. Coordinate conversions. //// ========================== /** Determine the (Altitude, Azimuth) coordinates of the *SkyPoint from its (RA, Dec) coordinates, given the local *sidereal time and the observer's latitude. *@param LST pointer to the local sidereal time *@param lat pointer to the geographic latitude */ void EquatorialToHorizontal( const CachingDms* LST, const CachingDms* lat ); // Deprecated method provided for compatibility void EquatorialToHorizontal( const dms* LST, const dms* lat ); /** Determine the (RA, Dec) coordinates of the *SkyPoint from its (Altitude, Azimuth) coordinates, given the local *sidereal time and the observer's latitude. *@param LST pointer to the local sidereal time *@param lat pointer to the geographic latitude */ void HorizontalToEquatorial( const dms* LST, const dms* lat ); /** Determine the Ecliptic coordinates of the SkyPoint, given the Julian Date. *The ecliptic coordinates are returned as reference arguments (since *they are not stored internally) */ void findEcliptic( const CachingDms *Obliquity, dms &EcLong, dms &EcLat ); /** Set the current (RA, Dec) coordinates of the *SkyPoint, given pointers to its Ecliptic (Long, Lat) coordinates, and *to the current obliquity angle (the angle between the equator and ecliptic). */ void setFromEcliptic( const CachingDms *Obliquity, const dms& EcLong, const dms& EcLat ); /** Computes galactic coordinates from equatorial coordinates referred to * epoch 1950. RA and Dec are, therefore assumed to be B1950 * coordinates. */ void Equatorial1950ToGalactic(dms &galLong, dms &galLat); /** Computes equatorial coordinates referred to 1950 from galactic ones referred to * epoch B1950. RA and Dec are, therefore assumed to be B1950 * coordinates. */ void GalacticToEquatorial1950(const dms* galLong, const dms* galLat); //// //// 4. Coordinate update/corrections. //// ================================= /** Determine the current coordinates (RA, Dec) from the catalog *coordinates (RA0, Dec0), accounting for both precession and nutation. *@param num pointer to KSNumbers object containing current values of time-dependent variables. *@param includePlanets does nothing in this implementation (see KSPlanetBase::updateCoords()). *@param lat does nothing in this implementation (see KSPlanetBase::updateCoords()). *@param LST does nothing in this implementation (see KSPlanetBase::updateCoords()). *@param forceRecompute reapplies precession, nutation and aberration even if the time passed since the last computation is not significant. */ virtual void updateCoords( const KSNumbers *num, bool includePlanets=true, const CachingDms *lat=0, const CachingDms *LST=0, bool forceRecompute = false ); /** * @brief updateCoordsNow Shortcut for updateCoords( const KSNumbers *num, false, NULL, NULL, true) * @param num pointer to KSNumbers object containing current values of time-dependent variables. */ void updateCoordsNow(const KSNumbers *num) { updateCoords(num, false, NULL, NULL, true); } /** Computes the apparent coordinates for this SkyPoint for any epoch, *accounting for the effects of precession, nutation, and aberration. *Similar to updateCoords(), but the starting epoch need not be *J2000, and the target epoch need not be the present time. *@param jd0 Julian Day which identifies the original epoch *@param jdf Julian Day which identifies the final epoch */ void apparentCoord(long double jd0, long double jdf); /** Determine the effects of nutation for this SkyPoint. *@param num pointer to KSNumbers object containing current values of *time-dependent variables. */ void nutate(const KSNumbers *num); /** *@short Check if this sky point is close enough to the sun for * gravitational lensing to be significant */ bool checkBendLight(); /** Correct for the effect of "bending" of light around the sun for * positions near the sun. * * General Relativity tells us that a photon with an impact * parameter b is deflected through an angle 1.75" (Rs / b) where * Rs is the solar radius. * * @return: true if the light was bent, false otherwise */ bool bendlight(); /** *@short Obtain a Skypoint with RA0 and Dec0 set from the RA, Dec * of this skypoint. Also set the RA0, Dec0 of this SkyPoint if not * set already and the target epoch is J2000. */ SkyPoint deprecess( const KSNumbers *num, long double epoch=J2000 ); /** Determine the effects of aberration for this SkyPoint. *@param num pointer to KSNumbers object containing current values of *time-dependent variables. */ void aberrate(const KSNumbers *num); /** General case of precession. It precess from an original epoch to a *final epoch. In this case RA0, and Dec0 from SkyPoint object represent *the coordinates for the original epoch and not for J2000, as usual. *@param jd0 Julian Day which identifies the original epoch *@param jdf Julian Day which identifies the final epoch */ void precessFromAnyEpoch(long double jd0, long double jdf); /** Determine the E-terms of aberration *In the past, the mean places of stars published in catalogs included *the contribution to the aberration due to the ellipticity of the orbit *of the Earth. These terms, known as E-terms were almost constant, and *in the newer catalogs (FK5) are not included. Therefore to convert from *FK4 to FK5 one has to compute these E-terms. */ SkyPoint Eterms(void); /** Exact precession from Besselian epoch 1950 to epoch J2000. The *coordinates referred to the first epoch are in the FK4 catalog, while the latter are in the Fk5 one. *Reference: Smith, C. A.; Kaplan, G. H.; Hughes, J. A.; Seidelmann, *P. K.; Yallop, B. D.; Hohenkerk, C. Y. *Astronomical Journal, vol. 97, Jan. 1989, p. 265-279 *This transformation requires 4 steps: * - Correct E-terms * - Precess from B1950 to 1984, January 1st, 0h, using Newcomb expressions * - Add zero point correction in right ascension for 1984 * - Precess from 1984, January 1st, 0h to J2000 */ void B1950ToJ2000(void); /** Exact precession from epoch J2000 Besselian epoch 1950. The coordinates *referred to the first epoch are in the FK4 catalog, while the *latter are in the Fk5 one. *Reference: Smith, C. A.; Kaplan, G. H.; Hughes, J. A.; Seidelmann, *P. K.; Yallop, B. D.; Hohenkerk, C. Y. *Astronomical Journal, vol. 97, Jan. 1989, p. 265-279 *This transformation requires 4 steps: * - Precess from J2000 to 1984, January 1st, 0h * - Add zero point correction in right ascension for 1984 * - Precess from 1984, January 1st, 0h, to B1950 using Newcomb expressions * - Correct E-terms */ void J2000ToB1950(void); /** Coordinates in the FK4 catalog include the effect of aberration due *to the ellipticity of the orbit of the Earth. Coordinates in the FK5 *catalog do not include these terms. In order to convert from B1950 (FK4) *to actual mean places one has to use this function. */ void addEterms(void); /** Coordinates in the FK4 catalog include the effect of aberration due *to the ellipticity of the orbit of the Earth. Coordinates in the FK5 *catalog do not include these terms. In order to convert from * FK5 coordinates to B1950 (FK4) one has to use this function. */ void subtractEterms(void); /** Computes the angular distance between two SkyObjects. The algorithm * to compute this distance is: * cos(distance) = sin(d1)*sin(d2) + cos(d1)*cos(d2)*cos(a1-a2) * where a1,d1 are the coordinates of the first object and a2,d2 are * the coordinates of the second object. * However this algorithm is not accurate when the angular separation * is small. * Meeus provides a different algorithm in page 111 which we * implement here. * @param sp SkyPoint to which distance is to be calculated * @param positionAngle if a non-null pointer is passed, the position angle [E of N] in degrees from this SkyPoint to sp is computed and stored in the passed variable. * @return dms angle representing angular separation. **/ dms angularDistanceTo(const SkyPoint *sp, double * const positionAngle = 0) const; /** * @return returns true if _current_ epoch RA / Dec match */ inline bool operator == ( SkyPoint &p ) const { return ( ra() == p.ra() && dec() == p.dec() ); } /** Computes the velocity of the Sun projected on the direction of the source. * * @param jd Epoch expressed as julian day to which the source coordinates refer to. * @return Radial velocity of the source referred to the barycenter of the solar system in km/s **/ double vRSun(long double jd); /** Computes the radial velocity of a source referred to the solar system barycenter * from the radial velocity referred to the * Local Standard of Rest, aka known as VLSR. To compute it we need the coordinates of the * source the VLSR and the epoch for the source coordinates. * * @param vlsr radial velocity of the source referred to the LSR in km/s * @param jd Epoch expressed as julian day to which the source coordinates refer to. * @return Radial velocity of the source referred to the barycenter of the solar system in km/s **/ double vHeliocentric(double vlsr, long double jd); /** Computes the radial velocity of a source referred to the Local Standard of Rest, also known as VLSR * from the radial velocity referred to the solar system barycenter * * @param vhelio radial velocity of the source referred to the LSR in km/s * @param jd Epoch expressed as julian day to which the source coordinates refer to. * @return Radial velocity of the source referred to the barycenter of the solar system in km/s **/ double vHelioToVlsr(double vhelio, long double jd); /** Computes the velocity of any object projected on the direction of the source. * @param jd0 Julian day for which we compute the direction of the source * @return velocity of the Earth projected on the direction of the source kms-1 */ double vREarth(long double jd0); /** Computes the radial velocity of a source referred to the center of the earth * from the radial velocity referred to the solar system barycenter * * @param vhelio radial velocity of the source referred to the barycenter of the * solar system in km/s * @param jd Epoch expressed as julian day to which the source coordinates refer to. * @return Radial velocity of the source referred to the center of the Earth in km/s **/ double vGeocentric(double vhelio, long double jd); /** Computes the radial velocity of a source referred to the solar system barycenter * from the velocity referred to the center of the earth * * @param vgeo radial velocity of the source referred to the center of the Earth * [km/s] * @param jd Epoch expressed as julian day to which the source coordinates refer to. * @return Radial velocity of the source referred to the solar system barycenter in km/s **/ double vGeoToVHelio(double vgeo, long double jd); /** Computes the velocity of any object (oberver's site) projected on the * direction of the source. * @param vsite velocity of that object in cartesian coordinates * @return velocity of the object projected on the direction of the source kms-1 */ double vRSite(double vsite[3]); /** Computes the radial velocity of a source referred to the observer site on the surface * of the earth from the geocentric velovity and the velocity of the site referred to the center * of the Earth. * * @param vgeo radial velocity of the source referred to the center of the earth in km/s * @param vsite Velocity at which the observer moves referred to the center of the earth. * @return Radial velocity of the source referred to the observer's site in km/s **/ double vTopocentric(double vgeo, double vsite[3]); /** Computes the radial velocity of a source referred to the center of the Earth from * the radial velocity referred to an observer site on the surface of the earth * * @param vtopo radial velocity of the source referred to the observer's site in km/s * @param vsite Velocity at which the observer moves referred to the center of the earth. * @return Radial velocity of the source referred the center of the earth in km/s **/ double vTopoToVGeo(double vtopo, double vsite[3]); /** Find the SkyPoint obtained by moving distance dist * (arcseconds) away from the givenSkyPoint * * @param dist Distance to move through in arcseconds * @param from The SkyPoint to move away from - * @return a SkyPoint that is at the dist away from this SkyPoint in the direction away from from + * @return a SkyPoint that is at the dist away from this SkyPoint in the direction away from */ SkyPoint moveAway( const SkyPoint &from, double dist ) const; /** * @short Check if this point is circumpolar at the given geographic latitude */ bool checkCircumpolar( const dms *gLat ) const; /** Calculate refraction correction. Parameter and return value are in degrees */ static double refractionCorr( double alt ); /** * @short Apply refraction correction to altitude. * @param alt altitude to be corrected, in degrees * @return altitude after refraction correction, in degrees */ static double refract(const double alt); /** * @short Remove refraction correction. * @param alt altitude from which refraction correction must be removed, in degrees * @return altitude without refraction correction, in degrees */ static double unrefract(const double alt); /** * @short Apply refraction correction to altitude. Overloaded method using * dms provided for convenience * @see SkyPoint::refract( const double alt ) */ static inline dms refract(const dms alt) { return dms( refract( alt.Degrees() ) ); } /** * @short Remove refraction correction. Overloaded method using * dms provided for convenience * @see SkyPoint::unrefract( const double alt ) */ static inline dms unrefract(const dms alt) { return dms( unrefract( alt.Degrees() ) ); } /** * @short Compute the altitude of a given skypoint hour hours from the given date/time * @param p SkyPoint whose altitude is to be computed (const pointer, the method works on a clone) * @param dt Date/time that corresponds to 0 hour * @param geo GeoLocation object specifying the location * @param hour double specifying offset in hours from dt for which altitude is to be found * @return a dms containing (unrefracted?) altitude of the object at dt + hour hours at the given location * @note This method is used in multiple places across KStars * @todo Fix code duplication in AltVsTime and KSAlmanac by using this method instead! FIXME. */ static dms findAltitude( const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour = 0 ); /** * @short returns a time-transformed SkyPoint. See SkyPoint::findAltitude() for details * @todo Fix this documentation. */ static SkyPoint timeTransformed( const SkyPoint *p, const KStarsDateTime &dt, const GeoLocation *geo, const double hour = 0 ); /** *@short Critical height for atmospheric refraction * corrections. Below this, the height formula produces meaningles * results and the correction value is just interpolated. */ static const double altCrit; /** * @short Return the object's altitude at the upper culmination for the given latitude * @return the maximum altitude in degrees */ double maxAlt( const dms &lat ) const; /** * @short Return the object's altitude at the lower culmination for the given latitude * @return the minimum altitude in degrees */ double minAlt( const dms &lat ) const; #ifdef PROFILE_COORDINATE_CONVERSION static double cpuTime_EqToHz; static long unsigned eqToHzCalls; #endif protected: /** * Precess this SkyPoint's catalog coordinates to the epoch described by the * given KSNumbers object. * @param num pointer to a KSNumbers object describing the target epoch. */ void precess(const KSNumbers *num); #ifdef UNIT_TEST friend class TestSkyPoint; // Test class #endif private: CachingDms RA0, Dec0; //catalog coordinates CachingDms RA, Dec; //current true sky coordinates dms Alt, Az; static KSSun *m_Sun; protected: double lastPrecessJD; // JD at which the last coordinate update (see updateCoords) for this SkyPoint was done }; #endif