@loaders.gl/tile-converter 3.3.0-alpha.1 → 3.3.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/3d-tiles-attributes-worker.js +3 -3
  2. package/dist/3d-tiles-attributes-worker.js.map +1 -1
  3. package/dist/converter.min.js +10 -10
  4. package/dist/dist.min.js +791 -554
  5. package/dist/es5/3d-tiles-attributes-worker.js +1 -1
  6. package/dist/es5/i3s-attributes-worker.js +1 -1
  7. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +15 -4
  8. package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  9. package/dist/es5/i3s-converter/helpers/feature-attributes.js +60 -0
  10. package/dist/es5/i3s-converter/helpers/feature-attributes.js.map +1 -0
  11. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +39 -7
  12. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  13. package/dist/es5/i3s-converter/helpers/geometry-converter.js +161 -49
  14. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  15. package/dist/es5/i3s-converter/helpers/node-pages.js +102 -46
  16. package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
  17. package/dist/es5/i3s-converter/i3s-converter.js +235 -141
  18. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  19. package/dist/es5/lib/utils/write-queue.js +66 -28
  20. package/dist/es5/lib/utils/write-queue.js.map +1 -1
  21. package/dist/es5/pgm-loader.js +1 -1
  22. package/dist/esm/3d-tiles-attributes-worker.js +1 -1
  23. package/dist/esm/i3s-attributes-worker.js +1 -1
  24. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +15 -4
  25. package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
  26. package/dist/esm/i3s-converter/helpers/feature-attributes.js +34 -0
  27. package/dist/esm/i3s-converter/helpers/feature-attributes.js.map +1 -0
  28. package/dist/esm/i3s-converter/helpers/geometry-attributes.js +23 -7
  29. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  30. package/dist/esm/i3s-converter/helpers/geometry-converter.js +132 -30
  31. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  32. package/dist/esm/i3s-converter/helpers/node-pages.js +3 -3
  33. package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
  34. package/dist/esm/i3s-converter/i3s-converter.js +32 -42
  35. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  36. package/dist/esm/lib/utils/write-queue.js +10 -0
  37. package/dist/esm/lib/utils/write-queue.js.map +1 -1
  38. package/dist/esm/pgm-loader.js +1 -1
  39. package/dist/i3s-attributes-worker.js +3 -3
  40. package/dist/i3s-attributes-worker.js.map +2 -2
  41. package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -1
  42. package/dist/i3s-converter/helpers/batch-ids-extensions.js +12 -1
  43. package/dist/i3s-converter/helpers/feature-attributes.d.ts +24 -0
  44. package/dist/i3s-converter/helpers/feature-attributes.d.ts.map +1 -0
  45. package/dist/i3s-converter/helpers/feature-attributes.js +55 -0
  46. package/dist/i3s-converter/helpers/geometry-attributes.js +26 -7
  47. package/dist/i3s-converter/helpers/geometry-converter.d.ts +9 -2
  48. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
  49. package/dist/i3s-converter/helpers/geometry-converter.js +124 -33
  50. package/dist/i3s-converter/helpers/node-pages.js +3 -3
  51. package/dist/i3s-converter/i3s-converter.d.ts +7 -14
  52. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  53. package/dist/i3s-converter/i3s-converter.js +56 -50
  54. package/dist/i3s-converter/types.d.ts +0 -48
  55. package/dist/i3s-converter/types.d.ts.map +1 -1
  56. package/dist/lib/utils/write-queue.d.ts +1 -0
  57. package/dist/lib/utils/write-queue.d.ts.map +1 -1
  58. package/dist/lib/utils/write-queue.js +13 -0
  59. package/package.json +15 -15
  60. package/src/i3s-converter/helpers/batch-ids-extensions.ts +22 -5
  61. package/src/i3s-converter/helpers/feature-attributes.ts +65 -0
  62. package/src/i3s-converter/helpers/geometry-attributes.ts +30 -7
  63. package/src/i3s-converter/helpers/geometry-converter.ts +161 -37
  64. package/src/i3s-converter/helpers/node-pages.ts +3 -3
  65. package/src/i3s-converter/i3s-converter.ts +41 -51
  66. package/src/i3s-converter/types.ts +0 -51
  67. package/src/lib/utils/write-queue.ts +12 -0
@@ -1,10 +1,13 @@
1
1
  import type {Image, MeshPrimitive} from 'modules/gltf/src/lib/types/gltf-postprocessed-schema';
2
+ import type {B3DMContent, FeatureTableJson} from '@loaders.gl/3d-tiles';
3
+ import type {GLTF_EXT_feature_metadata} from '@loaders.gl/gltf';
2
4
 
