Skip to content

Commit

Permalink
fix(gltf, tile-converter): populate attributeStorageInfo with data ac…
Browse files Browse the repository at this point in the history
…cording to metadata class name specified
  • Loading branch information
mspivak-actionengine committed Oct 4, 2023
1 parent 77e6e32 commit 595b75b
Show file tree
Hide file tree
Showing 3 changed files with 95 additions and 84 deletions.
59 changes: 35 additions & 24 deletions modules/gltf/src/lib/extensions/EXT_structural_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,42 +48,53 @@ export function getPropertyTableFromExtStructuralMetadata(
extension: GLTF_EXT_structural_metadata_GLTF,
metadataClass?: string
): FeatureTableJson | null {
/**
* Note, 3dTiles is able to have multiple featureId attributes and multiple feature tables.
* In I3S we should decide which featureIds attribute will be passed to geometry data.
* So, we take only the feature table / feature texture to generate attributes storage info object.
* If the user has selected the metadataClass, the table with the corresponding class will be used,
* or just the first one otherwise.
*/
if (extension.propertyTables) {
/**
* Take only first feature table to generate attributes storage info object.
* TODO: Think about getting data from all feature tables?
* It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
* In I3S we should decide which featureIds attribute will be passed to geometry data.
*/
const firstPropertyTable = extension?.propertyTables[0];
const propertyTableWithData = {};

for (const propertyName in firstPropertyTable.properties) {
propertyTableWithData[propertyName] = firstPropertyTable.properties[propertyName].data;
for (const propertyTable of extension.propertyTables) {
if (propertyTable.class === metadataClass || !metadataClass) {
return getPropertyData(propertyTable);
}
}

return propertyTableWithData;
}

if (extension.propertyTextures) {
// TODO: Think about getting data from all property textures.
const firstPropertyTexture = extension?.propertyTextures[0];
const propertyTableWithData = {};

for (const propertyName in firstPropertyTexture.properties) {
propertyTableWithData[propertyName] = firstPropertyTexture.properties[propertyName].data;
for (const propertyTexture of extension.propertyTextures) {
if (propertyTexture.class === metadataClass || !metadataClass) {
return getPropertyData(propertyTexture);
}
}

return propertyTableWithData;
}

// eslint-disable-next-line no-console
console.warn(
'Cannot get property table from EXT_structural_metadata extension. There is neither propertyTables, nor propertyTextures in the extension.'
);
// console.warn(
// 'Cannot get property table from EXT_structural_metadata extension. There is neither propertyTables, nor propertyTextures in the extension.'
// );
return null;
}

/**
* Gets data from Property Table or Property Texture
* @param {GLTF_EXT_structural_metadata_PropertyTable | GLTF_EXT_structural_metadata_PropertyTexture} featureObject
* @returns Table containing property data
*/
function getPropertyData<
Type extends
| GLTF_EXT_structural_metadata_PropertyTable
| GLTF_EXT_structural_metadata_PropertyTexture
>(featureObject: Type) {
const propertyTableWithData = {};
for (const propertyName in featureObject.properties) {
propertyTableWithData[propertyName] = featureObject.properties[propertyName].data;
}
return propertyTableWithData;
}

/*
// Example of the extension.
// See more info at https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_structural_metadata
Expand Down
68 changes: 33 additions & 35 deletions modules/gltf/src/lib/extensions/deprecated/EXT_feature_metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,55 +46,53 @@ export function getPropertyTableFromExtFeatureMetadata(
extension: GLTF_EXT_feature_metadata_GLTF,
metadataClass?: string
): FeatureTableJson | null {
/**
* Note, 3dTiles is able to have multiple featureId attributes and multiple feature tables.
* In I3S we should decide which featureIds attribute will be passed to geometry data.
* So, we take only the feature table / feature texture to generate attributes storage info object.
* If the user has selected the metadataClass, the table with the corresponding class will be used,
* or just the first one otherwise.
*/
if (extension.featureTables) {
/**
* Take only first feature table to generate attributes storage info object.
* TODO: Think about getting data from all feature tables?
* It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
* In I3S we should decide which featureIds attribute will be passed to geometry data.
*/
const firstFeatureTableName = Object.keys(extension.featureTables)?.[0];

if (firstFeatureTableName) {
const featureTable = extension.featureTables[firstFeatureTableName];
const propertyTable = {};

for (const propertyName in featureTable.properties) {
propertyTable[propertyName] = featureTable.properties[propertyName].data;
for (const featureTableName in extension.featureTables) {
const featureTable = extension.featureTables[featureTableName];
if (featureTable.class === metadataClass || !metadataClass) {
return getPropertyData(featureTable);
}

return propertyTable;
}
}

if (extension.featureTextures) {
let featureTexture: string | undefined;
for (const textureKey in extension.featureTextures) {
const texture = extension.featureTextures[textureKey];
if (texture.class === metadataClass) {
featureTexture = textureKey;
for (const featureTextureName in extension.featureTextures) {
const featureTexture = extension.featureTextures[featureTextureName];
if (featureTexture.class === metadataClass || !metadataClass) {
return getPropertyData(featureTexture);
}
}

if (typeof featureTexture === 'string') {
const featureTable = extension.featureTextures[featureTexture];
const propertyTable = {};

for (const propertyName in featureTable.properties) {
propertyTable[propertyName] = featureTable.properties[propertyName].data;
}

return propertyTable;
}
}

// eslint-disable-next-line no-console
console.warn(
'Cannot get property table from EXT_feature_metadata extension. There is neither featureTables, nor featureTextures in the extension.'
);
// console.warn(
// 'Cannot get property table from EXT_feature_metadata extension. There is neither featureTables, nor featureTextures in the extension.'
// );
return null;
}

/**
* Gets data from Feature Table or Feature Texture
* @param {GLTF_EXT_feature_metadata_FeatureTable | GLTF_EXT_feature_metadata_FeatureTexture} featureObject
* @returns Table containing feature data
*/
function getPropertyData<
Type extends GLTF_EXT_feature_metadata_FeatureTable | GLTF_EXT_feature_metadata_FeatureTexture
>(featureObject: Type) {
const propertyTableWithData = {};
for (const propertyName in featureObject.properties) {
propertyTableWithData[propertyName] = featureObject.properties[propertyName].data;
}
return propertyTableWithData;
}

/**
* Decodes feature metadata from extension.
* @param scenegraph - Instance of the class for structured access to GLTF data.
Expand Down
52 changes: 27 additions & 25 deletions modules/tile-converter/src/i3s-converter/i3s-converter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1155,39 +1155,41 @@ export default class I3SConverter {
* @param propertyTable - Table with layer meta data.
*/
private _convertPropertyTableToNodeAttributes(propertyTable: FeatureTableJson): void {
/*
We will append attributes if only the attributeStorageInfo doesn't contain anything.
If it already has attributes, return from the function.
According to ver 1.9 (see https://github.com/Esri/i3s-spec/blob/master/docs/1.9/attributeStorageInfo.cmn.md):
"The attributeStorageInfo object describes the structure of the binary attribute data resource of a layer,
which is the same for every node in the layer."
*/
if (this.layers0!.attributeStorageInfo?.length) {
return;
}
let attributeIndex = 0;
const propertyTableWithObjectId = {
OBJECTID: [0],
...propertyTable
};

for (const key in propertyTableWithObjectId) {
/*
We will append new attributes only in case the property table is updated.
According to ver 1.9 (see https://github.com/Esri/i3s-spec/blob/master/docs/1.9/attributeStorageInfo.cmn.md):
"The attributeStorageInfo object describes the structure of the binary attribute data resource of a layer, which is the same for every node in the layer."
*/
const found = this.layers0!.attributeStorageInfo!.find((element) => element.name === key);
if (!found) {
const firstAttribute = propertyTableWithObjectId[key][0];
const attributeType = getAttributeType(key, firstAttribute);

const storageAttribute: AttributeStorageInfo = createdStorageAttribute(
attributeIndex,
key,
attributeType
);
const fieldAttributeType = getFieldAttributeType(attributeType);
const fieldAttribute = createFieldAttribute(key, fieldAttributeType);
const popupInfo = createPopupInfo(propertyTableWithObjectId);

this.layers0!.attributeStorageInfo!.push(storageAttribute);
this.layers0!.fields!.push(fieldAttribute);
this.layers0!.popupInfo = popupInfo;
this.layers0!.layerType = _3D_OBJECT_LAYER_TYPE;
}
attributeIndex += 1;
const firstAttribute = propertyTableWithObjectId[key][0];
const attributeType = getAttributeType(key, firstAttribute);

const storageAttribute: AttributeStorageInfo = createdStorageAttribute(
attributeIndex,
key,
attributeType
);
const fieldAttributeType = getFieldAttributeType(attributeType);
const fieldAttribute = createFieldAttribute(key, fieldAttributeType);
const popupInfo = createPopupInfo(propertyTableWithObjectId);

this.layers0!.attributeStorageInfo!.push(storageAttribute);
this.layers0!.fields!.push(fieldAttribute);
this.layers0!.popupInfo = popupInfo;
this.layers0!.layerType = _3D_OBJECT_LAYER_TYPE;
}
attributeIndex += 1;
}

/**
Expand Down

0 comments on commit 595b75b

Please sign in to comment.