Skip to content

Software

The GDE project is supported by a wide range of open-source libraries and tools, all of which can be found at git.gfz.de/globaldynamicexposure. Two core libraries taxonomylib and exposurelib are documented below.

Taxonomy-lib

taxonomylib is a Python library for working with building taxonomy strings, which is a structured text format to describe building characteristics such as occupancy type, structural material, height, construction date, and roof shape. The library provides two main classes: Taxonomy for parsing and manipulating individual taxonomy strings, and TaxonomyMatcher for comparing pairs of taxonomy definitions and finding their overlap.

Parse and inspect taxonomy strings. Load any OXM, GEM or HAZUS taxonomy string into a structured Python object. Once loaded, individual attributes — occupancy type (OCC), structural material (MAT), number of stories (HEI), building height in metres (HIM), construction date (DAT), roof shape (ROF), and dwelling units (OCD) — can be read and modified independently, without manually parsing the string.

Extract numeric building properties. The get_number_of_stories() method converts height information — whether given as an exact storey count, a range, or a total building height in metres — into a consistent set of values: the central estimate, minimum, maximum, and best integer estimate. Equivalent methods exist for construction date (get_date()) and dwelling units (get_dwelling_units()). All tag variants and unit conversions are handled automatically.

Build and validate taxonomy strings programmatically. Taxonomy objects can be constructed from scratch by setting individual attributes, with validation applied at each step. Once built, the object can be serialised back to a standard GEM taxonomy string or to JSON. Helper methods such as format_number_of_stories_tag() and format_year_of_construction_tag() handle the formatting rules for each attribute type, including exact values, bounded ranges, and open-ended ranges.

Match and intersect two taxonomy definitions. The TaxonomyMatcher class compares two Taxonomy objects across a configurable set of attributes and determines whether they are compatible. Occupancy matching respects the GEM hierarchy: RES is treated as a superset of RES2, and NOR matches any non-residential type. Range attributes such as storey count, construction date, and dwelling units are intersected numerically. The matcher returns None for any attribute where no overlap exists, making it straightforward to filter or join exposure records by taxonomy.

Examples

Getting the number of stories from a taxonomy string

from taxonomylib.taxonomylib import Taxonomy

taxonomy = Taxonomy.load_taxonomy_string("RES/CR/HBET:3-7/Y:1985")

num_stories, min_stories, max_stories, best_estimate = taxonomy.get_number_of_stories()
print(num_stories)    # 5.0  (midpoint of 3–7)
print(min_stories)    # 3
print(max_stories)    # 7
print(best_estimate)  # 5

Reading and modifying individual attributes

from taxonomylib.taxonomylib import Taxonomy

taxonomy = Taxonomy.load_taxonomy_string("RES/CR/H:5")

print(taxonomy.get_attribute("OCC"))  # 'RES'
print(taxonomy.get_attribute("MAT"))  # 'CR'

taxonomy.set_year_range(1960, 1980)
print(taxonomy.get_standard_string())  # 'RES/CR/H:5/YBET:1960-1980'

Checking whether two taxonomies match

from taxonomylib.taxonomylib import Taxonomy
from taxonomylib.taxonomymatcher import TaxonomyMatcher

matcher = TaxonomyMatcher(match_attributes=["OCC", "HEI", "DAT"])

residential_concrete = Taxonomy.load_taxonomy_string("RES/CR/HBET:3-7/YBET:1960-1990")
residential_masonry = Taxonomy.load_taxonomy_string("RES1/MUR/HBET:5-10/YBET:1980-2000")

print(matcher.match(residential_concrete, residential_masonry))  # True — occupancy, height, and date ranges all overlap

commercial_concrete = Taxonomy.load_taxonomy_string("COM/CR/H:4/YBET:1960-1990")
print(matcher.match(residential_concrete, commercial_concrete))  # False — RES and COM do not overlap

Using the occupancy hierarchy

from taxonomylib.taxonomylib import Taxonomy
from taxonomylib.taxonomymatcher import TaxonomyMatcher

matcher = TaxonomyMatcher(match_attributes=["OCC"])

# NOR (non-residential) matches COM2 because COM is non-residential
taxonomy_nonresidential = Taxonomy.load_taxonomy_string("NOR")
taxonomy_commercial = Taxonomy.load_taxonomy_string("COM2")
print(matcher.match(taxonomy_nonresidential, taxonomy_commercial))  # True

# NOR does not match RES
taxonomy_residential = Taxonomy.load_taxonomy_string("RES")
print(matcher.match(taxonomy_nonresidential, taxonomy_residential))  # False

Exposure-lib

exposurelib is a Python library for reading, writing, and transforming exposure databases coming from the Global Dynamic Exposure model. It provides a database abstraction layer — backed by either SpatiaLite or PostGIS — that organises building exposure data around three core concepts: entities (individual buildings, building parts, or spatial tiles), assets (building type definitions combining a taxonomy with attributes such as population and replacement cost), and entity-asset pairs that link the two with a building count. The spatial grid is built on Quadtree tiles, which allows exposure data to be queried and aggregated efficiently at any zoom level.

Query exposure by area. Exposure data can be retrieved using any combination of spatial filters: an ISO 3166-1 alpha-3 country code, a bounding box, a WKT geometry, a list of Quadkey tiles, or a set of administrative boundary IDs. The same filter interface is used across all query and copy operations, making it straightforward to extract data for a city, a district, or an arbitrary polygon without writing custom SQL.

Aggregate and copy between databases. The library supports copying exposure data from one database to another, with optional on-the-fly aggregation. Individual building entities can be collapsed into their parent tile entities, and sparse tiles below a minimum building count threshold can be merged upward through zoom levels until a target resolution is reached. Separately, exposure data can be aggregated onto custom administrative boundaries — for instance to produce district-level totals — and the result written into a dedicated district assets table.

Query asset attributes. Each asset carries both a set of attributes (population density, replacement cost, floor area). The library provides methods to retrieve the sum of any attribute across all assets in a country or district. Building-level data within a tile, such as footprint area, net floor area, and taxonomy can also be retrieved directly as Taxonomy objects.

For the full API reference, see docs.dynamicexposure.org/exposurelib.

Examples

Getting population for a building entity

Each entity-asset pair stores per-asset occupant counts for daytime (OPD), nighttime (OPN), and transit time (OPT). get_sum_entity_asset_attributes weights these by building count and relative size to give totals for an entity.

from exposurelib import SpatiaLiteExposure

db = SpatiaLiteExposure('./exposure.db')
db.connect()

entity_id = 42
daytime_occupants, nighttime_occupants, transit_occupants = db.get_sum_entity_asset_attributes(
    entity_id=entity_id,
    attribute_keys=["OPD", "OPN", "OPT"],
)
print(f"Daytime occupants:   {daytime_occupants:.1f}")
print(f"Nighttime occupants: {nighttime_occupants:.1f}")
print(f"Transit occupants:   {transit_occupants:.1f}")

db.close()

Getting the estimated floor area of a building entity

Because a building entity carries a probability distribution over structural types rather than a single asset, floor area is estimated as the probability-weighted sum across all linked assets: average_net_floor_area × number × relative_size.

from exposurelib import SpatiaLiteExposure

db = SpatiaLiteExposure('./exposure.db')
db.connect()

entity_id = 42
entity_assets = db.get_entity_asset_pairs([entity_id])

estimated_net_floor_area = sum(
    db.get_asset(asset_id)[3] * number * relative_size
    for _, asset_id, number, relative_size in entity_assets
    if db.get_asset(asset_id)[3] is not None
)
print(f"Estimated net floor area: {estimated_net_floor_area:.0f} m²")

db.close()

Getting replacement costs within a geometry

from exposurelib import SpatiaLiteExposure

db = SpatiaLiteExposure('./exposure.db')
db.connect()

# Bounding box around the Telegrafenberg, Potsdam
telegrafenberg_polygon = "POLYGON((13.060 52.378, 13.070 52.378, 13.070 52.383, 13.060 52.383, 13.060 52.378))"
tiles = db.get_tiles_from_multiple_spatial_filters(wkt_geometry=telegrafenberg_polygon)
entity_ids = db.get_entity_ids_from_quadkeys(tiles)

total_replacement_cost = 0.0
for entity_id in entity_ids:
    (replacement_cost,) = db.get_sum_entity_asset_attributes(
        entity_id=entity_id,
        attribute_keys=["TRC"],
    )
    if replacement_cost:
        total_replacement_cost += replacement_cost

print(f"Total replacement cost in area: €{total_replacement_cost:,.0f}")

db.close()