@loaders.gl/tile-converter 4.2.0-alpha.1 → 4.2.0-alpha.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts +19 -0
  2. package/dist/3d-tiles-converter/3d-tiles-converter.d.ts.map +1 -1
  3. package/dist/3d-tiles-converter/3d-tiles-converter.js +121 -30
  4. package/dist/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
  5. package/dist/3d-tiles-converter/helpers/load-i3s.d.ts +28 -1
  6. package/dist/3d-tiles-converter/helpers/load-i3s.d.ts.map +1 -1
  7. package/dist/3d-tiles-converter/helpers/load-i3s.js +63 -4
  8. package/dist/3d-tiles-converter/helpers/load-i3s.js.map +1 -1
  9. package/dist/converter-cli.js +30 -21
  10. package/dist/converter-cli.js.map +1 -1
  11. package/dist/converter.min.cjs +137 -130
  12. package/dist/deps-installer/deps-installer.js +1 -1
  13. package/dist/i3s-converter/helpers/load-3d-tiles.d.ts.map +1 -1
  14. package/dist/i3s-converter/helpers/load-3d-tiles.js +22 -2
  15. package/dist/i3s-converter/helpers/load-3d-tiles.js.map +1 -1
  16. package/dist/i3s-converter/i3s-converter.js +1 -1
  17. package/dist/i3s-converter/i3s-converter.js.map +1 -1
  18. package/dist/i3s-server/bin/i3s-server.min.cjs +86 -86
  19. package/dist/index.cjs +590 -70
  20. package/dist/lib/json-schemas/conversion-dump-json-schema.d.ts +463 -0
  21. package/dist/lib/json-schemas/conversion-dump-json-schema.d.ts.map +1 -0
  22. package/dist/lib/json-schemas/conversion-dump-json-schema.js +463 -0
  23. package/dist/lib/json-schemas/conversion-dump-json-schema.js.map +1 -0
  24. package/dist/lib/utils/conversion-dump.d.ts +12 -5
  25. package/dist/lib/utils/conversion-dump.d.ts.map +1 -1
  26. package/dist/lib/utils/conversion-dump.js +44 -24
  27. package/dist/lib/utils/conversion-dump.js.map +1 -1
  28. package/dist/lib/utils/file-utils.d.ts +6 -0
  29. package/dist/lib/utils/file-utils.d.ts.map +1 -1
  30. package/dist/lib/utils/file-utils.js +7 -0
  31. package/dist/lib/utils/file-utils.js.map +1 -1
  32. package/dist/pgm-loader.js +1 -1
  33. package/package.json +15 -14
  34. package/src/3d-tiles-converter/3d-tiles-converter.ts +159 -34
  35. package/src/3d-tiles-converter/helpers/load-i3s.ts +106 -7
  36. package/src/converter-cli.ts +44 -29
  37. package/src/i3s-converter/helpers/load-3d-tiles.ts +52 -2
  38. package/src/i3s-converter/i3s-converter.ts +1 -1
  39. package/src/lib/json-schemas/conversion-dump-json-schema.ts +285 -0
  40. package/src/lib/utils/conversion-dump.ts +79 -27
  41. package/src/lib/utils/file-utils.ts +13 -0
