@loaders.gl/i3s 4.0.0-alpha.4 → 4.0.0-alpha.5

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 (65) hide show
  1. package/dist/bundle.d.ts +2 -0
  2. package/dist/bundle.d.ts.map +1 -0
  3. package/dist/dist.min.js +10374 -0
  4. package/dist/i3s-attribute-loader.d.ts +14 -0
  5. package/dist/i3s-attribute-loader.d.ts.map +1 -0
  6. package/dist/i3s-attribute-loader.js +1 -1
  7. package/dist/i3s-building-scene-layer-loader.d.ts +6 -0
  8. package/dist/i3s-building-scene-layer-loader.d.ts.map +1 -0
  9. package/dist/i3s-building-scene-layer-loader.js +1 -1
  10. package/dist/i3s-content-loader.d.ts +6 -0
  11. package/dist/i3s-content-loader.d.ts.map +1 -0
  12. package/dist/i3s-content-loader.js +1 -1
  13. package/dist/i3s-content-worker.js +391 -270
  14. package/dist/i3s-loader.d.ts +6 -0
  15. package/dist/i3s-loader.d.ts.map +1 -0
  16. package/dist/i3s-loader.js +1 -1
  17. package/dist/i3s-node-page-loader.d.ts +6 -0
  18. package/dist/i3s-node-page-loader.d.ts.map +1 -0
  19. package/dist/i3s-node-page-loader.js +1 -1
  20. package/dist/index.d.ts +7 -0
  21. package/dist/index.d.ts.map +1 -0
  22. package/dist/index.js.map +1 -1
  23. package/dist/lib/helpers/i3s-nodepages-tiles.d.ts +73 -0
  24. package/dist/lib/helpers/i3s-nodepages-tiles.d.ts.map +1 -0
  25. package/dist/lib/helpers/i3s-nodepages-tiles.js +33 -23
  26. package/dist/lib/helpers/i3s-nodepages-tiles.js.map +1 -1
  27. package/dist/lib/parsers/constants.d.ts +40 -0
  28. package/dist/lib/parsers/constants.d.ts.map +1 -0
  29. package/dist/lib/parsers/constants.js +54 -38
  30. package/dist/lib/parsers/constants.js.map +1 -1
  31. package/dist/lib/parsers/parse-i3s-attribute.d.ts +10 -0
  32. package/dist/lib/parsers/parse-i3s-attribute.d.ts.map +1 -0
  33. package/dist/lib/parsers/parse-i3s-building-scene-layer.d.ts +9 -0
  34. package/dist/lib/parsers/parse-i3s-building-scene-layer.d.ts.map +1 -0
  35. package/dist/lib/parsers/parse-i3s-building-scene-layer.js +2 -0
  36. package/dist/lib/parsers/parse-i3s-building-scene-layer.js.map +1 -1
  37. package/dist/lib/parsers/parse-i3s-tile-content.d.ts +4 -0
  38. package/dist/lib/parsers/parse-i3s-tile-content.d.ts.map +1 -0
  39. package/dist/lib/parsers/parse-i3s-tile-content.js +76 -79
  40. package/dist/lib/parsers/parse-i3s-tile-content.js.map +1 -1
  41. package/dist/lib/parsers/parse-i3s.d.ts +6 -0
  42. package/dist/lib/parsers/parse-i3s.d.ts.map +1 -0
  43. package/dist/lib/parsers/parse-i3s.js +40 -25
  44. package/dist/lib/parsers/parse-i3s.js.map +1 -1
  45. package/dist/lib/utils/convert-i3s-obb-to-mbs.d.ts +2 -0
  46. package/dist/lib/utils/convert-i3s-obb-to-mbs.d.ts.map +1 -0
  47. package/dist/lib/utils/url-utils.d.ts +22 -0
  48. package/dist/lib/utils/url-utils.d.ts.map +1 -0
  49. package/dist/lib/utils/url-utils.js +2 -3
  50. package/dist/lib/utils/url-utils.js.map +1 -1
  51. package/dist/types.d.ts +643 -0
  52. package/dist/types.d.ts.map +1 -0
  53. package/dist/types.js +20 -1
  54. package/dist/types.js.map +1 -1
  55. package/dist/workers/i3s-content-worker.d.ts +2 -0
  56. package/dist/workers/i3s-content-worker.d.ts.map +1 -0
  57. package/package.json +12 -12
  58. package/src/index.ts +3 -3
  59. package/src/lib/helpers/i3s-nodepages-tiles.ts +71 -55
  60. package/src/lib/parsers/constants.ts +67 -54
  61. package/src/lib/parsers/parse-i3s-building-scene-layer.ts +2 -1
  62. package/src/lib/parsers/parse-i3s-tile-content.ts +150 -146
  63. package/src/lib/parsers/parse-i3s.ts +48 -37
  64. package/src/lib/utils/url-utils.ts +7 -7
  65. package/src/types.ts +412 -73
