diff --git a/city_model_structure/attributes/surface.py b/city_model_structure/attributes/surface.py index 5fefd4ce..88966dbc 100644 --- a/city_model_structure/attributes/surface.py +++ b/city_model_structure/attributes/surface.py @@ -24,8 +24,8 @@ class Surface: self._area_above_ground = None self._area_below_ground = None self._parent = None - self._envelope_lower_corner = None - self._envelope_upper_corner = None + self._bounds_lower_corner = None + self._bounds_upper_corner = None self._shared_surfaces = [] self._global_irradiance = dict() self._perimeter_polygon = perimeter_polygon @@ -91,16 +91,16 @@ class Surface: return min_coordinate @property - def envelope_lower_corner(self): - if self._envelope_lower_corner is None: - self._envelope_lower_corner = [self._min_coord('x'), self._min_coord('y'), self._min_coord('z')] - return self._envelope_lower_corner + def bounds_lower_corner(self): + if self._bounds_lower_corner is None: + self._bounds_lower_corner = [self._min_coord('x'), self._min_coord('y'), self._min_coord('z')] + return self._bounds_lower_corner @property - def envelope_upper_corner(self): - if self._envelope_upper_corner is None: - self._envelope_upper_corner = [self._max_coord('x'), self._max_coord('y'), self._max_coord('z')] - return self._envelope_upper_corner + def bounds_upper_corner(self): + if self._bounds_upper_corner is None: + self._bounds_upper_corner = [self._max_coord('x'), self._max_coord('y'), self._max_coord('z')] + return self._bounds_upper_corner @property def area_above_ground(self): diff --git a/city_model_structure/building.py b/city_model_structure/building.py index 066f628c..23e70403 100644 --- a/city_model_structure/building.py +++ b/city_model_structure/building.py @@ -24,8 +24,8 @@ class Building(CityObject): """ Building(CityObject) class """ - def __init__(self, name, lod, surfaces, terrains, year_of_construction, function, - city_lower_corner, zones_surfaces_ids=[]): + def __init__(self, name, lod, surfaces, year_of_construction, function, + city_lower_corner, terrains=None, zones_surfaces_ids=[]): super().__init__(lod, surfaces, name, city_lower_corner) self._basement_heated = None self._attic_heated = None @@ -57,9 +57,9 @@ class Building(CityObject): for t_zones in self._thermal_zones: t_zones.bounded = [ThermalBoundary(s, [t_zones]) for s in t_zones.surfaces] for surface in self.surfaces: - self._min_x = min(self._min_x, surface.envelope_lower_corner[0]) - self._min_y = min(self._min_y, surface.envelope_lower_corner[1]) - self._min_z = min(self._min_z, surface.envelope_lower_corner[2]) + self._min_x = min(self._min_x, surface.bounds_lower_corner[0]) + self._min_y = min(self._min_y, surface.bounds_lower_corner[1]) + self._min_z = min(self._min_z, surface.bounds_lower_corner[2]) if surface.type == 'Ground': self._grounds.append(surface) elif surface.type == 'Wall': @@ -293,7 +293,7 @@ class Building(CityObject): if self._eave_height is None: self._eave_height = 0 for wall in self.walls: - self._eave_height = max(self._eave_height, wall.envelope_upper_corner[2]) + self._eave_height = max(self._eave_height, wall.bounds_upper_corner[2]) return self._eave_height @property diff --git a/exports/formats/energy_ade.py b/exports/formats/energy_ade.py index 9a92b8d9..0cc94f63 100644 --- a/exports/formats/energy_ade.py +++ b/exports/formats/energy_ade.py @@ -211,10 +211,10 @@ class EnergyAde: 'gml:boundedBy': { 'gml:Envelope': { '@srsName': city.srs_name, - 'gml:lowerCorner': f'{surface.envelope_lower_corner[0]} {surface.envelope_lower_corner[1]}' - f' {surface.envelope_lower_corner[2]}', - 'gml:upperCorner': f'{surface.envelope_upper_corner[0]} {surface.envelope_upper_corner[1]}' - f' {surface.envelope_upper_corner[2]}' + 'gml:lowerCorner': f'{surface.bounds_lower_corner[0]} {surface.bounds_lower_corner[1]}' + f' {surface.bounds_lower_corner[2]}', + 'gml:upperCorner': f'{surface.bounds_upper_corner[0]} {surface.bounds_upper_corner[1]}' + f' {surface.bounds_upper_corner[2]}' } }, 'bldg:lod2MultiSurface': { diff --git a/exports/formats/triangular.py b/exports/formats/triangular.py index 62f307da..9cd48aa8 100644 --- a/exports/formats/triangular.py +++ b/exports/formats/triangular.py @@ -15,7 +15,6 @@ class Triangular: self._write_mode = write_mode self._export() - def _export(self): if self._city.name is None: self._city.name = 'unknown_city' diff --git a/imports/geometry_factory.py b/imports/geometry_factory.py index 7f932613..4b41a285 100644 --- a/imports/geometry_factory.py +++ b/imports/geometry_factory.py @@ -66,7 +66,7 @@ class GeometryFactory: """ just for debug. More information is provided without the lambda parameter """ - return CityGml(self._path).city + return Obj(self._path).city @property def _osm_subway(self): diff --git a/imports/geometry_feeders/citygml.py b/imports/geometry_feeders/citygml.py index d7b7f089..a36eb894 100644 --- a/imports/geometry_feeders/citygml.py +++ b/imports/geometry_feeders/citygml.py @@ -108,8 +108,8 @@ class CityGml: year_of_construction = o['Building']['yearOfConstruction'] if 'function' in o['Building']: function = o['Building']['function'] - self._city.add_city_object(Building(name, lod, surfaces, terrains, year_of_construction, function, - self._lower_corner)) + self._city.add_city_object(Building(name, lod, surfaces, year_of_construction, function, + self._lower_corner, terrains)) return self._city def _terrains(self, city_object, lod_terrain_str): diff --git a/imports/geometry_feeders/obj.py b/imports/geometry_feeders/obj.py index cd2672b7..31b6b5c0 100644 --- a/imports/geometry_feeders/obj.py +++ b/imports/geometry_feeders/obj.py @@ -3,11 +3,12 @@ Obj module parses obj files and import the geometry into the city model structur SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ -import trimesh.exchange.obj +import trimesh.exchange.load +from trimesh import Scene +import trimesh.geometry from city_model_structure.city import City from city_model_structure.building import Building from city_model_structure.attributes.surface import Surface -from helpers.geometry_helper import GeometryHelper from city_model_structure.attributes.polygon import Polygon @@ -18,17 +19,57 @@ class Obj: def __init__(self, path): self._city = None with open(path, 'r') as file: - self._scene = trimesh.exchange.obj.load_obj(file) - # todo: review class trimesh.exchange.load that returns Trimesh or Trimesh.scene + self._scene = trimesh.exchange.load.load(file, file_type='obj', force='scene') + self._corners = self._scene.bounds_corners + _bound_corner_min = None + _bound_corner_max = None + for corner in self._corners: + if _bound_corner_min is None: + _bound_corner_min = corner + elif _bound_corner_max is None: + _bound_corner_max = corner + else: + _bound_corner_min[0] = min(_bound_corner_min[0], corner[0]) + _bound_corner_min[1] = min(_bound_corner_min[1], corner[1]) + _bound_corner_min[2] = min(_bound_corner_min[2], corner[2]) + _bound_corner_max[0] = max(_bound_corner_max[0], corner[0]) + _bound_corner_max[1] = max(_bound_corner_max[1], corner[1]) + _bound_corner_max[2] = max(_bound_corner_max[2], corner[2]) + self._lower_corner = _bound_corner_min + self._upper_corner = _bound_corner_max @property - def scene(self) -> dict: + def scene(self) -> Scene: return self._scene @property - def city(self) -> dict: + def city(self) -> City: if self._city is None: - # todo: refactor this method to clearly choose the gml type - self._city = City(self._lower_corner, self._upper_corner, self._srs_name) + # todo: refactor this method to clearly choose the obj type + # todo: where do we get this information from? + srs_name = 'EPSG:26911' + self._city = City(self._lower_corner, self._upper_corner, srs_name) + scene = self.scene.geometry + keys = scene.keys() + for key in keys: + name = key + # todo: where do we get this information from? + lod = 1 + year_of_construction = 0 + function = '' + + obj = scene[key] + surfaces = [] + for face in obj.faces: + # todo: review for obj with windows + points = [] + for vertex_index in face: + points.append(obj.vertices[vertex_index]) + solid_polygon = Polygon(points) + perimeter_polygon = solid_polygon + surface = Surface(solid_polygon, perimeter_polygon) + surfaces.append(surface) + self._city.add_city_object(Building(name, lod, surfaces, year_of_construction, function, + self._lower_corner)) return self._city diff --git a/imports/physics_feeders/nrel_physics_interface.py b/imports/physics_feeders/nrel_physics_interface.py index f8563ee3..2e207bde 100644 --- a/imports/physics_feeders/nrel_physics_interface.py +++ b/imports/physics_feeders/nrel_physics_interface.py @@ -4,7 +4,6 @@ and enriches the city with archetypes and materials SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2020 Project Author Pilar Monsalvete pilar_monsalvete@yahoo.es """ -import sys import xmltodict from imports.physics_feeders.data_classes.nrel_building_achetype import NrelBuildingArchetype as nba diff --git a/tests/test_exports.py b/tests/test_exports.py index 96cf12d8..77077827 100644 --- a/tests/test_exports.py +++ b/tests/test_exports.py @@ -32,7 +32,11 @@ class TestExports(TestCase): 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 + for building in self._city_gml.buildings: + print(building.thermal_zones) PhysicsFactory('ca', self._city_gml).enrich() + for building in self._city_gml.buildings: + print(building.thermal_zones) UsageFactory('ca', self._city_gml).enrich() SchedulesFactory('comnet', self._city_gml).enrich() for building in self._city_gml.buildings: diff --git a/tests/test_geometry_factory.py b/tests/test_geometry_factory.py index 40d53af9..111b5f96 100644 --- a/tests/test_geometry_factory.py +++ b/tests/test_geometry_factory.py @@ -113,8 +113,8 @@ class TestGeometryFactory(TestCase): self.assertIsNotNone(surface.area_above_ground, 'surface area_above_ground is none') self.assertIsNotNone(surface.global_irradiance, 'monthly irradiance is none') self.assertIsNone(surface.swr, 'surface swr is not none') - self.assertIsNotNone(surface.envelope_lower_corner, 'surface envelope_lower_corner is none') - self.assertIsNotNone(surface.envelope_upper_corner, 'surface envelope_upper_corner is none') + self.assertIsNotNone(surface.bounds_lower_corner, 'surface envelope_lower_corner is none') + self.assertIsNotNone(surface.bounds_upper_corner, 'surface envelope_upper_corner is none') self.assertIsNotNone(surface.area_above_ground, 'surface area_above_ground is none') self.assertIsNotNone(surface.perimeter_polygon, 'surface perimeter_polygon is none') self.assertIsNone(surface.holes_polygons, 'surface hole_polygons is not none') diff --git a/tests/test_imports.py b/tests/test_imports.py index 1c9c22e2..60432cca 100644 --- a/tests/test_imports.py +++ b/tests/test_imports.py @@ -10,8 +10,6 @@ from imports.geometry_factory import GeometryFactory class MyTestCase(TestCase): - def test_something(self): - self.assertEqual(True, False) """ TestImports """ @@ -20,14 +18,18 @@ class MyTestCase(TestCase): Test setup :return: None """ - self._city_gml = None + self._city_obj = None self._example_path = (Path(__file__).parent / 'tests_data').resolve() self._output_path = (Path(__file__).parent / 'tests_outputs').resolve() def _get_city(self): - if self._city_gml is None: + if self._city_obj is None: file_path = (self._example_path / 'kelowna.obj').resolve() - scene = GeometryFactory('obj', file_path)._scene_debug - self._city_gml = scene.city - return self._city_gml + self._city_obj = GeometryFactory('obj', file_path)._city_debug + return self._city_obj + def test_import_obj(self): + city = self._get_city() + self.assertIsNotNone(city, 'city is none') + for building in city.buildings: + self.assertIsNotNone(building, 'building is none')