Commit 648df6fa authored by Rick Gruber-Riemer's avatar Rick Gruber-Riemer

Sanity check for settlement type in zones based on density

parent e2c66dbd
......@@ -74,6 +74,7 @@ class SettlementType(IntEnum):
periphery = 4 # default within lit area
rural = 5 # only implicitly used for building zones without city blocks.
def calc_levels_for_settlement_type(settlement_type: SettlementType) -> int:
if settlement_type is SettlementType.centre:
ratio_parameter = parameters.BUILDING_NUMBER_LEVELS_CENTRE
......@@ -501,7 +502,6 @@ class Building(object):
proxy_total_height = 0. # something that mimics the OSM 'height'
proxy_body_height = 0.
proxy_roof_height = 0.
proxy_levels = 0.
if 'height' in self.tags:
proxy_total_height = utils.osmparser.parse_length(self.tags['height'])
......@@ -522,13 +522,13 @@ class Building(object):
if proxy_roof_height == 0. and self.roof_complex:
proxy_roof_height = parameters.BUILDING_SKEL_ROOF_DEFAULT_HEIGHT
if proxy_body_height > 0. and proxy_total_height == 0.:
proxy_total_height = proxy_roof_height + proxy_body_height + self.min_height
pass # proxy_total_height = proxy_roof_height + proxy_body_height + self.min_height
elif proxy_body_height == 0. and proxy_total_height > 0.:
proxy_body_height = proxy_total_height - proxy_roof_height - self.min_height
proxy_levels = _parse_building_levels(self.tags)
# Now that we have all what OSM provides, use some heuristics, if we are missing height/levels
# Now that we have all what OSM provides, use some heuristics, if we are missing height/levels.
# The most important distinction is whether the building is in a relationship, because if yes then the
# height given needs to be respected to make sure that e.g. a building:part dome actually sits a the right
# position on the top
......@@ -551,11 +551,22 @@ class Building(object):
self.body_height = proxy_body_height
self.levels = proxy_levels
self._sanity_check_height_and_levels()
# now respect building min height parameter
proxy_total_height = self.body_height + self.min_height + proxy_roof_height
if parameters.BUILDING_MIN_HEIGHT > 0.0 and proxy_total_height < parameters.BUILDING_MIN_HEIGHT:
raise ValueError('The height given or calculated is less then the BUILDING_MIN_HEIGHT parameter.')
def _sanity_check_height_and_levels(self):
"""Apply some sanity checks to make sure that analysed level_heights and levels are sensitive."""
if 'height' in self.tags or 'building:height' in self.tags or 'levels' in self.tags or 'building:levels' \
in self.tags:
return # nothing to do, go with interpreted OSM values
else:
pass # FIXME
def _calculate_levels(self) -> int:
import owbb.models
if isinstance(self.zone, owbb.models.CityBlock):
......
......@@ -535,6 +535,13 @@ OWBB_PLACE_RADIUS_FACTOR_CITY Number 1. Linear corr
OWBB_PLACE_RADIUS_FACTOR_TOWN Number 1. Ditto for ``place=town``.
OWBB_PLACE_TILE_BORDER_EXTENSION Integer 10000 Extension of the perimeter (tile borders) to read place information from, as
e.g. a city might extend across til border areas.
OWBB_PLACE_SANITY_DENSITY Number 0.2 Make sure that settlement type dense is assigned, if the density of a building
zone is larger than a given ratio and the settlement type is rural or
periphery. The density is calculated as the total of
all buildings' floor area (inner rings' areas do also count) divided by the
area of the building zone.
Likewise if the density is lower than the ratio, but the settlement type is
dense or block (not centre), then the type is changed to periphery.
============================================= ======== ======= ==============================================================================
......
......@@ -412,6 +412,31 @@ def _link_building_zones_with_settlements(settlement_clusters: List[m.Settlement
break
def _sanity_check_settlement_types(building_zones: List[m.BuildingZone], highways_dict: Dict[int, m.Highway]) -> None:
upgraded = 0
downgraded = 0
for zone in building_zones:
my_density = zone.density
if my_density < parameters.OWBB_PLACE_SANITY_DENSITY:
if zone.settlement_type in [bl.SettlementType.dense, bl.SettlementType.block]:
zone.settlement_type = bl.SettlementType.periphery
downgraded += 1
for city_block in zone.linked_city_blocks:
city_block.settlement_type = bl.SettlementType.periphery
city_block.settlement_type_changed = True
else:
if zone.settlement_type in [bl.SettlementType.rural, bl.SettlementType.periphery]:
zone.settlement_type = bl.SettlementType.dense
upgraded += 1
# now also make sure we actually have city blocks
_assign_city_blocks(zone, highways_dict)
for city_block in zone.linked_city_blocks:
city_block.settlement_type = bl.SettlementType.dense
city_block.settlement_type_changed = True
logging.debug('Upgraded %i and downgraded %i settlement types for %i total building zones', upgraded, downgraded,
len(building_zones))
def count_zones_related_buildings(buildings: List[bl.Building], text: str) -> None:
total_related = 0
......@@ -489,7 +514,7 @@ def process(transformer: Transformation) -> Tuple[List[Polygon], List[bl.Buildin
count_zones_related_buildings(osm_buildings, 'after split major lines')
# =========== Link urban places with lit_area buffers ==================================
# =========== Link urban places with settlement_area buffers ==================================
settlement_clusters = _create_settlement_clusters(lit_areas, water_areas, urban_places)
last_time = time_logging('Time used in seconds for creating settlement_clusters', last_time)
......@@ -498,6 +523,9 @@ def process(transformer: Transformation) -> Tuple[List[Polygon], List[bl.Buildin
_link_building_zones_with_settlements(settlement_clusters, building_zones, highways_dict)
last_time = time_logging('Time used in seconds for linking building zones with settlement_clusters', last_time)
_sanity_check_settlement_types(building_zones, highways_dict)
last_time = time_logging('Time used in seconds for sanity checking settlement types', last_time)
count_zones_related_buildings(osm_buildings, 'after settlement linking')
# ============ Finally guess the land-use type ========================================
......
......@@ -327,6 +327,7 @@ class CityBlock:
self.osm_buildings = list() # List of already existing osm buildings
self.__settlement_type = None
self.settlement_type = building_lib.SettlementType.periphery
self.settlement_type_changed = False
self.__building_levels = 0
def relate_building(self, building: building_lib.Building) -> None:
......
......@@ -95,6 +95,8 @@ def _draw_settlement_zones(building_zones: List[m.BuildingZone], ax: maxs.Axes)
elif block.settlement_type is bl.SettlementType.periphery:
colour = 'yellow'
patch = PolygonPatch(block.geometry, facecolor=colour, edgecolor=colour)
if block.settlement_type_changed:
patch.set_hatch('/')
ax.add_patch(patch)
......@@ -296,7 +298,7 @@ def draw_zones(buildings: List[bl.Building], building_zones: List[m.BuildingZone
# Settlement type
my_figure = _create_a3_landscape_figure()
my_figure.suptitle("Built-up areas by settlement type \n[blue=centre (city), green=block, magenta=dense, \
yellow=periphery, grey=rural, brown=farmyard]")
yellow=periphery, grey=rural, brown=farmyard]. If hatched, then type upgraded or downgraded for sanity.")
ax = my_figure.add_subplot(111)
_draw_settlement_zones(building_zones, ax)
_set_ax_limits_from_bounds(ax, bounds)
......
......@@ -315,6 +315,8 @@ OWBB_PLACE_RADIUS_FACTOR_TOWN = 1.
OWBB_PLACE_TILE_BORDER_EXTENSION = 10000
OWBB_PLACE_SANITY_DENSITY = 0.2
# ==================== BUILDING GENERATION ============
OWBB_GENERATED_BUILDINGS_CACHE = False # has only effect if OWBB_LANDUSE_CACHE = False
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment