From 131f3f82b72c08d617994caa8afa8eeead8b1a71 Mon Sep 17 00:00:00 2001 From: Daniel Erbynn Date: Mon, 30 Sep 2024 08:44:10 -0400 Subject: [PATCH 01/14] add initial sample for sheet transformation --- samples.md | 165 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 165 insertions(+) create mode 100644 samples.md diff --git a/samples.md b/samples.md new file mode 100644 index 00000000..01b08fd9 --- /dev/null +++ b/samples.md @@ -0,0 +1,165 @@ +# Example Transformation for sheets + +## Overview + +This example demonstrates how to transform a sheet and all its content into a target iModel using the `SheetTransformer` and `IModelTarget` classes. + +## Example Code + +```typescript +import fs from "fs"; +import path from "path"; +import stream from "stream"; +import { promisify } from "util"; + +import { StudioHost } from "@bentley/studio-apps-backend-api"; +import { + CategorySelector, + DefinitionContainer, + DefinitionModel, + DisplayStyle3d, + DrawingCategory, + DrawingModel, + ECSqlStatement, + IModelDb, + IModelHost, + ModelSelector, + OrthographicViewDefinition, + PhysicalModel, + Sheet, + SheetModel, + SnapshotDb, + SpatialCategory, + TemplateRecipe2d, + TemplateRecipe3d +} from "@itwin/core-backend"; +import { DbResult, Id64Array, Id64String } from "@itwin/core-bentley"; +import { + BisCodeSpec, + Code, + CodeScopeSpec, + DefinitionElementProps, + ElementProps, + GeometricModel2dProps, + IModel, + Placement2d, + Placement3d, + RelatedElement, + SheetProps, + SubCategoryAppearance +} from "@itwin/core-common"; +import { Angle, Point2d, Point3d, Range2d, Range3d, StandardViewIndex, YawPitchRollAngles } from "@itwin/core-geometry"; +import { + IModelExporter, + IModelImporter, + IModelTransformer, + IModelTransformOptions, + TemplateModelCloner +} from "@itwin/imodel-transformer"; +import fetch from "node-fetch"; + +import { CreateSheetProps, SHEET_CONSTANTS } from "../../common/SheetCommandIpc"; +import { logError } from "../util/ErrorUtility"; +import { DPChannelApi } from "./DPChannelApi"; +import { ElementManipApi } from "./ElementManipApi"; +import { ModelManipApi } from "./ModelManipApi"; +export namespace SheetApi { + const finished = promisify(stream.finished); + async function downloadFile(fileUrl: string, outputPath: string): Promise { + const response = await fetch(fileUrl); + if (!response.ok) throw new Error(`Failed to fetch ${fileUrl}: ${response.statusText}`); + const fileStream = fs.createWriteStream(outputPath); + if (response.body) { + response.body.pipe(fileStream); + } + await finished(fileStream); + } + + async function downloadDocuments(documents: any, downloadFolder: string): Promise { + for (const doc of documents.documents) { + console.log(doc, downloadFolder); + const fileUrl = doc._links.fileUrl.href; + const fileName = `${doc.displayName}.${doc.extension}`; + const outputPath = path.join(downloadFolder, fileName); + try { + await downloadFile(fileUrl, outputPath); + console.log(`Downloaded ${fileName} to ${downloadFolder}`); + } catch (error) { + console.error(`Failed to download file: ${fileName}`, error); + } + } + } + + export const insertSheet = async (sheetName: string, createSheetProps: CreateSheetProps): Promise => { + const iModel: IModelDb | undefined = StudioHost.getActiveBriefcase(); + let seedDb: SnapshotDb | undefined; + let transformer: IModelTransformer | undefined; + try { + if (!iModel) { + throw new Error("iModelDb undefined"); + } + if (!sheetName) { + throw new Error("A sheet must be named."); + } + // let sheetModelId: Id64String; + // if (createSheetProps.sheetTemplate === "No Template" || createSheetProps.sheetTemplate === "") { + const [sheetModelId, documentListModelId] = await createSheetInternal(createSheetProps, iModel, sheetName); + const seedFileName = + "D:\\testmodels\\transformingSheetsIssue\\source.bim"; + seedDb = SnapshotDb.openFile(seedFileName); + if (!seedDb) { + throw new Error("Failed to open snapshot iModel."); + } + // Get the sheet data from the snapshot + const arr: any = []; + const query = "SELECT * FROM BisCore.Sheet"; + seedDb.withPreparedStatement(query, (statement) => { + while (statement.step() === DbResult.BE_SQLITE_ROW) { + const row = statement.getRow(); + arr.push(row); + } + }); + const importer = new IModelImporter(iModel); + importer.doNotUpdateElementIds.add(documentListModelId); // Do not update the documentListModelId, this is the one we've created for this iModel to receive the sheet template. + transformer = new IModelTransformer(seedDb, importer); + + transformer.context.remapElement("0x20000000009", documentListModelId); + await transformer.processModel("0x20000000009"); + + // Save changes to DB + iModel.saveChanges(); + + const sheetIdInTarget = transformer.context.findTargetElementId(arr[0].id); + const sheetProps = iModel.elements.getElementProps(sheetIdInTarget); + + await iModel.locks.acquireLocks({ shared: documentListModelId }); + iModel.elements.updateElement({...sheetProps, userLabel: sheetName, code: Sheet.createCode(iModel, documentListModelId, sheetName)}); + iModel.saveChanges(); + return sheetModelId; + } catch (error) { + iModel?.abandonChanges(); + const updatedErrMsg = logError(error, "Inserting sheet failed."); + throw new Error(updatedErrMsg); + } finally { + if (seedDb) { + seedDb.close(); + } + if (transformer) { + transformer.dispose(); + } + } + }; + + async function createSheetInternal(createSheetProps: CreateSheetProps, _iModel: IModelDb, _sheetName: string) { + if (createSheetProps.scale <= 0) { + throw new Error("A sheet's scale must be greater than 0."); + } + if (createSheetProps.height <= 0 || createSheetProps.width <= 0) { + throw new Error("A sheet's height and width must be greater than 0."); + } + + // Get or make documentListModelId + const documentListModelId = await DPChannelApi.getOrCreateDocumentList(SHEET_CONSTANTS.documentListName); + + return ["", documentListModelId]; + } \ No newline at end of file From ddc71cce823de044f2a29bc278b458d73f384135 Mon Sep 17 00:00:00 2001 From: Daniel Erbynn Date: Wed, 2 Oct 2024 10:47:58 -0400 Subject: [PATCH 02/14] update to add sheet transformer --- samples.md | 106 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 65 insertions(+), 41 deletions(-) diff --git a/samples.md b/samples.md index 01b08fd9..dbe81c0a 100644 --- a/samples.md +++ b/samples.md @@ -2,7 +2,7 @@ ## Overview -This example demonstrates how to transform a sheet and all its content into a target iModel using the `SheetTransformer` and `IModelTarget` classes. +This example shows how to transform a sheet and all its content into a target iModel using the `SheetTransformer` classes. The goal of this transformation was to copy a 2d sheet and its contents from a bim file to another iModel using the `@itwin/imodel-transformer` client. ## Example Code @@ -21,6 +21,7 @@ import { DrawingCategory, DrawingModel, ECSqlStatement, + Element, IModelDb, IModelHost, ModelSelector, @@ -33,7 +34,7 @@ import { TemplateRecipe2d, TemplateRecipe3d } from "@itwin/core-backend"; -import { DbResult, Id64Array, Id64String } from "@itwin/core-bentley"; +import { DbResult, Guid, Id64Array, Id64String } from "@itwin/core-bentley"; import { BisCodeSpec, Code, @@ -52,6 +53,7 @@ import { Angle, Point2d, Point3d, Range2d, Range3d, StandardViewIndex, YawPitchR import { IModelExporter, IModelImporter, + IModelImportOptions, IModelTransformer, IModelTransformOptions, TemplateModelCloner @@ -63,32 +65,8 @@ import { logError } from "../util/ErrorUtility"; import { DPChannelApi } from "./DPChannelApi"; import { ElementManipApi } from "./ElementManipApi"; import { ModelManipApi } from "./ModelManipApi"; -export namespace SheetApi { - const finished = promisify(stream.finished); - async function downloadFile(fileUrl: string, outputPath: string): Promise { - const response = await fetch(fileUrl); - if (!response.ok) throw new Error(`Failed to fetch ${fileUrl}: ${response.statusText}`); - const fileStream = fs.createWriteStream(outputPath); - if (response.body) { - response.body.pipe(fileStream); - } - await finished(fileStream); - } - async function downloadDocuments(documents: any, downloadFolder: string): Promise { - for (const doc of documents.documents) { - console.log(doc, downloadFolder); - const fileUrl = doc._links.fileUrl.href; - const fileName = `${doc.displayName}.${doc.extension}`; - const outputPath = path.join(downloadFolder, fileName); - try { - await downloadFile(fileUrl, outputPath); - console.log(`Downloaded ${fileName} to ${downloadFolder}`); - } catch (error) { - console.error(`Failed to download file: ${fileName}`, error); - } - } - } +export namespace SheetApi { export const insertSheet = async (sheetName: string, createSheetProps: CreateSheetProps): Promise => { const iModel: IModelDb | undefined = StudioHost.getActiveBriefcase(); @@ -101,8 +79,8 @@ export namespace SheetApi { if (!sheetName) { throw new Error("A sheet must be named."); } - // let sheetModelId: Id64String; - // if (createSheetProps.sheetTemplate === "No Template" || createSheetProps.sheetTemplate === "") { + + // create a blank sheetModelId(where we will insert the sheet data), create documentListModel (where we will insert list of document elements) const [sheetModelId, documentListModelId] = await createSheetInternal(createSheetProps, iModel, sheetName); const seedFileName = "D:\\testmodels\\transformingSheetsIssue\\source.bim"; @@ -110,7 +88,8 @@ export namespace SheetApi { if (!seedDb) { throw new Error("Failed to open snapshot iModel."); } - // Get the sheet data from the snapshot + + // Get the sheet data from the snapshot (this will contain the sheet data) const arr: any = []; const query = "SELECT * FROM BisCore.Sheet"; seedDb.withPreparedStatement(query, (statement) => { @@ -119,22 +98,23 @@ export namespace SheetApi { arr.push(row); } }); + const importer = new IModelImporter(iModel); importer.doNotUpdateElementIds.add(documentListModelId); // Do not update the documentListModelId, this is the one we've created for this iModel to receive the sheet template. - transformer = new IModelTransformer(seedDb, importer); + transformer = new SheetTransformer(seedDb, importer, arr[0].id, sheetName); + // bring all data from this source model into document list model transformer.context.remapElement("0x20000000009", documentListModelId); + + // bring all data(drawing graphics, line styles, etc) in arr[0] to the blank sheetModel + transformer.context.remapElement(arr[0].id, sheetModelId); + + // export contents and sub-models to the target iModel await transformer.processModel("0x20000000009"); // Save changes to DB iModel.saveChanges(); - const sheetIdInTarget = transformer.context.findTargetElementId(arr[0].id); - const sheetProps = iModel.elements.getElementProps(sheetIdInTarget); - - await iModel.locks.acquireLocks({ shared: documentListModelId }); - iModel.elements.updateElement({...sheetProps, userLabel: sheetName, code: Sheet.createCode(iModel, documentListModelId, sheetName)}); - iModel.saveChanges(); return sheetModelId; } catch (error) { iModel?.abandonChanges(); @@ -150,7 +130,31 @@ export namespace SheetApi { } }; - async function createSheetInternal(createSheetProps: CreateSheetProps, _iModel: IModelDb, _sheetName: string) { + class SheetTransformer extends IModelTransformer { + private _sheetIdInSource: Id64String; + private _sheetName: string; + public constructor(sourceDb: IModelDb, target: IModelImporter, sheetIdInSource: Id64String, sheetName: string) { + super(sourceDb, target, { noProvenance: true }); + this._sheetIdInSource = sheetIdInSource; + this._sheetName = sheetName; + } + + // Override to add sheet userLabel, code, federationGuid to the target element + public override onTransformElement(sourceElement: Element): ElementProps { + const targetElementProps = super.onTransformElement(sourceElement); + + // Add userLabel, code, and federationGuid information for target props + if (sourceElement instanceof Sheet && sourceElement.id === this._sheetIdInSource) { + targetElementProps.userLabel = this._sheetName; + targetElementProps.code = Sheet.createCode(this.targetDb, targetElementProps.model, this._sheetName); + } + targetElementProps.federationGuid = Guid.createValue(); // We want each element to have a new federation guid, so that they are not considered the same as the source elements. + + return targetElementProps; + } + } + + async function createSheetInternal(createSheetProps: CreateSheetProps, iModel: IModelDb, sheetName: string) { if (createSheetProps.scale <= 0) { throw new Error("A sheet's scale must be greater than 0."); } @@ -158,8 +162,28 @@ export namespace SheetApi { throw new Error("A sheet's height and width must be greater than 0."); } - // Get or make documentListModelId + // Get or create documentListModel const documentListModelId = await DPChannelApi.getOrCreateDocumentList(SHEET_CONSTANTS.documentListName); - return ["", documentListModelId]; - } \ No newline at end of file + // Acquire locks and create sheet + await iModel.locks.acquireLocks({ shared: documentListModelId }); + + // insert sheet element into iModel + const sheetElementProps: SheetProps = { + ...createSheetProps, + classFullName: Sheet.classFullName, + code: Sheet.createCode(iModel, documentListModelId, sheetName), + model: documentListModelId + }; + const sheetElementId = iModel.elements.insertElement(sheetElementProps); + + // insert sheet model into iModel + const sheetModelProps: GeometricModel2dProps = { + classFullName: SheetModel.classFullName, + modeledElement: { id: sheetElementId, relClassName: "BisCore:ModelModelsElement" } as RelatedElement + }; + const sheetModelId = iModel.models.insertModel(sheetModelProps); + + return [sheetModelId, documentListModelId]; + } +} From f399322443de199997ae2d465bfd953a25fae6a2 Mon Sep 17 00:00:00 2001 From: Daniel Erbynn Date: Wed, 2 Oct 2024 10:49:43 -0400 Subject: [PATCH 03/14] update sample name --- samples.md => sheet-transformation.md | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename samples.md => sheet-transformation.md (100%) diff --git a/samples.md b/sheet-transformation.md similarity index 100% rename from samples.md rename to sheet-transformation.md From 0c9034099944c8d9aa2e213ce445d62544db827c Mon Sep 17 00:00:00 2001 From: Daniel Erbynn Date: Wed, 2 Oct 2024 13:48:26 -0400 Subject: [PATCH 04/14] add unused imports --- sheet-transformation.md | 35 +---------------------------------- 1 file changed, 1 insertion(+), 34 deletions(-) diff --git a/sheet-transformation.md b/sheet-transformation.md index dbe81c0a..ac01ad48 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -7,64 +7,31 @@ This example shows how to transform a sheet and all its content into a target iM ## Example Code ```typescript -import fs from "fs"; -import path from "path"; -import stream from "stream"; -import { promisify } from "util"; -import { StudioHost } from "@bentley/studio-apps-backend-api"; import { - CategorySelector, - DefinitionContainer, - DefinitionModel, - DisplayStyle3d, - DrawingCategory, - DrawingModel, - ECSqlStatement, Element, IModelDb, - IModelHost, - ModelSelector, - OrthographicViewDefinition, - PhysicalModel, Sheet, SheetModel, SnapshotDb, - SpatialCategory, - TemplateRecipe2d, - TemplateRecipe3d } from "@itwin/core-backend"; -import { DbResult, Guid, Id64Array, Id64String } from "@itwin/core-bentley"; +import { DbResult, Guid, Id64String } from "@itwin/core-bentley"; import { - BisCodeSpec, Code, - CodeScopeSpec, - DefinitionElementProps, ElementProps, GeometricModel2dProps, IModel, - Placement2d, - Placement3d, RelatedElement, SheetProps, - SubCategoryAppearance } from "@itwin/core-common"; -import { Angle, Point2d, Point3d, Range2d, Range3d, StandardViewIndex, YawPitchRollAngles } from "@itwin/core-geometry"; import { - IModelExporter, IModelImporter, - IModelImportOptions, IModelTransformer, - IModelTransformOptions, - TemplateModelCloner } from "@itwin/imodel-transformer"; -import fetch from "node-fetch"; import { CreateSheetProps, SHEET_CONSTANTS } from "../../common/SheetCommandIpc"; import { logError } from "../util/ErrorUtility"; import { DPChannelApi } from "./DPChannelApi"; -import { ElementManipApi } from "./ElementManipApi"; -import { ModelManipApi } from "./ModelManipApi"; export namespace SheetApi { From 45007e1506fac9c12323cf37eb1b70e8eec1f1e0 Mon Sep 17 00:00:00 2001 From: derbynn <44528840+derbynn@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:07:29 -0400 Subject: [PATCH 05/14] Update targetElementProps comment Co-authored-by: Nick Tessier <22119573+nick4598@users.noreply.github.com> --- sheet-transformation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sheet-transformation.md b/sheet-transformation.md index ac01ad48..fcd5e1de 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -115,7 +115,7 @@ export namespace SheetApi { targetElementProps.userLabel = this._sheetName; targetElementProps.code = Sheet.createCode(this.targetDb, targetElementProps.model, this._sheetName); } - targetElementProps.federationGuid = Guid.createValue(); // We want each element to have a new federation guid, so that they are not considered the same as the source elements. + targetElementProps.federationGuid = Guid.createValue(); // We want each element to have a new federation guid, so that they are not considered the same as the source elements. This allows us to use the same sheet in the source and create many copies of it in the target if we so choose. return targetElementProps; } From fe61558974dfa206ed9b7134e834687f9d2705aa Mon Sep 17 00:00:00 2001 From: derbynn <44528840+derbynn@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:07:57 -0400 Subject: [PATCH 06/14] Update overview description Co-authored-by: Nick Tessier <22119573+nick4598@users.noreply.github.com> --- sheet-transformation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sheet-transformation.md b/sheet-transformation.md index fcd5e1de..465c5ca0 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -3,6 +3,7 @@ ## Overview This example shows how to transform a sheet and all its content into a target iModel using the `SheetTransformer` classes. The goal of this transformation was to copy a 2d sheet and its contents from a bim file to another iModel using the `@itwin/imodel-transformer` client. +This would be useful if an organization had created an empty sheet with a title block that they wanted to reuse. ## Example Code From 66cd2444f4671c4d5af65efc00de331fd4d891fc Mon Sep 17 00:00:00 2001 From: derbynn <44528840+derbynn@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:09:16 -0400 Subject: [PATCH 07/14] Update createSheetInternal comment Co-authored-by: Nick Tessier <22119573+nick4598@users.noreply.github.com> --- sheet-transformation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sheet-transformation.md b/sheet-transformation.md index 465c5ca0..3be5e361 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -49,6 +49,7 @@ export namespace SheetApi { } // create a blank sheetModelId(where we will insert the sheet data), create documentListModel (where we will insert list of document elements) + // This creates a sheet, sheetModel, and documentListModel in the target iModel which will be updated using the contents of the sheet, sheetModel, and documentListModel in the seedFileName. const [sheetModelId, documentListModelId] = await createSheetInternal(createSheetProps, iModel, sheetName); const seedFileName = "D:\\testmodels\\transformingSheetsIssue\\source.bim"; From 6d6b05381195ccf1e34e1f3abf4669f2888ba90f Mon Sep 17 00:00:00 2001 From: derbynn <44528840+derbynn@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:10:57 -0400 Subject: [PATCH 08/14] Update remap comments Co-authored-by: Nick Tessier <22119573+nick4598@users.noreply.github.com> --- sheet-transformation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sheet-transformation.md b/sheet-transformation.md index 3be5e361..232a2177 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -72,7 +72,7 @@ export namespace SheetApi { importer.doNotUpdateElementIds.add(documentListModelId); // Do not update the documentListModelId, this is the one we've created for this iModel to receive the sheet template. transformer = new SheetTransformer(seedDb, importer, arr[0].id, sheetName); - // bring all data from this source model into document list model + // Tell the transformer to treat the documentListModel id in the source `0x20000000009` as the same as the one we have created in the target. This allows for all children elements of the documentListModel in the source such as the sheetModel to also make their way into the target iModel under the `documentListModel` transformer.context.remapElement("0x20000000009", documentListModelId); // bring all data(drawing graphics, line styles, etc) in arr[0] to the blank sheetModel From 3bb7ca571d9bdb3b3a74231b21680e759342dcc4 Mon Sep 17 00:00:00 2001 From: derbynn <44528840+derbynn@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:11:11 -0400 Subject: [PATCH 09/14] Update remap comments Co-authored-by: Nick Tessier <22119573+nick4598@users.noreply.github.com> --- sheet-transformation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/sheet-transformation.md b/sheet-transformation.md index 232a2177..1fe2ce92 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -75,7 +75,8 @@ export namespace SheetApi { // Tell the transformer to treat the documentListModel id in the source `0x20000000009` as the same as the one we have created in the target. This allows for all children elements of the documentListModel in the source such as the sheetModel to also make their way into the target iModel under the `documentListModel` transformer.context.remapElement("0x20000000009", documentListModelId); - // bring all data(drawing graphics, line styles, etc) in arr[0] to the blank sheetModel + // bring all data(drawing graphics, line styles, etc) in arr[0] to the blank sheetModel. + This is a similar thought process to the documentListModel remapping, we want all children elements of the sheetModel in the source like drawing graphics, line styles etc to end up as children of the sheetModel that we created in the target. transformer.context.remapElement(arr[0].id, sheetModelId); // export contents and sub-models to the target iModel From 6c502ac405649de48162a7372c4f4ea60e0cb481 Mon Sep 17 00:00:00 2001 From: derbynn <44528840+derbynn@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:12:40 -0400 Subject: [PATCH 10/14] Update sheet-transformation.md Co-authored-by: Nick Tessier <22119573+nick4598@users.noreply.github.com> --- sheet-transformation.md | 1 + 1 file changed, 1 insertion(+) diff --git a/sheet-transformation.md b/sheet-transformation.md index 1fe2ce92..17d4be5a 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -114,6 +114,7 @@ export namespace SheetApi { const targetElementProps = super.onTransformElement(sourceElement); // Add userLabel, code, and federationGuid information for target props + // sheetName is user provided information. if (sourceElement instanceof Sheet && sourceElement.id === this._sheetIdInSource) { targetElementProps.userLabel = this._sheetName; targetElementProps.code = Sheet.createCode(this.targetDb, targetElementProps.model, this._sheetName); From c9390eb6959306520126f80b1f20409a5928515f Mon Sep 17 00:00:00 2001 From: derbynn <44528840+derbynn@users.noreply.github.com> Date: Thu, 3 Oct 2024 13:14:49 -0400 Subject: [PATCH 11/14] Update arr comments Co-authored-by: Nick Tessier <22119573+nick4598@users.noreply.github.com> --- sheet-transformation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sheet-transformation.md b/sheet-transformation.md index 17d4be5a..0ab8128c 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -58,7 +58,7 @@ export namespace SheetApi { throw new Error("Failed to open snapshot iModel."); } - // Get the sheet data from the snapshot (this will contain the sheet data) + // Get the sheet data from the snapshot. Note this imodel has one sheet. const arr: any = []; const query = "SELECT * FROM BisCore.Sheet"; seedDb.withPreparedStatement(query, (statement) => { From 451bb7f6fd996f4ddfccfd18bf586dda5a49078a Mon Sep 17 00:00:00 2001 From: Daniel Erbynn Date: Thu, 3 Oct 2024 13:44:22 -0400 Subject: [PATCH 12/14] format comments and add hyperlinks --- sheet-transformation.md | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/sheet-transformation.md b/sheet-transformation.md index 0ab8128c..c4953644 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -48,8 +48,7 @@ export namespace SheetApi { throw new Error("A sheet must be named."); } - // create a blank sheetModelId(where we will insert the sheet data), create documentListModel (where we will insert list of document elements) - // This creates a sheet, sheetModel, and documentListModel in the target iModel which will be updated using the contents of the sheet, sheetModel, and documentListModel in the seedFileName. + // This creates a [sheet](https://www.itwinjs.org/reference/core-backend/elements/sheet/)(a document element), [sheetModel](https://www.itwinjs.org/reference/core-backend/models/sheetmodel/), and [documentListModel]( https://www.itwinjs.org/reference/core-backend/models/documentlistmodel/) in the target iModel which will be updated using the contents of the sheet, sheetModel, and documentListModel in the seedFileName. const [sheetModelId, documentListModelId] = await createSheetInternal(createSheetProps, iModel, sheetName); const seedFileName = "D:\\testmodels\\transformingSheetsIssue\\source.bim"; @@ -69,14 +68,20 @@ export namespace SheetApi { }); const importer = new IModelImporter(iModel); - importer.doNotUpdateElementIds.add(documentListModelId); // Do not update the documentListModelId, this is the one we've created for this iModel to receive the sheet template. + + // Do not update the documentListModelId, this is the one we've created for this iModel to receive the sheet template. + // We do this in order to keep the properties of the documentListModel that we set in `createSheetInternal`. + // Without this line the documentListModel's properties in the source iModel overwrite the target iModel's documentListModel. + importer.doNotUpdateElementIds.add(documentListModelId); + transformer = new SheetTransformer(seedDb, importer, arr[0].id, sheetName); - // Tell the transformer to treat the documentListModel id in the source `0x20000000009` as the same as the one we have created in the target. This allows for all children elements of the documentListModel in the source such as the sheetModel to also make their way into the target iModel under the `documentListModel` + // Tell the transformer to treat the documentListModel id in the source `0x20000000009` as the same as the one we have created in the target. + // This allows for all children elements of the documentListModel in the source such as the sheetModel to also make their way into the target iModel under the `documentListModel` transformer.context.remapElement("0x20000000009", documentListModelId); - // bring all data(drawing graphics, line styles, etc) in arr[0] to the blank sheetModel. - This is a similar thought process to the documentListModel remapping, we want all children elements of the sheetModel in the source like drawing graphics, line styles etc to end up as children of the sheetModel that we created in the target. + // bring all data (drawing graphics, line styles, etc) in arr[0] to the blank sheetModel. + // This is a similar thought process to the documentListModel remapping, we want all children elements of the sheetModel in the source like drawing graphics, line styles etc to end up as children of the sheetModel that we created in the target. transformer.context.remapElement(arr[0].id, sheetModelId); // export contents and sub-models to the target iModel @@ -119,7 +124,10 @@ export namespace SheetApi { targetElementProps.userLabel = this._sheetName; targetElementProps.code = Sheet.createCode(this.targetDb, targetElementProps.model, this._sheetName); } - targetElementProps.federationGuid = Guid.createValue(); // We want each element to have a new federation guid, so that they are not considered the same as the source elements. This allows us to use the same sheet in the source and create many copies of it in the target if we so choose. + + // We want each element to have a new federation guid, so that they are not considered the same as the source elements. + // This allows us to use the same sheet in the source and create many copies of it in the target if we so choose. + targetElementProps.federationGuid = Guid.createValue(); return targetElementProps; } From a66b70384f1dae0e2f11c116ebc3b539dda9fe33 Mon Sep 17 00:00:00 2001 From: Daniel Erbynn Date: Thu, 3 Oct 2024 14:30:27 -0400 Subject: [PATCH 13/14] add links for sheet documentation --- sheet-transformation.md | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/sheet-transformation.md b/sheet-transformation.md index c4953644..7b585994 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -47,8 +47,12 @@ export namespace SheetApi { if (!sheetName) { throw new Error("A sheet must be named."); } - - // This creates a [sheet](https://www.itwinjs.org/reference/core-backend/elements/sheet/)(a document element), [sheetModel](https://www.itwinjs.org/reference/core-backend/models/sheetmodel/), and [documentListModel]( https://www.itwinjs.org/reference/core-backend/models/documentlistmodel/) in the target iModel which will be updated using the contents of the sheet, sheetModel, and documentListModel in the seedFileName. + + // This creates a sheet(a document element), sheetModel, and documentListModel in the target iModel + // which will be updated using the contents of the sheet, sheetModel, and documentListModel in the seedFile + // sheet - https://www.itwinjs.org/reference/core-backend/elements/sheet/ + // sheetModel - https://www.itwinjs.org/reference/core-backend/models/sheetmodel/ + // documentListModel - https://www.itwinjs.org/reference/core-backend/models/documentlistmodel/ const [sheetModelId, documentListModelId] = await createSheetInternal(createSheetProps, iModel, sheetName); const seedFileName = "D:\\testmodels\\transformingSheetsIssue\\source.bim"; From 74c650546444f08c9e7914322f2e0b445049dbe2 Mon Sep 17 00:00:00 2001 From: Daniel Erbynn Date: Fri, 4 Oct 2024 10:24:02 -0400 Subject: [PATCH 14/14] update sheet-transformation.md --- sheet-transformation.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/sheet-transformation.md b/sheet-transformation.md index 7b585994..e9b66518 100644 --- a/sheet-transformation.md +++ b/sheet-transformation.md @@ -49,7 +49,7 @@ export namespace SheetApi { } // This creates a sheet(a document element), sheetModel, and documentListModel in the target iModel - // which will be updated using the contents of the sheet, sheetModel, and documentListModel in the seedFile + // which will be updated using the contents of the sheet, sheetModel, and documentListModel in the seedFile. // sheet - https://www.itwinjs.org/reference/core-backend/elements/sheet/ // sheetModel - https://www.itwinjs.org/reference/core-backend/models/sheetmodel/ // documentListModel - https://www.itwinjs.org/reference/core-backend/models/documentlistmodel/ @@ -84,14 +84,14 @@ export namespace SheetApi { // This allows for all children elements of the documentListModel in the source such as the sheetModel to also make their way into the target iModel under the `documentListModel` transformer.context.remapElement("0x20000000009", documentListModelId); - // bring all data (drawing graphics, line styles, etc) in arr[0] to the blank sheetModel. + // Bring all data (drawing graphics, line styles, etc) in arr[0] to the blank sheetModel. // This is a similar thought process to the documentListModel remapping, we want all children elements of the sheetModel in the source like drawing graphics, line styles etc to end up as children of the sheetModel that we created in the target. transformer.context.remapElement(arr[0].id, sheetModelId); - // export contents and sub-models to the target iModel + // Export contents and sub-models to the target iModel. await transformer.processModel("0x20000000009"); - // Save changes to DB + // Save changes to DB. iModel.saveChanges(); return sheetModelId; @@ -118,12 +118,12 @@ export namespace SheetApi { this._sheetName = sheetName; } - // Override to add sheet userLabel, code, federationGuid to the target element + // Override to add sheet userLabel, code, federationGuid to the target element. public override onTransformElement(sourceElement: Element): ElementProps { const targetElementProps = super.onTransformElement(sourceElement); // Add userLabel, code, and federationGuid information for target props - // sheetName is user provided information. + // SheetName is user provided information. if (sourceElement instanceof Sheet && sourceElement.id === this._sheetIdInSource) { targetElementProps.userLabel = this._sheetName; targetElementProps.code = Sheet.createCode(this.targetDb, targetElementProps.model, this._sheetName); @@ -145,13 +145,13 @@ export namespace SheetApi { throw new Error("A sheet's height and width must be greater than 0."); } - // Get or create documentListModel + // Get or create documentListModel. const documentListModelId = await DPChannelApi.getOrCreateDocumentList(SHEET_CONSTANTS.documentListName); - // Acquire locks and create sheet + // Acquire locks and create sheet. await iModel.locks.acquireLocks({ shared: documentListModelId }); - // insert sheet element into iModel + // Insert sheet element into iModel. const sheetElementProps: SheetProps = { ...createSheetProps, classFullName: Sheet.classFullName, @@ -160,7 +160,7 @@ export namespace SheetApi { }; const sheetElementId = iModel.elements.insertElement(sheetElementProps); - // insert sheet model into iModel + // Insert sheet model into iModel. const sheetModelProps: GeometricModel2dProps = { classFullName: SheetModel.classFullName, modeledElement: { id: sheetElementId, relClassName: "BisCore:ModelModelsElement" } as RelatedElement