Skip to content
Snippets Groups Projects

Resolve "Change the processing of the `stories` rule to a `height` GEM Taxonomy tag"

Compare and Show latest version
6 files
+ 128
33
Compare changes
  • Side-by-side
  • Inline
Files
6
@@ -39,10 +39,10 @@ class HeightAndFloorspaceRule:
gem_taxonomy_height = []
# The number of stories ideally comes from the tags from OpenStreetMap. If we are able
# to use that, we can also calculate the floorspace. The height tags in OSM are also
# parsed, but no floorspace is estimated.
# to use that, we can also calculate the floorspace. The height tags in
# OSM are also parsed, but no floorspace is estimated.
try:
stories, floorspace = HeightAndFloorspaceRule.get_stories_and_floorspace_from_osm(tags, area)
stories, floorspace = self.get_stories_and_floorspace_from_osm(tags, area)
# OSM can have unexpected data types in their tagging scheme, therefore we do not raise
# an exception in case of a ValueError, but instead ignore the values.
except ValueError:
@@ -51,7 +51,7 @@ class HeightAndFloorspaceRule:
gem_taxonomy_height.append(stories)
try:
height = HeightAndFloorspaceRule.get_height_from_osm(tags)
height = self.get_height_from_osm(tags)
# OSM can have unexpected data types in their tagging scheme, therefore we do not raise
# an exception in case of a ValueError, but instead ignore the values.
except ValueError:
@@ -59,21 +59,22 @@ class HeightAndFloorspaceRule:
if height:
gem_taxonomy_height.append(height)
# If any of the number of stories and height are found in OSM, these are
# returned.
# If any of the number of stories and height are found in OSM, these are returned.
if len(gem_taxonomy_height) > 0:
return {"height": "+".join(gem_taxonomy_height), "floorspace": floorspace}
return {
"height": "+".join(gem_taxonomy_height),
"floorspace": floorspace,
}
# If no information can be retrieved from OSM, we use the height estimation from GHSL
# and do not estimate any floorspace.
ghsl_height = HeightAndFloorspaceRule.get_height_from_ghsl(ghsl_characteristics_type)
ghsl_height = self.get_height_from_ghsl(ghsl_characteristics_type)
if ghsl_height:
return {"height": ghsl_height, "floorspace": None}
return {"height": None, "floorspace": None}
@staticmethod
def get_stories_and_floorspace_from_osm(tags: dict, area: float):
def get_stories_and_floorspace_from_osm(self, tags: dict, area: float):
"""
Get the number of stories and the floorspace, based on the tags `building:levels`,
`roof:levels`, `building:levels:underground` and `building:min_level` and the footprint
@@ -92,10 +93,10 @@ class HeightAndFloorspaceRule:
from math import ceil
# Parse the story tags from OpenStreetMap.
main_stories = HeightAndFloorspaceRule.tag_to_float(tags, "building:levels")
roof_stories = HeightAndFloorspaceRule.tag_to_float(tags, "roof:levels")
underground_stories = HeightAndFloorspaceRule.tag_to_float(tags, "building:levels:underground")
min_stories = HeightAndFloorspaceRule.tag_to_float(tags, "building:min_level")
main_stories = self.tag_to_float(tags, "building:levels")
roof_stories = self.tag_to_float(tags, "roof:levels")
underground_stories = self.tag_to_float(tags, "building:levels:underground")
min_stories = self.tag_to_float(tags, "building:min_level")
# Parse the main stories and roof stories.
stories = None
@@ -129,9 +130,13 @@ class HeightAndFloorspaceRule:
if stories < 0 or underground_stories < 0:
raise ValueError("Number of stories cannot be below 0.")
elif stories == 0 and not (underground_stories > 0):
raise ValueError("Number of stories cannot be 0, if there are no underground stories.")
raise ValueError(
"Number of stories cannot be 0, if there are no underground stories."
)
elif underground_stories == 0 and not (stories > 0):
raise ValueError("Number of underground stories cannot be 0, if there are no stories.")
raise ValueError(
"Number of underground stories cannot be 0, if there are no stories."
)
elif stories > 175:
raise ValueError("Number of stories cannot be above 175.")
@@ -144,10 +149,9 @@ class HeightAndFloorspaceRule:
return "+".join(story_tags), floorspace
@staticmethod
def get_height_from_osm(tags: dict):
def get_height_from_osm(self, tags: dict):
"""
Get the height of a building, based on the `height` and `min_height` tags.
Get the heightb of a building, based on the `height` and `min_height` tags.
Args:
tags (dict):
@@ -158,14 +162,14 @@ class HeightAndFloorspaceRule:
"""
# Parse the height tag from OpenStreetMap.
height = HeightAndFloorspaceRule.tag_to_float(tags, "height")
height = self.tag_to_float(tags, "height")
# Check if height exists
if not height:
return None
# Check if there is a `min_height` tag
min_height = HeightAndFloorspaceRule.tag_to_float(tags, "min_height")
min_height = self.tag_to_float(tags, "min_height")
if min_height:
height -= min_height
@@ -174,11 +178,10 @@ class HeightAndFloorspaceRule:
return f"HHT:{height:.2f}"
@staticmethod
def get_height_from_ghsl(ghsl_characteristics_type: int):
def get_height_from_ghsl(self, ghsl_characteristics_type: int):
"""
Get the GEM Taxonomy height tag, based on the type in the GHSL characteristics layer
that has the most overlap with the building.
Get the GEM Taxonomy height tag, based on the type in the GHSL
characteristics layer that has the most overlap with the building.
Args:
ghsl_characteristics_type (int):
@@ -192,18 +195,17 @@ class HeightAndFloorspaceRule:
11: "HBET:1-2", # res_3
12: "HBET:1-2", # res_3_6
13: "HBET:1-5", # res_6_15
14: None, # res_15_30
15: None, # res_30
14: None, # res_15_30
15: None, # res_30
21: "HBET:1-2", # nonres_3
22: "HBET:1-2", # nonres_3_6
23: "HBET:1-5", # nonres_6_15
24: None, # nonres_15_30
25: None, # nonres_30
24: None, # nonres_15_30
25: None, # nonres_30
}
return ghsl_type_map.get(ghsl_characteristics_type, None)
@staticmethod
def tag_to_float(tags: dict, tag_key: str):
def tag_to_float(self, tags: dict, tag_key: str):
"""
Try to parse a tag as a floating point value. If the value cannot be parsed, return
None.
Loading