Spatial Queries
Every instance of the SpatialElement ECClass in an iModel is spatially indexed. The spatial index allows fast look-ups by spatial criteria. Each row in the spatial index is conceptually a bounding cube, plus the ECInstanceId of an element that is enclosed by it.
The index is exposed to ECSQL via the ECClass BisCore.SpatialIndex. See ECSQL Reference for details.
Try it yourself
Goal: Return all SpatialElements that are contained or overlap a cube defined by the minimum coordinate (0, 0, 0) and maximum coordinate (415|120|15).
ECSQL
SELECT e.ECInstanceId, e.UserLabel, i.MinX, i.MinY, i.MinZ, i.MaxX, i.MaxY, i.MaxZ FROM bis.SpatialElement e JOIN bis.SpatialIndex i ON e.ECInstanceId=i.ECInstanceId WHERE i.MinX<=415 AND i.MinY<=120 AND i.MinZ<=15 AND i.MaxX >= 0 AND i.MaxY >= 0 AND i.MaxZ >= 0
For more complex spatial criteria the MATCH
keyword together with the special built-in spatial index matching function iModel_spatial_overlap_aabb
is used. The MATCH clause acts like a sub-selection that generates a set of ECInstanceIds, which it gathers from the spatial index rows that match the specified criteria.
The function iModel_spatial_overlap_aabb
selects all nodes that overlap a specified axis-aligned bounding box.
See also other ECSQL built-in geometry functions which can be used along with spatial queries as additional WHERE criteria.
Try it yourself The argument to the match function can only be passed via an ECSQL parameter. As the iModelConsole does not support parameter values, the following example cannot be tried out with the iModelConsole. You would have to put the sample code into your own playground.
Goal: Return all SpatialElements which overlap the Space with id 0x1000000001f and which are in the Category with id 0x1000000000a.
ECSQL
SELECT e.ECInstanceId, e.CodeValue FROM bis.SpatialElement e JOIN bis.SpatialIndex i ON e.ECInstanceId=i.ECInstanceId WHERE i.ECInstanceId MATCH iModel_spatial_overlap_aabb(?) AND e.Category.Id=0x1000000000a
Sample code
const spaceElement: SpatialElement = iModelDb.elements.getElement( "0x1000000001f", ) as SpatialElement; iModelDb.withPreparedStatement( "SELECT e.ECInstanceId, e.ECClassId, e.CodeValue FROM bis.SpatialElement e JOIN bis.SpatialIndex i ON e.ECInstanceId=i.ECInstanceId WHERE i.ECInstanceId MATCH iModel_spatial_overlap_aabb(?) AND e.Category.Id=0x1000000000a", (stmt: ECSqlStatement) => { stmt.bindRange3d(1, spaceElement.placement.calculateRange()); while (stmt.step() === DbResult.BE_SQLITE_ROW) { const row: any = stmt.getRow(); console.log(row); } }, );
Result
{ id : "0x1000000001e", className: "MyDomain.Story", codeValue: "A-G" } { id : "0x10000000023", className: "MyDomain.Story", codeValue: "A-1" }
Last Updated: 13 May, 2024