Retrieve Validation Model Data

Introduction

Model validation functions are used to check a model you’ve created to make sure everything is as expected. In the case of property validation, this means making sure element properties fit specific criteria. In the case of clash detection, this means certain elements are not touching or are within a certain range of each other.

Info

Skill level:

Basic

Duration:

20 minutes

1. (Optional) Create a Test iTwin and iModel for testing

Follow the steps listed here

You will be redirected to the "My iTwins - iModel Home" page. Click the "Copy IDs" button in the top right. The iTwin ID (also referred to as the project ID) and the iModel ID will be copied to the clipboard. Both of these IDs will be used frequently throughout validation API calls.

2. Get a token

To make API requests, a user token is needed. There are several ways to get it.

Implement Authorization Code Flow in the application

Follow this article to implement Authorization code workflow in your application. You will need to include the scopes clashdetection:read and clashdetection:modify.

Grab a user token from the API reference “Try it out” Section

  1. Go here
  2. Click "Try it out" button.
  3. On Authorization section select "AuthorizationCode".
  4. After popup closes Authorization header with your user token value should be visible.
  5. Save user token value for this tutorial.

Use the user token to replace JWT_TOKEN dynamic parameter in the next steps.

3. Retrieving Models and Categories

For creating any Clash Detection test, you will need a list of the models or categories you wish to check for clashing elements.

There are two ways to retrieve these:

3.1 Extraction/Retrieval Request APIs

First, you can send an HTTP request to POST https://api.bentley.com/clashdetection/modelsAndCategories/imodels/{iModelId} using your iModel ID in place of {iModelId}. If model information has not previously been extracted for the iModel, an agent will be started to process the request. A status of 202 is returned if the agent was successfully started.

Next, you can repeat calls to GET https://api.bentley.com//clashdetection/modelsAndCategories/imodels{iModelId} until a status of available is returned with the response.

The response will include a complete list of models and categories, with their respective ids and display names.

Extraction Request Syntax


HTTP
POST https://api.bentley.com/clashdetection/modelsAndCategories/imodels/00000000-0000-0000-0000-000000000000 HTTP/1.1

Request Headers


HTTP
Accept: application/vnd.bentley.itwin-platform.v1+json
Authorization: Bearer JWT_TOKEN

Request Body


JSON
{
  "projectId": "00000000-0000-0000-0000-000000000000"
}

Retrieval Request Syntax


HTTP
GET https://api.bentley.com/clashdetection/modelsAndCategories/imodels/00000000-0000-0000-0000-000000000000?projectId=00000000-0000-0000-0000-000000000000 HTTP/1.1

Request Headers


HTTP
Accept: application/vnd.bentley.itwin-platform.v1+json
Authorization: Bearer JWT_TOKEN

Response Body


JSON
{
  "status":"available",
  "models":[
    {
      "id":"0x21",
      "displayName":"ProcessPhysicalModel"
    }
  ],
  "categories":[
    {
      "id":"0x20000000002",
      "displayName":"Uncategorized"
    },
    {
      "id":"0x30000000048",
      "displayName":"PID LineStyle Default"
    },
    {
      "id":"0x3000000004a",
      "displayName":"Border"
    },
    {
      "id":"0x4000000000d",
      "displayName":"Tag-Category"
    },
    {
      "id":"0x40000000e71",
      "displayName":"Structure"
    }
  ]
}

3.2 Direct iModel Query

For applications with access to an iModel, you can directly query for the list of models and categories.

3.2.1 Creating a Sample App

To assist you with implementing this, we have included two sample widgets on the right which can be dropped into an iTwin viewer app.

For help with creating an iTwin viewer app, see iTwin Viewer Quick Start.

Once you have a sample app ready to test with, you can create a couple new .tsx files called CategoryListWidget.tsx and ModelListWidget.tsx and copy the sample code into them.

Sample Category List widget and provider


