Partial implementation energy ADE, till geometry export (LOD2) this commit is broken, due some missing attribute (max_(coord)) at surface class

This commit is contained in:
Guille Gutierrez 2021-03-25 09:11:30 -04:00
parent dbbe3d56fc
commit 636047b99c
5 changed files with 169 additions and 21 deletions

View File

@ -7,6 +7,7 @@ contributors: Pilar Monsalvete pilar_monsalvete@yahoo.es
from typing import List from typing import List
import numpy as np
from city_model_structure.attributes.surface import Surface from city_model_structure.attributes.surface import Surface
from city_model_structure.attributes.thermal_boundary import ThermalBoundary from city_model_structure.attributes.thermal_boundary import ThermalBoundary
@ -35,6 +36,7 @@ class Building(CityObject):
self._average_storey_height = None self._average_storey_height = None
self._storeys_above_ground = None self._storeys_above_ground = None
self._floor_area = None self._floor_area = None
self._roof_type = None
self._usage_zones = [] self._usage_zones = []
self._type = 'building' self._type = 'building'
self._heating = dict() self._heating = dict()
@ -43,7 +45,9 @@ class Building(CityObject):
self._global_horizontal = dict() self._global_horizontal = dict()
self._diffuse = dict() self._diffuse = dict()
self._beam = dict() self._beam = dict()
self._grounds = []
self._roofs = []
self._walls = []
# ToDo: Check this for LOD4 # ToDo: Check this for LOD4
self._thermal_zones = [] self._thermal_zones = []
if self.lod < 4: if self.lod < 4:
@ -52,11 +56,34 @@ class Building(CityObject):
for t_zones in self._thermal_zones: for t_zones in self._thermal_zones:
t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces] t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces]
surface_id = 0
for surface in self.surfaces: for surface in self.surfaces:
surface.lower_corner = self._lower_corner if surface.type == 'Ground':
surface.parent(self, surface_id) self._grounds.append(surface)
surface_id += 1 elif surface.type == 'Wall':
self._walls.append(surface)
else:
self._roofs.append(surface)
@property
def grounds(self) -> [Surface]:
"""
Building ground surfaces
"""
return self._grounds
@property
def roofs(self) -> [Surface]:
"""
Building roof surfaces
"""
return self._roofs
@property
def walls(self) -> [Surface]:
"""
Building wall surfaces
"""
return self._walls
@property @property
def usage_zones(self) -> List[UsageZone]: def usage_zones(self) -> List[UsageZone]:
@ -325,6 +352,21 @@ class Building(CityObject):
storeys.append(rest_trimesh) storeys.append(rest_trimesh)
return storeys return storeys
@property
def roof_type(self):
"""
Roof type for the building flat or pitch
"""
if self._roof_type is None:
self._roof_type = 'flat'
for roof in self.roofs:
grads = np.rad2deg(roof.inclination)
if 355 > grads > 5:
self._roof_type = 'pitch'
break
print (self._roof_type)
return self._roof_type
@property @property
def floor_area(self): def floor_area(self):
""" """

View File

