""" Geometry helper SPDX - License - Identifier: LGPL - 3.0 - or -later Copyright © 2022 Concordia CERC group Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca """ import math import sys import numpy as np from numpy import ndarray class GeometryHelper: """ Geometry helper """ @staticmethod def to_points_matrix(points): """ Transform a point vector into a point matrix :param points: [x, y, z, x, y, z ...] :return: [[x,y,z],[x,y,z]...] """ rows = points.size // 3 points = points.reshape(rows, 3) return points @staticmethod def gml_surface_to_hub(surface): """ Transform citygml surface names into hub names """ if surface == 'WallSurface': return 'Wall' if surface == 'GroundSurface': return 'Ground' return 'Roof' @staticmethod def points_from_string(coordinates) -> np.ndarray: """ Creates a ndarray from a string :return: [Point] """ points = np.fromstring(coordinates, dtype=float, sep=' ') points = GeometryHelper.to_points_matrix(points) return points @staticmethod def remove_last_point_from_string(points) -> [ndarray]: """ Return the point list without the last element :return [ndarray] """ array = points.split(' ') res = " " return res.join(array[0:len(array) - 3]) @staticmethod def invert_points(points) -> [ndarray]: """ Invert the point list :return: [ndarray] """ res = [] for point in points: res.insert(0, point) return res @staticmethod def ground_area(points): """ Get ground surface area in square meters :return: float """ # New method to calculate area if len(points) < 3: sys.stderr.write('Warning: the area of a line or point cannot be calculated 1. Area = 0\n') return 0 alpha = 0 vec_1 = points[1] - points[0] for i in range(2, len(points)): vec_2 = points[i] - points[0] alpha += GeometryHelper.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') return 0 # horizontal_points = points area = 0 for i in range(0, len(horizontal_points) - 1): point = horizontal_points[i] next_point = horizontal_points[i + 1] area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0]) next_point = horizontal_points[0] point = horizontal_points[len(horizontal_points) - 1] area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0]) _area = abs(area) return _area @staticmethod def angle_between_vectors(vec_1, vec_2): """ angle between vectors in radians :param vec_1: vector :param vec_2: vector :return: float """ if np.linalg.norm(vec_1) == 0 or np.linalg.norm(vec_2) == 0: sys.stderr.write("Warning: impossible to calculate angle between planes' normal. Return 0\n") return 0 cosine = np.dot(vec_1, vec_2) / np.linalg.norm(vec_1) / np.linalg.norm(vec_2) if cosine > 1 and cosine - 1 < 1e-5: cosine = 1 elif cosine < -1 and cosine + 1 > -1e-5: cosine = -1 alpha = math.acos(cosine) return alpha