Add tests, fix a bug.
This commit is contained in:
parent
eb983f103f
commit
08d098f985
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
||||
|
|
|
|||
|
|
@ -1362,7 +1362,8 @@ void updateExtensionWithBatchTableHierarchy(
|
|||
}
|
||||
|
||||
BatchTableHierarchyPropertyValues batchTableHierarchyValues(
|
||||
batchTableHierarchy);
|
||||
batchTableHierarchy,
|
||||
featureTable.count);
|
||||
|
||||
for (const std::string& name : properties) {
|
||||
ClassProperty& classProperty =
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue