Merge remote-tracking branch 'origin/master'

# Conflicts:
#	city_model_structure/building.py
This commit is contained in:
Pilar 2021-03-25 09:15:05 -04:00
commit e353a7c800
5 changed files with 169 additions and 19 deletions

View File

@ -7,6 +7,7 @@ contributors: Pilar Monsalvete pilar_monsalvete@yahoo.es
from typing import List
import numpy as np
from city_model_structure.attributes.surface import Surface
from city_model_structure.attributes.thermal_boundary import ThermalBoundary
@ -37,6 +38,7 @@ class Building(CityObject):
self._average_storey_height = None
self._storeys_above_ground = None
self._floor_area = None
self._roof_type = None
self._usage_zones = []
self._type = 'building'
self._heating = dict()
@ -50,6 +52,9 @@ class Building(CityObject):
self._min_z = ConfigurationHelper().max_coordinate
self._centroid = None
self._grounds = []
self._roofs = []
self._walls = []
# ToDo: Check this for LOD4
self._thermal_zones = []
if self.lod < 4:
@ -58,13 +63,37 @@ class Building(CityObject):
for t_zones in self._thermal_zones:
t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces]
surface_id = 0
for surface in self.surfaces:
surface.parent(self, surface_id)
self._min_x = min(self._min_x, surface.min_x)
self._min_y = min(self._min_y, surface.min_y)
self._min_z = min(self._min_z, surface.min_z)
surface_id += 1
if surface.type == 'Ground':
self._grounds.append(surface)
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
def usage_zones(self) -> List[UsageZone]:
@ -356,6 +385,21 @@ class Building(CityObject):
storeys.append(rest_trimesh)
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
def floor_area(self):
"""

View File

@ -53,25 +53,26 @@ class EnergyAde:
for building in self._city.buildings:
building_dic = {
'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)
energy_ade['core:CityModel']['core:cityObjectMember'] = buildings
file_name = self._city.name + '_ade.gml'
file_path = Path(self._path / file_name).resolve()
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
def _get_measures(building, building_dic):
def _measures(building, building_dic):
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:
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:
measures.append(measure)
if len(measures) != 0:
@ -80,12 +81,12 @@ class EnergyAde:
periods = []
for key in building.heating:
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)
for key in building.cooling:
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)
if len(periods) != 0:
@ -94,7 +95,7 @@ class EnergyAde:
return building_dic
@staticmethod
def _get_measure(measure_dict, key_value, name, source):
def _measure(measure_dict, key_value, name, source):
measure = None
if key_value in measure_dict:
measure = {
@ -107,7 +108,7 @@ class EnergyAde:
return measure
@staticmethod
def _get_period(measure_dict, key_value, description, source):
def _period(measure_dict, key_value, description, source):
period = {
'@gml:id': uuid.uuid4(),
'energy:energyAmount': {
@ -131,3 +132,105 @@ class EnergyAde:
}
}
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):
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:
def __init__(self, city, path, triangular_format):
def __init__(self, city, path, triangular_format, write_mode='w'):
self._city = city
self._path = path
self._triangular_format = triangular_format
self._write_mode = write_mode
self._export()
def _export(self):
if self._city.name is None:
self._city.name = 'unknown_city'
file_name = self._city.name + '.' + self._triangular_format
file_path = (Path(self._path).resolve() / file_name).resolve()
print(file_path)
trimesh = Trimesh()
for building in self._city.buildings:
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))

View File

@ -6,6 +6,7 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
from pathlib import Path
from unittest import TestCase
from imports.geometry_factory import GeometryFactory
from exports.exports_factory import ExportsFactory
from city_model_structure.city import City
@ -25,7 +26,7 @@ class TestExports(TestCase):
def _get_city(self):
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)
return self._city_gml