Skip to content
View as Markdown llms.txt

Tiling

Tiling infrastructure: grid generation, orchestration, and merging.

TileService

Generate a grid of tiles covering a GeoJSON polygon.

Parameters:

Name Type Description Default
polygon dict

A GeoJSON Polygon object ({"type": "Polygon", "coordinates": ...}). Validated and winding-order-normalized on construction.

required
logger Logger

Python logger instance.

required
analysis_type AnalysesName

Reserved for future per-type tile size differentiation.

None
max_tiles_override int

Override the default maximum number of non-empty tiles (100 by default). None keeps the default.

None

polygon instance-attribute

polygon: dict = validate_polygon(polygon)

Validated GeoJSON Polygon.

points instance-attribute

points: List[Point] = [
    Point(latitude=pos[1], longitude=pos[0]) for pos in ring[:-1]
]

Internal polygon vertices as Point objects (lat/lon named fields).

logger instance-attribute

logger: Logger = logger

Logger passed to the service.

analysis_type instance-attribute

analysis_type: Optional[Union[AnalysesName, str]] = analysis_type

Analysis type the grid is generated for, or None.

tile_size instance-attribute

tile_size: TileSize = TileSize(
    x=self.config.inference_size_m, y=self.config.inference_size_m
)

Tile size in meters, from the tiling config.

max_non_empty instance-attribute

max_non_empty: Optional[int] = max_tiles_override

The caller's override of the maximum non-empty tiles, or None.

config instance-attribute

config: TilingConfig = get_tiling_config(
    str(analysis_type) if analysis_type else None
)

Tiling configuration for the analysis type.

generate_tiles_for_polygon

generate_tiles_for_polygon() -> List[List[Tile]]

Generate a grid of tiles covering the polygon's bounding box.

Returns:

Type Description
list[list[Tile]]

2-D grid where tiles[row][col] is a Tile. Row 0 is the southernmost row; column 0 is the westernmost column. Each tile is marked empty=True if it does not intersect the polygon (neither centroid inside nor bbox overlap).

Raises:

Type Description
PolygonValidationError

If the number of non-empty tiles exceeds the cap in force. The message names the tile count, the cap, the grid family, the max_tiles_override value that would clear it, and what the run would cost at DEFAULT_TOKENS_PER_JOB tokens a tile.

AreaPreview dataclass

Preview and cost estimate for an area analysis before submission.

would_bill_jobs, estimated_time_s and estimated_cost_tokens are priced from the planned number of jobs, never from tile_count alone. Grid analyses submit one job per tile, so would_bill_jobs == tile_count there. A facade (analysis_surfaces) run can split a tile into several billed sub-batches when its estimated sensor count exceeds the server cap. Passing payload= (with buildings= if the geometry is not embedded) runs the same offline batch split run_area submits, so would_bill_jobs reflects the real job count before any HTTP call.

Warnings

Without payload=, would_bill_jobs falls back to tile_count, which is safe when there is no facade geometry to preview yet but an under-estimate for a facade payload.

Attributes:

Name Type Description
tile_count int

Number of tiles covering the polygon.

estimated_time_s float

Estimated run time in seconds.

estimated_cost_tokens int

Estimated cost in tokens.

would_bill_jobs int or None

Number of jobs the run would submit and bill.

sensor_count int or None

Total sensors across the planned batches. None for a grid analysis (no facade batching) or when payload= was omitted.

AreaResult dataclass

Bases: _PhysicalGridMixin

Public result from an area analysis.

Attributes:

Name Type Description
merged_grid Any

The merged grid, a READ-ONLY numpy array in the wire dtype of the run: float16 (solar radiation, sky view factor, thermal comfort statistics, wind), float32 (direct sun hours, daylight availability, and the class codes of a categorical result), int16 (UTCI/TCI: stored value = physical value x value_divisor), or float64 (a run that mixes dtypes). NaN is no value for the float dtypes; valid tells it for int16. Use physical_grid() for physical values: an int16 UTCI grid read without the divisor is 10 times too large.

value_divisor int

int16 grids only: physical value = stored value / value_divisor (10 for UTCI/TCI). 1 for the float dtypes.

valid ndarray or None

int16 grids only: the validity bitmap (uint8, one bit per cell in row-major order, least significant bit first). None for the float dtypes.

polygon dict

The source GeoJSON polygon.

analysis_type str

Which analysis type was run.

