mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-02-08 12:39:44 -06:00
665 lines
23 KiB
C++
665 lines
23 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2018 *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
|
* conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies *
|
|
* or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
****************************************************************************************/
|
|
|
|
#include <modules/solarbrowsing/util/spacecraftimagerymanager.h>
|
|
|
|
#include <openspace/engine/globals.h>
|
|
#include <openspace/engine/openspaceengine.h>
|
|
#include <openspace/json.h>
|
|
#include <openspace/rendering/transferfunction.h>
|
|
#include <openspace/util/spicemanager.h>
|
|
#include <openspace/util/timemanager.h>
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/misc/threadpool.h>
|
|
#include <ghoul/opengl/texture.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <chrono>
|
|
#include <format>
|
|
#include <fstream>
|
|
#include <future>
|
|
#include <string>
|
|
#include <iostream>
|
|
|
|
namespace {
|
|
constexpr const char* _loggerCat = "SpacecraftImageryManager";
|
|
constexpr const double SUN_RADIUS = 1391600000.0 * 0.5;
|
|
} // namespace
|
|
|
|
namespace openspace {
|
|
|
|
// Conversion needed before passing dates into the spice manager
|
|
std::string SpacecraftImageryManager::ISO8601(std::string& datetime) {
|
|
std::string month = datetime.substr(5, 3);
|
|
|
|
std::string MM = "";
|
|
if (month == "JAN") MM = "01";
|
|
else if (month == "FEB") MM = "02";
|
|
else if (month == "MAR") MM = "03";
|
|
else if (month == "APR") MM = "04";
|
|
else if (month == "MAY") MM = "05";
|
|
else if (month == "JUN") MM = "06";
|
|
else if (month == "JUL") MM = "07";
|
|
else if (month == "AUG") MM = "08";
|
|
else if (month == "SEP") MM = "09";
|
|
else if (month == "OCT") MM = "10";
|
|
else if (month == "NOV") MM = "11";
|
|
else if (month == "DEC") MM = "12";
|
|
else ghoul_assert(false, "Bad month");
|
|
|
|
datetime.replace(4, 5, "-" + MM + "-");
|
|
return datetime;
|
|
}
|
|
|
|
void SpacecraftImageryManager::loadTransferFunctions(const std::filesystem::path& dir,
|
|
std::unordered_map<std::string, std::shared_ptr<TransferFunction>>& tfMap)
|
|
{
|
|
|
|
if (!std::filesystem::is_directory(dir)) {
|
|
LERROR(std::format("Could not load directory '{}'", dir.string()));
|
|
}
|
|
|
|
std::vector<std::filesystem::path> sequencePaths = ghoul::filesystem::walkDirectory(
|
|
dir,
|
|
ghoul::filesystem::Recursive::Yes,
|
|
ghoul::filesystem::Sorted::Yes
|
|
);
|
|
|
|
for (const std::filesystem::path& seqPath : sequencePaths) {
|
|
if (seqPath.extension() == ".txt") {
|
|
std::string key = seqPath.stem().string();
|
|
tfMap[key] = std::make_shared<TransferFunction>(seqPath);
|
|
}
|
|
}
|
|
}
|
|
|
|
bool SpacecraftImageryManager::loadMetadataFromDisk(const std::filesystem::path& rootDir,
|
|
ImageMetadataMap& imageMetadataMap)
|
|
{
|
|
if (!std::filesystem::is_directory(rootDir)) {
|
|
throw ghoul::RuntimeError(std::format(
|
|
"Could not load directory '{}'", rootDir
|
|
));
|
|
}
|
|
|
|
bool metadataLoaded = false;
|
|
|
|
std::vector<std::filesystem::path> sequencePaths = ghoul::filesystem::walkDirectory(
|
|
rootDir,
|
|
ghoul::filesystem::Recursive::No,
|
|
ghoul::filesystem::Sorted::No
|
|
);
|
|
|
|
for (const std::filesystem::path& seqPath : sequencePaths) {
|
|
const std::string extension = seqPath.extension().string();
|
|
const std::string base = seqPath.filename().string();
|
|
const size_t foundCachedImageData = base.find("_cached");
|
|
if (extension != ".txt" || foundCachedImageData == std::string::npos) {
|
|
continue;
|
|
}
|
|
|
|
const size_t separator = base.rfind("_");
|
|
const std::string instrument = base.substr(0, separator);
|
|
LDEBUG(std::format("Loading instrument: {}", instrument));
|
|
|
|
metadataLoaded = true;
|
|
std::ifstream myfile(seqPath);
|
|
if (!myfile.is_open()) {
|
|
LERROR(std::format("Failed to open metadata file '{}'", seqPath));
|
|
return false;
|
|
}
|
|
|
|
int numStates;
|
|
myfile >> numStates;
|
|
|
|
for (int i = 0; i < numStates; i++) {
|
|
ImageMetadata im;
|
|
|
|
myfile >> std::ws; // Skip the rest of the line
|
|
std::string date;
|
|
std::getline(myfile, date);
|
|
|
|
if (date.empty()) {
|
|
LERROR(std::format(
|
|
"Failed to read metadata state: date, file: '{}'", seqPath
|
|
));
|
|
return false;
|
|
}
|
|
|
|
double timeObserved =
|
|
SpiceManager::ref().ephemerisTimeFromDate(ISO8601(date));
|
|
|
|
std::string relPath;
|
|
myfile >> relPath;
|
|
|
|
if (myfile.bad()) {
|
|
LERROR(std::format(
|
|
"Failed to read metadata state: relPath, file: '{}'", seqPath
|
|
));
|
|
return false;
|
|
}
|
|
|
|
im.filePath = rootDir / relPath;
|
|
|
|
myfile >> im.fullResolution;
|
|
|
|
if (myfile.bad()) {
|
|
LERROR(std::format(
|
|
"Failed to read metadata state: fullResolution, file: '{}'", seqPath
|
|
));
|
|
return false;
|
|
}
|
|
|
|
myfile >> im.scale;
|
|
|
|
if (myfile.bad()) {
|
|
LERROR(std::format(
|
|
"Failed to read metadata state: scale, file: '{}'", seqPath
|
|
));
|
|
return false;
|
|
}
|
|
|
|
float x, y;
|
|
myfile >> x >> y;
|
|
im.centerPixel = glm::vec2(x,y);
|
|
myfile >> im.isCoronaGraph;
|
|
|
|
if (myfile.bad()) {
|
|
LERROR(std::format(
|
|
"Failed to read metadata state : isCoronaGraph, file : '{}'", seqPath
|
|
));
|
|
return false;
|
|
}
|
|
|
|
imageMetadataMap[instrument].addKeyframe(timeObserved, std::move(im));
|
|
}
|
|
myfile.close();
|
|
}
|
|
return metadataLoaded;
|
|
}
|
|
|
|
void SpacecraftImageryManager::saveMetadataToDisk(const std::filesystem::path& rootPath,
|
|
const ImageMetadataMap& imageMetadataMap)
|
|
{
|
|
for (const auto& [instrument, sequence] : imageMetadataMap) {
|
|
const std::filesystem::path cacheFile =
|
|
rootPath / std::format("{}_cached.txt", instrument);
|
|
|
|
std::ofstream ofs(cacheFile);
|
|
if (!ofs.is_open()) {
|
|
LERROR(std::format("Failed to open file '{}'", cacheFile));
|
|
continue;
|
|
}
|
|
|
|
ofs << sequence.nKeyframes() << '\n';
|
|
|
|
for (const Keyframe<ImageMetadata>& metadata : sequence.keyframes()) {
|
|
const std::string date = SpiceManager::ref().dateFromEphemerisTime(
|
|
metadata.timestamp
|
|
);
|
|
|
|
const ImageMetadata& im = metadata.data;
|
|
const std::filesystem::path relativePath = std::filesystem::relative(
|
|
im.filePath,
|
|
rootPath
|
|
);
|
|
|
|
ofs << std::format("{}\n{}\n{}\n{}\n{}\n{}\n{}\n",
|
|
date,
|
|
relativePath.generic_string(),
|
|
im.fullResolution,
|
|
im.scale,
|
|
im.centerPixel.x,
|
|
im.centerPixel.y,
|
|
static_cast<int>(im.isCoronaGraph) // Output bool as 0/1
|
|
);
|
|
}
|
|
ofs.close();
|
|
}
|
|
}
|
|
|
|
// @TODO emiax: If openjpeg ever starts supporting reading XML metadata,
|
|
// this implementation should be improved in order not to search the entire buffer for
|
|
// XML data. There is an issue here:
|
|
// (https://github.com/uclouvain/openjpeg/issues/929)
|
|
std::optional<ImageMetadata> SpacecraftImageryManager::parseJ2kMetadata(
|
|
const std::filesystem::path& filePath)
|
|
{
|
|
ImageMetadata im;
|
|
im.filePath = filePath;
|
|
|
|
std::ifstream stream(filePath, std::ios::binary | std::ios::ate);
|
|
std::streamsize size = stream.tellg();
|
|
stream.seekg(0, std::ios::beg);
|
|
std::vector<char> buffer(size);
|
|
if (!stream.read(buffer.data(), size)) {
|
|
LERROR(std::format("Failed to read data from '{}' ", filePath));
|
|
return im;
|
|
}
|
|
std::string_view bufferView(buffer.data(), size);
|
|
|
|
auto extractInnerXml = [](std::string_view view, const std::string& elementName) ->
|
|
std::optional<std::string_view>
|
|
{
|
|
const std::string startTag = std::format("<{}>", elementName);
|
|
const std::string endTag = std::format("</{}>", elementName);
|
|
|
|
const auto begin = std::search(
|
|
view.begin(),
|
|
view.end(),
|
|
startTag.begin(),
|
|
startTag.end()
|
|
);
|
|
|
|
if (begin == view.end()) {
|
|
return std::nullopt;
|
|
; }
|
|
|
|
const auto afterBeginTag = begin + startTag.size();
|
|
|
|
const auto end = std::search(
|
|
afterBeginTag,
|
|
view.end(),
|
|
endTag.begin(),
|
|
endTag.end()
|
|
);
|
|
|
|
if (end == view.end()) {
|
|
return std::nullopt;
|
|
}
|
|
|
|
return std::string_view(&*afterBeginTag, end - afterBeginTag);
|
|
};
|
|
|
|
std::optional<std::string_view> metaData = extractInnerXml(bufferView, "meta");
|
|
|
|
if (!metaData.has_value()) {
|
|
LERROR(std::format("Could not find metadata in {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::string_view> telescop = extractInnerXml(
|
|
metaData.value(),
|
|
"TELESCOP"
|
|
);
|
|
|
|
if (!telescop.has_value()) {
|
|
LERROR(std::format("Could not find TELESCOP tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::string_view> naxis = extractInnerXml(
|
|
metaData.value(),
|
|
"NAXIS1"
|
|
);
|
|
|
|
if (!naxis.has_value()) {
|
|
LERROR(std::format("Could not find NAXIS1 tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::string_view> centerPixelX = extractInnerXml(
|
|
bufferView,
|
|
"CRPIX1"
|
|
);
|
|
|
|
if (!centerPixelX.has_value()) {
|
|
LERROR(std::format("Could not find CRPIX1 tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
|
|
std::optional<std::string_view> centerPixelY = extractInnerXml(
|
|
bufferView,
|
|
"CRPIX2"
|
|
);
|
|
|
|
if (!centerPixelY.has_value()) {
|
|
LERROR(std::format("Could not find CRPIX2 tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
|
|
im.fullResolution = std::stoi(std::string(naxis.value()));
|
|
const float halfRes = im.fullResolution / 2.f;
|
|
|
|
glm::vec2 centerPixel;
|
|
centerPixel.x = std::stof(std::string(centerPixelX.value()));
|
|
centerPixel.y = std::stof(std::string(centerPixelY.value()));
|
|
const glm::vec2 offset = ((halfRes - centerPixel) / halfRes) * glm::vec2(SUN_RADIUS);
|
|
im.centerPixel = offset;
|
|
|
|
if (telescop.value() == "SOHO") {
|
|
std::optional<std::string_view> plateScl = extractInnerXml(
|
|
metaData.value(),
|
|
"PLATESCL"
|
|
);
|
|
|
|
if (!plateScl.has_value()) {
|
|
LERROR(std::format("Could not find NAXIS1 tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
|
|
const float plateScale = std::stof(std::string(plateScl.value()));
|
|
im.scale = 1.f / (plateScale / 2.f);
|
|
im.isCoronaGraph = true;
|
|
}
|
|
else if (telescop.value() == "SDO") {
|
|
std::optional<std::string_view> rsunObs = extractInnerXml(bufferView, "RSUN_OBS");
|
|
std::optional<std::string_view> cDelt1 = extractInnerXml(bufferView, "CDELT1");
|
|
|
|
if (!rsunObs.has_value()) {
|
|
LERROR(std::format("Could not find RSUN_OBS tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
if (!cDelt1.has_value()) {
|
|
LERROR(std::format("Could not find CDELT1 tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
|
|
const float rSunObsValue = std::stof(std::string(rsunObs.value()));
|
|
const float cDelt1Value = std::stof(std::string(cDelt1.value()));
|
|
im.scale = (rSunObsValue / cDelt1Value) / (im.fullResolution / 2.f);
|
|
im.isCoronaGraph = false;
|
|
}
|
|
else { // Telescope is assumed to be STEREO
|
|
std::optional<std::string_view> rsun = extractInnerXml(bufferView, "RSUN");
|
|
std::optional<std::string_view> cDelt1 = extractInnerXml(bufferView, "CDELT1");
|
|
|
|
if (!rsun.has_value()) {
|
|
LERROR(std::format("Could not find RSUN_OBS tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
if (!cDelt1.has_value()) {
|
|
LERROR(std::format("Could not find CDELT1 tag {}", filePath));
|
|
return std::nullopt;
|
|
}
|
|
|
|
const float rSunvalue = std::stof(std::string(rsun.value()));
|
|
const float cDelt1Value = std::stof(std::string(cDelt1.value()));
|
|
im.scale = (rSunvalue / cDelt1Value) / (im.fullResolution / 2.f);
|
|
im.isCoronaGraph = false;
|
|
|
|
std::optional<std::string_view> detector = extractInnerXml(bufferView, "DETECTOR");
|
|
|
|
if (detector.has_value()) {
|
|
im.isCoronaGraph =
|
|
detector.value() == "COR1" || detector.value() == "COR2";
|
|
}
|
|
else {
|
|
LWARNING(std::format(
|
|
"Could not find DETECTOR tag {}", filePath
|
|
));
|
|
}
|
|
}
|
|
return im;
|
|
}
|
|
|
|
// This is currently not used. Instead, the parseJ2kMetadata is used,
|
|
// extracting the data directoy from the JPEG2000 file by naively searching the entire
|
|
// buffer for metadata, avoiding pre-processing steps.
|
|
// If you want to use this, you need to extract metadata to json first,
|
|
// for example using: https://github.com/novalain/j2kcodec
|
|
//ImageMetadata SpacecraftImageryManager::parseJsonMetadata(
|
|
// const ghoul::filesystem::File& file)
|
|
//{
|
|
// ImageMetadata im;
|
|
//im.filename = file.path().string();
|
|
//const std::string filename = std::string(file.path().string() + ".json");
|
|
|
|
//if (!std::filesystem::exists(filename)) {
|
|
// LERROR(std::format("'{}' has no specified json metadata", file.path()));
|
|
// // TODO: Hardcoded values, when there are no json files.
|
|
// return im;
|
|
//}
|
|
|
|
//// Parse JSON metadata
|
|
//using json = nlohmann::json;
|
|
|
|
//std::ifstream i(filename);
|
|
//if (i.fail()) {
|
|
// LERROR(std::format("Error opening file '{}'", filename));
|
|
//}
|
|
|
|
//json j;
|
|
//i >> j;
|
|
//const json data = j["meta"]["fits"];
|
|
|
|
//if (data.is_null()) {
|
|
// LERROR(std::format("Error in metadata '{}'", filename));
|
|
//}
|
|
|
|
//json value = data["TELESCOP"];
|
|
//if (value.is_null() && !value.is_string()) {
|
|
// LERROR("Metadata did contain information about type of spacecraft");
|
|
// return im;
|
|
//}
|
|
//// TODO: value might not exist
|
|
//std::string spacecraftType = value;
|
|
|
|
//value = data["NAXIS1"];
|
|
//if (value.is_null()) {
|
|
// LERROR("Metadata did not contain information about resolution");
|
|
//}
|
|
|
|
//const std::string sFullResolution = value;
|
|
//im.fullResolution = std::stoi(sFullResolution);
|
|
//// Special case of sdo - RSUN is given in pixels
|
|
//// For SOHO the radius of the sun is not specified - we instead use platescl
|
|
//if (spacecraftType == "SOHO") {
|
|
// const std::string sScale = data["PLATESCL"];
|
|
// const float plateScale = stof(sScale);
|
|
// im.scale = 1.f / (plateScale / 2.f);
|
|
// im.isCoronaGraph = true;
|
|
//} else {
|
|
// float sunRadiusPixels = 0.f;
|
|
// // SDO has RSUN specified in pixels
|
|
// if (spacecraftType == "SDO") {
|
|
// value = data["RSUN_OBS"];
|
|
// if (value.is_null()) {
|
|
// LERROR("SDO Metadata: RSUN_OBS missing!");
|
|
// }
|
|
// std::string sSunRadiusArcsec = value;
|
|
// value = data["CDELT1"];
|
|
// if (value.is_null()) {
|
|
// LERROR("SDO Metadata: CDELT1 missing!");
|
|
// }
|
|
// std::string sCdelt1 = value;
|
|
// const float cdelt1 = stof(sCdelt1);
|
|
// const float sunRadiusArcsec = stof(sSunRadiusArcsec);
|
|
// sunRadiusPixels = sunRadiusArcsec / cdelt1;
|
|
// im.isCoronaGraph = false;
|
|
// } else {
|
|
// // STEREO has RSUN specified in arcsecs - need to divide by factor
|
|
// std::string sCdelt1 = data["CDELT1"];
|
|
// const float cdelt1 = stof(sCdelt1);
|
|
// std::string sSunRadiusArcsec = data["RSUN"];
|
|
// const float sunRadiusArcsec = stof(sSunRadiusArcsec);
|
|
// sunRadiusPixels = sunRadiusArcsec / cdelt1;
|
|
|
|
// value = data["DETECTOR"];
|
|
// if (value.is_null()) {
|
|
// LERROR("No observer specified in Stereo");
|
|
// }
|
|
|
|
// std::string sObserver = value;
|
|
// if (sObserver == "COR1" || sObserver == "COR2") {
|
|
// im.isCoronaGraph = true;
|
|
// } else {
|
|
// im.isCoronaGraph = false;
|
|
// }
|
|
// }
|
|
// float scale = sunRadiusPixels / (im.fullResolution / 2.f);
|
|
// im.scale = scale;
|
|
//}
|
|
|
|
//const std::string sCenterPixelX = data["CRPIX1"];
|
|
//const std::string sCenterPixelY = data["CRPIX2"];
|
|
|
|
//const float centerPixelX = stof(sCenterPixelX);
|
|
//const float centerPixelY = stof(sCenterPixelY);
|
|
//const float halfRes = im.fullResolution / 2.f;
|
|
|
|
//const float offsetX = ((halfRes - centerPixelX) / halfRes) * SUN_RADIUS;
|
|
//const float offsetY = ((halfRes - centerPixelY) / halfRes) * SUN_RADIUS;
|
|
|
|
//im.centerPixel = glm::vec2(offsetX, offsetY);
|
|
|
|
// return im;
|
|
//}
|
|
|
|
void SpacecraftImageryManager::loadImageMetadata(const std::filesystem::path& rootDir,
|
|
ImageMetadataMap& imageMetadataMap)
|
|
{
|
|
if (!std::filesystem::is_directory(rootDir)) {
|
|
throw ghoul::RuntimeError(std::format(
|
|
"Could not load directory '{}'", rootDir
|
|
));
|
|
}
|
|
|
|
LDEBUG("Begin loading spacecraft imagery metadata");
|
|
ImageMetadataMap result;
|
|
// Pre-processed data
|
|
if (loadMetadataFromDisk(rootDir, result)) {
|
|
imageMetadataMap.insert(result.begin(), result.end());
|
|
return;
|
|
}
|
|
|
|
LDEBUG("Loading sequence directory");
|
|
std::vector<std::filesystem::path> sequencePaths = ghoul::filesystem::walkDirectory(
|
|
rootDir,
|
|
ghoul::filesystem::Recursive::Yes,
|
|
ghoul::filesystem::Sorted::Yes
|
|
);
|
|
|
|
LDEBUG("Filtering data values");
|
|
sequencePaths.erase(
|
|
std::remove_if(
|
|
sequencePaths.begin(),
|
|
sequencePaths.end(),
|
|
[](const std::filesystem::path& path) {
|
|
const std::string& ext = path.extension().string();
|
|
return (ext != ".jp2") && (ext != ".j2k");
|
|
}
|
|
),
|
|
sequencePaths.end()
|
|
);
|
|
|
|
// TODO anden 2026-02-04
|
|
// Steps to validate cache:
|
|
// 1. Check that all files in the cache still exists
|
|
// 2. Remove any entry from cache that doesn't have a valid path
|
|
// 3. Read new files:
|
|
// 3.5 Filter away any file already in the cache
|
|
// 4. Add new files to cache
|
|
|
|
|
|
// TODO anden 2026-02-04
|
|
// Steps for streaming new image data
|
|
// 1. Check if image exists in cache
|
|
// 2. If not -> spawn a thread to download it
|
|
// 3. once downloaded put it through the normal pipeline of storing the file in
|
|
// correct folder.
|
|
// 4. Add the image data to the cache (file and in memory)
|
|
|
|
|
|
LDEBUG("Reading metadata");
|
|
size_t count = 0;
|
|
|
|
std::mutex spiceAndPushMutex;
|
|
|
|
std::vector<std::future<void>> futures;
|
|
futures.reserve(sequencePaths.size());
|
|
|
|
std::cout << '\n';
|
|
auto exec = [&](const std::filesystem::path& seqPath) {
|
|
// An example image has the following naming scheme:
|
|
// 2024_05_08__00_58_23_814__SDO_AIA-211.jp2
|
|
std::string fileName = seqPath.stem().string();
|
|
size_t posSatelliteInfoStart = fileName.rfind("__") + 2;
|
|
std::string satelliteInfo = fileName.substr(posSatelliteInfoStart); // e.g., SDO_AIA-211
|
|
|
|
// Name
|
|
size_t posSatelliteNameEnd = satelliteInfo.find_first_of("_");
|
|
//std::string satelliteName = satelliteInfo.substr(0, posSatelliteNameEnd); // e.g., SDO
|
|
|
|
// Instrument
|
|
size_t posInstrumentNameStart = posSatelliteNameEnd + 1;
|
|
std::string instrumentName = satelliteInfo.substr(posInstrumentNameStart); // e.g., AIA-211
|
|
|
|
int year, month, day, hour, minute, second, millisecond;
|
|
|
|
int scanned = std::sscanf(fileName.c_str(), "%d_%d_%d__%d_%d_%d_%d",
|
|
&year, &month, &day, &hour, &minute, &second, &millisecond
|
|
);
|
|
|
|
if (scanned == 7) {
|
|
std::string dateTime = std::format(
|
|
"{:04}-{:02}-{:02}T{:02}:{:02}:{:02}.{:03}",
|
|
year, month, day,
|
|
hour, minute, second, millisecond
|
|
);
|
|
|
|
std::optional<ImageMetadata> im = parseJ2kMetadata(seqPath);
|
|
if (im.has_value()) {
|
|
spiceAndPushMutex.lock();
|
|
result[instrumentName].addKeyframe(
|
|
global::timeManager->time().convertTime(dateTime),
|
|
std::move(im.value())
|
|
);
|
|
spiceAndPushMutex.unlock();
|
|
}
|
|
else {
|
|
LERROR(std::format(
|
|
"Failed to parse J2K metadata from file '{}'",
|
|
seqPath
|
|
));
|
|
}
|
|
}
|
|
else {
|
|
LERROR(std::format("Failed to parse date '{}' from file '{}'",
|
|
fileName, seqPath
|
|
));
|
|
}
|
|
++count;
|
|
|
|
if (count % 250 == 0) {
|
|
LINFO(std::format(
|
|
"Processing image {} out of {} ", count, sequencePaths.size()
|
|
));
|
|
}
|
|
};
|
|
|
|
for (const std::filesystem::path& seqPath : sequencePaths) {
|
|
exec(seqPath);
|
|
}
|
|
|
|
LDEBUG("Finish loading imagery metadata");
|
|
LDEBUG("Saving imagery metadata");
|
|
saveMetadataToDisk(rootDir, result);
|
|
|
|
imageMetadataMap.insert(result.begin(), result.end());
|
|
LDEBUG(std::format("{} images loaded", count));
|
|
LDEBUG(std::format("{} values in metamap", imageMetadataMap.size()));
|
|
}
|
|
|
|
} //namespace openspace
|