@loaders.gl/tile-converter 3.1.7 → 3.2.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (224) hide show
  1. package/dist/3d-tiles-attributes-worker.d.ts +28 -0
  2. package/dist/3d-tiles-attributes-worker.d.ts.map +1 -0
  3. package/dist/3d-tiles-attributes-worker.js +4 -0
  4. package/dist/3d-tiles-attributes-worker.js.map +7 -0
  5. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts +82 -0
  6. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts.map +1 -0
  7. package/dist/3d-tiles-converter/3d-tiles-converter.js +268 -0
  8. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts +84 -0
  9. package/dist/3d-tiles-converter/helpers/b3dm-converter.d.ts.map +1 -0
  10. package/dist/3d-tiles-converter/helpers/b3dm-converter.js +278 -0
  11. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.d.ts +13 -0
  12. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.d.ts.map +1 -0
  13. package/dist/3d-tiles-converter/helpers/i3s-obb-to-3d-tiles-obb.js +23 -0
  14. package/dist/3d-tiles-converter/helpers/texture-atlas.d.ts +9 -0
  15. package/dist/3d-tiles-converter/helpers/texture-atlas.d.ts.map +1 -0
  16. package/dist/3d-tiles-converter/helpers/texture-atlas.js +52 -0
  17. package/dist/3d-tiles-converter/json-templates/tileset.d.ts +15 -0
  18. package/dist/3d-tiles-converter/json-templates/tileset.d.ts.map +1 -0
  19. package/dist/3d-tiles-converter/json-templates/tileset.js +43 -0
  20. package/dist/bundle.d.ts +2 -0
  21. package/dist/bundle.d.ts.map +1 -0
  22. package/dist/bundle.js +5 -0
  23. package/dist/converter.min.js +20 -20
  24. package/dist/deps-installer/deps-installer.d.ts +4 -0
  25. package/dist/deps-installer/deps-installer.d.ts.map +1 -0
  26. package/dist/deps-installer/deps-installer.js +21 -0
  27. package/dist/dist.min.js +1082 -1131
  28. package/dist/es5/3d-tiles-attributes-worker.js +29 -0
  29. package/dist/es5/3d-tiles-attributes-worker.js.map +1 -0
  30. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js +104 -44
  31. package/dist/es5/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  32. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js +34 -43
  33. package/dist/es5/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  34. package/dist/es5/i3s-attributes-worker.js +29 -0
  35. package/dist/es5/i3s-attributes-worker.js.map +1 -0
  36. package/dist/es5/i3s-converter/helpers/coordinate-converter.js +19 -11
  37. package/dist/es5/i3s-converter/helpers/coordinate-converter.js.map +1 -1
  38. package/dist/es5/i3s-converter/helpers/geometry-attributes.js +2 -2
  39. package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  40. package/dist/es5/i3s-converter/helpers/geometry-converter.js +267 -178
  41. package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
  42. package/dist/es5/i3s-converter/helpers/gltf-attributes.js +71 -0
  43. package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -0
  44. package/dist/es5/i3s-converter/helpers/node-pages.js +43 -52
  45. package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
  46. package/dist/es5/i3s-converter/i3s-converter.js +264 -219
  47. package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
  48. package/dist/es5/index.js +8 -0
  49. package/dist/es5/index.js.map +1 -1
  50. package/dist/es5/lib/utils/compress-util.js +14 -17
  51. package/dist/es5/lib/utils/compress-util.js.map +1 -1
  52. package/dist/es5/lib/utils/file-utils.js +39 -14
  53. package/dist/es5/lib/utils/file-utils.js.map +1 -1
  54. package/dist/es5/lib/utils/lod-conversion-utils.js.map +1 -1
  55. package/dist/es5/lib/utils/queue.js +61 -0
  56. package/dist/es5/lib/utils/queue.js.map +1 -0
  57. package/dist/es5/lib/utils/statistic-utills.js.map +1 -1
  58. package/dist/es5/lib/utils/write-queue.js +225 -0
  59. package/dist/es5/lib/utils/write-queue.js.map +1 -0
  60. package/dist/es5/pgm-loader.js +1 -1
  61. package/dist/es5/pgm-loader.js.map +1 -1
  62. package/dist/es5/workers/3d-tiles-attributes-worker.js +37 -0
  63. package/dist/es5/workers/3d-tiles-attributes-worker.js.map +1 -0
  64. package/dist/es5/workers/i3s-attributes-worker.js +40 -0
  65. package/dist/es5/workers/i3s-attributes-worker.js.map +1 -0
  66. package/dist/esm/3d-tiles-attributes-worker.js +16 -0
  67. package/dist/esm/3d-tiles-attributes-worker.js.map +1 -0
  68. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js +32 -5
  69. package/dist/esm/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  70. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js +23 -23
  71. package/dist/esm/3d-tiles-converter/helpers/b3dm-converter.js.map +1 -1
  72. package/dist/esm/i3s-attributes-worker.js +16 -0
  73. package/dist/esm/i3s-attributes-worker.js.map +1 -0
  74. package/dist/esm/i3s-converter/helpers/coordinate-converter.js +19 -11
  75. package/dist/esm/i3s-converter/helpers/coordinate-converter.js.map +1 -1
  76. package/dist/esm/i3s-converter/helpers/geometry-attributes.js +2 -2
  77. package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
  78. package/dist/esm/i3s-converter/helpers/geometry-converter.js +117 -58
  79. package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
  80. package/dist/esm/i3s-converter/helpers/gltf-attributes.js +54 -0
  81. package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -0
  82. package/dist/esm/i3s-converter/helpers/node-pages.js +12 -9
  83. package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
  84. package/dist/esm/i3s-converter/i3s-converter.js +115 -28
  85. package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
  86. package/dist/esm/index.js +1 -0
  87. package/dist/esm/index.js.map +1 -1
  88. package/dist/esm/lib/utils/compress-util.js +6 -8
  89. package/dist/esm/lib/utils/compress-util.js.map +1 -1
  90. package/dist/esm/lib/utils/file-utils.js +11 -1
  91. package/dist/esm/lib/utils/file-utils.js.map +1 -1
  92. package/dist/esm/lib/utils/lod-conversion-utils.js.map +1 -1
  93. package/dist/esm/lib/utils/queue.js +19 -0
  94. package/dist/esm/lib/utils/queue.js.map +1 -0
  95. package/dist/esm/lib/utils/statistic-utills.js.map +1 -1
  96. package/dist/esm/lib/utils/write-queue.js +88 -0
  97. package/dist/esm/lib/utils/write-queue.js.map +1 -0
  98. package/dist/esm/pgm-loader.js +1 -1
  99. package/dist/esm/pgm-loader.js.map +1 -1
  100. package/dist/esm/workers/3d-tiles-attributes-worker.js +5 -0
  101. package/dist/esm/workers/3d-tiles-attributes-worker.js.map +1 -0
  102. package/dist/esm/workers/i3s-attributes-worker.js +4 -0
  103. package/dist/esm/workers/i3s-attributes-worker.js.map +1 -0
  104. package/dist/i3s-attributes-worker.d.ts +33 -0
  105. package/dist/i3s-attributes-worker.d.ts.map +1 -0
  106. package/dist/i3s-attributes-worker.js +10 -0
  107. package/dist/i3s-attributes-worker.js.map +7 -0
  108. package/dist/i3s-converter/helpers/coordinate-converter.d.ts +41 -0
  109. package/dist/i3s-converter/helpers/coordinate-converter.d.ts.map +1 -0
  110. package/dist/i3s-converter/helpers/coordinate-converter.js +122 -0
  111. package/dist/i3s-converter/helpers/create-scene-server-path.d.ts +9 -0
  112. package/dist/i3s-converter/helpers/create-scene-server-path.d.ts.map +1 -0
  113. package/dist/i3s-converter/helpers/create-scene-server-path.js +28 -0
  114. package/dist/i3s-converter/helpers/geometry-attributes.d.ts +8 -0
  115. package/dist/i3s-converter/helpers/geometry-attributes.d.ts.map +1 -0
  116. package/dist/i3s-converter/helpers/geometry-attributes.js +177 -0
  117. package/dist/i3s-converter/helpers/geometry-converter.d.ts +29 -0
  118. package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -0
  119. package/dist/i3s-converter/helpers/geometry-converter.js +901 -0
  120. package/dist/i3s-converter/helpers/gltf-attributes.d.ts +9 -0
  121. package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -0
  122. package/dist/i3s-converter/helpers/gltf-attributes.js +56 -0
  123. package/dist/i3s-converter/helpers/node-debug.d.ts +8 -0
  124. package/dist/i3s-converter/helpers/node-debug.d.ts.map +1 -0
  125. package/dist/i3s-converter/helpers/node-debug.js +114 -0
  126. package/dist/i3s-converter/helpers/node-pages.d.ts +117 -0
  127. package/dist/i3s-converter/helpers/node-pages.d.ts.map +1 -0
  128. package/dist/i3s-converter/helpers/node-pages.js +208 -0
  129. package/dist/i3s-converter/i3s-converter.d.ts +325 -0
  130. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -0
  131. package/dist/i3s-converter/i3s-converter.js +1056 -0
  132. package/dist/i3s-converter/json-templates/layers.d.ts +95 -0
  133. package/dist/i3s-converter/json-templates/layers.d.ts.map +1 -0
  134. package/dist/i3s-converter/json-templates/layers.js +199 -0
  135. package/dist/i3s-converter/json-templates/metadata.d.ts +22 -0
  136. package/dist/i3s-converter/json-templates/metadata.d.ts.map +1 -0
  137. package/dist/i3s-converter/json-templates/metadata.js +25 -0
  138. package/dist/i3s-converter/json-templates/node.d.ts +61 -0
  139. package/dist/i3s-converter/json-templates/node.d.ts.map +1 -0
  140. package/dist/i3s-converter/json-templates/node.js +89 -0
  141. package/dist/i3s-converter/json-templates/scene-server.d.ts +28 -0
  142. package/dist/i3s-converter/json-templates/scene-server.d.ts.map +1 -0
  143. package/dist/i3s-converter/json-templates/scene-server.js +31 -0
  144. package/dist/i3s-converter/json-templates/shared-resources.d.ts +14 -0
  145. package/dist/i3s-converter/json-templates/shared-resources.d.ts.map +1 -0
  146. package/dist/i3s-converter/json-templates/shared-resources.js +129 -0
  147. package/dist/i3s-converter/json-templates/store.d.ts +95 -0
  148. package/dist/i3s-converter/json-templates/store.d.ts.map +1 -0
  149. package/dist/i3s-converter/json-templates/store.js +103 -0
  150. package/dist/i3s-converter/types.d.ts +114 -0
  151. package/dist/i3s-converter/types.d.ts.map +1 -0
  152. package/dist/i3s-converter/types.js +2 -0
  153. package/dist/i3s-server/app.d.ts +3 -0
  154. package/dist/i3s-server/app.d.ts.map +1 -0
  155. package/dist/i3s-server/app.js +14 -0
  156. package/dist/i3s-server/controllers/index-controller.d.ts +2 -0
  157. package/dist/i3s-server/controllers/index-controller.d.ts.map +1 -0
  158. package/dist/i3s-server/controllers/index-controller.js +23 -0
  159. package/dist/i3s-server/routes/index.d.ts +3 -0
  160. package/dist/i3s-server/routes/index.d.ts.map +1 -0
  161. package/dist/i3s-server/routes/index.js +16 -0
  162. package/dist/index.d.ts +6 -0
  163. package/dist/index.d.ts.map +1 -0
  164. package/dist/index.js +16 -0
  165. package/dist/lib/utils/compress-util.d.ts +45 -0
  166. package/dist/lib/utils/compress-util.d.ts.map +1 -0
  167. package/dist/lib/utils/compress-util.js +257 -0
  168. package/{src → dist}/lib/utils/file-utils.d.ts +6 -14
  169. package/dist/lib/utils/file-utils.d.ts.map +1 -0
  170. package/dist/lib/utils/file-utils.js +81 -0
  171. package/dist/lib/utils/lod-conversion-utils.d.ts +41 -0
  172. package/dist/lib/utils/lod-conversion-utils.d.ts.map +1 -0
  173. package/dist/lib/utils/lod-conversion-utils.js +76 -0
  174. package/dist/lib/utils/queue.d.ts +7 -0
  175. package/dist/lib/utils/queue.d.ts.map +1 -0
  176. package/dist/lib/utils/queue.js +18 -0
  177. package/dist/lib/utils/statistic-utills.d.ts +3 -0
  178. package/dist/lib/utils/statistic-utills.d.ts.map +1 -0
  179. package/dist/lib/utils/statistic-utills.js +64 -0
  180. package/dist/lib/utils/write-queue.d.ts +22 -0
  181. package/dist/lib/utils/write-queue.d.ts.map +1 -0
  182. package/dist/lib/utils/write-queue.js +62 -0
  183. package/dist/pgm-loader.d.ts +6 -0
  184. package/dist/pgm-loader.d.ts.map +1 -0
  185. package/dist/pgm-loader.js +23 -0
  186. package/dist/workers/3d-tiles-attributes-worker.d.ts +2 -0
  187. package/dist/workers/3d-tiles-attributes-worker.d.ts.map +1 -0
  188. package/dist/workers/3d-tiles-attributes-worker.js +9 -0
  189. package/dist/workers/i3s-attributes-worker.d.ts +2 -0
  190. package/dist/workers/i3s-attributes-worker.d.ts.map +1 -0
  191. package/dist/workers/i3s-attributes-worker.js +5 -0
  192. package/package.json +20 -18
  193. package/src/3d-tiles-attributes-worker.ts +43 -0
  194. package/src/3d-tiles-converter/3d-tiles-converter.ts +48 -5
  195. package/src/3d-tiles-converter/helpers/b3dm-converter.ts +21 -18
  196. package/src/i3s-attributes-worker.ts +46 -0
  197. package/src/i3s-converter/helpers/coordinate-converter.ts +29 -24
  198. package/src/i3s-converter/helpers/geometry-attributes.ts +4 -3
  199. package/src/i3s-converter/helpers/{geometry-converter.js → geometry-converter.ts} +421 -175
  200. package/src/i3s-converter/helpers/gltf-attributes.ts +68 -0
  201. package/src/i3s-converter/helpers/node-pages.ts +25 -17
  202. package/src/i3s-converter/i3s-converter.ts +124 -69
  203. package/src/i3s-converter/types.ts +90 -8
  204. package/src/index.ts +1 -0
  205. package/src/lib/utils/{compress-util.js → compress-util.ts} +105 -18
  206. package/src/lib/utils/file-utils.ts +84 -0
  207. package/src/lib/utils/{lod-conversion-utils.js → lod-conversion-utils.ts} +27 -5
  208. package/src/lib/utils/queue.ts +17 -0
  209. package/src/lib/utils/{statistic-utills.js → statistic-utills.ts} +0 -0
  210. package/src/lib/utils/write-queue.ts +75 -0
  211. package/src/workers/3d-tiles-attributes-worker.ts +6 -0
  212. package/src/workers/i3s-attributes-worker.ts +6 -0
  213. package/dist/es5/i3s-converter/helpers/geometry-converter.d.ts +0 -44
  214. package/dist/es5/lib/utils/compress-util.d.ts +0 -53
  215. package/dist/es5/lib/utils/file-utils.d.ts +0 -43
  216. package/dist/es5/lib/utils/lod-conversion-utils.d.ts +0 -32
  217. package/dist/esm/i3s-converter/helpers/geometry-converter.d.ts +0 -44
  218. package/dist/esm/lib/utils/compress-util.d.ts +0 -53
  219. package/dist/esm/lib/utils/file-utils.d.ts +0 -43
  220. package/dist/esm/lib/utils/lod-conversion-utils.d.ts +0 -32
  221. package/src/i3s-converter/helpers/geometry-converter.d.ts +0 -44
  222. package/src/lib/utils/compress-util.d.ts +0 -53
  223. package/src/lib/utils/file-utils.js +0 -38
  224. package/src/lib/utils/lod-conversion-utils.d.ts +0 -32