grid_shape tuple[int, int]

(num_rows, num_cols) of the merged grid.

bounds tuple[float, float, float, float] | None

Geographic extent of merged_grid as (min_lng, min_lat, max_lng, max_lat). When the input polygon is not an integer multiple of step_m, the actual grid extent is padded north and east to the next step boundary; this field reports that true extent so visualisers can place the bitmap without a SW-anchored squash. None when no grid was produced (e.g. empty schedule).

failed_jobs list[str]

Job IDs where the job status was Failed.

skipped_jobs list[str]

Job IDs where download failed (succeeded job but network error), or jobs that were still pending/running when merge was called.

total_jobs int

Total number of jobs.

succeeded_jobs int

Number of jobs that succeeded.

min_legend, max_legend float or None

The exact value range measured over merged_grid, not a fold of the per-tile backend estimates. None for a categorical result, an empty grid, or when the grid holds no finite value.

legend list[str] or None

The class labels of a categorical result (wind-comfort classes), sorted and indexed by the class codes in merged_grid. None for a numeric result.

failed_tiles list[TileFailure]

Per-tile failure records classified by TileFailurePhase (submit / compute / download / skipped). Same tile_id never appears twice; first-seen phase wins (submit > compute > download > skipped). Empty when every tile produced usable output.

to_dict

to_dict() -> dict

Serialise to a plain dict.

merged_grid holds the PHYSICAL values as a nested list (None for no value), as before 1.0.0: a dict reader needs no divisor. value_dtype and value_divisor record the wire dtype, so from_dict() gives back the same dtype and the same values.

Returns:

Type Description
dict

A JSON-compatible dict that from_dict() turns back into an equivalent result.

from_dict classmethod

from_dict(data: dict) -> AreaResult

Reconstruct from a dict produced by to_dict().

Unknown keys are silently ignored for forward compatibility.

Parameters:

Name Type Description Default
data dict

A dict produced by to_dict().

required

Returns:

Type Description
AreaResult

The reconstructed result. merged_grid has the stored value_dtype (float64 for a dict without one).

Raises:

Type Description
KeyError

If a required key (polygon, analysis_type or grid_shape) is missing.

AreaSchedule dataclass

Schedule of submitted jobs for an area analysis.

Produced by run_area() and consumed by check_area_state(), merge_area_jobs(), and run_area_and_wait(). Use to_dict() and from_dict() to persist a schedule and resume it later.

Attributes:

Name Type Description
jobs dict[str, str]

Mapping of tile_id to job_id.

polygon dict

The source GeoJSON polygon.

analysis_type str

Which analysis type was run.

config_hash str

SHA-256 hash of the payload's config-relevant fields.

tile_positions dict[str, tuple[int, int]]

Mapping of tile_id to (row, col) grid position.

grid_shape tuple[int, int]

(num_rows, num_cols) of the tile grid.

webhook_url str or None

Webhook URL used for this submission.

webhook_events tuple[str, ...] or None

Webhook events subscribed to.

failed_submissions tuple[str, ...]

Tile IDs where job submission failed in a way that is SAFE to retry -- the server either never saw the request or answered with a definitive rejection.

uncertain_submissions tuple[str, ...]

Tile IDs whose submission outcome is UNKNOWN: the request may already have been accepted and queued server-side (a lost/timed-out response, or a 2xx reply whose body didn't parse into a job). Deliberately a SEPARATE bucket from failed_submissions, not a variant of it: run_area(..., retry_from=schedule) builds its resubmission set from failed_submissions alone, so an uncertain tile is never automatically resubmitted -- the SDK cannot tell whether that would create a second billed job, so it does not decide for the caller. Resolving one requires a deliberate step outside this field (check job history for the tile, then start a fresh, non-retry submission if nothing exists). It is carried forward unchanged across a retry_from run and can never appear in a retry's own result.

config TilingConfig or None

The tiling configuration (tile size, step and context margins) the tiles were built with.

submission_abort_status int or None

HTTP status that fail-fasted the submission batch, or None when submission ran to completion. Currently only 402 (insufficient credits): the condition is account-global, so the SDK stops submitting remaining tiles after the first one — those tiles land in failed_submissions without an HTTP call. Consumers should treat 402 as "don't retry, top up credits"; plain failed_submissions entries with status None keep their usual transient-retry semantics.

surface_fields bool

True when the submitted payload carried facade or bring-your-own-sensor fields (analysis_surfaces / sensor_points); the merge uses it to choose the surface merge.

buildings_hash str or None

Fingerprint of the buildings/geometries map that produced this schedule. Geometry drives sub-tile batch boundaries but is not part of config_hash, so the retry guard compares this separately. None on schedules built before the field existed.

terrain_context_margin_m float or None

The terrain reach these tiles were sliced to, in metres — the FLOORED value actually used (max(config.context_margin_m, requested)), not the value passed to run_area. So a default solar run reads back 128.0, not 0.0. Guarded on retry and on merge. None on schedules built before the field existed.

max_sensors_per_job float or None

The facade per-job cap that planned this schedule. Its unit depends on batching_policy_version: on an exact v2 schedule it is the caller's cap in RETAINED sensors, None for the default target; on a legacy v1 schedule it is the resolved estimate-policy cap. A missing saved policy version means legacy v1 for compatibility. Guarded on retry and on merge when present.

batching_policy_version int or None

Facade batching policy that planned the batches: 2 is the exact retained-sensor policy (it requires complete batch_membership and batch_sensor_counts); None or 1 is the legacy estimate policy.

batch_count_contract_version int or None

Version of the sensor-counting rules the batch sensor counts were computed under. A retry of a schedule counted under an older version is refused by name.

batch_membership dict[str, tuple[str, ...]] or None

Building IDs in each facade batch, keyed by batch key ({tile_id}#batch{i}). None on a grid schedule.

batch_sensor_counts dict[str, int] or None

Retained sensor count of each facade batch, keyed by batch key. None on a grid schedule.

transport str or None

Transport the jobs were submitted with ("json" or "binary"). None on schedules built before the field existed.

wire_version int or None

Binary wire version of the submission (1 for the binary transport), None for a JSON submission.

binary_acknowledgements dict[str, dict] or None

The server's acknowledgement of each binary submission, keyed by schedule key (tile ID or batch key). None for a JSON submission.

weather_identity str or None

The weather these tiles submitted — the columns, the payload latitude and longitude and the window — as a digest, or None for an analysis that reads no weather array. config_hash covers none of that, so it alone tells two runs of one window against two climates apart. Guarded on retry and merge; never sent.

schedule_contract_version int or None

Version of this client-side record; 4 is the run identity described under weather_identity. Versions 2 and 3 held an EPW FILE identity and are refused by name on a weather-bearing resume. None predates the field.

site_identity str or None

Digest of the raw site inputs (buildings with their per-building overrides, trees, ground materials, context and terrain geometry). Guarded on retry and merge; never sent. None on schedules built before the field existed.

run_id str or None

Random ID made once per schedule and kept across every retry of it. Client-side only; never sent.

attempts dict[str, int] or None

Attempt number of the current job or submission of each schedule key; a missing key is attempt 1. Client-side only; never sent.

merge

merge(other: AreaSchedule) -> AreaSchedule

Merge another schedule into this one (e.g. after retry).

Validates that both schedules refer to the same polygon (via fingerprint), analysis_type, and config_hash. Overlapping tile_ids in other override this schedule's jobs. Failures that are now covered by other are removed.

Parameters:

Name Type Description Default
other AreaSchedule

The retry schedule to merge in.

required

Returns:

Type Description
AreaSchedule

A new merged schedule.

Raises:

Type Description
ValueError

If polygon, analysis_type, or config_hash do not match.

to_dict

to_dict() -> dict

Serialise to a plain dict for persistence.

Tuple fields are converted to lists for JSON compatibility.

Returns:

Type Description
dict

A JSON-compatible dict that from_dict() turns back into an equal schedule.

from_dict classmethod

from_dict(data: dict) -> AreaSchedule

Reconstruct from a dict produced by to_dict().

Unknown keys are silently ignored for forward compatibility.

Parameters:

Name Type Description Default
data dict

A dict produced by to_dict().

required

Returns:

Type Description
AreaSchedule

The reconstructed schedule.

Raises:

Type Description
KeyError

If a required key (jobs, polygon, analysis_type, config_hash or grid_shape) is missing.

ValueError

If the saved batch records are inconsistent.

AreaState dataclass

Snapshot of area-level job status.

Produced by check_area_state() and consumed by run_area_and_wait() callbacks.

Attributes:

Name Type Description
status str

Derived status: "empty" (no jobs), "pending", "running", "completed", "failed", or "partial".

job_states dict[str, JobStatus]

Mapping of job_id to its current status.

succeeded int

Count of succeeded jobs.

failed int

Count of failed jobs.

running int

Count of running jobs.

pending int

Count of pending jobs.

total int

Total number of jobs.

is_complete bool

True when all jobs are in a terminal state. Also True when the schedule has no job but has uncertain tiles: nothing is in flight and an uncertain tile is never resubmitted, so waiting cannot change the state. The merge then reports each uncertain tile.

uncertain int

Count of tiles whose submission outcome is unknown: the SDK has no job id to poll for them. The merge reports each as a TileFailure; resolve them through job history.

from_job_states classmethod

from_job_states(
    job_states: Dict[str, JobStatus], *, uncertain: int = 0
) -> AreaState

Compute an AreaState from a mapping of job_id to JobStatus.

Status priority: 1. If no jobs -> "empty" (distinct from "completed"; submission may have failed wholesale or been short-circuited) 2. If all jobs pending -> "pending" 3. If any non-terminal job exists (pending or running) -> "running" 4. If all terminal and all succeeded -> "completed", or "partial" when a submission is uncertain 5. If all terminal and all failed -> "failed" 6. If all terminal with mixed results -> "partial"

Parameters:

Name Type Description Default
job_states dict[str, JobStatus]

Mapping of job_id to its current status.

required
uncertain int

Number of tiles whose submission outcome is unknown. Default 0.

0

Returns:

Type Description
AreaState

The derived snapshot.

AreaTimeoutError

Bases: Exception

Raised when run_area_and_wait exceeds its area_timeout.

Attributes:

Name Type Description
area_state AreaState

The state snapshot at the time of timeout.

message str

Human-readable timeout message.

TileGrid dataclass

Immutable wrapper around a 2-D grid of tiles.

Produced by tile generation for a polygon and consumed by downstream composable methods (run_area, get_by_tiles).

Tile IDs are deterministic UUIDs derived from the polygon geometry and grid position — the same polygon always produces the same IDs.

Attributes:

Name Type Description
tiles list[list[Tile]]

2-D grid where tiles[row][col] is a Tile. Row 0 is the southernmost row; column 0 is the westernmost column.

num_rows int

Number of rows in the grid.

num_cols int

Number of columns in the grid.

polygon dict

The source GeoJSON polygon used to generate this grid.

config TilingConfig or None

The tiling configuration (tile size, step and context margins) the grid was generated with.

non_empty_tiles property

non_empty_tiles: List[Tuple[int, int, Tile]]

Return non-empty tiles with their grid positions.

Returns:

Type Description
list[tuple[int, int, Tile]]

Each element is (row, col, tile) for tiles where tile.empty is False.

TiledResult dataclass

Bases: _PhysicalGridMixin

Aggregated result from a tiled analysis run.

Attributes:

Name Type Description
merged_grid Any

Merged, blended, clipped grid: a READ-ONLY numpy array in the wire dtype of the run (float16, float32, int16 or float64; see AreaResult.merged_grid). Use physical_grid() for physical values. Typed as Any to avoid import-time numpy dependency.

value_divisor int

int16 grids only: physical value = stored value / value_divisor. 1 for the float dtypes.

valid ndarray or None

int16 grids only: the validity bitmap (uint8, one bit per cell, least significant bit first). None for the float dtypes, where NaN is no value.

polygon dict

Original input GeoJSON polygon.

tile_count int

Number of tiles that were executed.

grid_shape tuple[int, int]

(rows, cols) of the merged grid.

failed_tiles list[TileFailure]

Tiles that failed after all retries.

skipped_tiles list[str]

Tile IDs that were skipped.

execution_time float

Total wall-clock seconds for the tiled run.

per_tile_results list[TileResult]

Raw per-tile results.

analysis_type str

Which analysis type was run.

min_legend, max_legend float or None

The exact value range measured over merged_grid, not a fold of the per-tile backend estimates. None for a categorical result, an empty grid, or when the grid holds no finite value.

legend list[str] or None

The class labels of a categorical result (wind-comfort classes), sorted and indexed by the class codes in merged_grid. None for a numeric result.

TileResult dataclass

Raw result from a single analysis tile.

Attributes:

Name Type Description
tile_id str

Unique identifier of the tile.

row int

Row index in the tile grid.

col int

Column index in the tile grid.

data dict or None

The raw result payload for the tile, or None when the tile produced no result.