1. iTwin.js 3.0 版本新特性

随着大多数主要版本的发布,出现了许多新功能和一些突破性的 API 更改。为此,提供迁移到 3.0 版本的升级工具,该工具可以方便地对 3.0 版本中发生的所有重命名做出更改。

本文将从以下几方面对此次版本升级进行说明。

1.1. 1. 升级指引

为帮助 iModel.js 从 2.x 更新至 3.0, 已发布了一个使用 JSCodeshiftcodemod 工具,它可以作为将项目更新到 iTwin 3.0 的开始。JS3.0 这个工具可以自动执行一些升级任务,比如更新包名,用替换 API 替换不推荐或删除的 API。如何使用 codemod 可参考 readme 文档。

1.2. 2. 新特性

1.2.1. 2.1 显示系统(Display System)

关于显示系统的更多内容,请参阅此处

2.1.1 实现 Viewport.zoomToElements

Viewport.zoomToElements 为前端提供一个接口 -- 根据构件(集)的 Ids 查询构件的属性,并将视口缩放至查询的构件上。主要在以下两个方面进行了提升:

  • 属性

    先前查询属性信息需要通过 IModelConnection.Elements.getProps 接口,该接口返回构件的所有属性信息(可能多大几兆(Mb)数据),通过遍历每个构件的 PlacementProps 进行查询。当前改进后通过 IModelConnection.Elements.getPlacements 接口查询参数内的构件。

  • 查询效率

    先前查询时将会在 iModel 的二维和三维模型中进行遍历查找;改进后,只会在指定的空间范围内查询,例如在三维视图中查询,只遍历三维构件信息,反之亦然。

使用方法可参考以下:

const elems = vp.iModel.selectionSet.elements;
if (0 < elems.size)
  await vp.zoomToElements(elems, { animateFrustumChange: true });

2.1.2 菲涅尔反射

灯光设置(LightSettings)中增加支持非真实 菲涅耳效果

  • 菲涅尔效果在建筑模型中的应用 Fresnel effect applied to an architectural model
  • 菲涅尔效果在工程模型中的应用 Fresnel effect applied to a plant model

菲涅尔效果启用参考代码:

// Enable ambient occlusion.
viewport.viewFlags = viewport.viewFlags.with("ambientOcclusion", true);

// Configure the lighting.
viewport.displayStyle.lightSettings = LightSettings.fromJSON({
  // A relatively bright ambient light is the only light source.
  ambient: {
    intensity: 0.55,
  },
  // Increase the brightness of surfaces that are closer to parallel with the viewer's line of sight.
  fresnel: {
    intensity: 0.8,
    invert: true,
  },
  // Disable directional lighting.
  solar: {
    intensity: 0,
  },
});

2.1.3 视口背景透明

可在 DisplayStyleSettings.backgroundColor 中设置视口(Viewport)的背景透明度。此场景可应用在一个页面中需要查看多个视图的情形上。

视口背景透明应用效果如下:

Three overlapping viewports with transparent background colors

启用参考代码:

// start with a new "blank" spatial view to show the extents of the project, from top view
const blankView = SpatialViewState.createBlank(
  iModel,
  ext.low,
  ext.high.minus(ext.low)
);
const style = blankView.displayStyle;
// 设置背景颜色和透明度
style.backgroundColor = ColorDef.blue.withTransparency(100);

2.1.4 网格展示

iTwin 显示的图形视口主要由三角网格组成。有时,可视化这些网格的三角剖分非常有用。现在启用 ViewFlags.wiremesh 显示,启用后每个三角形的边将覆盖在网格顶部,作为大约 1 像素宽的抗锯齿黑线。

网格展示启用效果:

Wiremesh applied to a plant model

Wiremesh applied to a reality model

在视口中启用网格展示:

viewport.viewFlags = viewport.viewFlags.with("wiremesh", true);

2.1.5 视口同步

TwoWayViewportSync 在两个视口(Viewport)之间建立连接,这样对一个视口的任何更改都会反映在另一个视口中。这不仅包括对平截头体的更改,还包括对显示样式、类别和模型选择器等的更改。然而,同步一切并不总是可取的;如果视口正在查看两个不同的 iModel 连接,这甚至没有意义,因为来自一个 iModel 的类别和模型 ID 与另一个 iModel 中没有意义。

现在,TwoWayViewportSync 是可扩展的,允许子类通过重写 TwoWayViewportSync.connectViewportsTwoWayViewportSync.syncViewports 来指定应同步视口的哪些方面。要使用子类 MyViewportSync 在两个视口之间建立连接,请使用 MyViewportSync.connect(viewport1, viewport2)

提供了一个新的子类 TwoWayViewportFrustumSync,它只同步视口的平截头体。视口将查看相同体积的空间,但可能显示不同的内容或应用不同的显示样式。要建立此连接,请使用 TwoWayViewportFrustumSync.connect(viewport1, viewport2)

2.1.5 环境装饰

DisplayStyle3dSettings 可以指定要绘制为环境装饰的天空框 (SkyBox) 和地平面 (GroundPlane)。以前,DisplayStyle3dSettings.environment 是一个可变的 JSON 参数,而 DisplayStyle3dState.environment 是在核心前端包中定义一个可变的环境对象。这使得 API 冗余,并导致在将视口的装饰与环境设置的更改同步时出现错误。

现在,DisplayStyle3dSettings.environment 是一个不可变的环境对象,由地平面、天空框和控制每个对象显示的标志组成。这些更改需要对现有代码进行调整,以切换其中一项的显示。例如:

// Replace this:
style.environment.sky.display = true;
style.environment.ground.display = true;
// With this:
style.environment = style.environment.withDisplay({ sky: true, ground: true });

效果如下:

Environment decorations

此外,直到现在,SkySphere 或 SkyBox 使用的图像都需要由 iModel 中存储的纹理元素(Texture)承载。现在,它们还可以被指定为解析到 HTMLImageElement 的 URL,从而允许在不修改 iModel 的情况下创建自定义 Skybox。

2.1.6 外观覆盖方法合并

