@loaders.gl/tile-converter 3.2.7 → 3.3.0-alpha.3
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-cli.js +30 -7
- package/dist/converter.min.js +1 -1
- package/dist/dist.min.js +1105 -621
- package/dist/es5/3d-tiles-attributes-worker.js +1 -1
- package/dist/es5/3d-tiles-attributes-worker.js.map +1 -1
- package/dist/es5/converter-cli.js +42 -12
- package/dist/es5/converter-cli.js.map +1 -1
- package/dist/es5/i3s-attributes-worker.js +1 -1
- package/dist/es5/i3s-attributes-worker.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js +146 -0
- package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
- 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 +177 -59
- package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/gltf-attributes.js +15 -1
- package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
- package/dist/es5/i3s-converter/i3s-converter.js +50 -51
- package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
- package/dist/es5/lib/utils/write-queue.js +3 -5
- package/dist/es5/lib/utils/write-queue.js.map +1 -1
- package/dist/es5/pgm-loader.js +1 -1
- package/dist/es5/pgm-loader.js.map +1 -1
- package/dist/esm/3d-tiles-attributes-worker.js +1 -1
- package/dist/esm/3d-tiles-attributes-worker.js.map +1 -1
- package/dist/esm/converter-cli.js +37 -7
- package/dist/esm/converter-cli.js.map +1 -1
- package/dist/esm/i3s-attributes-worker.js +1 -1
- package/dist/esm/i3s-attributes-worker.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js +128 -0
- package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -0
- 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 +145 -38
- package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/gltf-attributes.js +15 -1
- package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
- package/dist/esm/i3s-converter/i3s-converter.js +21 -27
- package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
- package/dist/esm/lib/utils/write-queue.js +3 -5
- package/dist/esm/lib/utils/write-queue.js.map +1 -1
- package/dist/esm/pgm-loader.js +1 -1
- package/dist/esm/pgm-loader.js.map +1 -1
- package/dist/i3s-attributes-worker.js +3 -3
- package/dist/i3s-attributes-worker.js.map +3 -3
- package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +12 -0
- package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts.map +1 -0
- package/dist/i3s-converter/helpers/batch-ids-extensions.js +138 -0
- 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 +140 -44
- package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
- package/dist/i3s-converter/helpers/gltf-attributes.js +13 -0
- 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 +44 -35
- package/dist/lib/utils/write-queue.d.ts.map +1 -1
- package/dist/lib/utils/write-queue.js +3 -4
- package/package.json +15 -15
- package/src/converter-cli.ts +33 -7
- package/src/i3s-converter/helpers/batch-ids-extensions.ts +199 -0
- 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 +187 -48
- package/src/i3s-converter/helpers/gltf-attributes.ts +15 -0
- package/src/i3s-converter/i3s-converter.ts +38 -41
- package/src/lib/utils/write-queue.ts +7 -5
|
@@ -47,7 +47,7 @@ function calculateFaceRangesAndFeaturesCount(featureIndices: number[]): {
|
|
|
47
47
|
} {
|
|
48
48
|
let rangeIndex = 1;
|
|
49
49
|
let featureIndex = 1;
|
|
50
|
-
let currentFeatureId = featureIndices
|
|
50
|
+
let currentFeatureId = getFrequentValue(featureIndices.slice(0, VALUES_PER_VERTEX));
|
|
51
51
|
const faceRangeList: any[] = [];
|
|
52
52
|
const featureIds: any[] = [];
|
|
53
53
|
const uniqueFeatureIds = [currentFeatureId];
|
|
@@ -55,20 +55,21 @@ function calculateFaceRangesAndFeaturesCount(featureIndices: number[]): {
|
|
|
55
55
|
faceRangeList[0] = 0;
|
|
56
56
|
featureIds[0] = currentFeatureId;
|
|
57
57
|
|
|
58
|
-
for (let index =
|
|
59
|
-
|
|
58
|
+
for (let index = VALUES_PER_VERTEX; index < featureIndices.length; index += VALUES_PER_VERTEX) {
|
|
59
|
+
const newFeatureId = getFrequentValue(featureIndices.slice(index, index + VALUES_PER_VERTEX));
|
|
60
|
+
if (currentFeatureId !== newFeatureId) {
|
|
60
61
|
faceRangeList[rangeIndex] = index / VALUES_PER_VERTEX - 1;
|
|
61
62
|
faceRangeList[rangeIndex + 1] = index / VALUES_PER_VERTEX;
|
|
62
|
-
featureIds[featureIndex] =
|
|
63
|
+
featureIds[featureIndex] = newFeatureId;
|
|
63
64
|
|
|
64
|
-
if (!uniqueFeatureIds.includes(
|
|
65
|
-
uniqueFeatureIds.push(
|
|
65
|
+
if (!uniqueFeatureIds.includes(newFeatureId)) {
|
|
66
|
+
uniqueFeatureIds.push(newFeatureId);
|
|
66
67
|
}
|
|
67
68
|
|
|
68
69
|
rangeIndex += 2;
|
|
69
70
|
featureIndex += 1;
|
|
70
71
|
}
|
|
71
|
-
currentFeatureId =
|
|
72
|
+
currentFeatureId = newFeatureId;
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
faceRangeList[rangeIndex] = featureIndices.length / VALUES_PER_VERTEX - 1;
|
|
@@ -79,6 +80,28 @@ function calculateFaceRangesAndFeaturesCount(featureIndices: number[]): {
|
|
|
79
80
|
return {faceRange, featureCount, featureIds};
|
|
80
81
|
}
|
|
81
82
|
|
|
83
|
+
/**
|
|
84
|
+
* Find most frequent value to avoid situation where one vertex can be part of multiple features (objects).
|
|
85
|
+
* @param values
|
|
86
|
+
*/
|
|
87
|
+
function getFrequentValue(values: number[]): number {
|
|
88
|
+
const map: {[key: number]: number} = {};
|
|
89
|
+
|
|
90
|
+
let mostFrequentValue = values[0];
|
|
91
|
+
let maxCount = 1;
|
|
92
|
+
|
|
93
|
+
for (const value of values) {
|
|
94
|
+
// Save item and it's frequency count to the map.
|
|
95
|
+
map[value] = (map[value] || 0) + 1;
|
|
96
|
+
// Find max count of frequency.
|
|
97
|
+
maxCount = maxCount > map[value] ? maxCount : map[value];
|
|
98
|
+
// Find the most frequent value.
|
|
99
|
+
mostFrequentValue = maxCount > map[value] ? mostFrequentValue : value;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return mostFrequentValue;
|
|
103
|
+
}
|
|
104
|
+
|
|
82
105
|
/**
|
|
83
106
|
* Generate list of attribute object grouped by feature ids.
|
|
84
107
|
* @param attributes
|
|
@@ -1,8 +1,13 @@
|
|
|
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';
|
|
4
|
+
|
|
1
5
|
import {Vector3, Matrix4, Vector4} from '@math.gl/core';
|
|
2
6
|
import {Ellipsoid} from '@math.gl/geospatial';
|
|
3
7
|
|
|
4
8
|
import {DracoWriterWorker} from '@loaders.gl/draco';
|
|
5
9
|
import {assert, encode} from '@loaders.gl/core';
|
|
10
|
+
import {Tile3D} from '@loaders.gl/tiles';
|
|
6
11
|
import {concatenateArrayBuffers, concatenateTypedArrays} from '@loaders.gl/loader-utils';
|
|
7
12
|
import md5 from 'md5';
|
|
8
13
|
import {generateAttributes} from './geometry-attributes';
|
|
@@ -13,7 +18,6 @@ import {
|
|
|
13
18
|
I3SMaterialWithTexture,
|
|
14
19
|
SharedResourcesArrays
|
|
15
20
|
} from '../types';
|
|
16
|
-
import {B3DMContent} from '@loaders.gl/3d-tiles';
|
|
17
21
|
import {GLTFMaterialPostprocessed, GLTFNodePostprocessed} from '@loaders.gl/gltf';
|
|
18
22
|
import {
|
|
19
23
|
AttributeStorageInfo,
|
|
@@ -30,6 +34,8 @@ import {
|
|
|
30
34
|
} from 'modules/gltf/src/lib/types/gltf-types';
|
|
31
35
|
import {B3DMAttributesData /*transformI3SAttributesOnWorker */} from '../../i3s-attributes-worker';
|
|
32
36
|
import {prepareDataForAttributesConversion} from './gltf-attributes';
|
|
37
|
+
import {handleBatchIdsExtensions} from './batch-ids-extensions';
|
|
38
|
+
import {checkPropertiesLength, flattenPropertyTableByFeatureIds} from './feature-attributes';
|
|
33
39
|
|
|
34
40
|
// Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/pbrMetallicRoughness.cmn.md
|
|
35
41
|
const DEFAULT_ROUGHNESS_FACTOR = 1;
|
|
@@ -50,6 +56,9 @@ const OBJECT_ID_TYPE = 'Oid32';
|
|
|
50
56
|
*/
|
|
51
57
|
const BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES = ['CUSTOM_ATTRIBUTE_2', '_BATCHID', 'BATCHID'];
|
|
52
58
|
|
|
59
|
+
const EXT_FEATURE_METADATA = 'EXT_feature_metadata';
|
|
60
|
+
const EXT_MESH_FEATURES = 'EXT_mesh_features';
|
|
61
|
+
|
|
53
62
|
let scratchVector = new Vector3();
|
|
54
63
|
|
|
55
64
|
/**
|
|
@@ -67,6 +76,7 @@ let scratchVector = new Vector3();
|
|
|
67
76
|
export default async function convertB3dmToI3sGeometry(
|
|
68
77
|
tileContent: B3DMContent,
|
|
69
78
|
nodeId: number,
|
|
79
|
+
propertyTable: FeatureTableJson | null,
|
|
70
80
|
featuresHashArray: string[],
|
|
71
81
|
attributeStorageInfo: AttributeStorageInfo[] | undefined,
|
|
72
82
|
draco: boolean,
|
|
@@ -128,6 +138,7 @@ export default async function convertB3dmToI3sGeometry(
|
|
|
128
138
|
tileContent,
|
|
129
139
|
nodeId: nodesCounter,
|
|
130
140
|
featuresHashArray,
|
|
141
|
+
propertyTable,
|
|
131
142
|
attributeStorageInfo,
|
|
132
143
|
draco,
|
|
133
144
|
workerSource
|
|
@@ -191,6 +202,7 @@ async function _makeNodeResources({
|
|
|
191
202
|
tileContent,
|
|
192
203
|
nodeId,
|
|
193
204
|
featuresHashArray,
|
|
205
|
+
propertyTable,
|
|
194
206
|
attributeStorageInfo,
|
|
195
207
|
draco,
|
|
196
208
|
workerSource
|
|
@@ -201,6 +213,7 @@ async function _makeNodeResources({
|
|
|
201
213
|
tileContent: B3DMContent;
|
|
202
214
|
nodeId: number;
|
|
203
215
|
featuresHashArray: string[];
|
|
216
|
+
propertyTable: FeatureTableJson | null;
|
|
204
217
|
attributeStorageInfo?: AttributeStorageInfo[];
|
|
205
218
|
draco: boolean;
|
|
206
219
|
workerSource: {[key: string]: string};
|
|
@@ -250,11 +263,15 @@ async function _makeNodeResources({
|
|
|
250
263
|
)
|
|
251
264
|
: null;
|
|
252
265
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
266
|
+
let attributes: ArrayBuffer[] = [];
|
|
267
|
+
|
|
268
|
+
if (attributeStorageInfo && propertyTable) {
|
|
269
|
+
attributes = convertPropertyTableToAttributeBuffers(
|
|
270
|
+
featureIds,
|
|
271
|
+
propertyTable,
|
|
272
|
+
attributeStorageInfo
|
|
273
|
+
);
|
|
274
|
+
}
|
|
258
275
|
|
|
259
276
|
return {
|
|
260
277
|
geometry: fileBuffer,
|
|
@@ -407,9 +424,12 @@ function convertNode(
|
|
|
407
424
|
const transformationMatrix = getCompositeTransformationMatrix(node, matrix);
|
|
408
425
|
|
|
409
426
|
const mesh = node.mesh;
|
|
427
|
+
const images = node.images;
|
|
428
|
+
|
|
410
429
|
if (mesh) {
|
|
411
430
|
convertMesh(
|
|
412
431
|
mesh,
|
|
432
|
+
images,
|
|
413
433
|
cartographicOrigin,
|
|
414
434
|
cartesianModelMatrix,
|
|
415
435
|
attributesMap,
|
|
@@ -441,6 +461,7 @@ function convertNode(
|
|
|
441
461
|
*/
|
|
442
462
|
function convertMesh(
|
|
443
463
|
mesh: GLTFMeshPostprocessed,
|
|
464
|
+
images: Image[],
|
|
444
465
|
cartographicOrigin: Vector3,
|
|
445
466
|
cartesianModelMatrix: Matrix4,
|
|
446
467
|
attributesMap: Map<string, ConvertedAttributes>,
|
|
@@ -498,7 +519,7 @@ function convertMesh(
|
|
|
498
519
|
|
|
499
520
|
outputAttributes.featureIndicesGroups = outputAttributes.featureIndicesGroups || [];
|
|
500
521
|
outputAttributes.featureIndicesGroups.push(
|
|
501
|
-
flattenBatchIds(
|
|
522
|
+
flattenBatchIds(getBatchIds(attributes, primitive, images), primitive.indices?.value)
|
|
502
523
|
);
|
|
503
524
|
}
|
|
504
525
|
}
|
|
@@ -665,14 +686,23 @@ function flattenBatchIds(batchedIds: number[], indices: Uint8Array): number[] {
|
|
|
665
686
|
}
|
|
666
687
|
|
|
667
688
|
/**
|
|
668
|
-
*
|
|
669
|
-
* @param attributes
|
|
670
|
-
* @
|
|
689
|
+
* Get batchIds for featureIds creation
|
|
690
|
+
* @param attributes
|
|
691
|
+
* @param primitive
|
|
692
|
+
* @param textures
|
|
671
693
|
*/
|
|
672
|
-
function
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
694
|
+
function getBatchIds(
|
|
695
|
+
attributes: {
|
|
696
|
+
[key: string]: GLTFAccessorPostprocessed;
|
|
697
|
+
},
|
|
698
|
+
primitive: MeshPrimitive,
|
|
699
|
+
images: Image[]
|
|
700
|
+
): number[] {
|
|
701
|
+
const batchIds: number[] = handleBatchIdsExtensions(attributes, primitive, images);
|
|
702
|
+
|
|
703
|
+
if (batchIds.length) {
|
|
704
|
+
return batchIds;
|
|
705
|
+
}
|
|
676
706
|
|
|
677
707
|
for (let index = 0; index < BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES.length; index++) {
|
|
678
708
|
const possibleBatchIdAttributeName = BATCHED_ID_POSSIBLE_ATTRIBUTE_NAMES[index];
|
|
@@ -680,12 +710,11 @@ function getBatchIdsByAttributeName(attributes: {
|
|
|
680
710
|
attributes[possibleBatchIdAttributeName] &&
|
|
681
711
|
attributes[possibleBatchIdAttributeName].value
|
|
682
712
|
) {
|
|
683
|
-
|
|
684
|
-
break;
|
|
713
|
+
return attributes[possibleBatchIdAttributeName].value;
|
|
685
714
|
}
|
|
686
715
|
}
|
|
687
716
|
|
|
688
|
-
return
|
|
717
|
+
return [];
|
|
689
718
|
}
|
|
690
719
|
|
|
691
720
|
/**
|
|
@@ -1036,49 +1065,66 @@ function replaceIndicesByUnique(indicesArray, featureMap) {
|
|
|
1036
1065
|
}
|
|
1037
1066
|
|
|
1038
1067
|
/**
|
|
1039
|
-
* Convert
|
|
1040
|
-
* @param {Object}
|
|
1068
|
+
* Convert property table data to attribute buffers.
|
|
1069
|
+
* @param {Object} propertyTable - table with metadata for particular feature.
|
|
1041
1070
|
* @param {Array} featureIds
|
|
1042
1071
|
* @param {Array} attributeStorageInfo
|
|
1043
1072
|
* @returns {Array} - Array of file buffers.
|
|
1044
1073
|
*/
|
|
1045
|
-
function
|
|
1074
|
+
function convertPropertyTableToAttributeBuffers(
|
|
1075
|
+
featureIds: number[],
|
|
1076
|
+
propertyTable: FeatureTableJson,
|
|
1077
|
+
attributeStorageInfo: AttributeStorageInfo[]
|
|
1078
|
+
) {
|
|
1046
1079
|
const attributeBuffers: ArrayBuffer[] = [];
|
|
1047
1080
|
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
};
|
|
1081
|
+
const needFlattenPropertyTable = checkPropertiesLength(featureIds, propertyTable);
|
|
1082
|
+
const properties = needFlattenPropertyTable
|
|
1083
|
+
? flattenPropertyTableByFeatureIds(featureIds, propertyTable)
|
|
1084
|
+
: propertyTable;
|
|
1053
1085
|
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
switch (type) {
|
|
1060
|
-
case OBJECT_ID_TYPE:
|
|
1061
|
-
case SHORT_INT_TYPE:
|
|
1062
|
-
attributeBuffer = generateShortIntegerAttributeBuffer(batchTableWithFeatureIds[key]);
|
|
1063
|
-
break;
|
|
1064
|
-
case DOUBLE_TYPE:
|
|
1065
|
-
attributeBuffer = generateDoubleAttributeBuffer(batchTableWithFeatureIds[key]);
|
|
1066
|
-
break;
|
|
1067
|
-
case STRING_TYPE:
|
|
1068
|
-
attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
|
|
1069
|
-
break;
|
|
1070
|
-
default:
|
|
1071
|
-
attributeBuffer = generateStringAttributeBuffer(batchTableWithFeatureIds[key]);
|
|
1072
|
-
}
|
|
1086
|
+
const propertyTableWithObjectIds = {
|
|
1087
|
+
OBJECTID: featureIds,
|
|
1088
|
+
...properties
|
|
1089
|
+
};
|
|
1073
1090
|
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
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);
|
|
1078
1097
|
}
|
|
1079
1098
|
|
|
1080
1099
|
return attributeBuffers;
|
|
1081
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
|
+
|
|
1082
1128
|
/**
|
|
1083
1129
|
* Return attribute type.
|
|
1084
1130
|
* @param {String} key
|
|
@@ -1242,3 +1288,96 @@ function generateFeatureIndexAttribute(featureIndex, faceRange) {
|
|
|
1242
1288
|
|
|
1243
1289
|
return orderedFeatureIndices;
|
|
1244
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
|
+
}
|
|
@@ -35,6 +35,20 @@ export function prepareDataForAttributesConversion(tileContent: B3DMContent): B3
|
|
|
35
35
|
tileContent.gltf?.nodes ||
|
|
36
36
|
[];
|
|
37
37
|
|
|
38
|
+
const images =
|
|
39
|
+
tileContent.gltf?.images?.map((imageObject) => {
|
|
40
|
+
// Need data only for uncompressed images because we can't get batchIds from compressed textures.
|
|
41
|
+
const data = imageObject?.image?.compressed ? null : imageObject?.image?.data.subarray();
|
|
42
|
+
return {
|
|
43
|
+
data,
|
|
44
|
+
compressed: Boolean(imageObject?.image?.compressed),
|
|
45
|
+
height: imageObject.image.height,
|
|
46
|
+
width: imageObject.image.width,
|
|
47
|
+
components: imageObject.image.components,
|
|
48
|
+
mimeType: imageObject.mimeType
|
|
49
|
+
};
|
|
50
|
+
}) || [];
|
|
51
|
+
|
|
38
52
|
const prepearedNodes = nodes.map((node) => {
|
|
39
53
|
if (!node.mesh) {
|
|
40
54
|
return node;
|
|
@@ -42,6 +56,7 @@ export function prepareDataForAttributesConversion(tileContent: B3DMContent): B3
|
|
|
42
56
|
|
|
43
57
|
return {
|
|
44
58
|
...node,
|
|
59
|
+
images,
|
|
45
60
|
mesh: {
|
|
46
61
|
...node.mesh,
|
|
47
62
|
primitives: node.mesh?.primitives.map((primitive) => ({
|
|
@@ -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
|
|
@@ -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,
|
|
@@ -938,12 +928,19 @@ export default class I3SConverter {
|
|
|
938
928
|
|
|
939
929
|
if (this.generateTextures) {
|
|
940
930
|
formats.push({name: '1', format: 'ktx2'});
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
931
|
+
// For Node.js texture.image.data is type of Buffer
|
|
932
|
+
const copyArrayBuffer = texture.image.data.subarray();
|
|
933
|
+
const arrayToEncode = new Uint8Array(copyArrayBuffer);
|
|
934
|
+
const ktx2TextureData = encode(
|
|
935
|
+
{...texture.image, data: arrayToEncode},
|
|
936
|
+
KTX2BasisWriterWorker,
|
|
937
|
+
{
|
|
938
|
+
...KTX2BasisWriterWorker.options,
|
|
939
|
+
source: this.workerSource.ktx2,
|
|
940
|
+
reuseWorkers: true,
|
|
941
|
+
_nodeWorkers: true
|
|
942
|
+
}
|
|
943
|
+
);
|
|
947
944
|
|
|
948
945
|
await this.writeTextureFile(ktx2TextureData, '1', 'ktx2', childPath, slpkChildPath);
|
|
949
946
|
}
|
|
@@ -1073,7 +1070,7 @@ export default class I3SConverter {
|
|
|
1073
1070
|
/**
|
|
1074
1071
|
* Generate storage attribute for map segmentation.
|
|
1075
1072
|
* @param attributeIndex - order index of attribute (f_0, f_1 ...).
|
|
1076
|
-
* @param key - attribute key from
|
|
1073
|
+
* @param key - attribute key from propertyTable.
|
|
1077
1074
|
* @param attributeType - attribute type.
|
|
1078
1075
|
* @return Updated storageAttribute.
|
|
1079
1076
|
*/
|
|
@@ -1112,7 +1109,7 @@ export default class I3SConverter {
|
|
|
1112
1109
|
/**
|
|
1113
1110
|
* Get the attribute type for attributeStorageInfo https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
|
|
1114
1111
|
* @param key - attribute's key
|
|
1115
|
-
* @param attribute - attribute's type in
|
|
1112
|
+
* @param attribute - attribute's type in propertyTable
|
|
1116
1113
|
*/
|
|
1117
1114
|
private getAttributeType(key: string, attribute: string): string {
|
|
1118
1115
|
if (key === OBJECT_ID_TYPE) {
|
|
@@ -1180,24 +1177,24 @@ export default class I3SConverter {
|
|
|
1180
1177
|
}
|
|
1181
1178
|
|
|
1182
1179
|
/**
|
|
1183
|
-
* Do conversion of 3DTiles
|
|
1184
|
-
* @param
|
|
1180
|
+
* Do conversion of 3DTiles property table to I3s node attributes.
|
|
1181
|
+
* @param propertyTable - Table with layer meta data.
|
|
1185
1182
|
*/
|
|
1186
|
-
private
|
|
1183
|
+
private _convertPropertyTableToNodeAttributes(propertyTable: FeatureTableJson): void {
|
|
1187
1184
|
let attributeIndex = 0;
|
|
1188
|
-
const
|
|
1185
|
+
const propertyTableWithObjectId = {
|
|
1189
1186
|
OBJECTID: [0],
|
|
1190
|
-
...
|
|
1187
|
+
...propertyTable
|
|
1191
1188
|
};
|
|
1192
1189
|
|
|
1193
|
-
for (const key in
|
|
1194
|
-
const firstAttribute =
|
|
1190
|
+
for (const key in propertyTableWithObjectId) {
|
|
1191
|
+
const firstAttribute = propertyTableWithObjectId[key][0];
|
|
1195
1192
|
const attributeType = this.getAttributeType(key, firstAttribute);
|
|
1196
1193
|
|
|
1197
1194
|
const storageAttribute = this._createdStorageAttribute(attributeIndex, key, attributeType);
|
|
1198
1195
|
const fieldAttributeType = this._getFieldAttributeType(attributeType);
|
|
1199
1196
|
const fieldAttribute = this._createFieldAttribute(key, fieldAttributeType);
|
|
1200
|
-
const popupInfo = this._createPopupInfo(
|
|
1197
|
+
const popupInfo = this._createPopupInfo(propertyTableWithObjectId);
|
|
1201
1198
|
|
|
1202
1199
|
this.layers0!.attributeStorageInfo!.push(storageAttribute);
|
|
1203
1200
|
this.layers0!.fields!.push(fieldAttribute);
|
|
@@ -1209,7 +1206,7 @@ export default class I3SConverter {
|
|
|
1209
1206
|
}
|
|
1210
1207
|
|
|
1211
1208
|
/**
|
|
1212
|
-
* Find and return attribute type based on key form
|
|
1209
|
+
* Find and return attribute type based on key form propertyTable.
|
|
1213
1210
|
* @param attributeType
|
|
1214
1211
|
*/
|
|
1215
1212
|
private _getFieldAttributeType(attributeType: Attribute): ESRIField {
|
|
@@ -1229,10 +1226,10 @@ export default class I3SConverter {
|
|
|
1229
1226
|
|
|
1230
1227
|
/**
|
|
1231
1228
|
* Generate popup info to show metadata on the map.
|
|
1232
|
-
* @param
|
|
1229
|
+
* @param propertyTable - table data with OBJECTID.
|
|
1233
1230
|
* @return data for correct rendering of popup.
|
|
1234
1231
|
*/
|
|
1235
|
-
private _createPopupInfo(
|
|
1232
|
+
private _createPopupInfo(propertyTable: FeatureTableJson): PopupInfo {
|
|
1236
1233
|
const title = '{OBJECTID}';
|
|
1237
1234
|
const mediaInfos = [];
|
|
1238
1235
|
const fieldInfos: FieldInfo[] = [];
|
|
@@ -1242,7 +1239,7 @@ export default class I3SConverter {
|
|
|
1242
1239
|
}[] = [];
|
|
1243
1240
|
const expressionInfos = [];
|
|
1244
1241
|
|
|
1245
|
-
for (const key in
|
|
1242
|
+
for (const key in propertyTable) {
|
|
1246
1243
|
fieldInfos.push({
|
|
1247
1244
|
fieldName: key,
|
|
1248
1245
|
visible: true,
|
|
@@ -69,19 +69,21 @@ export default class WriteQueue<T extends WriteQueueItem> extends Queue<T> {
|
|
|
69
69
|
archiveKeys.push(archiveKey);
|
|
70
70
|
promises.push(writePromise);
|
|
71
71
|
}
|
|
72
|
-
const writeResults = await Promise.
|
|
72
|
+
const writeResults = await Promise.allSettled(promises);
|
|
73
73
|
this.updateFileMap(archiveKeys, writeResults);
|
|
74
74
|
}
|
|
75
75
|
this.writePromise = null;
|
|
76
76
|
}
|
|
77
77
|
|
|
78
|
-
private updateFileMap(
|
|
78
|
+
private updateFileMap(
|
|
79
|
+
archiveKeys: (string | undefined)[],
|
|
80
|
+
writeResults: PromiseSettledResult<string>[]
|
|
81
|
+
) {
|
|
79
82
|
for (let i = 0; i < archiveKeys.length; i++) {
|
|
80
83
|
const archiveKey = archiveKeys[i];
|
|
81
|
-
if (
|
|
82
|
-
|
|
84
|
+
if (archiveKey && 'value' in writeResults[i]) {
|
|
85
|
+
this.fileMap[archiveKey] = (writeResults[i] as PromiseFulfilledResult<string>).value;
|
|
83
86
|
}
|
|
84
|
-
this.fileMap[archiveKey] = writeResults[i];
|
|
85
87
|
}
|
|
86
88
|
}
|
|
87
89
|
}
|