diff --git a/src/gui/AstroCalcDialog.cpp b/src/gui/AstroCalcDialog.cpp
index 91aaa013898f9..86d2e866784f5 100644
--- a/src/gui/AstroCalcDialog.cpp
+++ b/src/gui/AstroCalcDialog.cpp
@@ -4084,50 +4084,182 @@ BesselParameters::BesselParameters(double &xdot, double &ydot, double &ddot, dou
cdot = xdot+mudot*y*std::sin(d)+mudot*L*tf*std::cos(d);
}
-void AstroCalcDialog::saveSolarEclipseKML()
+void AstroCalcDialog::generateKML(const EclipseMapData& data, const QString& dateString, QTextStream& stream) const
{
- // Make sure that we have circumstances of an eclipse in the table
- if (ui->solareclipsecontactsTreeWidget->topLevelItemCount() == 0)
- return;
+ using EclipseType = EclipseMapData::EclipseType;
+
+ stream << "\n\n" << '\n';
+ stream << ""+q_("Solar Eclipse")+dateString+"\n"+q_("Created by Stellarium")+"\n";
+ stream << "\n";
+ stream << "\n";
+ stream << "\n";
+ stream << "\n";
- const double currentJD = core->getJD(); // save current JD
- const bool saveTopocentric = core->getUseTopocentricCoordinates();
- core->setUseTopocentricCoordinates(false);
- core->update(0);
- double JD = ui->solareclipsecontactsTreeWidget->topLevelItem(1)->data(SolarEclipseContactDate, Qt::UserRole).toDouble();
- // Find exact time of minimum distance between axis of lunar shadow cone to the center of Earth
- JD = getJDofMinimumDistance(JD);
- double JDMid = JD;
- int Year, Month, Day;
- StelUtils::getDateFromJulianDay(JDMid, &Year, &Month, &Day);
- // Use year-month-day in the file name
- QString eclipseDateStr = QString("-%1-%2-%3").arg(QString::number(Year), QString::number(Month), QString::number(Day));
- QString filter = "KML";
- filter.append(" (*.kml)");
- QString defaultFilter("(*.kml)");
- QString filePath = QFileDialog::getSaveFileName(&StelMainView::getInstance(),
- q_("Save KML as..."),
- QDir::homePath() + "/solareclipse"+eclipseDateStr+".kml",
- filter,
- &defaultFilter);
- if (filePath.isEmpty())
{
- core->setUseTopocentricCoordinates(saveTopocentric);
- core->update(0);
- return;
+ const auto timeStr = localeMgr->getPrintableTimeLocal(data.greatestEclipse.JD);
+ stream << "\n"+q_("Greatest eclipse")+" ("+timeStr+")\n\n";
+ stream << data.greatestEclipse.longitude << "," << data.greatestEclipse.latitude << ",0.0\n";
+ stream << "\n\n\n";
}
- QFile file(filePath);
- if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
{
- core->setUseTopocentricCoordinates(saveTopocentric);
- core->update(0);
- QMessageBox::critical(&StelMainView::getInstance(), q_("Failed to open output file"),
- q_("Failed to open output KML file: %1").arg(file.errorString()), QMessageBox::Ok);
- return;
+ const auto timeStr = localeMgr->getPrintableTimeLocal(data.firstContactWithEarth.JD);
+ stream << "\n"+q_("First contact with Earth")+" ("+timeStr+")\n\n";
+ stream << data.firstContactWithEarth.longitude << "," << data.firstContactWithEarth.latitude << ",0.0\n";
+ stream << "\n\n\n";
}
- QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+ {
+ const auto timeStr = localeMgr->getPrintableTimeLocal(data.lastContactWithEarth.JD);
+ stream << "\n"+q_("Last contact with Earth")+" ("+timeStr+")\n\n";
+ stream << data.lastContactWithEarth.longitude << "," << data.lastContactWithEarth.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ const auto startLinePlaceMark = [&stream](const QString& name, const EclipseMapData::EclipseType type = EclipseType::Undefined)
+ {
+ switch(type)
+ {
+ case EclipseMapData::EclipseType::Total:
+ stream << "\n" << name << "\n#Total\n\n1\n";
+ break;
+ case EclipseMapData::EclipseType::Annular:
+ stream << "\n" << name << "\n#Annular\n\n1\n";
+ break;
+ case EclipseMapData::EclipseType::Hybrid:
+ stream << "\n" << name << "\n#Hybrid\n\n1\n";
+ break;
+ default:
+ stream << "\n" << name << "\n#PLimits\n\n1\n";
+ break;
+ }
+ };
+
+ for(const auto& penumbraLimit : data.penumbraLimits)
+ {
+ startLinePlaceMark("PenumbraLimit");
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : penumbraLimit)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ for(const auto& riseSetLimit : data.riseSetLimits)
+ {
+ struct RiseSetLimitVisitor
+ {
+ QTextStream& stream;
+ std::function startLinePlaceMark;
+ RiseSetLimitVisitor(QTextStream& stream, const std::function& startLinePlaceMark)
+ : stream(stream), startLinePlaceMark(startLinePlaceMark)
+ {
+ }
+ void operator()(const EclipseMapData::TwoLimits& limits) const
+ {
+ // P1-P2 curve
+ startLinePlaceMark("RiseSetLimit", EclipseType::Undefined);
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : limits.p12curve)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+
+ // P3-P4 curve
+ startLinePlaceMark("RiseSetLimit", EclipseType::Undefined);
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : limits.p34curve)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+ void operator()(const EclipseMapData::SingleLimit& limit) const
+ {
+ startLinePlaceMark("RiseSetLimit", EclipseType::Undefined);
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : limit.curve)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+ };
+ std::visit(RiseSetLimitVisitor{stream, startLinePlaceMark}, riseSetLimit);
+ }
+
+ for(const auto& curve : data.maxEclipseAtRiseSet)
+ {
+ startLinePlaceMark("MaxEclipseSunriseSunset");
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : curve)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ if(data.centralEclipseStart.JD > 0)
+ {
+ const auto timeStr = localeMgr->getPrintableTimeLocal(data.centralEclipseStart.JD);
+ stream << "\n"+q_("Central eclipse begins")+" ("+timeStr+")\n\n";
+ stream << data.centralEclipseStart.longitude << "," << data.centralEclipseStart.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ if(data.centralEclipseEnd.JD > 0)
+ {
+ const auto timeStr = localeMgr->getPrintableTimeLocal(data.centralEclipseEnd.JD);
+ stream << "\n"+q_("Central eclipse ends")+" ("+timeStr+")\n\n";
+ stream << data.centralEclipseEnd.longitude << "," << data.centralEclipseEnd.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ if(!data.centerLine.empty())
+ {
+ startLinePlaceMark("Center line", data.eclipseType);
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : data.centerLine)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ for(const auto& outline : data.umbraOutlines)
+ {
+ const auto timeStr = localeMgr->getPrintableTimeLocal(outline.JD);
+ startLinePlaceMark(timeStr, outline.eclipseType);
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : outline.curve)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ for(const auto& outline : data.extremeUmbraLimit1)
+ {
+ startLinePlaceMark("Limit", outline.eclipseType);
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : outline.curve)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ for(const auto& outline : data.extremeUmbraLimit2)
+ {
+ startLinePlaceMark("Limit", outline.eclipseType);
+ stream << "1\nabsoluto\n\n";
+ for(const auto& p : outline.curve)
+ stream << p.longitude << "," << p.latitude << ",0.0\n";
+ stream << "\n\n\n";
+ }
+
+ stream << "\n\n";
+}
+
+auto AstroCalcDialog::generateEclipseMap(const double JDMid) -> EclipseMapData
+{
+ const bool savedTopocentric = core->getUseTopocentricCoordinates();
+ const double currentJD = core->getJD(); // save current JD
+ core->setUseTopocentricCoordinates(false);
+ core->update(0);
+
+ EclipseMapData data;
+
bool partialEclipse = false;
bool nonCentralEclipse = false;
double x,y,d,tf1,tf2,L1,L2,mu;
@@ -4167,59 +4299,33 @@ void AstroCalcDialog::saveSolarEclipseKML()
JDP3 = getJDofContact(JDMid,false,true,true,false);
}
- QTextStream stream(&file);
- stream << "\n\n" << '\n';
- stream << ""+q_("Solar Eclipse")+eclipseDateStr+"\n"+q_("Created by Stellarium")+"\n";
- stream << "\n";
- stream << "\n";
- stream << "\n";
- stream << "\n";
+ // Generate GE
+ data.greatestEclipse.JD = JDMid;
+ SolarEclipseData(JDMid,dRatio,data.greatestEclipse.latitude,data.greatestEclipse.longitude,altitude,pathWidth,duration,magnitude);
+
+ // Generate P1
+ data.firstContactWithEarth.JD = JDP1;
+ SolarEclipseData(JDP1,dRatio,data.firstContactWithEarth.latitude,data.firstContactWithEarth.longitude,altitude,pathWidth,duration,magnitude);
- // Plot GE
- SolarEclipseData(JDMid,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
- QString eclipseTime = localeMgr->getPrintableTimeLocal(JDMid);
- stream << "\n"+q_("Greatest eclipse")+" ("+eclipseTime+")\n\n";
- stream << lngDeg << "," << latDeg << ",0.0\n";
- stream << "\n\n\n";
-
- // Plot P1
- SolarEclipseData(JDP1,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
- double latP1 = latDeg, lngP1 = lngDeg;
- eclipseTime = localeMgr->getPrintableTimeLocal(JDP1);
- stream << "\n"+q_("First contact with Earth")+" ("+eclipseTime+")\n\n";
- stream << lngDeg << "," << latDeg << ",0.0\n";
- stream << "\n\n\n";
-
- // Plot P4
- SolarEclipseData(JDP4,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
- double latP4 = latDeg, lngP4 = lngDeg;
- eclipseTime = localeMgr->getPrintableTimeLocal(JDP4);
- stream << "\n"+q_("Last contact with Earth")+" ("+eclipseTime+")\n\n";
- stream << lngDeg << "," << latDeg << ",0.0\n";
- stream << "\n\n\n";
+ // Generate P4
+ data.lastContactWithEarth.JD = JDP4;
+ SolarEclipseData(JDP4,dRatio,data.lastContactWithEarth.latitude,data.lastContactWithEarth.longitude,altitude,pathWidth,duration,magnitude);
// Northern/southern Limits of penumbra
bool north = true;
for (int j = 0; j < 2; j++)
{
if (j != 0) north = false;
- stream << "\nPenumbraLimit\n#PLimits\n\n1\n";
- stream << "1\nabsoluto\n\n";
- JD = JDP1;
+ double JD = JDP1;
int i = 0;
while (JD < JDP4)
{
JD = JDP1 + i/1440.0;
coordinates = getNSLimitofShadow(JD,north,true);
if (coordinates.first <= 90.)
- stream << coordinates.second << "," << coordinates.first << ",0.0\n";
+ data.penumbraLimits[j].emplace_back(coordinates.second, coordinates.first);
i++;
}
- stream << "\n\n\n";
}
// Eclipse begins/ends at sunrise/sunset curve
@@ -4237,10 +4343,11 @@ void AstroCalcDialog::saveSolarEclipseKML()
coordinates = getContactCoordinates(x,y,d,mu);
latP2 = coordinates.first;
lngP2 = coordinates.second;
- stream << "\nRiseSetLimit\n#PLimits\n\n1\n";
- stream << "1\nabsoluto\n\n";
- stream << lngP1 << "," << latP1 << ",0.0\n";
- JD = JDP1;
+ auto& limit = data.riseSetLimits[j].emplace();
+
+ limit.p12curve.emplace_back(data.firstContactWithEarth.longitude,
+ data.firstContactWithEarth.latitude);
+ double JD = JDP1;
int i = 0;
while (JD < JDP2)
{
@@ -4250,13 +4357,10 @@ void AstroCalcDialog::saveSolarEclipseKML()
SolarEclipseBessel(x,y,d,tf1,tf2,L1,L2,mu);
coordinates = getRiseSetLineCoordinates(first,x,y,d,L1,mu);
if (coordinates.first <= 90.)
- {
- stream << coordinates.second << "," << coordinates.first << ",0.0\n";
- }
+ limit.p12curve.emplace_back(coordinates.second, coordinates.first);
i++;
}
- stream << lngP2 << "," << latP2 << ",0.0\n";
- stream << "\n\n\n";
+ limit.p12curve.emplace_back(lngP2, latP2);
// P3 to P4 curve
core->setJD(JDP3);
@@ -4265,9 +4369,7 @@ void AstroCalcDialog::saveSolarEclipseKML()
coordinates = getContactCoordinates(x,y,d,mu);
latP3 = coordinates.first;
lngP3 = coordinates.second;
- stream << "\nRiseSetLimit\n#PLimits\n\n1\n";
- stream << "1\nabsoluto\n\n";
- stream << lngP3 << "," << latP3 << ",0.0\n";
+ limit.p34curve.emplace_back(lngP3, latP3);
JD = JDP3;
i = 0;
while (JD < JDP4)
@@ -4278,13 +4380,11 @@ void AstroCalcDialog::saveSolarEclipseKML()
SolarEclipseBessel(x,y,d,tf1,tf2,L1,L2,mu);
coordinates = getRiseSetLineCoordinates(first,x,y,d,L1,mu);
if (coordinates.first <= 90.)
- {
- stream << coordinates.second << "," << coordinates.first << ",0.0\n";
- }
+ limit.p34curve.emplace_back(coordinates.second, coordinates.first);
i++;
}
- stream << lngP4 << "," << latP4 << ",0.0\n";
- stream << "\n\n\n";
+ limit.p34curve.emplace_back(data.lastContactWithEarth.longitude,
+ data.lastContactWithEarth.latitude);
}
}
else
@@ -4295,10 +4395,10 @@ void AstroCalcDialog::saveSolarEclipseKML()
for (int j = 0; j < 2; j++)
{
if (j != 0) first = false;
- stream << "\nRiseSetLimit\n#PLimits\n\n1\n";
- stream << "1\nabsoluto\n\n";
- stream << lngP1 << "," << latP1 << ",0.0\n";
- JD = JDP1;
+ auto& limit = data.riseSetLimits[j].emplace();
+ limit.curve.emplace_back(data.firstContactWithEarth.longitude,
+ data.firstContactWithEarth.latitude);
+ double JD = JDP1;
int i = 0;
while (JD < JDP4)
{
@@ -4308,13 +4408,11 @@ void AstroCalcDialog::saveSolarEclipseKML()
SolarEclipseBessel(x,y,d,tf1,tf2,L1,L2,mu);
coordinates = getRiseSetLineCoordinates(first,x,y,d,L1,mu);
if (coordinates.first <= 90.)
- {
- stream << coordinates.second << "," << coordinates.first << ",0.0\n";
- }
+ limit.curve.emplace_back(coordinates.second, coordinates.first);
i++;
}
- stream << lngP4 << "," << latP4 << ",0.0\n";
- stream << "\n\n\n";
+ limit.curve.emplace_back(data.lastContactWithEarth.longitude,
+ data.lastContactWithEarth.latitude);
}
}
@@ -4326,9 +4424,9 @@ void AstroCalcDialog::saveSolarEclipseKML()
for (int j = 0; j < 2; j++)
{
if ( j!= 0) first = false;
- stream << "\nMaxEclipseSunriseSunset\n#PLimits\n\n1\n";
- stream << "1\nabsoluto\n\n";
- JD = JDP1;
+ data.maxEclipseAtRiseSet.emplace_back();
+ auto* curve = &data.maxEclipseAtRiseSet.back();
+ double JD = JDP1;
int i = 0, count = 0;
double lat0 = 0., lon0 = 0., dlat, dlon, diff;
while (JD < JDP4)
@@ -4343,16 +4441,13 @@ void AstroCalcDialog::saveSolarEclipseKML()
diff = std::sqrt(dlat*dlat+dlon*dlon);
if (diff>10.)
{
- stream << "\n\n\n";
- stream << "\nMaxEclipseSunriseSunset\n#PLimits\n\n1\n";
- stream << "1\nabsoluto\n\n";
+ data.maxEclipseAtRiseSet.emplace_back();
+ curve = &data.maxEclipseAtRiseSet.back();
}
if (!first && count == 1) startJD2 = JD;
if (!first && count == 1 && bothPenumbralLimits && (startJD1 < JDMid) && (startJD2 < JDMid))
- {
- stream << startLon1 << "," << startLat1 << ",0.0\n"; // connect start of part 1 to start of part 2
- }
- stream << coordinates.second << "," << coordinates.first << ",0.0\n";
+ curve->emplace_back(startLon1, startLat1); // connect start of part 1 to start of part 2
+ curve->emplace_back(coordinates.second, coordinates.first);
if (first && bothPenumbralLimits)
{
endJD1 = JD;
@@ -4372,10 +4467,7 @@ void AstroCalcDialog::saveSolarEclipseKML()
}
if (!first && bothPenumbralLimits) endJD2 = JD;
if (!first && bothPenumbralLimits && (endJD1 > JDMid) && (endJD2 > JDMid))
- {
- stream << endLon1 << "," << endLat1 << ",0.0\n"; // connect end of part 2 to end of part 1
- }
- stream << "\n\n\n";
+ curve->emplace_back(endLon1, endLat1); // connect end of part 2 to end of part 1
}
if (!partialEclipse)
@@ -4387,7 +4479,7 @@ void AstroCalcDialog::saveSolarEclipseKML()
if (!nonCentralEclipse)
{
// C1
- JD = getJDofContact(JDMid,true,false,false,true);
+ double JD = getJDofContact(JDMid,true,false,false,true);
JD = int(JD)+(int((JD-int(JD))*86400.)-1)/86400.;
SolarEclipseData(JD,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
int steps = 0;
@@ -4405,11 +4497,10 @@ void AstroCalcDialog::saveSolarEclipseKML()
double latC1 = coordinates.first;
double lngC1 = coordinates.second;
- // Plot C1
- eclipseTime = localeMgr->getPrintableTimeLocal(JDC1);
- stream << "\n"+q_("Central eclipse begins")+" ("+eclipseTime+")\n\n";
- stream << lngC1 << "," << latC1 << ",0.0\n";
- stream << "\n\n\n";
+ // Generate C1
+ data.centralEclipseStart.JD = JDC1;
+ data.centralEclipseStart.longitude = lngC1;
+ data.centralEclipseStart.latitude = latC1;
// C2
JD = getJDofContact(JDMid,false,false,false,true);
@@ -4430,11 +4521,10 @@ void AstroCalcDialog::saveSolarEclipseKML()
double latC2 = coordinates.first;
double lngC2 = coordinates.second;
- // Plot C2
- eclipseTime = localeMgr->getPrintableTimeLocal(JDC2);
- stream << "\n"+q_("Central eclipse ends")+" ("+eclipseTime+")\n\n";
- stream << lngC2 << "," << latC2 << ",0.0\n";
- stream << "\n\n\n";
+ // Generate C2
+ data.centralEclipseEnd.JD = JDC2;
+ data.centralEclipseEnd.longitude = lngC2;
+ data.centralEclipseEnd.latitude = latC2;
// Center line
JD = JDC1;
@@ -4445,22 +4535,20 @@ void AstroCalcDialog::saveSolarEclipseKML()
SolarEclipseData(JDC2,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
double dRatioC2 = dRatio;
if (dRatioC1 >= 1. && dRatioMid >= 1. && dRatioC2 >= 1.)
- stream << "\nCenter line\n#Total\n\n1\n";
+ data.eclipseType = EclipseMapData::EclipseType::Total;
else if (dRatioC1 < 1. && dRatioMid < 1. && dRatioC2 < 1.)
- stream << "\nCenter line\n#Annular\n\n1\n";
+ data.eclipseType = EclipseMapData::EclipseType::Annular;
else
- stream << "\nCenter line\n#Hybrid\n\n1\n";
- stream << "1\nabsoluto\n\n";
- stream << lngC1 << "," << latC1 << ",0.0\n";
+ data.eclipseType = EclipseMapData::EclipseType::Hybrid;
+ data.centerLine.emplace_back(lngC1, latC1);
while (JD+(1./1440.) < JDC2)
{
- JD = JDC1 + i/1440.; // plot every one minute
+ JD = JDC1 + i/1440.; // generate every one minute
SolarEclipseData(JD,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
- stream << lngDeg << "," << latDeg << ",0.0\n";
+ data.centerLine.emplace_back(lngDeg, latDeg);
i++;
}
- stream << lngC2 << "," << latC2 << ",0.0\n";
- stream << "\n\n\n";
+ data.centerLine.emplace_back(lngC2, latC2);
}
else
{
@@ -4477,24 +4565,24 @@ void AstroCalcDialog::saveSolarEclipseKML()
// we want to draw (ant)umbral shadow on world map at exact times like 09:00, 09:10, 09:20, ...
double beginJD = int(JDU1)+(10.*int(1440.*(JDU1-int(JDU1))/10.)+10.)/1440.;
double endJD = int(JDU4)+(10.*int(1440.*(JDU4-int(JDU4))/10.))/1440.;
- JD = beginJD;
+ double JD = beginJD;
int i = 0;
double lat0 = 0., lon0 = 0.;
while (JD < endJD)
{
- JD = beginJD + i/144.; // plot every 10 minutes
+ JD = beginJD + i/144.; // generate every 10 minutes
core->setJD(JD);
core->update(0);
SolarEclipseBessel(x,y,d,tf1,tf2,L1,L2,mu);
SolarEclipseData(JD,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
double angle = 0.;
bool firstPoint = false;
- QString eclipseTime = localeMgr->getPrintableTimeLocal(JD);
+ auto& outline = data.umbraOutlines.emplace_back();
+ outline.JD = JD;
if (dRatio>=1.)
- stream << "\n"+eclipseTime+"\n#Total\n\n1\n";
+ outline.eclipseType = EclipseMapData::EclipseType::Total;
else
- stream << "\n"+eclipseTime+"\n#Annular\n\n1\n";
- stream << "1\nabsoluto\n\n";
+ outline.eclipseType = EclipseMapData::EclipseType::Annular;
int pointNumber = 0;
while (pointNumber < 60)
{
@@ -4502,7 +4590,7 @@ void AstroCalcDialog::saveSolarEclipseKML()
coordinates = getShadowOutlineCoordinates(angle,x,y,d,L2,tf2,mu);
if (coordinates.first <= 90.)
{
- stream << coordinates.second << "," << coordinates.first << ",0.0\n";
+ outline.curve.emplace_back(coordinates.second, coordinates.first);
if (!firstPoint)
{
lat0 = coordinates.first;
@@ -4512,8 +4600,7 @@ void AstroCalcDialog::saveSolarEclipseKML()
}
pointNumber++;
}
- stream << lon0 << "," << lat0 << ",0.0\n"; // completing the circle
- stream << "\n\n\n";
+ outline.curve.emplace_back(lon0, lat0); // completing the circle
i++;
}
@@ -4528,20 +4615,20 @@ void AstroCalcDialog::saveSolarEclipseKML()
double dRatio,altitude,pathWidth,duration,magnitude;
SolarEclipseData(JDC1,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
+ auto& extremeLimit1 = data.extremeUmbraLimit1.emplace_back();
if (dRatioC1 >= 1. && dRatioMid >= 1. && dRatioC2 >= 1.)
- stream << "\nLimit\n#Total\n\n1\n";
+ extremeLimit1.eclipseType = EclipseMapData::EclipseType::Total;
else if (dRatioC1 < 1. && dRatioMid < 1. && dRatioC2 < 1.)
- stream << "\nLimit\n#Annular\n\n1\n";
+ extremeLimit1.eclipseType = EclipseMapData::EclipseType::Annular;
else
- stream << "\nLimit\n#Hybrid\n\n1\n";
- stream << "1\nabsoluto\n\n";
+ extremeLimit1.eclipseType = EclipseMapData::EclipseType::Hybrid;
// 1st extreme limit at C1
if (C1a.first <= 90. || C1b.first <= 90.)
{
if (dRatio>=1.)
- stream << C1a.second << "," << C1a.first << ",0.0\n";
+ extremeLimit1.curve.emplace_back(C1a.second, C1a.first);
else
- stream << C1b.second << "," << C1b.first << ",0.0\n";
+ extremeLimit1.curve.emplace_back(C1b.second, C1b.first);
}
JD = JDC1-20./1440.;
i = 0;
@@ -4550,7 +4637,7 @@ void AstroCalcDialog::saveSolarEclipseKML()
JD = JDC1+(i-20.)/1440.;
coordinates = getNSLimitofShadow(JD,true,false);
if (coordinates.first <= 90.)
- stream << coordinates.second << "," << coordinates.first << ",0.0\n";
+ extremeLimit1.curve.emplace_back(coordinates.second, coordinates.first);
i++;
}
@@ -4559,27 +4646,26 @@ void AstroCalcDialog::saveSolarEclipseKML()
if (C2a.first <= 90. || C2b.first <= 90.)
{
if (dRatio>=1.)
- stream << C2a.second << "," << C2a.first << ",0.0\n";
+ extremeLimit1.curve.emplace_back(C2a.second, C2a.first);
else
- stream << C2b.second << "," << C2b.first << ",0.0\n";
+ extremeLimit1.curve.emplace_back(C2b.second, C2b.first);
}
- stream << "\n\n\n";
+ auto& extremeLimit2 = data.extremeUmbraLimit1.emplace_back();
// 2nd extreme limit at C1
if (dRatioC1 >= 1. && dRatioMid >= 1. && dRatioC2 >= 1.)
- stream << "\nLimit\n#Total\n\n1\n";
+ extremeLimit2.eclipseType = EclipseMapData::EclipseType::Total;
else if (dRatioC1 < 1. && dRatioMid < 1. && dRatioC2 < 1.)
- stream << "\nLimit\n#Annular\n\n1\n";
+ extremeLimit2.eclipseType = EclipseMapData::EclipseType::Annular;
else
- stream << "\nLimit\n#Hybrid\n\n1\n";
- stream << "1\nabsoluto\n\n";
+ extremeLimit2.eclipseType = EclipseMapData::EclipseType::Hybrid;
SolarEclipseData(JDC1,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
if (C1a.first <= 90. || C1b.first <= 90.)
{
if (dRatio>=1.)
- stream << C1b.second << "," << C1b.first << ",0.0\n";
+ extremeLimit2.curve.emplace_back(C1b.second, C1b.first);
else
- stream << C1a.second << "," << C1a.first << ",0.0\n";
+ extremeLimit2.curve.emplace_back(C1a.second, C1a.first);
}
JD = JDC1-20./1440.;
i = 0;
@@ -4588,7 +4674,7 @@ void AstroCalcDialog::saveSolarEclipseKML()
JD = JDC1+(i-20.)/1440.;
coordinates = getNSLimitofShadow(JD,false,false);
if (coordinates.first <= 90.)
- stream << coordinates.second << "," << coordinates.first << ",0.0\n";
+ extremeLimit2.curve.emplace_back(coordinates.second, coordinates.first);
i++;
}
SolarEclipseData(JDC2,dRatio,latDeg,lngDeg,altitude,pathWidth,duration,magnitude);
@@ -4596,19 +4682,65 @@ void AstroCalcDialog::saveSolarEclipseKML()
if (C2a.first <= 90. || C2b.first <= 90.)
{
if (dRatio>=1.)
- stream << C2b.second << "," << C2b.first << ",0.0\n";
+ extremeLimit2.curve.emplace_back(C2b.second, C2b.first);
else
- stream << C2a.second << "," << C2a.first << ",0.0\n";
+ extremeLimit2.curve.emplace_back(C2a.second, C2a.first);
}
- stream << "\n\n\n";
}
- stream << "\n\n";
- file.close();
- QGuiApplication::restoreOverrideCursor();
core->setJD(currentJD);
- core->setUseTopocentricCoordinates(saveTopocentric);
+ core->setUseTopocentricCoordinates(savedTopocentric);
core->update(0);
+
+ return data;
+}
+
+void AstroCalcDialog::saveSolarEclipseKML()
+{
+ // Make sure that we have circumstances of an eclipse in the table
+ if (ui->solareclipsecontactsTreeWidget->topLevelItemCount() == 0)
+ return;
+
+ const bool savedTopocentric = core->getUseTopocentricCoordinates();
+ core->setUseTopocentricCoordinates(false);
+ core->update(0);
+
+ const double eclipseJD = ui->solareclipsecontactsTreeWidget->topLevelItem(1)->data(SolarEclipseContactDate, Qt::UserRole).toDouble();
+ // Find exact time of minimum distance between axis of lunar shadow cone to the center of Earth
+ const double JDMid = getJDofMinimumDistance(eclipseJD);
+
+ int year, month, day;
+ StelUtils::getDateFromJulianDay(JDMid, &year, &month, &day);
+
+ core->setUseTopocentricCoordinates(savedTopocentric);
+ core->update(0);
+
+ // Use year-month-day in the file name
+ const auto eclipseDateStr = QString("-%1-%2-%3").arg(year).arg(month).arg(day);
+ QString filter = "KML";
+ filter.append(" (*.kml)");
+ QString defaultFilter("(*.kml)");
+ QString filePath = QFileDialog::getSaveFileName(&StelMainView::getInstance(),
+ q_("Save KML as..."),
+ QDir::homePath() + "/solareclipse"+eclipseDateStr+".kml",
+ filter,
+ &defaultFilter);
+ if (filePath.isEmpty()) return;
+
+ QFile file(filePath);
+ if(!file.open(QIODevice::WriteOnly | QIODevice::Text))
+ {
+ QMessageBox::critical(&StelMainView::getInstance(), q_("Failed to open output file"),
+ q_("Failed to open output KML file: %1").arg(file.errorString()), QMessageBox::Ok);
+ return;
+ }
+
+ QGuiApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
+ const auto data = generateEclipseMap(JDMid);
+ QTextStream stream(&file);
+ generateKML(data, eclipseDateStr, stream);
+ file.close();
+ QGuiApplication::restoreOverrideCursor();
}
QPair AstroCalcDialog::getNSLimitofShadow(double JD, bool northernLimit, bool penumbra)
diff --git a/src/gui/AstroCalcDialog.hpp b/src/gui/AstroCalcDialog.hpp
index b56d720b5fe3e..ece39bdec2020 100644
--- a/src/gui/AstroCalcDialog.hpp
+++ b/src/gui/AstroCalcDialog.hpp
@@ -23,6 +23,7 @@
#ifndef ASTROCALCDIALOG_HPP
#define ASTROCALCDIALOG_HPP
+#include
#include
#include
#include
@@ -76,6 +77,81 @@ class AstroCalcDialog : public StelDialog
{
Q_OBJECT
+ struct EclipseMapData
+ {
+ enum class EclipseType
+ {
+ Undefined,
+ Total,
+ Annular,
+ Hybrid,
+ };
+ struct GeoPoint
+ {
+ double longitude;
+ double latitude;
+
+ GeoPoint() = default;
+ GeoPoint(double lon, double lat)
+ : longitude(lon), latitude(lat)
+ {
+ }
+ };
+ struct GeoTimePoint
+ {
+ double JD = -1;
+ double longitude;
+ double latitude;
+
+ GeoTimePoint() = default;
+ GeoTimePoint(double JD, double lon, double lat)
+ : JD(JD), longitude(lon), latitude(lat)
+ {
+ }
+ };
+ struct UmbraLimit
+ {
+ std::vector curve;
+ EclipseType eclipseType = EclipseType::Undefined;
+ };
+ struct UmbraOutline
+ {
+ std::vector curve;
+ double JD;
+ EclipseType eclipseType = EclipseType::Undefined;
+ };
+ GeoTimePoint greatestEclipse;
+ GeoTimePoint firstContactWithEarth; // AKA P1
+ GeoTimePoint lastContactWithEarth; // AKA P4
+ GeoTimePoint centralEclipseStart; // AKA C1
+ GeoTimePoint centralEclipseEnd; // AKA C2
+
+ // The array elements are {northLimit, southLimit}
+ std::vector penumbraLimits[2];
+
+ // The curves in arrays are split into two lines by the computation algorithm
+ struct TwoLimits
+ {
+ std::vector p12curve;
+ std::vector p34curve;
+ };
+ struct SingleLimit
+ {
+ std::vector curve;
+ };
+ std::variant riseSetLimits[2];
+
+ // These curves appear to be split generally in multiple sections
+ std::vector> maxEclipseAtRiseSet;
+
+ std::vector centerLine;
+ std::vector umbraOutlines;
+ std::vector extremeUmbraLimit1;
+ std::vector extremeUmbraLimit2;
+
+ EclipseType eclipseType;
+ };
+
public:
//! Defines the number and the order of the columns in the table that lists celestial bodies positions
//! @enum CPositionsColumns
@@ -652,6 +728,9 @@ private slots:
//! @todo Limit the width to the width of the screen *available to the window*.
void updateTabBarListWidgetWidth();
+ EclipseMapData generateEclipseMap(double JDMid);
+ void generateKML(const EclipseMapData& data, const QString& dateString, QTextStream& stream) const;
+
void enableAngularLimits(bool enable);
QString getSelectedObjectNameI18n(StelObjectP selectedObject);