mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-05 11:09:37 -06:00
Add documentation and example for the TileProviderByIndex (#3220)
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
-- Basic
|
||||
-- This example file adds a layer to a globe that has a base layer and then replaces one
|
||||
-- hemisphere of the planet with a single image. Recommended reading for this example is
|
||||
-- the documentation on the DefaultTileProvider.
|
||||
|
||||
-- Download some example images that we can use
|
||||
local images = asset.resource({
|
||||
Name = "Earth Textures",
|
||||
Type = "HttpSynchronization",
|
||||
Identifier = "earth_textures",
|
||||
Version = 3
|
||||
})
|
||||
|
||||
-- Define the TileProvider
|
||||
local TileProvider = {
|
||||
Identifier = "Example",
|
||||
Type = "TileProviderByIndex",
|
||||
Enabled = true,
|
||||
-- The default tile provider that is used for the whole globe
|
||||
DefaultTileProvider = {
|
||||
Identifier = "Blue_Marble",
|
||||
FilePath = images .. "earth_bluemarble.jpg"
|
||||
},
|
||||
TileProviders = {
|
||||
-- We only define one additional tile provider that overwrites the right
|
||||
-- hemisphere of the globe
|
||||
{
|
||||
Index = { X = 0, Y = 0, Level = 2 },
|
||||
TileProvider = {
|
||||
Identifier = "Blue_Marble_Night",
|
||||
FilePath = images .. "earth_night.png"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-- Define the scene graph node
|
||||
local Node = {
|
||||
Identifier = "TileProviderByIndex_Example",
|
||||
Renderable = {
|
||||
Type = "RenderableGlobe",
|
||||
Layers = {
|
||||
-- The globe has exactly one layer, which is the one we defined above
|
||||
ColorLayers = { TileProvider }
|
||||
}
|
||||
},
|
||||
GUI = {
|
||||
Name = "Basic",
|
||||
Path = "/Examples/TileProviderByIndex"
|
||||
}
|
||||
}
|
||||
|
||||
asset.onInitialize(function()
|
||||
openspace.addSceneGraphNode(Node)
|
||||
end)
|
||||
|
||||
asset.onDeinitialize(function()
|
||||
openspace.removeSceneGraphNode(Node)
|
||||
end)
|
||||
@@ -174,10 +174,6 @@ namespace {
|
||||
// Sets the blend mode of this layer to determine how it interacts with other
|
||||
// layers on top of this
|
||||
std::optional<BlendMode> blendMode;
|
||||
|
||||
// If the primary layer creation fails, this layer is used as a fallback
|
||||
std::optional<ghoul::Dictionary>
|
||||
fallback [[codegen::reference("globebrowsing_layer")]];
|
||||
};
|
||||
#include "layer_codegen.cpp"
|
||||
} // namespace
|
||||
|
||||
@@ -36,6 +36,30 @@ TileIndex::TileIndex(uint32_t x_, uint32_t y_, uint8_t level_)
|
||||
, level(level_)
|
||||
{}
|
||||
|
||||
TileIndex::TileIndex(TileHashKey hash) {
|
||||
// Extract the level as the lowest 5 bits
|
||||
uint64_t hlevel = hash & ((1 << 5) - 1);
|
||||
ghoul_assert(hlevel < (1 << 5), "Error in hashing for level");
|
||||
// And then shift the remainder down so that the next value is in the lowest bits
|
||||
hash -= hlevel;
|
||||
hash = hash >> 5;
|
||||
|
||||
// Extract the x as the lowest 30 bits
|
||||
uint64_t hx = hash & ((1 << 30) - 1);
|
||||
ghoul_assert(hx < (1 << 30), "Error in hashing for x");
|
||||
// And then shift the remainder down so that the next value is in the lowest bits
|
||||
hash -= hx;
|
||||
hash = hash >> 30;
|
||||
|
||||
// The remainder in the hash must then be the y index
|
||||
uint64_t hy = hash;
|
||||
ghoul_assert(hy < (1 << 29), "Error in hashing for y");
|
||||
|
||||
level = static_cast<uint8_t>(hlevel);
|
||||
x = static_cast<uint32_t>(hx);
|
||||
y = static_cast<uint32_t>(hy);
|
||||
}
|
||||
|
||||
TileIndex TileIndex::child(Quad q) const {
|
||||
return TileIndex(2 * x + q % 2, 2 * y + q / 2, level + 1);
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ struct TileIndex {
|
||||
using TileHashKey = uint64_t;
|
||||
|
||||
TileIndex(uint32_t x_, uint32_t y_, uint8_t level_);
|
||||
explicit TileIndex(TileHashKey hash);
|
||||
|
||||
uint32_t x = 0;
|
||||
uint32_t y = 0;
|
||||
|
||||
@@ -27,33 +27,50 @@
|
||||
#include <openspace/documentation/documentation.h>
|
||||
|
||||
namespace {
|
||||
// This TileProvider provides the ability to override the contents for tiles at
|
||||
// specific indices. A default tile provider has to be specified that is used by
|
||||
// default for the entire globe. If a tile provider is specified for a specific tile,
|
||||
// then the default tile provide is used for all other indices and the specialized
|
||||
// tile provider `P` is used for the specified index. Any number of specialized tile
|
||||
// providers can be provided to overwrite specific locations on the globe.
|
||||
//
|
||||
// This tile provider can be used to, for example, show an inset image that is merged
|
||||
// with a larger globe-spanning image.
|
||||
struct [[codegen::Dictionary(TileProviderByIndex)]] Parameters {
|
||||
ghoul::Dictionary defaultProvider;
|
||||
ghoul::Dictionary defaultTileProvider
|
||||
[[codegen::reference("globebrowsing_layer")]];
|
||||
|
||||
// An IndexProvider is a tile provider that is only valid for a specific
|
||||
// combination of x, y, and level. Whenever a globe tries to render a tile and
|
||||
// this tile provider has an IndexProvider of that index, it will use the
|
||||
// specialized tile provider instead.
|
||||
struct IndexProvider {
|
||||
struct Index {
|
||||
// The x coordinate for this index. This specifies the horizontal
|
||||
// direction (longitude) component
|
||||
// direction (longitude) component. Acceptable values for this coordinate
|
||||
// have to be smaller than $2 * 2^{level}$.
|
||||
int x [[codegen::greaterequal(0)]];
|
||||
|
||||
// The y coordinate for this index. This specifies the vertical direction
|
||||
// (latitude) component
|
||||
// (latitude) component. Acceptable values for this coordinate have to be
|
||||
// smaller than $2^{level}$.
|
||||
int y [[codegen::greaterequal(0)]];
|
||||
|
||||
// The z-level which corresponds to the depth of the tile pyramid, which
|
||||
// directly impacts the applied resolution of the tileprovider shown here
|
||||
int level [[codegen::inrange(0, 255)]];
|
||||
// directly impacts the applied resolution of the tileprovider shown here.
|
||||
// Not that _in general_ the level would start at 2.
|
||||
int level [[codegen::inrange(0, 23)]];
|
||||
};
|
||||
// The index for which the provided tile provider is used
|
||||
Index tileIndex;
|
||||
// The index for which the provided tile provider is used.
|
||||
Index index;
|
||||
|
||||
// The dictionary that described the tileprovider to be used by the provided
|
||||
// index
|
||||
ghoul::Dictionary tileProvider;
|
||||
// The dictionary that describes the TileProvider to be used by the provided
|
||||
// `index`.
|
||||
ghoul::Dictionary tileProvider [[codegen::reference("globebrowsing_layer")]];
|
||||
};
|
||||
|
||||
// The list of all tileprovides and the indices at which they are used
|
||||
std::vector<IndexProvider> indexTileProviders;
|
||||
// The list of all TileProviders and the indices at which they are used.
|
||||
std::vector<IndexProvider> tileProviders;
|
||||
};
|
||||
#include "tileproviderbyindex_codegen.cpp"
|
||||
} // namespace
|
||||
@@ -67,21 +84,30 @@ documentation::Documentation TileProviderByIndex::Documentation() {
|
||||
TileProviderByIndex::TileProviderByIndex(const ghoul::Dictionary& dictionary) {
|
||||
ZoneScoped;
|
||||
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
// For now we need to inject the LayerGroupID this way. We don't want it to be part of
|
||||
// the parameters struct as that would mean it would be visible to the end user, which
|
||||
// we don't want since this value just comes from whoever creates it, not the user
|
||||
ghoul_assert(dictionary.hasValue<int>("LayerGroupID"), "No Layer Group ID provided");
|
||||
const layers::Group::ID group = static_cast<layers::Group::ID>(
|
||||
dictionary.value<int>("LayerGroupID")
|
||||
);
|
||||
|
||||
layers::Layer::ID typeID = layers::Layer::ID::DefaultTileProvider;
|
||||
if (p.defaultProvider.hasValue<std::string>("Type")) {
|
||||
const std::string type = p.defaultProvider.value<std::string>("Type");
|
||||
if (p.defaultTileProvider.hasValue<std::string>("Type")) {
|
||||
const std::string type = p.defaultTileProvider.value<std::string>("Type");
|
||||
typeID = ghoul::from_string<layers::Layer::ID>(type);
|
||||
}
|
||||
|
||||
_defaultTileProvider = createFromDictionary(typeID, p.defaultProvider);
|
||||
p.defaultTileProvider.setValue("LayerGroupID", static_cast<int>(group));
|
||||
_defaultTileProvider = createFromDictionary(typeID, p.defaultTileProvider);
|
||||
|
||||
for (const Parameters::IndexProvider& ip : p.indexTileProviders) {
|
||||
for (Parameters::IndexProvider& ip : p.tileProviders) {
|
||||
const TileIndex tileIndex = TileIndex(
|
||||
ip.tileIndex.x,
|
||||
ip.tileIndex.y,
|
||||
static_cast<uint8_t>(ip.tileIndex.level)
|
||||
ip.index.x,
|
||||
ip.index.y,
|
||||
static_cast<uint8_t>(ip.index.level)
|
||||
);
|
||||
|
||||
layers::Layer::ID providerID = layers::Layer::ID::DefaultTileProvider;
|
||||
@@ -90,6 +116,7 @@ TileProviderByIndex::TileProviderByIndex(const ghoul::Dictionary& dictionary) {
|
||||
providerID = ghoul::from_string<layers::Layer::ID>(type);
|
||||
}
|
||||
|
||||
ip.tileProvider.setValue("LayerGroupID", static_cast<int>(group));
|
||||
std::unique_ptr<TileProvider> stp = createFromDictionary(
|
||||
providerID,
|
||||
ip.tileProvider
|
||||
@@ -103,13 +130,17 @@ Tile TileProviderByIndex::tile(const TileIndex& tileIndex) {
|
||||
ZoneScoped;
|
||||
const auto it = _providers.find(tileIndex.hashKey());
|
||||
const bool hasProvider = it != _providers.end();
|
||||
return hasProvider ? it->second->tile(tileIndex) : Tile();
|
||||
return hasProvider ?
|
||||
it->second->tile(tileIndex) :
|
||||
_defaultTileProvider->tile(tileIndex);
|
||||
}
|
||||
|
||||
Tile::Status TileProviderByIndex::tileStatus(const TileIndex& index) {
|
||||
const auto it = _providers.find(index.hashKey());
|
||||
const bool hasProvider = it != _providers.end();
|
||||
return hasProvider ? it->second->tileStatus(index) : Tile::Status::Unavailable;
|
||||
return hasProvider ?
|
||||
it->second->tileStatus(index) :
|
||||
_defaultTileProvider->tileStatus(index);
|
||||
}
|
||||
|
||||
TileDepthTransform TileProviderByIndex::depthTransform() {
|
||||
@@ -139,7 +170,16 @@ int TileProviderByIndex::minLevel() {
|
||||
}
|
||||
|
||||
int TileProviderByIndex::maxLevel() {
|
||||
return _defaultTileProvider->maxLevel();
|
||||
int result = _defaultTileProvider->maxLevel();
|
||||
|
||||
using K = TileIndex::TileHashKey;
|
||||
using V = std::unique_ptr<TileProvider>;
|
||||
for (std::pair<const K, V>& it : _providers) {
|
||||
TileIndex index = TileIndex(it.first);
|
||||
result = std::max(result, static_cast<int>(index.level));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float TileProviderByIndex::noDataValueAsFloat() {
|
||||
|
||||
@@ -28,8 +28,6 @@
|
||||
|
||||
namespace {
|
||||
struct [[codegen::Dictionary(TileProviderByLevel)]] Parameters {
|
||||
int layerGroupID;
|
||||
|
||||
struct Provider {
|
||||
int maxLevel [[codegen::greaterequal(0)]];
|
||||
ghoul::Dictionary tileProvider;
|
||||
@@ -50,7 +48,13 @@ TileProviderByLevel::TileProviderByLevel(const ghoul::Dictionary& dictionary) {
|
||||
|
||||
const Parameters p = codegen::bake<Parameters>(dictionary);
|
||||
|
||||
const layers::Group::ID group = static_cast<layers::Group::ID>(p.layerGroupID);
|
||||
// For now we need to inject the LayerGroupID this way. We don't want it to be part of
|
||||
// the parameters struct as that would mean it would be visible to the end user, which
|
||||
// we don't want since this value just comes from whoever creates it, not the user
|
||||
ghoul_assert(dictionary.hasValue<int>("LayerGroupID"), "No Layer Group ID provided");
|
||||
const layers::Group::ID group = static_cast<layers::Group::ID>(
|
||||
dictionary.value<int>("LayerGroupID")
|
||||
);
|
||||
|
||||
for (Parameters::Provider provider : p.levelTileProviders) {
|
||||
ghoul::Dictionary& tileProviderDict = provider.tileProvider;
|
||||
|
||||
Reference in New Issue
Block a user