3
5
  import {Vector3, Matrix4, Vector4} from '@math.gl/core';
4
6
  import {Ellipsoid} from '@math.gl/geospatial';
5
7
 
6
8
  import {DracoWriterWorker} from '@loaders.gl/draco';
7
9
  import {assert, encode} from '@loaders.gl/core';
10
+ import {Tile3D} from '@loaders.gl/tiles';
8
11
  import {concatenateArrayBuffers, concatenateTypedArrays} from '@loaders.gl/loader-utils';
9
12
  import md5 from 'md5';
10
13
  import {generateAttributes} from './geometry-attributes';
@@ -15,7 +18,6 @@ import {
15
18
  I3SMaterialWithTexture,
16
19
  SharedResourcesArrays
17
20
  } from '../types';
18
- import {B3DMContent} from '@loaders.gl/3d-tiles';
19
21
  import {GLTFMaterialPostprocessed, GLTFNodePostprocessed} from '@loaders.gl/gltf';
20
22
  import {
21
23
  AttributeStorageInfo,
@@ -33,6 +35,7 @@ import {
33
35
  import {B3DMAttributesData /*transformI3SAttributesOnWorker */} from '../../i3s-attributes-worker';
34
36
  import {prepareDataForAttributesConversion} from './gltf-attributes';
35
37
  import {handleBatchIdsExtensions} from './batch-ids-extensions';
38
+ import {checkPropertiesLength, flattenPropertyTableByFeatureIds} from './feature-attributes';
36
39
 
37
40
  // Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
38
41
  const DEFAULT_ROUGHNESS_FACTOR = 1;
@@ -53,6 +56,9 @@ const OBJECT_ID_TYPE = 'Oid32';
53
56
  */
54
57
  const BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES = ['CUSTOM_ATTRIBUTE_2', '_BATCHID', 'BATCHID'];
55
58
 
59
+ const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
60
+ const EXT_MESH_FEATURES = 'EXT_mesh_features';
61
+
56
62
  let scratchVector = new Vector3();
57
63
 
58
64
  /**
@@ -70,6 +76,7 @@ let scratchVector = new Vector3();
70
76
  export default async function convertB3dmToI3sGeometry(
71
77
  tileContent: B3DMContent,
72
78
  nodeId: number,
79
+ propertyTable: FeatureTableJson | null,
73
80
  featuresHashArray: string[],
74
81
  attributeStorageInfo: AttributeStorageInfo[] | undefined,
75
82
  draco: boolean,
@@ -131,6 +138,7 @@ export default async function convertB3dmToI3sGeometry(
131
138
  tileContent,
132
139
  nodeId: nodesCounter,
133
140
  featuresHashArray,
141
+ propertyTable,
134
142
  attributeStorageInfo,
135
143
  draco,
136
144
  workerSource
@@ -194,6 +202,7 @@ async function _makeNodeResources({
194
202
  tileContent,
195
203
  nodeId,
196
204
  featuresHashArray,
205
+ propertyTable,
197
206
  attributeStorageInfo,
198
207
  draco,
199
208
  workerSource
@@ -204,6 +213,7 @@ async function _makeNodeResources({
204
213
  tileContent: B3DMContent;
205
214
  nodeId: number;
206
215
  featuresHashArray: string[];
216
+ propertyTable: FeatureTableJson | null;
207
217
  attributeStorageInfo?: AttributeStorageInfo[];
208
218
  draco: boolean;
209
219
  workerSource: {[key: string]: string};
@@ -253,11 +263,15 @@ async function _makeNodeResources({
253
263
  )
254
264
  : null;
255
265
 
256
- const attributes = convertBatchTableToAttributeBuffers(
257
- tileContent.batchTableJson,
258
- featureIds,
259
- attributeStorageInfo
260
- );
266
+ let attributes: ArrayBuffer[] = [];
267
+
268
+ if (attributeStorageInfo && propertyTable) {
269
+ attributes = convertPropertyTableToAttributeBuffers(
270
+ featureIds,
271
+ propertyTable,
272
+ attributeStorageInfo
273
+ );
274
+ }
261
275
 
262
276
  return {
263
277
  geometry: fileBuffer,
@@ -1051,49 +1065,66 @@ function replaceIndicesByUnique(indicesArray, featureMap) {
1051
1065
  }
1052
1066
 
1053
1067
  /**
1054
- * Convert batch table data to attribute buffers.
1055
- * @param {Object} batchTable - table with metadata for particular feature.
1068
+ * Convert property table data to attribute buffers.
1069
+ * @param {Object} propertyTable - table with metadata for particular feature.
1056
1070
  * @param {Array} featureIds
1057
1071
  * @param {Array} attributeStorageInfo
1058
1072
  * @returns {Array} - Array of file buffers.
1059
1073
  */
1060
- function convertBatchTableToAttributeBuffers(batchTable, featureIds, attributeStorageInfo) {
1074
+ function convertPropertyTableToAttributeBuffers(
1075
+ featureIds: number[],
1076
+ propertyTable: FeatureTableJson,
1077
+ attributeStorageInfo: AttributeStorageInfo[]
1078
+ ) {
1061
1079
  const attributeBuffers: ArrayBuffer[] = [];
1062
1080
 
1063
- if (batchTable) {
1064
- const batchTableWithFeatureIds = {
1065
- OBJECTID: featureIds,
1066
- ...batchTable
1067
- };
1081
+ const needFlattenPropertyTable = checkPropertiesLength(featureIds, propertyTable);
1082
+ const properties = needFlattenPropertyTable
1083
+ ? flattenPropertyTableByFeatureIds(featureIds, propertyTable)
1084
+ : propertyTable;
1068
1085
 
1069
- for (const key in batchTableWithFeatureIds) {
1070
- const type = getAttributeType(key, attributeStorageInfo);
1071
-
1072
- let attributeBuffer: ArrayBuffer | null = null;
1073
-
1074
- switch (type) {
1075
- case OBJECT_ID_TYPE:
1076
- case SHORT_INT_TYPE:
1077
- attributeBuffer = generateShortIntegerAttributeBuffer(batchTableWithFeatureIds[key]);
1078
- break;
1079
- case DOUBLE_TYPE:
1080
- attributeBuffer = generateDoubleAttributeBuffer(batchTableWithFeatureIds[key]);
1081
- break;
1082
- case STRING_TYPE:
1083
- attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
1084
- break;
1085
- default:
1086
- attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
1087
- }
1086
+ const propertyTableWithObjectIds = {
1087
+ OBJECTID: featureIds,
1088
+ ...properties
1089
+ };
1088
1090
 
1089
- if (attributeBuffer) {
1090
- attributeBuffers.push(attributeBuffer);
1091
- }
1092
- }
1091
+ for (const propertyName in propertyTableWithObjectIds) {
1092
+ const type = getAttributeType(propertyName, attributeStorageInfo);
1093
+ const value = propertyTableWithObjectIds[propertyName];
1094
+ const attributeBuffer = generateAttributeBuffer(type, value);
1095
+
1096
+ attributeBuffers.push(attributeBuffer);
1093
1097
  }
1094
1098
 
1095
1099
  return attributeBuffers;
1096
1100
  }
1101
+
1102
+ /**
1103
+ * Generates attribute buffer based on attribute type
1104
+ * @param type
1105
+ * @param value
1106
+ */
1107
+ function generateAttributeBuffer(type: string, value: any): ArrayBuffer {
1108
+ let attributeBuffer: ArrayBuffer;
1109
+
1110
+ switch (type) {
1111
+ case OBJECT_ID_TYPE:
1112
+ case SHORT_INT_TYPE:
1113
+ attributeBuffer = generateShortIntegerAttributeBuffer(value);
1114
+ break;
1115
+ case DOUBLE_TYPE:
1116
+ attributeBuffer = generateDoubleAttributeBuffer(value);
1117
+ break;
1118
+ case STRING_TYPE:
1119
+ attributeBuffer = generateStringAttributeBuffer(value);
1120
+ break;
1121
+ default:
1122
+ attributeBuffer = generateStringAttributeBuffer(value);
1123
+ }
1124
+
1125
+ return attributeBuffer;
1126
+ }
1127
+
1097
1128
  /**
1098
1129
  * Return attribute type.
1099
1130
  * @param {String} key
@@ -1257,3 +1288,96 @@ function generateFeatureIndexAttribute(featureIndex, faceRange) {
1257
1288
 
1258
1289
  return orderedFeatureIndices;
1259
1290
  }
1291
+
1292
+ /**
1293
+ * Find property table in tile
1294
+ * For example it can be batchTable for b3dm files or property table in gLTF extension.
1295
+ * @param sourceTile
1296
+ */
1297
+ export function getPropertyTable(sourceTile: Tile3D): FeatureTableJson | null {
1298
+ const batchTableJson = sourceTile?.content?.batchTableJson;
1299
+
1300
+ if (batchTableJson) {
1301
+ return batchTableJson;
1302
+ }
1303
+
1304
+ const {extensionName, extension} = getPropertyTableExtension(sourceTile);
1305
+
1306
+ switch (extensionName) {
1307
+ case EXT_MESH_FEATURES: {
1308
+ console.warn('The I3S converter does not yet support the EXT_mesh_features extension');
1309
+ return null;
1310
+ }
1311
+ case EXT_FEATURE_METADATA: {
1312
+ return getPropertyTableFromExtFeatureMetadata(extension);
1313
+ }
1314
+ default:
1315
+ return null;
1316
+ }
1317
+ }
1318
+
1319
+ /**
1320
+ * Check extensions which can be with property table inside.
1321
+ * @param sourceTile
1322
+ */
1323
+ function getPropertyTableExtension(sourceTile: Tile3D) {
1324
+ const extensionsWithPropertyTables = [EXT_FEATURE_METADATA, EXT_MESH_FEATURES];
1325
+ const extensionsUsed = sourceTile?.content?.gltf?.extensionsUsed;
1326
+
1327
+ if (!extensionsUsed) {
1328
+ return {extensionName: null, extension: null};
1329
+ }
1330
+
1331
+ let extensionName: string = '';
1332
+
1333
+ for (const extensionItem of sourceTile?.content?.gltf?.extensionsUsed) {
1334
+ if (extensionsWithPropertyTables.includes(extensionItem)) {
1335
+ extensionName = extensionItem;
1336
+ break;
1337
+ }
1338
+ }
1339
+
1340
+ const extension = sourceTile?.content?.gltf?.extensions?.[extensionName];
1341
+
1342
+ return {extensionName, extension};
1343
+ }
1344
+
1345
+ /**
1346
+ * Handle EXT_feature_metadata to get property table
1347
+ * @param extension
1348
+ * TODO add EXT_feature_metadata feature textures support.
1349
+ */
1350
+ function getPropertyTableFromExtFeatureMetadata(
1351
+ extension: GLTF_EXT_feature_metadata
1352
+ ): FeatureTableJson | null {
1353
+ if (extension?.featureTextures) {
1354
+ console.warn(
1355
+ 'The I3S converter does not yet support the EXT_feature_metadata feature textures'
1356
+ );
1357
+ return null;
1358
+ }
1359
+
1360
+ if (extension?.featureTables) {
1361
+ /**
1362
+ * Take only first feature table to generate attributes storage info object.
1363
+ * TODO: Think about getting data from all feature tables?
1364
+ * It can be tricky just because 3dTiles is able to have multiple featureId attributes and multiple feature tables.
1365
+ * In I3S we should decide which featureIds attribute will be passed to geometry data.
1366
+ */
1367
+ const firstFeatureTableName = Object.keys(extension.featureTables)?.[0];
1368
+
1369
+ if (firstFeatureTableName) {
1370
+ const featureTable = extension?.featureTables[firstFeatureTableName];
1371
+ const propertyTable = {};
1372
+
1373
+ for (const propertyName in featureTable.properties) {
1374
+ propertyTable[propertyName] = featureTable.properties[propertyName].data;
1375
+ }
1376
+
1377
+ return propertyTable;
1378
+ }
1379
+ }
1380
+
1381
+ console.warn("The I3S converter couldn't handle EXT_feature_metadata extension");
1382
+ return null;
1383
+ }
@@ -204,14 +204,14 @@ export default class NodePages {
204
204
  for (const [index, nodePage] of this.nodePages.entries()) {
205
205
  const nodePageStr = JSON.stringify(nodePage);
206
206
  const slpkPath = join(layers0Path, 'nodepages');
207
- writeQueue.enqueue({
207
+ await writeQueue.enqueue({
208
208
  archiveKey: `nodePages/${index.toString()}.json.gz`,
209
209
  writePromise: this.writeFile(slpkPath, nodePageStr, `${index.toString()}.json`)
210
210
  });
211
211
  }
212
212
  const metadata = transform({nodeCount: this.nodesCounter}, metadataTemplate());
213
213
  const compress = false;
214
- writeQueue.enqueue({
214
+ await writeQueue.enqueue({
215
215
  archiveKey: 'metadata.json',
216
216
  writePromise: this.writeFile(
217
217
  layers0Path,
@@ -224,7 +224,7 @@ export default class NodePages {
224
224
  for (const [index, nodePage] of this.nodePages.entries()) {
225
225
  const nodePageStr = JSON.stringify(nodePage);
226
226
  const nodePagePath = join(layers0Path, 'nodepages', index.toString());
227
- writeQueue.enqueue({writePromise: this.writeFile(nodePagePath, nodePageStr)});
227
+ await writeQueue.enqueue({writePromise: this.writeFile(nodePagePath, nodePageStr)});
228
228
  }
229
229
  }
230
230
  }
@@ -1,5 +1,5 @@
1
1
  import type {Tile3D, Tileset3DProps} from '@loaders.gl/tiles';
2
- import type {BatchTableJson, B3DMContent} from '@loaders.gl/3d-tiles';
2
+ import type {FeatureTableJson} from '@loaders.gl/3d-tiles';
3
3
  import type {WriteQueueItem} from '../lib/utils/write-queue';
4
4
  import type {
5
5
  AttributeStorageInfo,
@@ -34,7 +34,7 @@ import {
34
34
  // addFileToZip
35
35
  } from '../lib/utils/compress-util';
36
36
  import {calculateFilesSize, timeConverter} from '../lib/utils/statistic-utills';
37
- import convertB3dmToI3sGeometry from './helpers/geometry-converter';
37
+ import convertB3dmToI3sGeometry, {getPropertyTable} from './helpers/geometry-converter';
38
38
  import {
39
39
  createBoundingVolumes,
40
40
  convertBoundingVolumeToI3SFullExtent
@@ -341,7 +341,7 @@ export default class I3SConverter {
341
341
  const childPath = join(this.layers0Path, 'nodes', child.path!);
342
342
 
343
343
  if (this.options.slpk) {
344
- this.writeQueue.enqueue({
344
+ await this.writeQueue.enqueue({
345
345
  archiveKey: 'nodes/1/3dNodeIndexDocument.json.gz',
346
346
  writePromise: writeFileForSlpk(
347
347
  childPath,
@@ -350,7 +350,7 @@ export default class I3SConverter {
350
350
  )
351
351
  });
352
352
  } else {
353
- this.writeQueue.enqueue({writePromise: writeFile(childPath, JSON.stringify(child))});
353
+ await this.writeQueue.enqueue({writePromise: writeFile(childPath, JSON.stringify(child))});
354
354
  }
355
355
  } else {
356
356
  await this._addChildrenWithNeighborsAndWriteFile({
@@ -368,7 +368,7 @@ export default class I3SConverter {
368
368
  */
369
369
  private async _writeLayers0(): Promise<void> {
370
370
  if (this.options.slpk) {
371
- this.writeQueue.enqueue({
371
+ await this.writeQueue.enqueue({
372
372
  archiveKey: '3dSceneLayer.json.gz',
373
373
  writePromise: writeFileForSlpk(
374
374
  this.layers0Path,
@@ -377,7 +377,7 @@ export default class I3SConverter {
377
377
  )
378
378
  });
379
379
  } else {
380
- this.writeQueue.enqueue({
380
+ await this.writeQueue.enqueue({
381
381
  writePromise: writeFile(this.layers0Path, JSON.stringify(this.layers0))
382
382
  });
383
383
  }
@@ -392,12 +392,12 @@ export default class I3SConverter {
392
392
  rootPath: string
393
393
  ): Promise<void> {
394
394
  if (this.options.slpk) {
395
- this.writeQueue.enqueue({
395
+ await this.writeQueue.enqueue({
396
396
  archiveKey: `nodes/${nodePath}/3dNodeIndexDocument.json.gz`,
397
397
  writePromise: writeFileForSlpk(rootPath, JSON.stringify(root0), '3dNodeIndexDocument.json')
398
398
  });
399
399
  } else {
400
- this.writeQueue.enqueue({writePromise: writeFile(rootPath, JSON.stringify(root0))});
400
+ await this.writeQueue.enqueue({writePromise: writeFile(rootPath, JSON.stringify(root0))});
401
401
  }
402
402
  }
403
403
 
@@ -572,13 +572,13 @@ export default class I3SConverter {
572
572
 
573
573
  let boundingVolumes = createBoundingVolumes(sourceTile, this.geoidHeightModel!);
574
574
 
575
- const batchTable = sourceTile?.content?.batchTableJson;
575
+ const propertyTable = getPropertyTable(sourceTile);
576
576
 
577
- if (batchTable) {
578
- this._convertAttributeStorageInfo(sourceTile.content);
577
+ if (propertyTable && !this.layers0?.attributeStorageInfo?.length) {
578
+ this._convertPropertyTableToNodeAttributes(propertyTable);
579
579
  }
580
580
 
581
- const resourcesData = await this._convertResources(sourceTile);
581
+ const resourcesData = await this._convertResources(sourceTile, propertyTable);
582
582
 
583
583
  const nodes: Node3DIndexDocument[] = [];
584
584
  const nodesInPage: NodeInPage[] = [];
@@ -648,20 +648,6 @@ export default class I3SConverter {
648
648
  return nodes;
649
649
  }
650
650
 
651
- /**
652
- * Convert attributesStorageInfo https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
653
- * from B3DM batch table
654
- * @param sourceTileContent - tile content of 3DTile
655
- * @return {void}
656
- */
657
- private _convertAttributeStorageInfo(sourceTileContent: B3DMContent): void {
658
- // In legacy b3dm files sometimes sourceTileContent is null.
659
- const batchTable = sourceTileContent && sourceTileContent.batchTableJson;
660
- if (batchTable && !this.layers0?.attributeStorageInfo?.length) {
661
- this._convertBatchTableInfoToNodeAttributes(batchTable);
662
- }
663
- }
664
-
665
651
  /**
666
652
  * Convert tile to one or more I3S nodes
667
653
  * @param sourceTile - source tile (3DTile)
@@ -674,13 +660,17 @@ export default class I3SConverter {
674
660
  * result.attributes - feature attributes
675
661
  * result.featureCount - number of features
676
662
  */
677
- private async _convertResources(sourceTile: TileHeader): Promise<I3SConvertedResources[] | null> {
663
+ private async _convertResources(
664
+ sourceTile: TileHeader,
665
+ propertyTable: FeatureTableJson | null
666
+ ): Promise<I3SConvertedResources[] | null> {
678
667
  if (!this.isContentSupported(sourceTile)) {
679
668
  return null;
680
669
  }
681
670
  const resourcesData = await convertB3dmToI3sGeometry(
682
671
  sourceTile.content,
683
672
  Number(this.nodePages.nodesCounter),
673
+ propertyTable,
684
674
  this.featuresHashArray,
685
675
  this.layers0?.attributeStorageInfo,
686
676
  this.options.draco,
@@ -856,13 +846,13 @@ export default class I3SConverter {
856
846
  ): Promise<void> {
857
847
  if (this.options.slpk) {
858
848
  const slpkGeometryPath = join(childPath, 'geometries');
859
- this.writeQueue.enqueue({
849
+ await this.writeQueue.enqueue({
860
850
  archiveKey: `${slpkChildPath}/geometries/0.bin.gz`,
861
851
  writePromise: writeFileForSlpk(slpkGeometryPath, geometryBuffer, '0.bin')
862
852
  });
863
853
  } else {
864
854
  const geometryPath = join(childPath, 'geometries/0/');
865
- this.writeQueue.enqueue({
855
+ await this.writeQueue.enqueue({
866
856
  writePromise: writeFile(geometryPath, geometryBuffer, 'index.bin')
867
857
  });
868
858
  }
@@ -870,13 +860,13 @@ export default class I3SConverter {
870
860
  if (this.options.draco) {
871
861
  if (this.options.slpk) {
872
862
  const slpkCompressedGeometryPath = join(childPath, 'geometries');
873
- this.writeQueue.enqueue({
863
+ await this.writeQueue.enqueue({
874
864
  archiveKey: `${slpkChildPath}/geometries/1.bin.gz`,
875
865
  writePromise: writeFileForSlpk(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
876
866
  });
877
867
  } else {
878
868
  const compressedGeometryPath = join(childPath, 'geometries/1/');
879
- this.writeQueue.enqueue({
869
+ await this.writeQueue.enqueue({
880
870
  writePromise: writeFile(compressedGeometryPath, compressedGeometry, 'index.bin')
881
871
  });
882
872
  }
@@ -904,13 +894,13 @@ export default class I3SConverter {
904
894
  const sharedDataStr = JSON.stringify(sharedData);
905
895
  if (this.options.slpk) {
906
896
  const slpkSharedPath = join(childPath, 'shared');
907
- this.writeQueue.enqueue({
897
+ await this.writeQueue.enqueue({
908
898
  archiveKey: `${slpkChildPath}/shared/sharedResource.json.gz`,
909
899
  writePromise: writeFileForSlpk(slpkSharedPath, sharedDataStr, 'sharedResource.json')
910
900
  });
911
901
  } else {
912
902
  const sharedPath = join(childPath, 'shared/');
913
- this.writeQueue.enqueue({writePromise: writeFile(sharedPath, sharedDataStr)});
903
+ await this.writeQueue.enqueue({writePromise: writeFile(sharedPath, sharedDataStr)});
914
904
  }
915
905
  }
916
906
 
@@ -1001,13 +991,13 @@ export default class I3SConverter {
1001
991
  const slpkTexturePath = join(childPath, 'textures');
1002
992
  const compress = false;
1003
993
 
1004
- this.writeQueue.enqueue({
994
+ await this.writeQueue.enqueue({
1005
995
  archiveKey: `${slpkChildPath}/textures/${name}.${format}`,
1006
996
  writePromise: writeFileForSlpk(slpkTexturePath, textureData, `${name}.${format}`, compress)
1007
997
  });
1008
998
  } else {
1009
999
  const texturePath = join(childPath, `textures/${name}/`);
1010
- this.writeQueue.enqueue({
1000
+ await this.writeQueue.enqueue({
1011
1001
  writePromise: writeFile(texturePath, textureData, `index.${format}`)
1012
1002
  });
1013
1003
  }
@@ -1031,13 +1021,13 @@ export default class I3SConverter {
1031
1021
 
1032
1022
  if (this.options.slpk) {
1033
1023
  const slpkAttributesPath = join(childPath, 'attributes', folderName);
1034
- this.writeQueue.enqueue({
1024
+ await this.writeQueue.enqueue({
1035
1025
  archiveKey: `${slpkChildPath}/attributes/${folderName}.bin.gz`,
1036
1026
  writePromise: writeFileForSlpk(slpkAttributesPath, fileBuffer, '0.bin')
1037
1027
  });
1038
1028
  } else {
1039
1029
  const attributesPath = join(childPath, `attributes/${folderName}/0`);
1040
- this.writeQueue.enqueue({
1030
+ await this.writeQueue.enqueue({
1041
1031
  writePromise: writeFile(attributesPath, fileBuffer, 'index.bin')
1042
1032
  });
1043
1033
  }
@@ -1080,7 +1070,7 @@ export default class I3SConverter {
1080
1070
  /**
1081
1071
  * Generate storage attribute for map segmentation.
1082
1072
  * @param attributeIndex - order index of attribute (f_0, f_1 ...).
1083
- * @param key - attribute key from batch table.\
1073
+ * @param key - attribute key from propertyTable.
1084
1074
  * @param attributeType - attribute type.
1085
1075
  * @return Updated storageAttribute.
1086
1076
  */
@@ -1119,7 +1109,7 @@ export default class I3SConverter {
1119
1109
  /**
1120
1110
  * Get the attribute type for attributeStorageInfo https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
1121
1111
  * @param key - attribute's key
1122
- * @param attribute - attribute's type in batchTable
1112
+ * @param attribute - attribute's type in propertyTable
1123
1113
  */
1124
1114
  private getAttributeType(key: string, attribute: string): string {
1125
1115
  if (key === OBJECT_ID_TYPE) {
@@ -1187,24 +1177,24 @@ export default class I3SConverter {
1187
1177
  }
1188
1178
 
1189
1179
  /**
1190
- * Do conversion of 3DTiles batch table to I3s node attributes.
1191
- * @param batchTable - Table with layer meta data.
1180
+ * Do conversion of 3DTiles property table to I3s node attributes.
1181
+ * @param propertyTable - Table with layer meta data.
1192
1182
  */
1193
- private _convertBatchTableInfoToNodeAttributes(batchTable: BatchTableJson): void {
1183
+ private _convertPropertyTableToNodeAttributes(propertyTable: FeatureTableJson): void {
1194
1184
  let attributeIndex = 0;
1195
- const batchTableWithObjectId = {
1185
+ const propertyTableWithObjectId = {
1196
1186
  OBJECTID: [0],
1197
- ...batchTable
1187
+ ...propertyTable
1198
1188
  };
1199
1189
 
1200
- for (const key in batchTableWithObjectId) {
1201
- const firstAttribute = batchTableWithObjectId[key][0];
1190
+ for (const key in propertyTableWithObjectId) {
1191
+ const firstAttribute = propertyTableWithObjectId[key][0];
1202
1192
  const attributeType = this.getAttributeType(key, firstAttribute);
1203
1193
 
1204
1194
  const storageAttribute = this._createdStorageAttribute(attributeIndex, key, attributeType);
1205
1195
  const fieldAttributeType = this._getFieldAttributeType(attributeType);
1206
1196
  const fieldAttribute = this._createFieldAttribute(key, fieldAttributeType);
1207
- const popupInfo = this._createPopupInfo(batchTableWithObjectId);
1197
+ const popupInfo = this._createPopupInfo(propertyTableWithObjectId);
1208
1198
 
1209
1199
  this.layers0!.attributeStorageInfo!.push(storageAttribute);
1210
1200
  this.layers0!.fields!.push(fieldAttribute);
@@ -1216,7 +1206,7 @@ export default class I3SConverter {
1216
1206
  }
1217
1207
 
1218
1208
  /**
1219
- * Find and return attribute type based on key form Batch table.
1209
+ * Find and return attribute type based on key form propertyTable.
1220
1210
  * @param attributeType
1221
1211
  */
1222
1212
  private _getFieldAttributeType(attributeType: Attribute): ESRIField {
@@ -1236,10 +1226,10 @@ export default class I3SConverter {
1236
1226
 
1237
1227
  /**
1238
1228
  * Generate popup info to show metadata on the map.
1239
- * @param batchTable - Batch table data with OBJECTID.
1229
+ * @param propertyTable - table data with OBJECTID.
1240
1230
  * @return data for correct rendering of popup.
1241
1231
  */
1242
- private _createPopupInfo(batchTable: BatchTableJson): PopupInfo {
1232
+ private _createPopupInfo(propertyTable: FeatureTableJson): PopupInfo {
1243
1233
  const title = '{OBJECTID}';
1244
1234
  const mediaInfos = [];
1245
1235
  const fieldInfos: FieldInfo[] = [];
@@ -1249,7 +1239,7 @@ export default class I3SConverter {
1249
1239
  }[] = [];
1250
1240
  const expressionInfos = [];
1251
1241
 
1252
- for (const key in batchTable) {
1242
+ for (const key in propertyTable) {
1253
1243
  fieldInfos.push({
1254
1244
  fieldName: key,
1255
1245
  visible: true,
@@ -122,54 +122,3 @@ export type I3SMaterialWithTexture = {
122
122
  /** Texture content (image) */
123
123
  texture?: ImageDataType;
124
124
  };
125
-
126
- /**
127
- * 3DTilesNext EXT_feature_metadata extension
128
- * Spec - https://github.com/CesiumGS/glTF/tree/3d-tiles-next/extensions/2.0/Vendor/EXT_feature_metadata
129
- */
130
- export type ExtFeatureMetadata = {
131
- /** Feature ids definition in attributes */
132
- featureIdAttributes?: ExtFeatureMetadataAttribute[];
133
- /** Feature ids definition in textures */
134
- featureIdTextures?: ExtFeatureMetadataAttribute[];
135
- };
136
-
137
- /**
138
- * Attribute which described featureIds definition.
139
- */
140
- export type ExtFeatureMetadataAttribute = {
141
- /** Name of feature table */
142
- featureTable: string;
143
- /** Described how feature ids are defined */
144
- featureIds: ExtFeatureMetadataFeatureIds;
145
- };
146
-
147
- /**
148
- * Defining featureIds by attributes or implicitly.
149
- */
150
- type ExtFeatureMetadataFeatureIds = {
151
- /** Name of attribute where featureIds are defined */
152
- attribute?: string;
153
- /** Sets a constant feature ID for each vertex. The default is 0. */
154
- constant?: number;
155
- /** Sets the rate at which feature IDs increment.
156
- * If divisor is zero then constant is used.
157
- * If divisor is greater than zero the feature ID increments once per divisor sets of vertices, starting at constant.
158
- * The default is 0
159
- */
160
- divisor?: number;
161
- /** gLTF textureInfo object - https://github.com/CesiumGS/glTF/blob/3d-tiles-next/specification/2.0/schema/textureInfo.schema.json */
162
- texture?: ExtFeatureMetadataTexture;
163
- /** Must be a single channel ("r", "g", "b", or "a") */
164
- channels?: 'r' | 'g' | 'b' | 'a';
165
- };
166
-
167
- /**
168
- * Reference to a texture.
169
- */
170
- type ExtFeatureMetadataTexture = {
171
- /** The set index of texture's TEXCOORD attribute used for texture coordinate mapping.*/
172
- texCoord: number;
173
- /** The index of the texture. */
174
- index: number;
175
- };
@@ -1,4 +1,8 @@
1
1
  import {Queue} from './queue';
2
+ import process from 'process';
3
+
4
+ /** Memory limit size is based on testing */
5
+ const MEMORY_LIMIT = 4 * 1024 * 1024 * 1024; // 4GB
2
6
 
3
7
  export type WriteQueueItem = {
4
8
  archiveKey?: string;
@@ -18,6 +22,14 @@ export default class WriteQueue<T extends WriteQueueItem> extends Queue<T> {
18
22
  this.writeConcurrency = writeConcurrency;
19
23
  }
20
24
 
25
+ async enqueue(val: T) {
26
+ super.enqueue(val);
27
+ /** https://nodejs.org/docs/latest-v14.x/api/process.html#process_process_memoryusage */
28
+ if (process.memoryUsage().rss > MEMORY_LIMIT) {
29
+ await this.startWrite();
30
+ }
31
+ }
32
+
21
33
  startListening() {
22
34
  this.intervalId = setInterval(this.startWrite.bind(this), this.listeningInterval);
23
35
  }