如何将2个bim中的图形模型显示在一个视图中


如何将2个bim中的图形模型显示在一个视图中 我们通过TiledGraphicsProvider接口可以实现将2个bim中的图形模型显示在同一个视图中。

示例如下所示:

现有V1.bim,其预览如下所示:

有V2.bim,其预览如下所示:

我们首先在APP中加载并预览V1.bim,然后通过TiledGraphicsProvider接口,导入V2.bim中的Tile内容,即可得到如下所示预览内容:

核心代码如下所示:

async function addFeatureSymbology() {
  const vp = IModelApp.viewManager.selectedView;
  if (vp) {
    await addProvider(vp);
  }
}
async function removeFeatureSymbology() {
  const vp = IModelApp.viewManager.selectedView;
  if (vp) {
    await removeProvider(vp);
  }
}
/** 对TileTree的引用源自与用户打开的对象不同的IModelConnection */
class ExternalTreeRef extends TileTreeReference {
  private readonly _ref: TileTreeReference;
  private readonly _ovrs: FeatureSymbology.Overrides;

  public constructor(ref: TileTreeReference, ovrs: FeatureSymbology.Overrides) {
    super();
    this._ref = ref;
    this._ovrs = ovrs;
  }

  public get castsShadows() {
    return this._ref.castsShadows;
  }

  public get treeOwner() {
    return this._ref.treeOwner;
  }

  public addToScene(context: SceneContext): void {
    const tree = this.treeOwner.load();
    if (undefined === tree) return;

    // ###TODO transform
    const args = this.createDrawArgs(context);
    if (undefined === args) return;

    tree.draw(args);

    args.graphics.symbologyOverrides = this._ovrs;
    const branch = context.createBranch(args.graphics, args.location);
    context.outputGraphic(branch);
  }
}

class Provider implements TiledGraphicsProvider {
  private readonly _refs: TileTreeReference[] = [];
  public readonly iModel: IModelConnection;

  private constructor(
    vp: Viewport,
    iModel: IModelConnection,
    ovrs: FeatureSymbology.Overrides
  ) {
    this.iModel = iModel;
    for (const kvp of iModel.models.loaded) {
      const spatial = kvp[1].asSpatialModel;
      if (undefined !== spatial) {
        const ref = spatial.createTileTreeReference(vp.view);
        this._refs.push(new ExternalTreeRef(ref, ovrs));
      }
    }
  }

  public static async create(
    vp: Viewport,
    iModel: IModelConnection
  ): Promise<Provider> {
    const query = { from: SpatialModelState.classFullName, wantPrivate: false };
    const props = await iModel.models.queryProps(query);

    const modelIds = [];
    for (const prop of props) if (undefined !== prop.id) modelIds.push(prop.id);

    await iModel.models.load(modelIds);

    // Enable all categories (and subcategories thereof)
    const ecsql =
      "SELECT DISTINCT Category.Id as CategoryId from BisCore.GeometricElement3d WHERE Category.Id IN (SELECT ECInstanceId from BisCore.SpatialCategory)";
    const catIds: string[] = [];
    for await (const catId of iModel.query(ecsql))
      catIds.push(catId.categoryId);

    const subcatsRequest = iModel.subcategories.load(catIds);
    if (undefined !== subcatsRequest) await subcatsRequest.promise;

    // Ignore the symbology overrides defined on the viewport - instead, set up our own to draw our iModel's categories.
    const ovrs = new FeatureSymbology.Overrides();
    for (const catId of catIds) {
      const subcats = iModel.subcategories.getSubCategories(catId);
      if (undefined !== subcats)
        for (const subcat of subcats) ovrs.setVisibleSubCategory(subcat);
    }

    return new Provider(vp, iModel, ovrs);
  }

  public forEachTileTreeRef(
    _vp: Viewport,
    func: (ref: TileTreeReference) => void
  ): void {
    for (const ref of this._refs) func(ref);
  }
}
const providersByViewport = new Map<Viewport, Provider>();

/** A simple proof-of-concept for drawing tiles from a different IModelConnection into a Viewport. */
export async function addProvider(vp: Viewport): Promise<void> {
  const existing = providersByViewport.get(vp);
  if (undefined !== existing) {
    vp.dropTiledGraphicsProvider(existing);
    providersByViewport.delete(vp);
    await existing.iModel.close();
    return;
  }
  const filename = Config.App.getString("imjs_offline_imodel_Next");
  if (undefined === filename) return;
  let iModel;
  try {
    iModel = await SnapshotConnection.openFile(filename);
    const provider = await Provider.create(vp, iModel);
    providersByViewport.set(vp, provider);
    vp.addTiledGraphicsProvider(provider);
  } catch (err) {
    alert(err.toString());
  }
}
export async function removeProvider(vp: Viewport): Promise<void> {
  const existing = providersByViewport.get(vp);
  if (undefined !== existing) {
    vp.dropTiledGraphicsProvider(existing);
    providersByViewport.delete(vp);
    await existing.iModel.close();
  } else {
    alert("Provider不存在.");
  }
}