BuildingsAcquisitionError

Bases: Exception

Direct building acquisition failed in a way the caller must see.

BuildingsServiceClient

Bases: ScrubbedSessionState

Client for 3D building meshes, read from the public sources.

api_key, base_url, gateway_base_url and telemetry are accepted for construction compatibility with the other service clients and are unused: this client sends no request of its own. It implements the context-manager protocol.

Parameters:

NameTypeDescriptionDefault
api_keystrAPI key, accepted for compatibility with the other service clients.required
loggerLoggerLogger used for progress and warning messages.required
base_urlstrBase URL, accepted for compatibility with the other service clients.required
gateway_base_urlstrAccepted for compatibility and unused.None
telemetryTelemetryAccepted for compatibility and unused.None
acquisitionoptionalRemoved option. Passing any value raises TypeError.REMOVED

Attributes:

NameTypeDescription
loggerLoggerLogger used for progress and warning messages.
base_urlstrBase URL the client was constructed with.

close

Python
close() -> None

Release the client's resources. Kept for API compatibility.

get_area

Python
get_area(
polygon: dict,
*,
config: Optional[BuildingsConfig] = None,
on_progress: Optional[Callable[[TileProgress], None]] = None,
max_tiles_override: Optional[int] = None,
timeout: int = 60,
total_timeout: int = 600,
max_workers: int = DEFAULT_TILE_ACQUISITION_WORKERS,
overture_release: Optional[str] = None,
analysis_type: Optional[str] = None,
acquisition: Any = REMOVED,
) -> AreaBuildings

Fetch and deduplicate buildings for a polygon area.

The whole site is read at once: a single rectangle up to 4 km2, and a grid of roughly 2 x 2 km read chunks above it, at most two in flight. The meshes come back in one site frame, anchored at the polygon bounding-box south-west corner, and run_area re-anchors them per tile. A polygon straddling a registered city's outline is read tile by tile instead, because a site-level read would give the city's bodies to the tiles outside it.

There is no partial answer on the site path. A chunk that fails raises TiledRunError, whose failed_tiles names the failed chunks and whose __cause__ is the first underlying error.

Parameters:

NameTypeDescriptionDefault
polygondictGeoJSON Polygon.required
configBuildingsConfigAccepted for compatibility and unused: it shaped the removed POST /buildings body.None
on_progresscallableProgress callback (TileProgress) -> None, once per read CHUNK on the site path. tile_id is the chunk id and total_count the chunk count.None
max_tiles_overrideintOverride the maximum number of non-empty tiles allowed.None
timeoutintPer-REQUEST budget in seconds (default 60), trimmed to what is left of total_timeout.60
total_timeoutintWall-clock deadline over the whole site read, in seconds (default 600).600
max_workersintRead chunks in flight on the site path. A CEILING of 2 applies, so this can only ask for FEWER. On the mixed-source per-tile path it is still the thread count.DEFAULT_TILE_ACQUISITION_WORKERS
overture_releasestrPin the Overture footprints to one immutable release. The resolved release is reported on the result.None
analysis_typestrThe analysis this acquisition is for. It decides the tile grid and the READ MARGIN, the half extent of each read rectangle: 256 m for the two wind analyses and 384 m for every other one, which needs the shadow casters further out. Omit it for the widest margin, valid for every analysis.None

Returns:

TypeDescription
AreaBuildingsDeduplicated buildings with coordinates in polygon-bbox-SW meter space, and origin naming that frame — pass the whole object to run_area(buildings=…) and it re-anchors from it, so one acquisition can serve several sub-areas. building_ids is always empty: the read path has no numeric building ids. failed_tiles is empty on the site path: a read that could not cover the polygon raises instead.

Raises:

TypeDescription
PolygonValidationErrorIf polygon is not a valid GeoJSON Polygon, or covers more non-empty tiles than allowed (see max_tiles_override).
TiledRunErrorIf a read chunk fails on the site path.
TypeErrorIf the removed acquisition argument is passed.

get_by_tiles

Python
get_by_tiles(
tiles: TileGrid,
*,
config: Optional[BuildingsConfig] = None,
on_progress: Optional[Any] = None,
timeout: int = 60,
total_timeout: int = 600,
max_workers: int = DEFAULT_TILE_ACQUISITION_WORKERS,
) -> Dict[str, Optional[dict]]

Fetch buildings for each non-empty tile in a pre-generated grid.

Returns a per-tile mapping {tile_id: buildings_dict | None} intended for inspection or manual per-tile dispatch.

All tile IDs from the grid are present as keys in the returned dict. A None value means the fetch failed for that tile (distinguishable from "no buildings found", which would be an empty dict).

To combine get_by_tiles output into a single dict for visualisation (not for run_area) see merge_buildings.

Warnings

This shape is NOT a drop-in for InfraredClient.run_area's buildings= kwarg, which expects a flat {building_key: building_data} mapping (each value a dict with top-level coordinates, mesh_id, indices). Passing this method's per-tile output directly into run_area(buildings=...) will fail every tile with "missing or non-sequence coordinates". For the area pipeline use client.buildings.get_area(polygon).buildings, the canonical flat shape.

