Skip to content

Commit

Permalink
Merge pull request #143 from CesiumGS/fix-merge-path-handling
Browse files Browse the repository at this point in the history
Fix merge path handling
  • Loading branch information
lilleyse authored Jul 24, 2024
2 parents 2a47318 + 4080cff commit b9ce25a
Show file tree
Hide file tree
Showing 4 changed files with 279 additions and 75 deletions.
165 changes: 145 additions & 20 deletions specs/tools/tilesetProcessing/TilesetMergerSpec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,31 +7,39 @@ import { TilesetOperations } from "../../../src/tools";
import { SpecHelpers } from "../../SpecHelpers";

const SPECS_DATA_BASE_DIRECTORY = SpecHelpers.getSpecsDataBaseDirectory();

const basicInputs = [
SPECS_DATA_BASE_DIRECTORY + "/mergeTilesets/basicMerge/TilesetA",
SPECS_DATA_BASE_DIRECTORY + "/mergeTilesets/basicMerge/sub/TilesetA",
];
const basicOutput =
SPECS_DATA_BASE_DIRECTORY + "/output/mergeTilesets/basicMerge";
const overwrite = true;

describe("TilesetMerger", function () {
afterEach(function () {
SpecHelpers.forceDeleteDirectory(
SPECS_DATA_BASE_DIRECTORY + "/output/mergeTilesets"
Paths.join(SPECS_DATA_BASE_DIRECTORY, "output/mergeTilesets")
);
});
it("merges tilesets from directories into a single tileset directory", async function () {
const inputDirectories = [
Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"mergeTilesets/basicMerge/TilesetA"
),
Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"mergeTilesets/basicMerge/sub/TilesetA"
),
];
const outputDirectory = Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"output/mergeTilesets/basicMerge"
);
const outputFile = Paths.join(outputDirectory, "tileset.json");

it("merges tilesets into a single tileset", async function () {
await TilesetOperations.merge(basicInputs, basicOutput, overwrite);
await TilesetOperations.merge(inputDirectories, outputDirectory, overwrite);

// Ensure that the output directory contains the expected files:
// All files of the input, disambiguated for the same base name
// (i.e. "TilesetA" and "TilesetA-0" - this is not specified,
// but has to be assumed here)
const actualRelativeFiles =
SpecHelpers.collectRelativeFileNames(basicOutput);
SpecHelpers.collectRelativeFileNames(outputDirectory);
actualRelativeFiles.sort();
const expectedRelativeFiles = [
"TilesetA-0/ll.b3dm",
Expand All @@ -52,9 +60,66 @@ describe("TilesetMerger", function () {

// 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 tilesetJsonBuffer = fs.readFileSync(outputFile);
const tileset = JSON.parse(tilesetJsonBuffer.toString());
const actualContentUris = await SpecHelpers.collectExplicitContentUris(
tileset.root
);
actualContentUris.sort();

const expectedContentUris = [
"TilesetA-0/tileset.json",
"TilesetA/tileset.json",
];
expect(actualContentUris).toEqual(expectedContentUris);
});

it("merges tilesets from files into a single tileset file", async function () {
const inputFiles = [
Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"mergeTilesets/basicMerge/TilesetA/tileset.json"
),
Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"mergeTilesets/basicMerge/sub/TilesetA/tileset.json"
),
];
const outputDirectory = Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"output/mergeTilesets/basicMerge"
);
const outputFile = Paths.join(outputDirectory, "tileset.json");

await TilesetOperations.merge(inputFiles, outputFile, overwrite);

// Ensure that the output directory contains the expected files:
// All files of the input, disambiguated for the same base name
// (i.e. "TilesetA" and "TilesetA-0" - this is not specified,
// but has to be assumed here)
const actualRelativeFiles =
SpecHelpers.collectRelativeFileNames(outputDirectory);
actualRelativeFiles.sort();
const expectedRelativeFiles = [
"TilesetA-0/ll.b3dm",
"TilesetA-0/lr.b3dm",
"TilesetA-0/parent.b3dm",
"TilesetA-0/tileset.json",
"TilesetA-0/ul.b3dm",
"TilesetA-0/ur.b3dm",
"TilesetA/ll.b3dm",
"TilesetA/lr.b3dm",
"TilesetA/parent.b3dm",
"TilesetA/tileset.json",
"TilesetA/ul.b3dm",
"TilesetA/ur.b3dm",
"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(outputFile);
const tileset = JSON.parse(tilesetJsonBuffer.toString());
const actualContentUris = await SpecHelpers.collectExplicitContentUris(
tileset.root
Expand All @@ -68,30 +133,90 @@ describe("TilesetMerger", function () {
expect(actualContentUris).toEqual(expectedContentUris);
});

it("merges tilesets into a single tileset for mergeJson", async function () {
await TilesetOperations.mergeJson(basicInputs, basicOutput, overwrite);
it("merges tilesets from directories into a single tileset in a directory for mergeJson", async function () {
const inputDirectories = [
Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"mergeTilesets/basicMerge/TilesetA"
),
Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"mergeTilesets/basicMerge/sub/TilesetA"
),
];
const outputDirectory = Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"output/mergeTilesets/basicMerge"
);
const outputFile = Paths.join(outputDirectory, "tileset.json");

await TilesetOperations.mergeJson(
inputDirectories,
outputDirectory,
overwrite
);

// Ensure that the output directory contains the expected files:
const actualRelativeFiles =
SpecHelpers.collectRelativeFileNames(basicOutput);
SpecHelpers.collectRelativeFileNames(outputDirectory);
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 tilesetJsonBuffer = fs.readFileSync(outputFile);
const tileset = JSON.parse(tilesetJsonBuffer.toString());
const actualContentUris = await SpecHelpers.collectExplicitContentUris(
tileset.root
);
actualContentUris.sort();

const expectedContentUris = [
"../../../mergeTilesets/basicMerge/TilesetA/tileset.json",
"../../../mergeTilesets/basicMerge/sub/TilesetA/tileset.json",
];
expect(actualContentUris).toEqual(expectedContentUris);
});

it("merges tilesets from files into a single tileset file for mergeJson", async function () {
const inputFiles = [
Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"mergeTilesets/basicMerge/TilesetA/tileset.json"
),
Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"mergeTilesets/basicMerge/sub/TilesetA/tileset.json"
),
];
const outputDirectory = Paths.join(
SPECS_DATA_BASE_DIRECTORY,
"output/mergeTilesets/basicMerge"
);
const outputFile = Paths.join(outputDirectory, "tileset.json");

await TilesetOperations.mergeJson(inputFiles, outputFile, overwrite);

// Ensure that the output directory contains the expected files:
const actualRelativeFiles =
SpecHelpers.collectRelativeFileNames(outputDirectory);
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(outputFile);
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",
"../../../mergeTilesets/basicMerge/TilesetA/tileset.json",
"../../../mergeTilesets/basicMerge/sub/TilesetA/tileset.json",
];
expect(actualContentUris).toEqual(expectedContentUris);
});
Expand Down
11 changes: 11 additions & 0 deletions src/base/base/Paths.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,17 @@ export class Paths {
* the result uses `/` forward slashes as the directory
* separator.
*
* Note:
* - The first argument is assumed to be a directory
* - The second argument is assumed to be a file name
* - The result is the relativized file name
*
* For example: In a call like
* `relativize("./example/directoryA", "./example/directoryB/file.txt")`
* the second argument has to be the file name, and the result
* will be "../directoryB/file.txt", which is the path of the
* file _relative to_ the directory that is given as the first argument.
*
* @param directory - The directory
* @param fileName - The file name
* @returns The resulting path
Expand Down
34 changes: 34 additions & 0 deletions src/tilesets/tilesets/Tilesets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,40 @@ export class Tilesets {
return "tileset.json";
}

/**
* Determine a directory name from the given tileset name.
*
* When the given name ends with `.json`, `.3tz`, or `.3dtiles`
* (case-insensitively), then the directory name of that file
* name is returned. Otherwise, the given name is assumed to
* be a directory name and returned directly.
*
* NOTE: This is working around the ambiguity that is related to
* https://github.com/CesiumGS/3d-tiles/issues/184 : When someone
* uses a path like "./data/target" as a target name, then it is
* not clear whether the result should be
*
* - a JSON file "./data/target" (without extension)
* - a file called "./data/target/tileset.json"
*
* The latter is the default assumption that is used for source
* data: When the source is a directory, then it assumes
* that there is a 'tileset.json' file in this directory.
*
* For the target, checking whether there is a file extension
* seems to be a reasonable workaround.
*
* @param tilesetName - The tileset name
* @returns The directory name
*/
static determineTilesetDirectoryName(tilesetName: string) {
const n = tilesetName.toLowerCase();
if (n.endsWith(".json") || n.endsWith(".3tz") || n.endsWith(".3dtiles")) {
return path.dirname(tilesetName);
}
return tilesetName;
}

/**
* Returns whether the given names likely refer to the same package.
*
Expand Down
Loading

0 comments on commit b9ce25a

Please sign in to comment.