一个视口可以有任意数量的 FeatureOverrideProvider,每个都可以指定如何覆盖元素、模型和/或子类别的外观。有时,多个提供者希望覆盖相同对象外观的各个方面,这会产生冲突。 FeatureOverrides.overrideElement, FeatureOverrides.overrideModel, 和 FeatureOverrides.overrideSubCategory 三个方法每个都接受一个默认为 true 的布尔替换现有参数。这意味着,如果一个提供程序覆盖某个元素的线宽,而另一个提供程序希望覆盖同一元素的透明度,则调用程序的唯一选择是替换现有的覆盖,从而只覆盖透明度;或者保留现有替代,从而只替代线宽。但在大多数情况下,更好的结果是合并这两组覆盖,以便覆盖透明度和线宽。

现在引入一个新的功能,FeatureOverrides.override 方法来支持合并外观覆盖。调用者可以指定处理冲突的四种策略之一,或者接受默认策略:

  • replace

    现有的外观覆盖被调用者自己的覆盖替换,相当于 overrideElement 等方法的默认 replaceExisting=true;

  • skip

    保留现有的外观覆盖,忽略调用方自己的覆盖,这相当于对 overrideElement 等方法的 replaceExisting=false;

  • extend

    将新外观与现有外观合并,以便新外观可以覆盖未被现有外观覆盖的外观的任何方面;

  • subsume(默认设置) 将新外观与现有外观合并,以便仅当新外观未覆盖现有外观时,才保留由现有外观覆盖的外观的任何方面。

例如,如果一个提供程序覆盖元素的颜色透明度,而另一个提供程序尝试覆盖其透明度线宽,则使用 “extend” 选项意味着第二个提供程序将只覆盖线宽,而保留现有的颜色和透明度覆盖不变。使用 “subsume” 选项,第二个提供者将覆盖透明度和线宽,保留现有的颜色覆盖不变。

因为以前的默认行为通常是不推荐的,所以 overrideElement、overrideModel 和 overrideSubCategory 已被弃用,取而代之的是新的 override 方法。代码更新如下:

// To use the new default "extend" behavior, replace these:
ovrs.overrideElement("0x123", appearance);
ovrs.overrideModel("0x456", appearance);
ovrs.overrideSubCategory("0x789", appearance);
// With these:
ovrs.override({ elementId: "0x123", appearance });
ovrs.override({ modelId: "0x456", appearance });
ovrs.override({ subCategoryId: " 0x789", appearance });

// To use the previous default "replace" behavior, replace this:
ovrs.overrideElement("0x123", appearance, true); // third argument is optional - defaults to true
// With this:
ovrs.override({ elementId: "0x123", appearance, onConflict: "replace" });

// To use the `replaceExisting=false` behavior, replace this:
ovrs.overrideModel("0x456", appearance, false);
// With this:
ovrs.override({ modelId: "0x456", appearance, onConflict: "skip" });

2.1.7 粒子系统提升

ParticleCollectionBuilder 的性能进行了改进,并在 ParticleProps 中添加了可选的旋转矩阵,以便粒子可以旋转。

2.1.8 一种新的 MarkerSet 聚类算法

MarkerSet 类现在根据标记位置之间的屏幕距离而不是其矩形的重叠来对标记进行聚类,因此聚类是有效的。Cluster.rect 属性已被删除。相反,有一个新成员 MarkerSet.clusterRadius,用于控制附近标记何时聚集。

2.1.9 支持 glTF 图形

glTF 已经成为网络上 3d 图形事实上的标准格式。现在,您可以使用 readGltfGraphics 从 glTF 资源创建一个RenderGraphic,用于装饰器。此示例演示如何将 glTF 资源转换为图形,并使用装饰器显示它。

注:readGltfGraphics 以 glTF 2.0 规范为目标,但完整规范的实施仍在进行中。当前的实现可以成功读取多个 glTF 内容,但如果某个图形未能正确加载或显示,请提交问题

2.1.10 替换 Viewport.readImage

Viewport.readImage 有一个繁琐的 API 和几个漏洞。特别是,如果要求它读取图像的子区域,它将错误地计算 y 值;默认情况下,它会生成一个颠倒的图像。它已被弃用,取而代之的是 Viewport.readImageBuffer。升级代码如下:

// Use default arguments for readImage.
viewport.readImage(); // old - upside-down by default!
viewport.readImageBuffer({ upsideDown: true }); // new

// Read the entire image right-side-up - the typical case:
viewport.readImage(undefined, undefined, false); // old - must explicitly request right-side-up!
viewport.readImageBuffer(); // new

// Read a sub-rect of the image
viewport.readImage(rect); // old - produces incorrect results!
viewport.readImageBuffer({ rect }); // new

// Resize the image
viewport.readImage(undefined, size); // old
viewport.readImageBuffer({ size });

2.1.11 新的标记聚类算法

MarkerSet 类现在根据标记位置之间的屏幕距离而不是其矩形的重叠来对标记进行聚类,因此聚类是有效的。rect 属性不再需要,已被删除。相反,有一个新成员 MarkerSet.clusterRadius,用于控制附近标记何时聚集。

1.2.2. 2.2 Presentation

2.2.1 新增 Presentation 规则

  • 必选的 Schema

    添加了一个新的 requiredSchemas 属性:Ruleset.requiredSchemasRuleBase.requiredSchemas。该属性提供了一种基于 iModel 中可用的 ECSchema/域 筛选表示规则的简单方法。有关详细信息,请参见“定义 Presentation 规则的 ECSchema 要求”页面。

  • 根节点规则条件判断

    子节点规则类似,根节点规则现在也有一个条件属性。该属性在启用或禁用规则方面提供了更大的灵活性,包括使用规则集变量

  • 设置类多态性的更精确方法

    以前,多态性是使用 ContentInstancesOfSpecificClassesSpecification.handleInstancesPolymorphically 和 InstanceNodesOfSpecificClassesSpecification.arePolymorphic 属性,现已启用。取而代之的是 MultiSchemaClassesSpecification.arePolymorphic,如果未指定新属性,则充当默认值。 此更改允许 ContentInstancesOfSpecificClassesSpecification 和 InstanceNofSpecificClassesSpecification 在必要时指定具有不同多态性值的多个类。

  • Excluding 类 添加了一个新的 excludeClasses 属性:ContentInstanceOfSpecificClassesSpecification.excludeClass 和 InstanceNodeOfSpecificClassesSpecification.excludedClasses。该属性提供了一种从结果集中省略特定类实例的简单方法。这以前只能通过 instanceFilter 属性实现,但新方法更简洁。

