@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.
- package/dist/3d-tiles-attributes-worker.js +3 -3
- package/dist/3d-tiles-attributes-worker.js.map +1 -1
- package/dist/converter.min.js +10 -10
- package/dist/dist.min.js +791 -554
- package/dist/es5/3d-tiles-attributes-worker.js +1 -1
- package/dist/es5/i3s-attributes-worker.js +1 -1
- package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +15 -4
- package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/feature-attributes.js +60 -0
- package/dist/es5/i3s-converter/helpers/feature-attributes.js.map +1 -0
- package/dist/es5/i3s-converter/helpers/geometry-attributes.js +39 -7
- package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/geometry-converter.js +161 -49
- package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/node-pages.js +102 -46
- package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
- package/dist/es5/i3s-converter/i3s-converter.js +235 -141
- package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
- package/dist/es5/lib/utils/write-queue.js +66 -28
- package/dist/es5/lib/utils/write-queue.js.map +1 -1
- package/dist/es5/pgm-loader.js +1 -1
- package/dist/esm/3d-tiles-attributes-worker.js +1 -1
- package/dist/esm/i3s-attributes-worker.js +1 -1
- package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +15 -4
- package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/feature-attributes.js +34 -0
- package/dist/esm/i3s-converter/helpers/feature-attributes.js.map +1 -0
- package/dist/esm/i3s-converter/helpers/geometry-attributes.js +23 -7
- package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/geometry-converter.js +132 -30
- package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/node-pages.js +3 -3
- package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
- package/dist/esm/i3s-converter/i3s-converter.js +32 -42
- package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
- package/dist/esm/lib/utils/write-queue.js +10 -0
- package/dist/esm/lib/utils/write-queue.js.map +1 -1
- package/dist/esm/pgm-loader.js +1 -1
- package/dist/i3s-attributes-worker.js +3 -3
- package/dist/i3s-attributes-worker.js.map +2 -2
- package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -1
- package/dist/i3s-converter/helpers/batch-ids-extensions.js +12 -1
- package/dist/i3s-converter/helpers/feature-attributes.d.ts +24 -0
- package/dist/i3s-converter/helpers/feature-attributes.d.ts.map +1 -0
- package/dist/i3s-converter/helpers/feature-attributes.js +55 -0
- package/dist/i3s-converter/helpers/geometry-attributes.js +26 -7
- package/dist/i3s-converter/helpers/geometry-converter.d.ts +9 -2
- package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
- package/dist/i3s-converter/helpers/geometry-converter.js +124 -33
- package/dist/i3s-converter/helpers/node-pages.js +3 -3
- package/dist/i3s-converter/i3s-converter.d.ts +7 -14
- package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
- package/dist/i3s-converter/i3s-converter.js +56 -50
- package/dist/i3s-converter/types.d.ts +0 -48
- package/dist/i3s-converter/types.d.ts.map +1 -1
- package/dist/lib/utils/write-queue.d.ts +1 -0
- package/dist/lib/utils/write-queue.d.ts.map +1 -1
- package/dist/lib/utils/write-queue.js +13 -0
- package/package.json +15 -15
- package/src/i3s-converter/helpers/batch-ids-extensions.ts +22 -5
- package/src/i3s-converter/helpers/feature-attributes.ts +65 -0
- package/src/i3s-converter/helpers/geometry-attributes.ts +30 -7
- package/src/i3s-converter/helpers/geometry-converter.ts +161 -37
- package/src/i3s-converter/helpers/node-pages.ts +3 -3
- package/src/i3s-converter/i3s-converter.ts +41 -51
- package/src/i3s-converter/types.ts +0 -51
- 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
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
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
|
|
1055
|
-
* @param {Object}
|
|
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
|
|
1074
|
+
function convertPropertyTableToAttributeBuffers(
|
|
1075
|
+
featureIds: number[],
|
|
1076
|
+
propertyTable: FeatureTableJson,
|
|
1077
|
+
attributeStorageInfo: AttributeStorageInfo[]
|
|
1078
|
+
) {
|
|
1061
1079
|
const attributeBuffers: ArrayBuffer[] = [];
|
|
1062
1080
|
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
};
|
|
1081
|
+
const needFlattenPropertyTable = checkPropertiesLength(featureIds, propertyTable);
|
|
1082
|
+
const properties = needFlattenPropertyTable
|
|
1083
|
+
? flattenPropertyTableByFeatureIds(featureIds, propertyTable)
|
|
1084
|
+
: propertyTable;
|
|
1068
1085
|
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
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
|
-
|
|
1090
|
-
|
|
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 {
|
|
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
|
|
575
|
+
const propertyTable = getPropertyTable(sourceTile);
|
|
576
576
|
|
|
577
|
-
if (
|
|
578
|
-
this.
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
1191
|
-
* @param
|
|
1180
|
+
* Do conversion of 3DTiles property table to I3s node attributes.
|
|
1181
|
+
* @param propertyTable - Table with layer meta data.
|
|
1192
1182
|
*/
|
|
1193
|
-
private
|
|
1183
|
+
private _convertPropertyTableToNodeAttributes(propertyTable: FeatureTableJson): void {
|
|
1194
1184
|
let attributeIndex = 0;
|
|
1195
|
-
const
|
|
1185
|
+
const propertyTableWithObjectId = {
|
|
1196
1186
|
OBJECTID: [0],
|
|
1197
|
-
...
|
|
1187
|
+
...propertyTable
|
|
1198
1188
|
};
|
|
1199
1189
|
|
|
1200
|
-
for (const key in
|
|
1201
|
-
const firstAttribute =
|
|
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(
|
|
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
|
|
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
|
|
1229
|
+
* @param propertyTable - table data with OBJECTID.
|
|
1240
1230
|
* @return data for correct rendering of popup.
|
|
1241
1231
|
*/
|
|
1242
|
-
private _createPopupInfo(
|
|
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
|
|
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
|
}
|