Skip to content

Empty result on bounding box query #1377

Open

Description

@aothms

Describe the bug

I'm using the python bimserver client to try and download a spatial subset of an IFC model and I'm running into the following issues:

  • I can't get the NotificationRegistryInterface.getProgress() to work reliably, this is probably a user error because the web interface seems to handle this fine (but maybe partly with websockets?) I only get state=STARTED even when the subsequent download works fine.
  • The result for my inBoundingBox query seems to be always empty. I tried various permutations of this. This also seems to be the case in bimvie.ws
  • (I can foresee some issues in the ifc2x3tc1-stdlib:... approach, without the includes one gets a file with just those direct elements, but let's say I want to write a query for all IfcMaterialLayerSets and I want the result to include the traversal to all layers + materials, are the includes for every potential attribute needed, is there a generic forward traversal? Also the fact that this is schema specific seems non-optimal, what if I would want to query multiple roids with diverse schemas (assuming this becomes possible at some point))

To Reproduce

Python script:

python

import json
from pathlib import Path
import sys
from time import sleep
import ifcopenshell
import base64

sys.path.append(str(Path(__file__).resolve().parent / "python-bimserver-client"))

import bimserver

hostname = "http://localhost:9000"
client = bimserver.Api(hostname, "admin@admin.admin", "admin")

serializer_id = client.ServiceInterface.getSerializerByName(serializerName="Ifc2x3tc1 (Streaming)").get("oid")
deserializer_id = client.ServiceInterface.getDeserializerByName(deserializerName="Ifc2x3tc1 (Streaming)").get("oid")


def get_or_create_project(name):
    existing = client.ServiceInterface.getProjectsByName(name=name)
    if existing:
        return existing[0].get("oid")
    else:
        return client.ServiceInterface.addProject(projectName=name, schema="ifc2x3tc1").get("oid")


project_id = get_or_create_project("Duplex (arch)")
revision_id = client.ServiceInterface.getProjectByPoid(poid=project_id).get("lastRevisionId")

if revision_id == -1:

    with open("Duplex_A_20110907_optimized.ifc", "rb") as f:
        ifc_data = f.read()

    revision_id = client.ServiceInterface.checkinSync(
        poid=project_id,
        comment="my first commit",
        deserializerOid=deserializer_id,
        fileSize=len(ifc_data),
        fileName="Duplex_A_20110907_optimized.ifc",
        data=base64.b64encode(ifc_data).decode("utf-8"),
        sync="false",
        merge="false",
    ).get("roid")

query = {
    "type": {"name": "IfcWall", "includeAllSubTypes": True},
    "includes": [
        "ifc2x3tc1-stdlib:ContainedInStructure",
        "ifc2x3tc1-stdlib:OwnerHistory",
        "ifc2x3tc1-stdlib:Representation",
        "ifc2x3tc1-stdlib:ObjectPlacement",
    ],
}

topicId = client.ServiceInterface.download(
    query=json.dumps(query),
    roids=[revision_id],
    serializerOid=serializer_id,
    sync=False,
)

# while (state := client.NotificationRegistryInterface.getProgress(topicId=topicId).get('state')) != 'FINISHED':
#     print(state)
#     sleep(1.)

sleep(1.0)

content = (
    bimserver.urlopen(f"{hostname}/download?token={client.token}&topicId={topicId}", data=b"").read().decode("ascii")
)
ifc_file = ifcopenshell.file.from_string(content)

print(ifc_file)

print({i.is_a() for i in ifc_file})
# {'IfcWall', 'IfcWallStandardCase'}
# {'IfcAxis2Placement3D', 'IfcPersonAndOrganization', 'IfcCurveStyleFontPattern', 'IfcSurfaceStyle', 'IfcExtrudedAreaSolid', 'IfcDirection', 'IfcCartesianPoint', 'IfcRelAggregates', 'IfcRelContainedInSpatialStructure', 'IfcMaterialLayer', 'IfcFillAreaStyle', 'IfcGeometricRepresentationSubContext', 'IfcRelAssociatesMaterial', 'IfcPolyLoop', 'IfcMaterial', 'IfcBooleanClippingResult', 'IfcOwnerHistory', 'IfcApplication', 'IfcDraughtingPreDefinedCurveFont', 'IfcDimensionalExponents', 'IfcShapeRepresentation', 'IfcMaterialLayerSet', 'IfcStyledItem', 'IfcAxis2Placement2D', 'IfcConversionBasedUnit', 'IfcGeometricRepresentationContext', 'IfcFaceBasedSurfaceModel', 'IfcRectangleProfileDef', 'IfcFillAreaStyleHatching', 'IfcBuildingStorey', 'IfcProductDefinitionShape', 'IfcColourRgb', 'IfcProject', 'IfcPerson', 'IfcMaterialLayerSetUsage', 'IfcSIUnit', 'IfcFaceOuterBound', 'IfcPlane', 'IfcOrganization', 'IfcConnectedFaceSet', 'IfcBuilding', 'IfcWallStandardCase', 'IfcLocalPlacement', 'IfcMeasureWithUnit', 'IfcCurveStyle', 'IfcArbitraryClosedProfileDef', 'IfcPolyline', 'IfcSurfaceStyleRendering', 'IfcCurveStyleFont', 'IfcMaterialDefinitionRepresentation', 'IfcPresentationStyleAssignment', 'IfcWall', 'IfcStyledRepresentation', 'IfcFace', 'IfcUnitAssignment', 'IfcPolygonalBoundedHalfSpace', 'IfcSite'}

query = {
    "type": "IfcElement",
    "includeAllSubTypes": True,
    "inBoundingBox": {
        "x": -10000,
        "y": -10000,
        "z": -10000,
        "width": 20000,
        "height": 20000,
        "depth": 20000,
    },
}

topicId = client.ServiceInterface.download(
    query=json.dumps(query),
    roids=[revision_id],
    serializerOid=serializer_id,
    sync=False,
)
while client.NotificationRegistryInterface.getProgress(topicId=topicId).get("state") != "FINISHED":
    sleep(1.0)
content = (
    bimserver.urlopen(f"{hostname}/download?token={client.token}&topicId={topicId}", data=b"").read().decode("ascii")
)

ifc_file = ifcopenshell.file.from_string(content)

print(ifc_file.to_string())

Activity

  1. hlg commented on Jul 27, 2025

    @hlg
    Member

    I try to provide quick answers to the three questions without looking into the details:

    • notifications: You get FINISHED state only when the download is actually finished and that will not happen before you call download. If you want to wait for the preparations on the server side, check for the title to be "Done preparing" (see BIMvie.ws, the Javascript client or the Java client - BimServerClient.waitForDonePreparing). I think that could actually be an enumerated state in itself, but is currently not. For streaming serializers there is not much happening upfront as they start writing as soon as possible. For the non-streaming serializers though, the results are fetched from the database into memory before the actual serialization process starts and results are streamed - which may take noticeable time. You can also have a look at how the LevelOut webapp shows progress, although the code has some overhead I guess - and I am not sure how reliable that worked.
    • query via boundaries: I just tried by setting very large boundaries and got entities included in the result - so it should actually work. Note that it is apparently based on boundary values in millimeter.
    • query library: A query as such is not schema-specific, but the libraries are, of course. This actually helps to abstract from the schema, because you can define schema-specific includes under the same name to account for the differences. There is a catch-all query, but none that just follows references from given entry points. I think this is because for most useful queries you want to follow not just direct attributes, but inverses as well and as soon as you include inverse traversal, it does not make sense anymore to generally follow all attributes.

    Maybe that helps already.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions