From 6b7dac81237b075f29bf43fa97bb769b167afb5e Mon Sep 17 00:00:00 2001 From: Pilar Date: Tue, 22 Jun 2021 13:16:17 -0400 Subject: [PATCH] added class Point and used in polygon.py --- city_model_structure/attributes/point.py | 32 ++++ city_model_structure/attributes/polygon.py | 144 ++++++++++++++++-- city_model_structure/attributes/polyhedron.py | 18 +-- city_model_structure/attributes/surface.py | 4 +- city_model_structure/city_object.py | 2 +- exports/formats/energy_ade.py | 8 +- exports/formats/idf.py | 4 +- .../formats/simplified_radiosity_algorithm.py | 2 +- .../helpers/construction_helper.py | 6 +- 9 files changed, 182 insertions(+), 38 deletions(-) create mode 100644 city_model_structure/attributes/point.py diff --git a/city_model_structure/attributes/point.py b/city_model_structure/attributes/point.py new file mode 100644 index 00000000..646f8065 --- /dev/null +++ b/city_model_structure/attributes/point.py @@ -0,0 +1,32 @@ +""" +Point module +SPDX - License - Identifier: LGPL - 3.0 - or -later +Copyright © 2020 Project Author Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca +""" + +import math + + +class Point: + """ + Point class + """ + + def __init__(self, coordinates): + self._coordinates = coordinates + + @property + def coordinates(self): + return self._coordinates + + def distance_to_point(self, other_point): + """ + distance between points in an n-D Euclidean space + :param other_point: point or vertex + :return: float + """ + power = 0 + for dimension in range(0, len(self.coordinates)): + power += math.pow(other_point.coordinates[dimension]-self.coordinates[dimension], 2) + distance = math.sqrt(power) + return distance diff --git a/city_model_structure/attributes/polygon.py b/city_model_structure/attributes/polygon.py index ac2d7217..c9082e75 100644 --- a/city_model_structure/attributes/polygon.py +++ b/city_model_structure/attributes/polygon.py @@ -9,6 +9,7 @@ from typing import List import sys import numpy as np import math +from city_model_structure.attributes.point import Point class Polygon: @@ -16,20 +17,59 @@ class Polygon: Polygon class """ - def __init__(self, points): +# def __init__(self, points): + def __init__(self, coordinates): + self._area = None - self._points = points +# self._points = points + self._points = None self._points_list = None self._normal = None + self._inverse = None + self._edges = None +# self._coordinates = None + self._coordinates = coordinates +# @property +# def points(self) -> List[Point]: +# """ +# List of points belonging to the polygon [[x, y, z],...] +# :return: List[Point] +# """ +# return self._points +# +# @property +# def coordinates(self) -> List[np.ndarray]: +# """ +# List of points in the shape of its coordinates belonging to the polygon [[x, y, z],...] +# :return: np.array +# """ +# if self._coordinates is None: +# self._coordinates = [] +# for point in self.points: +# self._coordinates.append(np.array(point.coordinates)) +# return self._coordinates +# @property - def points(self) -> np.ndarray: + def points(self) -> List[Point]: """ List of points belonging to the polygon [[x, y, z],...] - :return: ndarray[coordinates] + :return: List[Point] """ + if self._points is None: + self._points = [] + for coordinate in self.coordinates: + self._points.append(Point(coordinate)) return self._points + @property + def coordinates(self) -> List[np.ndarray]: + """ + List of points in the shape of its coordinates belonging to the polygon [[x, y, z],...] + :return: np.array + """ + return self._coordinates + @property def points_list(self) -> np.ndarray: """ @@ -37,10 +77,21 @@ class Polygon: :return: np.ndarray """ if self._points_list is None: - s = self.points + s = self.coordinates self._points_list = np.reshape(s, len(s) * 3) return self._points_list + @property + def edges(self): + if self._edges is None: + self._edges = [] + for i in range(0, len(self.points)-1): + point_1 = self.points[i] + point_2 = self.points[i+1] + self._edges.append([point_1, point_2]) + self._edges.append([self.points[len(self.points)-1], self.points[0]]) + return self._edges + @property def area(self): """ @@ -53,9 +104,9 @@ class Polygon: sys.stderr.write('Warning: the area of a line or point cannot be calculated 1. Area = 0\n') return 0 alpha = 0 - vec_1 = self.points[1] - self.points[0] + vec_1 = self.points[1].coordinates - self.points[0].coordinates for i in range(2, len(self.points)): - vec_2 = self.points[i] - self.points[0] + vec_2 = self.points[i].coordinates - self.points[0].coordinates alpha += self._angle_between_vectors(vec_1, vec_2) if alpha == 0: sys.stderr.write('Warning: the area of a line or point cannot be calculated 2. Area = 0\n') @@ -87,7 +138,7 @@ class Polygon: if x == 0 and y == 0: # Already horizontal for point in self.points: - horizontal_points.append([point[0], point[1], 0]) + horizontal_points.append([point.coordinates[0], point.coordinates[1], 0]) else: alpha = self._angle_between_vectors(normal_vector, z_vector) rotation_line = np.cross(normal_vector, z_vector) @@ -106,7 +157,7 @@ class Polygon: sys.stderr.write('Warning: rotation base matrix returned None\n') else: for point in self.points: - new_point = np.matmul(rotation_base_matrix, point) + new_point = np.matmul(rotation_base_matrix, point.coordinates) horizontal_points.append(new_point) return horizontal_points @@ -117,7 +168,7 @@ class Polygon: :return: np.ndarray """ if self._normal is None: - points = self.points + points = self.coordinates # todo: IF THE FIRST ONE IS 0, START WITH THE NEXT point_origin = points[len(points)-2] vector_1 = points[len(points)-1] - point_origin @@ -200,7 +251,7 @@ class Polygon: ear = self._triangle(points_list, total_points_list, concave_points[i]) rest_points = [] for p in total_points_list: - rest_points.append(list(self.points[p])) + rest_points.append(list(self.coordinates[p])) if self._is_ear(ear, rest_points): ears.append(ear) point_to_remove = concave_points[i] @@ -357,19 +408,19 @@ class Polygon: area_points = 0 point_is_not_vertex = True for i in range(0, 3): - if abs(np.linalg.norm(point) - np.linalg.norm(ear.points[i])) < 0.0001: + if abs(np.linalg.norm(point) - np.linalg.norm(ear.coordinates[i])) < 0.0001: point_is_not_vertex = False break if point_is_not_vertex: for i in range(0, 3): if i != 2: - new_points = ear.points[i][:] - new_points = np.append(new_points, ear.points[i + 1][:]) + new_points = ear.coordinates[i][:] + new_points = np.append(new_points, ear.coordinates[i + 1][:]) new_points = np.append(new_points, point[:]) else: - new_points = ear.points[i][:] + new_points = ear.coordinates[i][:] new_points = np.append(new_points, point[:]) - new_points = np.append(new_points, ear.points[0][:]) + new_points = np.append(new_points, ear.coordinates[0][:]) rows = new_points.size // 3 new_points = new_points.reshape(rows, 3) new_triangle = Polygon(new_points) @@ -460,3 +511,64 @@ class Polygon: cosine = -1 alpha = math.acos(cosine) return alpha + + @property + def inverse(self): + if self._inverse is None: + self._inverse = self.points[::-1] + return self._inverse + +# def divide(self, polygon): + +# return polygon_1, polygon_2, intersection + + def reshape(self, triangles) -> Polygon: + edges_list = [] + for i in range(0, len(triangles)): + for edge in triangles[i].edges: + print('edge') + print(edge[0].coordinates, edge[1].coordinates) + if not self._edge_in_edges_list(edge, edges_list): + edges_list.append(edge) + print('list') + for e in edges_list: + print(e[0].coordinates, e[1].coordinates) + else: + print('remove') + edges_list = self._remove_from_list(edge, edges_list) + for e in edges_list: + print(e[0].coordinates, e[1].coordinates) + points = self._order_points(edges_list) + return Polygon(points) + + @staticmethod + def _edge_in_edges_list(edge, edges_list): + for ed in edges_list: + if (ed[0].distance_to_point(edge[0]) == 0 and ed[1].distance_to_point(edge[1]) == 0) or\ + (ed[1].distance_to_point(edge[0]) == 0 and ed[0].distance_to_point(edge[1]) == 0): + return True + return False + + @staticmethod + def _order_points(edges_list): + points = edges_list[0] + for i in range(1, len(edges_list)): + point_1 = edges_list[i][0] + point_2 = points[len(points)-1] + if point_1.distance_to_point(point_2) == 0: + points.append(edges_list[i][1]) + points.remove(points[len(points)-1]) + array_points = [] + for point in points: + print(point.coordinates) + array_points.append(point.coordinates) + return np.array(array_points) + + @staticmethod + def _remove_from_list(edge, edges_list): + new_list = [] + for ed in edges_list: + if not((ed[0].distance_to_point(edge[0]) == 0 and ed[1].distance_to_point(edge[1]) == 0) or + (ed[1].distance_to_point(edge[0]) == 0 and ed[0].distance_to_point(edge[1]) == 0)): + new_list.append(ed) + return new_list diff --git a/city_model_structure/attributes/polyhedron.py b/city_model_structure/attributes/polyhedron.py index 82a19fb0..ab538331 100644 --- a/city_model_structure/attributes/polyhedron.py +++ b/city_model_structure/attributes/polyhedron.py @@ -59,7 +59,7 @@ class Polyhedron: """ if self._vertices is None: vertices, self._vertices = [], [] - _ = [vertices.extend(s.points) for s in self._polygons] + _ = [vertices.extend(s.coordinates) for s in self._polygons] for vertex_1 in vertices: found = False for vertex_2 in self._vertices: @@ -88,14 +88,14 @@ class Polyhedron: for polygon in self._polygons: face = [] - points = polygon.points + points = polygon.coordinates if len(points) != 3: sub_polygons = polygon.triangulate() # todo: I modified this! To be checked @Guille if len(sub_polygons) >= 1: for sub_polygon in sub_polygons: face = [] - points = sub_polygon.points + points = sub_polygon.coordinates for point in points: face.append(self._position_of(point, face)) self._faces.append(face) @@ -143,7 +143,7 @@ class Polyhedron: if self._max_z is None: self._max_z = ConfigurationHelper().min_coordinate for polygon in self._polygons: - for point in polygon.points: + for point in polygon.coordinates: self._max_z = max(self._max_z, point[2]) return self._max_z @@ -156,7 +156,7 @@ class Polyhedron: if self._max_y is None: self._max_y = ConfigurationHelper().min_coordinate for polygon in self._polygons: - for point in polygon.points: + for point in polygon.coordinates: if self._max_y < point[1]: self._max_y = point[1] return self._max_y @@ -170,7 +170,7 @@ class Polyhedron: if self._max_x is None: self._max_x = ConfigurationHelper().min_coordinate for polygon in self._polygons: - for point in polygon.points: + for point in polygon.coordinates: self._max_x = max(self._max_x, point[0]) return self._max_x @@ -183,7 +183,7 @@ class Polyhedron: if self._min_z is None: self._min_z = self.max_z for polygon in self._polygons: - for point in polygon.points: + for point in polygon.coordinates: if self._min_z > point[2]: self._min_z = point[2] return self._min_z @@ -197,7 +197,7 @@ class Polyhedron: if self._min_y is None: self._min_y = self.max_y for polygon in self._polygons: - for point in polygon.points: + for point in polygon.coordinates: if self._min_y > point[1]: self._min_y = point[1] return self._min_y @@ -211,7 +211,7 @@ class Polyhedron: if self._min_x is None: self._min_x = self.max_x for polygon in self._polygons: - for point in polygon.points: + for point in polygon.coordinates: if self._min_x > point[0]: self._min_x = point[0] return self._min_x diff --git a/city_model_structure/attributes/surface.py b/city_model_structure/attributes/surface.py index 7e886349..ab591249 100644 --- a/city_model_structure/attributes/surface.py +++ b/city_model_structure/attributes/surface.py @@ -88,7 +88,7 @@ class Surface: else: axis = 2 max_coordinate = '' - for point in self.perimeter_polygon.points: + for point in self.perimeter_polygon.coordinates: if max_coordinate == '': max_coordinate = point[axis] elif max_coordinate < point[axis]: @@ -103,7 +103,7 @@ class Surface: else: axis = 2 min_coordinate = '' - for point in self.perimeter_polygon.points: + for point in self.perimeter_polygon.coordinates: if min_coordinate == '': min_coordinate = point[axis] elif min_coordinate > point[axis]: diff --git a/city_model_structure/city_object.py b/city_model_structure/city_object.py index 2b6bb3c2..6009a5c8 100644 --- a/city_model_structure/city_object.py +++ b/city_model_structure/city_object.py @@ -58,7 +58,7 @@ class CityObject: City object volume in cubic meters :return: float """ - return self.detailed_polyhedron.volume + return self.simplified_polyhedron.volume @property def detailed_polyhedron(self) -> Polyhedron: diff --git a/exports/formats/energy_ade.py b/exports/formats/energy_ade.py index fcca7f4a..eb198d53 100644 --- a/exports/formats/energy_ade.py +++ b/exports/formats/energy_ade.py @@ -231,9 +231,9 @@ class EnergyAde: '@gml:id': f'PolyId{surface.name}_0', 'gml:posList': { '@srsDimension': '3', - '@count': len(surface.solid_polygon.points) + 1, + '@count': len(surface.solid_polygon.coordinates) + 1, '#text': f'{" ".join(map(str, surface.solid_polygon.points_list))} ' - f'{" ".join(map(str, surface.solid_polygon.points[0]))}' + f'{" ".join(map(str, surface.solid_polygon.coordinates[0]))}' } } } @@ -343,9 +343,9 @@ class EnergyAde: '@gml:id': f'GML_{uuid.uuid4()}', 'gml:posList': { '@srsDimension': '3', - '@count': len(thermal_boundary.surface.solid_polygon.points) + 1, + '@count': len(thermal_boundary.surface.solid_polygon.coordinates) + 1, '#text': f'{" ".join(map(str, thermal_boundary.surface.solid_polygon.points_list))} ' - f'{" ".join(map(str, thermal_boundary.surface.solid_polygon.points[0]))}' + f'{" ".join(map(str, thermal_boundary.surface.solid_polygon.coordinates[0]))}' } } } diff --git a/exports/formats/idf.py b/exports/formats/idf.py index 81b46ee8..309a24d4 100644 --- a/exports/formats/idf.py +++ b/exports/formats/idf.py @@ -145,7 +145,7 @@ class Idf: return points_list def add_block(self, building): - _points = IdfHelper._matrix_to_2d_list(building.foot_print.points) + _points = IdfHelper._matrix_to_2d_list(building.foot_print.coordinates) self._idf.add_block(name=building.name, coordinates=_points, height=building.max_height, num_stories=int(building.storeys_above_ground)) self._add_heating_system(building) @@ -162,7 +162,7 @@ class Idf: wall = self._idf.newidfobject(self._SURFACE, Name=f'{building.name}-{boundary.surface.name}', Surface_Type=idf_surface, Zone_Name=zone_name, Construction_Name=boundary.construction_name) - coordinates = IdfHelper._matrix_to_list(boundary.surface.points) + coordinates = IdfHelper._matrix_to_list(boundary.surface.coordinates) wall.setcoords(coordinates) index += 1 self._add_heating_system(building) diff --git a/exports/formats/simplified_radiosity_algorithm.py b/exports/formats/simplified_radiosity_algorithm.py index 6c241e06..7ccf96ad 100644 --- a/exports/formats/simplified_radiosity_algorithm.py +++ b/exports/formats/simplified_radiosity_algorithm.py @@ -48,7 +48,7 @@ class SimplifiedRadiosityAlgorithm: '@id': f'{surface.id}', '@ShortWaveReflectance': f'{surface.swr}' } - for point_index, point in enumerate(surface.perimeter_polygon.points): + for point_index, point in enumerate(surface.perimeter_polygon.coordinates): point = self._correct_point(point) surface_dict[f'V{point_index}'] = { '@x': f'{point[0]}', diff --git a/imports/construction/helpers/construction_helper.py b/imports/construction/helpers/construction_helper.py index 0a3c1f50..2b6042fb 100644 --- a/imports/construction/helpers/construction_helper.py +++ b/imports/construction/helpers/construction_helper.py @@ -77,9 +77,9 @@ class ConstructionHelper: nrcan_function_default_value = 'residential' nrcan_window_types = [cte.WINDOW] nrcan_construction_types = { - cte.WALL: 'exterior wall', - cte.GROUND_WALL: 'ground wall', - cte.GROUND: 'exterior slab', + cte.WALL: 'wall', + cte.GROUND_WALL: 'basement_wall', + cte.GROUND: 'floor', cte.ATTIC_FLOOR: 'attic floor', cte.INTERIOR_SLAB: 'interior slab', cte.ROOF: 'roof'