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

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 (169) hide show
  1. package/dist/arcgis-webscene-loader.d.ts +7 -0
  2. package/dist/arcgis-webscene-loader.d.ts.map +1 -0
  3. package/dist/arcgis-webscene-loader.js +28 -0
  4. package/dist/bundle.js +2 -2
  5. package/dist/dist.min.js +8804 -8547
  6. package/dist/es5/arcgis-webscene-loader.js +40 -0
  7. package/dist/es5/arcgis-webscene-loader.js.map +1 -0
  8. package/dist/es5/bundle.js +6 -0
  9. package/dist/es5/bundle.js.map +1 -0
  10. package/dist/es5/i3s-attribute-loader.js +195 -0
  11. package/dist/es5/i3s-attribute-loader.js.map +1 -0
  12. package/dist/es5/i3s-building-scene-layer-loader.js +46 -0
  13. package/dist/es5/i3s-building-scene-layer-loader.js.map +1 -0
  14. package/dist/es5/i3s-content-loader.js +57 -0
  15. package/dist/es5/i3s-content-loader.js.map +1 -0
  16. package/dist/es5/i3s-loader.js +193 -0
  17. package/dist/es5/i3s-loader.js.map +1 -0
  18. package/dist/es5/i3s-node-page-loader.js +39 -0
  19. package/dist/es5/i3s-node-page-loader.js.map +1 -0
  20. package/dist/es5/index.js +61 -0
  21. package/dist/es5/index.js.map +1 -0
  22. package/dist/es5/lib/helpers/i3s-nodepages-tiles.js +312 -0
  23. package/dist/es5/lib/helpers/i3s-nodepages-tiles.js.map +1 -0
  24. package/dist/es5/lib/parsers/constants.js +72 -0
  25. package/dist/es5/lib/parsers/constants.js.map +1 -0
  26. package/dist/es5/lib/parsers/parse-arcgis-webscene.js +158 -0
  27. package/dist/es5/lib/parsers/parse-arcgis-webscene.js.map +1 -0
  28. package/dist/es5/lib/parsers/parse-i3s-attribute.js +76 -0
  29. package/dist/es5/lib/parsers/parse-i3s-attribute.js.map +1 -0
  30. package/dist/es5/lib/parsers/parse-i3s-building-scene-layer.js +65 -0
  31. package/dist/es5/lib/parsers/parse-i3s-building-scene-layer.js.map +1 -0
  32. package/dist/es5/lib/parsers/parse-i3s-tile-content.js +510 -0
  33. package/dist/es5/lib/parsers/parse-i3s-tile-content.js.map +1 -0
  34. package/dist/es5/lib/parsers/parse-i3s.js +116 -0
  35. package/dist/es5/lib/parsers/parse-i3s.js.map +1 -0
  36. package/dist/es5/lib/utils/convert-i3s-obb-to-mbs.js +17 -0
  37. package/dist/es5/lib/utils/convert-i3s-obb-to-mbs.js.map +1 -0
  38. package/dist/es5/lib/utils/customizeColors.js +159 -0
  39. package/dist/es5/lib/utils/customizeColors.js.map +1 -0
  40. package/dist/es5/lib/utils/url-utils.js +33 -0
  41. package/dist/es5/lib/utils/url-utils.js.map +1 -0
  42. package/dist/es5/types.js +26 -0
  43. package/dist/es5/types.js.map +1 -0
  44. package/dist/es5/workers/i3s-content-nodejs-worker.js +7 -0
  45. package/dist/es5/workers/i3s-content-nodejs-worker.js.map +1 -0
  46. package/dist/es5/workers/i3s-content-worker.js +6 -0
  47. package/dist/es5/workers/i3s-content-worker.js.map +1 -0
  48. package/dist/esm/arcgis-webscene-loader.js +16 -0
  49. package/dist/esm/arcgis-webscene-loader.js.map +1 -0
  50. package/dist/esm/bundle.js +4 -0
  51. package/dist/esm/bundle.js.map +1 -0
  52. package/dist/esm/i3s-attribute-loader.js +119 -0
  53. package/dist/esm/i3s-attribute-loader.js.map +1 -0
  54. package/dist/esm/i3s-building-scene-layer-loader.js +19 -0
  55. package/dist/esm/i3s-building-scene-layer-loader.js.map +1 -0
  56. package/dist/esm/i3s-content-loader.js +31 -0
  57. package/dist/esm/i3s-content-loader.js.map +1 -0
  58. package/dist/esm/i3s-loader.js +87 -0
  59. package/dist/esm/i3s-loader.js.map +1 -0
  60. package/dist/esm/i3s-node-page-loader.js +15 -0
  61. package/dist/esm/i3s-node-page-loader.js.map +1 -0
  62. package/dist/esm/index.js +8 -0
  63. package/dist/esm/index.js.map +1 -0
  64. package/dist/esm/lib/helpers/i3s-nodepages-tiles.js +198 -0
  65. package/dist/esm/lib/helpers/i3s-nodepages-tiles.js.map +1 -0
  66. package/dist/esm/lib/parsers/constants.js +57 -0
  67. package/dist/esm/lib/parsers/constants.js.map +1 -0
  68. package/dist/esm/lib/parsers/parse-arcgis-webscene.js +70 -0
  69. package/dist/esm/lib/parsers/parse-arcgis-webscene.js.map +1 -0
  70. package/dist/esm/lib/parsers/parse-i3s-attribute.js +60 -0
  71. package/dist/esm/lib/parsers/parse-i3s-attribute.js.map +1 -0
  72. package/dist/esm/lib/parsers/parse-i3s-building-scene-layer.js +39 -0
  73. package/dist/esm/lib/parsers/parse-i3s-building-scene-layer.js.map +1 -0
  74. package/dist/esm/lib/parsers/parse-i3s-tile-content.js +435 -0
  75. package/dist/esm/lib/parsers/parse-i3s-tile-content.js.map +1 -0
  76. package/dist/esm/lib/parsers/parse-i3s.js +83 -0
  77. package/dist/esm/lib/parsers/parse-i3s.js.map +1 -0
  78. package/dist/esm/lib/utils/convert-i3s-obb-to-mbs.js +9 -0
  79. package/dist/esm/lib/utils/convert-i3s-obb-to-mbs.js.map +1 -0
  80. package/dist/esm/lib/utils/customizeColors.js +92 -0
  81. package/dist/esm/lib/utils/customizeColors.js.map +1 -0
  82. package/dist/esm/lib/utils/url-utils.js +28 -0
  83. package/dist/esm/lib/utils/url-utils.js.map +1 -0
  84. package/dist/esm/types.js +18 -0
  85. package/dist/esm/types.js.map +1 -0
  86. package/dist/esm/workers/i3s-content-nodejs-worker.js +5 -0
  87. package/dist/esm/workers/i3s-content-nodejs-worker.js.map +1 -0
  88. package/dist/esm/workers/i3s-content-worker.js +4 -0
  89. package/dist/esm/workers/i3s-content-worker.js.map +1 -0
  90. package/dist/i3s-attribute-loader.d.ts +9 -2
  91. package/dist/i3s-attribute-loader.d.ts.map +1 -1
  92. package/dist/i3s-attribute-loader.js +155 -132
  93. package/dist/i3s-building-scene-layer-loader.js +23 -18
  94. package/dist/i3s-content-loader.d.ts.map +1 -1
  95. package/dist/i3s-content-loader.js +30 -22
  96. package/dist/i3s-content-nodejs-worker.js +198 -0
  97. package/dist/i3s-content-nodejs-worker.js.map +7 -0
  98. package/dist/i3s-content-worker.js +1119 -1100
  99. package/dist/i3s-loader.d.ts +5 -0
  100. package/dist/i3s-loader.d.ts.map +1 -1
  101. package/dist/i3s-loader.js +88 -83
  102. package/dist/i3s-node-page-loader.d.ts +3 -2
  103. package/dist/i3s-node-page-loader.d.ts.map +1 -1
  104. package/dist/i3s-node-page-loader.js +20 -20
  105. package/dist/index.d.ts +4 -2
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +19 -6
  108. package/dist/lib/helpers/i3s-nodepages-tiles.d.ts +1 -0
  109. package/dist/lib/helpers/i3s-nodepages-tiles.d.ts.map +1 -1
  110. package/dist/lib/helpers/i3s-nodepages-tiles.js +231 -228
  111. package/dist/lib/parsers/constants.js +85 -63
  112. package/dist/lib/parsers/parse-arcgis-webscene.d.ts +7 -0
  113. package/dist/lib/parsers/parse-arcgis-webscene.d.ts.map +1 -0
  114. package/dist/lib/parsers/parse-arcgis-webscene.js +88 -0
  115. package/dist/lib/parsers/parse-i3s-attribute.d.ts +6 -4
  116. package/dist/lib/parsers/parse-i3s-attribute.d.ts.map +1 -1
  117. package/dist/lib/parsers/parse-i3s-attribute.js +87 -62
  118. package/dist/lib/parsers/parse-i3s-building-scene-layer.js +41 -40
  119. package/dist/lib/parsers/parse-i3s-tile-content.d.ts +2 -2
  120. package/dist/lib/parsers/parse-i3s-tile-content.d.ts.map +1 -1
  121. package/dist/lib/parsers/parse-i3s-tile-content.js +454 -454
  122. package/dist/lib/parsers/parse-i3s.d.ts +1 -1
  123. package/dist/lib/parsers/parse-i3s.d.ts.map +1 -1
  124. package/dist/lib/parsers/parse-i3s.js +92 -88
  125. package/dist/lib/utils/convert-i3s-obb-to-mbs.js +19 -8
  126. package/dist/lib/utils/customizeColors.d.ts +14 -0
  127. package/dist/lib/utils/customizeColors.d.ts.map +1 -0
  128. package/dist/lib/utils/customizeColors.js +94 -0
  129. package/dist/lib/utils/url-utils.js +41 -28
  130. package/dist/types.d.ts +518 -97
  131. package/dist/types.d.ts.map +1 -1
  132. package/dist/types.js +18 -19
  133. package/dist/workers/i3s-content-nodejs-worker.d.ts +2 -0
  134. package/dist/workers/i3s-content-nodejs-worker.d.ts.map +1 -0
  135. package/dist/workers/i3s-content-nodejs-worker.js +6 -0
  136. package/dist/workers/i3s-content-worker.js +5 -4
  137. package/package.json +15 -15
  138. package/src/arcgis-webscene-loader.ts +31 -0
  139. package/src/i3s-attribute-loader.ts +9 -9
  140. package/src/i3s-content-loader.ts +19 -6
  141. package/src/i3s-loader.ts +22 -13
  142. package/src/i3s-node-page-loader.ts +6 -10
  143. package/src/index.ts +22 -5
  144. package/src/lib/helpers/i3s-nodepages-tiles.ts +14 -3
  145. package/src/lib/parsers/constants.ts +1 -1
  146. package/src/lib/parsers/parse-arcgis-webscene.ts +102 -0
  147. package/src/lib/parsers/parse-i3s-attribute.ts +21 -14
  148. package/src/lib/parsers/parse-i3s-tile-content.ts +99 -76
  149. package/src/lib/parsers/parse-i3s.ts +8 -3
  150. package/src/lib/utils/customizeColors.ts +134 -0
  151. package/src/types.ts +482 -33
  152. package/src/workers/i3s-content-nodejs-worker.ts +5 -0
  153. package/dist/bundle.js.map +0 -1
  154. package/dist/i3s-attribute-loader.js.map +0 -1
  155. package/dist/i3s-building-scene-layer-loader.js.map +0 -1
  156. package/dist/i3s-content-loader.js.map +0 -1
  157. package/dist/i3s-loader.js.map +0 -1
  158. package/dist/i3s-node-page-loader.js.map +0 -1
  159. package/dist/index.js.map +0 -1
  160. package/dist/lib/helpers/i3s-nodepages-tiles.js.map +0 -1
  161. package/dist/lib/parsers/constants.js.map +0 -1
  162. package/dist/lib/parsers/parse-i3s-attribute.js.map +0 -1
  163. package/dist/lib/parsers/parse-i3s-building-scene-layer.js.map +0 -1
  164. package/dist/lib/parsers/parse-i3s-tile-content.js.map +0 -1
  165. package/dist/lib/parsers/parse-i3s.js.map +0 -1
  166. package/dist/lib/utils/convert-i3s-obb-to-mbs.js.map +0 -1
  167. package/dist/lib/utils/url-utils.js.map +0 -1
  168. package/dist/types.js.map +0 -1
  169. package/dist/workers/i3s-content-worker.js.map +0 -1