Parameters:

NameTypeDescriptionDefault
tilesTileGridTile grid produced by tile generation for a polygon.required
configBuildingsConfigAccepted for compatibility and unused: it shaped the removed POST /buildings body.None
on_progresscallableProgress callback (TileProgress) -> None.None
timeoutintPer-tile timeout in seconds (default 60).60
total_timeoutintTotal wall-clock timeout in seconds (default 600).600
max_workersintMaximum parallel threads for tile execution (default 20).DEFAULT_TILE_ACQUISITION_WORKERS

Returns:

TypeDescription
`dict[str, dict \None]`Maps each non-empty tile's ID to its buildings dict, or None if the fetch failed for that tile.

AreaBuildings

Bases: Payload

Result of get_area().

Frozen Pydantic model — field reassignment is blocked, but mutable containers (buildings dict) can still be mutated in-place (Pydantic v2 behavior), enabling the edit-before-run workflow.

Attributes:

NameTypeDescription
buildingsdict[str, [DotBimMesh](#infrared_sdk.buildings.DotBimMesh)]Building meshes keyed by building identifier.
attributesdict[str, dict]{height, min_height, num_floors, source_id} per building, under the same keys as buildings. Populated on both read paths (the site-level read and the per-tile read a mixed-source polygon falls back to). Empty when the meshes did not come from a direct read, which carries meshes and no attributes. Empty therefore means "not available on this path", never "this building has no height".
building_idslist[int]Numeric building IDs from the API response.
polygondictThe input GeoJSON polygon.
total_buildingsintNumber of buildings in buildings.
execution_timefloatWall-clock seconds for the fetch.
failed_tileslist[dict]Per-tile failure records for tiles that did not produce buildings after all retries (empty when every tile succeeded). Each entry has tile_id, row, col, error. Callers can use len(area.failed_tiles) to detect partial-coverage results.
overture_release(str, optional)Overture Maps release the footprints were read from, when the direct acquisition path produced them (None on the service path, which does not report one). The public index pointer moves daily; pass the value back as overture_release= to pin a later run to the same snapshot.
read_margin_m(float, optional)Half extent, in metres, of the read rectangle every tile was fetched with: 256 m for the wind analyses, 384 m otherwise (see analysis_type). run_area refuses a run whose analysis needs more than this.
analysis_type(str, optional)The analysis type the read margin was taken from. None on an object built by hand, which makes no claim and is never refused.
origin(tuple, optional)(lon, lat) of the site frame the meshes are stored in: the polygon bounding-box south-west corner. run_area(buildings=) reads it and re-anchors from THIS frame, so acquiring once for a large polygon and running several sub-areas places the buildings correctly. A bare {id: mesh} map carries no origin and is read as being in the run polygon's own frame, which is the behaviour that has always applied.

BuildingsConfig

Bases: CamelCasePayload

Configuration for tiled buildings retrieval.

Accepted for compatibility and unused: it shaped the removed buildings request.

Attributes:

NameTypeDescription
output_formatOutputFormatDesired output format (default DotBim).
compressboolWhether to request compressed responses.
optimizations(BuildingOptimizations, optional)Optimizations of the removed per-tile request; no effect.

BuildingsRequest

Bases: CamelCasePayload

Request body for a buildings query.

Kept for compatibility: the SDK no longer sends this request.

Attributes:

NameTypeDescription
coordinatesBuildingCoordinatesCentre of the query.
sizeBuildingSizeExtent of the query.
return_building_idsboolWhether to return numeric building IDs (default False).
output_formatOutputFormatDesired output format (default DotBim).
compressboolWhether to request a compressed response (default False).
optimizations(BuildingOptimizations, optional)Optimisations to apply.

DotBimMesh

Bases: Payload

A single DotBim mesh entry from the buildings API.

Follows the Payload base pattern (frozen, kebab-case aliases).

Attributes:

NameTypeDescription
mesh_idintNumeric identifier of the mesh.
coordinateslist[float]Flat [x, y, z, ...] coordinate array.
indiceslist[int]Triangle index array. Required.

Notes

indices is required. A mesh without it is silently dropped by the simulation service before any computation runs: no error, no warning, and a tile that reads as if the building had never been submitted, yet is billed in full. The SDK therefore refuses to build one.

merge_buildings

Python
merge_buildings(buildings_map: Dict[str, Optional[dict]]) -> dict

Combine per-tile buildings dicts into one for visualization.

Merges all non-None values from buildings_map into a single dict. Each key in the individual buildings dicts whose value is a list is concatenated; other keys are taken from the last dict encountered.

Parameters:

NameTypeDescriptionDefault
buildings_map`dict[str, dict \None]`Maps tile_id -> buildings dict (or None).required

Returns:

TypeDescription
dictCombined buildings dict, or empty dict if all are None.