Merge branch 'lod' into 'master'

Lod

See merge request Guille/hub!41
This commit is contained in:
Guillermo Gutierrez Morote 2022-11-25 20:33:34 +00:00
commit 553257c720
20 changed files with 112 additions and 42 deletions

View File

@ -20,8 +20,8 @@ class Building(CityObject):
"""
Building(CityObject) class
"""
def __init__(self, name, lod, surfaces, year_of_construction, function, city_lower_corner, terrains=None):
super().__init__(name, lod, surfaces, city_lower_corner)
def __init__(self, name, surfaces, year_of_construction, function, city_lower_corner, terrains=None):
super().__init__(name, surfaces, city_lower_corner)
self._households = None
self._basement_heated = None
self._attic_heated = None
@ -131,6 +131,9 @@ class Building(CityObject):
def attic_heated(self) -> Union[None, int]:
"""
Get if the city object attic is heated
0: no attic in the building
1: attic exists but is not heated
2: attic exists and is heated
:return: None or int
"""
return self._attic_heated
@ -139,6 +142,9 @@ class Building(CityObject):
def attic_heated(self, value):
"""
Set if the city object attic is heated
0: no attic in the building
1: attic exists but is not heated
2: attic exists and is heated
:param value: int
"""
if value is not None:
@ -148,6 +154,9 @@ class Building(CityObject):
def basement_heated(self) -> Union[None, int]:
"""
Get if the city object basement is heated
0: no basement in the building
1: basement exists but is not heated
2: basement exists and is heated
:return: None or int
"""
return self._basement_heated
@ -156,6 +165,9 @@ class Building(CityObject):
def basement_heated(self, value):
"""
Set if the city object basement is heated
0: no basement in the building
1: basement exists but is not heated
2: basement exists and is heated
:param value: int
"""
if value is not None:

View File

@ -17,8 +17,8 @@ class BusSystem(CityObject):
"""
BusSystem(CityObject) class
"""
def __init__(self, name, lod, surfaces, city_lower_corner):
super().__init__(name, lod, surfaces, city_lower_corner)
def __init__(self, name, surfaces, city_lower_corner):
super().__init__(name, surfaces, city_lower_corner)
self._bus_routes = None
self._bus_network = None
self._buses = None

View File

@ -21,6 +21,7 @@ from city_model_structure.city_objects_cluster import CityObjectsCluster
from city_model_structure.buildings_cluster import BuildingsCluster
from city_model_structure.fuel import Fuel
from city_model_structure.iot.station import Station
from city_model_structure.level_of_detail import LevelOfDetail
from city_model_structure.machine import Machine
from city_model_structure.parts_consisting_building import PartsConsistingBuilding
from city_model_structure.subway_entrance import SubwayEntrance
@ -59,6 +60,7 @@ class City:
self._machines = None
self._stations = []
self._lca_materials = None
self._level_of_detail = LevelOfDetail()
@property
def fuels(self) -> [Fuel]:
@ -287,7 +289,6 @@ class City:
selected_region_upper_corner = [center[0] + radius, center[1] + radius, center[2] + radius]
selected_region_city = City(selected_region_lower_corner, selected_region_upper_corner, srs_name=self.srs_name)
selected_region_city.climate_file = self.climate_file
# selected_region_city.climate_reference_city = self.climate_reference_city
for city_object in self.city_objects:
location = city_object.centroid
if location is not None:
@ -426,9 +427,9 @@ class City:
"""
self._lca_materials = value
def lca_material(self, lca_id) -> LcaMaterial:
def lca_material(self, lca_id) -> Union[LcaMaterial, None]:
"""
Get the lca materiol matching the given Id
Get the lca material matching the given Id
:return: LcaMaterial or None
"""
for lca_material in self.lca_materials:
@ -448,3 +449,7 @@ class City:
for city_object in city.city_objects:
_merge_city.add_city_object(city_object)
return _merge_city
@property
def level_of_detail(self):
return self._level_of_detail

View File

@ -18,9 +18,8 @@ class CityObject:
"""
class CityObject
"""
def __init__(self, name, lod, surfaces, city_lower_corner):
def __init__(self, name, surfaces, city_lower_corner):
self._name = name
self._lod = lod
self._surfaces = surfaces
self._city_lower_corner = city_lower_corner
self._type = None
@ -45,15 +44,6 @@ class CityObject:
"""
return self._name
@property
def lod(self) -> int:
"""
Get city object level of detail 1, 2, 3 or 4
:return: int
"""
lod = int(math.log(self._lod, 2) + 1)
return lod
@property
def type(self) -> str:
"""

View File

@ -20,8 +20,7 @@ class CityObjectsCluster(ABC, CityObject):
self._cluster_type = cluster_type
self._city_objects = city_objects
self._sensors = []
self._lod = ''
super().__init__(name, self._lod, None, None)
super().__init__(name, None, None)
@property
def name(self):

View File

@ -16,8 +16,8 @@ class EnergySystem(CityObject):
EnergySystem(CityObject) class
"""
def __init__(self, name, lod, surfaces, city_lower_corner):
super().__init__(name, lod, surfaces, city_lower_corner)
def __init__(self, name, surfaces, city_lower_corner):
super().__init__(name, surfaces, city_lower_corner)
self._air_source_hp = None
self._water_to_water_hp = None
self._type = 'energy_system'

