diff --git a/apps/OpenSpace/ext/launcher/include/profile/horizonsdialog.h b/apps/OpenSpace/ext/launcher/include/profile/horizonsdialog.h
index 3ae2bb593f..0cbd7cece8 100644
--- a/apps/OpenSpace/ext/launcher/include/profile/horizonsdialog.h
+++ b/apps/OpenSpace/ext/launcher/include/profile/horizonsdialog.h
@@ -77,7 +77,7 @@ private:
json sendRequest(const std::string url);
json handleReply(QNetworkReply* reply);
bool checkHttpStatus(const QVariant& statusCode);
- std::filesystem::path handleAnswer(json& answer);
+ openspace::HorizonsFile handleAnswer(json& answer);
bool handleResult(openspace::HorizonsFile::ResultCode& result);
void appendLog(const std::string& message, const LogLevel level);
diff --git a/apps/OpenSpace/ext/launcher/src/profile/horizonsdialog.cpp b/apps/OpenSpace/ext/launcher/src/profile/horizonsdialog.cpp
index 58ceeed090..8fa72a861a 100644
--- a/apps/OpenSpace/ext/launcher/src/profile/horizonsdialog.cpp
+++ b/apps/OpenSpace/ext/launcher/src/profile/horizonsdialog.cpp
@@ -22,8 +22,6 @@
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
****************************************************************************************/
-
-
#include "profile/horizonsdialog.h"
#include "profile/line.h"
@@ -57,19 +55,6 @@ namespace {
constexpr const char* Years = "calendar years";
constexpr const char* Unitless = "equal intervals (unitless)";
- std::string replaceAll(const std::string& string, const std::string& from, const std::string& to) {
- if (from.empty())
- return "";
-
- std::string result = string;
- size_t startPos = 0;
- while ((startPos = result.find(from, startPos)) != std::string::npos) {
- result.replace(startPos, from.length(), to);
- startPos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
- }
- return result;
- }
-
std::string trim(const std::string& text) {
std::string result = text;
if (text.empty())
@@ -146,8 +131,8 @@ void HorizonsDialog::createWidgets() {
generateLabel->setObjectName("heading");
layout->addWidget(generateLabel);
- QLabel* infoLabel = new QLabel("
For more information about the Horizons system "
- "please visit: "
+ QLabel* infoLabel = new QLabel("For more information about the Horizons "
+ "system please visit: "
"https://ssd.jpl.nasa.gov/horizons/
",
this
);
@@ -158,7 +143,7 @@ void HorizonsDialog::createWidgets() {
}
{
QBoxLayout* container = new QHBoxLayout(this);
- QLabel* typeLabel = new QLabel("Horizons data type", this);
+ QLabel* typeLabel = new QLabel("Horizons data type:", this);
container->addWidget(typeLabel);
_typeCombo = new QComboBox(this);
@@ -179,7 +164,9 @@ void HorizonsDialog::createWidgets() {
container->addWidget(nameLabel);
_nameEdit = new QLineEdit(QString::fromStdString("horizons.dat"), this);
- _nameEdit->setToolTip("Name of the generated Horizons file. Must end with '.dat'");
+ _nameEdit->setToolTip(
+ "Name of the generated Horizons file. Must end with '.dat'"
+ );
container->addWidget(_nameEdit);
layout->addLayout(container);
@@ -190,7 +177,9 @@ void HorizonsDialog::createWidgets() {
container->addWidget(directoryLabel);
_directoryEdit = new QLineEdit(this);
- _directoryEdit->setToolTip("Directory where the generated Horizons file is saved");
+ _directoryEdit->setToolTip(
+ "Directory where the generated Horizons file is saved"
+ );
container->addWidget(_directoryEdit);
QPushButton* directoryButton = new QPushButton("Browse", this);
@@ -210,15 +199,20 @@ void HorizonsDialog::createWidgets() {
QLabel* targetLabel = new QLabel("Target Body:", this);
container->addWidget(targetLabel);
- _targetEdit = new QLineEdit(QString::fromStdString("Mars Reconnaissance Orbiter"), this);
- _targetEdit->setToolTip("Which target or body would you like Horizons trajectery data for?");
+ _targetEdit =
+ new QLineEdit(QString::fromStdString("Mars Reconnaissance Orbiter"), this);
+ _targetEdit->setToolTip(
+ "Which target or body would you like Horizons trajectery data for?"
+ );
container->addWidget(_targetEdit);
layout->addLayout(container);
_chooseTargetCombo = new QComboBox(this);
_chooseTargetCombo->hide();
- _chooseTargetCombo->setToolTip("Choose a target from the search, or search again");
+ _chooseTargetCombo->setToolTip(
+ "Choose a target from the search, or search again"
+ );
layout->addWidget(_chooseTargetCombo);
}
{
@@ -234,7 +228,9 @@ void HorizonsDialog::createWidgets() {
_chooseObserverCombo = new QComboBox(this);
_chooseObserverCombo->hide();
- _chooseObserverCombo->setToolTip("Choose an observer from the search, or search again");
+ _chooseObserverCombo->setToolTip(
+ "Choose an observer from the search, or search again"
+ );
layout->addWidget(_chooseObserverCombo);
}
{
@@ -348,12 +344,12 @@ bool HorizonsDialog::handleRequest() {
return false;
}
- std::filesystem::path file = handleAnswer(answer);
- if (!std::filesystem::is_regular_file(file)) {
+ openspace::HorizonsFile file = handleAnswer(answer);
+ if (file.isEmpty()) {
return false;
}
- _horizonsFile = openspace::HorizonsFile(file);
+ _horizonsFile = std::move(file);
openspace::HorizonsFile::ResultCode result =
_horizonsFile.isValidHorizonsFile();
@@ -460,7 +456,8 @@ std::string HorizonsDialog::constructUrl() {
std::string command;
if (_chooseTargetCombo->count() > 0 && _chooseTargetCombo->currentIndex() != 0) {
- command = _chooseTargetCombo->itemData(_chooseTargetCombo->currentIndex()).toString().toStdString();
+ command = _chooseTargetCombo->itemData(_chooseTargetCombo->currentIndex())
+ .toString().toStdString();
_targetName = _chooseTargetCombo->currentText().toStdString();
_targetEdit->setText(command.c_str());
}
@@ -518,7 +515,15 @@ std::string HorizonsDialog::constructUrl() {
return "";
}
- return openspace::HorizonsFile::constructUrl(type, command, center, _startTime, _endTime, _stepEdit->text().toStdString(), unit);
+ return openspace::HorizonsFile::constructUrl(
+ type,
+ command,
+ center,
+ _startTime,
+ _endTime,
+ _stepEdit->text().toStdString(),
+ unit
+ );
}
// Send request synchronously, EventLoop waits until request has finished
@@ -591,7 +596,9 @@ json HorizonsDialog::handleReply(QNetworkReply* reply) {
bool HorizonsDialog::checkHttpStatus(const QVariant& statusCode) {
bool isKnown = true;
- if (statusCode.isValid() && statusCode.toInt() != int(HorizonsDialog::HTTPCodes::Ok)) {
+ if (statusCode.isValid() &&
+ statusCode.toInt() != int(HorizonsDialog::HTTPCodes::Ok))
+ {
std::string message;
int code = statusCode.toInt();
@@ -628,8 +635,9 @@ bool HorizonsDialog::checkHttpStatus(const QVariant& statusCode) {
return isKnown;
}
-std::filesystem::path HorizonsDialog::handleAnswer(json& answer) {
- openspace::HorizonsFile::ResultCode isValid = openspace::HorizonsFile::isValidAnswer(answer);
+openspace::HorizonsFile HorizonsDialog::handleAnswer(json& answer) {
+ openspace::HorizonsFile::ResultCode isValid =
+ openspace::HorizonsFile::isValidAnswer(answer);
if (isValid != openspace::HorizonsFile::ResultCode::Valid &&
isValid != openspace::HorizonsFile::ResultCode::MultipleObserverStations &&
isValid != openspace::HorizonsFile::ResultCode::ErrorTimeRange)
@@ -637,9 +645,9 @@ std::filesystem::path HorizonsDialog::handleAnswer(json& answer) {
// Special case with MultipleObserverStations since it is detected as an error
// but could be fixed by parsing the matches and let user choose
// Special case with ErrorTimeRange since it is detected as an error
- // but could be nice to display the avalable itme range of target to the user
+ // but could be nice to display the available time range of target to the user
handleResult(isValid);
- return std::filesystem::path();
+ return openspace::HorizonsFile();
}
// Create a text file and write reply to it
@@ -655,21 +663,17 @@ std::filesystem::path HorizonsDialog::handleAnswer(json& answer) {
"Malformed answer recieved: " + answer.dump(),
HorizonsDialog::LogLevel::Error
);
- return std::filesystem::path();
+ return openspace::HorizonsFile();
}
// Check if the file already exists
if (std::filesystem::is_regular_file(fullFilePath)) {
_errorMsg->setText("File already exist, try another filename");
- return std::filesystem::path();
+ return openspace::HorizonsFile();
}
- // Write response into a new file
- std::ofstream file(filePath);
- file << replaceAll(*result, "\\n", "\n") << std::endl;
- file.close();
-
- return fullFilePath;
+ // Return a new file with the result
+ return openspace::HorizonsFile(fullFilePath, *result);
}
bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) {
@@ -735,7 +739,8 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) {
appendLog("There is not enough data to compute the state of target '" +
_targetName + "' in relation to the observer '" + _observerName +
"' for the time range '" + _startTime + "' to '" + _endTime +
- "'. Try to use another observer for the current target or another time range.",
+ "'. Try to use another observer for the current target or another "
+ "time range.",
HorizonsDialog::LogLevel::Error
);
break;
@@ -750,7 +755,10 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) {
);
std::vector matchingstations =
- _horizonsFile.parseMatches("Observatory Name", "Multiple matching stations found");
+ _horizonsFile.parseMatches(
+ "Observatory Name",
+ "Multiple matching stations found"
+ );
if (matchingstations.empty()) {
appendLog("Could not parse the matching stations",
HorizonsDialog::LogLevel::Error
@@ -805,7 +813,8 @@ bool HorizonsDialog::handleResult(openspace::HorizonsFile::ResultCode& result) {
// Case Small Bodies:
// Line before data: Matching small-bodies
// Format: Record #, Epoch-yr, >MATCH DESIG<, Primary Desig, Name
- // Line after data: (X matches. To SELECT, enter record # (integer), followed by semi-colon.)
+ // Line after data:
+ // (X matches. To SELECT, enter record # (integer), followed by semi-colon.)
// Case Major Bodies:
// Line before data: Multiple major-bodies match string "X*"
diff --git a/modules/space/horizonsfile.cpp b/modules/space/horizonsfile.cpp
index eb3fc6556c..fda4b97fce 100644
--- a/modules/space/horizonsfile.cpp
+++ b/modules/space/horizonsfile.cpp
@@ -42,9 +42,9 @@ namespace {
constexpr const char* VectorUrl = "https://ssd.jpl.nasa.gov/api/horizons.api?format="
"json&MAKE_EPHEM='YES'&EPHEM_TYPE='VECTORS'&VEC_TABLE='1'&VEC_LABELS='NO'&"
"CSV_FORMAT='NO'";
- constexpr const char* ObserverUrl = "https://ssd.jpl.nasa.gov/api/horizons.api?format="
- "json&MAKE_EPHEM='YES'&EPHEM_TYPE='OBSERVER'&QUANTITIES='20,33'&RANGE_UNITS='KM'&"
- "SUPPRESS_RANGE_RATE='YES'&CSV_FORMAT='NO'";
+ constexpr const char* ObserverUrl = "https://ssd.jpl.nasa.gov/api/horizons.api?"
+ "format=json&MAKE_EPHEM='YES'&EPHEM_TYPE='OBSERVER'&QUANTITIES='20,33'&"
+ "RANGE_UNITS='KM'&SUPPRESS_RANGE_RATE='YES'&CSV_FORMAT='NO'";
constexpr const char* Command = "&COMMAND=";
constexpr const char* Center = "&CENTER=";
constexpr const char* StartTime = "&START_TIME=";
@@ -52,7 +52,9 @@ namespace {
constexpr const char* StepSize = "&STEP_SIZE=";
constexpr const char* WhiteSpace = "%20";
- std::string replaceAll(const std::string& string, const std::string& from, const std::string& to) {
+ std::string replaceAll(const std::string& string, const std::string& from,
+ const std::string& to)
+ {
if (from.empty())
return "";
@@ -60,7 +62,9 @@ namespace {
size_t startPos = 0;
while ((startPos = result.find(from, startPos)) != std::string::npos) {
result.replace(startPos, from.length(), to);
- startPos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
+
+ // In case 'to' contains 'from', ex replacing 'x' with 'yx'
+ startPos += to.length();
}
return result;
}
@@ -76,6 +80,14 @@ HorizonsFile::HorizonsFile(std::filesystem::path file)
: _file(std::move(file))
{}
+HorizonsFile::HorizonsFile(std::filesystem::path filePath, const std::string& result) {
+ // Write the response into a new file and save it
+ std::ofstream file(filePath);
+ file << replaceAll(result, "\\n", "\n") << std::endl;
+ file.close();
+ _file = std::move(filePath);
+}
+
void HorizonsFile::setFile(std::filesystem::path file) {
_file = std::move(file);
}
@@ -142,7 +154,9 @@ HorizonsFile::ResultCode HorizonsFile::isValidAnswer(const json& answer) {
auto sourceIt = signature.find("source");
if (sourceIt != signature.end()) {
if (*sourceIt != ApiSource) {
- LWARNING(fmt::format("Horizons answer from unkown source '{}'", *sourceIt));
+ LWARNING(fmt::format("Horizons answer from unkown source '{}'",
+ *sourceIt)
+ );
}
}
else {
@@ -157,7 +171,9 @@ HorizonsFile::ResultCode HorizonsFile::isValidAnswer(const json& answer) {
}
}
else {
- LWARNING("Could not find version information, version might not be supported");
+ LWARNING(
+ "Could not find version information, version might not be supported"
+ );
}
}
else {
@@ -201,7 +217,9 @@ HorizonsFile::ResultCode HorizonsFile::isValidAnswer(const json& answer) {
// -- - -------- ------ - ------ - ----------------;
// * Observer station *
// Multiple matching stations found.
- else if (errorMessage.find("Multiple matching stations found") != std::string::npos) {
+ else if (errorMessage.find("Multiple matching stations found") !=
+ std::string::npos)
+ {
return ResultCode::MultipleObserverStations;
}
// Unknown error
@@ -213,6 +231,10 @@ HorizonsFile::ResultCode HorizonsFile::isValidAnswer(const json& answer) {
return ResultCode::Valid;
}
+bool HorizonsFile::isEmpty() const {
+ return !std::filesystem::is_regular_file(_file);
+}
+
// Check whether the given Horizons file is valid or not
// Return an error code with what is the problem if there was one
HorizonsFile::ResultCode HorizonsFile::isValidHorizonsFile() const {
@@ -230,7 +252,10 @@ HorizonsFile::ResultCode HorizonsFile::isValidHorizonsFile() const {
std::getline(fileStream, line); // First line is just stars (*) no information, skip
// Valid Target?
- if (fileStream.good() && (line.find("Revised") != std::string::npos || line.find("JPL") != std::string::npos)) {
+ if (fileStream.good() &&
+ (line.find("Revised") != std::string::npos ||
+ line.find("JPL") != std::string::npos))
+ {
// If the target is valid, the first line is the date it was last revised
// In case of comets it says the Source in the top
foundTarget = true;
@@ -421,7 +446,8 @@ void HorizonsFile::displayErrorMessage(const ResultCode code) const {
// Case Small Bodies:
// Line before data: Matching small-bodies
// Format: Record #, Epoch-yr, >MATCH DESIG<, Primary Desig, Name
- // Line after data: (X matches. To SELECT, enter record # (integer), followed by semi-colon.)
+ // Line after data:
+ // (X matches. To SELECT, enter record # (integer), followed by semi-colon.)
// Case Major Bodies:
// Line before data: Multiple major-bodies match string "X*"
@@ -635,7 +661,7 @@ HorizonsFile::HorizonsResult HorizonsFile::readObserverFile() const {
}
std::vector HorizonsFile::parseMatches(const std::string& startPhrase,
- const std::string& endPhrase) const
+ const std::string& endPhrase) const
{
std::ifstream fileStream(_file);
std::vector matches;
diff --git a/modules/space/horizonsfile.h b/modules/space/horizonsfile.h
index f6d1fb0331..829fd53a70 100644
--- a/modules/space/horizonsfile.h
+++ b/modules/space/horizonsfile.h
@@ -37,18 +37,30 @@ namespace openspace {
/**
* A Horizons file is a text file generated from NASA JPL HORIZONS Website
- * (https://ssd.jpl.nasa.gov/horizons.cgi). The implementation that reads these files
- * expects a file with format:
+ * (https://ssd.jpl.nasa.gov/horizons.cgi). The implementation supports both Vector
+ * and Observer as Horizons data table
+ *
+ * In case of Vector table data the implementation expects a file with format:
+ * TIME(JulianDayNumber = A.D. YYYY-MM-DD HH:MM:SS TDB)
+ * X(km) Y(km) Z(km)
+ * TIME - Only the "YYYY-MM-DD HH:MM:SS" part is of interest, the rest is ignored
+ * X - X position in kilometers in Ecliptic J2000 reference frame
+ * Y - Y position in kilometers in Ecliptic J2000 reference frame
+ * Z - Z position in kilometers in Ecliptic J2000 reference frame
+ * Changes required in the "Table Settings" for compatible data:
+ * 1. Under "Select Output Quantities" choose option "Position components {x, y, z} only"
+ * 2. Uncheck the "Vector labels" options
+ *
+ * In case of Observer table data the implementation expects a file with format:
* TIME(YYYY-MM-DD HH:MM:SS) Range(km) GalLon(degrees) GalLat(degrees)
- * Range - The distance from target to observer. Enabled with "Observer range &
- * range-rate" (nr 20) in "Table Setting". This also generates a delta that must be
- * suppressed under "Additional Table Settings". User must set output settings to
- * kilometers under "Range units".
- * GalLon - Galactic Longitude. Enabled with "Galactic longitude & latitude" (nr 33) in
- * "Table Setting".
- * GalLat - Galactic Latitude. Output is by default set to degrees.
- * Make sure that no other settings are enables in the "Table Setting" than the ones
- * descripbed above
+ * Range - The distance from target to observer in kilometers
+ * GalLon - Galactic Longitude in degrees
+ * GalLat - Galactic Latitude in degrees
+ * Changes required in the "Table Settings" for compatible data:
+ * 1. Under "Observer Table Settings" uncheck all options except
+ * "Observer range & range-rate" and "Galactic longitude & latitude"
+ * 2. Change "Range units" to "kilometers (km)" instead of "astronomical units (au)"
+ * 3. Check the "Suppress range-rate" option
*/
class HorizonsFile {
public:
@@ -97,15 +109,18 @@ public:
HorizonsFile();
HorizonsFile(std::filesystem::path file);
+ HorizonsFile(std::filesystem::path filePath, const std::string& result);
void setFile(std::filesystem::path file);
const std::filesystem::path& file() const;
static std::string constructUrl(Type type, const std::string& target,
const std::string& observer, const std::string& startTime,
- const std::string& stopTime, const std::string& stepSize, const std::string& unit);
+ const std::string& stopTime, const std::string& stepSize,
+ const std::string& unit);
static ResultCode isValidAnswer(const json& answer);
+ bool isEmpty() const;
ResultCode isValidHorizonsFile() const;
void displayErrorMessage(const ResultCode code) const;
HorizonsResult readFile() const;
diff --git a/modules/space/translation/horizonstranslation.h b/modules/space/translation/horizonstranslation.h
index 5a23dd9d61..fa73028e61 100644
--- a/modules/space/translation/horizonstranslation.h
+++ b/modules/space/translation/horizonstranslation.h
@@ -41,7 +41,7 @@ namespace documentation { struct Documentation; }
/**
* The HorizonsTranslation is based on text files generated from NASA JPL HORIZONS Website
- * (https://ssd.jpl.nasa.gov/horizons.cgi). The implementation supports both Vector table
+ * (https://ssd.jpl.nasa.gov/horizons.cgi). The implementation supports both Vector
* and Observer as Horizons data table
*
* In case of Vector table data the implementation expects a file with format:
@@ -52,7 +52,7 @@ namespace documentation { struct Documentation; }
* Y - Y position in kilometers in Ecliptic J2000 reference frame
* Z - Z position in kilometers in Ecliptic J2000 reference frame
* Changes required in the "Table Settings" for compatible data:
- * 1. Under "Select Output Quantities" choose option "Position components {x, y, z} only
+ * 1. Under "Select Output Quantities" choose option "Position components {x, y, z} only"
* 2. Uncheck the "Vector labels" options
*
* In case of Observer table data the implementation expects a file with format: