diff --git a/package.json b/package.json index 7b1ac2b..b59e800 100644 --- a/package.json +++ b/package.json @@ -105,4 +105,4 @@ "package-prepare": "npm run eslint && npm run prettier-check && npm run build && npm run test && npm run coverage && npm run docs-generate && npm run generate-third-party", "package": "npm run package-clean && npm run package-prepare && npm pack" } -} +} \ No newline at end of file diff --git a/specs/tools/tilesetProcessing/TilesetMergerSpec.ts b/specs/tools/tilesetProcessing/TilesetMergerSpec.ts index d7a6c54..bd7467e 100644 --- a/specs/tools/tilesetProcessing/TilesetMergerSpec.ts +++ b/specs/tools/tilesetProcessing/TilesetMergerSpec.ts @@ -67,4 +67,32 @@ describe("TilesetMerger", function () { ]; expect(actualContentUris).toEqual(expectedContentUris); }); + + it("merges tilesets into a single tileset for mergeJson", async function () { + await TilesetOperations.mergeJson(basicInputs, basicOutput, overwrite); + + // Ensure that the output directory contains the expected files: + const actualRelativeFiles = + SpecHelpers.collectRelativeFileNames(basicOutput); + actualRelativeFiles.sort(); + const expectedRelativeFiles = ["tileset.json"]; + expect(actualRelativeFiles).toEqual(expectedRelativeFiles); + + // Ensure that the single 'tileset.json' contains the + // proper content URIs for the external tilesets: + const tilesetJsonBuffer = fs.readFileSync( + Paths.join(basicOutput, "tileset.json") + ); + const tileset = JSON.parse(tilesetJsonBuffer.toString()); + const actualContentUris = await SpecHelpers.collectExplicitContentUris( + tileset.root + ); + actualContentUris.sort(); + + const expectedContentUris = [ + "specs/data/mergeTilesets/basicMerge/TilesetA/tileset.json", + "specs/data/mergeTilesets/basicMerge/sub/TilesetA/tileset.json", + ]; + expect(actualContentUris).toEqual(expectedContentUris); + }); }); diff --git a/src/cli/ToolsMain.ts b/src/cli/ToolsMain.ts index 57050f9..72fcb86 100644 --- a/src/cli/ToolsMain.ts +++ b/src/cli/ToolsMain.ts @@ -565,6 +565,18 @@ export class ToolsMain { logger.debug(`Executing merge DONE`); } + static async mergeJson(inputs: string[], output: string, force: boolean) { + logger.debug(`Executing mergeJson`); + logger.debug(` inputs: ${inputs}`); + logger.debug(` output: ${output}`); + logger.debug(` force: ${force}`); + + ToolsMain.ensureCanWrite(output, force); + await TilesetOperations.mergeJson(inputs, output, force); + + logger.debug(`Executing merge DONE`); + } + static async pipeline(input: string, force: boolean) { logger.debug(`Executing pipeline`); logger.debug(` input: ${input}`); diff --git a/src/cli/main.ts b/src/cli/main.ts index d5e4104..337dfad 100644 --- a/src/cli/main.ts +++ b/src/cli/main.ts @@ -257,7 +257,18 @@ function parseToolArgs(a: string[]) { .command( "merge", "Merge any number of tilesets together into a single tileset.", - { i: inputArrayDefinition, o: outputStringDefinition } + { + i: inputArrayDefinition, + o: outputStringDefinition, + } + ) + .command( + "mergeJson", + "Merge any number of tilesets together into a single tileset without copying resources to output directory.", + { + i: inputArrayDefinition, + o: outputStringDefinition, + } ) .command( "upgrade", @@ -537,6 +548,8 @@ async function runCommand(command: string, toolArgs: any, optionArgs: any) { ); } else if (command === "merge") { await ToolsMain.merge(inputs, output, force); + } else if (command === "mergeJson") { + await ToolsMain.mergeJson(inputs, output, force); } else if (command === "pipeline") { await ToolsMain.pipeline(input, force); } else if (command === "analyze") { diff --git a/src/tools/tilesetProcessing/TilesetMerger.ts b/src/tools/tilesetProcessing/TilesetMerger.ts index 7f64020..1cf7d9e 100644 --- a/src/tools/tilesetProcessing/TilesetMerger.ts +++ b/src/tools/tilesetProcessing/TilesetMerger.ts @@ -81,6 +81,50 @@ export class TilesetMerger { tilesetSourceNames: string[], tilesetTargetName: string, overwrite: boolean + ): Promise { + return this.mergeOperation( + tilesetSourceNames, + tilesetTargetName, + overwrite, + false + ); + } + + /** + * Merges the tileset from the specified sources into one tileset + * that refers to the sources as external ones, and writes the + * result into the given target without copying resources to + * output directory. + * + * @param tilesetSourceNames - The tileset source names + * @param tilesetTargetName - The tileset target name + * @param overwrite - Whether target files should be overwritten + * @returns A promise that resolves when the process is finished + * @throws TilesetError When the input could not be processed + * @throws TilesetError When the output already exists + * and `overwrite` was `false`. + */ + async mergeJson( + tilesetSourceNames: string[], + tilesetTargetName: string, + overwrite: boolean + ): Promise { + return this.mergeOperation( + tilesetSourceNames, + tilesetTargetName, + overwrite, + true + ); + } + + /** + * Internal method to differentiate between `merge` and `mergeJson` + */ + async mergeOperation( + tilesetSourceNames: string[], + tilesetTargetName: string, + overwrite: boolean, + jsonOnly: boolean ): Promise { // Create the sources and target for (const tilesetSourceName of tilesetSourceNames) { @@ -106,7 +150,9 @@ export class TilesetMerger { const tilesetSource = TilesetSources.createAndOpen(tilesetSourceName); this.tilesetSources.push(tilesetSource); this.tilesetSourceJsonFileNames.push(tilesetSourceJsonFileName); - this.tilesetSourceIdentifiers.push(tilesetSourceIdentifier); + this.tilesetSourceIdentifiers.push( + !jsonOnly ? tilesetSourceIdentifier : tilesetSourceName + ); } this.tilesetTargetJsonFileName = @@ -117,7 +163,7 @@ export class TilesetMerger { ); // Perform the actual merge - this.mergeInternal(); + this.mergeInternal(jsonOnly); // Clean up by closing the sources and the target for (const tilesetSource of this.tilesetSources) { @@ -134,7 +180,7 @@ export class TilesetMerger { /** * Internal method for `merge` */ - private mergeInternal() { + private mergeInternal(jsonOnly: boolean) { if ( this.tilesetSources.length == 0 || !this.tilesetTarget || @@ -192,7 +238,7 @@ export class TilesetMerger { ); // Copy the resources from the sources to the target - this.copyResources(); + if (!jsonOnly) this.copyResources(); } /** diff --git a/src/tools/tilesetProcessing/TilesetOperations.ts b/src/tools/tilesetProcessing/TilesetOperations.ts index f05e468..892878a 100644 --- a/src/tools/tilesetProcessing/TilesetOperations.ts +++ b/src/tools/tilesetProcessing/TilesetOperations.ts @@ -61,6 +61,30 @@ export class TilesetOperations { await tilesetMerger.merge(tilesetSourceNames, tilesetTargetName, overwrite); } + /** + * Performs the `mergeJson` command line operation. + * + * @param tilesetSourceName - The tileset source name + * @param tilesetTargetName - The tileset target name + * @param overwrite - Whether the target should be overwritten if + * it already exists + * @returns A promise that resolves when the process is finished + * @throws TilesetError When the input could not be processed, + * or when the output already exists and `overwrite` was `false`. + */ + static async mergeJson( + tilesetSourceNames: string[], + tilesetTargetName: string, + overwrite: boolean + ): Promise { + const tilesetMerger = new TilesetMerger(); + await tilesetMerger.mergeJson( + tilesetSourceNames, + tilesetTargetName, + overwrite + ); + } + /** * Performs the `upgrade` command line operation. *