2.2.2 新增 API

  • 上下文实例键(Key) 一个新的 PresentationManager.getContentInstanceKeys 接口更有效地获取上下文实例的 Key。
  • 上下文源(Soucre)

    PresentationManager 中新增 getContentSources API:

    API 允许根据默认的表示规则找出用于获取特定元素类属性的类。默认的表示规则集设置为在请求特定类型元素的内容时包含各种相关类的属性。例如:

    • 包括为 bis.Element 创建上下文时的 bis.ElementUniqueAspectbis.ElementMultiAspect属性
    • 包括为 bis.PhysicalElement 创建上下文时的 bis.PhysicalType 属性

    在创建 ECSQL 查询语句或在其他地方需要知道元素的属性时,这些信息很有帮助。

  • 一次性获取多个构建属性

    新增的 PresentationManager.getElementProperties 重写 API 为基于给定元素请求元素属性。与 PresentationManager.getContent 相比更加的灵活,新的 API 旨在以简化且更高效的方式检索属性,尤其是在请求大量元素的属性时。

2.2.3 自动升级

以下任一事件支持层次结构和上下文组件的自动更新,为此进行了多项改进:

  • IpcApp 原生应用的 iModel 数据
  • Presentation 规则集修改后
  • 规则集参数指改变后

要启用该功能,在设置 Presentation 规则驱动的组件时,必须设置一个附加属性:

  • 使用 UsePresentationControlleNodeLoader 钩子为 ControlleTree 创建 节点加载器时,将 enableHierarchyAutoUpdate:true 属性和生成的 onItemsRendered 回调传递给 ControlleTree:

    const { nodeLoader, onItemsRendered } = usePresentationTreeNodeLoader({
      ...otherLoaderProps,
      enableHierarchyAutoUpdate: true,
    });
    return (
      <ControlledTree
        {...otherTreeProps}
        nodeLoader={nodeLoader}
        onItemsRendered={onItemsRendered}
      />
    );
    
  • 创建 PresentationPropertyDataProvider 时,将 enableContentAutoUpdate:true 作为属性传递。

  • 创建 PresentationTableDataProvider 时,将 enableContentAutoUpdate:true 作为属性传递。

此外,要启用 iModels 的更改跟踪,IPC 后端应使用以下属性初始化 Presentation:

Presentation.initialize({
  ...otherProps,

  // tell presentation system that data in iModels might change
  mode: PresentationManagerMode.ReadWrite,

  // tell presentation system how often (in milliseconds) it should poll for changes
  updatesPollInterval: 20,
});

1.2.3. 2.3 AppUi 新特性

2.3.1 UiItemsProvider 提升

UiItemsProviders 现在将过滤掉重复的 UiItems,以确保只有一个特定项目(例如小部件或工具)将添加到给定阶段。例如,如果希望选择工具在某个阶段中可用,则可以将其包含在 UiItemsProvider 返回的内容工具中,而无需检查它是否已在要扩展的阶段中。在本次发布之前,一旦前台被实例化,前台指定的 applicationData 就会变得模糊。现在,UiItemsProvider 将该信息传递给每个 provide*() 回调,以便 UiItemsProvider 在需要时使用。

2.3.1 更改窗口大小

已经实现了一个新的 ResizeObserver,它支持主窗口和任何弹出窗口。

2.3.1 Frontstages 新定义的可选项

Class/Component Description
StandardFrontstageProvider Frontstage provider that provides an 'empty' stage that is to be populated via UiItemsProviders.
StandardContentToolsProvider UiItemsProvider that will add common tool entries to Tool Widget.
StandardNavigationToolsProvider UiItemsProvider that will add common view tool entries to Navigation Widget.
StandardStatusbarItemsProvider UiItemsProvider that will add common statusbar items.
ContentToolWidgetComposer Provides an empty Tool Widget that is to be populate via UiItemsProviders.
ViewToolWidgetComposer Provides an empty Navigation Widget that is to be populate via UiItemsProviders.
StandardContentLayouts Provides standard view layouts that can be used when defining a ContentGroup.
ContentGroupProvider Class that generates a ContentGroup at runtime when the frontstageDef is being constructed.

2.3.1 新的日期时间线标记

TimelineComponent React 组件现在接受在基于日期的时间线中标记特定日期的属性。如果时间线具有定义的开始日期和结束日期,则可以通过在 TimelineComponentProps 的新 markDate 成员中指定 TimelineDateMarkerProps 的实例,在时间线中标记它们之间的日期。如果未定义成员的日期,将使用今天的日期。默认标记是一个短竖条,但可以在 dateMarker 属性中指定一个节点来自定义标记的外观。

2.3.1 新的浮动小部件功能

通过 UiItemsProviders 提供的小部件现在可以设置 defaultState:WidgetState.FloatingisFloatingStateSupported:true 可在浮动容器中打开小部件。还可以指定 defaultFloatingPosition 属性来定义浮动容器的位置。如果未定义位置,容器将在 AppUi 区域居中。 getFloatingWidgetContainerIds() 方法已添加到 frontstageDef,以检索所有浮动小部件容器的 ID。 这些 ID 可用于通过 frontstageDef.getFloatingWidgetContainerBounds 查询浮动容器的大小。然后可以使用 frontstageDef.setFloatingWidgetContainerBounds 设置浮动小部件容器的大小和位置。

2.3.1 新的开启/关闭视口覆盖 API