@@ -0,0 +1,285 @@
1
+ export const dumpJsonSchema = {
2
+ type: 'object',
3
+ properties: {
4
+ options: {
5
+ type: 'object',
6
+ properties: {
7
+ inputUrl: {type: 'string'},
8
+ outputPath: {type: 'string'},
9
+ tilesetName: {type: 'string'},
10
+ maxDepth: {type: 'number'},
11
+ slpk: {type: 'boolean'},
12
+ egmFilePath: {type: 'string'},
13
+ token: {type: 'string'},
14
+ draco: {type: 'boolean'},
15
+ mergeMaterials: {type: 'boolean'},
16
+ generateTextures: {type: 'boolean'},
17
+ generateBoundingVolumes: {type: 'boolean'},
18
+ metadataClass: {type: 'string'},
19
+ analyze: {type: 'boolean'}
20
+ },
21
+ required: ['inputUrl', 'outputPath', 'tilesetName']
22
+ },
23
+ tilesConverted: {
24
+ type: 'object',
25
+ patternProperties: {
26
+ '.*': {
27
+ type: 'object',
28
+ properties: {
29
+ nodes: {
30
+ type: 'array',
31
+ items: {
32
+ type: 'object',
33
+ properties: {
34
+ nodeId: {type: ['number', 'string']},
35
+ done: {type: 'boolean'},
36
+ progress: {type: 'object', patternProperties: {'.*': {type: 'boolean'}}},
37
+ dumpMetadata: {
38
+ type: 'object',
39
+ properties: {
40
+ boundingVolumes: {
41
+ type: ['object', 'null'],
42
+ properties: {
43
+ mbs: {
44
+ type: 'array',
45
+ minItems: 4,
46
+ maxItems: 4,
47
+ items: {type: 'number'}
48
+ },
49
+ obb: {
50
+ type: 'object',
51
+ properties: {
52
+ center: {
53
+ type: 'array',
54
+ minItems: 3,
55
+ maxItems: 3,
56
+ items: {type: 'number'}
57
+ },
58
+ halfSize: {
59
+ type: 'array',
60
+ minItems: 3,
61
+ maxItems: 3,
62
+ items: {type: 'number'}
63
+ },
64
+ quaternion: {
65
+ type: 'array',
66
+ minItems: 4,
67
+ maxItems: 4,
68
+ items: {type: 'number'}
69
+ }
70
+ },
71
+ required: ['center', 'halfSize', 'quaternion']
72
+ }
73
+ },
74
+ required: ['mbs', 'obb']
75
+ },
76
+ attributesCount: {type: 'number'},
77
+ featureCount: {type: 'number'},
78
+ geometry: {type: 'boolean'},
79
+ hasUvRegions: {type: 'boolean'},
80
+ materialId: {type: 'number'},
81
+ texelCountHint: {type: 'number'},
82
+ vertexCount: {type: 'number'}
83
+ },
84
+ required: [
85
+ 'boundingVolumes',
86
+ 'featureCount',
87
+ 'geometry',
88
+ 'hasUvRegions',
89
+ 'materialId',
90
+ 'vertexCount'
91
+ ]
92
+ }
93
+ },
94
+ required: ['nodeId', 'done']
95
+ }
96
+ }
97
+ },
98
+ required: ['nodes']
99
+ }
100
+ }
101
+ },
102
+ textureSetDefinitions: {
103
+ type: 'array',
104
+ items: {
105
+ type: 'object',
106
+ properties: {
107
+ formats: {
108
+ type: 'array',
109
+ items: {
110
+ type: 'object',
111
+ properties: {
112
+ name: {type: 'string'},
113
+ format: {enum: ['jpg', 'png', 'ktx-etc2', 'dds', 'ktx2']}
114
+ },
115
+ required: ['name', 'format']
116
+ }
117
+ },
118
+ atlas: {type: 'boolean'}
119
+ },
120
+ required: ['formats']
121
+ }
122
+ },
123
+ attributeMetadataInfo: {
124
+ type: 'object',
125
+ properties: {
126
+ attributeStorageInfo: {
127
+ type: 'array',
128
+ items: {
129
+ type: 'object',
130
+ properties: {
131
+ key: {type: 'string'},
132
+ name: {type: 'string'},
133
+ header: {
134
+ type: 'array',
135
+ items: {
136
+ type: 'object',
137
+ properties: {property: {type: 'string'}, valueType: {type: 'string'}},
138
+ required: ['property', 'valueType']
139
+ }
140
+ },
141
+ ordering: {type: 'array', items: {type: 'string'}},
142
+ attributeValues: {$ref: '#/$defs/AttributeValue'},
143
+ attributeByteCounts: {$ref: '#/$defs/AttributeValue'},
144
+ objectIds: {$ref: '#/$defs/AttributeValue'}
145
+ },
146
+ required: ['key', 'name', 'header']
147
+ }
148
+ },
149
+ fields: {
150
+ type: 'array',
151
+ items: {
152
+ type: 'object',
153
+ properties: {
154
+ name: {type: 'string'},
155
+ type: {$ref: '#/$defs/ESRIField'},
156
+ alias: {type: 'string'},
157
+ domain: {$ref: '#/$defs/Domain'}
158
+ },
159
+ required: ['name', 'type']
160
+ }
161
+ },
162
+ popupInfo: {
163
+ type: 'object',
164
+ properties: {
165
+ title: {type: 'string'},
166
+ description: {type: 'string'},
167
+ expressionInfos: {type: 'array', items: {}},
168
+ fieldInfos: {type: 'array', items: {$ref: '#/$defs/FieldInfo'}},
169
+ mediaInfos: {type: 'array', items: {}},
170
+ popupElements: {
171
+ type: 'array',
172
+ items: {
173
+ type: 'object',
174
+ properties: {
175
+ text: {type: 'string'},
176
+ type: {type: 'string'},
177
+ fieldInfos: {type: 'array', items: {$ref: '#/$defs/FieldInfo'}}
178
+ }
179
+ }
180
+ }
181
+ }
182
+ }
183
+ },
184
+ required: ['attributeStorageInfo', 'fields']
185
+ },
186
+ materialDefinitions: {
187
+ type: 'array',
188
+ items: {
189
+ type: 'object',
190
+ properties: {
191
+ pbrMetallicRoughness: {
192
+ type: 'object',
193
+ properties: {
194
+ baseColorFactor: {
195
+ type: 'array',
196
+ minItems: 4,
197
+ maxItems: 4,
198
+ items: {type: 'number'}
199
+ },
200
+ baseColorTexture: {$ref: '#/$defs/I3SMaterialTexture'},
201
+ metallicFactor: {type: 'number'},
202
+ roughnessFactor: {type: 'number'},
203
+ metallicRoughnessTexture: {$ref: '#/$defs/I3SMaterialTexture'}
204
+ },
205
+ required: ['metallicFactor', 'roughnessFactor']
206
+ },
207
+ normalTexture: {$ref: '#/$defs/I3SMaterialTexture'},
208
+ occlusionTexture: {$ref: '#/$defs/I3SMaterialTexture'},
209
+ emissiveTexture: {$ref: '#/$defs/I3SMaterialTexture'},
210
+ emissiveFactor: {type: 'array', minItems: 3, maxItems: 3, items: {type: 'number'}},
211
+ alphaMode: {enum: ['opaque', 'mask', 'blend']},
212
+ alphaCutoff: {type: 'number'},
213
+ doubleSided: {type: 'boolean'},
214
+ cullFace: {enum: ['none', 'front', 'back']}
215
+ },
216
+ required: ['pbrMetallicRoughness', 'alphaMode']
217
+ }
218
+ }
219
+ },
220
+ required: ['options', 'tilesConverted'],
221
+ $defs: {
222
+ AttributeValue: {
223
+ type: 'object',
224
+ properties: {
225
+ valueType: {type: 'string'},
226
+ encoding: {type: 'string'},
227
+ valuesPerElement: {type: 'number'}
228
+ },
229
+ required: ['valueType']
230
+ },
231
+ ESRIField: {
232
+ enum: [
233
+ 'esriFieldTypeDate',
234
+ 'esriFieldTypeSingle',
235
+ 'esriFieldTypeDouble',
236
+ 'esriFieldTypeGUID',
237
+ 'esriFieldTypeGlobalID',
238
+ 'esriFieldTypeInteger',
239
+ 'esriFieldTypeOID',
240
+ 'esriFieldTypeSmallInteger',
241
+ 'esriFieldTypeString'
242
+ ]
243
+ },
244
+ Domain: {
245
+ type: 'object',
246
+ properties: {
247
+ type: {type: 'string'},
248
+ name: {type: 'string'},
249
+ description: {type: 'string'},
250
+ fieldType: {type: 'string'},
251
+ range: {type: 'array', items: {type: 'number'}},
252
+ codedValues: {
253
+ type: 'array',
254
+ items: {
255
+ type: 'object',
256
+ properties: {name: {type: 'string'}, code: {type: ['string', 'number']}},
257
+ required: ['name', 'code']
258
+ }
259
+ },
260
+ mergePolicy: {type: 'string'},
261
+ splitPolicy: {type: 'string'}
262
+ },
263
+ required: ['type', 'name']
264
+ },
265
+ FieldInfo: {
266
+ type: 'object',
267
+ properties: {
268
+ fieldName: {type: 'string'},
269
+ visible: {type: 'boolean'},
270
+ isEditable: {type: 'boolean'},
271
+ label: {type: 'string'}
272
+ },
273
+ required: ['fieldName', 'visible', 'isEditable', 'label']
274
+ },
275
+ I3SMaterialTexture: {
276
+ type: 'object',
277
+ properties: {
278
+ textureSetDefinitionId: {type: 'number'},
279
+ texCoord: {type: 'number'},
280
+ factor: {type: 'number'}
281
+ },
282
+ required: ['textureSetDefinitionId']
283
+ }
284
+ }
285
+ };
@@ -1,9 +1,12 @@
1
1
  import {isDeepStrictEqual} from 'util';
