Add tests, fix a bug.

This commit is contained in:
Kevin Ring 2022-04-28 09:20:46 +10:00
parent eb983f103f
commit 08d098f985
4 changed files with 241 additions and 93 deletions

View File

@ -13,8 +13,10 @@ rapidjson::Value createEmptyArray() {
} // namespace
BatchTableHierarchyPropertyValues::BatchTableHierarchyPropertyValues(
const rapidjson::Value& batchTableHierarchy)
const rapidjson::Value& batchTableHierarchy,
int64_t batchLength)
: _batchTableHierarchy(batchTableHierarchy),
_batchLength(batchLength),
_pClassIDs(nullptr),
_pParentIDs(nullptr),
_instanceIndices(),
@ -124,11 +126,11 @@ BatchTableHierarchyPropertyValues::begin() const {
BatchTableHierarchyPropertyValues::const_iterator
BatchTableHierarchyPropertyValues::end() const {
return createIterator(int64_t(this->_instanceIndices.size()));
return createIterator(this->size());
}
int64_t BatchTableHierarchyPropertyValues::size() const {
return int64_t(this->_instanceIndices.size());
return std::min(int64_t(this->_instanceIndices.size()), this->_batchLength);
}
BatchTableHierarchyPropertyValues::const_iterator

View File

@ -52,9 +52,12 @@ public:
* This value must remain valid for the entire lifetime of the
* BatchTableHierarchyPropertyValues instance, or undefined behavior will
* result.
* @param batchLength The number of features, which may be less than the
* number of instances in the batch table hierarchy.
*/
BatchTableHierarchyPropertyValues(
const rapidjson::Value& batchTableHierarchy);
const rapidjson::Value& batchTableHierarchy,
int64_t batchLength);
/**
* @brief Sets the name of the property whose values are to be enumerated.
@ -80,11 +83,8 @@ public:
/**
* @brief Gets the total number of features.
*
* Note that because a batch table hierarchy has extra entities that do not
* correspond to actual features, this size may be greater than the number of
* features. A correctly-constructed batch table hierarchy should not report a
* size less than the number of features, but clients must not assume that the
* batch table hierarchy is correctly constructed.
* This is the smaller of the number of features (given to the constructor as
* `batchLength`) and the number of instances in the batch table hierarchy.
*/
int64_t size() const;
@ -92,6 +92,7 @@ private:
const_iterator createIterator(int64_t index) const;
const rapidjson::Value& _batchTableHierarchy;
int64_t _batchLength;
const rapidjson::Value* _pClassIDs;
const rapidjson::Value* _pParentIDs;

View File

