bimfabrikhh_core_rs
A Rust library with Python bindings: parse CityGML 1.0 / 2.0 and CityJSON 1.1 and write IFC4 STEP.
About This Project
bimfabrikhh_core_rs is developed and maintained by Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung (LGV), BIM-Leitstelle
This library is part of BIMFabrikHH. The core is implemented in Rust; Python bindings expose the conversion API. It converts urban building models to IFC4 STEP.
This project is in an early stage of development. The API and features will keep evolving.
Author: Ahmed Salem (BIM-Leitstelle, LGV Hamburg)
Quick Install
pip install bimfabrikhh_core_rs
Optional pydantic helpers for property-set specs, IFC type maps, and owner identity:
pip install "bimfabrikhh_core_rs[psets]"
Wheels are published for Linux x86_64, Windows x64, and macOS Apple Silicon. One wheel per platform works for Python 3.11+.
Overview
The conversion API has no Python runtime dependencies and does not use ifcopenshell. Geometry is parsed and written in
Rust. You fetch the data; the package converts it. There is no HTTP inside the library.
- CityGML 1.0 / 2.0 and CityJSON 1.1 → one IFC4 STEP file
mode="typed"— one product per semantic surface (IfcWall,IfcRoof,IfcSlab, …)mode="mesh"— oneIfcBuildingElementProxyper building- Property sets, colours, CAD layer, and owner history are opt-in
- Coordinates stay in the CRS they arrive in, unless you pass
transform=(e.g. pyproj);crstags the file (omit it for no georeferencing)
Public API
Conversion
gml_to_ifc(path, ifc_path, ...)— CityGML file or list of files → IFCcityjson_to_ifc(source, ifc_path, ...)— CityJSON path, list of paths, or document text → IFCwrite_ifc(products, path, ...)— IFC from product dicts you built yourselftrees_to_ifc(trees, ifc_path, ...)— trunk + crown meshes → IFC (TreesGenericApp path). Optionaldrape_vertices/drape_facessetsz = 0from the DGM triangle (else nearest vertex).terrain_to_ifc(vertices, faces, ifc_path, ...)— triangulated DGM → IFC (TerrainGenericApp write path)
Parse
parse_gml(path, ...)— CityGML → list of building dictsparse_cityjson(path, ...)— CityJSON file → building dictsparse_cityjson_text(text, ...)— CityJSON already in memory (after HTTP)declared_crs(path)— EPSG codes a CityGML (srsName) or CityJSON (referenceSystem) file declares
Optional helpers (bimfabrikhh_core_rs[psets])
PsetSpec/Attr/Const/Quantity— declarative property-set ruleshamburg_defaults()/attribute_mapping.specs()/tree_mapping.specs()/terrain_mapping.specs()— Hamburg property nameshamburg_ifc_types()/IfcTypeMap— surface type → IFC entityOwnerIdentity/owner.defaults()— FILE_NAME +IfcPerson/IfcOrganization/IfcApplication
Coordinate system (bimfabrikhh_core_rs.crs, no dependencies)
etrs89_utm(zone, vertical_datum=...)/projected_crs(...)—IfcProjectedCRSvalues forcrs=
Key Features
- No HTTP — fetch tiles yourself; pass paths or CityJSON text
- Typed or mesh — per-surface products or one block per building
- Declarative psets — omit
psets=and none are written - Deterministic GUIDs — the same input produces a byte-identical IFC
- Hamburg-ready — optional type map, property mapping,
_BIM_Stadtmodelllayer, and Nullpunkt
Complete Working Example
from bimfabrikhh_core_rs import cityjson_to_ifc, gml_to_ifc, parse_cityjson_text
from bimfabrikhh_core_rs.crs import etrs89_utm
# CityGML tile → typed IFC (IfcWall / IfcRoof / IfcSlab per surface)
gml_to_ifc("tile.gml", "out.ifc", mode="typed", crs=etrs89_utm(33, vertical_datum="EPSG:7837"))
# CityJSON → one IfcBuildingElementProxy mesh per building
cityjson_to_ifc("page.city.json", "blocks.ifc", mode="mesh", crs=etrs89_utm(32, vertical_datum="EPSG:7837"))
# Inspect before writing
buildings = parse_cityjson_text(open("page.city.json").read())
print(len(buildings), buildings[0]["gml_name"])
Crop, colours, and Hamburg types:
from bimfabrikhh_core_rs import cityjson_to_ifc
from bimfabrikhh_core_rs.attribute_mapping import specs
from bimfabrikhh_core_rs.crs import etrs89_utm
from bimfabrikhh_core_rs.ifc_types import hamburg_ifc_types
cityjson_to_ifc(
"page.city.json",
"hamburg.ifc",
mode="typed",
crs=etrs89_utm(32, vertical_datum="EPSG:7837"),
bbox_epsg=(565012.0, 5933559.0, 566570.0, 5934571.0),
basepoint=(565012.0, 5933559.0),
ifc_types=hamburg_ifc_types(),
psets=specs(),
cad_layer=True,
)
mode is "typed" or "mesh". Omit ifc_types and every product is IfcBuildingElementProxy. Omit psets and the
IFC has no property sets. bbox_epsg is (min_x, min_y, max_x, max_y) in the CRS of the source file.
Rust has no PROJ. To write the buildings in another CRS, pass transform: any callable transform(xs, ys) -> (xs, ys),
e.g. pyproj's Transformer.transform. It is called once with the x/y of all kept buildings (after the bbox_epsg
filter); Z is not changed. crs and basepoint then describe the output CRS:
from pyproj import Transformer
from bimfabrikhh_core_rs import gml_to_ifc
from bimfabrikhh_core_rs.crs import projected_crs
to_gk = Transformer.from_crs("EPSG:25832", "EPSG:10285", always_xy=True)
gml_to_ifc(
"tile.gml",
"gk.ifc",
transform=to_gk.transform,
crs=projected_crs("EPSG:10285", "ETRS89/DREF91/2016", map_projection="Transverse Mercator", map_zone="3",
vertical_datum="EPSG:7837"),
)
crs is the map CRS of the coordinates, written as IfcProjectedCRS + IfcMapConversion. Rust has no PROJ, so you
pass the fields: a dict from bimfabrikhh_core_rs.crs (etrs89_utm(32, vertical_datum="EPSG:7837") for UTM 32N with
DHHN2016 heights), any dict with the keys name, geodetic_datum and optionally description, vertical_datum,
map_projection, map_zone, or an object with model_dump() such as BIMFabrikHH_core's CoordinateSystem. Omit
crs and the IFC gets no georeferencing; leave vertical_datum out when the height system is unknown.
gml_to_ifc and cityjson_to_ifc check crs against what the files declare (srsName, e.g.
urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH, or CityJSON metadata.referenceSystem). A missing vertical_datum is
filled from the files; a different EPSG: vertical datum raises, and so does a different horizontal code unless you
pass transform. Files that declare different CRS raise as well. Heights are never transformed.
from bimfabrikhh_core_rs import declared_crs
declared_crs("tile.gml")
# {'srs_name': 'urn:adv:crs:ETRS89_UTM32*DE_DHHN2016_NH', 'horizontal_epsg': 25832, 'vertical_epsg': 7837}
Trees (same look as BIMFabrik TreesGenericApp; Python still prepares records):
from bimfabrikhh_core_rs import trees_to_ifc
from bimfabrikhh_core_rs.crs import etrs89_utm
from bimfabrikhh_core_rs.tree_mapping import specs
trees_to_ifc(
[
{
"name": "Baum_001",
"position": (565100.0, 5933600.0, 0.0),
"trunk_radius": 0.3,
"trunk_height": 4.0,
"crown_radius": 2.5,
"detail": 1,
"segments": 8,
"attributes": {"baumnummer": "001", "gattung_deutsch": "Eiche", "art_baum": "Quercus robur"},
}
],
"trees.ifc",
crs=etrs89_utm(32, vertical_datum="EPSG:7837"),
psets=specs(),
basepoint=(565100.0, 5933600.0),
# drape_vertices=mesh.vertices, # optional: lift z=0 from nearest DGM sample
)
trunk_height is the cylinder height, not the Stammhöhe. The crown sphere is centred on the cylinder top, so pass
Stammhöhe + crown_radius (BIMFabrikHH TreeDimensions.trunk_cylinder_height). The treetop then ends at
Stammhöhe + Kronendurchmesser, the published _Baumhoehe. In the example above the Stammhöhe is 1.5 m and the treetop
is at 6.5 m.
Terrain (Python still samples / Delaunay; Rust only writes IFC):
from bimfabrikhh_core_rs import terrain_to_ifc
from bimfabrikhh_core_rs.crs import etrs89_utm
from bimfabrikhh_core_rs.terrain_mapping import specs
terrain_to_ifc(vertices, faces, "dgm.ifc", crs=etrs89_utm(32, vertical_datum="EPSG:7837"), psets=specs(), basepoint=(565000.0, 5933000.0))
Property sets
Pass psets= a list of specs describing rules rather than values. The spec is validated once and evaluated in Rust for
every building and surface.
from bimfabrikhh_core_rs import cityjson_to_ifc
from bimfabrikhh_core_rs.psets import Attr, Const, PsetSpec, Quantity
building = PsetSpec(
name="Pset_Objektinformation",
scope="building",
properties={
"_IDEbene1": Const("Stadtmodell"),
"_StadtmodellLoD": Attr("lod"),
"_Dachform": Attr("roof_type", catalog="dachform"),
"_GrundhoeheNN": Attr("extra:Grundhöhe NN"),
},
)
surface = PsetSpec(
name="BIMFabrikHH_Quantities",
scope="surface",
properties={
"GrossArea": Quantity("area", decimals=4),
"SurfaceType": Quantity("surface_type"),
},
)
cityjson_to_ifc("page.city.json", "out.ifc", psets=[building, surface])
| Value | Reads |
|---|---|
Const(value) |
a fixed string |
Attr(key, catalog=…, decimals=…, default=…) |
a building field (id, gml_name, lod, function, measured_height, storeys_above_ground, roof_type, or extra:<name>) |
Quantity(key, decimals=…) |
surface geometry (area, perimeter, tilt, surface_type) |
A missing source field omits that property. A plain dict works without pydantic.
Other options
from bimfabrikhh_core_rs import cityjson_to_ifc
from bimfabrikhh_core_rs.ifc_types import IfcTypeMap
from bimfabrikhh_core_rs.owner import defaults
cityjson_to_ifc(
"page.city.json",
"out.ifc",
ifc_types=IfcTypeMap(RoofSurface="IfcRoof", WallSurface="IfcWall"),
colors={"RoofSurface": (1.0, 0.0, 0.0)},
owner=defaults(),
)
Default colours match BIMFabrikHH: roofs red, everything else yellow. styles=False writes no colour. Owner history
FILE_NAME author defaults to BIM-Leitstelle, LGV Hamburg; owner_history=False skips IfcOwnerHistory.
Examples
Additional examples are available in the GitHub repository.
Links
- PyPI: https://pypi.org/project/bimfabrikhh-core-rs/
- GitHub: https://github.com/LGV-BIM-Leitstelle/bimfabrikhh_core_rs
- Issues: https://github.com/LGV-BIM-Leitstelle/bimfabrikhh_core_rs/issues
License
This project is licensed under the GNU Lesser General Public License v2.1 (LGPL-2.1).
Copyright (C) 2026 Freie und Hansestadt Hamburg, Landesbetrieb Geoinformation und Vermessung BIM-Leitstelle, Ahmed Salem
Metadata
Release files for bimfabrikhh-core-rs 0.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| bimfabrikhh_core_rs-0.2.0.tar.gz | 113.2 kB | With provenance | Details for bimfabrikhh_core_rs-0.2.0.tar.gz |
Built distributions (wheels)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| bimfabrikhh_core_rs-0.2.0-cp311-abi3-win_amd64.whl | CPython 3.11 | abi3 | Windows x86-64 | Details for bimfabrikhh_core_rs-0.2.0-cp311-abi3-win_amd64.whl |
| bimfabrikhh_core_rs-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl | CPython 3.11 | abi3 | Linux glibc 2.17+ x86-64 | Details for bimfabrikhh_core_rs-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
| bimfabrikhh_core_rs-0.2.0-cp311-abi3-macosx_11_0_arm64.whl | CPython 3.11 | abi3 | macOS 11.0+ ARM64 | Details for bimfabrikhh_core_rs-0.2.0-cp311-abi3-macosx_11_0_arm64.whl |
Total release size: 2.1 MB
Release files / bimfabrikhh_core_rs-0.2.0.tar.gz
| Download URL | bimfabrikhh_core_rs-0.2.0.tar.gz |
|---|---|
| Size | 113.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
9218583e5a1033b50a4b04fe6f738b00f88bf5059177d79d5d46513214a8fec0
|
|
BLAKE2b-256 checksum How to use checksums |
effd3e414324b6d7faf2338a09bd2a3bf03f55df6a97db34c6c597c9443d445a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / bimfabrikhh_core_rs-0.2.0-cp311-abi3-win_amd64.whl
| Download URL | bimfabrikhh_core_rs-0.2.0-cp311-abi3-win_amd64.whl |
|---|---|
| Size | 567.2 kB |
| Tags | CPython 3.11 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
b83e9d605336f3888ed7cdb063505db85313d0796222bd1ebd274e8530d1b4ea
|
|
BLAKE2b-256 checksum How to use checksums |
8022555c66e33de5a81c31fec070a9d20a4022305d834e9e34ef7b07699f1e5d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / bimfabrikhh_core_rs-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
| Download URL | bimfabrikhh_core_rs-0.2.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl |
|---|---|
| Size | 734.1 kB |
| Tags | CPython 3.11 Linux glibc 2.17+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
ec90434e974bd600dd515433c0f11ca514a8aded35ca286360151197a267355f
|
|
BLAKE2b-256 checksum How to use checksums |
41e7004748b6854f7c5aa93096deb4d975ce369890b8a29cdc83086682f7c717
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / bimfabrikhh_core_rs-0.2.0-cp311-abi3-macosx_11_0_arm64.whl
| Download URL | bimfabrikhh_core_rs-0.2.0-cp311-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 676.9 kB |
| Tags | CPython 3.11 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
8ba5fa504d1524d92fa79b2e5ea08b94b6c26e1d738148cfbe130bb51a31a2c4
|
|
BLAKE2b-256 checksum How to use checksums |
419d450022a6ea9890311de9088bcbe3f07211ec3120594e93d473ad67ed1632
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logSecurity for bimfabrikhh-core-rs 0.2.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 4 files in this release have been uploaded with provenance metadata.
Attestation for bimfabrikhh_core_rs-0.2.0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.