TYPESCRIPT
import { useCallback, useEffect, useMemo, useState } from "react";
import { useActiveIModelConnection } from "@itwin/appui-react";
import { AbstractWidgetProps, StagePanelLocation, StagePanelSection, UiItemsProvider, WidgetState } from "@itwin/appui-abstract";
import { QueryRowFormat } from "@itwin/core-common";
import { Table } from "@itwin/itwinui-react";
export interface CategoryInfo {
  id: string;
  name: string;
};
export type CreateTypeFromInterface<Interface> = {
  [Property in keyof Interface]: Interface[Property];
};
export type CategoryInfoType = CreateTypeFromInterface<CategoryInfo>;
const CategoryListWidget = () => {
  const [availableCategories, setAvailableCategories] = useState<CategoryInfoType[]>([]);
  const iModel = useActiveIModelConnection();
  // Example for querying list of all categories in the iModel
  const queryCategories = useCallback(async (): Promise<CategoryInfo[]> => {
    const categories: CategoryInfo[] = [];
    // Query for list of unique category ids in iModel
    const selectUsedSpatialCategoryIds = "SELECT DISTINCT Category.Id as id from BisCore.GeometricElement3d WHERE Category.Id IN (SELECT ECInstanceId from BisCore.SpatialCategory)";
    // Use category ids to query for label and code values
    const ecsql = "SELECT ECInstanceId as id, UserLabel as label, CodeValue as code FROM BisCore.SpatialCategory WHERE ECInstanceId IN (" + selectUsedSpatialCategoryIds + ")";
    const rowIterator = iModel?.query(ecsql, undefined, { rowFormat: QueryRowFormat.UseJsPropertyNames });
    if (rowIterator) {
        for await (const row of rowIterator) {
        categories.push({ id: row.id, name: row.label ?? row.code });
      }
    }  
    return categories;
  }, [iModel]);
  useEffect(() => {
    queryCategories()
      .then((categoryInfos: CategoryInfo[]) => {
        setAvailableCategories(categoryInfos);
      })
      .catch((_e) => {
        setAvailableCategories([]);
      });
  }, [queryCategories]);
  const columnDefinition = useMemo(() => [
    {
      Header: "Table",
      columns: [
        {
          id: "categoryId",
          Header: "Id",
          accessor: "id",
        },
        {
          id: "categoryName",
          Header: "Name",
          accessor: "name",
        },
      ],
    },
  ], []);
  return (
    <Table
      data={availableCategories}
      columns={columnDefinition}
      emptyTableContent={"No data"}
      density="extra-condensed"
      style={{ height: "100%" }} />
  );
};
export class CategoryListWidgetProvider implements UiItemsProvider {
  public readonly id: string = "CategoryListWidgetProvider";
  public provideWidgets(_stageId: string, _stageUsage: string, location: StagePanelLocation, _section?: StagePanelSection): ReadonlyArray<AbstractWidgetProps> {
    const widgets: AbstractWidgetProps[] = [];
    if (location === StagePanelLocation.Bottom && _section === StagePanelSection.Start) {
      widgets.push(
        {
          id: "CategorListWidget",
          label: "Category List",
          defaultState: WidgetState.Open,
          getWidgetContent: () => <CategoryListWidget />,
        }
      );
    }
    return widgets;
  }
}

Sample Model List widget and provider