UiFramework 现在提供了一个 setViewOverlyDisplay(display:boolean) 方法来启用或禁用显示覆盖图的视口。默认情况下,显示屏处于启用状态。当前设置在 UiFramework.viewOverlayDisplay 中可用。

1.2.4. 2.4 ECSql

2.4.1 Id64Set 参数绑定

现在可以有效地将大量 ECInstanceID 绑定到查询参数。这对 IN 子句非常有用。例如,假设您想要选择属于模型选择器的所有空间模型的一些属性。之前你需要写这样的东西:

const ids = Array.from(modelSelector.models).join(",");
db.query(
  "SELECT IsPlanProjection, JsonProperties FROM bis.SpatialModel WHERE ECInstanceId IN (" +
    ids +
    ")"
);

逗号分隔的 ID 列表可能非常长——在某些情况下,它可能太长,需要拆分为多个查询! 现在,可以将一组 ID 绑定为 IN 子句的参数。ID 将以紧凑的字符串格式序列化。

const params = new QueryBinder().bindIdSet("modelIds", modelSelector.models);
db.query(
  "SELECT IsPlanProjection, JsonProperties FROM bis.SpatialModel WHERE InVirtualSet(:modelIds, ECInstanceId)",
  params
);

1.3. 3. 重大变更

1.3.1. 3.1 程序启动设置

1.3.2. 3.1.1 删除默认的密钥

@itwin/core frontend 的早期版本包括必应地图、MapBox 和 Cesium ION 的 API 密钥,这些密钥将用于所有 itwin.js 应用程序。这些公用密钥不再受支持,很快将被禁用。所有应用程序现在都需要提供自己的密钥。

需要加载密钥的第三方服务:

  • MapBox

    用于在 BackgroundMapProvider 视图中显示地图图像。名称设置为“MapBoxProvider”。

  • Bing Maps

    • 使用 BackgroundMapProvider 在视图中显示地图图像。名称设置为“BingProvider”。
    • BingLocationProvider 提供的定位服务,以及使用这些服务的工具,如 ViewGlobeLocationTool。
    • BingElevationProvider 提供的高程服务,包括精确的 3d 地形显示。
  • Cesium ION
    • 在带有地形设置的视图中显示三维地形。providerName 设置为“CesiumWorldTerrain”。
    • OpenStreetMap 建筑的展示。

IModelAppOptions.mapLayerOptions 可用于为 Bing 地图、MapBox 和/或任何其他地图层提供程序配置密钥。TileAdmin.Props.cesiumIonKey 可用于配置 Csium ION 的密钥。例如,以下配置 Bing 地图和启动时的 Cesium ION 密钥:

const appOptions: IModelAppOptions = {
  mapLayerOptions: {
    BingMaps: {
      key: "some key",
      value: "key",
    },
  },
  tileAdmin: {
    cesiumIonKey: "key",
  },
};

await IModelApp.startup(appOptions);

3.1.2 iModelApp 弃用设置

上一个 IModelApp.settings API 已被删除,以支持 IModelApp.userPreferences。更新后的 API 将明确区分用户(用户首选项)控制范围内的“设置”和 iModel/iTwin(工作区)管理员控制范围内的“设置”。分离的目的是明确谁有能力修改和覆盖给定的设置,同时使 API 更易于使用。

新的 UserPreferencesAccess 接口是一个简单易用的 API,可以用多种不同的方式实现。core-frontend 不要求实现,可以使用本地存储(通过存储 API)或云托管存储机制轻松设置,以便跨用户会话共享。

3.1.2 本地化语言包初始化

在以前的版本中,本地化是通过 I18N 类提供的。iTwin.js 目前已经更新为使用 Localization 接口。IModelApp 的初始化现在需要一个实现 Localization 的可选对象。ITwinLocalization 类提供默认实现,可以在构造函数中使用 LocalizationOptions 选项进行自定义,并提供 IModelAppOptions.localization 设置。

提供本地化选项的前一种方法是:

const i18nOptions: I18NOptions = {
  urlTemplate: `${window.location.origin}/locales//.json`,
};

await IModelApp.startup({ i18n: i18nOptions });

现在改成:

const localizationOptions: LocalizationOptions = {
  urlTemplate: `${window.location.origin}/locales//.json`,
};

await IModelApp.startup({
  localization: new ITwinLocalization(localizationOptions),
});

3.1.2 简化 CloudStorageService 设置

IModelHostConfiguration.tileCacheCredentials 更改为 IModelHostConfiguration.tileCacheAzureCredentials,用于为 Tile 缓存设置 Azure 云存储。IModelHost.tileCacheService 被移动到 IModelHostConfiguration.tileCacheService,用于通过使用自定义 CloudStorageService 设置此属性,为任何服务提供商提供不同的实现。如果 tileCacheAzureCredentials 和 tileCacheService 都被省略,则将使用本地缓存,如果两者都被设置,则将抛出错误。

  • Azure

    将 Azure 云存储用于 Tile 缓存集需设置 IModelHostConfiguration.tileCacheAzureCredentials 属性:

    const config = new IModelHostConfiguration();
    // Replace this:
    config.tileCacheCredentials = {
      service: "azure",
      account: "account",
      accessKey: "accessKey",
    };
    // With this:
    config.tileCacheAzureCredentials = {
      account: "account",
      accessKey: "accessKey",
    };
    
  • 阿里云

    要使用阿里云存储 IModelHostConfiguration.tileCacheService 属性提供了 AliCloudStorageService 实现:

    import { AliCloudStorageService } from "@itwin/core-backend";
    
    const config = new IModelHostConfiguration();
    // Replace this:
    config.tileCacheCredentials = {
      service: "alicloud",
      account: "account",
      accessKey: "accessKey",
    };
    // With this:
    config.tileCacheService = new AliCloudStorageService({
      region: "region",
      accessKeyId: "accessKeyId",
      accessKeySecret: "accessKeySecret",
    });
    
  • 其他云服务商

    要使用任何其他外部存储集,请执行 IModelHostConfiguration.tileCacheService 带有自定义 CloudStorageService 实现:

    const config = new IModelHostConfiguration();
    // Replace this:
    config.tileCacheCredentials = {
      service: "external",
      account: "",
      accessKey: "",
    };
    IModelHost.tileCacheService = new CustomCloudStorageService();
    // With this:
    config.tileCacheService = new CustomCloudStorageService();
    

