diff --git a/city_model_structure/attributes/surface.py b/city_model_structure/attributes/surface.py index 53a2723b..5be1f94d 100644 --- a/city_model_structure/attributes/surface.py +++ b/city_model_structure/attributes/surface.py @@ -42,6 +42,14 @@ class Surface: self._name = uuid.uuid4() return self._name + @property + def id(self): + """ + Surface id + :return str + """ + return str(self.name)[:8] + @property def swr(self): """ diff --git a/city_model_structure/city.py b/city_model_structure/city.py index af213e7c..9085e28f 100644 --- a/city_model_structure/city.py +++ b/city_model_structure/city.py @@ -14,6 +14,7 @@ from pyproj import Transformer from city_model_structure.building import Building from city_model_structure.city_object import CityObject from helpers.geometry_helper import GeometryHelper +from helpers.location import Location import math @@ -32,12 +33,13 @@ class City: # todo: right now extracted at city level, in the future should be extracted also at building level if exist self._location = None self._country_code = None + self._climate_reference_city = None + self._climate_file = None self._latitude = None self._longitude = None self._time_zone = None - @property - def _get_location(self): + def _get_location(self) -> Location: if self._location is None: gps = pyproj.CRS('EPSG:4326') # LatLon with WGS84 datum used by GPS units and Google Earth try: @@ -57,7 +59,7 @@ class City: City country code :return: str """ - return self._get_location[0] + return self._get_location().country @property def name(self): @@ -65,7 +67,23 @@ class City: City name :return: str """ - return self._get_location[1] + return self._get_location().city + + @property + def climate_reference_city(self): + return self._climate_reference_city + + @climate_reference_city.setter + def climate_reference_city(self, value): + self._climate_reference_city = value + + @property + def climate_file(self): + return self._climate_file + + @climate_file.setter + def climate_file(self, value): + self._climate_file = value @property def city_objects(self) -> Union[List[CityObject], None]: diff --git a/exports/exports_factory.py b/exports/exports_factory.py index 8924e6eb..583f7a88 100644 --- a/exports/exports_factory.py +++ b/exports/exports_factory.py @@ -7,6 +7,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 +from exports.formats.simplified_radiosity_algorithm import SimplifiedRadiosityAlgorithm class ExportsFactory: @@ -44,6 +45,14 @@ class ExportsFactory: @property def _obj(self): + """ + Export the city geometry to obj + :return: None + """ + return Obj(self._city, self._path) + + @property + def _grounded_obj(self): """ Export the city geometry to obj :return: None @@ -58,6 +67,10 @@ class ExportsFactory: """ raise NotImplementedError() + @property + def _sra(self): + return SimplifiedRadiosityAlgorithm(self._city, (self._path/ f'{self._city.name}_sra.xml')) + def export(self): """ Export the city model structure to the given export type diff --git a/exports/formats/simplified_radiosity_algorithm.py b/exports/formats/simplified_radiosity_algorithm.py new file mode 100644 index 00000000..a6134494 --- /dev/null +++ b/exports/formats/simplified_radiosity_algorithm.py @@ -0,0 +1,83 @@ +""" +Simplified Radiosity Algorithm +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Guillermo.GutierrezMorote@concordia.ca +""" +from pathlib import Path +import xmltodict + +class SimplifiedRadiosityAlgorithm: + + def __init__(self, city, file_name, begin_month=1, begin_day=1, end_month=12, end_day=31): + self._file_name = file_name + self._begin_month = begin_month + self._begin_day = begin_day + self._end_month = end_month + self._end_day = end_day + self._city = city + self._export() + + def _correct_point(self, point): + # correct the x, y, z values into the reference frame set by city lower_corner + x = point[0] - self._city.lower_corner[0] + y = point[1] - self._city.lower_corner[1] + z = point[2] - self._city.lower_corner[2] + return [x, y, z] + + def _export(self): + print('export') + buildings = [] + for building_index, building in enumerate(self._city.buildings): + building_dict = { + '@Name': f'{building.name}', + '@id': f'{building_index}', + '@key': f'{building.name}', + '@Simulate': 'True' + } + walls, roofs, floors = [], [], [] + for surface in building.surfaces: + surface_dict = { + '@id': f'{surface.id}', + '@ShortWaveReflectance': f'{surface.swr}' + } + for point_index, point in enumerate(surface.perimeter_polygon.points): + # todo: check corrected points + point = self._correct_point(point) + surface_dict[f'V{point_index}'] = { + '@x': f'{point[0]}', + '@y': f'{point[1]}', + '@z': f'{point[2]}' + } + if surface.type == 'Wall': + walls.append(surface_dict) + elif surface.type == 'Roof': + roofs.append(surface_dict) + else: + floors.append(surface_dict) + building_dict['Wall'] = walls + building_dict['Roof'] = roofs + building_dict['Floor'] = floors + buildings.append(building_dict) + sra = { + 'CitySim': { + '@name': f'{self._file_name.name}', + 'Simulation': { + '@beginMonth': f'{self._begin_month}', + '@beginDay': f'{self._begin_day}', + '@endMonth': f'{self._end_month}', + '@endDay': f'{self._end_day}', + }, + 'Climate': { + '@location': f'{self._city.climate_file}', + '@city': f'{self._city.climate_reference_city}' + }, + 'District': { + 'FarFieldObstructions': None, + 'Building': buildings + } + } + } + + with open(self._file_name, "w") as file: + file.write(xmltodict.unparse(sra, pretty=True, short_empty_elements=True)) + return diff --git a/helpers/geometry_helper.py b/helpers/geometry_helper.py index 4362b667..bf808702 100644 --- a/helpers/geometry_helper.py +++ b/helpers/geometry_helper.py @@ -12,6 +12,7 @@ from trimesh import intersections from helpers.configuration_helper import ConfigurationHelper from city_model_structure.attributes.polygon import Polygon from city_model_structure.attributes.polyhedron import Polyhedron +from helpers.location import Location class GeometryHelper: @@ -192,12 +193,12 @@ class GeometryHelper: @staticmethod def get_location(latitude, longitude): url = 'https://nominatim.openstreetmap.org/reverse?lat={latitude}&lon={longitude}&format=json' - resp = requests.get(url.format(latitude=latitude, longitude=longitude)) - if resp.status_code != 200: + response = requests.get(url.format(latitude=latitude, longitude=longitude)) + if response.status_code != 200: # This means something went wrong. - raise Exception('GET /tasks/ {}'.format(resp.status_code)) + raise Exception('GET /tasks/ {}'.format(response.status_code)) else: - response = resp.json() + response = response.json() # todo: this is wrong, remove in the future city = 'new_york_city' country = 'us' @@ -205,7 +206,7 @@ class GeometryHelper: city = response['address']['city'] if 'country_code' in response['address']: country = response['address']['country_code'] - return [country, city] + return Location(country, city) @staticmethod def distance_between_points(vertex1, vertex2): diff --git a/helpers/location.py b/helpers/location.py new file mode 100644 index 00000000..05a426c9 --- /dev/null +++ b/helpers/location.py @@ -0,0 +1,12 @@ +class Location: + def __init__(self, country, city): + self._country = country + self._city = city + + @property + def city(self): + return self._city + + @property + def country(self): + return self._country \ No newline at end of file diff --git a/tests/test_exports.py b/tests/test_exports.py index 96cf12d8..bad8ba7e 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -35,6 +35,9 @@ class TestExports(TestCase): PhysicsFactory('ca', self._city_gml).enrich() UsageFactory('ca', self._city_gml).enrich() SchedulesFactory('comnet', self._city_gml).enrich() + cli = 'C:\\Users\\Pilar\\PycharmProjects\\monthlyenergybalance\\tests_data\\weather\\inseldb_Summerland.cli' + self._city_gml.climate_file = Path(cli) + self._city_gml.climate_reference_city = 'Summerland' for building in self._city_gml.buildings: building.heating['month'] = pd.DataFrame({'INSEL': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]}) building.cooling['month'] = pd.DataFrame({'INSEL': [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0]}) @@ -54,3 +57,6 @@ class TestExports(TestCase): def test_energy_ade_export(self): self._export('energy_ade') + + def test_sra_export(self): + self._export('sra') \ No newline at end of file