From 75d8909b01ec5671d3e637f392850dedc64e351b Mon Sep 17 00:00:00 2001 From: Reto Kaiser Date: Wed, 28 Aug 2024 20:43:16 +0200 Subject: [PATCH] Refactor tests to directly check the CLI interface --- docs/background.md | 2 +- package-lock.json | 94 ++++++++++++++--- package.json | 3 +- src/3dtiles/BatchTable.mjs | 34 +++--- src/3dtiles/BatchTable.test.mjs | 44 ++++---- src/3dtiles/Batched3DModel.mjs | 2 +- src/3dtiles/parseB3dm.mjs | 76 ++++++++++++++ src/Converter.mjs | 2 +- src/Converter.test.mjs | 154 --------------------------- test/cli.test.mjs | 180 ++++++++++++++++++++++++++++++++ 10 files changed, 377 insertions(+), 214 deletions(-) create mode 100644 src/3dtiles/parseB3dm.mjs delete mode 100644 src/Converter.test.mjs create mode 100644 test/cli.test.mjs diff --git a/docs/background.md b/docs/background.md index 6674acf..d0bf49f 100644 --- a/docs/background.md +++ b/docs/background.md @@ -109,5 +109,5 @@ Together these two pieces of information (models' geometry and models' propertie [citygml]: https://www.citygml.org/ -[3d-tiles]: https://github.com/AnalyticalGraphicsInc/3d-tiles +[3d-tiles]: https://github.com/CesiumGS/3d-tiles [gltf]: https://www.khronos.org/gltf/ diff --git a/package-lock.json b/package-lock.json index fd7309b..22ba202 100644 --- a/package-lock.json +++ b/package-lock.json @@ -27,6 +27,7 @@ }, "devDependencies": { "chai": "^4.1.2", + "fs-jetpack": "^5.1.0", "mocha": "^10.1.0" }, "engines": { @@ -853,6 +854,36 @@ "universalify": "^0.1.0" } }, + "node_modules/fs-jetpack": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-5.1.0.tgz", + "integrity": "sha512-Xn4fDhLydXkuzepZVsr02jakLlmoARPy+YWIclo4kh0GyNGUHnTqeH/w/qIsVn50dFxtp8otPL2t/HcPJBbxUA==", + "dev": true, + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/fs-jetpack/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/fs-jetpack/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1036,12 +1067,9 @@ } }, "node_modules/graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=", - "engines": { - "node": ">=0.4.0" - } + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/har-schema": { "version": "2.0.0", @@ -1468,7 +1496,7 @@ "node_modules/jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -5508,9 +5536,12 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "node_modules/universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } }, "node_modules/uri-js": { "version": "4.4.1", @@ -6437,6 +6468,35 @@ "universalify": "^0.1.0" } }, + "fs-jetpack": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-5.1.0.tgz", + "integrity": "sha512-Xn4fDhLydXkuzepZVsr02jakLlmoARPy+YWIclo4kh0GyNGUHnTqeH/w/qIsVn50dFxtp8otPL2t/HcPJBbxUA==", + "dev": true, + "requires": { + "minimatch": "^5.1.0" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -6588,9 +6648,9 @@ } }, "graceful-fs": { - "version": "4.1.11", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.11.tgz", - "integrity": "sha1-Dovf5NHduIVNZOBOp8AOKgJuVlg=" + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "har-schema": { "version": "2.0.0", @@ -6911,7 +6971,7 @@ "jsonfile": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "requires": { "graceful-fs": "^4.1.6" } @@ -9697,9 +9757,9 @@ "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==" }, "universalify": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.1.tgz", - "integrity": "sha1-+nG63UQ3r0wUiEHjs7Fl+enlkLc=" + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" }, "uri-js": { "version": "4.4.1", diff --git a/package.json b/package.json index cd83ab7..2642c5f 100644 --- a/package.json +++ b/package.json @@ -22,7 +22,8 @@ }, "devDependencies": { "chai": "^4.1.2", - "mocha": "^10.1.0" + "mocha": "^10.1.0", + "fs-jetpack": "^5.1.0" }, "scripts": { "test": "node_modules/.bin/mocha" diff --git a/src/3dtiles/BatchTable.mjs b/src/3dtiles/BatchTable.mjs index 76e5ef5..9e2bdbe 100644 --- a/src/3dtiles/BatchTable.mjs +++ b/src/3dtiles/BatchTable.mjs @@ -1,29 +1,29 @@ /** - * @see https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/BatchTable/README.md + * @see https://github.com/CesiumGS/3d-tiles/blob/1.0/specification/TileFormats/BatchTable/README.md */ class BatchTable { constructor () { - this.features = {} + this.items = {} } /** - * @param {String|Number} id + * @param {String|Number} batchId * @param {Object} properties */ - addFeature (id, properties) { - id = String(id) - if (this.features[id]) { - throw new Error('A feature with this ID already exists: ' + id) + addBatchItem (batchId, properties) { + batchId = String(batchId) + if (this.items[batchId]) { + throw new Error('An item with this ID already exists: ' + batchId) } - this.features[id] = properties + this.items[batchId] = properties } /** * @returns {String[]} */ - getIds () { - return Object.keys(this.features) + getBatchIds () { + return Object.keys(this.items) } /** @@ -31,8 +31,8 @@ class BatchTable { */ getPropertyNames () { let propertyNames = {} - for (const id in this.features) { - let properties = this.features[id] + for (const id in this.items) { + let properties = this.items[id] for (const name in properties) { propertyNames[name] = true } @@ -44,7 +44,7 @@ class BatchTable { * @returns {Object} */ getBatchTableJson () { - let ids = this.getIds() + let ids = this.getBatchIds() let propertyNames = this.getPropertyNames() let batchTable = { @@ -52,7 +52,7 @@ class BatchTable { } propertyNames.forEach(name => { batchTable[name] = ids.map((id, i) => { - let value = this.features[id][name] + let value = this.items[id][name] if (typeof value === 'undefined') { value = null } @@ -67,7 +67,7 @@ class BatchTable { * @returns {Number} */ getLength () { - return Object.keys(this.features).length + return Object.keys(this.items).length } /** @@ -75,8 +75,8 @@ class BatchTable { */ getMinMax () { let minmax = {} - for (const id in this.features) { - let properties = this.features[id] + for (const id in this.items) { + let properties = this.items[id] for (const name in properties) { let value = properties[name] if (typeof value === 'number') { diff --git a/src/3dtiles/BatchTable.test.mjs b/src/3dtiles/BatchTable.test.mjs index 0f4e4ab..db77b7d 100644 --- a/src/3dtiles/BatchTable.test.mjs +++ b/src/3dtiles/BatchTable.test.mjs @@ -5,10 +5,10 @@ describe('BatchTable', function () { describe('#addFeature()', function () { it('should throw on duplicate ID', function () { let table = new BatchTable() - table.addFeature('feature1', {foo: 12}) + table.addBatchItem('item1', {foo: 12}) assert.throws(() => { - table.addFeature('feature1', {foo: 13}) + table.addBatchItem('item1', {foo: 13}) }, /already exists/) }) }) @@ -16,10 +16,10 @@ describe('BatchTable', function () { describe('#addFeature()', function () { it('should throw on duplicate ID, once string, once integer', function () { let table = new BatchTable() - table.addFeature('2', {foo: 12}) + table.addBatchItem('2', {foo: 12}) assert.throws(() => { - table.addFeature(2, {foo: 13}) + table.addBatchItem(2, {foo: 13}) }, /already exists/) }) }) @@ -27,20 +27,20 @@ describe('BatchTable', function () { describe('#getIds()', function () { it('should return all IDs', function () { let table = new BatchTable() - table.addFeature('feature1', {foo: 12, bar: 'bar1'}) - table.addFeature('feature2', {foo: 44}) - table.addFeature('feature3', {foo: 99, bar: 'bar3'}) + table.addBatchItem('item1', {foo: 12, bar: 'bar1'}) + table.addBatchItem('item2', {foo: 44}) + table.addBatchItem('item3', {foo: 99, bar: 'bar3'}) - assert.deepEqual(table.getIds(), ['feature1', 'feature2', 'feature3']) + assert.deepEqual(table.getBatchIds(), ['item1', 'item2', 'item3']) }) }) describe('#getPropertyNames()', function () { it('should return all property names', function () { let table = new BatchTable() - table.addFeature('feature1', {foo: 12, bar: 'bar1'}) - table.addFeature('feature2', {foo: 44}) - table.addFeature('feature3', {foo: 99, bar: 'bar3'}) + table.addBatchItem('item1', {foo: 12, bar: 'bar1'}) + table.addBatchItem('item2', {foo: 44}) + table.addBatchItem('item3', {foo: 99, bar: 'bar3'}) assert.deepEqual(table.getPropertyNames(), ['foo', 'bar']) }) @@ -49,15 +49,15 @@ describe('BatchTable', function () { describe('#getBatchTableJson()', function () { it('should return a valid batch table', function () { let table = new BatchTable() - table.addFeature('feature1', {foo: 12, bar: 'bar1'}) - table.addFeature('feature2', {foo: 44}) - table.addFeature('feature3', {foo: 99, bar: 'bar3'}) + table.addBatchItem('item1', {foo: 12, bar: 'bar1'}) + table.addBatchItem('item2', {foo: 44}) + table.addBatchItem('item3', {foo: 99, bar: 'bar3'}) assert.deepEqual(table.getBatchTableJson(), { foo: [12, 44, 99], bar: ['bar1', null, 'bar3'], - id: ['feature1', 'feature2', 'feature3'], + id: ['item1', 'item2', 'item3'], }) }) }) @@ -65,9 +65,9 @@ describe('BatchTable', function () { describe('#getLength()', function () { it('should return the number of features', function () { let table = new BatchTable() - table.addFeature('feature1', {foo: 12, bar: 'bar1'}) - table.addFeature('feature2', {foo: 44}) - table.addFeature('feature3', {foo: 99, bar: 'bar3'}) + table.addBatchItem('item1', {foo: 12, bar: 'bar1'}) + table.addBatchItem('item2', {foo: 44}) + table.addBatchItem('item3', {foo: 99, bar: 'bar3'}) assert.equal(table.getLength(), 3) }) @@ -76,10 +76,10 @@ describe('BatchTable', function () { describe('#getMinMax()', function () { it('should return the minimum and maximum values of numeric properties', function () { let table = new BatchTable() - table.addFeature('feature4', {foo: 4, bar: 44, str: 'str4'}) - table.addFeature('feature1', {foo: 1, bar: 'str11', str: 'str1'}) - table.addFeature('feature2', {foo: 2}) - table.addFeature('feature3', {foo: 3, bar: 33, str: 'str3'}) + table.addBatchItem('item4', {foo: 4, bar: 44, str: 'str4'}) + table.addBatchItem('item1', {foo: 1, bar: 'str11', str: 'str1'}) + table.addBatchItem('item2', {foo: 2}) + table.addBatchItem('item3', {foo: 3, bar: 33, str: 'str3'}) assert.deepEqual(table.getMinMax(), { foo: {minimum: 1, maximum: 4}, diff --git a/src/3dtiles/Batched3DModel.mjs b/src/3dtiles/Batched3DModel.mjs index 61bf564..c58cac0 100644 --- a/src/3dtiles/Batched3DModel.mjs +++ b/src/3dtiles/Batched3DModel.mjs @@ -3,7 +3,7 @@ import createB3dm from './createB3dm.mjs' import Cesium from 'cesium' /** - * @see https://github.com/AnalyticalGraphicsInc/3d-tiles/blob/master/TileFormats/Batched3DModel/README.md + * @see https://github.com/CesiumGS/3d-tiles/blob/1.0/specification/TileFormats/Batched3DModel/README.md */ class Batched3DModel { /** diff --git a/src/3dtiles/parseB3dm.mjs b/src/3dtiles/parseB3dm.mjs new file mode 100644 index 0000000..884f0d9 --- /dev/null +++ b/src/3dtiles/parseB3dm.mjs @@ -0,0 +1,76 @@ +export default parseB3dm + +/** + * Parse a Batched 3D Model (b3dm) from binary data + * + * @see https://github.com/CesiumGS/3d-tiles/blob/1.0/specification/TileFormats/Batched3DModel/README.md + * + * @param {Buffer} buffer + * @returns {ParsedB3dm} The parsed b3dm model + */ +function parseB3dm(buffer) { + let headerLength = 28; + if (buffer.length < headerLength) { + throw new Error(`Expected ${headerLength} bytes but only got ${buffer.length}`); + } + + // Read header + const byteLength = buffer.readUInt32LE(8); + let featureTableJsonLength = buffer.readUInt32LE(12); + let featureTableBinaryLength = buffer.readUInt32LE(16); + let batchTableJsonLength = buffer.readUInt32LE(20); + let batchTableBinaryLength = buffer.readUInt32LE(24); + + // Calculate start/end of the sections + const featureTableJsonStart = headerLength; + const featureTableJsonEnd = featureTableJsonStart + featureTableJsonLength; + const featureTableBinaryStart = featureTableJsonEnd; + const featureTableBinaryEnd = featureTableBinaryStart + featureTableBinaryLength; + const batchTableJsonStart = featureTableBinaryEnd; + const batchTableJsonEnd = batchTableJsonStart + batchTableJsonLength; + const batchTableBinaryStart = batchTableJsonEnd; + const batchTableBinaryEnd = batchTableBinaryStart + batchTableBinaryLength; + const payloadStart = batchTableBinaryEnd; + const payloadEnd = byteLength; + + // Extract the data + const batchTableJsonBuffer = buffer.subarray(batchTableJsonStart, batchTableJsonEnd); + const payloadBuffer = buffer.subarray(payloadStart, payloadEnd); + + // Produce the parsed b3dm representation + const batchTableJson = parseJsonFromBuffer(batchTableJsonBuffer); + return new ParsedB3dm(batchTableJson, payloadBuffer) +} + +/** + * Parses JSON data from the given buffer. + * + * If the given buffer is empty, then an empty object will + * be returned. + * + * @param {Buffer} buffer + * @returns The parsed object + * @throws DataError If the JSON could not be parsed + */ +function parseJsonFromBuffer(buffer) { + if (buffer.length === 0) { + return {}; + } + try { + return JSON.parse(buffer.toString("utf8")); + } catch (e) { + throw new Error(`Could not parse JSON from buffer: ${e}`); + } +} + + +class ParsedB3dm { + /** + * @param {Object} batchTable + * @param {Buffer} gltf + */ + constructor(batchTable, gltf) { + this.batchTable = batchTable + this.gltf = gltf + } +} diff --git a/src/Converter.mjs b/src/Converter.mjs index bb1b8cd..d6c9191 100644 --- a/src/Converter.mjs +++ b/src/Converter.mjs @@ -78,7 +78,7 @@ class Converter { let batchTable = new BatchTable() cityObjects.forEach((cityObject, i) => { - batchTable.addFeature(i, this._getProperties(cityObject)) + batchTable.addBatchItem(i, this._getProperties(cityObject)) }) let gltf = await createGltf({ diff --git a/src/Converter.test.mjs b/src/Converter.test.mjs deleted file mode 100644 index f979b20..0000000 --- a/src/Converter.test.mjs +++ /dev/null @@ -1,154 +0,0 @@ -import CityDocument from './citygml/Document.mjs' -import Converter from './Converter.mjs' -import chai from 'chai' - -describe('Converter', async function () { - this.timeout(10000) - - describe('zurich-lod2-citygml1.xml', () => { - let tilesetJson - let b3dm - - before(async () => { - let converter = new Converter() - let cityDocument = CityDocument.fromFile('test/data/zurich-lod2-citygml1.xml') - let tileset = await converter.convertCityDocument(cityDocument) - - tilesetJson = tileset.getJson('foo.b3dm') - b3dm = tileset.getBatched3DModel() - }) - - it('should create a non-emty tileset', () => { - chai.assert.isNotEmpty(tilesetJson) - }) - - it('should create a JSON-parseable tileset', () => { - chai.assert.isNotEmpty(JSON.parse(tilesetJson)) - }) - - it('should create a non-emty B3DM buffer', async () => { - chai.assert.isNotEmpty(await b3dm.getBuffer()) - }) - - it('should create the correct B3DM region', () => { - chai.assert.deepEqual(b3dm.getRegion(), [ - 0.14907155162642596, - 0.826804434491562, - 0.14908311706269073, - 0.8268148017875332, - 403.19936, - 432.65393, - ]) - }) - - it('should create the correct BatchTable', () => { - chai.assert.deepEqual(b3dm.getBatchTable().getBatchTableJson(), { - 'id': ['0', '1', '2', '3'], - 'GID': ['z41ac39cc00001bb7', 'z45b11a6a00002f9e', 'z41ac39cc00001a71', 'z46f29566000001f7'], - 'EGID': ['302040712', '2372759', '302040570', '140715'], - 'E:\\Working\\08_20120322\\COWI_LoD2_08_20120322_part06.xml': ['z41ac39cc00001bb7', 'z45b11a6a00002f9e', 'z41ac39cc00001a71', 'z46f29566000001f7'], - 'Region': [8, 8, 8, 8], - 'QualitaetStatus': [1, 1, 1, 1], - 'Geomtype': [1, 2, 1, 2] - }) - }) - }) - - describe('sig3d-genericattributes-citygml2.xml', () => { - let tilesetJson - let b3dm - - before(async () => { - let converter = new Converter() - let cityDocument = CityDocument.fromFile('test/data/sig3d-genericattributes-citygml2.xml') - let tileset = await converter.convertCityDocument(cityDocument) - - tilesetJson = tileset.getJson('foo.b3dm') - b3dm = tileset.getBatched3DModel() - }) - - it('should create a non-emty tileset', () => { - chai.assert.isNotEmpty(tilesetJson) - }) - - it('should create a JSON-parseable tileset', () => { - chai.assert.isNotEmpty(JSON.parse(tilesetJson)) - }) - - it('should create a non-emty B3DM buffer', async () => { - chai.assert.isNotEmpty(await b3dm.getBuffer()) - }) - - it('should create the correct B3DM region', () => { - chai.assert.deepEqual(b3dm.getRegion(), [ - -0.8846020828104612, - 1.5515922059263958, - -0.8845940828005763, - 1.551593676293702, - 0, - 9, - ]) - }) - - it('should create the correct BatchTable', () => { - chai.assert.deepEqual(b3dm.getBatchTable().getBatchTableJson(), { - 'id': ['0'], - 'Bauweise': ['Massivbau'], - 'Anzahl der Eingänge': [3], - 'Grundflächenzahl GFZ': [0.33], - 'Datum der Baufreigabe': ['2012-03-09'], - 'Web Seite': ['http://www.sig3d.org'], - 'Breite des Gebäudes': [20.75], - 'Höhe': [9], - 'Grundflächen': [140], - 'Volumen': [1260], - } - ) - }) - }) - - describe('delft-citygml2.xml', () => { - let tilesetJson - let b3dm - - before(async () => { - let converter = new Converter() - let cityDocument = CityDocument.fromFile('test/data/delft-citygml2.xml') - let tileset = await converter.convertCityDocument(cityDocument) - - tilesetJson = tileset.getJson('foo.b3dm') - b3dm = tileset.getBatched3DModel() - }) - - it('should create a non-emty tileset', () => { - chai.assert.isNotEmpty(tilesetJson) - }) - - it('should create a JSON-parseable tileset', () => { - chai.assert.isNotEmpty(JSON.parse(tilesetJson)) - }) - - it('should create a non-emty B3DM buffer', async () => { - chai.assert.isNotEmpty(await b3dm.getBuffer()) - }) - - it('should create the correct B3DM region', () => { - chai.assert.deepEqual(b3dm.getRegion(), [ - 0.07601011853885127, - 0.9075773939506161, - 0.07648148496824216, - 0.9078300429446181, - 0, - 100, - ]) - }) - - it('should create the correct BatchTable', () => { - chai.assert.deepEqual(b3dm.getBatchTable().getBatchTableJson(), { - 'id': ['0', '1', '2'], - } - ) - }) - }) - -}) diff --git a/test/cli.test.mjs b/test/cli.test.mjs new file mode 100644 index 0000000..471b7e5 --- /dev/null +++ b/test/cli.test.mjs @@ -0,0 +1,180 @@ +import chai from 'chai' +import fsJetpack from 'fs-jetpack' +import fs from 'node:fs' +import {promisify} from 'node:util' +import {exec} from 'node:child_process' +import parseB3dm from "../src/3dtiles/parseB3dm.mjs"; + +const execAsync = promisify(exec) + +describe('CLI', async function () { + this.timeout(10000) + + describe('zurich-lod2-citygml1.xml', () => { + let outputFolder + let tilesetJson + let b3dmParsed + + before(async () => { + let inputPath = 'test/data/zurich-lod2-citygml1.xml' + outputFolder = fsJetpack.tmpDir({prefix: 'citygml-to-3dtiles---tests---'}) + await execAsync(`./bin/citygml-to-3dtiles.mjs '${inputPath}' '${outputFolder.path()}'`) + + tilesetJson = JSON.parse(fs.readFileSync(outputFolder.path('tileset.json'))) + b3dmParsed = parseB3dm(fs.readFileSync(outputFolder.path('full.b3dm'))) + }) + + after(async () => { + outputFolder.remove() + }) + + it('The tileset should have the expected content', () => { + chai.assert.deepEqual(tilesetJson, { + "asset": { + "version": "0.0", + }, + "geometricError": 99, + "properties": { + "Geomtype": { + "maximum": 2, + "minimum": 1, + }, + "QualitaetStatus": { + "maximum": 1, + "minimum": 1, + }, + "Region": { + "maximum": 8, + "minimum": 8, + }, + }, + "root": { + "boundingVolume": { + "region": [ + 0.14907155162642596, + 0.826804434491562, + 0.14908311706269073, + 0.8268148017875332, + 403.19936, + 432.65393, + ], + }, + "content": { + "url": "full.b3dm", + }, + "geometricError": 0, + "refine": "ADD", + } + }) + }) + + it('The B3DM-gltf should look reasonable', async () => { + chai.assert.include(b3dmParsed.gltf.toString(), 'void main(') + }) + + it('The B3DM-BatchTable should have the expected content', () => { + chai.assert.deepEqual(b3dmParsed.batchTable, { + 'id': ['0', '1', '2', '3'], + 'GID': ['z41ac39cc00001bb7', 'z45b11a6a00002f9e', 'z41ac39cc00001a71', 'z46f29566000001f7'], + 'EGID': ['302040712', '2372759', '302040570', '140715'], + 'E:\\Working\\08_20120322\\COWI_LoD2_08_20120322_part06.xml': ['z41ac39cc00001bb7', 'z45b11a6a00002f9e', 'z41ac39cc00001a71', 'z46f29566000001f7'], + 'Region': [8, 8, 8, 8], + 'QualitaetStatus': [1, 1, 1, 1], + 'Geomtype': [1, 2, 1, 2] + }) + }) + }) + + + describe('sig3d-genericattributes-citygml2.xml', () => { + let outputFolder + let tilesetJson + let b3dmParsed + + before(async () => { + let inputPath = 'test/data/sig3d-genericattributes-citygml2.xml' + outputFolder = fsJetpack.tmpDir({prefix: 'citygml-to-3dtiles---tests---'}) + await execAsync(`./bin/citygml-to-3dtiles.mjs '${inputPath}' '${outputFolder.path()}'`) + + tilesetJson = JSON.parse(fs.readFileSync(outputFolder.path('tileset.json'))) + b3dmParsed = parseB3dm(fs.readFileSync(outputFolder.path('full.b3dm'))) + }) + + after(async () => { + outputFolder.remove() + }) + + it('The tileset should have the expected region', () => { + chai.assert.deepEqual(tilesetJson['root']['boundingVolume']['region'], [ + -0.8846020828104612, + 1.5515922059263958, + -0.8845940828005763, + 1.551593676293702, + 0, + 9, + ]) + }) + + it('The B3DM-gltf should look reasonable', async () => { + chai.assert.include(b3dmParsed.gltf.toString(), 'void main(') + }) + + it('The B3DM-BatchTable should have the expected content', () => { + chai.assert.deepEqual(b3dmParsed.batchTable, { + 'id': ['0'], + 'Bauweise': ['Massivbau'], + 'Anzahl der Eingänge': [3], + 'Grundflächenzahl GFZ': [0.33], + 'Datum der Baufreigabe': ['2012-03-09'], + 'Web Seite': ['http://www.sig3d.org'], + 'Breite des Gebäudes': [20.75], + 'Höhe': [9], + 'Grundflächen': [140], + 'Volumen': [1260], + } + ) + }) + }) + + describe('delft-citygml2.xml', () => { + let outputFolder + let tilesetJson + let b3dmParsed + + before(async () => { + let inputPath = 'test/data/delft-citygml2.xml' + outputFolder = fsJetpack.tmpDir({prefix: 'citygml-to-3dtiles---tests---'}) + await execAsync(`./bin/citygml-to-3dtiles.mjs '${inputPath}' '${outputFolder.path()}'`) + + tilesetJson = JSON.parse(fs.readFileSync(outputFolder.path('tileset.json'))) + b3dmParsed = parseB3dm(fs.readFileSync(outputFolder.path('full.b3dm'))) + }) + + after(async () => { + outputFolder.remove() + }) + + it('The tileset should have the expected region', () => { + chai.assert.deepEqual(tilesetJson['root']['boundingVolume']['region'], [ + 0.07601011853885127, + 0.9075773939506161, + 0.07648148496824216, + 0.9078300429446181, + 0, + 100, + ]) + }) + + it('The B3DM-gltf should look reasonable', async () => { + chai.assert.include(b3dmParsed.gltf.toString(), 'void main(') + }) + + it('The B3DM-BatchTable should have the expected content', () => { + chai.assert.deepEqual(b3dmParsed.batchTable, { + 'id': ['0', '1', '2'], + } + ) + }) + }) + +})