Add documentation and example for the TileProviderByIndex (#3220)

This commit is contained in:
Alexander Bock
2024-04-30 08:20:40 +02:00
committed by GitHub
parent f1bbdc16cd
commit 359cf9950e
6 changed files with 154 additions and 30 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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;

View File

@@ -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() {

View File

@@ -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;