Merge remote-tracking branch 'origin/master' into geojson

# Conflicts:
#	city_model_structure/building.py
#	city_model_structure/bus_system.py
#	city_model_structure/city_object.py
#	city_model_structure/city_objects_cluster.py
#	city_model_structure/energy_system.py
#	imports/energy_systems/air_source_hp_parameters.py
#	imports/energy_systems/water_to_water_hp_parameters.py
#	imports/geometry/citygml.py
#	imports/geometry/gpandas.py
#	imports/geometry/obj.py
#	imports/geometry/rhino.py
#	imports/geometry_factory.py
This commit is contained in:
Guille Gutierrez 2022-11-28 13:13:55 -05:00
commit 8c6e34cd22
19 changed files with 108 additions and 41 deletions

View File

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

View File

@ -17,8 +17,8 @@ class BusSystem(CityObject):
""" """
BusSystem(CityObject) class BusSystem(CityObject) class
""" """
def __init__(self, name, lod, surfaces): def __init__(self, name, surfaces, city_lower_corner):
super().__init__(name, lod, surfaces) super().__init__(name, surfaces, city_lower_corner)
self._bus_routes = None self._bus_routes = None
self._bus_network = None self._bus_network = None
self._buses = 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.buildings_cluster import BuildingsCluster
from city_model_structure.fuel import Fuel from city_model_structure.fuel import Fuel
from city_model_structure.iot.station import Station 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.machine import Machine
from city_model_structure.parts_consisting_building import PartsConsistingBuilding from city_model_structure.parts_consisting_building import PartsConsistingBuilding
from city_model_structure.subway_entrance import SubwayEntrance from city_model_structure.subway_entrance import SubwayEntrance
@ -59,6 +60,7 @@ class City:
self._machines = None self._machines = None
self._stations = [] self._stations = []
self._lca_materials = None self._lca_materials = None
self._level_of_detail = LevelOfDetail()
@property @property
def fuels(self) -> [Fuel]: def fuels(self) -> [Fuel]:
@ -291,7 +293,6 @@ class City:
selected_region_upper_corner = [center[0] + radius, center[1] + radius, center[2] + radius] 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 = 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_file = self.climate_file
# selected_region_city.climate_reference_city = self.climate_reference_city
for city_object in self.city_objects: for city_object in self.city_objects:
location = city_object.centroid location = city_object.centroid
if location is not None: if location is not None:
@ -430,9 +431,9 @@ class City:
""" """
self._lca_materials = value 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 :return: LcaMaterial or None
""" """
for lca_material in self.lca_materials: for lca_material in self.lca_materials:
@ -452,3 +453,7 @@ class City:
for city_object in city.city_objects: for city_object in city.city_objects:
_merge_city.add_city_object(city_object) _merge_city.add_city_object(city_object)
return _merge_city return _merge_city
@property
def level_of_detail(self):
return self._level_of_detail

View File

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

View File

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

View File

@ -16,8 +16,8 @@ class EnergySystem(CityObject):
EnergySystem(CityObject) class EnergySystem(CityObject) class
""" """
def __init__(self, name, lod, surfaces): def __init__(self, name, surfaces):
super().__init__(name, lod, surfaces) super().__init__(name, surfaces)
self._air_source_hp = None self._air_source_hp = None
self._water_to_water_hp = None self._water_to_water_hp = None
self._type = 'energy_system' 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 Enrich the city by using NREL information
""" """
UsPhysicsParameters(self._city, self._base_path).enrich_buildings() UsPhysicsParameters(self._city, self._base_path).enrich_buildings()
self._city.level_of_detail.construction = 2
def enrich(self): def enrich(self):
""" """

View File

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

View File

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

View File

