Code cleaning and adding new functions to save and load a city
This commit is contained in:
parent
d638794dc4
commit
46a88bf890
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
|
@ -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):
|
||||
"""
|
||||
|
|
Loading…
Reference in New Issue
Block a user