Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow access to Tileset-level metadata (schema, schemaUri, groups, metadata) #709

Merged
merged 33 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from 29 commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
31f0ed5
Expose Schema on Tileset.
kring Aug 18, 2023
0dbf930
Start reading metadata and groups properties.
kring Aug 18, 2023
056eecc
Give tileset a root "external" tile.
kring Aug 21, 2023
44ad786
Expose metadata on root tile and external tilesets.
kring Aug 21, 2023
789fed8
Support metadata on external tilesets.
kring Aug 21, 2023
e99b00d
Formatting.
kring Aug 21, 2023
79d33e2
Fix Clang/GCC compiler error.
kring Aug 21, 2023
8f8cb9b
Generate Reader classes, remove hand-written versions.
kring Aug 22, 2023
cfaaf4d
Add doc for readers.
kring Aug 22, 2023
c0fc8b6
More tileset metadata to a separate struct.
kring Aug 22, 2023
ef2ac5d
Add missing file.
kring Aug 22, 2023
c68207a
Add MetadataQuery class.
kring Aug 23, 2023
056edba
Add a test based on material variants.
kring Aug 23, 2023
20cbc17
Formatting.
kring Aug 23, 2023
d0a3091
Fix dodgy destructor declaration.
kring Aug 23, 2023
ea6a74e
Fix another clang warning.
kring Aug 23, 2023
3585ba3
Merge remote-tracking branch 'origin/json-read-tweaks' into tileset-m…
kring Aug 23, 2023
532d4e7
Merge remote-tracking branch 'origin/generated-readers' into tileset-…
kring Aug 23, 2023
d24e5f9
Update CHANGES.md, remove unnecessary change.
kring Aug 23, 2023
fe26c94
Remove unnecessary forward declaration.
kring Aug 23, 2023
9f73df1
Merge remote-tracking branch 'origin/generated-readers' into tileset-…
kring Aug 24, 2023
9a1abb2
Add missing dll export of FoundMetadataProperty.
kring Aug 24, 2023
153890d
Add `Tileset::loadMetadata`.
kring Aug 28, 2023
6145b54
Update CHANGES.md.
kring Aug 28, 2023
51e798e
Add test for async schema loading.
kring Aug 28, 2023
946887a
Add getArrayOfStrings helper to JsonValue.
kring Aug 28, 2023
5bf8a7b
struct -> class
kring Aug 28, 2023
31fc053
Fix clang warning.
kring Aug 28, 2023
3f40341
Fix VS2017/2019 warning.
kring Aug 28, 2023
5637a68
Merge remote-tracking branch 'origin/generated-readers' into tileset-…
kring Aug 30, 2023
7c8f4b1
Fix test failures, change some CHECK to REQUIRE.
kring Aug 30, 2023
57476c3
Changes from review.
kring Aug 30, 2023
a2d5b92
Fix misnaming.
kring Aug 30, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@
- Renamed `ExtensionReaderContext` to `JsonReaderOptions`, and the `getExtensions` method on various JSON reader classes to `getOptions`.
- `IExtensionJsonHandler` no longer derives from `IJsonHandler`. Instead, it has a new pure virtual method, `getHandler`, that must be implemented to allow clients to obtain the `IJsonHandler`. In almost all implementations, this should simply return `*this`.
- In `SubtreeReader`, `SchemaReader`, and `TilesetReader`, the `readSubtree`, `readSchema`, and `readTileset` methods (respectively) have been renamed to `readFromJson` and return a templated `ReadJsonResult` instead of a bespoke result class.
- `TileExternalContent` is now heap allocated and stored in `TileContent` with a `std::unique_ptr`.
- The root `Tile` of a `Cesium3DTilesSelection::Tileset` now represents the tileset.json itself, and the `root` tile specified in the tileset.json is its only child. This makes the shape of the tile tree consistent between a standard top-level tileset and an external tileset embedded elsewhere in the tree. In both cases, the "tile" that represents the tileset.json itself has content of type `TileExternalContent`.

##### Additions :tada:

- Unknown properties in objects read with a `JsonReader` are now stored in the `unknownProperties` property on `ExtensibleObject` by default. To ignore them, as was done in previous versions, call `setCaptureUnknownProperties` on `JsonReaderOptions`.
- Added `ValueType` type alias to `ArrayJsonHandler`, for consistency with other JSON handlers.
- Added an overload of `JsonReader::readJson` that takes a `rapidjson::Value` instead of a byte buffer. This allows a subtree of a `rapidjson::Document` to be easily and efficiently converted into statically-typed classes via `IJsonHandler`.
- Added `*Reader` classes to `CesiumGltfReader` and `Cesium3DTilesReader` to allow each of the classes to be individually read from JSON.
- Added `getExternalContent` method to the `TileContent` class.
- `TileExternalContent` now holds the metadata (`schema`, `schemaUri`, `metadata`, and `groups`) stored in the tileset.json.
- Added `loadMetadata` and `getMetadata` methods to `Cesium3DTilesSelection::Tileset`. They provide access to `TilesetMetadata` instance representing the metadata associated with a tileset.json.
- Added `MetadataQuery` class to make it easier to find properties with specific semantics in `TilesetMetadata`.

### v0.27.0 - 2023-09-01

Expand Down
74 changes: 74 additions & 0 deletions Cesium3DTiles/include/Cesium3DTiles/MetadataQuery.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
#pragma once

#include <Cesium3DTiles/MetadataEntity.h>
#include <Cesium3DTiles/Schema.h>
#include <CesiumUtility/JsonValue.h>

#include <string>
#include <unordered_map>

namespace Cesium3DTiles {

/**
* @brief Holds the details of a found property in a {@link MetadataEntity}.
*
* Because this structure holds _references_ to the original {@link Schema} and
* {@link MetadataEntity} instances, it will be invalided if either are
* destroyed or modified. Continuing to access this result in that scenario will
* result in undefined behavior.
*/
struct CESIUM3DTILES_API FoundMetadataProperty {
/**
* @brief A reference to the identifier of the class that contains the found
* property within the {@link Schema}.
*/
const std::string& classIdentifier;

/**
* @brief A reference to the {@link Class} that contains the found property
* within the {@link Schema}.
*/
const Class& classDefinition;

/**
* @brief A reference to the identifier of the found property within the
* {@link Schema}.
*/
const std::string& propertyIdentifier;

/**
* @brief A reference to the {@link ClassProperty} describing the found
* property within the {@lnik Schema}.
*/
const ClassProperty& propertyDefinition;

/**
* @brief A reference to the value of the found property within the
* {@link MetadataEntity}.
*/
const CesiumUtility::JsonValue& propertyValue;
};

/**
* @brief Convenience functions for querying {@link MetadataEntity} instances.
*/
class CESIUM3DTILES_API MetadataQuery {
public:
/**
* @brief Gets the first property with a given
* {@link ClassProperty::semantic}.
*
* @param schema The schema to use to look up semantics.
* @param entity The metadata entity to search for a property with the
* semantic.
* @param semantic The semantic to find.
* @return The details of the found property, or `std::nullopt` if a property
* with the given semantic does not exist.
*/
static std::optional<FoundMetadataProperty> findFirstPropertyWithSemantic(
const Schema& schema,
const MetadataEntity& entity,
const std::string& semantic);
};

} // namespace Cesium3DTiles
38 changes: 38 additions & 0 deletions Cesium3DTiles/src/MetadataQuery.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#include <Cesium3DTiles/MetadataQuery.h>

namespace Cesium3DTiles {

std::optional<FoundMetadataProperty>
MetadataQuery::findFirstPropertyWithSemantic(
const Schema& schema,
const MetadataEntity& entity,
const std::string& semantic) {
auto classIt = schema.classes.find(entity.classProperty);
if (classIt == schema.classes.end()) {
return std::nullopt;
}

const Cesium3DTiles::Class& klass = classIt->second;

for (auto it = entity.properties.begin(); it != entity.properties.end();
++it) {
const std::pair<std::string, CesiumUtility::JsonValue>& property = *it;
auto propertyIt = klass.properties.find(property.first);
if (propertyIt == klass.properties.end())
continue;

const ClassProperty& classProperty = propertyIt->second;
if (classProperty.semantic == semantic) {
return FoundMetadataProperty{
classIt->first,
classIt->second,
it->first,
propertyIt->second,
it->second};
}
}

return std::nullopt;
}

} // namespace Cesium3DTiles
55 changes: 55 additions & 0 deletions Cesium3DTiles/test/TestMetadataQuery.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
#include <Cesium3DTiles/MetadataQuery.h>

#include <catch2/catch.hpp>

using namespace Cesium3DTiles;
using namespace CesiumUtility;