2
2
  import {DUMP_FILE_SUFFIX} from '../../constants';
3
- import {isFileExists, openJson, removeFile, writeFile} from './file-utils';
3
+ import {isFileExists, openJson, removeFile, renameFile, writeFile} from './file-utils';
4
4
  import {join} from 'path';
5
5
  import {BoundingVolumes, I3SMaterialDefinition, TextureSetDefinitionFormats} from '@loaders.gl/i3s';
6
6
  import {AttributeMetadataInfoObject} from '../../i3s-converter/helpers/attribute-metadata-info';
7
+ import process from 'process';
8
+ import Ajv from 'ajv';
9
+ import {dumpJsonSchema} from '../json-schemas/conversion-dump-json-schema';
7
10
 
8
11
  export type ConversionDumpOptions = {
9
12
  inputUrl: string;
@@ -22,7 +25,7 @@ export type ConversionDumpOptions = {
22
25
  };
23
26
 
24
27
  type NodeDoneStatus = {
25
- nodeId: number;
28
+ nodeId: number | string;
26
29
  done: boolean;
27
30
  progress?: Record<string, boolean>;
28
31
  dumpMetadata?: DumpMetadata;
@@ -60,7 +63,7 @@ export class ConversionDump {
60
63
  /** Attributes Metadata */
61
64
  attributeMetadataInfo?: AttributeMetadataInfoObject;
62
65
  /** Array of materials definitions */
63
- materialDefinitions: I3SMaterialDefinition[] = [];
66
+ materialDefinitions?: I3SMaterialDefinition[];
64
67
 
65
68
  constructor() {
66
69
  this.tilesConverted = {};
@@ -108,23 +111,34 @@ export class ConversionDump {
108
111
  `${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
109
112
  );
110
113
  if (await isFileExists(dumpFilename)) {
111
- const {
112
- options,
113
- tilesConverted,
114
- textureSetDefinitions,
115
- attributeMetadataInfo,
116
- materialDefinitions
117
- } = await openJson(
118
- join(this.options.outputPath, this.options.tilesetName),
119
- `${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
120
- );
121
- if (isDeepStrictEqual(options, JSON.parse(JSON.stringify(this.options)))) {
122
- this.tilesConverted = tilesConverted;
123
- this.textureSetDefinitions = textureSetDefinitions;
124
- this.attributeMetadataInfo = attributeMetadataInfo;
125
- this.materialDefinitions = materialDefinitions;
126
- this.restored = true;
127
- return;
114
+ try {
115
+ const dump = await openJson(
116
+ join(this.options.outputPath, this.options.tilesetName),
117
+ `${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
118
+ );
119
+
120
+ const {
121
+ options,
122
+ tilesConverted,
123
+ textureSetDefinitions,
124
+ attributeMetadataInfo,
125
+ materialDefinitions
126
+ } = dump;
127
+
128
+ const ajv = new Ajv();
129
+ const dumpJsonValidate = ajv.compile(dumpJsonSchema);
130
+ const isDumpValid = dumpJsonValidate(dump);
131
+
132
+ if (isDumpValid && isDeepStrictEqual(options, JSON.parse(JSON.stringify(this.options)))) {
133
+ this.tilesConverted = tilesConverted;
134
+ this.textureSetDefinitions = textureSetDefinitions;
135
+ this.attributeMetadataInfo = attributeMetadataInfo;
136
+ this.materialDefinitions = materialDefinitions;
137
+ this.restored = true;
138
+ return;
139
+ }
140
+ } catch (error) {
141
+ console.log("Can't open dump file", error);
128
142
  }
129
143
  }
130
144
  await this.deleteDumpFile();
@@ -142,8 +156,8 @@ export class ConversionDump {
142
156
  if (this.attributeMetadataInfo) {
143
157
  delete this.attributeMetadataInfo;
144
158
  }
145
- if (this.materialDefinitions.length > 0) {
146
- this.materialDefinitions = [];
159
+ if (this.materialDefinitions) {
160
+ delete this.materialDefinitions;
147
161
  }
148
162
  }
149
163
 
@@ -153,6 +167,7 @@ export class ConversionDump {
153
167
  private async updateDumpFile(): Promise<void> {
154
168
  if (this.options?.outputPath && this.options.tilesetName) {
155
169
  try {
170
+ const time = process.hrtime();
156
171
  await writeFile(
157
172
  join(this.options.outputPath, this.options.tilesetName),
158
173
  JSON.stringify({
@@ -162,7 +177,19 @@ export class ConversionDump {
162
177
  attributeMetadataInfo: this.attributeMetadataInfo,
163
178
  materialDefinitions: this.materialDefinitions
164
179
  }),
165
- `${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
180
+ `${this.options.tilesetName}${DUMP_FILE_SUFFIX}.${time[0]}.${time[1]}`
181
+ );
182
+ await renameFile(
183
+ join(
184
+ this.options.outputPath,
185
+ this.options.tilesetName,
186
+ `${this.options.tilesetName}${DUMP_FILE_SUFFIX}.${time[0]}.${time[1]}`
187
+ ),
188
+ join(
189
+ this.options.outputPath,
190
+ this.options.tilesetName,
191
+ `${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
192
+ )
166
193
  );
167
194
  } catch (error) {
168
195
  console.log("Can't update dump file", error);
@@ -218,9 +245,9 @@ export class ConversionDump {
218
245
  * @param fileName - source filename
219
246
  * @param nodeId - nodeId of the node
220
247
  */
221
- async addNode(filename: string, nodeId: number, dumpMetadata: DumpMetadata) {
248
+ async addNode(filename: string, nodeId: number | string, dumpMetadata?: DumpMetadata) {
222
249
  const {nodes} = this.getRecord(filename) || {nodes: []};
223
- nodes.push({nodeId, done: false, progress: {}, dumpMetadata});
250
+ nodes.push({nodeId, done: false, dumpMetadata});
224
251
  if (nodes.length === 1) {
225
252
  this.setRecord(filename, {nodes});
226
253
  }
@@ -250,7 +277,12 @@ export class ConversionDump {
250
277
  * @param resourceType - resource type to update status
251
278
  * @param value - value
252
279
  */
253
- updateDoneStatus(filename: string, nodeId: number, resourceType: string, value: boolean) {
280
+ updateDoneStatus(
281
+ filename: string,
282
+ nodeId: number | string,
283
+ resourceType: string,
284
+ value: boolean
285
+ ) {
254
286
  const nodeDump = this.tilesConverted[filename]?.nodes.find(
255
287
  (element) => element.nodeId === nodeId
256
288
  );
@@ -271,7 +303,7 @@ export class ConversionDump {
271
303
  * @param writeResults - array of writing resource files results
272
304
  */
273
305
  async updateConvertedTilesDump(
274
- changedRecords: {outputId?: number; sourceId?: string; resourceType?: string}[],
306
+ changedRecords: {outputId?: number | string; sourceId?: string; resourceType?: string}[],
275
307
  writeResults: PromiseSettledResult<string | null>[]
276
308
  ) {
277
309
  for (let i = 0; i < changedRecords.length; i++) {
@@ -299,6 +331,26 @@ export class ConversionDump {
299
331
  await this.updateDumpFile();
300
332
  }
301
333
 
334
+ /**
335
+ * Update 3d-tiles-converter dump file
336
+ * @param filename - source filename
337
+ * @param nodeId - nodeId
338
+ * @param done - conversion status
339
+ */
340
+ async updateConvertedNodesDumpFile(
341
+ filename: string,
342
+ nodeId: number | string,
343
+ done: boolean
344
+ ): Promise<void> {
345
+ const nodeDump = this.tilesConverted[filename]?.nodes.find(
346
+ (element) => element.nodeId === nodeId
347
+ );
348
+ if (nodeDump) {
349
+ nodeDump.done = done;
350
+ await this.updateDumpFile();
351
+ }
352
+ }
353
+
302
354
  /**
303
355
  * Check is source file conversion complete
304
356
  * @param filename - source filename
@@ -138,3 +138,16 @@ export function removeFile(path: string) {
138
138
  export function getAbsoluteFilePath(filePath: string) {
139
139
  return isAbsolute(filePath) ? filePath : join(process.cwd(), filePath);
140
140
  }
141
+
142
+ /**
143
+ * Rename file with old path by new path
144
+ * @param oldPath
145
+ * @param newPath
146
+ */
147
+ export async function renameFile(oldPath: string, newPath: string): Promise<void> {
148
+ try {
149
+ await fs.rename(oldPath, newPath);
150
+ } catch (err) {
151
+ console.log("Can't rename file", err);
152
+ }
153
+ }