Conversation
Residential buildings were selected by OSM tags alone, which overestimates the stock by about a third against Zensus 2022. ETHOS.BUILDA supplies one point per residential building, so a building is residential if such a point falls on it. Add dataset EthosBuilda, which downloads one CSV per NUTS-1 region from Zenodo (MD5 verified, only Schleswig-Holstein in test mode) and imports it through a staging table, unpacking the JSON encoded attributes. Rewrite osm_buildings_filter_residential.sql as the three step cascade of Till Krebber's method: point inside the polygon, nearest neighbour within 10 m, OSM tags for the remainder. Every building carries the provenance of its match in source, the matched ethos_id, the match_distance and the ETHOS attributes. Because residential buildings are no longer a subset of the filtered ones, read both wherever the filtered table stood for "all buildings with a demand": the household demand of load areas, the building mapping and the PV rooftop potentials. The latter concatenates in pandas rather than unioning in SQL, so its index is deduplicated to keep the shared buildings from counting twice. Measured on Schleswig-Holstein: 618,208 residential buildings = 0.713 x Zensus 2022, against 607,490 = 0.700 for the reference implementation. Refs #1310
The four ETHOS attributes are modelled, not observed, and refurbishment_state is a random draw from federal-state statistics — it reproduces a state's distribution but says nothing about an individual building. That column is passed through into openstreetmap.osm_buildings_residential, so the warning belongs where a reader finds it. Also name the four values of the new source column, and record two known limitations of the classification: care homes tagged only via amenity are no longer residential (they stay in osm_buildings_filtered and count as CTS), while ETHOS points landing on ancillary buildings are kept on purpose. Refs #1310
Buildings whose residential use is expressed only through amenity — care homes tagged amenity=nursing_home or amenity=social_facility on an uninformative building=yes polygon — fell through both rules of stage 3: it drops building='yes' on purpose, and the whitelist is checked against the building column, where OSM practically never puts these values (8 buildings in Schleswig-Holstein). The previous tag-based filter did match them via amenity, so dropping them would have been a silent regression. Stage 3b restores exactly those clauses, gated by amenity so the building='yes' overestimation does not return: measured on SH it recovers 80 buildings (0.013 %), where allowing building='yes' generally would add 531,674 and push the result to 1.33 x Zensus 2022. They carry their own source value 'osm_amenity' so their share stays auditable. This is a deliberate deviation from the reference implementation, whose whitelist names these values but never matches them. SH after the change: 618,288 from the cascade plus 866 census gap fills = 619,154 rows, all ids distinct. Refs #1310
jh-RLI
marked this pull request as ready for review
September 7, 2026 07:43
Four things the docstring got wrong, all found while reviewing the full Germany run of 2026-09-10: * The cascade has four stages, not three. Stage 3b -- care homes whose residential use is expressed only through `amenity` on an uninformative `building=yes` polygon -- was missing entirely. * `osm_amenity` was absent from the list of `source` values, so only four of the five values a reader can meet in the column were documented. * The tag filter overestimated the stock by 65 % against Zensus 2022 (33.0 M against 20.0 M), not "about a third" -- that figure came from the Schleswig-Holstein test area. * Resulting Tables listed `openstreetmap.osm_buildings` twice and omitted `boundaries.egon_map_zensus_buildings_filtered_all`, which the dataset declares as a target and fills in a task. Documentation only, so the dataset version stays at 0.0.11. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…hub.com/openego/eGon-data into features/#1310-ethos-builda-integration
nesnoj
self-requested a review
September 15, 2026 12:33
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1310. Addresses the residential half of #1311.
Residential buildings were selected by their OSM tags alone, which overestimates
the stock by about a third against Zensus 2022. ETHOS.BUILDA supplies one point
per residential building, so this PR decides "is this building residential?" by
intersecting OSM building polygons with those points instead.
New dataset
EthosBuilda(society.egon_ethos_builda_buildings) downloadsone CSV per NUTS-1 region from Zenodo (record
13771740, ETHOS.BUILDA v2.0.0,ODbL-1.0), verifies the published MD5, and imports it through an unlogged
staging table — the four building attributes are JSON objects
{"value","source","lineage"}, not scalars, and are unpacked there. Regions areloaded one at a time with the staging table truncated in between, which bounds
it to the largest single CSV (1.6 GB) instead of the full 7.5 GB. In test mode
only Schleswig-Holstein (
DEF) is fetched and imported.osm_buildings_filter_residential.sqlis rewritten as the three-stepcascade of Till Krebber's method (RLI):
source = 'ethos_intersect'ST_PointOnSurface→source = 'ethos_nearest'buildingtag on the residential whitelist →source = 'osm_tagging'osm_buildings_residentialgains 7 columns:source,ethos_id,match_distanceand the four ETHOS attributes (construction_year,size_class,refurbishment_state,tabula_type). The census gap fillerosm_buildings_extend_residential.sqlstays as a safety net and marks its rowssource = 'census_gap_fill'; itsINSERT ... SELECT *had to become an explicitcolumn list because the target table is now wider than
osm_buildings_filtered.Downstream consequence, and the reason this PR is not confined to the building
dataset: residential buildings are no longer a subset of the filtered ones.
Measured on SH, 2,165 residential buildings (0.35 %) sit outside
osm_buildings_filtered— 1,621 plausible dwellings whose OSM tag the filterlist does not carry, plus 544 ancillary buildings that ETHOS hits and that are
kept deliberately. So everywhere the filtered table stood for "all buildings
that can carry a demand", the residential table is now read alongside it:
loadareas_add_demand_hh.sql(both UNION blocks),mapping.py, andpv_rooftop_buildings.py. The first two useUNION(notUNION ALL), verifiedfree of double counting: the 616,043 overlapping rows have 0 deviations in
geom_point. The third merges withpd.concatin pandas rather than in SQL, soits index is deduplicated explicitly.
Also adds the missing
PRIMARY KEY (id)onboundaries.egon_map_zensus_buildings_residential— the only one of the threecensus mapping tables without one.
Measured on Schleswig-Holstein
EthosBuilda.to_postgresfilter_buildings_residential618,208 = 0.713 × Zensus 2022, against 607,490 = 0.700 for the reference
implementation (+1.8 %). Sources:
ethos_intersect579,076 (93.67 %),osm_tagging38,084 (6.16 %),ethos_nearest1,048 (0.17 %).📋 Pull Request Guidelines
🧑💻 Contributor Checklist
Before requesting a review, make sure you've completed all of the following:
(for more information on local test, check
toxin the Contributing section)(CI tests are automatically executed when creating a PR, you can see the results of the checks below)
(optional if no dataset changes are involved)
CHANGELOG.rstabout the changesAUTHORS.rstOptional:
🔍 Reviewer Checklist
During your review, please check the following:
CHANGELOG.rstupdated accordingly?📝 Additional Notes (optional)
Two checkboxes are deliberately left unchecked. Here is exactly what was and
was not run.
What ran: the changed tasks individually against a Schleswig-Holstein
database —
EthosBuilda.download,EthosBuilda.to_postgres,filter_buildings_residential,extend_buildings_residentialandcreate_buildings_residential_zensus_mapping, with the numbers above.What did not run: a full DAG run in test mode, and therefore none of the three
touched downstream datasets (
LoadArea,CtsDemandBuildings,PowerPlants).The local environment has a virtualenv built for a different branch —
omiwithout
omi.dialects, which breaksimport egon.data.metadataand with itpipeline.py— sotoxand an Airflow run were not possible.pipeline.pycompiles and
EthosBuildainstantiates correctly inside a DAG context(
ethos_builda.download,ethos_builda.to-postgres). Reviewers should treatthe downstream changes as unexecuted, in particular point 1 of the focus list.
extend_buildings_residentialand the census mapping were validated against astand-in table carrying the real schema and three real test cells (100 m
envelopes around commercial buildings with no residential building inside):
3 rows inserted,
source = 'census_gap_fill', all six ETHOS columnsNULL.That is a structural validation of the column list, not a quantity validation.
OSM snapshot caveat. eGon-data pins
germany-250101.osm.pbf, which is thesnapshot the reference implementation used — that is what makes an ID-level
comparison meaningful. The local workdir, however, held
schleswig-holstein-240101.osm.pbf, so the 618,208 above may rest on the 2024extract rather than the 2025 one.
download()fetches the pinned file (thetarget filename differs), so a fresh test-mode run settles it. Until then, treat
the comparison against 607,490 as indicative rather than snapshot-matched.
Deliberate deviations from the reference implementation, all measured on SH,
so a reviewer can attribute differences rather than guess:
once and an ETHOS point claims at most one building, resolved geometrically
and deterministically. Without it the primary key breaks. Affects 180
buildings (0.031 %) and 9 points (0.002 %).
ST_PointOnSurface, not the centroid, to stay faithfulto the reference code — the centroid would match 342 points more.
point belongs to a real dwelling and merely sits on the wrong polygon, so
dropping it would lose the building entirely.
amenityare lost. The previous residential filteralso matched
amenity IN ('retirement_home','nursing_home','assisted_living', 'group_home')andamenity='social_facility'with the correspondingsocial_facilitytag. The reference whitelist carries those values too, butchecks them against the
buildingcolumn, where OSM almost never puts them(8 buildings in SH). Measured on SH: the old clause matched 389 buildings,
of which 80 are no longer residential — all
building='yes', 50social_facilityand 30nursing_home; the other 309 are picked up by theintersection anyway. The loss arises only from the combination of dropping
building='yes'in stage 3 and checking the whitelist againstbuildingalone. All 80 remain in
osm_buildings_filtered, so they stay in thedownstream union and merely count as CTS rather than residential. A targeted
amenitybranch in stage 3 would recover exactly those 80 withoutreintroducing
building='yes'— say so in review if you want it.reference implementation's per-state decomposition the knee stays at 9 m, and
up to 10 m the reference matches 1,010 points against 1,014 here.
Metadata. oemetadata resources for both tables
(
society_egon_ethos_builda_buildings,openstreetmap_osm_buildings_residential) are written and validated ascompleteagainst the assembler, but they are not in this PR: the resourcestore exists only on
features/#1177-update-to-oemetadata-v2(20 resource YAMLsthere, none on
dev), so they will be added on that branch. Worth noting forthe metadata checkbox above: the store's dataset template declares
dl-by-de/2.0, which a resource without its own block inherits silently — theresult here is a Derivative Database of two ODbL-1.0 sources (OSM and ETHOS) and
must declare ODbL-1.0 explicitly. TABULA attribution is mandatory, not
courtesy.
Full-Germany run and validation against the reference results per federal
state and Zensus 2022 are not part of this PR; they run on a workstation
afterwards. The intersection stages are cheap on SH, but stage 2 is a nearest
neighbour over
ST_DWithinand the building count rises by roughly a factor of28 nationwide, so runtime there is estimated, not measured.
Out of scope, deliberately: the missing residential tags in
osm_buildings_filter.sql.bungalowalone is 60 % of the gap between the twotables and stands in the reference whitelist but not in eGon's filter list. That
is a real bug, but it predates ETHOS, has nationwide effects on CTS
distribution and PV potentials, and needs its own validation — it will be filed
separately.