3.1.2 配置包弃用

由于倾向于使用 dotenv 包,@bentley/config-loader 已被弃用。任何使用 env 配置文件将不受影响。

1.3.3. 3.2 鉴权

3.2.1 弃用 ClientRequestContext 和 AuthorizedClientRequestContext

ClientRequestContext 和 AuthorizedClientRequestContext 类用于识别 web 前端和云后端之间的 RPC 请求。它们已被移除。大多数以前使用 AuthorizedClientRequestContext 的地方现在都应该替换为 AccessToken

如果你有这样的代码:

requestContext.enter();

你可以简单地删除它。

此更改主要影响后端代码。对于后端 RPC 实现,所有未处理的异常将自动记录在相应的 RPC 元数据中。出于这个原因,通常最好抛出一个异常,而不是记录一个错误并在代码中返回一个可能从 RPC 调用也可能不从 RPC 调用的状态。

1.3.4. 3.3 iModel

3.3.1 继续过渡到 “ChangesetIndex”

每个变更集(Changeset)都有一个 Id(其内容和父变更集的字符串哈希)和一个索引(一个小整数,表示其在 iModel 时间线上的相对位置)。任何一个值都可以用于唯一标识变更集。然而,经常需要比较两个变更集标识符来确定相对顺序,或者提供一系列感兴趣的变更集。在这种情况下,Id 没有用处,必须通过到 iModelHub 服务器的往返转换为索引。不幸的是,很多 iTwin.js API 仅使用 ChangesetId 来标识变更集。这是不可取的,因为经常需要 ChangesetIndex,而 ChangesetId 很少有用。出于这个原因,我们正在将 API 迁移到 ChangesetIndex 而不是先前的版本。

在 2.19 版中,我们引入了 ChangesetIdWithIndex 类型来开始迁移。然而,对于 2.x 版本兼容性我们 无法 在以下几个地方使用它:

在 2.19 中,为了向后兼容,每个接口最初只有一个成员“changeSetId:string”,增加一个新成员“changeSetIndex?: number”。在 V3 中,这两个成员现在被成员“changeset:ChangesetIdWithIndex”替换。请注意,这是一个突破性的更改,您可能需要调整代码。

要获取变更集 Id,请使用changeset.id。要获取变更集索引,请使用changeset.index (可能未定义)。在 V4 中,这里可能变更为 changeset:ChangesetIndexAndId,并且需要索引。

注:“变更集”(Changeset)是一个词。API 在引用它们时不应使用大写字母“S”。

3.3.2 并发控制

之前的锁定元素的“ConcurrencyControl”接口已被LockControl接口取代。

在调用BriefcaseDb.saveChanges之前,ConcurrencyControl依赖于检测已更改元素的列表,并将锁的获取推迟到应用程序调用异步“request”方法来获取锁之后。新方法是要求应用程序调用异步方法LockControl.acquireLocks,在更新或删除元素之前获得独占锁,在插入之前获得父级和模型的共享锁。如果试图在没有所需锁的情况下修改或插入,则在尝试更改时会引发异常。因此,这将需要工具来进行必要的锁定调用。

以前,并发“模式”是由打开 briefcase 时的应用程序决定的。现在,在首次创建 iModel 时,它已被确定为 iModel 的一个属性(并且“version0”已上载)。默认情况下,iModel 使用悲观(即锁)模式,因此之前创建的所有 iModel 都需要锁。如果将 noLocks:true 作为参数传递给BackendHubAccess.createNewIModel,在上传之前,briefcase-local 值将保存在 rev0.bim 中。此后,该 iModel 的所有 briefcase 都将使用乐观(即无锁、更改合并)模式,因为每个人都将使用源自 rev0.bim。调用BriefcaseDb.openBriefcaseDb.useLockServer 方法检查该值。

锁仅适用于元素(Element)。“模式锁”是通过独占锁定元素 id 为 0x1(根主题 id)获得的。模型通过其建模元素(与模型具有相同的 id)锁定。

有关更多信息和示例,请参阅 ConcurrencyControl 学习文章。

3.3.2 BriefcaseManager, BriefcaseDb, 和 IModelDb 变更

BriefcaseManagerBriefcaseDb 中,几个方法的签名已被更改,以使之前需要的参数 requestContext 成为可选参数。该参数的名称不好,但仅用于提供“用户访问令牌”。由于任何与 briefcases 相关的地方,都可以通过静态方法 IModelHost.getAccessToken 获得经过身份验证的用户访问令牌,但是很少需要这个参数。调用方需要提供该参数的唯一情况是,对于希望通过单个后端模拟多个用户的测试(在测试之外是不允许的)。它现在是可选的,称为用户。

Method New arguments notes
BriefcaseDb.onOpen OpenBriefcaseArgs>) event signature change
BriefcaseDb.onOpened BriefcaseDb,OpenBriefcaseArgs event signature change
BriefcaseDb.open OpenBriefcaseArgs
BriefcaseDb.pullChanges PullChangesArgs was called pullAndMergeChanges
BriefcaseDb.pushChanges PushChangesArgs
BriefcaseDb.upgradeSchemas OpenBriefcaseArgs requestContext removed
BriefcaseManager.acquireNewBriefcaseId IModelIdArg
BriefcaseManager.downloadBriefcase RequestNewBriefcaseArg
IModelDb.importSchemas LocalFileName[] requestContext removed

3.3.2 返回类型更改

用于返回DbResult的后端方法IModelDb.saveFilePropertyIModelDb.deleteFileProperty。它们现在是“void”,如果发生错误,就会抛出异常。如果需要,可以在 exception 对象的errorNumber成员中检索错误值。

3.3.2 后端 Geocoordinate 方法参数更改

这两个方法IModelDb.GetImodelCoordinates FromGeoCoordinatesIModelDb.GetGeoCoordinates FromodelCoordinates用于获取字符串参数,该参数分别是字符串化的IModelCoordinatesRequestPropsGeoCoordinatesRequestProps。这些参数被更改为直接接受对象参数。如果出现编译错误,你应该删除 JSON.stringify

3.3.3 iModel 地理坐标与其他地理坐标的转换

现在可以在 iModel 地理坐标参考系和任何其他地理坐标参考系之间进行坐标转换。在此版本之前,坐标转换仅限于 iModel 地理坐标参考系和指定基准(通常为 WGS84)的纬度/经度之间的转换。

如需使用,请在后端创建 IModelCoordinatesRequestPropsGeoCoordinatesRequestProps,具体取决于要转换的目标坐标。设置 iModelCoordinateRequestProps.source 或 GeoCoordinatesRequestProps.target 属性包含基准(通常为 WGS84)名称的字符串或空字符串,以指定 iModel Geographic CRS 原生基准或包含坐标转换源或目标定义的 GeographicCRSProps 的字符串化版本。该请求可以添加坐标,以便适配 iModel.getGeoCoordinatesFromIModelCoordinates 或 iModel.getIModelCoordinatesFromGeoCoordinates。

地理坐标系统可以是完整的,也可以是不完整的。尽管支持完全定义的自定义地理坐标系统,但大多数情况下,只需指定地理坐标系统的 id 或 epsg 代码就足够了。

以下是典型的地理坐标系统示例:

{ horizontalCRS: { id: "CA83-II" }, verticalCRS: { id: "NAVD88" } }

{ horizontalCRS: { epsg: 26942 }, verticalCRS: { id: "NAVD88" } },

这些标识符指的是词典列表中地理坐标系统的键名或已知的 EPSG 代码。

也可以使用更复杂的地理坐标系统,例如以下用户定义的:

      {
        horizontalCRS: {
          id: "UserDef-On-NAD83/2011",
          description: "User Defined",
          datumId: "NAD83/2011",
          unit: "Meter",
          projection: {
            method: "TransverseMercator",
            centralMeridian: -1.5,
            latitudeOfOrigin: 52.30,
            scaleFactor: 1.0,
            falseEasting: 198873.0046,
            falseNorthing: 375064.3871,
          },
        },
        verticalCRS: {
          id: "GEOID",
        },
      }

在前端,GeoConverter 类被修改为接受一个包含椭球体的字符串或一个类似格式的 GeographicCRSProps

注意:IModelCoordinatesRequestProps.sourceGeoCoordinatesRequestProps.target 使用 sourceDatum 和 targetDatum 属性。

1.3.5. 3.4 公用方法

3.4.1 BentleyError 构造函数更改

在 V2.x 版本中,基本异常类BentleyError的构造函数接受 5 个参数,最后 3 个是可选的。参数 3 和 4 用于在构造函数本身中记录异常。这是一个坏主意,因为异常通常在“catch”语句中处理和恢复,所以没有实际的“问题”需要报告。在这种情况下,日志中的消息要么具有误导性,要么完全是错误的。此外,catch语句中的代码总是比抛出的低级代码(例如“invalid Id”与“invalid MyHashClass Id”)有更多关于错误发生原因的“上下文”,因此来自调用者的日志消息比来自调用者的日志消息更有帮助。因为每个抛出的异常都必须在某处被捕获,所以应该在捕获异常时进行日志记录,而不是在抛出异常时。

BentleyError构造函数现在接受 3 个参数,最后一个参数(metaData)是可选的。删除了以前的'log'和'category'参数。如果代码传递了 5 个参数,请删除第 3 个和第 4 个。如果之前传递了 3 或 4 个参数,只需保留前两个。此外,早期版本的构造函数要求元数据参数是返回对象的函数。metaData现在也可能只是一个对象。

3.4.2 Logger 方法

Logger 函数的可选metaData 参数以前是一个返回对象或未定义的函数。这是为了允许在关闭日志记录时创建要省略的元数据可能很昂贵的情况。然而,在很多情况下,元数据对象是直接可用的,因此无论是否启用日志记录,创建一个函数来返回它都会产生额外的开销。它现在也可能只是一个对象,所以你不必创建一个函数。

3.4.3 移除工具类型

AsyncFunction, AsyncMethodsOf, 和 PromiseReturnType 已在 @itwin/core-bentley 包中移除,并且在 @itwin/core-frontend 包中弃用。

1.3.6. 3.4 工具框架(Tool Framework)

3.4.1 Tool.runTool.parseAndRun 支持异步

在 V2.x 中,方法 Tool.runTool.parseAndRun 是同步的。这是有问题的,因为不可能调用工具并等待其完成。这两个方法现在都是'async'和'Promise'”。这显然是一个突破性的变化,任何重写这些方法的工具子类都需要变为异步,任何调用 Tool.runTool.parseAndRun 将需要适当地处理返回的 Promise (通常是等待它)。

在转换工具的过程中, Tool.runTool.parseAndRun 方法也变为 async,如果调用或重写它们,同样需要修改它们。

这些方法以前是同步的,现在是异步的:

3.4.2 注册工具

在以前的版本中,Tool.register 方法使用可选参数来提供本地化对象。因为它一直存在于 IModelApp 上,所以这个论点毫无用处,现在被删除了。如果你之前通过了,只需删除它。

1.3.7. 3.5 显示系统(Display System)

3.5.1 GraphicBuilder 的更改

创建 GraphicBuilder 时不再需要提供视口。相反,您可以向 RenderSystem.createGraphic 提供一个 CustomGraphicBuilderOptions 参数,这个参数可以计算生成的 RenderGraphic 所需的详细程度。

GraphicBuilder 的属性现在都是 只读 的——在创建生成器后,您不能再更改 placement、pickId、wantNormals 或 wantEdges。以前,调用者可以创建一个图形生成器,添加一些几何图形,然后在添加更多几何图形之前修改这些属性中的任何一个,而不是不产生令人惊讶的结果。

3.5.2 地图 API 更改

最初,背景地图要显示的图像类型由 BackgroundMapSettings.providerNameBackgroundMapSettings.mapType 定义。后来,以 MapImagerySettings 的形式添加了对来自任何源的任意数量地图图层的支持。因此,BackgroundMapSettings 属性与 MapmagerySettings.backgroundBase相比变得多余(且更受限制)。

MapImagerySettings 现在完全负责指定背景地图图像;BackgroundMapSettings 仅控制图像应用于视图的方式。相应的 JSON 属性已从 BackgroundMapProps 中删除;为了向后兼容,它们仍然存在于 PersistentBackgroundMapProps 中,如果 MapImageryProps.backgroundBase 没有指定背景图像,它们将被用作背景图像。

以前,大多数代码都会使用 Viewport.changeBackgroundMapPropsDisplayStyleState.changeBackgroundMapProps 更改地图图像。这样的代码将不再编译,而是应该使用Viewport.changeBackgroundMapProviderDisplayStyleState.changeBackgroundMapProvider。例如:

// Replace this:
viewport.changeBackgroundMapProps({
  providerName: "BingMapProvider",
  providerData: { mapType: BackgroundMapType.Street },
});
// With this:
viewport.changeBackgroundMapProvider({
  name: "BingMapProvider",
  type: BackgroundMapType.Street,
});

由于 BaseLayerSettings 可以是 BaseMapLayerSettings 或 ColorDef,并且前者可以配置为使用 BackgroundMapProvider 或任何其他图像源,因此查询当前提供程序现在变得更加复杂:

// Replace this:
const providerName: BackgroundMapProviderName =
  displayStyleSettings.backgroundMap.providerName;
// With something like:
let providerName: BackgroundMapProviderName | undefined;
if (
  displayStyleSettings.mapImagery.backgroundBase instanceof BaseMapLayerSettings
)
  providerName = displayStyleSettings.mapImagery.backgroundBase.provider?.name;

如果要从 BackgroundMapSettings 生成 JSON,并将其作为 DisplayStyleSettingsProps 对象持久化,请按如下方式更改代码:

// Replace this (no longer compiles):
displayStyleSettingsProps.backgroundMap = backgroundMapSettings.toJSON();
// With this:
displayStyleSettingsProps.backgroundMap =
  backgroundMapSettings.toPersistentJSON();

同样,如果您直接从持久的 DisplayStyleSettingsProps 读取 BackgroundMapSettings ,请按如下方式更改代码:

// Replace this (no longer compiles):
const mapSettings = BackgroundMapSettings.fromJSON(
  displayStyleSettings.backgroundMap
);
// With this:
const mapSettings = BackgroundMapSettings.fromPersistentJSON(
  displayStyleSettings.backgroundMap
);

DisplayStyleSettings.onBackgroundMapChanged 将不再改变背景地图时相应。相反的,应采用 DisplayStyleSettings.onMapImageryChanged

3.5.3 ViewFlags

包括 创建ViewState3d.lookAt创建纹理容差设置 等内容, 请参阅 ViewFlags 变更

1.3.8. 3.6 Presentation

请参阅 https://www.itwinjs.org/changehistory/#presentation

1.3.9. 3.7 AppUI 更改

@itwin/core-react 中的一些组件被弃用,取而代之的是 @itwin/itwinui-react 中的组件。@itwin/core-react 包中有几个结构被弃用,其他地方也有替代方案。 组件已被弃用,取而代之的是 @itwin/itwinui react 中的表。添加了一个新的 @itwin/imodel components react 软件包,其中包含与颜色、立方体、线宽、导航工具、数量输入、时间线和视口相关的项目。

iTwin.js ui 和 @itwin/presentation-components 包现在依赖于 React 版本 17。使用 ui 软件包的应用程序必须更新。有关 React 版本 17 的详细信息,请访问 React 博客

更多关于 AppUi 变更内容,请参阅 https://www.itwinjs.org/changehistory/#appui-changes

1.3.10. 3.9 编译器

@itwin/build-tools 将 Typescript 编译目标从 ES2017 提升到了 ES2019。

所有软件包将继续构建 CommonJS 变体,但现在将其编译至 lib/cjs。所有前端和共享(“通用”)软件包现在将构建一个 ESModules 变体,并将其编译至 lib/esm。此更改旨在改进应用程序的捆绑包大小,并允许动态导入,以便对未使用的代码进行树扫描。

如果之前直接从 lib 目录导入(例如 import {ElectronHost} from “@itwin/core-electron/lib/ElectronBackend”),您需要更新代码以从新目录 lib/cjs 导入(例如从 import { ElectronHost } from "@itwin/core-electron/lib/cjs/ElectronBackend" 导入)。

这也会影响导入 ui 控件中*.scss 样式的导入。如果之前是从 lib 目录导入 scss(例如 @import“~@itwin/ui-pkg/lib/ui-pkg/…),您需要更新代码才能从新目录 lib/esm 导入(例如@import“~@itwin/ui-pkg/lib/esm/ui-pkg/…”)。

3.9.1 升级至 @bentley/build-tools

  • 从 @itwin/build tools 中删除了 test 和 test-tsnode 脚本。请直接用 mocha 测试框架代替
  • 从 @itwin/build tools 中删除了 TSLint 支持。如果您仍在使用,请切换到 ESLint。
  • 删除了同一个包遗留的 .eslin.js。相反,请使用 @itwin/eslint-pluginimodeljs-recommended 的配置文件进行引用。
  • 已放弃对 ESLint 6.x 的支持。

1.3.11. 3.10 iModel 变换

3.10.1 新 @itwin/core-transformer 包

用于在 iModel 之间导入和导出数据的 API 已从@itwin/core-backend 转移到新的@itwin/core-transformer 包。这些 API 包括 IModelExporterIModelImporterIModelTransformer

3.10.2 IModelImporter 变更

IModelImporter 的配置现在仅由传递给构造函数的 IModelImportOptions 对象表示。不推荐使用 IModeImporter 属性 simpleElementGeometryautoExtendProjectExtentspreserveElementIdsForFiltering 修改选项;相反,在构建 IModelImporter 时设置这些选项,并在必要时从 IModelImporter.options 中读取它们。例如,替换以下内容:

  const importer = new IModelImporter(targetDb);
  importer.autoExtendProjectExtents = true;
  const isExtendingProjectExtents = importer.autoExtendProjectExtents;
}

替换成:

const importer = new IModelImporter(targetDb, {
  autoExtendProjectExtents: true,
});
const isExtendingProjectExtents = importer.options.autoExtendProjectExtents;

3.11.3 自定义处理前置悬空 Id

当 IModelTransformer 在 iModel 中遇到悬空的前置元素 id 引用(数据库中不存在该元素的 id)时,默认情况下会拒绝整个变换。现在,当在分析前置元素时进行变换,遇到这样的引用时,有多种方式可供选择。danglingPredecessorBehavior 选项默认为 reject,或者可以配置为 ignore,这将在转换到目标时保留悬挂引用。您可以这样配置新行为:

const transformer = new IModelTransformer(sourceDb, targetDb, {
  danglingPredecessorBehavior: "ignore",
});

1.4. 4. 其他变更

1.4.1. 4.1 iTwinId

有几个 api 在 iTwin.js 指 iModel 的“context”,意思是 iModel 所属的项目资产,作为其“contextId”。这是非常令人困惑的,因为“context”一词在一般计算机科学和 iTwin 中使用范围较广,特别是 iTwin.js。这在 V3.0 中得到了解决,通过识别每个 iModel 都存在于 iTwin 中,并且每个 iTwin 都有一个名为“iTwinId”的 GUID。公共 API 中所有表示的 contextId 实例现在都被 iTwinId 取代。 对于 IModel.contextId 来说,这是一个突破性的变化。因此,在代码中出现编译错误的任何地方替换 'contextId' -> 'iTwinId'。

1.4.2. 4.2 单位

UnitProps中的'altDisplayLabels'属性已被删除。AlternateLabels 现在通过AlternateUnitLabelsProvider 提供。QuantityFormatter 现在提供了一个用于将字符串解析为数量的函数。要添加自定义标签,请使用 QuantityFormatter.addAlternateLabels参见下面的示例。

IModelApp.quantityFormatter.addAlternateLabels("Units.FT", "feet", "foot");

1.4.3. 4.3 删除 oidc-signin-tool

oidc-signin-tool 包含各种授权测试工具。它已被重新定位到 @itwin/auth-clients 仓库。

1.4.4. 4.4 ECSql API

为了提高性能和灵活性,对用于执行 ECSql 语句的 API 进行了几处更改。这涉及到 IModelConnection、IModelDb 和 ECDbqueryqueryRowCountrestartQuery 方法的更改。

  • query 和 restartQuery 方法用于获取多个参数,这些参数指示可返回行数限制、优先级和配额等。这些参数组合成一个 QueryOptions
  • 以前,无法控制 query 和 restartQuery 方法返回的每一行的格式,默认格式冗长且效率低下。现在,这些方法接受 QueryRowFormat 作为描述所需格式的 QueryOptions 参数的一部分。默认格式将每行作为数组而不是对象返回.
  • query、restartQuery 和 queryRowCount 方法用于将语句绑定接受为 any[] | object 类型。绑定现在被指定为更类型安全的类型 QueryBinder

4.4.1 使用 QueryBinder 绑定参数

QueryBinder 是将参数绑定到 ECSql 语句的一种类型更安全的方法。它允许在单个语句中混合索引参数和命名参数。例如:

const params = new QueryBinder().bindString("name", "hello").bindId(1, "0x123");

for await (const row of db.query(
  "SELECT ECInstanceId, Name from bis.Element WHERE ECInstanceId=? AND Name=:name",
  params
)) {
  const obj = { id: row[0], name: row[1] };
  // ...
}

4.4.2 使用 query 方法升级代码

相应的方法使用如下所示:

query(ecsql: string, params?: QueryBinder, options?: QueryOptions): AsyncIterableIterator<any>;

options参数的rowFormat属性默认为QueryRowFormat。使用 EcSqlPropertyIndex。该格式效率更高,因此首选该格式,但它与以前的行格式不同。您可以将现有代码升级为使用旧格式,只需进行最小的更改。例如,如果现有代码以数组形式传递查询参数,请按如下所示进行更改:

// Replace this:
db.query("SELECT * FROM bis.Element WHERE ECInstanceId=?", ["0x1"]);
// With this:
db.query(
  "SELECT * FROM bis.Element WHERE ECInstanceId=?",
  QueryBinder.from(["0x1"]),
  { rowFormat: QueryRowFormat.UseJsPropertyNames }
);
// The code that accesses the properties of each row can remain unchanged.

类似地,如果现有代码将对象而不是数组作为查询参数传递,请按如下所示进行更改:

// Replace this:
db.query("SELECT * FROM bis.Element WHERE ECInstanceId = :id", { id: "0x1" });
// With this:
db.query(
  "SELECT * FROM bis.Element WHERE ECInstanceId=?",
  QueryBinder.from({ id: "0x1" }),
  { rowFormat: QueryRowFormat.UseJsPropertyNames }
);
// The code that accesses the properties of each row can remain unchanged.

4.4.3 使用 restartQuery 方法升级代码

参数的更改方式与 query 相同,因此可以按照上面 query 的说明进行更改。

4.4.4 使用 queryRowCount 方法升级代码

此方法的使用没有改变,但必须以 QueryBinder 对象而不是数组或对象的形式提供参数。按照上述查询所述升级现有代码。

1.4.5. 4.5 其他

更多更新可查看:

1.5. 5. 依赖包升级

1.5.1. 5.1 升级最低要求

已放弃对 Node 10 的支持。新的最低 Node 版本是 12.22.0。建议的版本是 Node 的最新 LTS 版本。请访问我们支持的平台文档,了解完整的兼容性分类。

1.6. 6. 其他 API 重命名及弃用

请参阅 https://www.itwinjs.org/changehistory/#api-rename

results matching ""

    No results matching ""