iModel文件的合并等操作


合并imodel文件专题

IModelImporter

用于将数据导入iModel的基类,用户可以根据需要重写子类。

示例代码如下所示:

//用户自定义基类,重写若干基类函数,以实现自己的需求。
class MyIModelImporter extends IModelImporter {
  public constructor(targetDb: IModelDb, options?: IModelImportOptions) {
    super(targetDb, options);
  }
  //重写基类函数,当插入一个模型的时候被调用;
  protected onInsertModel(modelProps: ModelProps): Id64String {
    console.log("添加了一个model");
    return super.onInsertModel(modelProps);
  }
  //重写基类函数,当插入一个元素的时候被调用;
  protected onInsertElement(elementProps: ElementProps): Id64String {
    console.log("添加了一个元素,其class=" + elementProps.classFullName);
    return super.onInsertElement(elementProps);
  }
  //重写基类函数,当删除一个元素的时候被调用;
  protected onDeleteElement(elementId: Id64String): void {
    console.log("删除的元素的id为:" + elementId);
    super.onDeleteElement(elementId);
  }
}
export async function IModelTransformer_IModelImporter() {
  //首先创建一个测试imodel文件,因为使用openFile打开是只读的。
  //所以,我们通过createFrom基于种子imodel文件创建一个可读可写的iMode文件。
  const seedFileName = "./data3/CompatibilityTestSeed.bim";
  const seedDb = SnapshotDb.openFile(seedFileName);
  const sourceFileName = "./data3/TargetIModel.bim";
  if (IModelJsFs.existsSync(sourceFileName)) {
    IModelJsFs.removeSync(sourceFileName);
  }
  //注意,我们所创建的imodel文件设置了密码为'abcdef123456'.
  let TargetIModel = SnapshotDb.createFrom(seedDb, sourceFileName, {
    password: "abcdef123456",
  });
  //创建成功之后,即可关闭种子文件。
  seedDb.close();
  //检测一下,新创建的imodel文件是否可读可写。
  if (TargetIModel.isReadonly) {
    console.log("只读");
  } else {
    console.log("可读可写");
  }

  //创建一个MyIModelImporter实例,然后进行一下操作:
  const MyImporter = new MyIModelImporter(TargetIModel);

  const subjectId = TargetIModel.elements.getRootSubject().id;
  //创建两个建模元素分区属性;
  const partitionProps: InformationPartitionElementProps = {
    classFullName: DefinitionPartition.classFullName,
    model: IModel.repositoryModelId,
    parent: new SubjectOwnsPartitionElements(subjectId),
    code: DefinitionPartition.createCode(TargetIModel, subjectId, "AddModel"),
  };
  const partitionProps2: InformationPartitionElementProps = {
    classFullName: DefinitionPartition.classFullName,
    model: IModel.repositoryModelId,
    parent: new SubjectOwnsPartitionElements(subjectId),
    code: DefinitionPartition.createCode(TargetIModel, subjectId, "TestRemove"),
  };
  //先导入2个建模元素;
  const partitionId = MyImporter.importElement(partitionProps);
  const partitionId2 = MyImporter.importElement(partitionProps2);
  //创建一个DefinitionModel属性,注意其建模元素id是之前所插入的建模元素id。
  const modelProps: ModelProps = {
    classFullName: DefinitionModel.classFullName,
    modeledElement: { id: partitionId },
    id: partitionId,
    name: "TestModel",
  };
  MyImporter.importModel(modelProps);
  //删除id为partitionId2的建模元素。
  MyImporter.deleteElement(partitionId2);
  TargetIModel.close();
  //测试所插入的模型是否存在。
  const testDb = SnapshotDb.openFile(sourceFileName, {
    password: "abcdef123456",
  });
  const modelP = testDb.models.tryGetModelProps(partitionId);
  if (modelP) {
    console.log("测试成功");
    console.log(
      "name = " +
        modelProps.name +
        "classfullName = " +
        modelProps.classFullName
    );
  }
}

运行结果如下所示:

使用IModelTransformer基本导入

基本操作:将一个imodel中的内容全部导入到另一个imodel中。

示例代码如下所示:

export async function IModelTransformer_Handle() {
  //打开源imodel文件作为源;
  const SourceFileName = "./data3/CompatibilityTestSeed.bim";
  const SourceIModel = SnapshotDb.openFile(SourceFileName);
  //计算源imodel中元素个数;
  const ElementNumOfSource = count(SourceIModel, Element.classFullName);
  console.log("源 element 个数=" + ElementNumOfSource.toString()); //输出62

  //创建一个空imodel文件作为目标;
  const targetFileName = "./data3/TestTargetiModel.bim";
  if (IModelJsFs.existsSync(targetFileName)) {
    IModelJsFs.removeSync(targetFileName);
  }
  const targetDbProps: CreateIModelProps = {
    rootSubject: { name: "Clone-Target" },
    ecefLocation: SourceIModel.ecefLocation,
  };
  const targetDb = SnapshotDb.createEmpty(targetFileName, targetDbProps);
  //计算导入之前目的imodel中元素个数;
  const importBeforeNum = count(targetDb, Element.classFullName);
  console.log("import before number = " + importBeforeNum); //输出3
  const transformer = new IModelTransformer(SourceIModel, targetDb);
  await transformer.processSchemas(new BackendRequestContext());
  //导入全部;
  transformer.processAll();
  transformer.dispose();
  //计算导入之后目的imodel中元素个数;
  const afterBeforeNum = count(targetDb, Element.classFullName);
  console.log("import after number = " + afterBeforeNum); //输出62
  SourceIModel.close();
  targetDb.close();
}