@ -1362,7 +1362,8 @@ void updateExtensionWithBatchTableHierarchy(
}
BatchTableHierarchyPropertyValues batchTableHierarchyValues(
batchTableHierarchy);
batchTableHierarchy,
featureTable.count);
for (const std::string& name : properties) {
ClassProperty& classProperty =

View File

@ -1192,7 +1192,7 @@ TEST_CASE("Cannot write pass batch length table") {
}
}
TEST_CASE("Converts Feature Classes 3DTILES_batch_table_hierarchy to "
TEST_CASE("Converts Feature Classes 3DTILES_batch_table_hierarchy example to "
"EXT_feature_metadata") {
Model gltf;
@ -1279,95 +1279,239 @@ TEST_CASE("Converts Feature Classes 3DTILES_batch_table_hierarchy to "
// Even though some of these properties are numeric, they become STRING
// because not every feature has every property, and only STRING can
// represent null.
std::map<std::string, std::string> expectedProperties{
std::make_pair("lampStrength", "STRING"),
std::make_pair("lampColor", "STRING"),
std::make_pair("carType", "STRING"),
std::make_pair("carColor", "STRING"),
std::make_pair("treeHeight", "STRING"),
std::make_pair("treeAge", "STRING")};
struct Expected {
std::string name;
std::string type;
std::vector<std::string> values;
};
std::vector<Expected> expectedProperties{
{"lampStrength",
"STRING",
{"10", "5", "7", "null", "null", "null", "null", "null"}},
{"lampColor",
"STRING",
{"yellow", "white", "white", "null", "null", "null", "null", "null"}},
{"carType",
"STRING",
{"null", "null", "null", "truck", "bus", "sedan", "null", "null"}},
{"carColor",
"STRING",
{"null", "null", "null", "green", "blue", "red", "null", "null"}},
{"treeHeight",
"STRING",
{"null", "null", "null", "null", "null", "null", "10", "15"}},
{"treeAge",
"STRING",
{"null", "null", "null", "null", "null", "null", "5", "8"}}};
for (const auto& expected : expectedProperties) {
auto it = defaultClass.properties.find(expected.first);
auto it = defaultClass.properties.find(expected.name);
REQUIRE(it != defaultClass.properties.end());
CHECK(it->second.type == expected.second);
}
CHECK(it->second.type == expected.type);
{
std::vector<std::string> expected =
{"10", "5", "7", "null", "null", "null", "null", "null"};
checkScalarProperty<std::string, std::string_view>(
gltf,
featureTable,
defaultClass,
"lampStrength",
"STRING",
expected,
expected.size());
}
{
std::vector<std::string> expected =
{"yellow", "white", "white", "null", "null", "null", "null", "null"};
checkScalarProperty<std::string, std::string_view>(
gltf,
featureTable,
defaultClass,
"lampColor",
"STRING",
expected,
expected.size());
}
{
std::vector<std::string> expected =
{"null", "null", "null", "truck", "bus", "sedan", "null", "null"};
checkScalarProperty<std::string, std::string_view>(
gltf,
featureTable,
defaultClass,
"carType",
"STRING",
expected,
expected.size());
}
{
std::vector<std::string> expected =
{"null", "null", "null", "green", "blue", "red", "null", "null"};
checkScalarProperty<std::string, std::string_view>(
gltf,
featureTable,
defaultClass,
"carColor",
"STRING",
expected,
expected.size());
}
{
std::vector<std::string> expected =
{"null", "null", "null", "null", "null", "null", "10", "15"};
checkScalarProperty<std::string, std::string_view>(
gltf,
featureTable,
defaultClass,
"treeHeight",
"STRING",
expected,
expected.size());
}
{
std::vector<std::string> expected =
{"null", "null", "null", "null", "null", "null", "5", "8"};
checkScalarProperty<std::string, std::string_view>(
gltf,
featureTable,
defaultClass,
"treeAge",
"STRING",
expected,
expected.size());
expected.name,
expected.type,
expected.values,
expected.values.size());
}
}
TEST_CASE("Converts Feature Hierarchy 3DTILES_batch_table_hierarchy example to "
"EXT_feature_metadata") {
Model gltf;
std::string featureTableJson = R"(
{
"BATCH_LENGTH": 6
}
)";
// "Feature hierarchy" example from the spec:
// https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_batch_table_hierarchy#feature-hierarchy
std::string batchTableJson = R"(
{
"extensions" : {
"3DTILES_batch_table_hierarchy" : {
"classes" : [
{
"name" : "Wall",
"length" : 6,
"instances" : {
"wall_color" : ["blue", "pink", "green", "lime", "black", "brown"],
"wall_windows" : [2, 4, 4, 2, 0, 3]
}
},
{
"name" : "Building",
"length" : 3,
"instances" : {
"building_name" : ["building_0", "building_1", "building_2"],
"building_id" : [0, 1, 2],
"building_address" : ["10 Main St", "12 Main St", "14 Main St"]
}
},
{
"name" : "Block",
"length" : 1,
"instances" : {
"block_lat_long" : [[0.12, 0.543]],
"block_district" : ["central"]
}
}
],
"instancesLength" : 10,
"classIds" : [0, 0, 0, 0, 0, 0, 1, 1, 1, 2],
"parentIds" : [6, 6, 7, 7, 8, 8, 9, 9, 9, 9]
}
}
}
)";
rapidjson::Document featureTableParsed;
featureTableParsed.Parse(featureTableJson.data(), featureTableJson.size());
rapidjson::Document batchTableParsed;
batchTableParsed.Parse(batchTableJson.data(), batchTableJson.size());
upgradeBatchTableToFeatureMetadata(
spdlog::default_logger(),
gltf,
featureTableParsed,
batchTableParsed,
gsl::span<const std::byte>());
ExtensionModelExtFeatureMetadata* pExtension =
gltf.getExtension<ExtensionModelExtFeatureMetadata>();
REQUIRE(pExtension);
// Check the schema
REQUIRE(pExtension->schema);
REQUIRE(pExtension->schema->classes.size() == 1);
auto firstClassIt = pExtension->schema->classes.begin();
CHECK(firstClassIt->first == "default");
CesiumGltf::Class& defaultClass = firstClassIt->second;
REQUIRE(defaultClass.properties.size() == 7);
// Check the feature table
auto firstFeatureTableIt = pExtension->featureTables.begin();
REQUIRE(firstFeatureTableIt != pExtension->featureTables.end());
FeatureTable& featureTable = firstFeatureTableIt->second;
CHECK(featureTable.classProperty == "default");
REQUIRE(featureTable.properties.size() == 7);
struct ExpectedString {
std::string name;
std::string type;
std::vector<std::string> values;
};
std::vector<ExpectedString> expectedStringProperties{
{"wall_color",
"STRING",
{"blue", "pink", "green", "lime", "black", "brown"}},
{"building_name",
"STRING",
{"building_0",
"building_0",
"building_1",
"building_1",
"building_2",
"building_2"}},
{"building_address",
"STRING",
{"10 Main St",
"10 Main St",
"12 Main St",
"12 Main St",
"14 Main St",
"14 Main St"}},
{"block_district",
"STRING",
{"central", "central", "central", "central", "central", "central"}}};
for (const auto& expected : expectedStringProperties) {
auto it = defaultClass.properties.find(expected.name);
REQUIRE(it != defaultClass.properties.end());
CHECK(it->second.type == expected.type);
checkScalarProperty<std::string, std::string_view>(
gltf,
featureTable,
defaultClass,
expected.name,
expected.type,
expected.values,
expected.values.size());
}
struct ExpectedInt8Properties {
std::string name;
std::string type;
std::vector<int8_t> values;
};
std::vector<ExpectedInt8Properties> expectedInt8Properties{
{"wall_windows", "INT8", {2, 4, 4, 2, 0, 3}},
{"building_id", "INT8", {0, 0, 1, 1, 2, 2}},
};
for (const auto& expected : expectedInt8Properties) {
auto it = defaultClass.properties.find(expected.name);
REQUIRE(it != defaultClass.properties.end());
CHECK(it->second.type == expected.type);
checkScalarProperty<int8_t>(
gltf,
featureTable,
defaultClass,
expected.name,
expected.type,
expected.values,
expected.values.size());
}
struct ExpectedDoubleArrayProperties {
std::string name;
std::string type;
std::string componentType;
int64_t componentCount;
std::vector<std::vector<double>> values;
};
std::vector<ExpectedDoubleArrayProperties> expectedDoubleArrayProperties{
{"block_lat_long",
"ARRAY",
"FLOAT64",
2,
{{0.12, 0.543},
{0.12, 0.543},
{0.12, 0.543},
{0.12, 0.543},
{0.12, 0.543},
{0.12, 0.543}}}};
for (const auto& expected : expectedDoubleArrayProperties) {
auto it = defaultClass.properties.find(expected.name);
REQUIRE(it != defaultClass.properties.end());
CHECK(it->second.type == expected.type);
CHECK(it->second.componentType == expected.componentType);
checkArrayProperty<double>(
gltf,
featureTable,
defaultClass,
expected.name,
expected.componentCount,
expected.componentType,
expected.values,
expected.values.size());
}
}