Configuration¶
One pydantic model, serialised verbatim into every run's manifest alongside the pinned Overture
release, the Google Earth Engine collection identifiers and date ranges, the resolved versions of
momepy, neatnet and geopandas, and a run timestamp.
DATA_DIR is resolved once, here. No other module reads os.environ, builds a path relative
to __file__, or joins its own paths — sources ask for settings.source_dir(name) instead. A
missing or unreachable DATA_DIR fails at config load, which is a much better place to find out
than three stages in.
Almost every field on this page carries its own docstring giving the value's provenance: a published threshold and its paper, or a fixture measurement and what it separates. Where a threshold was swept rather than chosen, the docstring says which sweep and at what operating point.
Two options ship switched off, and why
A threshold in this package is swept against a reference and chosen at an operating point, never picked because it looks reasonable. Anything whose threshold has not been swept ships disabled, and two options are currently in that state:
ucp.measure_on = "enclosures"measures the parameters on street-bounded blocks and transfers them to the units being classified. A street canyon has to be measured against streets, and a grid cell is bounded by none — on one Istanbul extent the aspect ratio is missing from 10.8% of built grid cells against 0.9% of enclosures. Default"units".classification.modal_filterreplaces an isolated unit's label with the one most of its neighbours carry — a minimum mapping unit, standard in this literature. Default off.
Switching either on changes labels, so a run with one enabled is not comparable with a run at the defaults until that threshold has been calibrated.
lczkit.config
¶
Pydantic configuration model for lczkit runs.
DATA_DIR is resolved exactly once, here, via Settings.load(). Every other module reaches data
through settings.input_dir, settings.output_dir, settings.source_dir(name) and
settings.run_dir; nothing else reads os.environ or builds a path from __file__ or the working
directory.
The whole model is serialised verbatim into each run's manifest, so every threshold a run used is
part of its record. Thresholds with no published value (cleaning limits, height confidences)
default to None and raise when used: lczkit.presets supplies the measured values.
HeightProduct
module-attribute
¶
HeightProduct = Annotated[Wsf3dProduct | GhslProduct | OpenBuildings25dProduct, Field(discriminator='kind')]
Where an areal tier's raster comes from. See lczkit.sources.height_products.
NodataPolicy
module-attribute
¶
What a nodata cell means for a land-cover product.
"exclude": no observation, so the cell leaves the denominator. "assign": the product masks this
surface on purpose, so the cell counts towards a named class. ETH canopy height sets built-up,
snow, ice and water to 255; reading those as "exclude" reports central Berlin as ~96% tree cover
instead of ~22%.
UnmappedPolicy
module-attribute
¶
What to do with a raster value no class mapping covers. "raise" (the default) because an
unmapped value means the mapping does not match the product on disk.
GeeAssetType
module-attribute
¶
Whether an Earth Engine asset is a collection to filter and mosaic, or a single image.
LandCoverBackend
module-attribute
¶
Which RasterSource reduces the land cover for a run.
"local" mosaics the ESA WorldCover tiles the extent spans and reduces them with exactextract;
"gee" reduces the configured Earth Engine asset server-side with reduceRegions. The tables are
schema-identical but not bit-identical: exactextract weights partial cells, reduceRegions
counts whole pixels by centre, about a one-percent difference on a 100 m unit.
RasterFormat
module-attribute
¶
Morphometrics raster format.
"gtiff" is a plain GeoTIFF; "cog" the same file tiled with overviews for partial HTTP reads.
"zarr" is written through xarray/rioxarray rather than GDAL's Zarr driver, which stores
the CRS only in a GDAL pam.aux.xml sidecar that a plain zarr/xarray reader cannot see.
OvertureConfig
¶
Bases: BaseModel
Configuration for OvertureSource, the Overture Maps vector source.
release
class-attribute
instance-attribute
¶
Pinned Overture release, e.g. "2026-07-22.0". Never "latest": OvertureSource raises if
this is unset.
source_dir_name
class-attribute
instance-attribute
¶
Subdirectory under input/ that OvertureSource caches into.
duckdb_threads
class-attribute
instance-attribute
¶
Threads each DuckDB connection may use; None is DuckDB's default (every core).
DuckDB ignores OMP_NUM_THREADS, so inside a worker pool this is the only way to keep N
workers from using N x (host cores) threads during ingestion.
CleaningConfig
¶
Bases: BaseModel
Thresholds for the building and street cleaning pipeline.
Majer & Fleischmann (arXiv:2603.00132) Supplementary D describes these operations only
qualitatively, so there are no literature defaults: every field is None and the cleaning
pipeline raises if it is used unset. lczkit.presets holds the fixture-derived values.
building_max_area_m2
class-attribute
instance-attribute
¶
Footprints larger than this are dropped as implausible.
building_min_area_m2
class-attribute
instance-attribute
¶
On buildings_topo, footprints smaller than this are dissolved into a touching neighbour.
Those touching nothing are kept: small is not spurious.
building_road_buffer_m
class-attribute
instance-attribute
¶
Half-width of the road buffer the buildings_topo street rule measures against, in metres.
Fixture-derived: 4.0 m separates perimeter blocks from structures standing in the roadway.
building_road_overlap_limit
class-attribute
instance-attribute
¶
Share of a footprint inside the road buffer above which it is dropped rather than trimmed. Fixture-derived (0.5); re-derive for a city whose road generalisation differs.
building_merge_limit_m2
class-attribute
instance-attribute
¶
geoplanar.merge_overlaps' merge_limit: overlapping polygons smaller than this are merged
into a neighbour regardless of overlap size.
building_overlap_limit
class-attribute
instance-attribute
¶
geoplanar.merge_overlaps' overlap_limit: larger polygons merge only if the shared overlap
exceeds this fraction of their area.
street_tile_size_m
class-attribute
instance-attribute
¶
Tile edge for chunked street simplification, in metres. None runs neatnet over the whole
extent, which stops completing in usable time above roughly 50 km².
street_tile_buffer_m
class-attribute
instance-attribute
¶
Margin simplified around each tile core, in metres. Seam artefacts fall sharply up to ~600 m and not beyond; below ~300 m a dual carriageway no longer fits in the buffer.
street_tile_workers
class-attribute
instance-attribute
¶
Processes to run tiles across. None uses every core the process may use.
street_artifact_threshold
class-attribute
instance-attribute
¶
Face-artifact index separating road artefacts from ordinary fabric. None derives it from
the data (pooled across tiles on the tiled path); setting it pins the value for an A/B.
Wsf3dProduct
¶
Bases: BaseModel
WSF-3D V02 building height (DLR, TanDEM-X), CC-BY-4.0: one global tiled GeoTIFF.
Int16 decimetres, nodata -32767 (README_BuildingHeight.txt), hence the tier's scale=0.1.
Not in the Earth Engine catalogue (DLR/WSF holds only a 10 m settlement mask), so it is a
one-off 2.1 GB HTTP download read by window thereafter.
GhslProduct
¶
Bases: BaseModel
GHS-BUILT-H ANBH R2023A (JRC), free reuse with attribution: 1000 km Mollweide tiles.
ANBH (BUVOL / BUSURF) is the mean height of the built fabric rather than the gross AGBH
averaged over open ground. Float32 metres, nodata 255, 100 m (GHSL Data Package 2023, p. 36).
Also in Earth Engine as JRC/GHSL/P2023A/GHS_BUILT_H/2018, with identical values, so it is
fetched over HTTP and needs no credential.
OpenBuildings25dProduct
¶
Bases: BaseModel
Google Open Buildings 2.5D Temporal v1, CC-BY-4.0, exported per window from Earth Engine.
Metres above terrain at an effective 4 m, annual 2016-2023, over Africa, South and South-East Asia, Latin America and the Caribbean. No public bucket exists. The request caps are Earth Engine's own.
ArealTierConfig
¶
Bases: BaseModel
One areal raster tier of the height cascade: where its raster comes from and how to read it.
Areal products assign a neighbourhood mean to each building, a categorically weaker measurement than a per-building height, and the output tags every building with the tier that resolved it.
name
instance-attribute
¶
The tier's height_source tag, e.g. "ghsl". Unique within a cascade.
source_dir_name
instance-attribute
¶
Subdirectory under input/ holding this product.
enabled
class-attribute
instance-attribute
¶
Whether the tier takes part in the cascade. Distinct from filename is None (product not
available): a tier measured and switched off must stay distinguishable from one never placed.
filename
class-attribute
instance-attribute
¶
Raster filename within input/<source_dir_name>/. Filled per study area by
lczkit.sources.height_products.resolve_areal_tiers when product is set; set it by hand for a
raster you placed yourself. None skips the tier.
product
class-attribute
instance-attribute
¶
product: HeightProduct | None = None
How to fetch the raster. None means it is placed by hand.
scale
class-attribute
instance-attribute
¶
Multiplier from raw raster values to metres (0.1 for a decimetre product).
nodata
class-attribute
instance-attribute
¶
Override the raster's declared nodata. None uses the file's own.
min_height_m
class-attribute
instance-attribute
¶
Values at or below this read as "no building here" and pass to the next tier.
Zero for every shipped tier, Open Buildings 2.5D included. Its damage is within-unit dispersion (CV 0.441 against reality's 0.195), which a floor on one tail does not fix.
confidence
class-attribute
instance-attribute
¶
height_confidence for every building this tier resolves. No default: see HeightConfig.
HeightConfig
¶
Bases: BaseModel
The building-height cascade: Overture attributes first, then areal_tiers in list order.
Adding an areal product is an entry in areal_tiers, not a code change. The confidences have
no default because no published number defines them: height_confidence is an ordinal ranking,
and an invented default would put a quality claim nobody chose into every manifest.
storey_height_m
class-attribute
instance-attribute
¶
Metres per storey for the num_floors fallback. Varies regionally; set it per city where
known.
overture_height_confidence
class-attribute
instance-attribute
¶
height_confidence for Overture's height, unless Overture supplies its own per-building
confidence, which is preferred.
overture_num_floors_confidence
class-attribute
instance-attribute
¶
height_confidence for heights derived as num_floors x storey_height_m.
areal_tiers
class-attribute
instance-attribute
¶
areal_tiers: list[ArealTierConfig] = Field(default_factory=_default_areal_tiers)
Areal tiers in cascade order.
GeeAssetConfig
¶
Bases: BaseModel
Earth Engine coordinates for one land-cover dataset, recorded in the run manifest.
collection_id
class-attribute
instance-attribute
¶
Full asset ID, e.g. "ESA/WorldCover/v200". None: no verified asset, and
EarthEngineSource refuses to guess one.
band
class-attribute
instance-attribute
¶
Band name within the asset, e.g. "Map".
start_date
class-attribute
instance-attribute
¶
Inclusive ISO date for filterDate. Required for a collection; recorded for an image.
end_date
class-attribute
instance-attribute
¶
Exclusive ISO date for filterDate. Same caveat as start_date.
scale_m
class-attribute
instance-attribute
¶
Reduction scale in metres. Match the native resolution: a coarser one resamples silently.
required_fields
¶
Fields EarthEngineSource cannot run without, given this asset's kind.
Source code in src/lczkit/config.py
LandCoverDatasetConfig
¶
Bases: BaseModel
One land-cover product and the mapping from its raw values to fraction classes.
LocalRasterSource and EarthEngineSource read the same instance, which is what makes the two
backends schema-identical. A dataset is either categorical (value_classes) or binned (bins
+ bin_classes), never both; classes are disjoint and sum to 1.0 over the cells that count.
source_dir_name
instance-attribute
¶
Subdirectory under input/ holding this product.
filename
class-attribute
instance-attribute
¶
COG filename within input/<source_dir_name>/; None if not available locally.
column_prefix
class-attribute
instance-attribute
¶
Prefixed to every class name to form output columns, so two datasets that both emit tree
can share a units table.
classes
instance-attribute
¶
The full, ordered output class list. A class with no cells still gets a 0.0 column, so two cities produce the same schema.
value_classes
class-attribute
instance-attribute
¶
Categorical mapping from raw value to class name.
bins
class-attribute
instance-attribute
¶
Ascending breakpoints for a continuous product: v falls in bin i where
bins[i-1] <= v < bins[i].
bin_classes
class-attribute
instance-attribute
¶
Class per bin, lowest first; len(bins) + 1 entries.
nodata
class-attribute
instance-attribute
¶
Override the raster's declared nodata. None uses the file's own.
nodata_class
class-attribute
instance-attribute
¶
Class nodata counts towards; required exactly when nodata_policy is "assign".
unmapped_class
class-attribute
instance-attribute
¶
Class unmapped values count towards; required exactly when unmapped_policy is
"assign".
gee
class-attribute
instance-attribute
¶
gee: GeeAssetConfig = Field(default_factory=GeeAssetConfig)
Earth Engine coordinates for the same product.
LandCoverConfig
¶
Bases: BaseModel
The land-cover fraction sources.
source
class-attribute
instance-attribute
¶
source: LandCoverBackend = 'local'
Which backend supplies the fractions. "local" needs nothing but HTTP and is what CI tests.
Choose "gee" for a dataset with no local product, or a window too large for
max_raster_cells. The two have not been benchmarked against each other for speed.
datasets
class-attribute
instance-attribute
¶
datasets: list[LandCoverDatasetConfig] = Field(default_factory=_default_land_cover_datasets)
The configured products, by name.
gee_project
class-attribute
instance-attribute
¶
Google Cloud project Earth Engine bills against. Read from GEE_PROJECT_NAME.
gee_batch_size
class-attribute
instance-attribute
¶
Units per reduceRegions call, keeping each request under Earth Engine's limits.
gee_max_units
class-attribute
instance-attribute
¶
Refuse an Earth Engine run over more than this many units. None means no ceiling.
max_raster_cells
class-attribute
instance-attribute
¶
Refuse a local read whose window exceeds this many cells (~450 x 450 km at 10 m).
dataset
¶
dataset(name: str) -> LandCoverDatasetConfig
The configured dataset called name, or a KeyError naming what is available.
Source code in src/lczkit/config.py
SemanticGroupConfig
¶
Bases: BaseModel
One functional group of Overture attribute values, and the LCZ class it is evidence for.
Transcribed from docs/references/tables/overture_lcz_semantic_mapping.md, which a test parses
and asserts against. A building matches on subtype or class. Groups are not a partition:
retail is evidence for both large-low-rise form and commercial function.
lcz_hint
class-attribute
instance-attribute
¶
The LCZ class this group is evidence for. Documentation only; nothing keys off it.
UcpConfig
¶
Bases: BaseModel
The urban canopy parameters.
Which land-cover classes and Overture values feed which parameter, plus momepy's street-profile settings restated so they reach the manifest.
street_profile_distance_m
class-attribute
instance-attribute
¶
Spacing of momepy.street_profile()'s perpendicular ticks. momepy's default.
street_profile_tick_length_m
class-attribute
instance-attribute
¶
Tick length; also the width reported for a street no building walls. momepy's default.
min_building_height_m
class-attribute
instance-attribute
¶
Floor applied to heights before taking logs for the geometric mean, so one zero-height row
cannot take a unit's Hr to zero. A numerical guard, below any plausible building.
measure_on
class-attribute
instance-attribute
¶
Units the parameters are measured on, as distinct from classified on.
"enclosures" measures on street-bounded blocks and moves the result to the target units,
area-weighted. It exists for aspect_ratio, which a grid cell no street crosses cannot measure:
null on 10.8% of one Istanbul extent's built grid cells against 0.9% of its enclosures. Not
calibrated against a reference, so a run with it on is not comparable with the defaults.
land_cover_dataset
class-attribute
instance-attribute
¶
Which LandCoverConfig.datasets entry supplies the surface fractions.
pervious_classes
class-attribute
instance-attribute
¶
Pervious before tree and water are folded in; see lczkit.ucp.surface.
impervious_classes
class-attribute
instance-attribute
¶
Impervious, roofs included; the building share is subtracted in lczkit.ucp.surface.
industrial_building_subtypes
class-attribute
instance-attribute
¶
Overture building subtype values counting as industrial.
industrial_building_classes
class-attribute
instance-attribute
¶
Overture building class values counting as industrial. warehouse is excluded on purpose:
it is the LCZ 8 case the LCZ 10 rule must keep apart.
industrial_land_use_subtypes
class-attribute
instance-attribute
¶
Overture land-use subtype values counting as industrial. Empty: industrial parcels sit
under developed, which also covers commercial and retail.
industrial_land_use_classes
class-attribute
instance-attribute
¶
Overture land-use class values counting as industrial. brownfield is excluded: it says
what a parcel was, not what it does.
semantic_groups
class-attribute
instance-attribute
¶
semantic_groups: list[SemanticGroupConfig] = Field(default_factory=_default_semantic_groups)
Functional groups read from Overture's subtype and class. Set [] to skip the layer.
Independent of the industrial_* lists above, which feed the column the LCZ 10 threshold was
calibrated on; heavy_industry is a slightly wider vocabulary reported beside it.
SemanticRuleConfig
¶
Bases: BaseModel
A functional assignment rule: a unit over min_fraction of column takes lcz.
The displaced label is kept as lcz_secondary. A rule ships enabled only once its threshold
has been swept against a reference; reason records the measurement either way.
column
instance-attribute
¶
The parameter column to threshold, e.g. sem_lightweight_buildings_of_building_area.
max_mean_building_area_m2
class-attribute
instance-attribute
¶
Optional gates on mean_building_area_m2, which a rule may read although the metric
does not weight it.
reason
class-attribute
instance-attribute
¶
Why the rule exists and how it was calibrated, for the manifest.
ClassificationConfig
¶
Bases: BaseModel
The prototype-distance classifier. Every threshold it applies lives here.
weight_preset
class-attribute
instance-attribute
¶
Entry in lczkit.classify.weights.PRESETS.
"bernard2024_partial" is Bernard et al. (2024)'s built-type default with the dimensions this
package cannot compute left out: SVF (4) and z0 (0.5) are not computed, so 17 of the published
21.5 weight units apply and building surface fraction carries ~47% of the metric. "equal" is
the uniform comparison.
built_min_building_fraction
class-attribute
instance-attribute
¶
Building surface fraction at or above which a unit is classified as a built type. Stewart & Oke's table draws this line itself: every built class is at least 10%, every natural class at most 10%.
reachable_natural_classes
class-attribute
instance-attribute
¶
Natural classes the classifier may assign, by Stewart & Oke label.
C is excluded because where nothing is built it ties with D on every computed parameter. F is not excluded by this list but by arithmetic: D's prototype box contains F's in every dimension, so adding "F" here cannot make it reachable. Distances to both are still reported.
natural_dominant_fraction
class-attribute
instance-attribute
¶
Tree or water cover at which a unit reads as LCZ A or G. lczkit's own; see
docs/references/tables/lczkit_natural_class_ranges.md.
natural_negligible_fraction
class-attribute
instance-attribute
¶
Tree or water cover a natural class treats as absent (Stewart & Oke's 10% boundary).
semantic_rules
class-attribute
instance-attribute
¶
semantic_rules: list[SemanticRuleConfig] = Field(default_factory=_default_semantic_rules)
Functional rules read off Overture's attributes, applied after the LCZ 10 rule.
lcz10_industrial_column
class-attribute
instance-attribute
¶
The industrial share the LCZ 10 rule reads: Bernard et al. (2024)'s FIND/B, so their
published 0.33 transfers to it. The threshold below was calibrated on this column.
lcz10_min_industrial_fraction
class-attribute
instance-attribute
¶
lcz10_industrial_column above which a unit is LCZ 10.
Swept over nineteen settings against the Rotterdam reference; 0.45 is the precision maximum and Bernard's 0.33 performs comparably. Precision is roughly flat (16.7-23.2%) across the range, so this sets how much of the map carries LCZ 10 more than how often the label is right: Overture cannot tell heavy from light industry.
A threshold only. Bernard et al. (2024) Sect. 2.3 also require the industrial share to exceed the residential and large-low-rise shares; that condition is not applied, and the sweep was run without it. The rule also fires whatever the family gate decided, so a nearly unbuilt cell holding one industrial building reads 1.0 and becomes LCZ 10.
lcz1_min_height_m
class-attribute
instance-attribute
¶
Optional Hr floor below which LCZ 1 is not assignable. Bernard et al. (2024) apply the
equivalent on storey counts; lczkit has no reliable storey count, so this is off.
modal_filter
class-attribute
instance-attribute
¶
Replace an isolated unit's label with its neighbours' modal label.
Off: the minimum-neighbour threshold has not been swept, and every stored figure was measured
without a filter. See lczkit.classify.smoothing.
modal_filter_min_like_neighbours
class-attribute
instance-attribute
¶
A unit with fewer contiguous neighbours sharing its label is isolated. A placeholder for a swept value, set to the weakest setting that does anything.
OutputConfig
¶
Bases: BaseModel
What a run writes into output/lczkit/<run_id>/.
break_count
class-attribute
instance-attribute
¶
Classification breaks precomputed per continuous variable, for the map site.
break_method
class-attribute
instance-attribute
¶
How the breaks are derived. Only quantiles are implemented.
viz_significant_figures
class-attribute
instance-attribute
¶
Significant figures floats are rounded to in units_viz.parquet.
viz_distance_scale
class-attribute
instance-attribute
¶
Multiplier applied to the 17 distances before they are stored as int16.
gis_format
class-attribute
instance-attribute
¶
Also write the unit table as units.gpkg.
units.parquet is valid GeoParquet with its CRS, but GDAL's Parquet driver is optional, so some
QGIS builds open it without a CRS. GeoPackage is in GDAL's core. Costs 1.8 s and 67 MB at
116 491 units.
VizConfig
¶
Bases: BaseModel
The static map site lczkit.viz.build_site writes into a run.
Zoom ranges are recorded because a tileset is only reproducible if the zooms that made it are.
unit_max_zoom
class-attribute
instance-attribute
¶
Unit tileset zooms. A 100 m cell is ~21 px at z14; MapLibre overzooms beyond.
basemap_max_zoom
class-attribute
instance-attribute
¶
One zoom short of the units: the basemap sits under a translucent fill, and dropping z14 halved a measured land-use tileset.
basemap_simplification
class-attribute
instance-attribute
¶
tippecanoe --simplification for the basemap only. Unit and building tilesets are left at
tippecanoe's faithful default.
basemap_layers
class-attribute
instance-attribute
¶
Persisted context layers to draw, in order. "land_use" is available and off: it was 94% of
the basemap's bytes at 9 km².
include_buildings
class-attribute
instance-attribute
¶
Tile the building footprints for extrusion. Off: at metropolitan scale they cost two to three times the rest of the site.
render_columns
class-attribute
instance-attribute
¶
render_columns: list[str] = Field(default_factory=lambda: ['lcz_primary', 'lcz_secondary', 'uniqueness', 'height_completeness', 'building_surface_fraction', 'impervious_surface_fraction', 'pervious_surface_fraction', 'tree_fraction', 'water_fraction', 'height_of_roughness_elements_m', 'aspect_ratio', 'street_openness', 'mean_building_area_m2', 'industrial_fraction_of_building_area'])
Attributes tiled at every zoom, for choropleths. Everything else rides in a detail tileset at maximum zoom only, because MVT repeats a feature's attributes in every tile at every zoom. Names a run did not produce are skipped.
render_column_prefixes
class-attribute
instance-attribute
¶
Column families tiled at every zoom, by prefix: the height tier fractions are named after whichever cascade ran.
detail_max_features
class-attribute
instance-attribute
¶
Above this many units the detail tileset is skipped and the sidebar shows render attributes only.
online_basemaps
class-attribute
instance-attribute
¶
Remote raster grounds offered in the site's picker, by key from
lczkit.viz.basemaps.PROVIDERS.
Empty by default, so a library-built site names no remote host and works offline years from
now. The command line offers the keyless grounds unless told --basemap none.
maptiler_key
class-attribute
instance-attribute
¶
MapTiler API key, from MAPTILER_API_KEY.
Excluded from serialisation so it never reaches the manifest (which the site copies); it
reaches only style.json's tile URLs, where the browser needs it in plain text.
UnitsConfig
¶
Bases: BaseModel
Which spatial units the pipeline classifies.
No automatic choice, by region or otherwise: the trade-off is documented and the strategy that ran is recorded in the manifest.
strategy
class-attribute
instance-attribute
¶
grid (default), enclosure or patch.
grid: 100 m cells, what every LCZ map, validation dataset and WRF workflow uses.enclosure: street-, rail- and water-bounded blocks. Over fifteen cities built-class agreement was +3.8 points and overall −0.2, so not adopted as the default.patch: enclosures merged to LCZ-patch scale; a block (median 0.04 ha) is much smaller than a WUDAPT patch (2.2-52 ha). Seelczkit.units.patches.
cell_size_m
class-attribute
instance-attribute
¶
Grid cell side. Other sizes are comparable with no published figure or reference.
patch_min_area_m2
class-attribute
instance-attribute
¶
patch only. A floor, not a centre: the median lands near twice it.
patch_max_area_m2
class-attribute
instance-attribute
¶
patch only. Oversized seeds are split to this before merging; None removes it.
patch_merge_on_morphology
class-attribute
instance-attribute
¶
patch only: merge towards the neighbour most similar in building surface fraction and
height, rather than on size alone.
drop_pedestrian_barriers
class-attribute
instance-attribute
¶
Leave footway/steps/path/cycleway/bridleway out of the barrier set for enclosure and
patch. They are 50-73% of the mapped network in Berlin, Hong Kong and Milan and 3.5-7.5%
elsewhere, so keeping them measures footpath survey effort rather than the city.
WudaptConfig
¶
Bases: BaseModel
The WUDAPT LCZ training areas: the globally available hand-labelled reference.
Note these polygons are the training data behind the Demuzere global map, so agreement between
the two is not an independent ceiling. See lczkit.validation.wudapt.
source_dir_name
class-attribute
instance-attribute
¶
Subdirectory under input/ holding the WUDAPT export.
filename
class-attribute
instance-attribute
¶
The dated LCZ Generator export, e.g. LCZ-Generator_training_areas_2024-10-01.gpkg. Must be
pinned: contributors keep adding to it, so an unpinned name changes the reference.
layer
class-attribute
instance-attribute
¶
GeoPackage layer; None takes the first, which is right for the published export.
class_column
class-attribute
instance-attribute
¶
WUDAPT's LCZ column, coded 1-17 as in Demuzere.
require_qc
class-attribute
instance-attribute
¶
Keep only polygons passing all three QC flags. Off: it halves the reference and moved agreement with So2Sat by +0.4, +2.9 and −1.9 points on Cairo, Mumbai and Jakarta.
min_oa
class-attribute
instance-attribute
¶
Minimum LCZ Generator accuracy of the polygon's submission. Off: at 0.7 it made agreement
with So2Sat worse on all three test cities, because oa scores a submission against itself.
min_area_m2
class-attribute
instance-attribute
¶
Drop polygons below this area.
max_area_m2
class-attribute
instance-attribute
¶
Drop polygons above this area. The largest is an 18 680 km² sea.
citation
class-attribute
instance-attribute
¶
Bechtel et al. (2015), IJGI 4(1), 199-219: the WUDAPT Level 0 protocol.
ValidationConfig
¶
Bases: BaseModel
Agreement against reference LCZ maps, reported per class and as a confusion matrix.
reference
class-attribute
instance-attribute
¶
reference: LandCoverDatasetConfig = Field(default_factory=_default_reference_dataset)
The Demuzere global map, described as a categorical raster.
reference_citation
class-attribute
instance-attribute
¶
Demuzere et al. (2022). The file in use is lcz_v3.tif, a later version than the paper
describes; the two are recorded separately.
ground_truth_citation
class-attribute
instance-attribute
¶
Zhu et al. (2020), So2Sat LCZ42: hand labels, and the primary reference where they exist.
lcz_v3 is a model with its own error and only a comparator.
wudapt
class-attribute
instance-attribute
¶
wudapt: WudaptConfig = Field(default_factory=WudaptConfig)
The WUDAPT training areas.
min_reference_coverage
class-attribute
instance-attribute
¶
Share of a unit the reference must cover for the unit to be scored.
height_completeness_deciles
class-attribute
instance-attribute
¶
Equal-width strata for the height-completeness breakdown.
MorphometricsConfig
¶
Bases: BaseModel
The 2D morphometrics stage (Majer & Fleischmann 2026), a descriptive output only.
Computed on enclosed tessellation cells and written as morphometrics.parquet (and a raster if
raster_resolution_m is set); never joined to units.parquet or read by the classifier. Off by
default because its cost rises steeply with extent. The neighbourhood scales are the paper's
and live in lczkit.morphometrics.compute, since the registry names every column after them.
tessellation_shrink
class-attribute
instance-attribute
¶
momepy.enclosed_tessellation shrink; momepy's default.
tessellation_segment
class-attribute
instance-attribute
¶
momepy.enclosed_tessellation segment; momepy's default.
tessellation_threshold
class-attribute
instance-attribute
¶
momepy.enclosed_tessellation threshold; momepy's default.
tessellation_n_jobs
class-attribute
instance-attribute
¶
Processes momepy.enclosed_tessellation may spawn; -1 is every core. Set 1 inside your
own process pool: thread pinning does not cap processes, and 8 workers left at -1 measured
2 923 processes on a 256-core node.
street_profile_distance_m
class-attribute
instance-attribute
¶
Tick spacing for the 2D street profile. Separate from UcpConfig's so tuning one does not
retune the other.
contextual
class-attribute
instance-attribute
¶
Add the 25th/50th/75th percentile of each attribute over neighbouring cells. Unlike the paper, the primary attributes are kept alongside.
contextual_steps
class-attribute
instance-attribute
¶
Topological steps the contextual neighbourhood spans.
max_tessellation_cells
class-attribute
instance-attribute
¶
Refuse the stage above this many cells. Measured on Berlin: 51 s at 5 406 cells and 1 029 s at 12 322, so the ceiling is set well below any extrapolation.
max_contextual_cells
class-attribute
instance-attribute
¶
A tighter ceiling for the contextual expansion, whose graph is denser.
raster_resolution_m
class-attribute
instance-attribute
¶
Rasterise the attributes at this resolution; None skips. lczkit morphometrics raster
regenerates it later at another resolution.
max_raster_cells
class-attribute
instance-attribute
¶
Refuse a raster grid larger than this.
raster_tile_deg
class-attribute
instance-attribute
¶
Split the raster into a geographic tile grid of this size, named grid_{lon:.2f}_{lat:.2f}
after each tile's centre as geotessera does. None writes one file. Empty tiles are
dropped.
max_raster_bytes
class-attribute
instance-attribute
¶
Refuse a band stack (bands x rows x cols x 4 bytes) above this. An arithmetic memory
ceiling, not a measured one.
Settings
¶
Bases: BaseModel
Resolved configuration for one run. Construct with Settings.load().
input_dir
property
¶
$DATA_DIR/input/: organised by data origin, shared with other projects.
output_dir
property
¶
$DATA_DIR/output/: organised by the tool that produced the results.
tile_cache_dir
property
¶
$DATA_DIR/output/lczkit/_cache/tiles/: memoised per-tile street simplification.
Under lczkit's own output tree rather than input/, because a simplified tile is derived
by lczkit, not source data, and it outlives the run that computed it.
source_dir
¶
load
classmethod
¶
load(*, run_id: str | None = None, dotenv_path: Path | str | None = None, create_run_dir: bool = True) -> Settings
Load .env, resolve DATA_DIR, and create output/lczkit/<run_id>/ unless told not to.
Also reads the optional GEE_PROJECT_NAME and MAPTILER_API_KEY. An absent variable leaves
any configured value alone rather than overwriting it with None. Never touches input/.
Raises ValueError if DATA_DIR is unset, and a ValidationError if it does not exist.
Source code in src/lczkit/config.py
maptiler_key
¶
MAPTILER_API_KEY from the environment, stripped, or None if unset or blank.
Separate from Settings.load because lczkit site build needs the key without DATA_DIR. The
strip matters: a trailing space in .env would otherwise make every tile request 403.
Source code in src/lczkit/config.py
Presets¶
A Settings produced by Settings.load() is deliberately not runnable — it has no bbox and no
release. Presets close that gap with the exact constants a published figure was produced under.
lczkit.presets
¶
Named, complete run configurations: the measured values that have no safe default.
CleaningConfig's thresholds and the height confidences default to None in lczkit.config and
raise when used, because an invented default would enter every manifest looking like a
measurement. A preset supplies them, so lczkit run and the published sites cannot drift apart.
There is one preset, published, because only one configuration has been measured.
OVERTURE_RELEASE
module-attribute
¶
The release the committed fixtures were built from, so only the extent differs between a run
here and the offline numbers. Never "latest": a floating release is not reproducible.
AREAL_CONFIDENCE
module-attribute
¶
height_confidence per areal tier, descending with coarseness below tier 1's 0.9 / 0.6.
Ordinal, with no published number behind it; recorded in the manifest.
RunPreset
dataclass
¶
RunPreset(name: str, description: str, overture_release: str, cleaning: CleaningConfig = _published_cleaning(), heights: HeightConfig = _published_heights(), land_cover: LandCoverConfig = LandCoverConfig(), ucp: UcpConfig = UcpConfig())
A complete set of the configuration a run cannot default its way into.
apply
¶
Write this preset over settings, in place, and return it.
Sections are copied, not shared, so two runs from one preset cannot mutate each other. An
environment-supplied gee_project survives unless the preset names one itself: replacing
the whole land_cover section used to discard it.
Source code in src/lczkit/presets.py
preset
¶
preset(name: str) -> RunPreset
The preset called name, or a KeyError naming the ones that exist.
Source code in src/lczkit/presets.py
apply_preset
¶
Apply the named preset to settings in place, returning it for chaining.
Places — the general locator¶
Every urban region on earth, by name, from the Global Urban Polygons and Points Dataset (GUPPD) —
a gazetteer from NASA's Socioeconomic Data and Applications Center and the European Commission's
Joint Research Centre: 5 558 regions across 173 countries, in one 564 KB table. This is what
lczkit run --city and lczkit cities resolve against, and it is a locator, not a reference —
nothing here labels or validates anything.
A name is not unique (149 of the 5 558 are shared), so an ambiguous query is refused with the candidates rather than answered with the first match. Getting that wrong would run the wrong continent and record a manifest that looks entirely correct.
lczkit.places
¶
Any city in the world, by name — the general locator a run's extent comes from.
A run needs an extent. --bbox has always been the general answer and needs nothing on disk, and
lczkit.cities is the other end of the range: 28 named cities whose windows are pinned to where
the So2Sat labels are dense, so a run is comparable with a recorded agreement figure. Neither is
what someone wanting a map of their own city reaches for — the first asks them to find four numbers
and the second only knows 28 places, and only if the label archive is on disk.
This module is the middle. NASA/JRC's GUPPD ships one small table of every urban region on earth — 5 558 of them across 173 countries, with a name, an ISO code, a country and a bounding box — and it is 564 KB. Nothing in this package read it until now.
It is a locator, not a reference. Nothing here labels, validates or measures anything; it turns
a name into four numbers and records which row it came from. The hand-labelled LCZ sets stay where
they are, in lczkit.validation, reached by the sweep scripts and not by anything on the path from
a city name to a map.
Sizing, because it decides whether a plain --city run is a sensible default. Measured over
the shipped table: the median urban region is 80 km², the 90th percentile 412 km², and only 239
of 5 558 exceed 900 km² — the extent Berlin's 9.8-minute benchmark was measured over. So the
ordinary case is minutes and the tail is real: Jakarta's region is 17 661 km². shrink is how a
caller trims one, and the command line says the area before it starts.
GUPPD_SOURCE_DIR_NAME
module-attribute
¶
Subdirectory under input/. GUPPD is filed under the agency rather than under its own name.
GUPPD_BOUNDS
module-attribute
¶
The post-processed bounds table within input/NASA/, keyed on SMOD_ID.
The full GUPPD release beside it is a 117 MB GeoPackage of urban-region polygons. Reading the CSV rather than the polygons is deliberate: a locator needs a rectangle, and the rectangle is what the CSV holds, so resolving a name costs a 564 KB read rather than opening a spatial file.
Place
dataclass
¶
Place(smod_id: str, name: str, iso: str, country: str, bbox: BBox)
One GUPPD urban region: what it is called, where it is, and which row said so.
smod_id
instance-attribute
¶
GUPPD's own identifier, e.g. "30_3528". Recorded in the run manifest, because a name is
ambiguous — 149 of the 5 558 names are shared by more than one region — and this is not.
iso
instance-attribute
¶
ISO 3166-1 alpha-3, e.g. "DEU". What --country matches against, alongside the name.
bbox
instance-attribute
¶
bbox: BBox
The region's bounding box in lon/lat degrees, as (west, south, east, north).
area_km2
property
¶
Roughly how much ground the bbox covers, for deciding whether to shrink it.
A cosine-corrected rectangle rather than a projected area: the answer is used to print an
order of magnitude and to decide whether to mention --extent-km, and reprojecting 5 558
rectangles to answer that would be work spent on a digit nobody reads.
label
property
¶
"Berlin, Germany (DEU)" — how a place is named in output and error messages.
normalise
¶
A name reduced to what two spellings of it have in common.
Accents are stripped, case is folded and everything that is not alphanumeric collapses away, so
bogota finds Bogota, sao paulo finds São Paulo and washington d.c. finds
Washington D.C.. Applied to both sides, never to stored data — the table keeps its own
spelling, which is what gets printed back.
Source code in src/lczkit/places.py
load_places
¶
Every GUPPD urban region, in file order.
Raises FileNotFoundError naming the path when the table is not on disk, and saying that
--bbox needs nothing — the alternative is a bare csv error several frames down that names
neither the dataset nor the way round it.
Source code in src/lczkit/places.py
in_country
¶
places narrowed to one country, matched on the ISO code or the country name.
Either in full or as a prefix, under normalise — so GBR, gb and "united kingdom" all
reach the United Kingdom. None returns everything, so a caller can pass an optional flag
through without branching.
Source code in src/lczkit/places.py
find
¶
Regions whose name matches query, exact matches first.
Two tiers rather than a score: a name that is the query outranks every name that merely
contains it, so london returns London GBR and London CAN ahead of East London ZAF, and
cambridge returns both Cambridges and nothing else. Within a tier the file's order is kept,
so the answer does not depend on a sort that was never specified.
An empty query returns every region in country, so lczkit cities --country KEN is a
listing rather than a no-op. in_country is what narrows it.
Source code in src/lczkit/places.py
place
¶
The single region query names, or a LookupError saying why there is not one.
An ambiguous query is an error, and the message lists the candidates with their countries. Silently taking the first would put a run over the wrong continent and record a manifest that looks entirely correct — there are two Cambridges and three Londons in this table, and nothing about a bbox afterwards would say which one was meant.
Source code in src/lczkit/places.py
City registry — the comparable-extent locator¶
The 28 study cities the validation sweeps run over — those with enough hand-drawn So2Sat LCZ42
labels to validate against — with the window resolution used to pick a comparable extent in each.
Reached by lczkit run --city ... --so2sat-window, which is a flag rather than a fallback: this
window and the GUPPD region of the same city are different ground, and only this one makes a run
comparable with a published agreement figure.
lczkit.cities
¶
The So2Sat study cities, and how each one's window is found.
A run needs an extent. --bbox is the general answer and needs nothing on disk; this module is the
convenience that lets a caller say --city berlin and get the same 30 km window the published
agreement figures were measured over, so a run stays comparable with them.
It reads input/So2Sat-LCZ42/, so it is the one locator that needs DATA_DIR populated. --bbox
does not, and lczkit.places covers every other city.
WINDOW_KM
module-attribute
¶
Side of the square window, in kilometres. ~900 km2, matching the measured Berlin extent.
City
dataclass
¶
One So2Sat city: its labels, its country, and the region it speaks for.
iso
instance-attribute
¶
ISO 3166-1 alpha-3, so this registry can be matched against the GUPPD gazetteer.
Added because the two locators otherwise collided on a name. Three of these keys name a city
that exists in more than one country — London (GBR and CAN), Santiago (CHL and PHL) and
Los Angeles (USA and Chile's Los Ángeles) — so lczkit cities marked rows that carry no
So2Sat window at all, and --city london --country CAN --so2sat-window would have run
London, UK's window while the caller asked for Canada. Silent wrong ground, which is the one
failure the two-locator design exists to prevent.
densest_window
¶
The side_km square holding the most labelled patch centres, as a lon/lat bbox.
Searched over a quantile grid of candidate centres rather than optimised: the objective is piecewise constant and this is deterministic, which matters more here than optimality — a window that moved between runs would make two runs of the same city incomparable.
Centres, not areas. So2Sat patches are 320 m squares on a 100 m stride and overlap about
sevenfold, so counting area would measure the sampling density rather than the city; the same
reason lczkit.validation.labelled anchors each label on its patch centre.
Source code in src/lczkit/cities.py
patches_path
¶
Where target's labelled patches live under input/.
Source code in src/lczkit/cities.py
so2sat_window
¶
target's densest labelled window — the extent the published figures were measured over.
Raises FileNotFoundError naming the path when So2Sat is not on disk, because the alternative
is a pyogrio error several frames down that does not say which city or which directory.
Source code in src/lczkit/cities.py
shrink
¶
A concentric window of roughly extent_km on a side.
Kept because a full 30 km window is a multi-hour run, and the first thing anyone does with a new command line is try it on something small.