Merge remote-tracking branch 'origin/master'
# Conflicts: # city_model_structure/building.py
This commit is contained in:
commit
e353a7c800
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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': {
|
||||
|
@ -130,4 +131,106 @@ 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
|
|
@ -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')
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user