@@ -0,0 +1,102 @@
1
+ import {JSONLoader, load} from '@loaders.gl/core';
2
+ import type {ArcGisWebSceneData, OperationalLayer} from '../../types';
3
+
4
+ /**
5
+ * WKID, or Well-Known ID, of the CRS. Specify either WKID or WKT of the CRS.
6
+ * Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.8/spatialReference.cmn.md
7
+ */
8
+ const SUPPORTED_WKID = 4326;
9
+
10
+ const ARCGIS_SCENE_SERVER_LAYER_TYPE = 'ArcGISSceneServiceLayer';
11
+ const BUILDING_SCENE_LAYER = 'BuildingSceneLayer';
12
+ const INTEGRATED_MESH_LAYER = 'IntegratedMeshLayer';
13
+ const GROUP_LAYER = 'GroupLayer';
14
+
15
+ /**
16
+ * Supported layers list
17
+ * Possible operational layers in WebScene: https://developers.arcgis.com/web-scene-specification/objects/operationalLayers/
18
+ */
19
+ const SUPPORTED_LAYERS_TYPES = [
20
+ ARCGIS_SCENE_SERVER_LAYER_TYPE,
21
+ INTEGRATED_MESH_LAYER,
22
+ BUILDING_SCENE_LAYER,
23
+ GROUP_LAYER
24
+ ];
25
+
26
+ const NO_AVAILABLE_SUPPORTED_LAYERS_ERROR = 'NO_AVAILABLE_SUPPORTED_LAYERS_ERROR';
27
+ const NOT_SUPPORTED_CRS_ERROR = 'NOT_SUPPORTED_CRS_ERROR';
28
+
29
+ /**
30
+ * Parses ArcGIS WebScene
31
+ * @param data
32
+ */
33
+ export async function parseWebscene(data: ArrayBuffer): Promise<ArcGisWebSceneData> {
34
+ const layer0 = JSON.parse(new TextDecoder().decode(data));
35
+ const {operationalLayers} = layer0;
36
+ const {layers, unsupportedLayers} = await parseOperationalLayers(operationalLayers, true);
37
+
38
+ if (!layers.length) {
39
+ throw new Error(NO_AVAILABLE_SUPPORTED_LAYERS_ERROR);
40
+ }
41
+
42
+ return {
43
+ header: layer0,
44
+ layers,
45
+ unsupportedLayers
46
+ };
47
+ }
48
+
49
+ /**
50
+ * Recursively parses WebScene operational layers.
51
+ * @param layersList
52
+ */
53
+ async function parseOperationalLayers(
54
+ layersList: OperationalLayer[],
55
+ needToCheckCRS: boolean
56
+ ): Promise<{layers: OperationalLayer[]; unsupportedLayers: OperationalLayer[]}> {
57
+ const layers: OperationalLayer[] = [];
58
+ let unsupportedLayers: OperationalLayer[] = [];
59
+
60
+ for (let index = 0; index < layersList.length; index++) {
61
+ const layer = layersList[index];
62
+ const isLayerSupported = SUPPORTED_LAYERS_TYPES.includes(layer.layerType);
63
+
64
+ if (isLayerSupported) {
65
+ if (needToCheckCRS && layer.layerType !== GROUP_LAYER) {
66
+ await checkSupportedIndexCRS(layer);
67
+ needToCheckCRS = false;
68
+ }
69
+
70
+ layers.push(layer);
71
+ } else {
72
+ unsupportedLayers.push(layer);
73
+ }
74
+
75
+ if (layer.layers?.length) {
76
+ const {layers: childLayers, unsupportedLayers: childUnsupportedLayers} =
77
+ await parseOperationalLayers(layer.layers, needToCheckCRS);
78
+ layer.layers = childLayers;
79
+ unsupportedLayers = [...unsupportedLayers, ...childUnsupportedLayers];
80
+ }
81
+ }
82
+
83
+ return {layers, unsupportedLayers};
84
+ }
85
+
86
+ /**
87
+ * Check if layer has supported CRS
88
+ * @param layer
89
+ */
90
+ async function checkSupportedIndexCRS(layer: OperationalLayer) {
91
+ try {
92
+ const layerJson = await load(layer.url, JSONLoader);
93
+ // @ts-expect-error
94
+ const wkid = layerJson?.spatialReference?.wkid;
95
+
96
+ if (wkid !== SUPPORTED_WKID) {
97
+ throw new Error(NOT_SUPPORTED_CRS_ERROR);
98
+ }
99
+ } catch (error) {
100
+ throw error;
101
+ }
102
+ }
@@ -1,3 +1,7 @@
1
+ // loaders.gl, MIT license
2
+
3
+ import {TypedArray} from '@loaders.gl/schema';
4
+
1
5
  import {
2
6
  STRING_ATTRIBUTE_TYPE,
3
7
  OBJECT_ID_ATTRIBUTE_TYPE,
@@ -5,13 +9,16 @@ import {
5
9
  INT_16_ATTRIBUTE_TYPE
6
10
  } from './constants';
7
11
 
12
+ type Attribute = string[] | TypedArray | null;
13
+ export type I3STileAttributes = Record<string, Attribute>;
14
+
8
15
  /**
9
16
  * Get particular tile and creates attribute object inside.
10
- * @param {ArrayBuffer} arrayBuffer
17
+ * @param arrayBuffer
11
18
  * @param {Object} options
12
19
  * @returns {Promise<object>}
13
20
  */
14
- export async function parseI3STileAttribute(arrayBuffer, options) {
21
+ export function parseI3STileAttribute(arrayBuffer: ArrayBuffer, options): I3STileAttributes {
15
22
  const {attributeName, attributeType} = options;
16
23
 
17
24
  if (!attributeName) {
@@ -25,10 +32,10 @@ export async function parseI3STileAttribute(arrayBuffer, options) {
25
32
  /**
26
33
  * Parse attributes based on attribute type.
27
34
  * @param {String} attributeType
28
- * @param {ArrayBuffer} arrayBuffer
29
- * @returns {any}
35
+ * @param arrayBuffer
36
+ * @returns
30
37
  */
31
- function parseAttribute(attributeType, arrayBuffer) {
38
+ function parseAttribute(attributeType, arrayBuffer: ArrayBuffer): Attribute {
32
39
  switch (attributeType) {
33
40
  case STRING_ATTRIBUTE_TYPE:
34
41
  return parseStringsAttribute(arrayBuffer);
@@ -46,10 +53,10 @@ function parseAttribute(attributeType, arrayBuffer) {
46
53
  /**
47
54
  * Parse short number attribute.
48
55
  * Short Integer spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
49
- * @param {ArrayBuffer} arrayBuffer
50
- * @returns {Uint32Array}
56
+ * @param arrayBuffer
57
+ * @returns
51
58
  */
52
- function parseShortNumberAttribute(arrayBuffer) {
59
+ function parseShortNumberAttribute(arrayBuffer: ArrayBuffer): Uint32Array {
53
60
  const countOffset = 4;
54
61
  return new Uint32Array(arrayBuffer, countOffset);
55
62
  }
@@ -57,10 +64,10 @@ function parseShortNumberAttribute(arrayBuffer) {
57
64
  /**
58
65
  * Parse Int16 short number attribute.
59
66
  * Parsing of such data is not documented. Added to handle Building Scene Layer Tileset attributes data.
60
- * @param {ArrayBuffer} arrayBuffer
61
- * @returns {Int16Array}
67
+ * @param arrayBuffer
68
+ * @returns
62
69
  */
63
- function parseInt16ShortNumberAttribute(arrayBuffer) {
70
+ function parseInt16ShortNumberAttribute(arrayBuffer: ArrayBuffer): Int16Array {
64
71
  const countOffset = 4;
65
72
  return new Int16Array(arrayBuffer, countOffset);
66
73
  }
@@ -68,10 +75,10 @@ function parseInt16ShortNumberAttribute(arrayBuffer) {
68
75
  /**
69
76
  * Parse float attribute.
70
77
  * Double Spec - https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
71
- * @param {ArrayBuffer} arrayBuffer
72
- * @returns {Float64Array}
78
+ * @param arrayBuffer
79
+ * @returns
73
80
  */
74
- function parseFloatAttribute(arrayBuffer) {
81
+ function parseFloatAttribute(arrayBuffer: ArrayBuffer): Float64Array {
75
82
  const countOffset = 8;
76
83
  return new Float64Array(arrayBuffer, countOffset);
77
84
  }
@@ -8,19 +8,22 @@ import {DracoLoader, DracoMesh} from '@loaders.gl/draco';
8
8
  import {BasisLoader, CompressedTextureLoader} from '@loaders.gl/textures';
9
9
 
10
10
  import {
11
- I3STilesetHeader,
12
- I3STileHeader,
13
11
  FeatureAttribute,
14
12
  VertexAttribute,
15
13
  I3SMeshAttributes,
16
14
  I3SMeshAttribute,
17
15
  TileContentTexture,
18
16
  HeaderAttributeProperty,
19
- I3SMaterialDefinition
17
+ I3SMaterialDefinition,
18
+ I3STileContent,
19
+ I3STileOptions,
20
+ I3STilesetOptions
20
21
  } from '../../types';
21
22
  import {getUrlWithToken} from '../utils/url-utils';
22
23
 
23
24
  import {GL_TYPE_MAP, getConstructorForDataFormat, sizeOf, COORDINATE_SYSTEM} from './constants';
25
+ import {I3SLoaderOptions} from '../../i3s-loader';
26
+ import {customizeColors} from '../utils/customizeColors';
24
27
 
25
28
  const scratchVector = new Vector3([0, 0, 0]);
26
29
 
@@ -42,80 +45,90 @@ const I3S_ATTRIBUTE_TYPE = 'i3s-attribute-type';
42
45
 
43
46
  export async function parseI3STileContent(
44
47
  arrayBuffer: ArrayBuffer,
45
- tile: I3STileHeader,
46
- tileset: I3STilesetHeader,
48
+ tileOptions: I3STileOptions,
49
+ tilesetOptions: I3STilesetOptions,
47
50
  options?: LoaderOptions,
48
51
  context?: LoaderContext
49
- ) {
50
- tile.content = tile.content || {};
51
- tile.content.featureIds = tile.content.featureIds || null;
52
-
53
- tile.content.attributes = {};
52
+ ): Promise<I3STileContent> {
53
+ const content: I3STileContent = {
54
+ attributes: {},
55
+ indices: null,
56
+ featureIds: [],
57
+ vertexCount: 0,
58
+ modelMatrix: new Matrix4(),
59
+ coordinateSystem: 0,
60
+ byteLength: 0,
61
+ texture: null
62
+ };
54
63
 
55
- if (tile.textureUrl) {
56
- const url = getUrlWithToken(tile.textureUrl, options?.i3s?.token);
57
- const loader = getLoaderForTextureFormat(tile.textureFormat);
58
- const response = await fetch(url);
64
+ if (tileOptions.textureUrl) {
65
+ // @ts-expect-error options is not properly typed
66
+ const url = getUrlWithToken(tileOptions.textureUrl, options?.i3s?.token);
67
+ const loader = getLoaderForTextureFormat(tileOptions.textureFormat);
68
+ const response = await fetch(url, options?.fetch as RequestInit);
59
69
  const arrayBuffer = await response.arrayBuffer();
60
70
 
71
+ // @ts-expect-error options is not properly typed
61
72
  if (options?.i3s.decodeTextures) {
62
73
  if (loader === ImageLoader) {
63
- const options = {...tile.textureLoaderOptions, image: {type: 'data'}};
74
+ const options = {...tileOptions.textureLoaderOptions, image: {type: 'data'}};
64
75
  try {
65
76
  // @ts-ignore context must be defined
66
77
  // Image constructor is not supported in worker thread.
67
78
  // Do parsing image data on the main thread by using context to avoid worker issues.
68
- tile.content.texture = await context.parse(arrayBuffer, options);
79
+ content.texture = await context.parse(arrayBuffer, options);
69
80
  } catch (e) {
70
81
  // context object is different between worker and node.js conversion script.
71
82
  // 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);
83
+ // @ts-expect-error
84
+ content.texture = await parse(arrayBuffer, loader, options);
73
85
  }
74
86
  } else if (loader === CompressedTextureLoader || loader === BasisLoader) {
75
- let texture = await load(arrayBuffer, loader, tile.textureLoaderOptions);
87
+ let texture = await load(arrayBuffer, loader, tileOptions.textureLoaderOptions);
76
88
  if (loader === BasisLoader) {
89
+ // @ts-expect-error
77
90
  texture = texture[0];
78
91
  }
79
- tile.content.texture = {
92
+ content.texture = {
80
93
  compressed: true,
81
94
  mipmaps: false,
95
+ // @ts-expect-error
82
96
  width: texture[0].width,
97
+ // @ts-expect-error
83
98
  height: texture[0].height,
99
+ // @ts-expect-error
84
100
  data: texture
85
101
  };
86
102
  }
87
103
  } else {
88
- tile.content.texture = arrayBuffer;
104
+ content.texture = arrayBuffer;
89
105
  }
90
106
  }
91
107
 
92
- tile.content.material = makePbrMaterial(tile.materialDefinition, tile.content.texture);
93
- if (tile.content.material) {
94
- tile.content.texture = null;
108
+ content.material = makePbrMaterial(tileOptions.materialDefinition, content.texture);
109
+ if (content.material) {
110
+ content.texture = null;
95
111
  }
96
112
 
97
- return await parseI3SNodeGeometry(arrayBuffer, tile, tileset, options);
113
+ return await parseI3SNodeGeometry(arrayBuffer, content, tileOptions, tilesetOptions, options);
98
114
  }
99
115
 
100
116
  /* eslint-disable max-statements */
101
117
  async function parseI3SNodeGeometry(
102
118
  arrayBuffer: ArrayBuffer,
103
- tile: I3STileHeader,
104
- tileset: I3STilesetHeader,
105
- options?: LoaderOptions
106
- ) {
107
- if (!tile.content) {
108
- return tile;
109
- }
110
-
111
- const content = tile.content;
119
+ content: I3STileContent,
120
+ tileOptions: I3STileOptions,
121
+ tilesetOptions: I3STilesetOptions,
122
+ options?: I3SLoaderOptions
123
+ ): Promise<I3STileContent> {
124
+ const contentByteLength = arrayBuffer.byteLength;
112
125
  let attributes: I3SMeshAttributes;
113
126
  let vertexCount: number;
114
127
  let byteOffset: number = 0;
115
128
  let featureCount: number = 0;
116
129
  let indices: TypedArray | undefined;
117
130
 
118
- if (tile.isDracoGeometry) {
131
+ if (tileOptions.isDracoGeometry) {
119
132
  const decompressedGeometry: DracoMesh = await parse(arrayBuffer, DracoLoader, {
120
133
  draco: {
121
134
  attributeNameEntry: I3S_ATTRIBUTE_TYPE
@@ -155,9 +168,9 @@ async function parseI3SNodeGeometry(
155
168
  ordering: attributesOrder,
156
169
  featureAttributes,
157
170
  featureAttributeOrder
158
- } = tileset.store.defaultGeometrySchema;
171
+ } = tilesetOptions.store.defaultGeometrySchema;
159
172
  // First 8 bytes reserved for header (vertexCount and featureCount)
160
- const headers = parseHeaders(tileset, arrayBuffer);
173
+ const headers = parseHeaders(arrayBuffer, tilesetOptions);
161
174
  byteOffset = headers.byteOffset;
162
175
  vertexCount = headers.vertexCount;
163
176
  featureCount = headers.featureCount;
@@ -187,7 +200,7 @@ async function parseI3SNodeGeometry(
187
200
  !options?.i3s?.coordinateSystem ||
188
201
  options.i3s.coordinateSystem === COORDINATE_SYSTEM.METER_OFFSETS
189
202
  ) {
190
- const enuMatrix = parsePositions(attributes.position, tile);
203
+ const enuMatrix = parsePositions(attributes.position, tileOptions);
191
204
  content.modelMatrix = enuMatrix.invert();
192
205
  content.coordinateSystem = COORDINATE_SYSTEM.METER_OFFSETS;
193
206
  } else {
@@ -195,17 +208,25 @@ async function parseI3SNodeGeometry(
195
208
  content.coordinateSystem = COORDINATE_SYSTEM.LNGLAT_OFFSETS;
196
209
  }
197
210
 
211
+ attributes.color = await customizeColors(
212
+ attributes.color,
213
+ attributes.id,
214
+ tileOptions,
215
+ tilesetOptions,
216
+ options
217
+ );
218
+
198
219
  content.attributes = {
199
220
  positions: attributes.position,
200
221
  normals: attributes.normal,
201
222
  colors: normalizeAttribute(attributes.color), // Normalize from UInt8
202
223
  texCoords: attributes.uv0,
203
- uvRegions: normalizeAttribute(attributes.uvRegion) // Normalize from UInt16
224
+ uvRegions: normalizeAttribute(attributes.uvRegion || attributes.region) // Normalize from UInt16
204
225
  };
205
226
  content.indices = indices || null;
206
227
 
207
228
  if (attributes.id && attributes.id.value) {
208
- tile.content.featureIds = attributes.id.value;
229
+ content.featureIds = attributes.id.value;
209
230
  }
210
231
 
211
232
  // Remove undefined attributes
@@ -216,9 +237,9 @@ async function parseI3SNodeGeometry(
216
237
  }
217
238
 
218
239
  content.vertexCount = vertexCount;
219
- content.byteLength = arrayBuffer.byteLength;
240
+ content.byteLength = contentByteLength;
220
241
 
221
- return tile;
242
+ return content;
222
243
  }
223
244
 
224
245
  /**
@@ -273,12 +294,12 @@ function normalizeAttribute(attribute: I3SMeshAttribute): I3SMeshAttribute {
273
294
  return attribute;
274
295
  }
275
296
 
276
- function parseHeaders(tileset: I3STilesetHeader, arrayBuffer: ArrayBuffer) {
297
+ function parseHeaders(arrayBuffer: ArrayBuffer, options: I3STilesetOptions) {
277
298
  let byteOffset = 0;
278
299
  // First 8 bytes reserved for header (vertexCount and featurecount)
279
300
  let vertexCount = 0;
280
301
  let featureCount = 0;
281
- for (const {property, type} of tileset.store.defaultGeometrySchema.header) {
302
+ for (const {property, type} of options.store.defaultGeometrySchema.header) {
282
303
  const TypedArrayTypeHeader = getConstructorForDataFormat(type);
283
304
  switch (property) {
284
305
  case HeaderAttributeProperty.vertexCount:
@@ -307,7 +328,7 @@ function normalizeAttributes(
307
328
  arrayBuffer: ArrayBuffer,
308
329
  byteOffset: number,
309
330
  vertexAttributes: VertexAttribute | FeatureAttribute,
310
- vertexCount: number,
331
+ attributeCount: number,
311
332
  attributesOrder: string[]
312
333
  ) {
313
334
  const attributes: I3SMeshAttributes = {};
@@ -317,44 +338,46 @@ function normalizeAttributes(
317
338
  if (vertexAttributes[attribute]) {
318
339
  const {valueType, valuesPerElement}: {valueType: string; valuesPerElement: number} =
319
340
  vertexAttributes[attribute];
320
- // update count and byteOffset count by calculating from defaultGeometrySchema + binnary content
321
- const count = vertexCount;
322
341
  // protect from arrayBuffer read overunns by NOT assuming node has regions always even though its declared in defaultGeometrySchema.
323
342
  // In i3s 1.6: client is required to decide that based on ./shared resource of the node (materialDefinitions.[Mat_id].params.vertexRegions == true)
324
343
  // In i3s 1.7 the property has been rolled into the 3d scene layer json/node pages.
325
344
  // Code below does not account when the bytelength is actually bigger than
326
345
  // the calculated value (b\c the tile potentially could have mesh segmentation information).
327
346
  // In those cases tiles without regions could fail or have garbage values.
328
- if (byteOffset + count * valuesPerElement > arrayBuffer.byteLength) {
329
- break;
330
- }
331
- const buffer = arrayBuffer.slice(byteOffset);
332
- let value: TypedArray;
333
-
334
- if (valueType === 'UInt64') {
335
- value = parseUint64Values(buffer, count * valuesPerElement, sizeOf(valueType));
336
- } else {
337
- const TypedArrayType = getConstructorForDataFormat(valueType);
338
- value = new TypedArrayType(buffer, 0, count * valuesPerElement);
339
- }
347
+ if (
348
+ byteOffset + attributeCount * valuesPerElement * sizeOf(valueType) <=
349
+ arrayBuffer.byteLength
350
+ ) {
351
+ const buffer = arrayBuffer.slice(byteOffset);
352
+ let value: TypedArray;
353
+
354
+ if (valueType === 'UInt64') {
355
+ value = parseUint64Values(buffer, attributeCount * valuesPerElement, sizeOf(valueType));
356
+ } else {
357
+ const TypedArrayType = getConstructorForDataFormat(valueType);
358
+ value = new TypedArrayType(buffer, 0, attributeCount * valuesPerElement);
359
+ }
340
360
 
341
- attributes[attribute] = {
342
- value,
343
- type: GL_TYPE_MAP[valueType],
344
- size: valuesPerElement
345
- };
346
-
347
- switch (attribute) {
348
- case 'color':
349
- attributes.color.normalized = true;
350
- break;
351
- case 'position':
352
- case 'region':
353
- case 'normal':
354
- default:
355
- }
361
+ attributes[attribute] = {
362
+ value,
363
+ type: GL_TYPE_MAP[valueType],
364
+ size: valuesPerElement
365
+ };
356
366
 
357
- byteOffset = byteOffset + count * valuesPerElement * sizeOf(valueType);
367
+ switch (attribute) {
368
+ case 'color':
369
+ attributes.color.normalized = true;
370
+ break;
371
+ case 'position':
372
+ case 'region':
373
+ case 'normal':
374
+ default:
375
+ }
376
+
377
+ byteOffset = byteOffset + attributeCount * valuesPerElement * sizeOf(valueType);
378
+ } else if (attribute !== 'uv0') {
379
+ break;
380
+ }
358
381
  }
359
382
  }
360
383
 
@@ -391,8 +414,8 @@ function parseUint64Values(
391
414
  return new Uint32Array(values);
392
415
  }
393
416
 
394
- function parsePositions(attribute: I3SMeshAttribute, tile: I3STileHeader): Matrix4 {
395
- const mbs = tile.mbs;
417
+ function parsePositions(attribute: I3SMeshAttribute, options: I3STileOptions): Matrix4 {
418
+ const mbs = options.mbs;
396
419
  const value = attribute.value;
397
420
  const metadata = attribute.metadata;
398
421
  const enuMatrix = new Matrix4();
@@ -13,7 +13,7 @@ import {
13
13
  } from '../../types';
14
14
  import type {LoaderOptions, LoaderContext} from '@loaders.gl/loader-utils';
15
15
 
16
- export function normalizeTileData(tile : Node3DIndexDocument, options : LoaderOptions, context: LoaderContext): I3STileHeader {
16
+ export function normalizeTileData(tile : Node3DIndexDocument, context: LoaderContext): I3STileHeader {
17
17
  const url: string = context.url || '';
18
18
  let contentUrl: string | undefined;
19
19
  if (tile.geometryData) {
@@ -82,12 +82,17 @@ export async function normalizeTilesetData(tileset : I3STilesetHeader, options :
82
82
 
83
83
  if (tileset.nodePages) {
84
84
  tileset.nodePagesTile = new I3SNodePagesTiles(tileset, options);
85
- tileset.root = await tileset.nodePagesTile.formTileFromNodePages(0);
85
+ tileset.root = tileset.nodePagesTile.formTileFromNodePages(0);
86
86
  } else {
87
+ // @ts-expect-error options is not properly typed
87
88
  const rootNodeUrl = getUrlWithToken(`${tileset.url}/nodes/root`, options.i3s?.token);
88
89
  // eslint-disable-next-line no-use-before-define
89
90
  tileset.root = await load(rootNodeUrl, tileset.loader, {
90
- i3s: {loadContent: false, isTileHeader: true, isTileset: false}
91
+ ...options,
92
+ i3s: {
93
+ // @ts-expect-error options is not properly typed
94
+ ...options.i3s,
95
+ loadContent: false, isTileHeader: true, isTileset: false}
91
96
  });
92
97
  }
93
98
 
@@ -0,0 +1,134 @@
1
+ import type {MeshAttribute} from '@loaders.gl/schema';
2
+ import type {COLOR, I3STileOptions, I3STilesetOptions} from '../../types';
3
+
4
+ import {load} from '@loaders.gl/core';
5
+ import {getAttributeValueType, I3SAttributeLoader} from '../../i3s-attribute-loader';
6
+ import {I3SLoaderOptions} from '../../i3s-loader';
7
+ import {getUrlWithToken} from '../utils/url-utils';
8
+
9
+ /**
10
+ * Modify vertex colors array to visualize 3D objects in a attribute driven way
11
+ * @param colors - vertex colors attribute
12
+ * @param featureIds - feature Ids attribute
13
+ * @param tileOptions - tile - related options
14
+ * @param tilesetOptions - tileset-related options
15
+ * @param options - loader options
16
+ * @returns midified colors attribute
17
+ */
18
+ export async function customizeColors(
19
+ colors: MeshAttribute,
20
+ featureIds: MeshAttribute,
21
+ tileOptions: I3STileOptions,
22
+ tilesetOptions: I3STilesetOptions,
23
+ options?: I3SLoaderOptions
24
+ ): Promise<MeshAttribute> {
25
+ if (!options?.i3s?.colorsByAttribute) {
26
+ return colors;
27
+ }
28
+
29
+ const colorizeAttributeField = tilesetOptions.fields.find(
30
+ ({name}) => name === options?.i3s?.colorsByAttribute?.attributeName
31
+ );
32
+ if (
33
+ !colorizeAttributeField ||
34
+ !['esriFieldTypeDouble', 'esriFieldTypeInteger', 'esriFieldTypeSmallInteger'].includes(
35
+ colorizeAttributeField.type
36
+ )
37
+ ) {
38
+ return colors;
39
+ }
40
+
41
+ const colorizeAttributeData = await loadFeatureAttributeData(
42
+ colorizeAttributeField.name,
43
+ tileOptions,
44
+ tilesetOptions,
45
+ options
46
+ );
47
+ if (!colorizeAttributeData) {
48
+ return colors;
49
+ }
50
+
51
+ const objectIdField = tilesetOptions.fields.find(({type}) => type === 'esriFieldTypeOID');
52
+ if (!objectIdField) {
53
+ return colors;
54
+ }
55
+
56
+ const objectIdAttributeData = await loadFeatureAttributeData(
57
+ objectIdField.name,
58
+ tileOptions,
59
+ tilesetOptions,
60
+ options
61
+ );
62
+ if (!objectIdAttributeData) {
63
+ return colors;
64
+ }
65
+
66
+ const attributeValuesMap: {[key: number]: COLOR} = {};
67
+ // @ts-expect-error
68
+ for (let i = 0; i < objectIdAttributeData[objectIdField.name].length; i++) {
69
+ // @ts-expect-error
70
+ attributeValuesMap[objectIdAttributeData[objectIdField.name][i]] = calculateColorForAttribute(
71
+ // @ts-expect-error
72
+ colorizeAttributeData[colorizeAttributeField.name][i] as number,
73
+ options
74
+ );
75
+ }
76
+
77
+ for (let i = 0; i < featureIds.value.length; i++) {
78
+ const color = attributeValuesMap[featureIds.value[i]];
79
+ if (!color) {
80
+ continue; // eslint-disable-line no-continue
81
+ }
82
+ colors.value.set(color, i * 4);
83
+ }
84
+
85
+ return colors;
86
+ }
87
+
88
+ /**
89
+ * Calculate rgba color from the attribute value
90
+ * @param attributeValue - value of the attribute
91
+ * @param options - loader options
92
+ * @returns - color array for a specific attribute value
93
+ */
94
+ function calculateColorForAttribute(attributeValue: number, options?: I3SLoaderOptions): COLOR {
95
+ if (!options?.i3s?.colorsByAttribute) {
96
+ return [255, 255, 255, 255];
97
+ }
98
+ const {minValue, maxValue, minColor, maxColor} = options.i3s.colorsByAttribute;
99
+ const rate = (attributeValue - minValue) / (maxValue - minValue);
100
+ const color: COLOR = [255, 255, 255, 255];
101
+ for (let i = 0; i < minColor.length; i++) {
102
+ color[i] = Math.round((maxColor[i] - minColor[i]) * rate + minColor[i]);
103
+ }
104
+ return color;
105
+ }
106
+
107
+ /**
108
+ * Load feature attribute data from the ArcGIS rest service
109
+ * @param attributeName - attribute name
110
+ * @param tileOptions - tile-related options
111
+ * @param tilesetOptions - tileset-related options
112
+ * @param options - loader options
113
+ * @returns - Array-like list of the attribute values
114
+ */
115
+ async function loadFeatureAttributeData(
116
+ attributeName: string,
117
+ {attributeUrls}: I3STileOptions,
118
+ {attributeStorageInfo}: I3STilesetOptions,
119
+ options?: I3SLoaderOptions
120
+ ): Promise<{[key: string]: string[] | Uint32Array | Uint16Array | Float64Array | null} | null> {
121
+ const attributeIndex = attributeStorageInfo.findIndex(({name}) => attributeName === name);
122
+ if (attributeIndex === -1) {
123
+ return null;
124
+ }
125
+ const objectIdAttributeUrl = getUrlWithToken(attributeUrls[attributeIndex], options?.i3s?.token);
126
+ const attributeType = getAttributeValueType(attributeStorageInfo[attributeIndex]);
127
+ const objectIdAttributeData = await load(objectIdAttributeUrl, I3SAttributeLoader, {
128
+ attributeName,
129
+ attributeType
130
+ });
131
+
132
+ // @ts-expect-error TODO action engine
133
+ return objectIdAttributeData;
134
+ }