@loaders.gl/tile-converter 4.0.0 → 4.0.1

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 (39) hide show
  1. package/dist/converter.min.cjs +109 -109
  2. package/dist/i3s-converter/helpers/attribute-metadata-info.d.ts +74 -0
  3. package/dist/i3s-converter/helpers/attribute-metadata-info.d.ts.map +1 -0
  4. package/dist/i3s-converter/helpers/attribute-metadata-info.js +157 -0
  5. package/dist/i3s-converter/helpers/attribute-metadata-info.js.map +1 -0
  6. package/dist/i3s-converter/helpers/feature-attributes.d.ts +3 -28
  7. package/dist/i3s-converter/helpers/feature-attributes.d.ts.map +1 -1
  8. package/dist/i3s-converter/helpers/feature-attributes.js +16 -125
  9. package/dist/i3s-converter/helpers/feature-attributes.js.map +1 -1
  10. package/dist/i3s-converter/i3s-converter.d.ts +11 -5
  11. package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
  12. package/dist/i3s-converter/i3s-converter.js +31 -44
  13. package/dist/i3s-converter/i3s-converter.js.map +1 -1
  14. package/dist/i3s-converter/types.d.ts +15 -0
  15. package/dist/i3s-converter/types.d.ts.map +1 -1
  16. package/dist/i3s-converter/types.js +6 -0
  17. package/dist/i3s-converter/types.js.map +1 -1
  18. package/dist/i3s-server/app.d.ts.map +1 -1
  19. package/dist/i3s-server/app.js +3 -6
  20. package/dist/i3s-server/app.js.map +1 -1
  21. package/dist/i3s-server/bin/i3s-server.min.cjs +86 -86
  22. package/dist/i3s-server/bin/www.js.map +1 -1
  23. package/dist/i3s-server/routes/index.d.ts +1 -1
  24. package/dist/i3s-server/routes/index.d.ts.map +1 -1
  25. package/dist/i3s-server/routes/index.js +2 -5
  26. package/dist/i3s-server/routes/index.js.map +1 -1
  27. package/dist/i3s-server/routes/slpk-router.d.ts.map +1 -1
  28. package/dist/i3s-server/routes/slpk-router.js +3 -3
  29. package/dist/i3s-server/routes/slpk-router.js.map +1 -1
  30. package/dist/index.cjs +269 -173
  31. package/package.json +15 -15
  32. package/src/i3s-converter/helpers/attribute-metadata-info.ts +246 -0
  33. package/src/i3s-converter/helpers/feature-attributes.ts +18 -180
  34. package/src/i3s-converter/i3s-converter.ts +46 -65
  35. package/src/i3s-converter/types.ts +16 -0
  36. package/src/i3s-server/app.ts +9 -4
  37. package/src/i3s-server/bin/www.ts +6 -0
  38. package/src/i3s-server/routes/index.ts +2 -4
  39. package/src/i3s-server/routes/slpk-router.ts +4 -3
@@ -1,6 +1,8 @@
1
1
  // loaders.gl, MIT license
2
2
  // Copyright (c) vis.gl contributors
3
3
 
