diff --git a/city_model_structure/building.py b/city_model_structure/building.py index 75a37371..862ad9ff 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -20,7 +20,7 @@ class Building(CityObject): """ Building(CityObject) class """ - def __init__(self, name, lod, surfaces, terrains, year_of_construction, function, lower_corner): + def __init__(self, name, lod, surfaces, terrains, year_of_construction, function, city_lower_corner): # todo: take the default values out of the classes!! super().__init__(lod, surfaces, name) self._basement_heated = None @@ -28,8 +28,8 @@ class Building(CityObject): self._terrains = terrains self._year_of_construction = year_of_construction self._function = function - # todo: this name is not clear. Is it lower corner of the building or lower corner of te city?? - self._lower_corner = lower_corner + #todo: change lower_corner to building lower_corner instead city lower corner + self._lower_corner = city_lower_corner self._heated = None self._cooled = None self._average_storey_height = None diff --git a/exports/exports_factory.py b/exports/exports_factory.py index 6a9d0216..c7a1a0e0 100644 --- a/exports/exports_factory.py +++ b/exports/exports_factory.py @@ -6,6 +6,7 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc from exports.formats.stl import Stl from exports.formats.obj import Obj +from exports.formats.energy_ade import EnergyAde class ExportsFactory: @@ -25,6 +26,14 @@ class ExportsFactory: """ raise NotImplementedError() + @property + def _energy_ade(self): + """ + Export to citygml with application domain extensions + :return: None + """ + return EnergyAde(self._city, self._path) + @property def _stl(self): """ diff --git a/exports/formats/energy_ade.py b/exports/formats/energy_ade.py new file mode 100644 index 00000000..01bf539a --- /dev/null +++ b/exports/formats/energy_ade.py @@ -0,0 +1,131 @@ +""" +ExportsFactory export a city into several formats +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" +import xmltodict +import uuid + + +class EnergyAde: + def __init__(self, city, path): + self._city = city + self._path = path + self._export() + + def _export(self): + print('start') + energy_ade = { + 'core:CityModel': { + '@xmlns:brid':'http://www.opengis.net/citygml/bridge/2.0', + '@xmlns:tran':'http://www.opengis.net/citygml/transportation/2.0', + '@xmlns:frn':'http://www.opengis.net/citygml/cityfurniture/2.0', + '@xmlns:wtr':'http://www.opengis.net/citygml/waterbody/2.0', + '@xmlns:sch':'http://www.ascc.net/xml/schematron', + '@xmlns:veg':'http://www.opengis.net/citygml/vegetation/2.0', + '@xmlns:xlink':'http://www.w3.org/1999/xlink', + '@xmlns:tun':'http://www.opengis.net/citygml/tunnel/2.0', + '@xmlns:tex':'http://www.opengis.net/citygml/texturedsurface/2.0', + '@xmlns:gml':'http://www.opengis.net/gml', + '@xmlns:gen':'http://www.opengis.net/citygml/generics/2.0', + '@xmlns:dem':'http://www.opengis.net/citygml/relief/2.0', + '@xmlns:app':'http://www.opengis.net/citygml/appearance/2.0', + '@xmlns:luse':'http://www.opengis.net/citygml/landuse/2.0', + '@xmlns:xAL':'urn:oasis:names:tc:ciq:xsdschema:xAL:2.0', + '@xmlns:xsi':'http://www.w3.org/2001/XMLSchema-instance', + '@xmlns:smil20lang':'http://www.w3.org/2001/SMIL20/Language', + '@xmlns:pbase':'http://www.opengis.net/citygml/profiles/base/2.0', + '@xmlns:smil20': 'http://www.w3.org/2001/SMIL20/', + '@xmlns:bldg':'http://www.opengis.net/citygml/building/2.0', + '@xmlns:core':'http://www.opengis.net/citygml/2.0', + '@xmlns:grp':'http://www.opengis.net/citygml/cityobjectgroup/2.0', + 'gml:boundedBy': { + 'gml:Envelope': { + '@srsName': self._city.srs_name, + '@srsDimension': 3, + 'gml:lowerCorner': ' '.join([str(e) for e in self._city.lower_corner]), + 'gml:upperCorner': ' '.join([str(e) for e in self._city.upper_corner]) + } + } + } + } + print(xmltodict.unparse(energy_ade,pretty=True)) + buildings = [] + for building in self._city.buildings: + building_dic = { + 'bldg:Building': { + '@gml:id': building.name, + } + } + building_dic = EnergyAde._get_measures(building, building_dic) + buildings.append(building_dic) + + energy_ade['core:CityModel'] = {'core:cityObjectMember': buildings} + print(xmltodict.unparse(energy_ade,pretty=True)) + + @staticmethod + def _get_measures(building, building_dic): + measures = [] + measure = EnergyAde._get_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') + if measure is not None: + measures.append(measure) + if len(measures) != 0: + building_dic['genobj:measureAttribute'] = measures + + periods = [] + for key in building.heating: + if key != 'year': + period = EnergyAde._get_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, 'Heating energy', 'INSEL') + periods.append(period) + + if len(periods) != 0: + building_dic['energy:demands']['energy:EnergyDemand'] = periods + + return building_dic + + @staticmethod + def _get_measure(measure_dict, key_value, name, source): + measure = None + if key_value in measure_dict: + measure = { + '@name': name, + 'genobj:value': { + '@uom': 'kWh', + '#text': ' '.join([str(e/1000) for e in measure_dict[key_value][source]]) + } + } + return measure + + @staticmethod + def _get_period(measure_dict, key_value, description, source): + period = { + '@gml:id': uuid.uuid4(), + 'energy:energyAmount': { + 'energy:RegularTimeSeries': { + 'energy:variableProperties': { + 'energy:TimeValuesProperties': { + 'energy:acquisitionMethod': 'simulation', + 'energy:source': source, + 'energy:thematicDescription': description, + }, + 'energy:timeInterval': { + '@unit': key_value, + '#text': '1', + }, + 'energy:values': { + '@uom': 'kWh', + '#text': ' '.join([str(e / 1000) for e in measure_dict[key_value][source]]) + } + } + } + } + } + return period \ No newline at end of file diff --git a/exports/formats/obj.py b/exports/formats/obj.py index f3f572ed..13e1fb2c 100644 --- a/exports/formats/obj.py +++ b/exports/formats/obj.py @@ -1,3 +1,10 @@ +""" +export a city into Obj format +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" + + from exports.formats.triangular import Triangular from pathlib import Path from imports.geometry_factory import GeometryFactory diff --git a/exports/formats/stl.py b/exports/formats/stl.py index 984c207a..40af72fb 100644 --- a/exports/formats/stl.py +++ b/exports/formats/stl.py @@ -1,3 +1,9 @@ +""" +export a city into Stl format +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" + from exports.formats.triangular import Triangular diff --git a/exports/formats/triangular.py b/exports/formats/triangular.py index 8a7b9dda..a69182c8 100644 --- a/exports/formats/triangular.py +++ b/exports/formats/triangular.py @@ -1,3 +1,8 @@ +""" +export a city from trimesh into Triangular format (obj or stl) +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca +""" from pathlib import Path from trimesh import Trimesh @@ -7,9 +12,9 @@ class Triangular: self._city = city self._path = path self._triangular_format = triangular_format - self._files() + self._export() - def _files(self): + def _export(self): if self._city.name is None: self._city.name = 'unknown_city' file_name = self._city.name + '.' + self._triangular_format diff --git a/imports/geometry_factory.py b/imports/geometry_factory.py index 447a29fe..64a5ce69 100644 --- a/imports/geometry_factory.py +++ b/imports/geometry_factory.py @@ -5,7 +5,7 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc """ from city_model_structure.city import City from city_model_structure.city_object import CityObject -from imports.geometry_feeders.city_gml import CityGml +from imports.geometry_feeders.citygml import CityGml from imports.geometry_feeders.osm_subway import OsmSubway from imports.geometry_feeders.obj import Obj diff --git a/imports/geometry_feeders/city_gml.py b/imports/geometry_feeders/citygml.py similarity index 99% rename from imports/geometry_feeders/city_gml.py rename to imports/geometry_feeders/citygml.py index 9cff2ac7..422b27de 100644 --- a/imports/geometry_feeders/city_gml.py +++ b/imports/geometry_feeders/citygml.py @@ -20,6 +20,7 @@ class CityGml: self._city = None with open(path) as gml: # Clean the namespaces is an important task to prevent wrong ns:field due poor citygml implementations + self._gml = xmltodict.parse(gml.read(), process_namespaces=True, xml_attribs=True, namespaces={ 'http://www.opengis.net/gml': None, 'http://www.opengis.net/citygml/1.0': None, diff --git a/tests/test_exports.py b/tests/test_exports.py index 0c427df5..1ae941c0 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -8,7 +8,7 @@ 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 class TestExports(TestCase): """ @@ -25,8 +25,8 @@ class TestExports(TestCase): def _get_city(self): if self._city_gml is None: - file_path = (self._example_path / 'one_building_in_kelowna.gml').resolve() - self._city_gml = GeometryFactory('citygml', file_path).city + file_path = (self._example_path / 'one_building_in_kelowna_populated_weather_demand.pickle').resolve() + city = City.load(file_path) return self._city_gml def _export(self, export_type): @@ -38,3 +38,6 @@ class TestExports(TestCase): def test_stl_export(self): self._export('stl') + + def test_energy_ade_export(self): + self._export('energy_ade')