mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-08 20:50:19 -06:00
1392 lines
47 KiB
C++
1392 lines
47 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2019 *
|
|
* *
|
|
* 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/globebrowsing/src/tileprovider.h>
|
|
|
|
#include <modules/globebrowsing/globebrowsingmodule.h>
|
|
#include <modules/globebrowsing/src/asynctiledataprovider.h>
|
|
#include <modules/globebrowsing/src/geodeticpatch.h>
|
|
#include <modules/globebrowsing/src/layermanager.h>
|
|
#include <modules/globebrowsing/src/memoryawaretilecache.h>
|
|
#include <modules/globebrowsing/src/rawtiledatareader.h>
|
|
#include <openspace/engine/globals.h>
|
|
#include <openspace/engine/moduleengine.h>
|
|
#include <openspace/util/factorymanager.h>
|
|
#include <openspace/util/timemanager.h>
|
|
#include <ghoul/filesystem/file.h>
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/font/fontmanager.h>
|
|
#include <ghoul/font/fontrenderer.h>
|
|
#include <ghoul/io/texture/texturereader.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <fstream>
|
|
#include "cpl_minixml.h"
|
|
|
|
namespace ghoul {
|
|
template <>
|
|
openspace::globebrowsing::tileprovider::TemporalTileProvider::TimeFormatType
|
|
from_string(const std::string& string)
|
|
{
|
|
using namespace openspace::globebrowsing::tileprovider;
|
|
if (string == "YYYY-MM-DD") {
|
|
return TemporalTileProvider::TimeFormatType::YYYY_MM_DD;
|
|
}
|
|
else if (string == "YYYY-MM-DDThh:mm:ssZ") {
|
|
return TemporalTileProvider::TimeFormatType::YYYY_MM_DDThhColonmmColonssZ;
|
|
}
|
|
else if (string == "YYYY-MM-DDThh_mm_ssZ") {
|
|
return TemporalTileProvider::TimeFormatType::YYYY_MM_DDThh_mm_ssZ;
|
|
}
|
|
else if (string == "YYYYMMDD_hhmmss") {
|
|
return TemporalTileProvider::TimeFormatType::YYYYMMDD_hhmmss;
|
|
}
|
|
else if (string == "YYYYMMDD_hhmm") {
|
|
return TemporalTileProvider::TimeFormatType::YYYYMMDD_hhmm;
|
|
}
|
|
else {
|
|
throw ghoul::RuntimeError("Unknown timeformat " + string);
|
|
}
|
|
}
|
|
} // namespace ghoul
|
|
|
|
|
|
namespace openspace::globebrowsing::tileprovider {
|
|
|
|
namespace {
|
|
|
|
std::unique_ptr<ghoul::opengl::Texture> DefaultTileTexture;
|
|
Tile DefaultTile = Tile{ nullptr, std::nullopt, Tile::Status::Unavailable };
|
|
|
|
constexpr const char* KeyFilePath = "FilePath";
|
|
|
|
namespace defaultprovider {
|
|
constexpr const char* KeyPerformPreProcessing = "PerformPreProcessing";
|
|
constexpr const char* KeyTilePixelSize = "TilePixelSize";
|
|
constexpr const char* KeyPadTiles = "PadTiles";
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo FilePathInfo = {
|
|
"FilePath",
|
|
"File Path",
|
|
"The path of the GDAL file or the image file that is to be used in this tile "
|
|
"provider."
|
|
};
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo TilePixelSizeInfo = {
|
|
"TilePixelSize",
|
|
"Tile Pixel Size",
|
|
"This value is the preferred size (in pixels) for each tile. Choosing the right "
|
|
"value is a tradeoff between more efficiency (larger images) and better quality "
|
|
"(smaller images). The tile pixel size has to be smaller than the size of the "
|
|
"complete image if a single image is used."
|
|
};
|
|
} // namespace defaultprovider
|
|
|
|
namespace singleimageprovider {
|
|
constexpr openspace::properties::Property::PropertyInfo FilePathInfo = {
|
|
"FilePath",
|
|
"File Path",
|
|
"The file path that is used for this image provider. The file must point to an "
|
|
"image that is then loaded and used for all tiles."
|
|
};
|
|
} // namespace singleimageprovider
|
|
|
|
namespace sizereferenceprovider {
|
|
constexpr const char* KeyRadii = "Radii";
|
|
} // namespace sizereferenceprovider
|
|
|
|
namespace byindexprovider {
|
|
constexpr const char* KeyDefaultProvider = "DefaultProvider";
|
|
constexpr const char* KeyProviders = "IndexTileProviders";
|
|
constexpr const char* KeyTileIndex = "TileIndex";
|
|
constexpr const char* KeyTileProvider = "TileProvider";
|
|
} // namespace byindexprovider
|
|
|
|
namespace bylevelprovider {
|
|
constexpr const char* KeyProviders = "LevelTileProviders";
|
|
constexpr const char* KeyMaxLevel = "MaxLevel";
|
|
constexpr const char* KeyTileProvider = "TileProvider";
|
|
constexpr const char* KeyLayerGroupID = "LayerGroupID";
|
|
} // namespace bylevelprovider
|
|
|
|
namespace temporal {
|
|
constexpr const char* KeyBasePath = "BasePath";
|
|
|
|
constexpr const char* UrlTimePlaceholder = "${OpenSpaceTimeId}";
|
|
constexpr const char* TimeStart = "OpenSpaceTimeStart";
|
|
constexpr const char* TimeEnd = "OpenSpaceTimeEnd";
|
|
constexpr const char* TimeResolution = "OpenSpaceTimeResolution";
|
|
constexpr const char* TimeFormat = "OpenSpaceTimeIdFormat";
|
|
|
|
constexpr openspace::properties::Property::PropertyInfo FilePathInfo = {
|
|
"FilePath",
|
|
"File Path",
|
|
"This is the path to the XML configuration file that describes the temporal tile "
|
|
"information."
|
|
};
|
|
} // namespace temporal
|
|
|
|
|
|
//
|
|
// DefaultTileProvider
|
|
//
|
|
|
|
void initAsyncTileDataReader(DefaultTileProvider& t, TileTextureInitData initData) {
|
|
t.asyncTextureDataProvider = std::make_unique<AsyncTileDataProvider>(
|
|
t.name,
|
|
std::make_unique<RawTileDataReader>(
|
|
t.filePath,
|
|
initData,
|
|
RawTileDataReader::PerformPreprocessing(t.performPreProcessing)
|
|
)
|
|
);
|
|
}
|
|
|
|
bool initTexturesFromLoadedData(DefaultTileProvider& t) {
|
|
if (t.asyncTextureDataProvider) {
|
|
std::optional<RawTile> tile = t.asyncTextureDataProvider->popFinishedRawTile();
|
|
if (tile) {
|
|
const cache::ProviderTileKey key = { tile->tileIndex, t.uniqueIdentifier };
|
|
ghoul_assert(!t.tileCache->exist(key), "Tile must not be existing in cache");
|
|
t.tileCache->createTileAndPut(key, std::move(tile.value()));
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//
|
|
// TextTileProvider
|
|
//
|
|
|
|
void initialize(TextTileProvider& t) {
|
|
t.font = global::fontManager.font("Mono", static_cast<float>(t.fontSize));
|
|
t.fontRenderer = ghoul::fontrendering::FontRenderer::createDefault();
|
|
t.fontRenderer->setFramebufferSize(glm::vec2(t.initData.dimensions));
|
|
glGenFramebuffers(1, &t.fbo);
|
|
}
|
|
|
|
void deinitialize(TextTileProvider& t) {
|
|
glDeleteFramebuffers(1, &t.fbo);
|
|
}
|
|
|
|
Tile tile(TextTileProvider& t, const TileIndex& tileIndex) {
|
|
cache::ProviderTileKey key = { tileIndex, t.uniqueIdentifier };
|
|
Tile tile = t.tileCache->get(key);
|
|
if (!tile.texture) {
|
|
ghoul::opengl::Texture* texture = t.tileCache->texture(t.initData);
|
|
|
|
// Keep track of defaultFBO and viewport to be able to reset state when done
|
|
GLint defaultFBO;
|
|
GLint viewport[4];
|
|
glGetIntegerv(GL_FRAMEBUFFER_BINDING, &defaultFBO);
|
|
glGetIntegerv(GL_VIEWPORT, viewport);
|
|
|
|
// Render to texture
|
|
glBindFramebuffer(GL_FRAMEBUFFER, t.fbo);
|
|
glFramebufferTexture2D(
|
|
GL_FRAMEBUFFER,
|
|
GL_COLOR_ATTACHMENT0,
|
|
GL_TEXTURE_2D,
|
|
*texture,
|
|
0
|
|
);
|
|
|
|
GLsizei w = static_cast<GLsizei>(texture->width());
|
|
GLsizei h = static_cast<GLsizei>(texture->height());
|
|
glViewport(0, 0, w, h);
|
|
glClearColor(0.f, 0.f, 0.f, 0.f);
|
|
glClear(GL_COLOR_BUFFER_BIT);
|
|
|
|
t.fontRenderer->render(*t.font, t.textPosition, t.text, t.textColor);
|
|
|
|
glBindFramebuffer(GL_FRAMEBUFFER, defaultFBO);
|
|
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
|
|
|
tile = Tile{ texture, std::nullopt, Tile::Status::OK };
|
|
t.tileCache->put(key, t.initData.hashKey, tile);
|
|
}
|
|
return tile;
|
|
}
|
|
|
|
void reset(TextTileProvider& t) {
|
|
t.tileCache->clear();
|
|
}
|
|
|
|
|
|
//
|
|
// TileProviderByLevel
|
|
//
|
|
|
|
TileProvider* levelProvider(TileProviderByLevel& t, int level) {
|
|
if (!t.levelTileProviders.empty()) {
|
|
int clampedLevel = glm::clamp(
|
|
level,
|
|
0,
|
|
static_cast<int>(t.providerIndices.size() - 1)
|
|
);
|
|
int idx = t.providerIndices[clampedLevel];
|
|
return t.levelTileProviders[idx].get();
|
|
}
|
|
else {
|
|
return nullptr;
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// TemporalTileProvider
|
|
//
|
|
|
|
std::string timeStringify(TemporalTileProvider::TimeFormatType type, const Time& t) {
|
|
switch (type) {
|
|
case TemporalTileProvider::TimeFormatType::YYYY_MM_DD:
|
|
return t.ISO8601().substr(0, 10);
|
|
case TemporalTileProvider::TimeFormatType::YYYYMMDD_hhmmss: {
|
|
std::string ts = t.ISO8601().substr(0, 19);
|
|
|
|
// YYYY_MM_DDThh_mm_ss -> YYYYMMDD_hhmmss
|
|
ts.erase(std::remove(ts.begin(), ts.end(), '-'), ts.end());
|
|
ts.erase(std::remove(ts.begin(), ts.end(), ':'), ts.end());
|
|
replace(ts.begin(), ts.end(), 'T', '_');
|
|
return ts;
|
|
}
|
|
case TemporalTileProvider::TimeFormatType::YYYYMMDD_hhmm: {
|
|
std::string ts = t.ISO8601().substr(0, 16);
|
|
|
|
// YYYY_MM_DDThh_mm -> YYYYMMDD_hhmm
|
|
ts.erase(std::remove(ts.begin(), ts.end(), '-'), ts.end());
|
|
ts.erase(std::remove(ts.begin(), ts.end(), ':'), ts.end());
|
|
replace(ts.begin(), ts.end(), 'T', '_');
|
|
return ts;
|
|
}
|
|
case TemporalTileProvider::TimeFormatType::YYYY_MM_DDThhColonmmColonssZ:
|
|
return t.ISO8601().substr(0, 19) + "Z";
|
|
case TemporalTileProvider::TimeFormatType::YYYY_MM_DDThh_mm_ssZ: {
|
|
std::string timeString = t.ISO8601().substr(0, 19) + "Z";
|
|
replace(timeString.begin(), timeString.end(), ':', '_');
|
|
return timeString;
|
|
}
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<TileProvider> initTileProvider(TemporalTileProvider& t,
|
|
const TemporalTileProvider::TimeKey& timekey)
|
|
{
|
|
static const std::vector<std::string> IgnoredTokens = {
|
|
// From: http://www.gdal.org/frmt_wms.html
|
|
"${x}",
|
|
"${y}",
|
|
"${z}",
|
|
"${version}",
|
|
"${format}",
|
|
"${layer}"
|
|
};
|
|
|
|
|
|
std::string xmlTemplate(t.gdalXmlTemplate);
|
|
const size_t pos = xmlTemplate.find(temporal::UrlTimePlaceholder);
|
|
const size_t numChars = strlen(temporal::UrlTimePlaceholder);
|
|
// @FRAGILE: This will only find the first instance. Dangerous if that instance is
|
|
// commented out ---abock
|
|
const std::string timeSpecifiedXml = xmlTemplate.replace(pos, numChars, timekey);
|
|
std::string gdalDatasetXml = timeSpecifiedXml;
|
|
|
|
FileSys.expandPathTokens(gdalDatasetXml, IgnoredTokens);
|
|
|
|
t.initDict.setValue<std::string>(KeyFilePath, gdalDatasetXml);
|
|
return std::make_unique<DefaultTileProvider>(t.initDict);
|
|
}
|
|
|
|
TileProvider* getTileProvider(TemporalTileProvider& t,
|
|
const TemporalTileProvider::TimeKey& timekey)
|
|
{
|
|
const auto it = t.tileProviderMap.find(timekey);
|
|
if (it != t.tileProviderMap.end()) {
|
|
return it->second.get();
|
|
}
|
|
else {
|
|
std::unique_ptr<TileProvider> tileProvider = initTileProvider(t, timekey);
|
|
initialize(*tileProvider);
|
|
|
|
TileProvider* res = tileProvider.get();
|
|
t.tileProviderMap[timekey] = std::move(tileProvider);
|
|
return res;
|
|
}
|
|
}
|
|
|
|
TileProvider* getTileProvider(TemporalTileProvider& t, const Time& time) {
|
|
Time tCopy(time);
|
|
if (t.timeQuantizer.quantize(tCopy, true)) {
|
|
TemporalTileProvider::TimeKey timeKey = timeStringify(t.timeFormat, tCopy);
|
|
try {
|
|
return getTileProvider(t, timeKey);
|
|
}
|
|
catch (const ghoul::RuntimeError& e) {
|
|
LERRORC("TemporalTileProvider", e.message);
|
|
return nullptr;
|
|
}
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
void ensureUpdated(TemporalTileProvider& t) {
|
|
if (!t.currentTileProvider) {
|
|
update(t);
|
|
}
|
|
}
|
|
|
|
std::string xmlValue(TemporalTileProvider& t, CPLXMLNode* node, const std::string& key,
|
|
const std::string& defaultVal)
|
|
{
|
|
CPLXMLNode* n = CPLSearchXMLNode(node, key.c_str());
|
|
if (!n) {
|
|
throw ghoul::RuntimeError(
|
|
fmt::format("Unable to parse file {}. {} missing", t.filePath.value(), key)
|
|
);
|
|
}
|
|
|
|
const bool hasValue = n && n->psChild && n->psChild->pszValue;
|
|
return hasValue ? n->psChild->pszValue : defaultVal;
|
|
}
|
|
|
|
std::string consumeTemporalMetaData(TemporalTileProvider& t, const std::string& xml) {
|
|
CPLXMLNode* node = CPLParseXMLString(xml.c_str());
|
|
|
|
std::string timeStart = xmlValue(t, node, temporal::TimeStart, "2000 Jan 1");
|
|
std::string timeResolution = xmlValue(t, node, temporal::TimeResolution, "2d");
|
|
std::string timeEnd = xmlValue(t, node, temporal::TimeEnd, "Today");
|
|
std::string timeIdFormat = xmlValue(
|
|
t,
|
|
node,
|
|
temporal::TimeFormat,
|
|
"YYYY-MM-DDThh:mm:ssZ"
|
|
);
|
|
|
|
Time start;
|
|
start.setTime(std::move(timeStart));
|
|
Time end(Time::now());
|
|
if (timeEnd == "Yesterday") {
|
|
end.advanceTime(-60.0 * 60.0 * 24.0); // Go back one day
|
|
}
|
|
else if (timeEnd != "Today") {
|
|
end.setTime(std::move(timeEnd));
|
|
}
|
|
|
|
try {
|
|
t.timeQuantizer = TimeQuantizer(start, end, timeResolution);
|
|
}
|
|
catch (const ghoul::RuntimeError& e) {
|
|
throw ghoul::RuntimeError(fmt::format(
|
|
"Could not create time quantizer for Temporal GDAL dataset '{}'. {}",
|
|
t.filePath.value(), e.message
|
|
));
|
|
}
|
|
t.timeFormat = ghoul::from_string<TemporalTileProvider::TimeFormatType>(timeIdFormat);
|
|
|
|
CPLXMLNode* gdalNode = CPLSearchXMLNode(node, "GDAL_WMS");
|
|
if (gdalNode) {
|
|
std::string gdalDescription = CPLSerializeXMLTree(gdalNode);
|
|
return gdalDescription;
|
|
}
|
|
else {
|
|
gdalNode = CPLSearchXMLNode(node, "FilePath");
|
|
std::string gdalDescription = std::string(gdalNode->psChild->pszValue);
|
|
return gdalDescription;
|
|
}
|
|
}
|
|
|
|
bool readFilePath(TemporalTileProvider& t) {
|
|
std::ifstream in(t.filePath.value().c_str());
|
|
std::string xml;
|
|
if (in.is_open()) {
|
|
// read file
|
|
xml = std::string(
|
|
std::istreambuf_iterator<char>(in),
|
|
(std::istreambuf_iterator<char>())
|
|
);
|
|
}
|
|
else {
|
|
// Assume that it is already an xml
|
|
xml = t.filePath;
|
|
}
|
|
|
|
// File path was not a path to a file but a GDAL config or empty
|
|
ghoul::filesystem::File f(t.filePath);
|
|
if (FileSys.fileExists(f)) {
|
|
t.initDict.setValue<std::string>(temporal::KeyBasePath, f.directoryName());
|
|
}
|
|
|
|
t.gdalXmlTemplate = consumeTemporalMetaData(t, xml);
|
|
return true;
|
|
}
|
|
|
|
} // namespace
|
|
|
|
unsigned int TileProvider::NumTileProviders = 0;
|
|
|
|
|
|
//
|
|
// General functions
|
|
//
|
|
void initializeDefaultTile() {
|
|
ghoul_assert(!DefaultTile.texture, "Default tile should not have been created");
|
|
using namespace ghoul::opengl;
|
|
|
|
// Create pixel data
|
|
TileTextureInitData initData(
|
|
8,
|
|
8,
|
|
GL_UNSIGNED_BYTE,
|
|
Texture::Format::RGBA,
|
|
TileTextureInitData::PadTiles::No,
|
|
TileTextureInitData::ShouldAllocateDataOnCPU::Yes
|
|
);
|
|
char* pixels = new char[initData.totalNumBytes];
|
|
memset(pixels, 0, initData.totalNumBytes * sizeof(char));
|
|
|
|
// Create ghoul texture
|
|
DefaultTileTexture = std::make_unique<Texture>(initData.dimensions);
|
|
DefaultTileTexture->setDataOwnership(Texture::TakeOwnership::Yes);
|
|
DefaultTileTexture->setPixelData(pixels);
|
|
DefaultTileTexture->uploadTexture();
|
|
DefaultTileTexture->setFilter(ghoul::opengl::Texture::FilterMode::LinearMipMap);
|
|
|
|
// Create tile
|
|
DefaultTile = Tile{ DefaultTileTexture.get(), std::nullopt, Tile::Status::OK };
|
|
}
|
|
|
|
void deinitializeDefaultTile() {
|
|
DefaultTileTexture = nullptr;
|
|
}
|
|
|
|
std::unique_ptr<TileProvider> createFromDictionary(layergroupid::TypeID layerTypeID,
|
|
const ghoul::Dictionary& dictionary)
|
|
{
|
|
const char* type = layergroupid::LAYER_TYPE_NAMES[static_cast<int>(layerTypeID)];
|
|
auto factory = FactoryManager::ref().factory<TileProvider>();
|
|
std::unique_ptr<TileProvider> result = factory->create(type, dictionary);
|
|
return result;
|
|
}
|
|
|
|
TileProvider::TileProvider() : properties::PropertyOwner({ "tileProvider" }) {}
|
|
|
|
DefaultTileProvider::DefaultTileProvider(const ghoul::Dictionary& dictionary)
|
|
: filePath(defaultprovider::FilePathInfo, "")
|
|
, tilePixelSize(defaultprovider::TilePixelSizeInfo, 32, 32, 2048)
|
|
{
|
|
type = Type::DefaultTileProvider;
|
|
|
|
tileCache = global::moduleEngine.module<GlobeBrowsingModule>()->tileCache();
|
|
name = "Name unspecified";
|
|
if (dictionary.hasKeyAndValue<std::string>("Name")) {
|
|
name = dictionary.value<std::string>("Name");
|
|
}
|
|
std::string _loggerCat = "DefaultTileProvider (" + name + ")";
|
|
|
|
// 1. Get required Keys
|
|
filePath = dictionary.value<std::string>(KeyFilePath);
|
|
layerGroupID = dictionary.value<layergroupid::GroupID>("LayerGroupID");
|
|
|
|
// 2. Initialize default values for any optional Keys
|
|
// getValue does not work for integers
|
|
int pixelSize = 0;
|
|
if (dictionary.hasKeyAndValue<double>(defaultprovider::KeyTilePixelSize)) {
|
|
pixelSize = static_cast<int>(
|
|
dictionary.value<double>(defaultprovider::KeyTilePixelSize)
|
|
);
|
|
LDEBUG(fmt::format("Default pixel size overridden: {}", pixelSize));
|
|
}
|
|
|
|
if (dictionary.hasKeyAndValue<bool>(defaultprovider::KeyPadTiles)) {
|
|
padTiles = dictionary.value<bool>(defaultprovider::KeyPadTiles);
|
|
}
|
|
|
|
TileTextureInitData initData(
|
|
tileTextureInitData(layerGroupID, padTiles, pixelSize)
|
|
);
|
|
tilePixelSize = initData.dimensions.x;
|
|
|
|
|
|
// Only preprocess height layers by default
|
|
switch (layerGroupID) {
|
|
case layergroupid::GroupID::HeightLayers: performPreProcessing = true; break;
|
|
default: performPreProcessing = false; break;
|
|
}
|
|
|
|
if (dictionary.hasKeyAndValue<bool>(defaultprovider::KeyPerformPreProcessing)) {
|
|
performPreProcessing = dictionary.value<bool>(
|
|
defaultprovider::KeyPerformPreProcessing
|
|
);
|
|
LDEBUG(fmt::format(
|
|
"Default PerformPreProcessing overridden: {}", performPreProcessing
|
|
));
|
|
}
|
|
|
|
initAsyncTileDataReader(*this, initData);
|
|
|
|
addProperty(filePath);
|
|
addProperty(tilePixelSize);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SingleImageProvider::SingleImageProvider(const ghoul::Dictionary& dictionary)
|
|
: filePath(singleimageprovider::FilePathInfo)
|
|
{
|
|
type = Type::SingleImageTileProvider;
|
|
|
|
filePath = dictionary.value<std::string>(KeyFilePath);
|
|
addProperty(filePath);
|
|
|
|
reset(*this);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TextTileProvider::TextTileProvider(TileTextureInitData initData, size_t fontSize)
|
|
: initData(std::move(initData))
|
|
, fontSize(fontSize)
|
|
{
|
|
tileCache = global::moduleEngine.module<GlobeBrowsingModule>()->tileCache();
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SizeReferenceTileProvider::SizeReferenceTileProvider(const ghoul::Dictionary& dictionary)
|
|
: TextTileProvider(tileTextureInitData(layergroupid::GroupID::ColorLayers, false))
|
|
{
|
|
type = Type::SizeReferenceTileProvider;
|
|
|
|
font = global::fontManager.font("Mono", static_cast<float>(fontSize));
|
|
|
|
if (dictionary.hasKeyAndValue<glm::dvec3>(sizereferenceprovider::KeyRadii)) {
|
|
ellipsoid = dictionary.value<glm::dvec3>(sizereferenceprovider::KeyRadii);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileIndexTileProvider::TileIndexTileProvider(const ghoul::Dictionary&)
|
|
: TextTileProvider(tileTextureInitData(layergroupid::GroupID::ColorLayers, false))
|
|
{
|
|
type = Type::TileIndexTileProvider;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileProviderByIndex::TileProviderByIndex(const ghoul::Dictionary& dictionary) {
|
|
type = Type::ByIndexTileProvider;
|
|
|
|
const ghoul::Dictionary& defaultProviderDict = dictionary.value<ghoul::Dictionary>(
|
|
byindexprovider::KeyDefaultProvider
|
|
);
|
|
|
|
layergroupid::TypeID typeID;
|
|
if (defaultProviderDict.hasKeyAndValue<std::string>("Type")) {
|
|
const std::string& t = defaultProviderDict.value<std::string>("Type");
|
|
typeID = ghoul::from_string<layergroupid::TypeID>(t);
|
|
|
|
if (typeID == layergroupid::TypeID::Unknown) {
|
|
throw ghoul::RuntimeError("Unknown layer type: " + t);
|
|
}
|
|
}
|
|
else {
|
|
typeID = layergroupid::TypeID::DefaultTileLayer;
|
|
}
|
|
|
|
defaultTileProvider = createFromDictionary(typeID, defaultProviderDict);
|
|
|
|
const ghoul::Dictionary& indexProvidersDict = dictionary.value<ghoul::Dictionary>(
|
|
byindexprovider::KeyProviders
|
|
);
|
|
for (size_t i = 1; i <= indexProvidersDict.size(); i++) {
|
|
ghoul::Dictionary indexProviderDict = indexProvidersDict.value<ghoul::Dictionary>(
|
|
std::to_string(i)
|
|
);
|
|
ghoul::Dictionary tileIndexDict = indexProviderDict.value<ghoul::Dictionary>(
|
|
byindexprovider::KeyTileIndex
|
|
);
|
|
ghoul::Dictionary providerDict = indexProviderDict.value<ghoul::Dictionary>(
|
|
byindexprovider::KeyTileProvider
|
|
);
|
|
|
|
constexpr const char* KeyLevel = "Level";
|
|
constexpr const char* KeyX = "X";
|
|
constexpr const char* KeyY = "Y";
|
|
|
|
int level = static_cast<int>(tileIndexDict.value<double>(KeyLevel));
|
|
int x = static_cast<int>(tileIndexDict.value<double>(KeyX));
|
|
int y = static_cast<int>(tileIndexDict.value<double>(KeyY));
|
|
const TileIndex tileIndex(x, y, level);
|
|
|
|
layergroupid::TypeID providerTypeID = layergroupid::TypeID::DefaultTileLayer;
|
|
if (defaultProviderDict.hasKeyAndValue<std::string>("Type")) {
|
|
const std::string& t = defaultProviderDict.value<std::string>("Type");
|
|
providerTypeID = ghoul::from_string<layergroupid::TypeID>(t);
|
|
|
|
if (providerTypeID == layergroupid::TypeID::Unknown) {
|
|
throw ghoul::RuntimeError("Unknown layer type: " + t);
|
|
}
|
|
}
|
|
|
|
std::unique_ptr<TileProvider> stp = createFromDictionary(
|
|
providerTypeID,
|
|
providerDict
|
|
);
|
|
TileIndex::TileHashKey key = tileIndex.hashKey();
|
|
tileProviderMap.insert(std::make_pair(key, std::move(stp)));
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TileProviderByLevel::TileProviderByLevel(const ghoul::Dictionary& dictionary) {
|
|
type = Type::ByLevelTileProvider;
|
|
|
|
layergroupid::GroupID layerGroupID = dictionary.value<layergroupid::GroupID>(
|
|
bylevelprovider::KeyLayerGroupID
|
|
);
|
|
|
|
if (dictionary.hasKeyAndValue<ghoul::Dictionary>(bylevelprovider::KeyProviders)) {
|
|
ghoul::Dictionary providers = dictionary.value<ghoul::Dictionary>(
|
|
bylevelprovider::KeyProviders
|
|
);
|
|
|
|
for (size_t i = 1; i <= providers.size(); i++) {
|
|
ghoul::Dictionary levelProviderDict = providers.value<ghoul::Dictionary>(
|
|
std::to_string(i)
|
|
);
|
|
double floatMaxLevel = levelProviderDict.value<double>(
|
|
bylevelprovider::KeyMaxLevel
|
|
);
|
|
int maxLevel = static_cast<int>(std::round(floatMaxLevel));
|
|
|
|
ghoul::Dictionary providerDict = levelProviderDict.value<ghoul::Dictionary>(
|
|
bylevelprovider::KeyTileProvider
|
|
);
|
|
providerDict.setValue(bylevelprovider::KeyLayerGroupID, layerGroupID);
|
|
|
|
layergroupid::TypeID typeID;
|
|
if (providerDict.hasKeyAndValue<std::string>("Type")) {
|
|
const std::string& typeString = providerDict.value<std::string>("Type");
|
|
typeID = ghoul::from_string<layergroupid::TypeID>(typeString);
|
|
|
|
if (typeID == layergroupid::TypeID::Unknown) {
|
|
throw ghoul::RuntimeError("Unknown layer type: " + typeString);
|
|
}
|
|
}
|
|
else {
|
|
typeID = layergroupid::TypeID::DefaultTileLayer;
|
|
}
|
|
|
|
std::unique_ptr<TileProvider> tp = std::unique_ptr<TileProvider>(
|
|
createFromDictionary(typeID, providerDict)
|
|
);
|
|
|
|
std::string provId = providerDict.value<std::string>("Identifier");
|
|
tp->setIdentifier(provId);
|
|
std::string providerName = providerDict.value<std::string>("Name");
|
|
tp->setGuiName(providerName);
|
|
addPropertySubOwner(tp.get());
|
|
|
|
levelTileProviders.push_back(std::move(tp));
|
|
|
|
// Ensure we can represent the max level
|
|
if (static_cast<int>(providerIndices.size()) < maxLevel) {
|
|
providerIndices.resize(maxLevel + 1, -1);
|
|
}
|
|
|
|
// map this level to the tile provider index
|
|
providerIndices[maxLevel] = static_cast<int>(levelTileProviders.size()) - 1;
|
|
}
|
|
}
|
|
|
|
// Fill in the gaps (value -1 ) in provider indices, from back to end
|
|
for (int i = static_cast<int>(providerIndices.size()) - 2; i >= 0; --i) {
|
|
if (providerIndices[i] == -1) {
|
|
providerIndices[i] = providerIndices[i + 1];
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TemporalTileProvider::TemporalTileProvider(const ghoul::Dictionary& dictionary)
|
|
: initDict(dictionary)
|
|
, filePath(temporal::FilePathInfo)
|
|
{
|
|
type = Type::TemporalTileProvider;
|
|
|
|
filePath = dictionary.value<std::string>(KeyFilePath);
|
|
addProperty(filePath);
|
|
|
|
successfulInitialization = readFilePath(*this);
|
|
|
|
if (!successfulInitialization) {
|
|
LERRORC("TemporalTileProvider", "Unable to read file " + filePath.value());
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool initialize(TileProvider& tp) {
|
|
ghoul_assert(!tp.isInitialized, "TileProvider can only be initialized once.");
|
|
|
|
tp.uniqueIdentifier = TileProvider::NumTileProviders++;
|
|
if (TileProvider::NumTileProviders == std::numeric_limits<unsigned int>::max()) {
|
|
--TileProvider::NumTileProviders;
|
|
return false;
|
|
}
|
|
|
|
tp.isInitialized = true;
|
|
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider:
|
|
break;
|
|
case Type::SingleImageTileProvider:
|
|
break;
|
|
case Type::SizeReferenceTileProvider: {
|
|
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
|
|
initialize(t);
|
|
break;
|
|
}
|
|
case Type::TileIndexTileProvider: {
|
|
TileIndexTileProvider& t = static_cast<TileIndexTileProvider&>(tp);
|
|
initialize(t);
|
|
break;
|
|
}
|
|
case Type::ByIndexTileProvider:
|
|
break;
|
|
case Type::ByLevelTileProvider: {
|
|
TileProviderByLevel& t = static_cast<TileProviderByLevel&>(tp);
|
|
bool success = true;
|
|
for (const std::unique_ptr<TileProvider>& prov : t.levelTileProviders) {
|
|
success &= initialize(*prov);
|
|
}
|
|
return success;
|
|
}
|
|
case Type::TemporalTileProvider:
|
|
break;
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
bool deinitialize(TileProvider& tp) {
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider:
|
|
break;
|
|
case Type::SingleImageTileProvider:
|
|
break;
|
|
case Type::SizeReferenceTileProvider: {
|
|
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
|
|
deinitialize(t);
|
|
break;
|
|
}
|
|
case Type::TileIndexTileProvider: {
|
|
TileIndexTileProvider& t = static_cast<TileIndexTileProvider&>(tp);
|
|
deinitialize(t);
|
|
break;
|
|
}
|
|
case Type::ByIndexTileProvider:
|
|
break;
|
|
case Type::ByLevelTileProvider: {
|
|
TileProviderByLevel& t = static_cast<TileProviderByLevel&>(tp);
|
|
bool success = true;
|
|
for (const std::unique_ptr<TileProvider>& prov : t.levelTileProviders) {
|
|
success &= deinitialize(*prov);
|
|
}
|
|
return success;
|
|
}
|
|
case Type::TemporalTileProvider:
|
|
break;
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Tile tile(TileProvider& tp, const TileIndex& tileIndex) {
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider: {
|
|
DefaultTileProvider& t = static_cast<DefaultTileProvider&>(tp);
|
|
if (t.asyncTextureDataProvider) {
|
|
if (tileIndex.level > maxLevel(t)) {
|
|
return Tile{ nullptr, std::nullopt, Tile::Status::OutOfRange };
|
|
}
|
|
const cache::ProviderTileKey key = { tileIndex, t.uniqueIdentifier };
|
|
const Tile tile = t.tileCache->get(key);
|
|
|
|
if (!tile.texture) {
|
|
t.asyncTextureDataProvider->enqueueTileIO(tileIndex);
|
|
}
|
|
|
|
return tile;
|
|
}
|
|
else {
|
|
return Tile{ nullptr, std::nullopt, Tile::Status::Unavailable };
|
|
}
|
|
}
|
|
case Type::SingleImageTileProvider: {
|
|
SingleImageProvider& t = static_cast<SingleImageProvider&>(tp);
|
|
return t.tile;
|
|
}
|
|
case Type::SizeReferenceTileProvider: {
|
|
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
|
|
|
|
const GeodeticPatch patch(tileIndex);
|
|
const bool aboveEquator = patch.isNorthern();
|
|
const double lat = aboveEquator ? patch.minLat() : patch.maxLat();
|
|
const double lon1 = patch.minLon();
|
|
const double lon2 = patch.maxLon();
|
|
int l = static_cast<int>(t.ellipsoid.longitudalDistance(lat, lon1, lon2));
|
|
|
|
const bool useKm = l > 9999;
|
|
if (useKm) {
|
|
l /= 1000;
|
|
}
|
|
l = static_cast<int>(std::round(l));
|
|
if (useKm) {
|
|
l *= 1000;
|
|
}
|
|
double tileLongitudalLength = l;
|
|
|
|
const char* unit;
|
|
if (tileLongitudalLength > 9999) {
|
|
tileLongitudalLength *= 0.001;
|
|
unit = "km";
|
|
}
|
|
else {
|
|
unit = "m";
|
|
}
|
|
|
|
t.text = fmt::format(" {:.0f} {:s}", tileLongitudalLength, unit);
|
|
t.textPosition = {
|
|
0.f,
|
|
aboveEquator ?
|
|
t.fontSize / 2.f :
|
|
t.initData.dimensions.y - 3.f * t.fontSize / 2.f
|
|
};
|
|
t.textColor = glm::vec4(1.f);
|
|
|
|
return tile(t, tileIndex);
|
|
}
|
|
case Type::TileIndexTileProvider: {
|
|
TileIndexTileProvider& t = static_cast<TileIndexTileProvider&>(tp);
|
|
t.text = fmt::format(
|
|
"level: {}\nx: {}\ny: {}", tileIndex.level, tileIndex.x, tileIndex.y
|
|
);
|
|
t.textPosition = glm::vec2(
|
|
t.initData.dimensions.x / 4 -
|
|
(t.initData.dimensions.x / 32) * log10(1 << tileIndex.level),
|
|
t.initData.dimensions.y / 2 + t.fontSize
|
|
);
|
|
t.textColor = glm::vec4(1.f);
|
|
|
|
return tile(t, tileIndex);
|
|
}
|
|
case Type::ByIndexTileProvider: {
|
|
TileProviderByIndex& t = static_cast<TileProviderByIndex&>(tp);
|
|
const auto it = t.tileProviderMap.find(tileIndex.hashKey());
|
|
const bool hasProvider = it != t.tileProviderMap.end();
|
|
return hasProvider ? tile(*it->second, tileIndex) : Tile();
|
|
}
|
|
case Type::ByLevelTileProvider: {
|
|
TileProviderByLevel& t = static_cast<TileProviderByLevel&>(tp);
|
|
TileProvider* provider = levelProvider(t, tileIndex.level);
|
|
if (provider) {
|
|
return tile(*provider, tileIndex);
|
|
}
|
|
else {
|
|
return Tile();
|
|
}
|
|
}
|
|
case Type::TemporalTileProvider: {
|
|
TemporalTileProvider& t = static_cast<TemporalTileProvider&>(tp);
|
|
if (t.successfulInitialization) {
|
|
ensureUpdated(t);
|
|
return tile(*t.currentTileProvider, tileIndex);
|
|
}
|
|
else {
|
|
return Tile();
|
|
}
|
|
}
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
Tile::Status tileStatus(TileProvider& tp, const TileIndex& index) {
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider: {
|
|
DefaultTileProvider& t = static_cast<DefaultTileProvider&>(tp);
|
|
if (t.asyncTextureDataProvider) {
|
|
const RawTileDataReader& rawTileDataReader =
|
|
t.asyncTextureDataProvider->rawTileDataReader();
|
|
|
|
if (index.level > rawTileDataReader.maxChunkLevel()) {
|
|
return Tile::Status::OutOfRange;
|
|
}
|
|
|
|
const cache::ProviderTileKey key = { index, t.uniqueIdentifier };
|
|
return t.tileCache->get(key).status;
|
|
}
|
|
else {
|
|
return Tile::Status::Unavailable;
|
|
}
|
|
}
|
|
case Type::SingleImageTileProvider: {
|
|
SingleImageProvider& t = static_cast<SingleImageProvider&>(tp);
|
|
return t.tile.status;
|
|
}
|
|
case Type::SizeReferenceTileProvider:
|
|
return Tile::Status::OK;
|
|
case Type::TileIndexTileProvider:
|
|
return Tile::Status::OK;
|
|
case Type::ByIndexTileProvider: {
|
|
TileProviderByIndex& t = static_cast<TileProviderByIndex&>(tp);
|
|
const auto it = t.tileProviderMap.find(index.hashKey());
|
|
const bool hasProvider = it != t.tileProviderMap.end();
|
|
return hasProvider ?
|
|
tileStatus(*it->second, index) :
|
|
Tile::Status::Unavailable;
|
|
}
|
|
case Type::ByLevelTileProvider: {
|
|
TileProviderByLevel& t = static_cast<TileProviderByLevel&>(tp);
|
|
TileProvider* provider = levelProvider(t, index.level);
|
|
return provider ? tileStatus(*provider, index) : Tile::Status::Unavailable;
|
|
}
|
|
case Type::TemporalTileProvider: {
|
|
TemporalTileProvider& t = static_cast<TemporalTileProvider&>(tp);
|
|
if (t.successfulInitialization) {
|
|
ensureUpdated(t);
|
|
return tileStatus(*t.currentTileProvider, index);
|
|
}
|
|
else {
|
|
return Tile::Status::Unavailable;
|
|
}
|
|
}
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
TileDepthTransform depthTransform(TileProvider& tp) {
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider: {
|
|
DefaultTileProvider& t = static_cast<DefaultTileProvider&>(tp);
|
|
if (t.asyncTextureDataProvider) {
|
|
return t.asyncTextureDataProvider->rawTileDataReader().depthTransform();
|
|
}
|
|
else {
|
|
return { 1.f, 0.f };
|
|
}
|
|
}
|
|
case Type::SingleImageTileProvider:
|
|
return { 0.f, 1.f };
|
|
case Type::SizeReferenceTileProvider:
|
|
return { 0.f, 1.f };
|
|
case Type::TileIndexTileProvider:
|
|
return { 0.f, 1.f };
|
|
case Type::ByIndexTileProvider: {
|
|
TileProviderByIndex& t = static_cast<TileProviderByIndex&>(tp);
|
|
return depthTransform(*t.defaultTileProvider);
|
|
}
|
|
case Type::ByLevelTileProvider:
|
|
return { 0.f, 1.f };
|
|
case Type::TemporalTileProvider: {
|
|
TemporalTileProvider& t = static_cast<TemporalTileProvider&>(tp);
|
|
if (t.successfulInitialization) {
|
|
ensureUpdated(t);
|
|
return depthTransform(*t.currentTileProvider);
|
|
}
|
|
else {
|
|
return { 1.f, 0.f };
|
|
}
|
|
}
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int update(TileProvider& tp) {
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider: {
|
|
DefaultTileProvider& t = static_cast<DefaultTileProvider&>(tp);
|
|
if (!t.asyncTextureDataProvider) {
|
|
break;
|
|
}
|
|
|
|
t.asyncTextureDataProvider->update();
|
|
bool hasUploaded = initTexturesFromLoadedData(t);
|
|
|
|
if (t.asyncTextureDataProvider->shouldBeDeleted()) {
|
|
t.asyncTextureDataProvider = nullptr;
|
|
initAsyncTileDataReader(
|
|
t,
|
|
tileTextureInitData(t.layerGroupID, t.padTiles, t.tilePixelSize)
|
|
);
|
|
}
|
|
if (hasUploaded) {
|
|
return 1;
|
|
}
|
|
break;
|
|
}
|
|
case Type::SingleImageTileProvider:
|
|
break;
|
|
case Type::SizeReferenceTileProvider:
|
|
break;
|
|
case Type::TileIndexTileProvider:
|
|
break;
|
|
case Type::ByIndexTileProvider: {
|
|
TileProviderByIndex& t = static_cast<TileProviderByIndex&>(tp);
|
|
using K = TileIndex::TileHashKey;
|
|
using V = std::unique_ptr<TileProvider>;
|
|
for (std::pair<const K, V>& it : t.tileProviderMap) {
|
|
update(*it.second);
|
|
}
|
|
update(*t.defaultTileProvider);
|
|
break;
|
|
}
|
|
case Type::ByLevelTileProvider: {
|
|
TileProviderByLevel& t = static_cast<TileProviderByLevel&>(tp);
|
|
for (const std::unique_ptr<TileProvider>& provider : t.levelTileProviders) {
|
|
update(*provider);
|
|
}
|
|
break;
|
|
}
|
|
case Type::TemporalTileProvider: {
|
|
TemporalTileProvider& t = static_cast<TemporalTileProvider&>(tp);
|
|
if (t.successfulInitialization) {
|
|
TileProvider* newCurrent = getTileProvider(t, global::timeManager.time());
|
|
if (newCurrent) {
|
|
t.currentTileProvider = newCurrent;
|
|
}
|
|
if (t.currentTileProvider) {
|
|
update(*t.currentTileProvider);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void reset(TileProvider& tp) {
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider: {
|
|
DefaultTileProvider& t = static_cast<DefaultTileProvider&>(tp);
|
|
t.tileCache->clear();
|
|
if (t.asyncTextureDataProvider) {
|
|
t.asyncTextureDataProvider->prepareToBeDeleted();
|
|
}
|
|
else {
|
|
initAsyncTileDataReader(
|
|
t,
|
|
tileTextureInitData(t.layerGroupID, t.padTiles, t.tilePixelSize)
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
case Type::SingleImageTileProvider: {
|
|
SingleImageProvider& t = static_cast<SingleImageProvider&>(tp);
|
|
|
|
if (t.filePath.value().empty()) {
|
|
return;
|
|
}
|
|
t.tileTexture = ghoul::io::TextureReader::ref().loadTexture(t.filePath);
|
|
if (!t.tileTexture) {
|
|
throw ghoul::RuntimeError(
|
|
fmt::format("Unable to load texture '{}'", t.filePath.value())
|
|
);
|
|
}
|
|
Tile::Status tileStatus = Tile::Status::OK;
|
|
|
|
t.tileTexture->uploadTexture();
|
|
t.tileTexture->setFilter(
|
|
ghoul::opengl::Texture::FilterMode::AnisotropicMipMap
|
|
);
|
|
|
|
t.tile = Tile{ t.tileTexture.get(), std::nullopt, tileStatus };
|
|
break;
|
|
}
|
|
case Type::SizeReferenceTileProvider: {
|
|
SizeReferenceTileProvider& t = static_cast<SizeReferenceTileProvider&>(tp);
|
|
reset(t);
|
|
break;
|
|
}
|
|
case Type::TileIndexTileProvider: {
|
|
TileIndexTileProvider& t = static_cast<TileIndexTileProvider&>(tp);
|
|
reset(t);
|
|
break;
|
|
}
|
|
case Type::ByIndexTileProvider: {
|
|
TileProviderByIndex& t = static_cast<TileProviderByIndex&>(tp);
|
|
using K = TileIndex::TileHashKey;
|
|
using V = std::unique_ptr<TileProvider>;
|
|
for (std::pair<const K, V>& it : t.tileProviderMap) {
|
|
reset(*it.second);
|
|
}
|
|
reset(*t.defaultTileProvider);
|
|
break;
|
|
}
|
|
case Type::ByLevelTileProvider: {
|
|
TileProviderByLevel& t = static_cast<TileProviderByLevel&>(tp);
|
|
for (const std::unique_ptr<TileProvider>& provider : t.levelTileProviders) {
|
|
reset(*provider);
|
|
}
|
|
break;
|
|
}
|
|
case Type::TemporalTileProvider: {
|
|
TemporalTileProvider& t = static_cast<TemporalTileProvider&>(tp);
|
|
if (t.successfulInitialization) {
|
|
using K = TemporalTileProvider::TimeKey;
|
|
using V = std::unique_ptr<TileProvider>;
|
|
for (std::pair<const K, V>& it : t.tileProviderMap) {
|
|
reset(*it.second);
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int maxLevel(TileProvider& tp) {
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider: {
|
|
DefaultTileProvider& t = static_cast<DefaultTileProvider&>(tp);
|
|
// 22 is the current theoretical maximum based on the number of hashes that
|
|
// are possible to uniquely identify a tile. See ProviderTileHasher in
|
|
// memoryawaretilecache.h
|
|
return t.asyncTextureDataProvider ?
|
|
t.asyncTextureDataProvider->rawTileDataReader().maxChunkLevel() :
|
|
22;
|
|
}
|
|
case Type::SingleImageTileProvider:
|
|
return 1337; // unlimited
|
|
case Type::SizeReferenceTileProvider:
|
|
return 1337; // unlimited
|
|
case Type::TileIndexTileProvider:
|
|
return 1337; // unlimited
|
|
case Type::ByIndexTileProvider: {
|
|
TileProviderByIndex& t = static_cast<TileProviderByIndex&>(tp);
|
|
return maxLevel(*t.defaultTileProvider);
|
|
}
|
|
case Type::ByLevelTileProvider: {
|
|
TileProviderByLevel& t = static_cast<TileProviderByLevel&>(tp);
|
|
return static_cast<int>(t.providerIndices.size() - 1);
|
|
}
|
|
case Type::TemporalTileProvider: {
|
|
TemporalTileProvider& t = static_cast<TemporalTileProvider&>(tp);
|
|
if (t.successfulInitialization) {
|
|
ensureUpdated(t);
|
|
return maxLevel(*t.currentTileProvider);
|
|
}
|
|
else {
|
|
return 0;
|
|
}
|
|
}
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
float noDataValueAsFloat(TileProvider& tp) {
|
|
ghoul_assert(tp.isInitialized, "TileProvider was not initialized.");
|
|
switch (tp.type) {
|
|
case Type::DefaultTileProvider: {
|
|
DefaultTileProvider& t = static_cast<DefaultTileProvider&>(tp);
|
|
return t.asyncTextureDataProvider ?
|
|
t.asyncTextureDataProvider->noDataValueAsFloat() :
|
|
std::numeric_limits<float>::min();
|
|
}
|
|
case Type::SingleImageTileProvider:
|
|
return std::numeric_limits<float>::min();
|
|
case Type::SizeReferenceTileProvider:
|
|
return std::numeric_limits<float>::min();
|
|
case Type::TileIndexTileProvider:
|
|
return std::numeric_limits<float>::min();
|
|
case Type::ByIndexTileProvider:
|
|
return std::numeric_limits<float>::min();
|
|
case Type::ByLevelTileProvider:
|
|
return std::numeric_limits<float>::min();
|
|
case Type::TemporalTileProvider:
|
|
return std::numeric_limits<float>::min();
|
|
default:
|
|
throw ghoul::MissingCaseException();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ChunkTile chunkTile(TileProvider& tp, TileIndex tileIndex, int parents, int maxParents) {
|
|
ghoul_assert(tp.isInitialized, "TileProvider was not initialized.");
|
|
|
|
auto ascendToParent = [](TileIndex& tileIndex, TileUvTransform& uv) {
|
|
uv.uvOffset *= 0.5;
|
|
uv.uvScale *= 0.5;
|
|
|
|
uv.uvOffset += tileIndex.positionRelativeParent();
|
|
|
|
tileIndex.x /= 2;
|
|
tileIndex.y /= 2;
|
|
tileIndex.level--;
|
|
};
|
|
|
|
TileUvTransform uvTransform = { glm::vec2(0.f, 0.f), glm::vec2(1.f, 1.f) };
|
|
|
|
// Step 1. Traverse 0 or more parents up the chunkTree as requested by the caller
|
|
for (int i = 0; i < parents && tileIndex.level > 1; i++) {
|
|
ascendToParent(tileIndex, uvTransform);
|
|
}
|
|
maxParents -= parents;
|
|
|
|
// Step 2. Traverse 0 or more parents up the chunkTree to make sure we're inside
|
|
// the range of defined data.
|
|
int maximumLevel = maxLevel(tp);
|
|
while (tileIndex.level > maximumLevel) {
|
|
ascendToParent(tileIndex, uvTransform);
|
|
maxParents--;
|
|
}
|
|
if (maxParents < 0) {
|
|
return ChunkTile{ Tile(), uvTransform, TileDepthTransform() };
|
|
}
|
|
|
|
// Step 3. Traverse 0 or more parents up the chunkTree until we find a chunk that
|
|
// has a loaded tile ready to use.
|
|
while (tileIndex.level > 1) {
|
|
Tile t = tile(tp, tileIndex);
|
|
if (t.status != Tile::Status::OK) {
|
|
if (--maxParents < 0) {
|
|
return ChunkTile{ Tile(), uvTransform, TileDepthTransform() };
|
|
}
|
|
ascendToParent(tileIndex, uvTransform);
|
|
}
|
|
else {
|
|
return ChunkTile{ std::move(t), uvTransform, TileDepthTransform() };
|
|
}
|
|
}
|
|
|
|
return ChunkTile{ Tile(), uvTransform, TileDepthTransform() };
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ChunkTilePile chunkTilePile(TileProvider& tp, TileIndex tileIndex, int pileSize) {
|
|
ghoul_assert(tp.isInitialized, "TileProvider was not initialized.");
|
|
ghoul_assert(pileSize >= 0, "pileSize must be positive");
|
|
|
|
ChunkTilePile chunkTilePile(pileSize);
|
|
for (int i = 0; i < pileSize; ++i) {
|
|
chunkTilePile[i] = chunkTile(tp, tileIndex, i);
|
|
if (chunkTilePile[i].tile.status == Tile::Status::Unavailable) {
|
|
if (i > 0) {
|
|
// First iteration
|
|
chunkTilePile[i].tile = chunkTilePile[i - 1].tile;
|
|
chunkTilePile[i].uvTransform.uvOffset =
|
|
chunkTilePile[i - 1].uvTransform.uvOffset;
|
|
chunkTilePile[i].uvTransform.uvScale =
|
|
chunkTilePile[i - 1].uvTransform.uvScale;
|
|
}
|
|
else {
|
|
chunkTilePile[i].tile = DefaultTile;
|
|
chunkTilePile[i].uvTransform.uvOffset = { 0.f, 0.f };
|
|
chunkTilePile[i].uvTransform.uvScale = { 1.f, 1.f };
|
|
}
|
|
}
|
|
}
|
|
return chunkTilePile;
|
|
}
|
|
|
|
} // namespace openspace::globebrowsing::tileprovider
|