4
+ import {AttributeMetadataInfo} from './helpers/attribute-metadata-info';
5
+
4
6
  import type {
5
7
  FeatureTableJson,
6
8
  Tiles3DLoaderOptions,
@@ -62,12 +64,8 @@ import {WorkerFarm} from '@loaders.gl/worker-utils';
62
64
  import WriteQueue from '../lib/utils/write-queue';
63
65
  import {BROWSER_ERROR_MESSAGE} from '../constants';
64
66
  import {
65
- getAttributeTypesFromPropertyTable,
66
- getAttributeTypesFromSchema,
67
- createdStorageAttribute,
68
- getFieldAttributeType,
69
- createFieldAttribute,
70
- createPopupInfo
67
+ getAttributeTypesMapFromPropertyTable,
68
+ getAttributeTypesMapFromSchema
71
69
  } from './helpers/feature-attributes';
72
70
  import {NodeIndexDocument} from './helpers/node-index-document';
73
71
  import {
@@ -94,6 +92,7 @@ const CESIUM_DATASET_PREFIX = 'https://';
94
92
  * Converter from 3d-tiles tileset to i3s layer
95
93
  */
96
94
  export default class I3SConverter {
95
+ attributeMetadataInfo: AttributeMetadataInfo;
97
96
  nodePages: NodePages;
98
97
  options: any;
99
98
  layers0Path: string;
@@ -139,8 +138,18 @@ export default class I3SConverter {
139
138
  meshTopologyTypes: new Set(),
140
139
  metadataClasses: new Set()
141
140
  };
141
+ /** Total count of tiles in tileset */
142
+ tileCountTotal: number = 0;
143
+ /** Count of tiles already converted plus one (refers to the tile currently being converted) */
144
+ tileCountCurrentlyConverting: number = 0;
145
+ /**
146
+ * The number of digits to appear after decimal point in the string representation of the tile count.
147
+ * It's calculated based on the total count of tiles.
148
+ */
149
+ numberOfDigitsInPercentage: number = 0;
142
150
 
143
151
  constructor() {
152
+ this.attributeMetadataInfo = new AttributeMetadataInfo();
144
153
  this.nodePages = new NodePages(writeFile, HARDCODED_NODES_PER_PAGE, this);
145
154
  this.options = {};
146
155
  this.layers0Path = '';
@@ -301,8 +310,12 @@ export default class I3SConverter {
301
310
  );
302
311
  const {meshTopologyTypes, metadataClasses} = this.preprocessData;
303
312
 
313
+ this.numberOfDigitsInPercentage =
314
+ this.tileCountTotal > 100 ? Math.ceil(Math.log10(this.tileCountTotal)) - 2 : 0;
315
+
304
316
  console.log(`------------------------------------------------`);
305
317
  console.log(`Preprocess results:`);
318
+ console.log(`Tile count: ${this.tileCountTotal}`);
306
319
  console.log(`glTF mesh topology types: ${Array.from(meshTopologyTypes).join(', ')}`);
307
320
 
308
321
  if (metadataClasses.size) {
@@ -344,6 +357,7 @@ export default class I3SConverter {
344
357
  return null;
345
358
  }
346
359
  if (sourceTile.id) {
360
+ this.tileCountTotal++;
347
361
  console.log(`[analyze]: ${sourceTile.id}`); // eslint-disable-line
348
362
  }
349
363
 
@@ -450,6 +464,14 @@ export default class I3SConverter {
450
464
  this.options.maxDepth
451
465
  );
452
466
 
467
+ this.layers0!.attributeStorageInfo = this.attributeMetadataInfo.attributeStorageInfo;
468
+ this.layers0!.fields = this.attributeMetadataInfo.fields;
469
+ this.layers0!.popupInfo = this.attributeMetadataInfo.popupInfo;
470
+
471
+ if (this.attributeMetadataInfo.attributeStorageInfo.length) {
472
+ this.layers0!.layerType = _3D_OBJECT_LAYER_TYPE;
473
+ }
474
+
453
475
  this.layers0!.materialDefinitions = this.materialDefinitions;
454
476
  // @ts-ignore
455
477
  this.layers0.geometryDefinitions = transform(
@@ -596,7 +618,14 @@ export default class I3SConverter {
596
618
  return traversalProps;
597
619
  }
598
620
  if (sourceTile.id) {
599
- console.log(`[convert]: ${sourceTile.id}`); // eslint-disable-line
621
+ // Print the tile number that is currently being converted.
622
+ this.tileCountCurrentlyConverting++;
623
+ let percentString = '';
624
+ if (this.tileCountTotal) {
625
+ const percent = (this.tileCountCurrentlyConverting / this.tileCountTotal) * 100;
626
+ percentString = ' ' + percent.toFixed(this.numberOfDigitsInPercentage);
627
+ }
628
+ console.log(`[convert${percentString}%]: ${sourceTile.id}`); // eslint-disable-line
600
629
  }
601
630
 
602
631
  const {parentNodes, transform} = traversalProps;
@@ -775,7 +804,7 @@ export default class I3SConverter {
775
804
  async () => (await this.nodePages.push({index: 0, obb: draftObb}, parentId)).index,
776
805
  propertyTable,
777
806
  this.featuresHashArray,
778
- this.layers0?.attributeStorageInfo,
807
+ this.attributeMetadataInfo.attributeStorageInfo,
779
808
  this.options.draco,
780
809
  this.generateBoundingVolumes,
781
810
  this.options.mergeMaterials,
@@ -1081,14 +1110,14 @@ export default class I3SConverter {
1081
1110
  childPath: string,
1082
1111
  slpkChildPath: string
1083
1112
  ): Promise<void> {
1084
- if (attributes?.length && this.layers0?.attributeStorageInfo?.length) {
1113
+ if (attributes?.length && this.attributeMetadataInfo.attributeStorageInfo.length) {
1085
1114
  const minimumLength =
1086
- attributes.length < this.layers0.attributeStorageInfo.length
1115
+ attributes.length < this.attributeMetadataInfo.attributeStorageInfo.length
1087
1116
  ? attributes.length
1088
- : this.layers0.attributeStorageInfo.length;
1117
+ : this.attributeMetadataInfo.attributeStorageInfo.length;
1089
1118
 
1090
1119
  for (let index = 0; index < minimumLength; index++) {
1091
- const folderName = this.layers0.attributeStorageInfo[index].key;
1120
+ const folderName = this.attributeMetadataInfo.attributeStorageInfo[index].key;
1092
1121
  const fileBuffer = new Uint8Array(attributes[index]);
1093
1122
 
1094
1123
  if (this.options.slpk) {
@@ -1174,67 +1203,19 @@ export default class I3SConverter {
1174
1203
  */
1175
1204
  let attributeTypesMap: Record<string, Attribute> | null = null;
1176
1205
  if (this.options.metadataClass) {
1177
- if (!this.layers0!.attributeStorageInfo!.length && tileContent?.gltf) {
1178
- attributeTypesMap = getAttributeTypesFromSchema(
1206
+ if (!this.attributeMetadataInfo.attributeStorageInfo.length && tileContent?.gltf) {
1207
+ attributeTypesMap = getAttributeTypesMapFromSchema(
1179
1208
  tileContent.gltf,
1180
1209
  this.options.metadataClass
1181
1210
  );
1182
1211
  }
1183
1212
  } else if (propertyTable) {
1184
- attributeTypesMap = getAttributeTypesFromPropertyTable(propertyTable);
1213
+ attributeTypesMap = getAttributeTypesMapFromPropertyTable(propertyTable);
1185
1214
  }
1186
1215
 
1187
1216
  if (attributeTypesMap) {
1188
- this.createStorageAttributes(attributeTypesMap);
1189
- }
1190
- }
1191
-
1192
- /**
1193
- * Creates Attribute Storage Info objects based on attribute's types
1194
- * @param attributeTypesMap - set of attribute's types
1195
- */
1196
- private createStorageAttributes(attributeTypesMap: Record<string, Attribute>): void {
1197
- if (!Object.keys(attributeTypesMap).length) {
1198
- return;
1199
- }
1200
- const attributeTypes: Record<string, Attribute> = {
1201
- OBJECTID: 'OBJECTID',
1202
- ...attributeTypesMap
1203
- };
1204
-
1205
- let isUpdated = false;
1206
- let attributeIndex = this.layers0!.attributeStorageInfo!.length;
1207
- for (const key in attributeTypes) {
1208
- /*
1209
- We will append a new attribute only in case it has not been added to the attribute storage info yet.
1210
- */
1211
- const elementFound = this.layers0!.attributeStorageInfo!.find(
1212
- (element) => element.name === key
1213
- );
1214
- if (!elementFound) {
1215
- const attributeType = attributeTypes[key];
1216
-
1217
- const storageAttribute = createdStorageAttribute(attributeIndex, key, attributeType);
1218
- const fieldAttributeType = getFieldAttributeType(attributeType);
1219
- const fieldAttribute = createFieldAttribute(key, fieldAttributeType);
1220
-
1221
- this.layers0!.attributeStorageInfo!.push(storageAttribute);
1222
- this.layers0!.fields!.push(fieldAttribute);
1223
- attributeIndex += 1;
1224
- isUpdated = true;
1225
- }
1226
- }
1227
- if (isUpdated) {
1228
- /*
1229
- The attributeStorageInfo is updated. So, popupInfo should be recreated.
1230
- Use attributeStorageInfo as a source of attribute names to create the popupInfo.
1231
- */
1232
- const attributeNames: string[] = [];
1233
- for (let info of this.layers0!.attributeStorageInfo!) {
1234
- attributeNames.push(info.name);
1235
- }
1236
- this.layers0!.popupInfo = createPopupInfo(attributeNames);
1237
- this.layers0!.layerType = _3D_OBJECT_LAYER_TYPE;
1217
+ // Add new storage attributes, fields and create popupInfo
1218
+ this.attributeMetadataInfo.addMetadataInfo(attributeTypesMap);
1238
1219
  }
1239
1220
  }
1240
1221
 
@@ -225,3 +225,19 @@ export type GLTFAttributesData = {
225
225
  /** Model matrix to convert coordinate system of POSITION and NORMAL attributes from METER_OFFSETS to CARTESIAN */
226
226
  cartesianModelMatrix: Matrix4;
227
227
  };
228
+
229
+ /**
230
+ * I3S' types difine the following:
231
+ * type Attribute = 'OBJECTID' | 'string' | 'double' | 'Int32' | string;
232
+ * The AttributeType contains the string values of the Attribute type.
233
+ */
234
+ export const AttributeType = {
235
+ /** Type of attribute that is linked with feature ids */
236
+ OBJECT_ID_TYPE: 'OBJECTID',
237
+ /** String data type name for feature attributes */
238
+ STRING_TYPE: 'string',
239
+ /** Double data type name for feature attributes */
240
+ DOUBLE_TYPE: 'double',
241
+ /** Integer data type name for feature attributes */
242
+ SHORT_INT_TYPE: 'Int32'
243
+ } as const;
@@ -2,13 +2,18 @@ import express from 'express';
2
2
  import path from 'path';
3
3
  import logger from 'morgan';
4
4
  import cors from 'cors';
5
+ // For local debug
6
+ // import {fileURLToPath} from 'url';
5
7
  import {loadArchive} from './controllers/slpk-controller';
8
+ import {router as indexRouter} from './routes';
9
+ import {sceneServerRouter, router} from './routes/slpk-router';
10
+
11
+ // For local debug
12
+ // const __filename = fileURLToPath(import.meta.url);
13
+ // const __dirname = path.dirname(__filename);
6
14
 
7
15
  const I3S_LAYER_PATH = process.env.I3sLayerPath || ''; // eslint-disable-line no-process-env, no-undef
8
16
  const FULL_LAYER_PATH = path.join(process.cwd(), I3S_LAYER_PATH); // eslint-disable-line no-undef
9
- loadArchive(FULL_LAYER_PATH);
10
-
11
- const indexRouter = require('./routes/index');
12
17
 
13
18
  export const app = express();
14
19
 
@@ -19,7 +24,7 @@ app.use(express.static(path.join(__dirname, 'public')));
19
24
  app.use(cors());
20
25
 
21
26
  if (/\.slpk$/.test(I3S_LAYER_PATH)) {
22
- const {sceneServerRouter, router} = require('./routes/slpk-router');
27
+ loadArchive(FULL_LAYER_PATH);
23
28
  app.use('/SceneServer/layers/0', router);
24
29
  app.use('/SceneServer', sceneServerRouter);
25
30
  } else {
@@ -5,8 +5,14 @@ import https from 'https';
5
5
  import http from 'http';
6
6
  import fs from 'fs';
7
7
  import path from 'path';
8
+ // For local debug
9
+ // import {fileURLToPath} from 'url';
8
10
  import {formErrorHandler, formListeningHandler, normalizePort} from '../utils/server-utils';
9
11
 
12
+ // For local debug
13
+ // const __filename = fileURLToPath(import.meta.url);
14
+ // const __dirname = path.dirname(__filename);
15
+
10
16
  /** Get port from environment and store in Express. */
11
17
  const httpPort = normalizePort(process.env.PORT || '80');
12
18
  if (httpPort === false) {
@@ -1,7 +1,7 @@
1
1
  import express from 'express';
2
+ import {getFileNameByUrl} from '../controllers/index-controller';
2
3
 
3
- const router = express.Router();
4
- const {getFileNameByUrl} = require('../controllers/index-controller');
4
+ export const router = express.Router();
5
5
 
6
6
  /* GET home page. */
7
7
  router.get('*', async function (req, res, next) {
@@ -13,5 +13,3 @@ router.get('*', async function (req, res, next) {
13
13
  res.send('File not found');
14
14
  }
15
15
  });
16
-
17
- module.exports = router;
@@ -2,11 +2,13 @@ import express from 'express';
2
2
  import {getFileByUrl} from '../controllers/slpk-controller';
3
3
  import {createSceneServer} from '../utils/create-scene-server';
4
4
 
5
+ const textDecoder = new TextDecoder();
6
+
5
7
  export const sceneServerRouter = express.Router();
6
8
  sceneServerRouter.get('*', async function (req, res, next) {
7
9
  const file = await getFileByUrl('/');
8
10
  if (file) {
9
- const layer = JSON.parse(file.toString());
11
+ const layer = JSON.parse(textDecoder.decode(file));
10
12
  const sceneServerResponse = createSceneServer(layer.name, layer);
11
13
  res.send(sceneServerResponse);
12
14
  } else {
@@ -17,10 +19,9 @@ sceneServerRouter.get('*', async function (req, res, next) {
17
19
 
18
20
  export const router = express.Router();
19
21
  router.get('*', async function (req, res, next) {
20
- console.log(req.path);
21
22
  const file = await getFileByUrl(req.path);
22
23
  if (file) {
23
- res.send(file);
24
+ res.send(Buffer.from(file));
24
25
  } else {
25
26
  res.status(404);
26
27
  res.send('File not found');