TYPESCRIPT
import { useCallback, useEffect, useMemo, useState } from "react";
import { AbstractWidgetProps, StagePanelLocation, StagePanelSection, UiItemsProvider, WidgetState } from "@itwin/appui-abstract";
import { useActiveIModelConnection } from "@itwin/appui-react";
import { Table } from "@itwin/itwinui-react";
import type { GeometricModel3dProps, ModelQueryParams } from "@itwin/core-common";
export type CreateTypeFromInterface<Interface> = {
  [Property in keyof Interface]: Interface[Property];
};
export interface ModelInfo {
  id: string;
  name: string;
}
export type ModelInfoType = CreateTypeFromInterface<ModelInfo>;
const ModelListWidget = () => {
  const [availableModels, setAvailableModels] = useState<ModelInfoType[]>([]);
  const iModel = useActiveIModelConnection();
  // Example for querying list of all 3D models in the iModel
  const queryModels = useCallback(async (): Promise<ModelInfo[]> => {
    const queryParams: ModelQueryParams = {
      from: "BisCore.GeometricModel3d",
      wantPrivate: false,
    };
    const modelProps = await iModel?.models.queryProps(queryParams) ?? [];
    return modelProps
      .map(({ id, name }: GeometricModel3dProps) => ({ id, name }))
      .filter(({ id }) => id) as ModelInfo[];
  }, [iModel]);
  useEffect(() => {
    queryModels()
      .then((modelInfos: ModelInfo[]) => {
        setAvailableModels(modelInfos);
      })
      .catch((_e) => {
        setAvailableModels([]);
      });
  }, [queryModels]);
  const columnDefinition = useMemo(() => [
    {
      Header: "Table",
      columns: [
        {
          id: "id",
          Header: "Id",
          accessor: "id",
        },
        {
          id: "modelName",
          Header: "Name",
          accessor: "name",
        },
      ],
    },
  ], []);
  return (
    <Table
      data={availableModels}
      columns={columnDefinition}
      emptyTableContent={"No data"}
      density="extra-condensed"
      style={{ height: "100%" }} />
  );
};
export class ModelListWidgetProvider implements UiItemsProvider {
  public readonly id: string = "ModelListWidgetProvider";
  public provideWidgets(_stageId: string, _stageUsage: string, location: StagePanelLocation, _section?: StagePanelSection): ReadonlyArray<AbstractWidgetProps> {
    const widgets: AbstractWidgetProps[] = [];
    if (location === StagePanelLocation.Bottom && _section === StagePanelSection.Start) {
      widgets.push(
        {
          id: "ModelListWidget",
          label: "Model List",
          defaultState: WidgetState.Open,
          getWidgetContent: () => <ModelListWidget />,
        }
      );
    }
    return widgets;
  }
}

3.2.2 Adding the widgets to the application

Once you have the viewer app ready with the sample widgets copied in, you can get them hooked up by adding the provider names to the uiProviders property of the Viewer react component (as shown on the right).

If you want a more in-depth explanation on the usage of providers, see iTwin Viewer Hello World tutorial.

Sample imports for widget providers


TYPESCRIPT
import { CategoryListWidgetProvider } from "./CategoryListWidget";
import { ModelListWidgetProvider } from "./ModelListWidget";

Sample for adding the CategoryListWidgetProvider and ModelListWidgetProvider to your iTwin viewer app.


JSX
<Viewer
  iTwinId={iTwinId ?? ""}
  iModelId={iModelId ?? ""}
  authClient={authClient}
  onIModelAppInit={onIModelAppInit}
  uiProviders={[
    new CategoryListWidgetProvider(),
    new ModelListWidgetProvider(),
  ]}
/>

4. Get Schema Info

For creating Property Validation rules you will need to get all the class/property names in the iModel schemas.

First, you can send an HTTP request to POST https://api.bentley.com/clashdetection/schemas/imodels/{iModelId} using your iModel ID in place of {iModelId}. If schema information has not previously been extracted for the iModel, an agent will be started to process the request. A status of 202 is returned if the agent was successfully started.

Next, you can repeat calls to GET https://api.bentley.com//clashdetection/schemas/imodels{iModelId} until a status of available is returned with the response.

The response will include a complete tree of schemas, classes and associated properties. For creating property validation rules, you will be using the name values. However, the label values may help you identify the correct classes and properties.

Extraction Request Syntax


HTTP
POST https://api.bentley.com/clashdetection/schemas/imodels/00000000-0000-0000-0000-000000000000 HTTP/1.1

Request Headers


HTTP
Accept: application/vnd.bentley.itwin-platform.v1+json
Authorization: Bearer JWT_TOKEN

Request Body


JSON
{
  "projectId": "00000000-0000-0000-0000-000000000000"
}

Retrieval Request Syntax


HTTP
GET https://api.bentley.com/clashdetection/schemas/imodels/00000000-0000-0000-0000-000000000000?projectId=00000000-0000-0000-0000-000000000000 HTTP/1.1

Request Headers


HTTP
Accept: application/vnd.bentley.itwin-platform.v1+json
Authorization: Bearer JWT_TOKEN

Response Body


JSON
{
  "schema":[
    {
      "name":"ProcessPhysical",
      "label":null,
      "entityClass":[
        {
          "name":"PIPING_COMPONENT",
          "label":"Piping Component",
          "properties":[
            {
              "name":"COMPONENT_NAME",
              "label":"Component Name"
            },
            {
              "name":"STATE",
              "label":"Component State"
            },
            {
              "name":"LENGTH_EFFECTIVE",
              "label":"Length Effective"
            },
            {
              "name":"DESIGN_LENGTH_CENTER_TO_BRANCH_END_EFFECTIVE",
              "label":"Design Length Center To Branch End Effective"
            },
            {
              "name":"DESIGN_LENGTH_CENTER_TO_OUTLET_END_EFFECTIVE",
              "label":"Design Length Center To Outlet End Effective"
            },
            {
              "name":"DESIGN_LENGTH_CENTER_TO_RUN_END_EFFECTIVE",
              "label":"Design Length Center To Run End Effective"
            },
            {
              "name":"WALL_THICKNESS",
              "label":"Wall Thickness"
            },
            {
              "name":"UPDATE_GRAPHICS",
              "label":"Update Graphics"
            },
            {
              "name":"NOMINAL_DIAMETER_RUN_END",
              "label":"Nominal Diameter Run End"
            },
            {
              "name":"Geometry",
              "label":"Element Geometry"
            },
            {
              "name":"SPOOL_ID",
              "label":"Spool Id"
            },
            {
              "name":"SPOOL_NUMBER",
              "label":"Spool Number"
            },
            {
              "name":"OPTION_CODE",
              "label":"Option Code"
            },
            {
              "name":"NOTES",
              "label":"Notes"
            },
            {
              "name":"CATALOG_NAME",
              "label":"Catalog Name"
            },
            {
              "name":"EC_CLASS_NAME",
              "label":"EC Class Name"
            },
            {
              "name":"UNIT_OF_MEASURE",
              "label":"Unit Of Measure"
            },
            {
              "name":"PIECE_MARK",
              "label":"Piece Mark"
            },
            {
              "name":"FABRICATION_CATEGORY",
              "label":"Fabrication Category"
            },
            {
              "name":"LINENUMBER",
              "label":"Line Number"
            },
            {
              "name":"INSULATION_THICKNESS",
              "label":"Insulation Thickness"
            },
            {
              "name":"INSULATION",
              "label":"Insulation Material"
            },
            {
              "name":"LENGTH",
              "label":"Length"
            },
            {
              "name":"INSIDE_DIAMETER",
              "label":"Inside Diameter"
            },
            {
              "name":"NORMAL_OPERATING_PRESSURE",
              "label":"Normal Operating Pressure"
            },
            {
              "name":"OUTSIDE_DIAMETER",
              "label":"Outside Diameter"
            },
            {
              "name":"PIPE_FLANGE_TYPE",
              "label":"Pipe Flange Type"
            },
            {
              "name":"GRADE",
              "label":"Grade"
            },
            {
              "name":"SHOP_FIELD",
              "label":"Shop Field"
            },
            {
              "name":"HUB_DEPTH",
              "label":"Hub Depth"
            },
            {
              "name":"HUB_WIDTH",
              "label":"Hub Width"
            },
            {
              "name":"TRACING",
              "label":"Tracing"
            }
          ],
          "aspects":[],
          "typeDefinitions":[]
        }
      ]
    }
  ]
}

4. Conclusion

You should be able to successfully retrieve the related IDs and schema info for your model. While this information is of limited value on its own, it is useful and necessary for creating clash detection and property validation tests, so check out the continue learning section to put your new knowledge to use.

More resources that you may like