@ -23,6 +23,7 @@ class CityGml:
""" """
def __init__(self, path, extrusion_height_field=None, year_of_construction_field=None, function_field=None): def __init__(self, path, extrusion_height_field=None, year_of_construction_field=None, function_field=None):
self._city = None self._city = None
self._lod = None
self._lod1_tags = ['lod1Solid', 'lod1MultiSurface'] self._lod1_tags = ['lod1Solid', 'lod1MultiSurface']
self._lod2_tags = ['lod2Solid', 'lod2MultiSurface', 'lod2MultiCurve'] self._lod2_tags = ['lod2Solid', 'lod2MultiSurface', 'lod2MultiCurve']
self._extrusion_height_field = extrusion_height_field self._extrusion_height_field = extrusion_height_field
@ -69,7 +70,7 @@ class CityGml:
self._srs_name = envelope['@srsName'] self._srs_name = envelope['@srsName']
else: else:
# If not coordinate system given assuming hub standard # If not coordinate system given assuming hub standard
self._srs_name = "EPSG:4326" self._srs_name = "EPSG:26911"
else: else:
# get the boundary from the city objects instead # get the boundary from the city objects instead
for city_object_member in self._gml['CityModel']['cityObjectMember']: for city_object_member in self._gml['CityModel']['cityObjectMember']:
@ -114,14 +115,16 @@ class CityGml:
if 'function' in city_object: if 'function' in city_object:
function = city_object['function'] function = city_object['function']
if any(key in city_object for key in self._lod1_tags): 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 surfaces = CityGmlLod1(city_object).surfaces
elif any(key in city_object for key in self._lod2_tags): 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 surfaces = CityGmlLod2(city_object).surfaces
else: else:
raise NotImplementedError("Not supported level of detail") raise NotImplementedError("Not supported level of detail")
return Building(name, lod, surfaces, year_of_construction, function, terrains=None) return Building(name, surfaces, year_of_construction, function, terrains=None)
def _create_parts_consisting_building(self, city_object): def _create_parts_consisting_building(self, city_object):
name = city_object['@id'] name = city_object['@id']
@ -147,4 +150,5 @@ class CityGml:
self._city.add_city_objects_cluster(self._create_parts_consisting_building(city_object)) self._city.add_city_objects_cluster(self._create_parts_consisting_building(city_object))
else: else:
self._city.add_city_object(self._create_building(city_object)) self._city.add_city_object(self._create_building(city_object))
self._city.level_of_detail.geometry = self._lod
return self._city return self._city

View File

@ -59,7 +59,6 @@ class GPandas:
self._city = City(self._lower_corner, self._upper_corner, self._srs_name) self._city = City(self._lower_corner, self._upper_corner, self._srs_name)
for scene_index, bldg in self._scene.iterrows(): for scene_index, bldg in self._scene.iterrows():
geometry = bldg.geom geometry = bldg.geom
print(geometry['coordinates'][0])
polygon = ShapelyPoly(geometry['coordinates'][0]) polygon = ShapelyPoly(geometry['coordinates'][0])
height = float(bldg['height']) height = float(bldg['height'])
building_mesh = trimesh.creation.extrude_polygon(polygon, height) building_mesh = trimesh.creation.extrude_polygon(polygon, height)
@ -82,8 +81,9 @@ class GPandas:
perimeter_polygon = solid_polygon perimeter_polygon = solid_polygon
surface = Surface(solid_polygon, perimeter_polygon) surface = Surface(solid_polygon, perimeter_polygon)
surfaces.append(surface) surfaces.append(surface)
building = Building(name, lod, surfaces, year_of_construction, function) building = Building(name, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
self._city.add_city_object(building) self._city.add_city_object(building)
self._city.level_of_detail.geometry = lod
return self._city return self._city
@staticmethod @staticmethod

View File

@ -77,6 +77,7 @@ class Obj:
perimeter_polygon = solid_polygon perimeter_polygon = solid_polygon
surface = Surface(solid_polygon, perimeter_polygon) surface = Surface(solid_polygon, perimeter_polygon)
surfaces.append(surface) surfaces.append(surface)
building = Building(name, lod, surfaces, year_of_construction, function) building = Building(name, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
self._city.add_city_object(building) self._city.add_city_object(building)
self._city.level_of_detail.geometry = lod
return self._city return self._city

View File

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

View File

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

View File

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

View File

@ -41,7 +41,6 @@ class TestGeometryFactory(TestCase):
def _check_buildings(self, city): def _check_buildings(self, city):
for building in city.buildings: for building in city.buildings:
self.assertIsNotNone(building.name, 'building name is none') 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.type, 'building type is none')
self.assertIsNotNone(building.volume, 'building volume is none') self.assertIsNotNone(building.volume, 'building volume is none')
self.assertIsNotNone(building.detailed_polyhedron, 'building detailed polyhedron 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 building.year_of_construction = 2006
if building.function is None: if building.function is None:
building.function = 'large office' building.function = 'large office'
building.attic_heated = False building.attic_heated = 0
building.basement_heated = False building.basement_heated = 0
ConstructionFactory('nrel', city).enrich() ConstructionFactory('nrel', city).enrich()
UsageFactory('comnet', 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.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} ' self.assertIsNotNone(usage_zone.mechanical_air_change, f'usage zone {usage_zone.usage} '
f'mechanical_air_change is none') f'mechanical_air_change is none')
# export files # export files
try: try:
EnergyBuildingsExportsFactory('insel_monthly_energy_balance', city, self._output_path).export_debug() EnergyBuildingsExportsFactory('insel_monthly_energy_balance', city, self._output_path).export()
except Exception: except Exception:
self.fail("Insel MonthlyEnergyBalance ExportsFactory raised ExceptionType unexpectedly!") self.fail("Insel MonthlyEnergyBalance ExportsFactory raised ExceptionType unexpectedly!")

View File

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