Code cleaning and adding new functions to save and load a city

This commit is contained in:
Guille 2020-12-02 11:56:33 -05:00
parent d638794dc4
commit 46a88bf890
5 changed files with 43 additions and 60 deletions

View File

@ -6,11 +6,10 @@ Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@conc
import numpy as np
from trimesh import Trimesh
from helpers.geometry_helper import GeometryHelper
from helpers.configuration_helper import ConfigurationHelper
from helpers.configuration_helper import ConfigurationHelper
from city_model_structure.attributes.surface import Surface
class Polyhedron:
"""
Polyhedron class
@ -31,12 +30,13 @@ class Polyhedron:
self._min_z = None
self._min_y = None
self._min_x = None
self._geometry = GeometryHelper()
self._geometry = GeometryHelper(delta=0.0, area_delta=0.5)
def _position_of(self, point):
def _position_of(self, point, face):
vertices = self.vertices
for i in range(len(vertices)):
if self._geometry.almost_equal(vertices[i], point):
# ensure not duplicated vertex
if i not in face and self._geometry.almost_equal(vertices[i], point):
return i
return -1
@ -103,13 +103,14 @@ class Polyhedron:
if len(points) != 3:
sub_surfaces = self._triangulate(surface)
for sub_surface in sub_surfaces:
face = []
points = sub_surface.points
for point in points:
face.append(self._position_of(point))
face.append(self._position_of(point, face))
self._faces.append(face)
else:
for point in points:
face.append(self._position_of(point))
face.append(self._position_of(point, face))
self._faces.append(face)
return self._faces
@ -223,7 +224,6 @@ class Polyhedron:
"""
return [self.max_x - self.min_x, self.max_y - self.min_y, self.max_z - self.min_z]
def stl_export(self, full_path):
"""
Export the polyhedron to stl given file

View File

@ -219,65 +219,41 @@ class Surface:
"""
# New method to calculate area
if self._area is None:
# print('NEW METHOD TO CALCULATE AREA')
# print('original:')
# print(self.points)
if len(self.points) < 3:
area = 0
else:
# 1. 3D -> 2D
z_vector = [0, 0, 1]
normal_vector = self.normal
# print(normal_vector)
turning_base_matrix = None
points_2d = []
x = normal_vector[0]
y = normal_vector[1]
z = normal_vector[2]
# print('x:', x)
# print('y:', y)
# print('z:', z)
if x != 0 or y != 0:
# cos(alpha) = n.z/|n|.|z|
# print('dot_mult:', np.dot(normal_vector, z_vector))
alpha = math.acos(np.dot(normal_vector, z_vector) / np.linalg.norm(normal_vector) / np.linalg.norm(z_vector))
# print('alpha:', alpha)
turning_line = np.cross(normal_vector, z_vector)
# print('turning_line:', turning_line)
third_axis = np.cross(normal_vector, turning_line)
# print('third_axis:', third_axis)
# orthonormal base
w_1 = turning_line / np.linalg.norm(turning_line)
w_2 = normal_vector
w_3 = third_axis / np.linalg.norm(third_axis)
# turning_base_matrix
turning_matrix = np.array([[1, 0, 0],
[0, math.cos(alpha), -math.sin(alpha)],
[0, math.sin(alpha), math.cos(alpha)]])
# print('turning_matrix:', turning_matrix)
[0, math.cos(alpha), -math.sin(alpha)],
[0, math.sin(alpha), math.cos(alpha)]])
base_matrix = np.array([w_1, w_2, w_3])
# print('base_matrix:', base_matrix)
turning_base_matrix = np.matmul(base_matrix.transpose(), turning_matrix.transpose())
turning_base_matrix = np.matmul(turning_base_matrix, base_matrix)
# print('turning_base_matrix:', turning_base_matrix)
if turning_base_matrix is None:
print('ERROR')
print('Error processing turning base matrix')
else:
for point in self.points:
new_point = np.matmul(turning_base_matrix, point)
# print('new_point:', new_point)
points_2d.append(new_point)
# points_2d.append([new_point[0], new_point[1]])
else:
for point in self.points:
points_2d.append([point[0], point[1], 0])
polygon_2d = pn.Polygon(np.array(points_2d))
# print('2D:')
# print(polygon_2d.points)
# 2. calculate area:
area = 0
for i in range(0, len(polygon_2d.points)-1):
point = polygon_2d.points[i]
@ -287,7 +263,6 @@ class Surface:
point = polygon_2d.points[len(polygon_2d.points)-1]
area += (next_point[1] + point[1]) / 2 * (next_point[0] - point[0])
self._area = abs(area)
# print(self._area)
return self._area
def _is_almost_same_terrain(self, terrain_points, ground_points):
@ -487,7 +462,7 @@ class Surface:
return Surface(coordinates, remove_last=False)
except Exception as err:
print('Error', err)
print('Warning: intersecting surfaces', err)
return None
@property