@@ -2,68 +2,80 @@ import type {TypedArray} from '@loaders.gl/schema';
2
2
  import {load, parse} from '@loaders.gl/core';
3
3
  import {Vector3, Matrix4} from '@math.gl/core';
4
4
  import {Ellipsoid} from '@math.gl/geospatial';
5
-
6
5
  import type {LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils';
7
6
  import {ImageLoader} from '@loaders.gl/images';
8
- import {DracoLoader} from '@loaders.gl/draco';
7
+ import {DracoLoader, DracoMesh} from '@loaders.gl/draco';
9
8
  import {BasisLoader, CompressedTextureLoader} from '@loaders.gl/textures';
10
9
 
11
- import {Tileset, Tile} from '../../types';
10
+ import {
11
+ I3STilesetHeader,
12
+ I3STileHeader,
13
+ FeatureAttribute,
14
+ VertexAttribute,
15
+ I3SMeshAttributes,
16
+ I3SMeshAttribute,
17
+ TileContentTexture,
18
+ HeaderAttributeProperty,
19
+ I3SMaterialDefinition
20
+ } from '../../types';
12
21
  import {getUrlWithToken} from '../utils/url-utils';
13
22
 
14
- import {
15
- GL_TYPE_MAP,
16
- TYPE_ARRAY_MAP,
17
- SIZEOF,
18
- I3S_NAMED_HEADER_ATTRIBUTES,
19
- I3S_NAMED_VERTEX_ATTRIBUTES,
20
- I3S_NAMED_GEOMETRY_ATTRIBUTES,
21
- COORDINATE_SYSTEM
22
- } from './constants';
23
+ import {GL_TYPE_MAP, getConstructorForDataFormat, sizeOf, COORDINATE_SYSTEM} from './constants';
23
24
 
24
25
  const scratchVector = new Vector3([0, 0, 0]);
25
26
 
26
- const FORMAT_LOADER_MAP = {
27
- jpeg: ImageLoader,
28
- png: ImageLoader,
29
- 'ktx-etc2': CompressedTextureLoader,
30
- dds: CompressedTextureLoader,
31
- ktx2: BasisLoader
32
- };
27
+ function getLoaderForTextureFormat(textureFormat?: 'jpg' | 'png' | 'ktx-etc2' | 'dds' | 'ktx2') {
28
+ switch (textureFormat) {
29
+ case 'ktx-etc2':
30
+ case 'dds':
31
+ return CompressedTextureLoader;
32
+ case 'ktx2':
33
+ return BasisLoader;
34
+ case 'jpg':
35
+ case 'png':
36
+ default:
37
+ return ImageLoader;
38
+ }
39
+ }
33
40
 
34
41
  const I3S_ATTRIBUTE_TYPE = 'i3s-attribute-type';
35
42
 
36
43
  export async function parseI3STileContent(
37
44
  arrayBuffer: ArrayBuffer,
38
- tile: Tile,
39
- tileset: Tileset,
45
+ tile: I3STileHeader,
46
+ tileset: I3STilesetHeader,
40
47
  options?: LoaderOptions,
41
48
  context?: LoaderContext
42
49
  ) {
43
50
  tile.content = tile.content || {};
44
51
  tile.content.featureIds = tile.content.featureIds || null;
45
52
 
46
- // construct featureData from defaultGeometrySchema;
47
- tile.content.featureData = constructFeatureDataStruct(tile, tileset);
48
53
  tile.content.attributes = {};
49
54
 
50
55
  if (tile.textureUrl) {
51
56
  const url = getUrlWithToken(tile.textureUrl, options?.i3s?.token);
52
- const loader = FORMAT_LOADER_MAP[tile.textureFormat] || ImageLoader;
53
- // @ts-ignore context must be defined
57
+ const loader = getLoaderForTextureFormat(tile.textureFormat);
54
58
  const response = await fetch(url);
55
59
  const arrayBuffer = await response.arrayBuffer();
56
60
 
57
61
  if (options?.i3s.decodeTextures) {
58
62
  if (loader === ImageLoader) {
59
63
  const options = {...tile.textureLoaderOptions, image: {type: 'data'}};
60
- // @ts-ignore context must be defined
61
- // Image constructor is not supported in worker thread.
62
- // Do parsing image data on the main thread by using context to avoid worker issues.
63
- tile.content.texture = await context.parse(arrayBuffer, options);
64
+ try {
65
+ // @ts-ignore context must be defined
66
+ // Image constructor is not supported in worker thread.
67
+ // Do parsing image data on the main thread by using context to avoid worker issues.
68
+ tile.content.texture = await context.parse(arrayBuffer, options);
69
+ } catch (e) {
70
+ // context object is different between worker and node.js conversion script.
71
+ // To prevent error we parse data in ordinary way if it is not parsed by using context.
72
+ tile.content.texture = await parse(arrayBuffer, loader, options);
73
+ }
64
74
  } else if (loader === CompressedTextureLoader || loader === BasisLoader) {
65
- // @ts-ignore context must be defined
66
- const texture = await load(arrayBuffer, loader, tile.textureLoaderOptions);
75
+ let texture = await load(arrayBuffer, loader, tile.textureLoaderOptions);
76
+ if (loader === BasisLoader) {
77
+ texture = texture[0];
78
+ }
67
79
  tile.content.texture = {
68
80
  compressed: true,
69
81
  mipmaps: false,
@@ -82,35 +94,36 @@ export async function parseI3STileContent(
82
94
  tile.content.texture = null;
83
95
  }
84
96
 
85
- return await parseI3SNodeGeometry(arrayBuffer, tile, options, context);
97
+ return await parseI3SNodeGeometry(arrayBuffer, tile, tileset, options);
86
98
  }
87
99
 
88
100
  /* eslint-disable max-statements */
89
101
  async function parseI3SNodeGeometry(
90
102
  arrayBuffer: ArrayBuffer,
91
- tile: Tile = {},
92
- options?: LoaderOptions,
93
- context?: LoaderContext
103
+ tile: I3STileHeader,
104
+ tileset: I3STilesetHeader,
105
+ options?: LoaderOptions
94
106
  ) {
95
107
  if (!tile.content) {
96
108
  return tile;
97
109
  }
98
110
 
99
111
  const content = tile.content;
100
- let attributes;
101
- let vertexCount;
102
- let byteOffset = 0;
103
- let featureCount = 0;
112
+ let attributes: I3SMeshAttributes;
113
+ let vertexCount: number;
114
+ let byteOffset: number = 0;
115
+ let featureCount: number = 0;
116
+ let indices: TypedArray | undefined;
104
117
 
105
118
  if (tile.isDracoGeometry) {
106
- const decompressedGeometry = await parse(arrayBuffer, DracoLoader, {
119
+ const decompressedGeometry: DracoMesh = await parse(arrayBuffer, DracoLoader, {
107
120
  draco: {
108
121
  attributeNameEntry: I3S_ATTRIBUTE_TYPE
109
122
  }
110
123
  });
111
-
124
+ // @ts-expect-error
112
125
  vertexCount = decompressedGeometry.header.vertexCount;
113
- const indices = decompressedGeometry.indices.value;
126
+ indices = decompressedGeometry.indices?.value;
114
127
  const {
115
128
  POSITION,
116
129
  NORMAL,
@@ -126,8 +139,7 @@ async function parseI3SNodeGeometry(
126
139
  color: COLOR_0,
127
140
  uv0: TEXCOORD_0,
128
141
  uvRegion,
129
- id: featureIndex,
130
- indices
142
+ id: featureIndex
131
143
  };
132
144
 
133
145
  updateAttributesMetadata(attributes, decompressedGeometry);
@@ -138,10 +150,14 @@ async function parseI3SNodeGeometry(
138
150
  flattenFeatureIdsByFeatureIndices(attributes, featureIds);
139
151
  }
140
152
  } else {
141
- const {vertexAttributes, attributesOrder, featureAttributes, featureAttributeOrder} =
142
- content.featureData;
153
+ const {
154
+ vertexAttributes,
155
+ ordering: attributesOrder,
156
+ featureAttributes,
157
+ featureAttributeOrder
158
+ } = tileset.store.defaultGeometrySchema;
143
159
  // First 8 bytes reserved for header (vertexCount and featureCount)
144
- const headers = parseHeaders(content, arrayBuffer);
160
+ const headers = parseHeaders(tileset, arrayBuffer);
145
161
  byteOffset = headers.byteOffset;
146
162
  vertexCount = headers.vertexCount;
147
163
  featureCount = headers.featureCount;
@@ -171,7 +187,7 @@ async function parseI3SNodeGeometry(
171
187
  !options?.i3s?.coordinateSystem ||
172
188
  options.i3s.coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS
173
189
  ) {
174
- const {enuMatrix} = parsePositions(attributes.position, tile);
190
+ const enuMatrix = parsePositions(attributes.position, tile);
175
191
  content.modelMatrix = enuMatrix.invert();
176
192
  content.coordinateSystem = COORDINATE_SYSTEM.METER_OFFSETS;
177
193
  } else {
@@ -186,7 +202,7 @@ async function parseI3SNodeGeometry(
186
202
  texCoords: attributes.uv0,
187
203
  uvRegions: normalizeAttribute(attributes.uvRegion) // Normalize from UInt16
188
204
  };
189
- content.indices = attributes.indices || null;
205
+ content.indices = indices || null;
190
206
 
191
207
  if (attributes.id && attributes.id.value) {
192
208
  tile.content.featureIds = attributes.id.value;
@@ -210,7 +226,10 @@ async function parseI3SNodeGeometry(
210
226
  * @param decompressedGeometry
211
227
  * @param attributes
212
228
  */
213
- function updateAttributesMetadata(attributes, decompressedGeometry) {
229
+ function updateAttributesMetadata(
230
+ attributes: I3SMeshAttributes,
231
+ decompressedGeometry: DracoMesh
232
+ ): void {
214
233
  for (const key in decompressedGeometry.loaderData.attributes) {
215
234
  const dracoAttribute = decompressedGeometry.loaderData.attributes[key];
216
235
 
@@ -230,20 +249,23 @@ function updateAttributesMetadata(attributes, decompressedGeometry) {
230
249
  /**
231
250
  * Do concatenation of attribute objects.
232
251
  * Done as separate fucntion to avoid ts errors.
233
- * @param {Object} normalizedVertexAttributes
234
- * @param {Object} normalizedFeatureAttributes
235
- * @returns {object} - result of attributes concatenation.
252
+ * @param normalizedVertexAttributes
253
+ * @param normalizedFeatureAttributes
254
+ * @returns - result of attributes concatenation.
236
255
  */
237
- function concatAttributes(normalizedVertexAttributes, normalizedFeatureAttributes) {
256
+ function concatAttributes(
257
+ normalizedVertexAttributes: I3SMeshAttributes,
258
+ normalizedFeatureAttributes: I3SMeshAttributes
259
+ ): I3SMeshAttributes {
238
260
  return {...normalizedVertexAttributes, ...normalizedFeatureAttributes};
239
261
  }
240
262
 
241
263
  /**
242
264
  * Normalize attribute to range [0..1] . Eg. convert colors buffer from [255,255,255,255] to [1,1,1,1]
243
- * @param {Object} attribute - geometry attribute
244
- * @returns {Object} - geometry attribute in right format
265
+ * @param attribute - geometry attribute
266
+ * @returns - geometry attribute in right format
245
267
  */
246
- function normalizeAttribute(attribute) {
268
+ function normalizeAttribute(attribute: I3SMeshAttribute): I3SMeshAttribute {
247
269
  if (!attribute) {
248
270
  return attribute;
249
271
  }
@@ -251,49 +273,27 @@ function normalizeAttribute(attribute) {
251
273
  return attribute;
252
274
  }
253
275
 
254
- function constructFeatureDataStruct(tile, tileset) {
255
- // seed featureData from defaultGeometrySchema
256
- const defaultGeometrySchema = tileset.store.defaultGeometrySchema;
257
- const featureData = defaultGeometrySchema;
258
- // populate the vertex attributes value types and values per element
259
- for (const geometryAttribute in I3S_NAMED_GEOMETRY_ATTRIBUTES) {
260
- for (const namedAttribute in I3S_NAMED_VERTEX_ATTRIBUTES) {
261
- const attribute = defaultGeometrySchema[geometryAttribute][namedAttribute];
262
- if (attribute) {
263
- const {byteOffset = 0, count = 0, valueType, valuesPerElement} = attribute;
264
-
265
- featureData[geometryAttribute][namedAttribute] = {
266
- valueType,
267
- valuesPerElement,
268
- byteOffset,
269
- count
270
- };
271
- }
272
- }
273
- }
274
-
275
- featureData.attributesOrder = defaultGeometrySchema.ordering;
276
- return featureData;
277
- }
278
-
279
- function parseHeaders(content, buffer) {
276
+ function parseHeaders(tileset: I3STilesetHeader, arrayBuffer: ArrayBuffer) {
280
277
  let byteOffset = 0;
281
278
  // First 8 bytes reserved for header (vertexCount and featurecount)
282
279
  let vertexCount = 0;
283
280
  let featureCount = 0;
284
- const headers = content.featureData[I3S_NAMED_HEADER_ATTRIBUTES.header];
285
- for (const header in headers) {
286
- const {property, type} = headers[header];
287
- const TypedArrayTypeHeader = TYPE_ARRAY_MAP[type];
288
- if (property === I3S_NAMED_HEADER_ATTRIBUTES.vertexCount) {
289
- vertexCount = new TypedArrayTypeHeader(buffer, 0, 4)[0];
290
- byteOffset += SIZEOF[type];
291
- }
292
- if (property === I3S_NAMED_HEADER_ATTRIBUTES.featureCount) {
293
- featureCount = new TypedArrayTypeHeader(buffer, 4, 4)[0];
294
- byteOffset += SIZEOF[type];
281
+ for (const {property, type} of tileset.store.defaultGeometrySchema.header) {
282
+ const TypedArrayTypeHeader = getConstructorForDataFormat(type);
283
+ switch (property) {
284
+ case HeaderAttributeProperty.vertexCount:
285
+ vertexCount = new TypedArrayTypeHeader(arrayBuffer, 0, 4)[0];
286
+ byteOffset += sizeOf(type);
287
+ break;
288
+ case HeaderAttributeProperty.featureCount:
289
+ featureCount = new TypedArrayTypeHeader(arrayBuffer, 4, 4)[0];
290
+ byteOffset += sizeOf(type);
291
+ break;
292
+ default:
293
+ break;
295
294
  }
296
295
  }
296
+
297
297
  return {
298
298
  vertexCount,
299
299
  featureCount,
@@ -304,18 +304,19 @@ function parseHeaders(content, buffer) {
304
304
  /* eslint-enable max-statements */
305
305
 
306
306
  function normalizeAttributes(
307
- arrayBuffer,
308
- byteOffset,
309
- vertexAttributes,
310
- vertexCount,
311
- attributesOrder
307
+ arrayBuffer: ArrayBuffer,
308
+ byteOffset: number,
309
+ vertexAttributes: VertexAttribute | FeatureAttribute,
310
+ vertexCount: number,
311
+ attributesOrder: string[]
312
312
  ) {
313
- const attributes = {};
313
+ const attributes: I3SMeshAttributes = {};
314
314
 
315
315
  // the order of attributes depend on the order being added to the vertexAttributes object
316
316
  for (const attribute of attributesOrder) {
317
317
  if (vertexAttributes[attribute]) {
318
- const {valueType, valuesPerElement} = vertexAttributes[attribute];
318
+ const {valueType, valuesPerElement}: {valueType: string; valuesPerElement: number} =
319
+ vertexAttributes[attribute];
319
320
  // update count and byteOffset count by calculating from defaultGeometrySchema + binnary content
320
321
  const count = vertexCount;
321
322
  // protect from arrayBuffer read overunns by NOT assuming node has regions always even though its declared in defaultGeometrySchema.
@@ -328,12 +329,12 @@ function normalizeAttributes(
328
329
  break;
329
330
  }
330
331
  const buffer = arrayBuffer.slice(byteOffset);
331
- let value: number[] | TypedArray = [];
332
+ let value: TypedArray;
332
333
 
333
334
  if (valueType === 'UInt64') {
334
- value = parseUint64Values(buffer, count * valuesPerElement, SIZEOF[valueType]);
335
+ value = parseUint64Values(buffer, count * valuesPerElement, sizeOf(valueType));
335
336
  } else {
336
- const TypedArrayType = TYPE_ARRAY_MAP[valueType];
337
+ const TypedArrayType = getConstructorForDataFormat(valueType);
337
338
  value = new TypedArrayType(buffer, 0, count * valuesPerElement);
338
339
  }
339
340
 
@@ -345,7 +346,6 @@ function normalizeAttributes(
345
346
 
346
347
  switch (attribute) {
347
348
  case 'color':
348
- // @ts-ignore
349
349
  attributes.color.normalized = true;
350
350
  break;
351
351
  case 'position':
@@ -354,7 +354,7 @@ function normalizeAttributes(
354
354
  default:
355
355
  }
356
356
 
357
- byteOffset = byteOffset + count * valuesPerElement * SIZEOF[valueType];
357
+ byteOffset = byteOffset + count * valuesPerElement * sizeOf(valueType);
358
358
  }
359
359
  }
360
360
 
@@ -372,7 +372,7 @@ function parseUint64Values(
372
372
  buffer: ArrayBuffer,
373
373
  elementsCount: number,
374
374
  attributeSize: number
375
- ): number[] {
375
+ ): Uint32Array {
376
376
  const values: number[] = [];
377
377
  const dataView = new DataView(buffer);
378
378
  let offset = 0;
@@ -388,10 +388,10 @@ function parseUint64Values(
388
388
  offset += attributeSize;
389
389
  }
390
390
 
391
- return values;
391
+ return new Uint32Array(values);
392
392
  }
393
393
 
394
- function parsePositions(attribute, tile) {
394
+ function parsePositions(attribute: I3SMeshAttribute, tile: I3STileHeader): Matrix4 {
395
395
  const mbs = tile.mbs;
396
396
  const value = attribute.value;
397
397
  const metadata = attribute.metadata;
@@ -402,20 +402,22 @@ function parsePositions(attribute, tile) {
402
402
  Ellipsoid.WGS84.eastNorthUpToFixedFrame(cartesianOrigin, enuMatrix);
403
403
  attribute.value = offsetsToCartesians(value, metadata, cartographicOrigin);
404
404
 
405
- return {
406
- enuMatrix
407
- };
405
+ return enuMatrix;
408
406
  }
409
407
 
410
408
  /**
411
409
  * Converts position coordinates to absolute cartesian coordinates
412
- * @param {Float32Array} vertices - "position" attribute data
413
- * @param {Object} metadata - When the geometry is DRACO compressed, contain position attribute's metadata
410
+ * @param vertices - "position" attribute data
411
+ * @param metadata - When the geometry is DRACO compressed, contain position attribute's metadata
414
412
  * https://github.com/Esri/i3s-spec/blob/master/docs/1.7/compressedAttributes.cmn.md
415
- * @param {Vector3} cartographicOrigin - Cartographic origin coordinates
416
- * @returns {Float64Array} - converted "position" data
413
+ * @param cartographicOrigin - Cartographic origin coordinates
414
+ * @returns - converted "position" data
417
415
  */
418
- function offsetsToCartesians(vertices, metadata = {}, cartographicOrigin) {
416
+ function offsetsToCartesians(
417
+ vertices: number[] | TypedArray,
418
+ metadata: any = {},
419
+ cartographicOrigin: Vector3
420
+ ): Float64Array {
419
421
  const positions = new Float64Array(vertices.length);
420
422
  const scaleX = (metadata['i3s-scale_x'] && metadata['i3s-scale_x'].double) || 1;
421
423
  const scaleY = (metadata['i3s-scale_y'] && metadata['i3s-scale_y'].double) || 1;
@@ -441,10 +443,10 @@ function offsetsToCartesians(vertices, metadata = {}, cartographicOrigin) {
441
443
  * @param positions positions attribute
442
444
  * @returns Matrix4 - model matrix for geometry transformation
443
445
  */
444
- function getModelMatrix(positions) {
446
+ function getModelMatrix(positions: I3SMeshAttribute): Matrix4 {
445
447
  const metadata = positions.metadata;
446
- const scaleX = metadata?.['i3s-scale_x']?.double || 1;
447
- const scaleY = metadata?.['i3s-scale_y']?.double || 1;
448
+ const scaleX: number = metadata?.['i3s-scale_x']?.double || 1;
449
+ const scaleY: number = metadata?.['i3s-scale_y']?.double || 1;
448
450
  const modelMatrix = new Matrix4();
449
451
  modelMatrix[0] = scaleX;
450
452
  modelMatrix[5] = scaleY;
@@ -453,12 +455,12 @@ function getModelMatrix(positions) {
453
455
 
454
456
  /**
455
457
  * Makes a glTF-compatible PBR material from an I3S material definition
456
- * @param {object} materialDefinition - i3s material definition
458
+ * @param materialDefinition - i3s material definition
457
459
  * https://github.com/Esri/i3s-spec/blob/master/docs/1.7/materialDefinitions.cmn.md
458
- * @param {object} texture - texture image
460
+ * @param texture - texture image
459
461
  * @returns {object}
460
462
  */
461
- function makePbrMaterial(materialDefinition, texture) {
463
+ function makePbrMaterial(materialDefinition?: I3SMaterialDefinition, texture?: TileContentTexture) {
462
464
  let pbrMaterial;
463
465
  if (materialDefinition) {
464
466
  pbrMaterial = {
@@ -496,17 +498,19 @@ function makePbrMaterial(materialDefinition, texture) {
496
498
  );
497
499
  }
498
500
 
499
- setMaterialTexture(pbrMaterial, texture);
501
+ if (texture) {
502
+ setMaterialTexture(pbrMaterial, texture);
503
+ }
500
504
 
501
505
  return pbrMaterial;
502
506
  }
503
507
 
504
508
  /**
505
509
  * Convert color from [255,255,255,255] to [1,1,1,1]
506
- * @param {Array} colorFactor - color array
507
- * @returns {Array} - new color array
510
+ * @param colorFactor - color array
511
+ * @returns - new color array
508
512
  */
509
- function convertColorFormat(colorFactor) {
513
+ function convertColorFormat(colorFactor: number[]): number[] {
510
514
  const normalizedColor = [...colorFactor];
511
515
  for (let index = 0; index < colorFactor.length; index++) {
512
516
  normalizedColor[index] = colorFactor[index] / 255;
@@ -517,10 +521,10 @@ function convertColorFormat(colorFactor) {
517
521
  /**
518
522
  * Set texture in PBR material
519
523
  * @param {object} material - i3s material definition
520
- * @param {object} image - texture image
521
- * @returns {void}
524
+ * @param image - texture image
525
+ * @returns
522
526
  */
523
- function setMaterialTexture(material, image) {
527
+ function setMaterialTexture(material, image: TileContentTexture): void {
524
528
  const texture = {source: {image}};
525
529
  // I3SLoader now support loading only one texture. This elseif sequence will assign this texture to one of
526
530
  // properties defined in materialDefinition
@@ -548,10 +552,10 @@ function setMaterialTexture(material, image) {
548
552
 
549
553
  /**
550
554
  * Flatten feature ids using face ranges
551
- * @param {object} normalizedFeatureAttributes
552
- * @returns {void}
555
+ * @param normalizedFeatureAttributes
556
+ * @returns
553
557
  */
554
- function flattenFeatureIdsByFaceRanges(normalizedFeatureAttributes) {
558
+ function flattenFeatureIdsByFaceRanges(normalizedFeatureAttributes: I3SMeshAttributes): void {
555
559
  const {id, faceRange} = normalizedFeatureAttributes;
556
560
 
557
561
  if (!id || !faceRange) {
@@ -584,11 +588,14 @@ function flattenFeatureIdsByFaceRanges(normalizedFeatureAttributes) {
584
588
 
585
589
  /**
586
590
  * Flatten feature ids using featureIndices
587
- * @param {object} attributes
588
- * @param {any} featureIds
589
- * @returns {void}
591
+ * @param attributes
592
+ * @param featureIds
593
+ * @returns
590
594
  */
591
- function flattenFeatureIdsByFeatureIndices(attributes, featureIds) {
595
+ function flattenFeatureIdsByFeatureIndices(
596
+ attributes: I3SMeshAttributes,
597
+ featureIds: Int32Array
598
+ ): void {
592
599
  const featureIndices = attributes.id.value;
593
600
  const result = new Float32Array(featureIndices.length);
594
601
 
@@ -601,14 +608,11 @@ function flattenFeatureIdsByFeatureIndices(attributes, featureIds) {
601
608
 
602
609
  /**
603
610
  * Flatten feature ids using featureIndices
604
- * @param {object} featureIndex
605
- * @returns {Int32Array}
611
+ * @param featureIndex
612
+ * @returns
606
613
  */
607
- function getFeatureIdsFromFeatureIndexMetadata(featureIndex) {
608
- return (
609
- featureIndex &&
610
- featureIndex.metadata &&
611
- featureIndex.metadata['i3s-feature-ids'] &&
612
- featureIndex.metadata['i3s-feature-ids'].intArray
613
- );
614
+ function getFeatureIdsFromFeatureIndexMetadata(
615
+ featureIndex: I3SMeshAttribute
616
+ ): Int32Array | undefined {
617
+ return featureIndex?.metadata?.['i3s-feature-ids']?.intArray;
614
618
  }
@@ -4,69 +4,80 @@ import {load} from '@loaders.gl/core';
4
4
  import {TILE_TYPE, TILE_REFINEMENT, TILESET_TYPE} from '@loaders.gl/tiles';
5
5
  import I3SNodePagesTiles from '../helpers/i3s-nodepages-tiles';
6
6
  import {generateTileAttributeUrls, getUrlWithToken} from '../utils/url-utils';
7
+ import {
8
+ I3STilesetHeader,
9
+ I3STileHeader,
10
+ Mbs,
11
+ I3SMinimalNodeData,
12
+ Node3DIndexDocument
13
+ } from '../../types';
14
+ import type {LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils';
7
15
 
8
- export function normalizeTileData(tile, options, context) {
9
- tile.url = context.url;
10
-
11
- if (tile.featureData) {
12
- tile.featureUrl = `${tile.url}/${tile.featureData[0].href}`;
13
- }
14
-
16
+ export function normalizeTileData(tile : Node3DIndexDocument, options : LoaderOptions, context: LoaderContext): I3STileHeader {
17
+ const url: string = context.url || '';
18
+ let contentUrl: string | undefined;
15
19
  if (tile.geometryData) {
16
- tile.contentUrl = `${tile.url}/${tile.geometryData[0].href}`;
20
+ contentUrl = `${url}/${tile.geometryData[0].href}`;
17
21
  }
18
22
 
23
+ let textureUrl: string | undefined;
19
24
  if (tile.textureData) {
20
- tile.textureUrl = `${tile.url}/${tile.textureData[0].href}`;
25
+ textureUrl = `${url}/${tile.textureData[0].href}`;
21
26
  }
22
27
 
28
+ let attributeUrls: string[] | undefined;
23
29
  if (tile.attributeData) {
24
- tile.attributeUrls = generateTileAttributeUrls(tile);
30
+ attributeUrls = generateTileAttributeUrls(url, tile);
25
31
  }
26
32
 
27
- return normalizeTileNonUrlData(tile);
33
+ return normalizeTileNonUrlData({
34
+ ...tile,
35
+ url,
36
+ contentUrl,
37
+ textureUrl,
38
+ attributeUrls,
39
+ isDracoGeometry: false
40
+ });
28
41
  }
29
42
 
30
- export function normalizeTileNonUrlData(tile) {
31
- const box = tile.obb
32
- ? [
33
- ...Ellipsoid.WGS84.cartographicToCartesian(tile.obb.center), // cartesian center of box
34
- ...tile.obb.halfSize, // halfSize
35
- ...tile.obb.quaternion // quaternion
36
- ]
37
- : undefined;
38
- let sphere;
43
+ export function normalizeTileNonUrlData(tile : I3SMinimalNodeData): I3STileHeader {
44
+ const boundingVolume: {box?: number[]; sphere?: number[]} = {};
45
+ let mbs: Mbs = [0, 0, 0, 1];
39
46
  if (tile.mbs) {
40
- sphere = [
47
+ mbs = tile.mbs;
48
+ boundingVolume.sphere = [
41
49
  ...Ellipsoid.WGS84.cartographicToCartesian(tile.mbs.slice(0, 3)), // cartesian center of sphere
42
50
  tile.mbs[3] // radius of sphere
51
+ ] as Mbs;
52
+ } else if (tile.obb) {
53
+ boundingVolume.box = [
54
+ ...Ellipsoid.WGS84.cartographicToCartesian(tile.obb.center), // cartesian center of box
55
+ ...tile.obb.halfSize, // halfSize
56
+ ...tile.obb.quaternion // quaternion
43
57
  ];
44
- } else if (box) {
45
58
  const obb = new OrientedBoundingBox().fromCenterHalfSizeQuaternion(
46
- box.slice(0, 3),
59
+ boundingVolume.box.slice(0, 3),
47
60
  tile.obb.halfSize,
48
61
  tile.obb.quaternion
49
62
  );
50
63
  const boundingSphere = obb.getBoundingSphere();
51
- sphere = [...boundingSphere.center, boundingSphere.radius];
52
- tile.mbs = [...tile.obb.center, boundingSphere.radius];
64
+ boundingVolume.sphere = [...boundingSphere.center , boundingSphere.radius] as Mbs;
65
+ mbs = [...tile.obb.center, boundingSphere.radius] as Mbs;
53
66
  }
54
67
 
55
- tile.boundingVolume = {
56
- sphere,
57
- box
58
- };
59
- tile.lodMetricType = tile.lodSelection[0].metricType;
60
- tile.lodMetricValue = tile.lodSelection[0].maxError;
61
- tile.transformMatrix = tile.transform;
62
- tile.type = TILE_TYPE.MESH;
63
- // TODO only support replacement for now
64
- tile.refine = TILE_REFINEMENT.REPLACE;
68
+ const lodMetricType = tile.lodSelection?.[0].metricType;
69
+ const lodMetricValue = tile.lodSelection?.[0].maxError;
70
+ const transformMatrix = tile.transform;
71
+ const type = TILE_TYPE.MESH;
72
+ /**
73
+ * I3S specification supports only REPLACE
74
+ */
75
+ const refine = TILE_REFINEMENT.REPLACE;
65
76
 
66
- return tile;
77
+ return {...tile, mbs, boundingVolume, lodMetricType, lodMetricValue, transformMatrix, type, refine};
67
78
  }
68
79
 
69
- export async function normalizeTilesetData(tileset, options, context) {
80
+ export async function normalizeTilesetData(tileset : I3STilesetHeader, options : LoaderOptions, context: LoaderContext) {
70
81
  tileset.url = context.url;
71
82
 
72
83
  if (tileset.nodePages) {