diff --git a/src/validation/ValidationContext.ts b/src/validation/ValidationContext.ts index 12abbe34..4cc2b827 100644 --- a/src/validation/ValidationContext.ts +++ b/src/validation/ValidationContext.ts @@ -63,6 +63,13 @@ export class ValidationContext { */ private _activeTilesetUris: Set; + /** + * The set of schema objects that will be used for validating + * metadata class property semantics, in the + * `ClassPropertySemanticsValidator`. + */ + private _semanticMatchingSchemas: Set; + constructor( baseUri: string, resourceResolver: ResourceResolver, @@ -74,6 +81,7 @@ export class ValidationContext { this._resourceResolver = resourceResolver; this._extensionsFound = new Set(); this._activeTilesetUris = new Set(); + this._semanticMatchingSchemas = new Set(); } /** @@ -105,6 +113,7 @@ export class ValidationContext { ); derived._extensionsFound = new Set(); derived._activeTilesetUris = this._activeTilesetUris; + derived._semanticMatchingSchemas = this._semanticMatchingSchemas; return derived; } @@ -140,6 +149,7 @@ export class ValidationContext { ); derived._extensionsFound = new Set(); derived._activeTilesetUris = this._activeTilesetUris; + derived._semanticMatchingSchemas = this._semanticMatchingSchemas; return derived; } @@ -181,6 +191,14 @@ export class ValidationContext { return this._activeTilesetUris.has(uri); } + addSemanticMatchingSchema(schema: any) { + this._semanticMatchingSchemas.add(schema); + } + + getSemanticMatchingSchemas(): any[] { + return [...this._semanticMatchingSchemas]; + } + getOptions(): ValidationOptions { return this._options; } diff --git a/src/validation/ValidationContexts.ts b/src/validation/ValidationContexts.ts new file mode 100644 index 00000000..07b015ab --- /dev/null +++ b/src/validation/ValidationContexts.ts @@ -0,0 +1,269 @@ +import fs from "fs"; + +import { ValidationContext } from "./ValidationContext"; + +/** + * Methods related to `ValidationContext` instances + */ +export class ValidationContexts { + /** + * The metadata schema that contains the definitions of the + * Cesium Metadata semantics. + * + * Note that this is not a valid `Schema` object, for the reasons + * described in `addSemanticMatchingSchema` + */ + private static cesiumMetadataSemanticsSchema = { + id: "CesiumMetadataSemantics-0.0.1", + classes: { + GeneralSemantics: { + properties: { + ID: { + description: "The unique identifier for the entity.", + type: "STRING", + }, + NAME: { + description: + "The name of the entity. Names should be human-readable, and do not have to be unique.", + type: "STRING", + }, + DESCRIPTION: { + description: + "Description of the entity. Typically at least a phrase, and possibly several sentences or paragraphs.", + type: "STRING", + }, + ATTRIBUTION_IDS: { + description: + "List of attribution IDs that index into a global list of attribution strings. This semantic may be assigned to metadata at any level of granularity including tileset, group, subtree, tile, content, feature, vertex, and texel granularity. The global list of attribution strings is located in a tileset or subtree with the property semantic ATTRIBUTION_STRINGS. The following precedence order is used to locate the attribution strings: first the containing subtree (if applicable), then the containing external tileset (if applicable), and finally the root tileset.", + type: "SCALAR", + array: true, + componentType: "UINT(8|16|32|64)", + }, + ATTRIBUTION_STRINGS: { + description: + "List of attribution strings. Each string contains information about a data provider or copyright text. Text may include embedded markup languages such as HTML. This semantic may be assigned to metadata at any granularity (wherever STRING property values can be encoded). When used in combination with ATTRIBUTION_IDS it is assigned to subtrees and tilesets.", + type: "STRING", + array: true, + }, + }, + }, + TilesetMetadataSemantics: { + properties: { + TILESET_FEATURE_ID_LABELS: { + description: + "The union of all the feature ID labels in glTF content using the EXT_mesh_features and EXT_instance_features extensions.", + type: "STRING", + array: true, + }, + TILESET_CRS_GEOCENTRIC: { + description: + "The geocentric coordinate reference system (CRS) of the tileset.", + type: "STRING", + }, + TILESET_CRS_COORDINATE_EPOCH: { + description: + "The coordinate epoch for coordinates that are referenced to a dynamic CRS such as WGS 84.", + type: "STRING", + }, + }, + }, + TileMetadataSemantics: { + properties: { + TILE_BOUNDING_BOX: { + description: + "The bounding volume of the tile, expressed as a box. Equivalent to tile.boundingVolume.box.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + array: true, + count: 12, + }, + TILE_BOUNDING_REGION: { + description: + "The bounding volume of the tile, expressed as a region. Equivalent to tile.boundingVolume.region.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + array: true, + count: 6, + }, + TILE_BOUNDING_SPHERE: { + description: + "The bounding volume of the tile, expressed as a sphere. Equivalent to tile.boundingVolume.sphere.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + array: true, + count: 4, + }, + TILE_BOUNDING_S2_CELL: { + description: + "The bounding volume of the tile, expressed as an S2 Cell ID using the 64-bit representation instead of the hexadecimal representation. Only applicable to 3DTILES_bounding_volume_S2.", + type: "SCALAR", + componentType: "UINT64", + }, + TILE_MINIMUM_HEIGHT: { + description: + "The minimum height of the tile above (or below) the WGS84 ellipsoid.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + }, + TILE_MAXIMUM_HEIGHT: { + description: + "The maximum height of the tile above (or below) the WGS84 ellipsoid.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + }, + TILE_HORIZON_OCCLUSION_POINT: { + description: + "The horizon occlusion point of the tile expressed in an ellipsoid-scaled fixed frame. If this point is below the horizon, the entire tile is below the horizon.", + type: "VEC3", + componentType: "FLOAT(32|64)", + }, + TILE_GEOMETRIC_ERROR: { + description: + "The geometric error of the tile. Equivalent to tile.geometricError.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + }, + TILE_REFINE: { + description: + "The tile refinement type. Valid values are 0 (ADD) and 1 (REPLACE). Equivalent to tile.refine.", + type: "SCALAR", + componentType: "UINT8", + }, + TILE_TRANSFORM: { + description: "The tile transform. Equivalent to tile.transform.", + type: "MAT4", + componentType: "FLOAT(32|64)", + }, + }, + }, + ContentMetadataSemantics: { + properties: { + CONTENT_BOUNDING_BOX: { + description: + "The bounding volume of the content of a tile, expressed as a box. Equivalent to tile.content.boundingVolume.box.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + array: true, + count: 12, + }, + CONTENT_BOUNDING_REGION: { + description: + "The bounding volume of the content of a tile, expressed as a region. Equivalent to tile.content.boundingVolume.region.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + array: true, + count: 6, + }, + CONTENT_BOUNDING_SPHERE: { + description: + "The bounding volume of the content of a tile, expressed as a sphere. Equivalent to tile.content.boundingVolume.sphere.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + array: true, + count: 4, + }, + CONTENT_BOUNDING_S2_CELL: { + description: + "The bounding volume of the content of a tile, expressed as an S2 Cell ID using the 64-bit representation instead of the hexadecimal representation. Only applicable to 3DTILES_bounding_volume_S2.", + type: "SCALAR", + componentType: "UINT64", + }, + CONTENT_MINIMUM_HEIGHT: { + description: + "The minimum height of the content of a tile above (or below) the WGS84 ellipsoid.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + }, + CONTENT_MAXIMUM_HEIGHT: { + description: + "The maximum height of the content of a tile above (or below) the WGS84 ellipsoid.", + type: "SCALAR", + componentType: "FLOAT(32|64)", + }, + CONTENT_HORIZON_OCCLUSION_POINT: { + description: + "The horizon occlusion point of the content of a tile expressed in an ellipsoid-scaled fixed frame. If this point is below the horizon, the entire content is below the horizon.", + type: "VEC3", + componentType: "FLOAT(32|64)", + }, + CONTENT_URI: { + description: + "The content uri. Overrides the implicit tile's generated content uri. Equivalent to tile.content.uri", + type: "STRING", + componentType: "FLOAT(32|64)", + }, + CONTENT_GROUP_ID: { + description: + "The content group ID. Equivalent to tile.content.group.", + type: "SCALAR", + componentType: "UINT(8|16|32|64)", + }, + }, + }, + }, + }; + + /** + * Initialize the schemas that are used for matching metadata property + * semantics. + * + * This will register the default Cesium Metadata Semantic definitions, + * as well as the semantic definitions from the specified schema files. + * + * @param context - The validation context + * @param schemaFileNames - The schema file names + */ + static initializeSemanticMatchingSchemas( + context: ValidationContext, + schemaFileNames: string[] | undefined + ) { + context.addSemanticMatchingSchema( + ValidationContexts.cesiumMetadataSemanticsSchema + ); + if (schemaFileNames) { + for (const schemaFileName of schemaFileNames) { + console.log( + "Registering metadata schema semantic definitions from ", + schemaFileName + ); + ValidationContexts.addSemanticMatchingSchema(context, schemaFileName); + } + } + } + + /** + * Add the specified schema to the given context, as a schema to be used + * for matching metadata property semantics. + * + * The specified file is supposed to contain a full, valid metadata schema, + * where the property names are just semantic names. + * + * To support legacy semantic definitions, it is allowed for the + * `matchingSchema.classes[className].properties[semanticName].componentType` + * to be a string that is used for creating a regular expression that the + * actual `componentType` has to match. E.g. this may be `"FLOAT(32|64)"` + * when the component type can either be `FLOAT32` or `FLOAT64`. + * + * Eventually, it might make sense to make the component types + * unambiguous, so that the semantics definition is actually + * a proper `Schema`. This could be achieved by specific semantics + * like `GEOMETRIC_ERROR_FLOAT32`. + * + * See https://github.com/CesiumGS/3d-tiles/issues/643 + * + * @param context - The validation context + * @param schemaFileName - The schema file name + */ + private static addSemanticMatchingSchema( + context: ValidationContext, + schemaFileName: string + ) { + try { + const fileContents = fs.readFileSync(schemaFileName); + const schema = JSON.parse(fileContents.toString()); + context.addSemanticMatchingSchema(schema); + } catch (e) { + console.error(e); + } + } +} diff --git a/src/validation/ValidationOptions.ts b/src/validation/ValidationOptions.ts index a182992a..9cc3737c 100644 --- a/src/validation/ValidationOptions.ts +++ b/src/validation/ValidationOptions.ts @@ -32,6 +32,12 @@ export class ValidationOptions { */ private _excludeContentTypes: string[] | undefined; + /** + * The names of files that contain metadata schemas with + * semantic definitions + */ + private _semanticSchemaFileNames: string[] | undefined; + /** * Default constructor. * @@ -48,10 +54,11 @@ export class ValidationOptions { this._contentValidationIssueSeverity = ValidationIssueSeverity.INFO; this._includeContentTypes = undefined; this._excludeContentTypes = undefined; + this._semanticSchemaFileNames = undefined; } /** - * The flag that incicates whether content data should + * The flag that indicates whether content data should * be validated at all. When this is `false`, then * all content data validations will be skipped. */ @@ -138,6 +145,18 @@ export class ValidationOptions { this._excludeContentTypes = value; } + /** + * The names of files that contain metadata schema definitions + * for the valid metadata semantics + */ + get semanticSchemaFileNames(): string[] | undefined { + return this._semanticSchemaFileNames; + } + + set semanticSchemaFileNames(value: string[] | undefined) { + this._semanticSchemaFileNames = value; + } + /** * Creates a new `ValidationOptions` object where each property is * initialized from the given JSON object. diff --git a/src/validation/Validators.ts b/src/validation/Validators.ts index 6a8050e0..171ce97a 100644 --- a/src/validation/Validators.ts +++ b/src/validation/Validators.ts @@ -25,6 +25,7 @@ import { ContentValidationIssues } from "../issues/ContentValidationIssues"; import { BoundingVolumeS2Validator } from "./extensions/BoundingVolumeS2Validator"; import { NgaGpmValidator } from "./extensions/NgaGpmValidator"; +import { ValidationContexts } from "./ValidationContexts"; /** * Utility methods related to `Validator` instances. @@ -57,7 +58,7 @@ export class Validators { * returns a promise to the `ValidationResult`. * * The given file may be a `tileset.json` file, or a tileset - * package file, as incdicated by a `.3tz` or `.3dtiles` file + * package file, as indicated by a `.3tz` or `.3dtiles` file * extensions. * * @param filePath - The file path @@ -118,6 +119,11 @@ export class Validators { resourceResolver, validationOptions ); + ValidationContexts.initializeSemanticMatchingSchemas( + context, + validationOptions?.semanticSchemaFileNames + ); + const tilesetUri = context.resolveUri(fileName); context.addActiveTilesetUri(tilesetUri); const resourceData = await resourceResolver.resolveData(fileName); @@ -169,6 +175,11 @@ export class Validators { resourceResolver, validationOptions ); + ValidationContexts.initializeSemanticMatchingSchemas( + context, + validationOptions?.semanticSchemaFileNames + ); + const tilesetUri = context.resolveUri(filePath); context.addActiveTilesetUri(tilesetUri); await TilesetPackageValidator.validatePackageFile(filePath, context); @@ -205,6 +216,11 @@ export class Validators { resourceResolver, validationOptions ); + ValidationContexts.initializeSemanticMatchingSchemas( + context, + validationOptions?.semanticSchemaFileNames + ); + const contentData = new LazyContentData(fileName, resourceResolver); // Check if the file exists, and bail out early if it doesn't @@ -267,6 +283,7 @@ export class Validators { const resourceData = await resourceResolver.resolveData(fileName); const validator = Validators.createDefaultSchemaValidator(); const context = new ValidationContext(directory, resourceResolver); + ValidationContexts.initializeSemanticMatchingSchemas(context, undefined); const jsonString = resourceData ? resourceData.toString() : ""; await validator.validateJsonString(jsonString, context); return context.getResult(); @@ -319,6 +336,7 @@ export class Validators { implicitTiling ); const context = new ValidationContext(directory, resourceResolver); + ValidationContexts.initializeSemanticMatchingSchemas(context, undefined); if (!defined(resourceData)) { const message = `Could not read subtree file ${filePath}`; const issue = IoValidationIssues.IO_ERROR(filePath, message); diff --git a/src/validation/metadata/ClassPropertySemanticsValidator.ts b/src/validation/metadata/ClassPropertySemanticsValidator.ts index 4d8cb7e7..80664f60 100644 --- a/src/validation/metadata/ClassPropertySemanticsValidator.ts +++ b/src/validation/metadata/ClassPropertySemanticsValidator.ts @@ -60,8 +60,7 @@ export class ClassPropertySemanticsValidator { // Validate that the type of the property matches // the type that is required via the semantic - const matchingSchema = - ClassPropertySemanticsValidator.createMatchingSchema(); + const semanticMatchingSchemas = context.getSemanticMatchingSchemas(); for (const propertyName of Object.keys(properties)) { const property = properties[propertyName]; if (!defined(property)) { @@ -79,7 +78,7 @@ export class ClassPropertySemanticsValidator { // the given semantic, a warning will be created const semanticMatcher = ClassPropertySemanticsValidator.findSemanticMatcher( - matchingSchema, + semanticMatchingSchemas, semantic ); if (!defined(semanticMatcher)) { @@ -143,7 +142,7 @@ export class ClassPropertySemanticsValidator { if (property.type !== semanticMatcher.type) { const message = - `Property '${propertyName} has semantic '${semantic}', ` + + `Property '${propertyName}' has semantic '${semantic}', ` + `which requires type '${semanticMatcher.type}', but the ` + `property has type '${property.type}'`; const issue = MetadataValidationIssues.METADATA_SEMANTIC_INVALID( @@ -159,7 +158,7 @@ export class ClassPropertySemanticsValidator { const regex = new RegExp("^" + semanticMatcher.componentType + "$"); if (!regex.test(componentType)) { const message = - `Property '${propertyName} has semantic '${semantic}', ` + + `Property '${propertyName}' has semantic '${semantic}', ` + `which requires the component type to match ` + `'${semanticMatcher.componentType}', but the ` + `property has component type '${componentType}'`; @@ -175,7 +174,7 @@ export class ClassPropertySemanticsValidator { const propertyArray = defaultValue(property.array, false); if (propertyArray !== matcherArray) { const message = - `Property '${propertyName} has semantic '${semantic}', ` + + `Property '${propertyName}' has semantic '${semantic}', ` + `which requires the 'array' property to be '${matcherArray}' ` + `but the 'array' property is '${property.array}'`; const issue = MetadataValidationIssues.METADATA_SEMANTIC_INVALID( @@ -189,7 +188,7 @@ export class ClassPropertySemanticsValidator { if (property.array === true) { if (property.count !== semanticMatcher.count) { const message = - `Property '${propertyName} has semantic '${semantic}', which ` + + `Property '${propertyName}' has semantic '${semantic}', which ` + `requires the 'count' property to be '${semanticMatcher.count}' ` + `but the 'count' property is '${property.count}'`; const issue = MetadataValidationIssues.METADATA_SEMANTIC_INVALID( @@ -205,7 +204,7 @@ export class ClassPropertySemanticsValidator { const propertyNormalized = defaultValue(property.normalized, false); if (propertyNormalized !== matcherNormalized) { const message = - `Property '${propertyName} has semantic '${semantic}', which ` + + `Property '${propertyName}' has semantic '${semantic}', which ` + `requires the 'normalized' property to be '${matcherNormalized}' ` + `but the 'normalized' property is '${property.normalized}'`; const issue = MetadataValidationIssues.METADATA_SEMANTIC_INVALID( @@ -220,7 +219,7 @@ export class ClassPropertySemanticsValidator { // but the check is done here for completeness if (property.enumType !== semanticMatcher.enumType) { const message = - `Property '${propertyName} has semantic '${semantic}', ` + + `Property '${propertyName}' has semantic '${semantic}', ` + `which requires enumType '${semanticMatcher.enumType}', but the ` + `property has enumType '${property.enumType}'`; const issue = MetadataValidationIssues.METADATA_SEMANTIC_INVALID( @@ -235,13 +234,13 @@ export class ClassPropertySemanticsValidator { } /** - * Finds a "matcher" for the specified semantic in the given matching schema. + * Finds a "matcher" for the specified semantic in the given matching schemas. * - * The given semantic is just the name of the semantic. This is used property - * name in the matching schema. The classes in the given schema are searched - * for a property that has this (semantic) name. If such a "matching property" - * is found, it is returned, and used for checking if the property that - * contained the given semantic matches the "matching property". + * The given semantic is just the name of the semantic. This is used as a + * property name in the matching schema. The classes in the given schemas are + * searched for a property that has this (semantic) name. If such a + * "matching property" is found, it is returned, and used for checking if + * the property that contained the given semantic matches the "matching property". * * Ideally, comparing this "matching property" and the actual property should * check whether the `type`, `component`, and `array` of the matching property @@ -250,238 +249,26 @@ export class ClassPropertySemanticsValidator { * the `componentType` to be a RegEx that the actual component type must * match against. * - * Also see the comments for `createMatchingSchema`. - * * @param matchingSchema - The matching metadata schema to search for semantics * @param semantic - The name of the semantic * @returns The matcher, or `undefined` */ private static findSemanticMatcher( - matchingSchema: any, + matchingSchemas: any, semantic: string ): any { - const matchingClasses = defaultValue(matchingSchema.classes, {}); - for (const className of Object.keys(matchingClasses)) { - const matchingClass = matchingClasses[className]; - const matchingProperties = defaultValue(matchingClass.properties, {}); - for (const semanticName of Object.keys(matchingProperties)) { - if (semanticName === semantic) { - return matchingProperties[semanticName]; + for (const matchingSchema of matchingSchemas) { + const matchingClasses = defaultValue(matchingSchema.classes, {}); + for (const className of Object.keys(matchingClasses)) { + const matchingClass = matchingClasses[className]; + const matchingProperties = defaultValue(matchingClass.properties, {}); + for (const semanticName of Object.keys(matchingProperties)) { + if (semanticName === semantic) { + return matchingProperties[semanticName]; + } } } } return undefined; } - - /** - * Creates an object that resembles a `Schema`, but that is - * used for matching the `semantic` of properties. - * - * The only difference to a `Schema` is that the - * `matchingSchema.classes[className].properties[semanticName].componentType` - * is a string that is used for creating a regular expression that the - * actual `componentType` has to match. E.g. this may be `"FLOAT(32|64)"` - * when the component type can either be `FLOAT32` or `FLOAT64`. - * - * Eventually, it might make sense to make the component types - * unambiguous, so that the semantics definition is actually - * a proper `Schema`. This could be achieved by specific semantics - * like `GEOMETRIC_ERROR_FLOAT32`. - * - * See https://github.com/CesiumGS/3d-tiles/issues/643 - * - * @returns The matching schema - */ - private static createMatchingSchema() { - const matchingSchema = { - id: "CesiumMetadataSemantics-0.0.1", - classes: { - GeneralSemantics: { - properties: { - ID: { - description: "The unique identifier for the entity.", - type: "STRING", - }, - NAME: { - description: - "The name of the entity. Names should be human-readable, and do not have to be unique.", - type: "STRING", - }, - DESCRIPTION: { - description: - "Description of the entity. Typically at least a phrase, and possibly several sentences or paragraphs.", - type: "STRING", - }, - ATTRIBUTION_IDS: { - description: - "List of attribution IDs that index into a global list of attribution strings. This semantic may be assigned to metadata at any level of granularity including tileset, group, subtree, tile, content, feature, vertex, and texel granularity. The global list of attribution strings is located in a tileset or subtree with the property semantic ATTRIBUTION_STRINGS. The following precedence order is used to locate the attribution strings: first the containing subtree (if applicable), then the containing external tileset (if applicable), and finally the root tileset.", - type: "SCALAR", - array: true, - componentType: "UINT(8|16|32|64)", - }, - ATTRIBUTION_STRINGS: { - description: - "List of attribution strings. Each string contains information about a data provider or copyright text. Text may include embedded markup languages such as HTML. This semantic may be assigned to metadata at any granularity (wherever STRING property values can be encoded). When used in combination with ATTRIBUTION_IDS it is assigned to subtrees and tilesets.", - type: "STRING", - array: true, - }, - }, - }, - TilesetMetadataSemantics: { - properties: { - TILESET_FEATURE_ID_LABELS: { - description: - "The union of all the feature ID labels in glTF content using the EXT_mesh_features and EXT_instance_features extensions.", - type: "STRING", - array: true, - }, - TILESET_CRS_GEOCENTRIC: { - description: - "The geocentric coordinate reference system (CRS) of the tileset.", - type: "STRING", - }, - TILESET_CRS_COORDINATE_EPOCH: { - description: - "The coordinate epoch for coordinates that are referenced to a dynamic CRS such as WGS 84.", - type: "STRING", - }, - }, - }, - TileMetadataSemantics: { - properties: { - TILE_BOUNDING_BOX: { - description: - "The bounding volume of the tile, expressed as a box. Equivalent to tile.boundingVolume.box.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - array: true, - count: 12, - }, - TILE_BOUNDING_REGION: { - description: - "The bounding volume of the tile, expressed as a region. Equivalent to tile.boundingVolume.region.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - array: true, - count: 6, - }, - TILE_BOUNDING_SPHERE: { - description: - "The bounding volume of the tile, expressed as a sphere. Equivalent to tile.boundingVolume.sphere.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - array: true, - count: 4, - }, - TILE_BOUNDING_S2_CELL: { - description: - "The bounding volume of the tile, expressed as an S2 Cell ID using the 64-bit representation instead of the hexadecimal representation. Only applicable to 3DTILES_bounding_volume_S2.", - type: "SCALAR", - componentType: "UINT64", - }, - TILE_MINIMUM_HEIGHT: { - description: - "The minimum height of the tile above (or below) the WGS84 ellipsoid.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - }, - TILE_MAXIMUM_HEIGHT: { - description: - "The maximum height of the tile above (or below) the WGS84 ellipsoid.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - }, - TILE_HORIZON_OCCLUSION_POINT: { - description: - "The horizon occlusion point of the tile expressed in an ellipsoid-scaled fixed frame. If this point is below the horizon, the entire tile is below the horizon.", - type: "VEC3", - componentType: "FLOAT(32|64)", - }, - TILE_GEOMETRIC_ERROR: { - description: - "The geometric error of the tile. Equivalent to tile.geometricError.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - }, - TILE_REFINE: { - description: - "The tile refinement type. Valid values are 0 (ADD) and 1 (REPLACE). Equivalent to tile.refine.", - type: "SCALAR", - componentType: "UINT8", - }, - TILE_TRANSFORM: { - description: "The tile transform. Equivalent to tile.transform.", - type: "MAT4", - componentType: "FLOAT(32|64)", - }, - }, - }, - ContentMetadataSemantics: { - properties: { - CONTENT_BOUNDING_BOX: { - description: - "The bounding volume of the content of a tile, expressed as a box. Equivalent to tile.content.boundingVolume.box.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - array: true, - count: 12, - }, - CONTENT_BOUNDING_REGION: { - description: - "The bounding volume of the content of a tile, expressed as a region. Equivalent to tile.content.boundingVolume.region.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - array: true, - count: 6, - }, - CONTENT_BOUNDING_SPHERE: { - description: - "The bounding volume of the content of a tile, expressed as a sphere. Equivalent to tile.content.boundingVolume.sphere.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - array: true, - count: 4, - }, - CONTENT_BOUNDING_S2_CELL: { - description: - "The bounding volume of the content of a tile, expressed as an S2 Cell ID using the 64-bit representation instead of the hexadecimal representation. Only applicable to 3DTILES_bounding_volume_S2.", - type: "SCALAR", - componentType: "UINT64", - }, - CONTENT_MINIMUM_HEIGHT: { - description: - "The minimum height of the content of a tile above (or below) the WGS84 ellipsoid.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - }, - CONTENT_MAXIMUM_HEIGHT: { - description: - "The maximum height of the content of a tile above (or below) the WGS84 ellipsoid.", - type: "SCALAR", - componentType: "FLOAT(32|64)", - }, - CONTENT_HORIZON_OCCLUSION_POINT: { - description: - "The horizon occlusion point of the content of a tile expressed in an ellipsoid-scaled fixed frame. If this point is below the horizon, the entire content is below the horizon.", - type: "VEC3", - componentType: "FLOAT(32|64)", - }, - CONTENT_URI: { - description: - "The content uri. Overrides the implicit tile’s generated content uri. Equivalent to tile.content.uri", - type: "STRING", - componentType: "FLOAT(32|64)", - }, - CONTENT_GROUP_ID: { - description: - "The content group ID. Equivalent to tile.content.group.", - type: "SCALAR", - componentType: "UINT(8|16|32|64)", - }, - }, - }, - }, - }; - return matchingSchema; - } }