View File

@ -0,0 +1,57 @@
"""
Level of detail module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
class LevelOfDetail:
"""
Level of detail for the city class
"""
def __init__(self):
self._geometry = None
self._construction = None
self._usage = None
@property
def geometry(self):
"""
Get the city minimal geometry level of detail
"""
return self._geometry
@geometry.setter
def geometry(self, value):
"""
Set the city minimal geometry level of detail
"""
self._geometry = value
@property
def construction(self):
"""
Get the city minimal construction level of detail
"""
return self._construction
@construction.setter
def construction(self, value):
"""
Set the city minimal construction level of detail
"""
self._construction = value
@property
def usage(self):
"""
Get the city minimal usage level of detail
"""
return self._usage
@usage.setter
def usage(self, value):
"""
Set the city minimal usage level of detail
"""
self._usage = value

View File

@ -24,6 +24,7 @@ class ConstructionFactory:
Enrich the city by using NREL information
"""
UsPhysicsParameters(self._city, self._base_path).enrich_buildings()
self._city.level_of_detail.construction = 2
def enrich(self):
"""

View File

@ -82,7 +82,7 @@ class AirSourceHeatPumpParameters:
heat_pump.heating_comp_power = h_data[1]
heat_pump.heating_capacity_coff = self._compute_coefficients(h_data)
energy_system = EnergySystem('{} capacity heat pump'.format(heat_pump.model), 0, [], None)
energy_system = EnergySystem('{} capacity heat pump'.format(heat_pump.model), [], None)
energy_system.air_source_hp = heat_pump
self._city.add_city_object(energy_system)
return self._city

View File

@ -129,7 +129,7 @@ class WaterToWaterHPParameters:
heat_pump.entering_water_temp = data['ewt']
heat_pump.leaving_water_temp = data['lwt']
heat_pump.power_demand_coff = self._compute_coefficients(data)
energy_system = EnergySystem(heat_pump.model, 0, [], None)
energy_system = EnergySystem(heat_pump.model, [], None)
energy_system.water_to_water_hp = heat_pump
self._city.add_city_object(energy_system)
return self._city

View File

@ -23,6 +23,7 @@ class CityGml:
"""
def __init__(self, path):
self._city = None
self._lod = None
self._lod1_tags = ['lod1Solid', 'lod1MultiSurface']
self._lod2_tags = ['lod2Solid', 'lod2MultiSurface', 'lod2MultiCurve']
self._lower_corner = None
@ -74,8 +75,6 @@ class CityGml:
continue
envelope = bound['Envelope']
self._srs_name = envelope['@srsName']
lower_corner = None
upper_corner = None
if '#text' in envelope['lowerCorner']:
lower_corner = np.fromstring(envelope['lowerCorner']['#text'], dtype=float, sep=' ')
upper_corner = np.fromstring(envelope['upperCorner']['#text'], dtype=float, sep=' ')
@ -110,14 +109,16 @@ class CityGml:
if 'function' in city_object:
function = city_object['function']
if any(key in city_object for key in self._lod1_tags):
lod = 1
if self._lod is None or self._lod > 1:
self._lod = 1
surfaces = CityGmlLod1(city_object).surfaces
elif any(key in city_object for key in self._lod2_tags):
lod = 2
if self._lod is None or self._lod > 2:
self._lod = 2
surfaces = CityGmlLod2(city_object).surfaces
else:
raise NotImplementedError("Not supported level of detail")
return Building(name, lod, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
return Building(name, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
def _create_parts_consisting_building(self, city_object):
name = city_object['@id']
@ -143,4 +144,5 @@ class CityGml:
self._city.add_city_objects_cluster(self._create_parts_consisting_building(city_object))
else:
self._city.add_city_object(self._create_building(city_object))
self._city.level_of_detail.geometry = self._lod
return self._city

View File

@ -57,6 +57,7 @@ class GPandas:
Get city out of a GeoPandas Table
"""
if self._city is None:
lod = 1
self._city = City(self._lower_corner, self._upper_corner, self._srs_name)
for scene_index, bldg in self._scene.iterrows():
geometry = bldg.geom
@ -67,7 +68,7 @@ class GPandas:
trimesh.repair.fix_winding(building_mesh)
year_of_construction = int(bldg['year_built'])
name = str(scene_index)
lod = 1
if year_of_construction > 2000:
function = cte.RESIDENTIAL
else:
@ -82,8 +83,9 @@ class GPandas:
perimeter_polygon = solid_polygon
surface = Surface(solid_polygon, perimeter_polygon)
surfaces.append(surface)
building = Building(name, lod, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
building = Building(name, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
self._city.add_city_object(building)
self._city.level_of_detail.geometry = lod
return self._city
@staticmethod

View File

@ -59,10 +59,11 @@ class Obj:
self._city = City(self._lower_corner, self._upper_corner, srs_name)
scene = self.scene.geometry
keys = scene.keys()
lod = 1
for key in keys:
name = key
# todo: where do we get this information from?
lod = 1
year_of_construction = 0
function = ''
@ -77,6 +78,7 @@ class Obj:
perimeter_polygon = solid_polygon
surface = Surface(solid_polygon, perimeter_polygon)
surfaces.append(surface)
building = Building(name, lod, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
building = Building(name, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
self._city.add_city_object(building)
self._city.level_of_detail.geometry = lod
return self._city

View File

@ -101,7 +101,7 @@ class Rhino:
if face is None:
break
hub_surfaces = hub_surfaces + self._add_face(face)
building = Building(name, 3, hub_surfaces, 'unknown', 'unknown', (self._min_x, self._min_y, self._min_z), [])
building = Building(name, hub_surfaces, 'unknown', 'unknown', (self._min_x, self._min_y, self._min_z), [])
city_objects.append(building)
lower_corner = (self._min_x, self._min_y, self._min_z)
upper_corner = (self._max_x, self._max_y, self._max_z)
@ -133,4 +133,5 @@ class Rhino:
for building in buildings:
city.add_city_object(building)
city.level_of_detail.geometry = 3
return city

View File

@ -78,4 +78,4 @@ class GeometryFactory:
Enrich the city given to the class using the class given handler
:return: City
"""
return GPandas(geopandas.read_file(self._path)).city
return CityGml(self._path).city

View File

@ -26,12 +26,14 @@ class UsageFactory:
"""
Enrich the city with HFT usage library
"""
self._city.level_of_detail.usage = 2
return HftUsageParameters(self._city, self._base_path).enrich_buildings()
def _comnet(self):
"""
Enrich the city with COMNET usage library
"""
self._city.level_of_detail.usage = 2
return ComnetUsageParameters(self._city, self._base_path).enrich_buildings()
def enrich(self):

View File

@ -28,6 +28,7 @@ class TestConstructionFactory(TestCase):
file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('citygml', path=file_path).city
self.assertIsNotNone(self._city, 'city is none')
self.assertIsNotNone(self._city.level_of_detail.geometry, 'wrong construction level of detail')
return self._city
@staticmethod
@ -70,7 +71,6 @@ class TestConstructionFactory(TestCase):
def _check_buildings(self, city):
for building in city.buildings:
self.assertIsNotNone(building.name, 'building name is none')
self.assertIsNotNone(building.lod, 'building lod is none')
self.assertIsNotNone(building.type, 'building type is none')
self.assertIsNotNone(building.volume, 'building volume is none')
self.assertIsNotNone(building.detailed_polyhedron, 'building detailed polyhedron is none')

View File

@ -54,7 +54,6 @@ class TestGeometryFactory(TestCase):
def _check_buildings(self, city):
for building in city.buildings:
self.assertIsNotNone(building.name, 'building name is none')
self.assertIsNotNone(building.lod, 'building lod is none')
self.assertIsNotNone(building.type, 'building type is none')
self.assertIsNotNone(building.volume, 'building volume is none')
self.assertIsNotNone(building.detailed_polyhedron, 'building detailed polyhedron is none')

View File

@ -100,8 +100,8 @@ class TestExports(TestCase):
building.year_of_construction = 2006
if building.function is None:
building.function = 'large office'
building.attic_heated = False
building.basement_heated = False
building.attic_heated = 0
building.basement_heated = 0
ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', city).enrich()
@ -141,9 +141,8 @@ class TestExports(TestCase):
self.assertIsNotNone(usage_zone.days_year, f'usage zone {usage_zone.usage} days_year is none')
self.assertIsNotNone(usage_zone.mechanical_air_change, f'usage zone {usage_zone.usage} '
f'mechanical_air_change is none')
# export files
try:
EnergyBuildingsExportsFactory('insel_monthly_energy_balance', city, self._output_path).export_debug()
EnergyBuildingsExportsFactory('insel_monthly_energy_balance', city, self._output_path).export()
except Exception:
self.fail("Insel MonthlyEnergyBalance ExportsFactory raised ExceptionType unexpectedly!")

View File

@ -33,7 +33,6 @@ class TestUsageFactory(TestCase):
def _check_buildings(self, city):
for building in city.buildings:
self.assertIsNotNone(building.name, 'building name is none')
self.assertIsNotNone(building.lod, 'building lod is none')
self.assertIsNotNone(building.type, 'building type is none')
self.assertIsNotNone(building.volume, 'building volume is none')
self.assertIsNotNone(building.detailed_polyhedron, 'building detailed polyhedron is none')