""" 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 from pathlib import Path class EnergyAde: def __init__(self, city, path): self._city = city self._path = path self._export() def _export(self): 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]) } } } } buildings = [] for building in self._city.buildings: building_dic = { 'bldg:Building': { '@gml:id': building.name } } 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, short_empty_elements=True)) @staticmethod def _measures(building, building_dic): measures = [] measure = EnergyAde._measure(building.heating, 'year', 'Energy demand heating', 'INSEL') if measure is not None: measures.append(measure) measure = EnergyAde._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._period(building.heating, key, 'Heating energy', 'INSEL') periods.append(period) for key in building.cooling: if key != 'year': period = EnergyAde._period(building.cooling, key, 'Cooling energy', 'INSEL') periods.append(period) if len(periods) != 0: building_dic['energy:demands'] = {'energy:EnergyDemand': periods} return building_dic @staticmethod def _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 _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(float(e) / 1000) for e in measure_dict[key_value][source]]) } } } } } 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