@ -53,25 +53,26 @@ class EnergyAde:
for building in self._city.buildings: for building in self._city.buildings:
building_dic = { building_dic = {
'bldg:Building': { 'bldg:Building': {
'@gml:id': building.name, '@gml:id': building.name
} }
} }
building_dic = EnergyAde._get_measures(building, building_dic) building_dic = EnergyAde._measures(building, building_dic)
building_dic = EnergyAde._building_geometry(building, building_dic, self._city)
buildings.append(building_dic) buildings.append(building_dic)
energy_ade['core:CityModel']['core:cityObjectMember'] = buildings energy_ade['core:CityModel']['core:cityObjectMember'] = buildings
file_name = self._city.name + '_ade.gml' file_name = self._city.name + '_ade.gml'
file_path = Path(self._path / file_name).resolve() file_path = Path(self._path / file_name).resolve()
with open(file_path, 'w' ) as file: with open(file_path, 'w' ) as file:
file.write(xmltodict.unparse(energy_ade,pretty=True)) file.write(xmltodict.unparse(energy_ade,pretty=True, short_empty_elements=True))
@staticmethod @staticmethod
def _get_measures(building, building_dic): def _measures(building, building_dic):
measures = [] measures = []
measure = EnergyAde._get_measure(building.heating, 'year', 'Energy demand heating', 'INSEL') measure = EnergyAde._measure(building.heating, 'year', 'Energy demand heating', 'INSEL')
if measure is not None: if measure is not None:
measures.append(measure) measures.append(measure)
measure = EnergyAde._get_measure(building.cooling, 'year', 'Energy demand cooling', 'INSEL') measure = EnergyAde._measure(building.cooling, 'year', 'Energy demand cooling', 'INSEL')
if measure is not None: if measure is not None:
measures.append(measure) measures.append(measure)
if len(measures) != 0: if len(measures) != 0:
@ -80,12 +81,12 @@ class EnergyAde:
periods = [] periods = []
for key in building.heating: for key in building.heating:
if key != 'year': if key != 'year':
period = EnergyAde._get_period(building.heating, key, 'Heating energy', 'INSEL') period = EnergyAde._period(building.heating, key, 'Heating energy', 'INSEL')
periods.append(period) periods.append(period)
for key in building.cooling: for key in building.cooling:
if key != 'year': if key != 'year':
period = EnergyAde._get_period(building.cooling, key, 'Cooling energy', 'INSEL') period = EnergyAde._period(building.cooling, key, 'Cooling energy', 'INSEL')
periods.append(period) periods.append(period)
if len(periods) != 0: if len(periods) != 0:
@ -94,7 +95,7 @@ class EnergyAde:
return building_dic return building_dic
@staticmethod @staticmethod
def _get_measure(measure_dict, key_value, name, source): def _measure(measure_dict, key_value, name, source):
measure = None measure = None
if key_value in measure_dict: if key_value in measure_dict:
measure = { measure = {
@ -107,7 +108,7 @@ class EnergyAde:
return measure return measure
@staticmethod @staticmethod
def _get_period(measure_dict, key_value, description, source): def _period(measure_dict, key_value, description, source):
period = { period = {
'@gml:id': uuid.uuid4(), '@gml:id': uuid.uuid4(),
'energy:energyAmount': { 'energy:energyAmount': {
@ -131,3 +132,105 @@ class EnergyAde:
} }
} }
return period return period
@staticmethod
def _building_geometry(building, building_dic, city):
building_dic['bldg:function'] = building.function
building_dic['bldg:usage'] = ', '.join([u.usage for u in building.usage_zones])
building_dic['bldg:yearOfConstruction'] = building.year_of_construction
building_dic['bldg:roofType'] = building.roof_type
building_dic['bldg:measuredHeight'] = {
'@uom': 'm',
'#text': f'{building.max_height}'
}
building_dic['bldg:storeysAboveGround'] = building.storeys_above_ground
if building.lod == 1:
building_dic = EnergyAde._lod1(building, building_dic, city)
elif building.lod == 2:
building_dic = EnergyAde._lod2(building, building_dic, city)
else:
raise NotImplementedError('Only lod 1 and 2 can be exported')
return building_dic
@staticmethod
def _lod1(building, building_dic, city):
raise NotImplementedError('Only lod 1 and 2 can be exported')
@staticmethod
def _lod2(building, building_dic, city):
surface_members = []
boundaries = [{
'gml:Envelope': {
'@srsName': city.srs_name,
'@srsDimension': 3,
'gml:lowerCorner': ' '.join([str(e) for e in city.lower_corner]),
'gml:upperCorner': ' '.join([str(e) for e in city.upper_corner])
}}]
for surface in building.surfaces:
surface_member = { '@xlink:href': f'#PolyId{surface.name}'}
surface_members.append(surface_member)
if surface.type == 'Wall':
surface_type = 'bldg:WallSurface'
elif surface.type == 'Ground':
surface_type = 'bldg:GroundSurface'
else:
surface_type = 'bldg:RoofSurface'
surface_dic = {
surface_type: {
'@gml:id': f'GML_{uuid.uuid4()}',
'gml:name': f'{surface.name} ({surface.type})',
'gml:boundedBy': {
'gml:Envelope': {
'@srsName': city.srs_name,
'gml:lowerCorner': f'{surface.min_x} {surface.min_y} {surface.min_z}',
'gml:upperCorner': f'{surface.max_x} {surface.max_y} {surface.max_z}'
}
},
'bldg:lod2MultiSurface': {
'gml:MultiSurface': {
'@srsName': city.srs_name,
'@gml:id': f'GML_{uuid.uuid4()}',
'surfaceMember': {
'gml:Polygon': {
'@srsName': city.srs_name,
'@gml:id': f'#PolyId{surface.name}',
'gml:exterior': {
'gml:LinearRing': {
'@gml:id': f'#PolyId{surface.name}_0',
'gml:posList': {
'@srsDimension': '3',
'@count': len(surface.points_list) + 1,
'#text': f'{" ".join(map(str, surface.points_list))} {" ".join(map(str, surface.points[0]))}'
}
}
}
}
}
}
}
}
}
print(surface_dic)
boundaries.append(surface_dic)
print(surface_dic)
building_dic['bldg:lod2Solid'] = {
'gml:Solid': {
'@gml:id': f'GML_{uuid.uuid4()}',
'gml:exterior': {
'gml:CompositeSurface': {
'@srsName': city.srs_name,
'@gml:id': f'GML_{uuid.uuid4()}',
'gml:surfaceMember': surface_members
}
}
}
}
building_dic['gml:boundedBy'] = boundaries
print(building_dic)
return building_dic

View File

@ -9,4 +9,4 @@ from exports.formats.triangular import Triangular
class Stl(Triangular): class Stl(Triangular):
def __init__(self, city, path): def __init__(self, city, path):
super().__init__(city, path, 'stl') super().__init__(city, path, 'stl', write_mode='wb')

View File

@ -8,20 +8,22 @@ from trimesh import Trimesh
class Triangular: class Triangular:
def __init__(self, city, path, triangular_format): def __init__(self, city, path, triangular_format, write_mode='w'):
self._city = city self._city = city
self._path = path self._path = path
self._triangular_format = triangular_format self._triangular_format = triangular_format
self._write_mode = write_mode
self._export() self._export()
def _export(self): def _export(self):
if self._city.name is None: if self._city.name is None:
self._city.name = 'unknown_city' self._city.name = 'unknown_city'
file_name = self._city.name + '.' + self._triangular_format file_name = self._city.name + '.' + self._triangular_format
file_path = (Path(self._path).resolve() / file_name).resolve() file_path = (Path(self._path).resolve() / file_name).resolve()
print(file_path)
trimesh = Trimesh() trimesh = Trimesh()
for building in self._city.buildings: for building in self._city.buildings:
trimesh = trimesh.union(building.simplified_polyhedron.trimesh) trimesh = trimesh.union(building.simplified_polyhedron.trimesh)
with open(file_path, 'w') as file:
with open(file_path, self._write_mode) as file:
file.write(trimesh.export(file_type=self._triangular_format)) file.write(trimesh.export(file_type=self._triangular_format))

View File

@ -6,6 +6,7 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
from pathlib import Path from pathlib import Path
from unittest import TestCase from unittest import TestCase
from imports.geometry_factory import GeometryFactory
from exports.exports_factory import ExportsFactory from exports.exports_factory import ExportsFactory
from city_model_structure.city import City from city_model_structure.city import City
@ -25,7 +26,7 @@ class TestExports(TestCase):
def _get_city(self): def _get_city(self):
if self._city_gml is None: if self._city_gml is None:
file_path = (self._example_path / 'kelowna.pickle').resolve() file_path = (self._example_path / 'one_building_in_kelowna.pickle').resolve()
self._city_gml = City.load(file_path) self._city_gml = City.load(file_path)
return self._city_gml return self._city_gml