View File

@ -3,7 +3,9 @@ City module
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
from __future__ import annotations
import sys
import pickle
from typing import List, Union
import pyproj
@ -161,3 +163,22 @@ class City:
:return: None
"""
self._name = value
@staticmethod
def load(city_filename) -> City:
"""
Load a city saved with city.save(city_filename)
:param city_filename: city filename
:return: City
"""
with open(city_filename, 'rb') as f:
return pickle.load(f)
def save(self, city_filename):
"""
Save a city into the given filename
:param city_filename: destination city filename
:return:
"""
with open(city_filename, 'wb') as f:
pickle.dump(self, f)

View File

@ -17,8 +17,9 @@ class GeometryHelper:
"""
Geometry helper class
"""
def __init__(self, delta=0.5):
def __init__(self, delta=0.5, area_delta=0.5):
self._delta = delta
self._area_delta = area_delta
@staticmethod
def adjacent_locations(location1, location2):
@ -41,7 +42,7 @@ class GeometryHelper:
:return: Boolean
"""
delta = math.fabs(a1 - a2)
return delta <= self._delta
return delta <= self._area_delta
def almost_equal(self, v1, v2):
"""

View File

@ -4,10 +4,10 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2020 Project Author Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
"""
import os
import pickle
from pathlib import Path
from unittest import TestCase
from city_model_structure.city import City
from factories.geometry_factory import GeometryFactory
@ -23,6 +23,7 @@ class TestGeometryFactory(TestCase):
self._city_gml = None
self._example_path = (Path(__file__).parent.parent / 'tests_data').resolve()
self._pickle_file = (self._example_path / 'city.pickle').resolve()
self._big_pickle_file = (self._example_path / 'big.pickle').resolve()
def _get_citygml(self):
if self._city_gml is None:
@ -55,9 +56,9 @@ class TestGeometryFactory(TestCase):
self.assertIsNotNone(city.upper_corner, 'upper_corner is none')
self.assertIsNotNone(city.name, 'name is none')
self.assertIsNotNone(city.country_code, 'country code is none')
def test_city_serialize(self):
city = self._get_citygml()
city.save(self._pickle_file)
city = City.load(self._pickle_file)
# repeat the city tests
self.assertIsNotNone(city.city_objects, 'city_objects is none')
for building in city.buildings:
self.assertIsNotNone(city.city_object(building.name), 'city_object return none')
@ -66,22 +67,7 @@ class TestGeometryFactory(TestCase):
self.assertIsNotNone(city.upper_corner, 'upper_corner is none')
self.assertIsNotNone(city.name, 'name is none')
self.assertIsNotNone(city.country_code, 'country code is none')
with open(self._pickle_file, 'wb') as f:
pickle.dump(city, f)
def test_city_deserialize(self):
with open(self._pickle_file, 'rb') as f:
city = pickle.load(f)
print(f'city with {len(city.buildings)} buildings')
for building in city.buildings:
self.assertIsNotNone(city.city_object(building.name), 'city_object return none')
print(building.name)
self.assertIsNotNone(city.srs_name, 'srs_name is none')
self.assertIsNotNone(city.lower_corner, 'lower_corner is none')
self.assertIsNotNone(city.upper_corner, 'upper_corner is none')
self.assertIsNotNone(city.name, 'name is none')
self.assertIsNotNone(city.country_code, 'country code is none')
os.remove(self._pickle_file.resolve())
def test_citygml_buildings(self):
"""