@@ -0,0 +1,1056 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const core_1 = require("@loaders.gl/core");
7
+ const tiles_1 = require("@loaders.gl/tiles");
8
+ const _3d_tiles_1 = require("@loaders.gl/3d-tiles");
9
+ const path_1 = require("path");
10
+ const uuid_1 = require("uuid");
11
+ const process_1 = __importDefault(require("process"));
12
+ const json_map_transform_1 = __importDefault(require("json-map-transform"));
13
+ const md5_1 = __importDefault(require("md5"));
14
+ const node_pages_1 = __importDefault(require("./helpers/node-pages"));
15
+ const file_utils_1 = require("../lib/utils/file-utils");
16
+ const compress_util_1 = require("../lib/utils/compress-util");
17
+ const statistic_utills_1 = require("../lib/utils/statistic-utills");
18
+ const geometry_converter_1 = __importDefault(require("./helpers/geometry-converter"));
19
+ const coordinate_converter_1 = require("./helpers/coordinate-converter");
20
+ const create_scene_server_path_1 = require("./helpers/create-scene-server-path");
21
+ const lod_conversion_utils_1 = require("../lib/utils/lod-conversion-utils");
22
+ const pgm_loader_1 = require("../pgm-loader");
23
+ const layers_1 = require("./json-templates/layers");
24
+ const node_1 = require("./json-templates/node");
25
+ const shared_resources_1 = require("./json-templates/shared-resources");
26
+ const node_debug_1 = require("./helpers/node-debug");
27
+ const textures_1 = require("@loaders.gl/textures");
28
+ const images_1 = require("@loaders.gl/images");
29
+ const worker_utils_1 = require("@loaders.gl/worker-utils");
30
+ const draco_1 = require("@loaders.gl/draco");
31
+ const write_queue_1 = __importDefault(require("../lib/utils/write-queue"));
32
+ const i3s_attributes_worker_1 = require("../i3s-attributes-worker");
33
+ const ION_DEFAULT_TOKEN = process_1.default.env.IonToken || // eslint-disable-line
34
+ 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJqdGkiOiJlYWMxMzcyYy0zZjJkLTQwODctODNlNi01MDRkZmMzMjIxOWIiLCJpZCI6OTYyMCwic2NvcGVzIjpbImFzbCIsImFzciIsImdjIl0sImlhdCI6MTU2Mjg2NjI3M30.1FNiClUyk00YH_nWfSGpiQAjR5V2OvREDq1PJ5QMjWQ'; // eslint-disable-line
35
+ const HARDCODED_NODES_PER_PAGE = 64;
36
+ const _3D_TILES = '3DTILES';
37
+ const _3D_OBJECT_LAYER_TYPE = '3DObject';
38
+ const STRING_TYPE = 'string';
39
+ const SHORT_INT_TYPE = 'Int32';
40
+ const DOUBLE_TYPE = 'double';
41
+ const OBJECT_ID_TYPE = 'OBJECTID';
42
+ const REFRESH_TOKEN_TIMEOUT = 1800; // 30 minutes in seconds
43
+ const CESIUM_DATASET_PREFIX = 'https://';
44
+ // const FS_FILE_TOO_LARGE = 'ERR_FS_FILE_TOO_LARGE';
45
+ /**
46
+ * Converter from 3d-tiles tileset to i3s layer
47
+ */
48
+ class I3SConverter {
49
+ constructor() {
50
+ this.boundingVolumeWarnings = [];
51
+ this.conversionStartTime = [0, 0];
52
+ this.refreshTokenTime = [0, 0];
53
+ this.sourceTileset = null;
54
+ this.geoidHeightModel = null;
55
+ this.Loader = _3d_tiles_1.Tiles3DLoader;
56
+ this.workerSource = {};
57
+ this.writeQueue = new write_queue_1.default();
58
+ this.nodePages = new node_pages_1.default(file_utils_1.writeFile, HARDCODED_NODES_PER_PAGE);
59
+ this.options = {};
60
+ this.layers0Path = '';
61
+ this.materialMap = new Map();
62
+ this.materialDefinitions = [];
63
+ this.vertexCounter = 0;
64
+ this.layers0 = null;
65
+ this.featuresHashArray = [];
66
+ this.refinementCounter = {
67
+ tilesCount: 0,
68
+ tilesWithAddRefineCount: 0
69
+ };
70
+ this.validate = false;
71
+ this.generateTextures = false;
72
+ this.generateBoundingVolumes = false;
73
+ this.layersHasTexture = false;
74
+ }
75
+ /**
76
+ * Convert a 3d tileset
77
+ * @param options
78
+ * @param options.inputUrl the url to read the tileset from
79
+ * @param options.outputPath the output filename
80
+ * @param options.tilesetName the output name of the tileset
81
+ * @param options.maxDepth The max tree depth of conversion
82
+ * @param options.slpk Generate slpk (Scene Layer Packages) output file
83
+ * @param options.sevenZipExe Location of 7z.exe archiver to create slpk on Windows
84
+ * @param options.egmFilePath location of *.pgm file to convert heights from ellipsoidal to gravity-related format
85
+ * @param options.token Token for Cesium ION tilesets authentication
86
+ * @param options.draco Generate I3S 1.7 draco compressed geometries
87
+ * @param options.validate -enable validation
88
+ */
89
+ async convert(options) {
90
+ this.conversionStartTime = process_1.default.hrtime();
91
+ const { tilesetName, slpk, egmFilePath, inputUrl, validate, outputPath, draco, sevenZipExe, maxDepth, token, generateTextures, generateBoundingVolumes } = options;
92
+ this.options = { maxDepth, slpk, sevenZipExe, egmFilePath, draco, token, inputUrl };
93
+ this.validate = Boolean(validate);
94
+ this.Loader = inputUrl.indexOf(CESIUM_DATASET_PREFIX) !== -1 ? _3d_tiles_1.CesiumIonLoader : _3d_tiles_1.Tiles3DLoader;
95
+ this.generateTextures = Boolean(generateTextures);
96
+ this.generateBoundingVolumes = Boolean(generateBoundingVolumes);
97
+ this.writeQueue = new write_queue_1.default();
98
+ this.writeQueue.startListening();
99
+ console.log('Loading egm file...'); // eslint-disable-line
100
+ this.geoidHeightModel = await (0, core_1.load)(egmFilePath, pgm_loader_1.PGMLoader);
101
+ console.log('Loading egm file completed!'); // eslint-disable-line
102
+ if (slpk) {
103
+ this.nodePages.useWriteFunction(file_utils_1.writeFileForSlpk);
104
+ }
105
+ await this.loadWorkers();
106
+ const preloadOptions = await this._fetchPreloadOptions();
107
+ const tilesetOptions = { loadOptions: { basis: { format: 'rgba32' } } };
108
+ if (preloadOptions.headers) {
109
+ tilesetOptions.loadOptions.fetch = { headers: preloadOptions.headers };
110
+ }
111
+ Object.assign(tilesetOptions, preloadOptions);
112
+ const sourceTilesetJson = await (0, core_1.load)(inputUrl, this.Loader, tilesetOptions.loadOptions);
113
+ // console.log(tilesetJson); // eslint-disable-line
114
+ this.sourceTileset = new tiles_1.Tileset3D(sourceTilesetJson, tilesetOptions);
115
+ await this._createAndSaveTileset(outputPath, tilesetName);
116
+ await this._finishConversion({ slpk: Boolean(slpk), outputPath, tilesetName });
117
+ // Clean up worker pools
118
+ const workerFarm = worker_utils_1.WorkerFarm.getWorkerFarm({});
119
+ workerFarm.destroy();
120
+ return sourceTilesetJson;
121
+ }
122
+ /**
123
+ * Convert and save the layer and embedded tiles
124
+ * @param outputPath - path to save output data
125
+ * @param tilesetName - new tileset path
126
+ */
127
+ async _createAndSaveTileset(outputPath, tilesetName) {
128
+ const tilesetPath = (0, path_1.join)(`${outputPath}`, `${tilesetName}`);
129
+ // Removing the tilesetPath needed to exclude erroneous files after conversion
130
+ try {
131
+ await (0, file_utils_1.removeDir)(tilesetPath);
132
+ }
133
+ catch (e) {
134
+ // do nothing
135
+ }
136
+ this.layers0Path = (0, path_1.join)(tilesetPath, 'SceneServer', 'layers', '0');
137
+ this._formLayers0(tilesetName);
138
+ this.materialDefinitions = [];
139
+ this.materialMap = new Map();
140
+ const sourceRootTile = this.sourceTileset.root;
141
+ const boundingVolumes = (0, coordinate_converter_1.createBoundingVolumes)(sourceRootTile, this.geoidHeightModel);
142
+ const parentId = this.nodePages.push({
143
+ index: 0,
144
+ lodThreshold: 0,
145
+ obb: boundingVolumes.obb,
146
+ children: []
147
+ });
148
+ const isCreateSlpk = this.options.slpk;
149
+ const root0 = this._formRootNodeIndexDocument(boundingVolumes);
150
+ await this._convertNodesTree(root0, sourceRootTile, parentId, boundingVolumes);
151
+ this.layers0.materialDefinitions = this.materialDefinitions;
152
+ if (this.layersHasTexture === false) {
153
+ this.layers0.store.defaultGeometrySchema.ordering =
154
+ this.layers0.store.defaultGeometrySchema.ordering.filter((attribute) => attribute !== 'uv0');
155
+ }
156
+ await this._writeLayers0();
157
+ (0, create_scene_server_path_1.createSceneServerPath)(tilesetName, this.layers0, tilesetPath);
158
+ await this._writeNodeIndexDocument(root0, 'root', (0, path_1.join)(this.layers0Path, 'nodes', 'root'));
159
+ await this.nodePages.save(this.layers0Path, this.writeQueue, isCreateSlpk);
160
+ await this.writeQueue.finalize();
161
+ await this._createSlpk(tilesetPath);
162
+ }
163
+ /**
164
+ * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
165
+ * @param tilesetName - Name of layer
166
+ */
167
+ _formLayers0(tilesetName) {
168
+ const fullExtent = (0, coordinate_converter_1.convertBoundingVolumeToI3SFullExtent)(this.sourceTileset?.boundingVolume || this.sourceTileset?.root?.boundingVolume);
169
+ const extent = [fullExtent.xmin, fullExtent.ymin, fullExtent.xmax, fullExtent.ymax];
170
+ const layers0data = {
171
+ version: `{${(0, uuid_1.v4)().toUpperCase()}}`,
172
+ id: 0,
173
+ name: tilesetName,
174
+ href: './layers/0',
175
+ store: {
176
+ id: `{${(0, uuid_1.v4)().toUpperCase()}}`,
177
+ extent
178
+ },
179
+ nodePages: {
180
+ nodesPerPage: HARDCODED_NODES_PER_PAGE
181
+ },
182
+ compressGeometry: this.options.draco
183
+ };
184
+ this.layers0 = (0, json_map_transform_1.default)(layers0data, (0, layers_1.LAYERS)());
185
+ }
186
+ /**
187
+ * Convert and save the layer and embedded tiles
188
+ * @param boundingVolumes - mbs and obb data about node's bounding volume
189
+ * @return 3DNodeIndexDocument data https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
190
+ */
191
+ _formRootNodeIndexDocument(boundingVolumes) {
192
+ const root0data = {
193
+ version: `{${(0, uuid_1.v4)().toUpperCase()}}`,
194
+ id: 'root',
195
+ level: 0,
196
+ lodSelection: [
197
+ {
198
+ metricType: 'maxScreenThresholdSQ',
199
+ maxError: 0
200
+ },
201
+ {
202
+ metricType: 'maxScreenThreshold',
203
+ maxError: 0
204
+ }
205
+ ],
206
+ ...boundingVolumes,
207
+ children: []
208
+ };
209
+ return (0, json_map_transform_1.default)(root0data, (0, node_1.NODE)());
210
+ }
211
+ /**
212
+ * Form object of 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md
213
+ * @param root0 - 3DNodeIndexDocument of root node https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md
214
+ * @param sourceRootTile - Source (3DTile) tile data
215
+ * @param parentId - node id in node pages
216
+ * @param boundingVolumes - mbs and obb data about node's bounding volume
217
+ */
218
+ async _convertNodesTree(root0, sourceRootTile, parentId, boundingVolumes) {
219
+ await this.sourceTileset._loadTile(sourceRootTile);
220
+ if (this.isContentSupported(sourceRootTile)) {
221
+ root0.children = root0.children || [];
222
+ root0.children.push({
223
+ id: '1',
224
+ href: './1',
225
+ ...boundingVolumes
226
+ });
227
+ const [child] = await this._createNode(root0, sourceRootTile, parentId, 0);
228
+ const childPath = (0, path_1.join)(this.layers0Path, 'nodes', child.path);
229
+ if (this.options.slpk) {
230
+ this.writeQueue.enqueue({
231
+ archiveKey: 'nodes/1/3dNodeIndexDocument.json.gz',
232
+ writePromise: (0, file_utils_1.writeFileForSlpk)(childPath, JSON.stringify(child), '3dNodeIndexDocument.json')
233
+ });
234
+ }
235
+ else {
236
+ this.writeQueue.enqueue({ writePromise: (0, file_utils_1.writeFile)(childPath, JSON.stringify(child)) });
237
+ }
238
+ }
239
+ else {
240
+ await this._addChildrenWithNeighborsAndWriteFile({
241
+ parentNode: root0,
242
+ sourceTiles: sourceRootTile.children,
243
+ parentId,
244
+ level: 1
245
+ });
246
+ }
247
+ await sourceRootTile.unloadContent();
248
+ }
249
+ /**
250
+ * Write 3DSceneLayer https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DSceneLayer.cmn.md in file
251
+ */
252
+ async _writeLayers0() {
253
+ if (this.options.slpk) {
254
+ this.writeQueue.enqueue({
255
+ archiveKey: '3dSceneLayer.json.gz',
256
+ writePromise: (0, file_utils_1.writeFileForSlpk)(this.layers0Path, JSON.stringify(this.layers0), '3dSceneLayer.json')
257
+ });
258
+ }
259
+ else {
260
+ this.writeQueue.enqueue({
261
+ writePromise: (0, file_utils_1.writeFile)(this.layers0Path, JSON.stringify(this.layers0))
262
+ });
263
+ }
264
+ }
265
+ /**
266
+ * Write 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md in file
267
+ */
268
+ async _writeNodeIndexDocument(root0, nodePath, rootPath) {
269
+ if (this.options.slpk) {
270
+ this.writeQueue.enqueue({
271
+ archiveKey: `nodes/${nodePath}/3dNodeIndexDocument.json.gz`,
272
+ writePromise: (0, file_utils_1.writeFileForSlpk)(rootPath, JSON.stringify(root0), '3dNodeIndexDocument.json')
273
+ });
274
+ }
275
+ else {
276
+ this.writeQueue.enqueue({ writePromise: (0, file_utils_1.writeFile)(rootPath, JSON.stringify(root0)) });
277
+ }
278
+ }
279
+ /**
280
+ * Pack files into *.slpk archive
281
+ * @param tilesetPath - Path to save file
282
+ */
283
+ async _createSlpk(tilesetPath) {
284
+ if (this.options.slpk) {
285
+ const slpkTilesetPath = (0, path_1.join)(tilesetPath, 'SceneServer', 'layers', '0');
286
+ const slpkFileName = `${tilesetPath}.slpk`;
287
+ await (0, compress_util_1.compressWithChildProcess)(slpkTilesetPath, slpkFileName, 0, '.', this.options.sevenZipExe);
288
+ // TODO: `addFileToZip` corrupts archive so it can't be validated with windows i3s_converter.exe
289
+ // const fileHash128Path = `${tilesetPath}/@specialIndexFileHASH128@`;
290
+ // try {
291
+ // await generateHash128FromZip(slpkFileName, fileHash128Path);
292
+ // await addFileToZip(
293
+ // tilesetPath,
294
+ // '@specialIndexFileHASH128@',
295
+ // slpkFileName,
296
+ // this.options.sevenZipExe
297
+ // );
298
+ // } catch (error) {
299
+ // if (error.code === FS_FILE_TOO_LARGE) {
300
+ // console.warn(`${slpkFileName} file is too big to generate a hash`); // eslint-disable-line
301
+ // } else {
302
+ // console.error(error); // eslint-disable-line
303
+ // }
304
+ // }
305
+ // All converted files are contained in slpk now they can be deleted
306
+ try {
307
+ await (0, file_utils_1.removeDir)(tilesetPath);
308
+ }
309
+ catch (e) {
310
+ // do nothing
311
+ }
312
+ }
313
+ }
314
+ /**
315
+ * Add child nodes recursively and write them to files
316
+ * @param data - arguments
317
+ * @param data.sourceTiles - array of source child nodes
318
+ * @param data.parentNode - 3DNodeIndexDocument of parent node for processing child nodes
319
+ * @param data.parentId - id of parent node in node pages
320
+ * @param data.level - level of node (distanse to root node in the tree)
321
+ */
322
+ async _addChildrenWithNeighborsAndWriteFile(data) {
323
+ const childNodes = [];
324
+ await this._addChildren({ ...data, childNodes });
325
+ await this._addNeighborsAndWriteFile(data.parentNode, childNodes);
326
+ }
327
+ /**
328
+ * Add child nodes recursively and write them to files
329
+ * @param data - arguments
330
+ * @param data.childNodes - array of target child nodes
331
+ * @param data.sourceTiles - array of source child nodes
332
+ * @param data.parentNode - 3DNodeIndexDocument of parent node for processing child nodes
333
+ * @param data.parentId - id of parent node in node pages
334
+ * @param data.level - level of node (distanse to root node in the tree)
335
+ */
336
+ async _addChildren(data) {
337
+ const { childNodes, sourceTiles, parentNode, parentId, level } = data;
338
+ if (this.options.maxDepth && level > this.options.maxDepth) {
339
+ return;
340
+ }
341
+ for (const sourceTile of sourceTiles) {
342
+ if (sourceTile.type === 'json') {
343
+ await this.sourceTileset._loadTile(sourceTile);
344
+ await this._addChildren({
345
+ parentNode,
346
+ sourceTiles: sourceTile.children,
347
+ childNodes,
348
+ parentId,
349
+ level: level + 1
350
+ });
351
+ await sourceTile.unloadContent();
352
+ }
353
+ else {
354
+ const children = await this._createNode(parentNode, sourceTile, parentId, level);
355
+ parentNode.children = parentNode.children || [];
356
+ for (const child of children) {
357
+ parentNode.children.push({
358
+ id: child.id,
359
+ href: `../${child.path}`,
360
+ obb: child.obb,
361
+ mbs: child.mbs
362
+ });
363
+ childNodes.push(child);
364
+ }
365
+ }
366
+ if (sourceTile.id) {
367
+ console.log(sourceTile.id); // eslint-disable-line
368
+ }
369
+ }
370
+ }
371
+ /**
372
+ * Add neightbors to 3DNodeIndexDocument and write it in a file
373
+ * @param parentNode - arguments
374
+ * @param childNodes - array of target child nodes
375
+ */
376
+ async _addNeighborsAndWriteFile(parentNode, childNodes) {
377
+ for (const node of childNodes) {
378
+ const childPath = (0, path_1.join)(this.layers0Path, 'nodes', node.path);
379
+ const nodePath = node.path;
380
+ delete node.path;
381
+ // Don't do large amount of "neightbors" to avoid big memory consumption
382
+ if (Number(parentNode?.children?.length) < 1000) {
383
+ for (const neighbor of parentNode.children || []) {
384
+ // eslint-disable-next-line max-depth
385
+ if (node.id === neighbor.id) {
386
+ continue; // eslint-disable-line
387
+ }
388
+ if (node.neighbors) {
389
+ node.neighbors.push({ ...neighbor });
390
+ }
391
+ }
392
+ }
393
+ else {
394
+ // eslint-disable-next-line no-console, no-undef
395
+ console.warn(`Node ${node.id}: neighbors attribute is omited because of large number of neigbors`);
396
+ delete node.neighbors;
397
+ }
398
+ await this._writeNodeIndexDocument(node, nodePath, childPath);
399
+ node.neighbors = [];
400
+ }
401
+ }
402
+ /**
403
+ * Convert tile to one or more I3S nodes
404
+ * @param parentTile - parent 3DNodeIndexDocument
405
+ * @param sourceTile - source tile (3DTile)
406
+ * @param parentId - id of parent node in node pages
407
+ * @param level - level of node (distanse to root node in the tree)
408
+ */
409
+ async _createNode(parentTile, sourceTile, parentId, level) {
410
+ if (this.validate) {
411
+ this._checkAddRefinementTypeForTile(sourceTile);
412
+ }
413
+ await this._updateTilesetOptions();
414
+ await this.sourceTileset._loadTile(sourceTile);
415
+ let boundingVolumes = (0, coordinate_converter_1.createBoundingVolumes)(sourceTile, this.geoidHeightModel);
416
+ const batchTable = sourceTile?.content?.batchTableJson;
417
+ if (batchTable) {
418
+ this._convertAttributeStorageInfo(sourceTile.content);
419
+ }
420
+ const resourcesData = await this._convertResources(sourceTile);
421
+ const nodes = [];
422
+ const nodesInPage = [];
423
+ const emptyResources = {
424
+ geometry: null,
425
+ compressedGeometry: null,
426
+ texture: null,
427
+ sharedResources: null,
428
+ meshMaterial: null,
429
+ vertexCount: null,
430
+ attributes: null,
431
+ featureCount: null,
432
+ boundingVolumes: null
433
+ };
434
+ for (const resources of resourcesData || [emptyResources]) {
435
+ this.layersHasTexture = this.layersHasTexture || Boolean(resources.texture);
436
+ if (this.generateBoundingVolumes && resources.boundingVolumes) {
437
+ boundingVolumes = resources.boundingVolumes;
438
+ }
439
+ const lodSelection = (0, lod_conversion_utils_1.convertGeometricErrorToScreenThreshold)(sourceTile, boundingVolumes);
440
+ const maxScreenThresholdSQ = lodSelection.find((val) => val.metricType === 'maxScreenThresholdSQ') || { maxError: 0 };
441
+ const nodeInPage = this._createNodeInNodePages(maxScreenThresholdSQ, boundingVolumes, sourceTile, parentId, resources);
442
+ const node = this._createNodeIndexDocument(parentTile, boundingVolumes, lodSelection, nodeInPage, resources);
443
+ if (nodeInPage.mesh) {
444
+ await this._writeResources(resources, node.path);
445
+ }
446
+ if (this.validate) {
447
+ this.boundingVolumeWarnings = (0, node_debug_1.validateNodeBoundingVolumes)(node);
448
+ if (this.boundingVolumeWarnings && this.boundingVolumeWarnings.length) {
449
+ console.warn('Bounding Volume Warnings: ', ...this.boundingVolumeWarnings); //eslint-disable-line
450
+ }
451
+ }
452
+ nodes.push(node);
453
+ nodesInPage.push(nodeInPage);
454
+ }
455
+ sourceTile.unloadContent();
456
+ await this._addChildrenWithNeighborsAndWriteFile({
457
+ parentNode: nodes[0],
458
+ sourceTiles: sourceTile.children,
459
+ parentId: nodesInPage[0].index,
460
+ level: level + 1
461
+ });
462
+ return nodes;
463
+ }
464
+ /**
465
+ * Convert attributesStorageInfo https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
466
+ * from B3DM batch table
467
+ * @param sourceTileContent - tile content of 3DTile
468
+ * @return {void}
469
+ */
470
+ _convertAttributeStorageInfo(sourceTileContent) {
471
+ // In legacy b3dm files sometimes sourceTileContent is null.
472
+ const batchTable = sourceTileContent && sourceTileContent.batchTableJson;
473
+ if (batchTable && !this.layers0?.attributeStorageInfo?.length) {
474
+ this._convertBatchTableInfoToNodeAttributes(batchTable);
475
+ }
476
+ }
477
+ /**
478
+ * Convert tile to one or more I3S nodes
479
+ * @param sourceTile - source tile (3DTile)
480
+ * result.geometry - ArrayBuffer with geometry attributes
481
+ * result.compressedGeometry - ArrayBuffer with compressed (draco) geometry
482
+ * result.texture - texture image
483
+ * result.sharedResources - shared resource data object
484
+ * result.meshMaterial - PBR-like material object
485
+ * result.vertexCount - number of vertices in geometry
486
+ * result.attributes - feature attributes
487
+ * result.featureCount - number of features
488
+ */
489
+ async _convertResources(sourceTile) {
490
+ if (!this.isContentSupported(sourceTile)) {
491
+ return null;
492
+ }
493
+ const resourcesData = await (0, geometry_converter_1.default)(sourceTile.content, Number(this.nodePages.nodesCounter), this.featuresHashArray, this.layers0?.attributeStorageInfo, this.options.draco, this.generateBoundingVolumes, this.geoidHeightModel, this.workerSource);
494
+ return resourcesData;
495
+ }
496
+ /**
497
+ * Create a new node object (https://github.com/Esri/i3s-spec/blob/master/docs/1.7/node.cmn.md)
498
+ * in node pages (https://github.com/Esri/i3s-spec/blob/master/docs/1.7/nodePage.cmn.md)
499
+ * @param maxScreenThresholdSQ - Level of Details (LOD) metric
500
+ * @param boundingVolumes - Bounding volumes
501
+ * @param sourceTile - source tile (3DTile)
502
+ * @param parentId - id of parent node in node pages
503
+ * @param resources - the node resources data
504
+ * @param resources.meshMaterial - PBR-like material object
505
+ * @param resources.texture - texture image
506
+ * @param resources.vertexCount - number of vertices in geometry
507
+ * @param resources.featureCount - number of features
508
+ * @return the node object in node pages
509
+ */
510
+ _createNodeInNodePages(maxScreenThresholdSQ, boundingVolumes, sourceTile, parentId, resources) {
511
+ const { meshMaterial, texture, vertexCount, featureCount, geometry } = resources;
512
+ const nodeInPage = {
513
+ index: 0,
514
+ lodThreshold: maxScreenThresholdSQ.maxError,
515
+ obb: boundingVolumes.obb,
516
+ children: []
517
+ };
518
+ if (geometry && this.isContentSupported(sourceTile)) {
519
+ nodeInPage.mesh = {
520
+ geometry: {
521
+ definition: texture ? 0 : 1,
522
+ resource: 0
523
+ },
524
+ attribute: {
525
+ resource: 0
526
+ },
527
+ material: {
528
+ definition: 0
529
+ }
530
+ };
531
+ }
532
+ const nodeId = this.nodePages.push(nodeInPage, parentId);
533
+ if (meshMaterial) {
534
+ this.nodePages.updateMaterialByNodeId(nodeId, this._findOrCreateMaterial(meshMaterial));
535
+ }
536
+ if (texture) {
537
+ const texelCountHint = texture.image.height * texture.image.width;
538
+ this.nodePages.updateTexelCountHintByNodeId(nodeId, texelCountHint);
539
+ }
540
+ if (vertexCount) {
541
+ this.vertexCounter += vertexCount;
542
+ this.nodePages.updateVertexCountByNodeId(nodeId, vertexCount);
543
+ }
544
+ this.nodePages.updateNodeAttributeByNodeId(nodeId);
545
+ if (featureCount) {
546
+ this.nodePages.updateFeatureCountByNodeId(nodeId, featureCount);
547
+ }
548
+ return nodeInPage;
549
+ }
550
+ /**
551
+ * Create a new node page object in node pages
552
+ * @param parentNode - 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md object of the parent node
553
+ * @param boundingVolumes - Bounding volumes
554
+ * @param lodSelection - Level of Details (LOD) metrics
555
+ * @param nodeInPage - corresponding node object in a node page
556
+ * @param resources - the node resources data
557
+ * @param resources.texture - texture image
558
+ * @param resources.attributes - feature attributes
559
+ * @return 3DNodeIndexDocument https://github.com/Esri/i3s-spec/blob/master/docs/1.7/3DNodeIndexDocument.cmn.md object
560
+ */
561
+ _createNodeIndexDocument(parentNode, boundingVolumes, lodSelection, nodeInPage, resources) {
562
+ const { texture, attributes } = resources;
563
+ const nodeId = nodeInPage.index;
564
+ const nodeData = {
565
+ version: parentNode.version,
566
+ id: nodeId.toString(),
567
+ path: nodeId.toString(),
568
+ level: parentNode.level + 1,
569
+ ...boundingVolumes,
570
+ lodSelection,
571
+ parentNode: {
572
+ id: parentNode.id,
573
+ href: `../${parentNode.id}`,
574
+ mbs: parentNode.mbs,
575
+ obb: parentNode.obb
576
+ },
577
+ children: [],
578
+ neighbors: []
579
+ };
580
+ const node = (0, json_map_transform_1.default)(nodeData, (0, node_1.NODE)());
581
+ if (nodeInPage.mesh) {
582
+ node.geometryData = [{ href: './geometries/0' }];
583
+ node.sharedResource = { href: './shared' };
584
+ if (texture) {
585
+ node.textureData = [{ href: './textures/0' }, { href: './textures/1' }];
586
+ }
587
+ if (attributes && attributes.length && this.layers0?.attributeStorageInfo?.length) {
588
+ node.attributeData = [];
589
+ for (let index = 0; index < attributes.length; index++) {
590
+ const folderName = this.layers0.attributeStorageInfo[index].key;
591
+ node.attributeData.push({ href: `./attributes/${folderName}/0` });
592
+ }
593
+ }
594
+ }
595
+ return node;
596
+ }
597
+ /**
598
+ * Write node resources in files
599
+ * @param resources - source tile (3DTile)
600
+ * @param resources.geometry - Uint8Array with geometry attributes
601
+ * @param resources.compressedGeometry - Uint8Array with compressed (draco) geometry
602
+ * @param resources.texture - texture image
603
+ * @param resources.sharedResources - shared resource data object
604
+ * @param resources.attributes - feature attributes
605
+ * @return {Promise<void>}
606
+ */
607
+ async _writeResources(resources, nodePath) {
608
+ const { geometry: geometryBuffer, compressedGeometry, texture, sharedResources, attributes } = resources;
609
+ const childPath = (0, path_1.join)(this.layers0Path, 'nodes', nodePath);
610
+ const slpkChildPath = (0, path_1.join)('nodes', nodePath);
611
+ await this._writeGeometries(geometryBuffer, compressedGeometry, childPath, slpkChildPath);
612
+ await this._writeShared(sharedResources, childPath, slpkChildPath, nodePath);
613
+ await this._writeTexture(texture, childPath, slpkChildPath);
614
+ await this._writeAttributes(attributes, childPath, slpkChildPath);
615
+ }
616
+ /**
617
+ * Write non-compressed and compressed geometries in files
618
+ * @param geometryBuffer - Uint8Array with geometry attributes
619
+ * @param compressedGeometry - Uint8Array with compressed (draco) geometry
620
+ * @param childPath - a child path to write resources
621
+ * @param slpkChildPath - resource path inside *slpk file
622
+ */
623
+ async _writeGeometries(geometryBuffer, compressedGeometry, childPath, slpkChildPath) {
624
+ if (this.options.slpk) {
625
+ const slpkGeometryPath = (0, path_1.join)(childPath, 'geometries');
626
+ this.writeQueue.enqueue({
627
+ archiveKey: `${slpkChildPath}/geometries/0.bin.gz`,
628
+ writePromise: (0, file_utils_1.writeFileForSlpk)(slpkGeometryPath, geometryBuffer, '0.bin')
629
+ });
630
+ }
631
+ else {
632
+ const geometryPath = (0, path_1.join)(childPath, 'geometries/0/');
633
+ this.writeQueue.enqueue({
634
+ writePromise: (0, file_utils_1.writeFile)(geometryPath, geometryBuffer, 'index.bin')
635
+ });
636
+ }
637
+ if (this.options.draco) {
638
+ if (this.options.slpk) {
639
+ const slpkCompressedGeometryPath = (0, path_1.join)(childPath, 'geometries');
640
+ this.writeQueue.enqueue({
641
+ archiveKey: `${slpkChildPath}/geometries/1.bin.gz`,
642
+ writePromise: (0, file_utils_1.writeFileForSlpk)(slpkCompressedGeometryPath, compressedGeometry, '1.bin')
643
+ });
644
+ }
645
+ else {
646
+ const compressedGeometryPath = (0, path_1.join)(childPath, 'geometries/1/');
647
+ this.writeQueue.enqueue({
648
+ writePromise: (0, file_utils_1.writeFile)(compressedGeometryPath, compressedGeometry, 'index.bin')
649
+ });
650
+ }
651
+ }
652
+ }
653
+ /**
654
+ * Write shared resources in a file
655
+ * @param sharedResources - shared resource data object
656
+ * @param childPath - a child path to write resources
657
+ * @param slpkChildPath - resource path inside *slpk file
658
+ * @param nodePath - a node path
659
+ */
660
+ async _writeShared(sharedResources, childPath, slpkChildPath, nodePath) {
661
+ if (!sharedResources) {
662
+ return;
663
+ }
664
+ sharedResources.nodePath = nodePath;
665
+ const sharedData = (0, json_map_transform_1.default)(sharedResources, (0, shared_resources_1.SHARED_RESOURCES)());
666
+ const sharedDataStr = JSON.stringify(sharedData);
667
+ if (this.options.slpk) {
668
+ const slpkSharedPath = (0, path_1.join)(childPath, 'shared');
669
+ this.writeQueue.enqueue({
670
+ archiveKey: `${slpkChildPath}/shared/sharedResource.json.gz`,
671
+ writePromise: (0, file_utils_1.writeFileForSlpk)(slpkSharedPath, sharedDataStr, 'sharedResource.json')
672
+ });
673
+ }
674
+ else {
675
+ const sharedPath = (0, path_1.join)(childPath, 'shared/');
676
+ this.writeQueue.enqueue({ writePromise: (0, file_utils_1.writeFile)(sharedPath, sharedDataStr) });
677
+ }
678
+ }
679
+ /**
680
+ * Generates textures based on texture mime type and fill in textureSetDefinitions data.
681
+ * @param texture - the texture image
682
+ * @param childPath - a child path to write resources
683
+ * @param slpkChildPath - the resource path inside *slpk file
684
+ */
685
+ async _writeTexture(texture, childPath, slpkChildPath) {
686
+ if (texture) {
687
+ const format = this._getFormatByMimeType(texture?.mimeType);
688
+ const formats = [];
689
+ const textureData = texture.bufferView.data;
690
+ switch (format) {
691
+ case 'jpg':
692
+ case 'png': {
693
+ formats.push({ name: '0', format });
694
+ await this.writeTextureFile(textureData, '0', format, childPath, slpkChildPath);
695
+ if (this.generateTextures) {
696
+ formats.push({ name: '1', format: 'ktx2' });
697
+ const ktx2TextureData = new Uint8Array(await (0, core_1.encode)(texture.image, textures_1.KTX2BasisWriter));
698
+ await this.writeTextureFile(ktx2TextureData, '1', 'ktx2', childPath, slpkChildPath);
699
+ }
700
+ break;
701
+ }
702
+ case 'ktx2': {
703
+ formats.push({ name: '1', format });
704
+ await this.writeTextureFile(textureData, '1', format, childPath, slpkChildPath);
705
+ if (this.generateTextures) {
706
+ formats.push({ name: '0', format: 'jpg' });
707
+ const decodedFromKTX2TextureData = new Uint8Array(await (0, core_1.encode)(texture.image.data[0], images_1.ImageWriter));
708
+ await this.writeTextureFile(decodedFromKTX2TextureData, '0', 'jpg', childPath, slpkChildPath);
709
+ }
710
+ }
711
+ }
712
+ if (!this.layers0.textureSetDefinitions.length) {
713
+ this.layers0.textureSetDefinitions.push({ formats });
714
+ }
715
+ }
716
+ }
717
+ /**
718
+ * Write the texture image in a file
719
+ * @param textureData
720
+ * @param name
721
+ * @param format
722
+ * @param childPath
723
+ * @param slpkChildPath
724
+ */
725
+ async writeTextureFile(textureData, name, format, childPath, slpkChildPath) {
726
+ if (this.options.slpk) {
727
+ const slpkTexturePath = (0, path_1.join)(childPath, 'textures');
728
+ const compress = false;
729
+ this.writeQueue.enqueue({
730
+ archiveKey: `${slpkChildPath}/textures/${name}.${format}`,
731
+ writePromise: (0, file_utils_1.writeFileForSlpk)(slpkTexturePath, textureData, `${name}.${format}`, compress)
732
+ });
733
+ }
734
+ else {
735
+ const texturePath = (0, path_1.join)(childPath, `textures/${name}/`);
736
+ this.writeQueue.enqueue({
737
+ writePromise: (0, file_utils_1.writeFile)(texturePath, textureData, `index.${format}`)
738
+ });
739
+ }
740
+ }
741
+ /**
742
+ * Write feature attributes in files
743
+ * @param attributes - feature attributes
744
+ * @param childPath - a child path to write resources
745
+ * @param slpkChildPath - the resource path inside *slpk file
746
+ */
747
+ async _writeAttributes(attributes = [], childPath, slpkChildPath) {
748
+ if (attributes?.length && this.layers0?.attributeStorageInfo?.length) {
749
+ for (let index = 0; index < attributes.length; index++) {
750
+ const folderName = this.layers0.attributeStorageInfo[index].key;
751
+ const fileBuffer = new Uint8Array(attributes[index]);
752
+ if (this.options.slpk) {
753
+ const slpkAttributesPath = (0, path_1.join)(childPath, 'attributes', folderName);
754
+ this.writeQueue.enqueue({
755
+ archiveKey: `${slpkChildPath}/attributes/${folderName}.bin.gz`,
756
+ writePromise: (0, file_utils_1.writeFileForSlpk)(slpkAttributesPath, fileBuffer, '0.bin')
757
+ });
758
+ }
759
+ else {
760
+ const attributesPath = (0, path_1.join)(childPath, `attributes/${folderName}/0`);
761
+ this.writeQueue.enqueue({
762
+ writePromise: (0, file_utils_1.writeFile)(attributesPath, fileBuffer, 'index.bin')
763
+ });
764
+ }
765
+ }
766
+ }
767
+ }
768
+ /**
769
+ * Return file format by its MIME type
770
+ * @param mimeType - feature attributes
771
+ */
772
+ _getFormatByMimeType(mimeType) {
773
+ switch (mimeType) {
774
+ case 'image/jpeg':
775
+ return 'jpg';
776
+ case 'image/png':
777
+ return 'png';
778
+ case 'image/ktx2':
779
+ return 'ktx2';
780
+ default:
781
+ return 'jpg';
782
+ }
783
+ }
784
+ /**
785
+ * Find or create material in materialDefinitions array
786
+ * @param material - end-to-end index of the node
787
+ * @return material id
788
+ */
789
+ _findOrCreateMaterial(material) {
790
+ const hash = (0, md5_1.default)(JSON.stringify(material));
791
+ if (this.materialMap.has(hash)) {
792
+ return this.materialMap.get(hash);
793
+ }
794
+ const newMaterialId = this.materialDefinitions.push(material) - 1;
795
+ this.materialMap.set(hash, newMaterialId);
796
+ return newMaterialId;
797
+ }
798
+ /**
799
+ * Generate storage attribute for map segmentation.
800
+ * @param attributeIndex - order index of attribute (f_0, f_1 ...).
801
+ * @param key - attribute key from batch table.\
802
+ * @param attributeType - attribute type.
803
+ * @return Updated storageAttribute.
804
+ */
805
+ _createdStorageAttribute(attributeIndex, key, attributeType) {
806
+ const storageAttribute = {
807
+ key: `f_${attributeIndex}`,
808
+ name: key,
809
+ ordering: ['attributeValues'],
810
+ header: [{ property: 'count', valueType: 'UInt32' }],
811
+ attributeValues: { valueType: 'Int32', valuesPerElement: 1 }
812
+ };
813
+ switch (attributeType) {
814
+ case OBJECT_ID_TYPE:
815
+ this._setupIdAttribute(storageAttribute);
816
+ break;
817
+ case STRING_TYPE:
818
+ this._setupStringAttribute(storageAttribute);
819
+ break;
820
+ case DOUBLE_TYPE:
821
+ this._setupDoubleAttribute(storageAttribute);
822
+ break;
823
+ case SHORT_INT_TYPE:
824
+ break;
825
+ default:
826
+ this._setupStringAttribute(storageAttribute);
827
+ }
828
+ return storageAttribute;
829
+ }
830
+ /**
831
+ * Get the attribute type for attributeStorageInfo https://github.com/Esri/i3s-spec/blob/master/docs/1.7/attributeStorageInfo.cmn.md
832
+ * @param key - attribute's key
833
+ * @param attribute - attribute's type in batchTable
834
+ */
835
+ getAttributeType(key, attribute) {
836
+ if (key === OBJECT_ID_TYPE) {
837
+ return OBJECT_ID_TYPE;
838
+ }
839
+ if (typeof attribute === STRING_TYPE) {
840
+ return STRING_TYPE;
841
+ }
842
+ else if (typeof attribute === 'number') {
843
+ return Number.isInteger(attribute) ? SHORT_INT_TYPE : DOUBLE_TYPE;
844
+ }
845
+ return STRING_TYPE;
846
+ }
847
+ /**
848
+ * Setup storage attribute as string.
849
+ * @param storageAttribute - attribute for map segmentation.
850
+ */
851
+ _setupStringAttribute(storageAttribute) {
852
+ storageAttribute.ordering.unshift('attributeByteCounts');
853
+ storageAttribute.header.push({ property: 'attributeValuesByteCount', valueType: 'UInt32' });
854
+ storageAttribute.attributeValues = {
855
+ valueType: 'String',
856
+ encoding: 'UTF-8',
857
+ valuesPerElement: 1
858
+ };
859
+ storageAttribute.attributeByteCounts = {
860
+ valueType: 'UInt32',
861
+ valuesPerElement: 1
862
+ };
863
+ }
864
+ /**
865
+ * Setup Id attribute for map segmentation.
866
+ * @param storageAttribute - attribute for map segmentation .
867
+ */
868
+ _setupIdAttribute(storageAttribute) {
869
+ storageAttribute.attributeValues = {
870
+ valueType: 'Oid32',
871
+ valuesPerElement: 1
872
+ };
873
+ }
874
+ /**
875
+ * Setup double attribute for map segmentation.
876
+ * @param storageAttribute - attribute for map segmentation .
877
+ */
878
+ _setupDoubleAttribute(storageAttribute) {
879
+ storageAttribute.attributeValues = {
880
+ valueType: 'Float64',
881
+ valuesPerElement: 1
882
+ };
883
+ }
884
+ /**
885
+ * Setup field attribute for map segmentation.
886
+ * @param key - attribute for map segmentation.
887
+ * @param fieldAttributeType - esri attribute type ('esriFieldTypeString' or 'esriFieldTypeOID').
888
+ */
889
+ _createFieldAttribute(key, fieldAttributeType) {
890
+ return {
891
+ name: key,
892
+ type: fieldAttributeType,
893
+ alias: key
894
+ };
895
+ }
896
+ /**
897
+ * Do conversion of 3DTiles batch table to I3s node attributes.
898
+ * @param batchTable - Table with layer meta data.
899
+ */
900
+ _convertBatchTableInfoToNodeAttributes(batchTable) {
901
+ let attributeIndex = 0;
902
+ const batchTableWithObjectId = {
903
+ OBJECTID: [0],
904
+ ...batchTable
905
+ };
906
+ for (const key in batchTableWithObjectId) {
907
+ const firstAttribute = batchTableWithObjectId[key][0];
908
+ const attributeType = this.getAttributeType(key, firstAttribute);
909
+ const storageAttribute = this._createdStorageAttribute(attributeIndex, key, attributeType);
910
+ const fieldAttributeType = this._getFieldAttributeType(attributeType);
911
+ const fieldAttribute = this._createFieldAttribute(key, fieldAttributeType);
912
+ const popupInfo = this._createPopupInfo(batchTableWithObjectId);
913
+ this.layers0.attributeStorageInfo.push(storageAttribute);
914
+ this.layers0.fields.push(fieldAttribute);
915
+ this.layers0.popupInfo = popupInfo;
916
+ this.layers0.layerType = _3D_OBJECT_LAYER_TYPE;
917
+ attributeIndex += 1;
918
+ }
919
+ }
920
+ /**
921
+ * Find and return attribute type based on key form Batch table.
922
+ * @param attributeType
923
+ */
924
+ _getFieldAttributeType(attributeType) {
925
+ switch (attributeType) {
926
+ case OBJECT_ID_TYPE:
927
+ return 'esriFieldTypeOID';
928
+ case STRING_TYPE:
929
+ return 'esriFieldTypeString';
930
+ case SHORT_INT_TYPE:
931
+ return 'esriFieldTypeInteger';
932
+ case DOUBLE_TYPE:
933
+ return 'esriFieldTypeDouble';
934
+ default:
935
+ return 'esriFieldTypeString';
936
+ }
937
+ }
938
+ /**
939
+ * Generate popup info to show metadata on the map.
940
+ * @param batchTable - Batch table data with OBJECTID.
941
+ * @return data for correct rendering of popup.
942
+ */
943
+ _createPopupInfo(batchTable) {
944
+ const title = '{OBJECTID}';
945
+ const mediaInfos = [];
946
+ const fieldInfos = [];
947
+ const popupElements = [];
948
+ const expressionInfos = [];
949
+ for (const key in batchTable) {
950
+ fieldInfos.push({
951
+ fieldName: key,
952
+ visible: true,
953
+ isEditable: false,
954
+ label: key
955
+ });
956
+ }
957
+ popupElements.push({
958
+ fieldInfos,
959
+ type: 'fields'
960
+ });
961
+ return {
962
+ title,
963
+ mediaInfos,
964
+ popupElements,
965
+ fieldInfos,
966
+ expressionInfos
967
+ };
968
+ }
969
+ /**
970
+ * Print statistics in the end of conversion
971
+ * @param params - output files data
972
+ */
973
+ async _finishConversion(params) {
974
+ const { tilesCount, tilesWithAddRefineCount } = this.refinementCounter;
975
+ const addRefinementPercentage = tilesWithAddRefineCount
976
+ ? (tilesWithAddRefineCount / tilesCount) * 100
977
+ : 0;
978
+ const filesSize = await (0, statistic_utills_1.calculateFilesSize)(params);
979
+ const diff = process_1.default.hrtime(this.conversionStartTime);
980
+ const conversionTime = (0, statistic_utills_1.timeConverter)(diff);
981
+ console.log(`------------------------------------------------`); // eslint-disable-line no-undef, no-console
982
+ console.log(`Finishing conversion of ${_3D_TILES}`); // eslint-disable-line no-undef, no-console
983
+ console.log(`Total conversion time: ${conversionTime}`); // eslint-disable-line no-undef, no-console
984
+ console.log(`Vertex count: `, this.vertexCounter); // eslint-disable-line no-undef, no-console
985
+ console.log(`File(s) size: `, filesSize, ' bytes'); // eslint-disable-line no-undef, no-console
986
+ console.log(`Percentage of tiles with "ADD" refinement type:`, addRefinementPercentage, '%'); // eslint-disable-line no-undef, no-console
987
+ console.log(`------------------------------------------------`); // eslint-disable-line no-undef, no-console
988
+ }
989
+ /**
990
+ * Fetch preload options for ION tileset
991
+ */
992
+ async _fetchPreloadOptions() {
993
+ if (!this.Loader.preload) {
994
+ return {};
995
+ }
996
+ const options = {
997
+ 'cesium-ion': { accessToken: this.options.token || ION_DEFAULT_TOKEN }
998
+ };
999
+ const preloadOptions = await this.Loader.preload(this.options.inputUrl, options);
1000
+ this.refreshTokenTime = process_1.default.hrtime();
1001
+ return { ...options, ...preloadOptions };
1002
+ }
1003
+ /**
1004
+ * Update options of source tileset
1005
+ */
1006
+ async _updateTilesetOptions() {
1007
+ const diff = process_1.default.hrtime(this.refreshTokenTime);
1008
+ if (diff[0] < REFRESH_TOKEN_TIMEOUT) {
1009
+ return;
1010
+ }
1011
+ this.refreshTokenTime = process_1.default.hrtime();
1012
+ const preloadOptions = await this._fetchPreloadOptions();
1013
+ this.sourceTileset.options = { ...this.sourceTileset.options, ...preloadOptions };
1014
+ if (preloadOptions.headers) {
1015
+ this.sourceTileset.loadOptions.fetch = {
1016
+ ...this.sourceTileset.loadOptions.fetch,
1017
+ headers: preloadOptions.headers
1018
+ };
1019
+ console.log('Authorization Bearer token has been updated'); // eslint-disable-line no-undef, no-console
1020
+ }
1021
+ }
1022
+ /** Do calculations of all tiles and tiles with "ADD" type of refinement.
1023
+ * @param tile
1024
+ */
1025
+ _checkAddRefinementTypeForTile(tile) {
1026
+ const ADD_TILE_REFINEMENT = 1;
1027
+ if (tile.refine === ADD_TILE_REFINEMENT) {
1028
+ this.refinementCounter.tilesWithAddRefineCount += 1;
1029
+ console.warn('This tile uses "ADD" type of refinement'); // eslint-disable-line
1030
+ }
1031
+ this.refinementCounter.tilesCount += 1;
1032
+ }
1033
+ /**
1034
+ * Check if the tile's content format is supported by the converter
1035
+ * @param sourceRootTile
1036
+ * @returns
1037
+ */
1038
+ isContentSupported(sourceRootTile) {
1039
+ return ['b3dm', 'glTF'].includes(sourceRootTile?.content?.type);
1040
+ }
1041
+ async loadWorkers() {
1042
+ console.log(`Loading workers source...`); // eslint-disable-line no-undef, no-console
1043
+ if (this.options.draco) {
1044
+ const url = (0, worker_utils_1.getWorkerURL)(draco_1.DracoWriterWorker, { ...(0, core_1.getLoaderOptions)() });
1045
+ const sourceResponse = await (0, core_1.fetchFile)(url);
1046
+ const source = await sourceResponse.text();
1047
+ this.workerSource.draco = source;
1048
+ }
1049
+ const i3sAttributesWorkerUrl = (0, worker_utils_1.getWorkerURL)(i3s_attributes_worker_1.I3SAttributesWorker, { ...(0, core_1.getLoaderOptions)() });
1050
+ const sourceResponse = await (0, core_1.fetchFile)(i3sAttributesWorkerUrl);
1051
+ const source = await sourceResponse.text();
1052
+ this.workerSource.I3SAttributes = source;
1053
+ console.log(`Loading workers source completed!`); // eslint-disable-line no-undef, no-console
1054
+ }
1055
+ }
1056
+ exports.default = I3SConverter;