TEST_CASE("MetadataQuery") {
SECTION("findFirstPropertyWithSemantic") {
Schema schema{};
Class& classDefinition =
schema.classes.emplace("someClass", Class()).first->second;

ClassProperty& classProperty1 =
classDefinition.properties.emplace("someProperty", ClassProperty())
.first->second;
classProperty1.type = ClassProperty::Type::SCALAR;
classProperty1.componentType = ClassProperty::ComponentType::FLOAT64;

ClassProperty& classProperty2 =
classDefinition.properties
.emplace("somePropertyWithSemantic", ClassProperty())
.first->second;
classProperty2.type = ClassProperty::Type::STRING;
classProperty2.semantic = "SOME_SEMANTIC";

MetadataEntity withoutSemantic;
withoutSemantic.classProperty = "someClass";
withoutSemantic.properties.emplace("someProperty", JsonValue(3.0));

MetadataEntity withSemantic = withoutSemantic;
withSemantic.properties.emplace(
"somePropertyWithSemantic",
JsonValue("the value"));

std::optional<FoundMetadataProperty> foundProperty1 =
MetadataQuery::findFirstPropertyWithSemantic(
schema,
withoutSemantic,
"SOME_SEMANTIC");
CHECK(!foundProperty1);

std::optional<FoundMetadataProperty> foundProperty2 =
MetadataQuery::findFirstPropertyWithSemantic(
schema,
withSemantic,
"SOME_SEMANTIC");
REQUIRE(foundProperty2);
CHECK(foundProperty2->classIdentifier == "someClass");
CHECK(&foundProperty2->classDefinition == &classDefinition);
CHECK(foundProperty2->propertyIdentifier == "somePropertyWithSemantic");
CHECK(&foundProperty2->propertyDefinition == &classProperty2);
CHECK(foundProperty2->propertyValue.getStringOrDefault("") == "the value");
}
}
2 changes: 2 additions & 0 deletions Cesium3DTilesSelection/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ target_include_directories(

target_link_libraries(Cesium3DTilesSelection
PUBLIC
Cesium3DTiles
Cesium3DTilesReader
CesiumAsync
CesiumGeospatial
CesiumGeometry
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ class CESIUM3DTILESSELECTION_API Tile final {
*/
Tile(
TilesetContentLoader* pLoader,
TileExternalContent externalContent) noexcept;
std::unique_ptr<TileExternalContent>&& externalContent) noexcept;

/**
* @brief Construct a tile with an empty content and a loader that is
Expand Down
33 changes: 23 additions & 10 deletions Cesium3DTilesSelection/include/Cesium3DTilesSelection/TileContent.h
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@
#include "CreditSystem.h"
#include "Library.h"
#include "RasterOverlayDetails.h"
#include "TilesetMetadata.h"

#include <CesiumGeospatial/Projection.h>
#include <CesiumGltf/Model.h>

#include <memory>
#include <variant>
#include <vector>

namespace Cesium3DTilesSelection {
/**
Expand Down Expand Up @@ -47,7 +49,12 @@ struct CESIUM3DTILESSELECTION_API TileEmptyContent {};
* external tileset. When this tile is loaded, all the tiles in the
* external tileset will become children of this external content tile
*/
struct CESIUM3DTILESSELECTION_API TileExternalContent {};
struct CESIUM3DTILESSELECTION_API TileExternalContent {
/**
* @brief The metadata associated with this tileset.
*/
TilesetMetadata metadata;
};

/**
* @brief A content tag that indicates a tile has a glTF model content and
Expand Down Expand Up @@ -198,7 +205,7 @@ class CESIUM3DTILESSELECTION_API TileContent {
using TileContentKindImpl = std::variant<
TileUnknownContent,
TileEmptyContent,
TileExternalContent,
std::unique_ptr<TileExternalContent>,
std::unique_ptr<TileRenderContent>>;

public:
Expand All @@ -218,7 +225,7 @@ class CESIUM3DTILESSELECTION_API TileContent {
* @brief Construct an external content for a tile whose content
* points to an external tileset
*/
TileContent(TileExternalContent content);
TileContent(std::unique_ptr<TileExternalContent>&& content);

/**
* @brief Set an unknown content tag for a tile. This constructor
Expand All @@ -236,7 +243,7 @@ class CESIUM3DTILESSELECTION_API TileContent {
* @brief Set an external content for a tile whose content
* points to an external tileset
*/
void setContentKind(TileExternalContent content);
void setContentKind(std::unique_ptr<TileExternalContent>&& content);

/**
* @brief Set a glTF model content for a tile
Expand Down Expand Up @@ -267,21 +274,27 @@ class CESIUM3DTILESSELECTION_API TileContent {
/**
* @brief Get the {@link TileRenderContent} which stores the glTF model
* and render resources of the tile
*
* @return The {@link TileRenderContent} which stores the glTF model
* and render resources of the tile
*/
const TileRenderContent* getRenderContent() const noexcept;

/**
* @brief Get the {@link TileRenderContent} which stores the glTF model
* and render resources of the tile
*
* @return The {@link TileRenderContent} which stores the glTF model
* and render resources of the tile
*/
TileRenderContent* getRenderContent() noexcept;

/**
* @brief Get the {@link TileExternalContent} which stores the details of
* the external tileset.
*/
const TileExternalContent* getExternalContent() const noexcept;

/**
* @brief Get the {@link TileExternalContent} which stores the details of
* the external tileset.
*/
TileExternalContent* getExternalContent() noexcept;

private:
TileContentKindImpl _contentKind;
};
Expand Down
65 changes: 58 additions & 7 deletions Cesium3DTilesSelection/include/Cesium3DTilesSelection/Tileset.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

namespace Cesium3DTilesSelection {
class TilesetContentManager;
class TilesetMetadata;

/**
* @brief A <a
Expand Down Expand Up @@ -74,13 +75,6 @@ class CESIUM3DTILESSELECTION_API Tileset final {
const TilesetOptions& options = TilesetOptions(),
const std::string& ionAssetEndpointUrl = "https://api.cesium.com/");

/**
* @brief A future that resolves when this Tileset has been destroyed (i.e.
* its destructor has been called) and all async operations that it was
* executing have completed.
*/
CesiumAsync::SharedFuture<void>& getAsyncDestructionCompleteEvent();

/**
* @brief Destroys this tileset.
*
Expand All @@ -93,6 +87,20 @@ class CESIUM3DTILESSELECTION_API Tileset final {
*/
~Tileset() noexcept;

/**
* @brief A future that resolves when this Tileset has been destroyed (i.e.
* its destructor has been called) and all async operations that it was
* executing have completed.
*/
CesiumAsync::SharedFuture<void>& getAsyncDestructionCompleteEvent();

/**
* @brief A future that resolves when the details of the root tile of this
* tileset are available. The root tile's content (e.g., 3D model), however,
* will not necessarily be loaded yet.
*/
CesiumAsync::SharedFuture<void>& getRootTileAvailableEvent();

/**
* @brief Get tileset credits.
*/
Expand Down Expand Up @@ -207,6 +215,49 @@ class CESIUM3DTILESSELECTION_API Tileset final {
*/
int64_t getTotalDataBytes() const noexcept;

/**
* @brief Gets the {@link TilesetMetadata} associated with the main or
* external tileset.json that contains a given tile. If the metadata is not
* yet loaded, this method returns nullptr.
*
* If this tileset's root tile is not yet available, this method returns
* nullptr.
*
* If the tileset has a {@link TilesetMetadata::schemaUri}, it will not
* necessarily have been loaded yet.
*
* If the provided tile is not the root tile of a tileset.json, this method
* walks up the {@link Tile::getParent} chain until it finds the closest
* root and then returns the metadata associated with the corresponding
* tileset.json.
*
* Consider calling {@link loadMetadata} instead, which will return a future
* that only resolves after the root tile is loaded and the `schemaUri`, if
* any, has been resolved.
*
* @param pTile The tile. If this parameter is nullptr, the metadata for the
* main tileset.json is returned.
* @return The found metadata, or nullptr if the root tile is not yet loaded.
*/
const TilesetMetadata* getMetadata(const Tile* pTile = nullptr) const;

/**
* @brief Asynchronously loads the metadata associated with the main
* tileset.json.
*
* Before the returned future resolves, the root tile of this tileset will be
* loaded and the {@link TilesetMetadata::schemaUri} will be loaded if one
* has been specified.
*
* If the tileset or `schemaUri` fail to load, the returned future will
* reject.
*
* @return A shared future that resolves to the loaded metadata. Once this
* future resolves, {@link findMetadata} can be used to synchronously obtain
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* future resolves, {@link findMetadata} can be used to synchronously obtain
* future resolves, {@link getMetadata} can be used to synchronously obtain

* the same metadata instance.
*/
CesiumAsync::Future<const TilesetMetadata*> loadMetadata();

private:
/**
* @brief The result of traversing one branch of the tile hierarchy.
Expand Down
Loading