使用IModelTransformer根据指定规则导入

将一个imodel中的内容,按照预先设定的规则将其内容导入到另一个imodel中。

示例代码如下所示:

export async function IModelTransformer_Handle() {
  //打开源imodel文件作为源;
  const SourceFileName = "./data3/CompatibilityTestSeed.bim";
  const SourceIModel = SnapshotDb.openFile(SourceFileName);
  //计算源imodel中元素个数;
  const ElementNumOfSource = count(SourceIModel, Element.classFullName);
  console.log("源 element 个数=" + ElementNumOfSource.toString()); //输出62

  //创建一个空imodel文件作为目标;
  const targetFileName = "./data3/TestTargetiModel.bim";
  if (IModelJsFs.existsSync(targetFileName)) {
    IModelJsFs.removeSync(targetFileName);
  }
  const targetDbProps: CreateIModelProps = {
    rootSubject: { name: "Clone-Target" },
    ecefLocation: SourceIModel.ecefLocation,
  };
  const targetDb = SnapshotDb.createEmpty(targetFileName, targetDbProps);
  //计算导入之前目的imodel中元素个数;
  const importBeforeNum = count(targetDb, Element.classFullName);
  console.log("import before number = " + importBeforeNum); //输出3

  // IModelExporter
  const expor = new IModelExporter(SourceIModel);
  // 统计DrawingGraphic的实例个数;
  const drawElementNumber = count(SourceIModel, DrawingGraphic.classFullName);
  console.log("DrawElementNumber = " + drawElementNumber.toString()); // 输出18;
  // 将DrawingGraphic的所有实例排除,也就是不导入到目标imodel中;
  expor.excludeElementClass(DrawingGraphic.classFullName);

  //IModelImporter
  const impor = new IModelImporter(targetDb);

  const transformer = new IModelTransformer(expor, impor);
  await transformer.processSchemas(new BackendRequestContext());
  //导入全部;
  transformer.processAll();
  transformer.dispose();
  //计算导入之后目的imodel中元素个数;
  const afterBeforeNum = count(targetDb, Element.classFullName);
  console.log("import after number = " + afterBeforeNum); //输出44,其中18个DrawingGraphic的实例没有导入。
  SourceIModel.close();
  targetDb.close();
}

function count(iModelDb: IModelDb, classFullName: string): number {
  return iModelDb.withPreparedStatement(
    `SELECT COUNT(*) FROM ${classFullName}`,
    (statement: ECSqlStatement): number => {
      return DbResult.BE_SQLITE_ROW === statement.step()
        ? statement.getValue(0).getInteger()
        : 0;
    }
  );
}

将两个imodel同时导入到另一个imodel中

现有2个imodel分别为A,B,欲将A,B导入到另一个imodel C中,需要首先在C中建立2个分区,然后将A,B imodel分别导入到不同的分区中。

示例代码如下所示:

export async function IModelTransformer_Handle2() {
  const SourceFileName1 = "./data2/Baytown.bim";
  const sourceFileName2 = "./data2/house.bim";
  const SrouceModel1 = SnapshotDb.openFile(SourceFileName1);
  const SrouceModel2 = SnapshotDb.openFile(sourceFileName2);
  //导入之前,分别计算2个源imodel中element的个数并打印;
  const num1 = count(SrouceModel1, Element.classFullName);
  const num2 = count(SrouceModel2, Element.classFullName);
  console.log("num1 = " + num1.toString()); //输出4962
  console.log("num2 = " + num2.toString()); //输出3169
  //创建一个新的imodel作为目标imodel,然后将2个源imodel导入到目标imodel中;
  const targetFileName = "./data2/TestTargetiModel.bim";
  if (IModelJsFs.existsSync(targetFileName)) {
    IModelJsFs.removeSync(targetFileName);
  }
  const targetDbProps: CreateIModelProps = {
    rootSubject: { name: "Clone-Target" },
  };
  const targetDb = SnapshotDb.createEmpty(targetFileName, targetDbProps);
  //在目标imodel中targetDb中创建2个分区,分别容纳2个源imodel;
  const parentSubject = targetDb.elements.getRootSubject().id;
  const subjectId1 = Subject.insert(targetDb, parentSubject, "subject1");
  const subjectId2 = Subject.insert(targetDb, parentSubject, "subject2");

  const rc = new BackendRequestContext();
  //注意在导入的时候,如果提示 UnhandledPromiseRejectionWarning: Schema Upgrade Failed: Error importing schema
  //可以尝试改变导入的imodel的顺序,这也就是为什么这里先导入2,再导入1的原因。
  //开始创建导入transformer2;
  const transformer2 = new IModelTransformer(SrouceModel2, targetDb, {
    targetScopeElementId: subjectId2,
  });
  await transformer2.processSchemas(rc);
  //导入全部;
  transformer2.processAll();
  transformer2.dispose();
  //开始创建导入transformer1;
  const transformer1 = new IModelTransformer(SrouceModel1, targetDb, {
    targetScopeElementId: subjectId1,
  });
  await transformer1.processSchemas(rc);
  //导入全部;
  transformer1.processAll();
  transformer1.dispose();
  //计算导入后目标imodel中element的个数
  const num = count(targetDb, Element.classFullName);
  console.log("num = " + num.toString()); //输出8128
  SrouceModel1.close();
  SrouceModel2.close();
  targetDb.close();
}

导入之前A,B imodel显示图分别为:

  

导入之后C imodel显示如下所示:

通过对比右边显示的View列表,我们即可发现已经将A,B imodel成功导入了C imodel中,并可以成功显示出来。