Additional changes, the db_control building info need to be changed

This commit is contained in:
Guille Gutierrez 2023-07-28 14:55:06 -04:00
parent f95c45660e
commit eb246a18e6
9 changed files with 46 additions and 197 deletions

View File

@ -1,144 +0,0 @@
"""
Rhino module parses rhino files and import the geometry into the city model structure
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.capip
"""
import numpy as np
from rhino3dm import *
from rhino3dm._rhino3dm import Extrusion, MeshType, File3dm
from hub.city_model_structure.attributes.point import Point
from hub.city_model_structure.attributes.polygon import Polygon
from hub.city_model_structure.building import Building
from hub.city_model_structure.building_demand.surface import Surface as HubSurface
from hub.city_model_structure.city import City
from hub.helpers.configuration_helper import ConfigurationHelper
from hub.imports.geometry.helpers.geometry_helper import GeometryHelper
class Rhino:
"""
Rhino class
"""
def __init__(self, path):
self._model = File3dm.Read(str(path))
max_float = float(ConfigurationHelper().max_coordinate)
min_float = float(ConfigurationHelper().min_coordinate)
self._min_x = self._min_y = self._min_z = max_float
self._max_x = self._max_y = self._max_z = min_float
@staticmethod
def _in_perimeter(wall, corner):
res = wall.contains_point(Point(corner))
return res
@staticmethod
def _add_hole(solid_polygon, hole):
first = solid_polygon.points[0]
points = first + hole.points + solid_polygon.points
return Polygon(points)
@staticmethod
def _solid_points(coordinates) -> np.ndarray:
solid_points = np.fromstring(coordinates, dtype=float, sep=' ')
solid_points = GeometryHelper.to_points_matrix(solid_points)
result = []
found = False
for row in solid_points:
for row2 in result:
if row[0] == row2[0] and row[1] == row2[1] and row[2] == row2[2]:
found = True
if not found:
result.append(row)
return solid_points
def _corners(self, point):
if point.X < self._min_x:
self._min_x = point.X
if point.Y < self._min_y:
self._min_y = point.Y
if point.Z < self._min_z:
self._min_z = point.Z
if point.X > self._max_x:
self._max_x = point.X
if point.Y > self._max_y:
self._max_y = point.Y
if point.Z > self._max_z:
self._max_z = point.Z
def _add_face(self, face):
hub_surfaces = []
_mesh = face.GetMesh(MeshType.Default)
for i in range(0, len(_mesh.Faces)):
mesh_faces = _mesh.Faces[i]
_points = ''
faces = []
for index in mesh_faces:
if index in faces:
continue
faces.append(index)
self._corners(_mesh.Vertices[index])
_points = _points + f'{_mesh.Vertices[index].X} {_mesh.Vertices[index].Y} {_mesh.Vertices[index].Z} '
polygon_points = Rhino._solid_points(_points.strip())
hub_surfaces.append(HubSurface(Polygon(polygon_points), Polygon(polygon_points)))
return hub_surfaces
@property
def city(self) -> City:
"""
Return a city based in the rhino file
:return: City
"""
buildings = []
city_objects = [] # building and "windows"
windows = []
_prev_name = ''
for obj in self._model.Objects:
name = obj.Attributes.Id
hub_surfaces = []
if isinstance(obj.Geometry, Extrusion):
surface = obj.Geometry
hub_surfaces = hub_surfaces + self._add_face(surface)
else:
for face in obj.Geometry.Faces:
if face is None:
break
hub_surfaces = hub_surfaces + self._add_face(face)
building = Building(name, hub_surfaces, 'unknown', 'unknown', [])
city_objects.append(building)
lower_corner = (self._min_x, self._min_y, self._min_z)
upper_corner = (self._max_x, self._max_y, self._max_z)
city = City(lower_corner, upper_corner, 'EPSG:26918')
for building in city_objects:
if len(building.surfaces) <= 2:
# is not a building but a window!
for surface in building.surfaces:
# add to windows the "hole" with the normal inverted
windows.append(Polygon(surface.perimeter_polygon.inverse))
else:
buildings.append(building)
# todo: this method will be pretty inefficient
for hole in windows:
corner = hole.coordinates[0]
for building in buildings:
for surface in building.surfaces:
plane = surface.perimeter_polygon.plane
# todo: this is a hack for dompark project it should not be done this way windows should be correctly modeled
# if the distance between the wall plane and the window is less than 2m
# and the window Z coordinate it's between the wall Z, it's a window of that wall
if plane.distance_to_point(corner) <= 2:
# check if the window is in the right high.
if surface.upper_corner[2] >= corner[2] >= surface.lower_corner[2]:
if surface.holes_polygons is None:
surface.holes_polygons = []
surface.holes_polygons.append(hole)
for building in buildings:
city.add_city_object(building)
building.level_of_detail.geometry = 3
city.level_of_detail.geometry = 3
return city

View File

@ -12,7 +12,6 @@ from hub.imports.geometry.citygml import CityGml
from hub.imports.geometry.geojson import Geojson from hub.imports.geometry.geojson import Geojson
from hub.imports.geometry.gpandas import GPandas from hub.imports.geometry.gpandas import GPandas
from hub.imports.geometry.obj import Obj from hub.imports.geometry.obj import Obj
from hub.imports.geometry.rhino import Rhino
class GeometryFactory: class GeometryFactory:
@ -80,14 +79,6 @@ class GeometryFactory:
self._function_field, self._function_field,
self._function_to_hub).city self._function_to_hub).city
@property
def _rhino(self) -> City:
"""
Enrich the city by using Rhino information as data source
:return: City
"""
return Rhino(self._path).city
@property @property
def city(self) -> City: def city(self) -> City:
""" """

View File

@ -74,19 +74,31 @@ class DBControl:
""" """
return self._city_object.get_by_name_or_alias_and_city(name, city_id) return self._city_object.get_by_name_or_alias_and_city(name, city_id)
def results(self, user_id, application_id, cities, result_names=None) -> Dict: def buildings_info(self, request_values, city_id) -> [CityObject]:
"""
Retrieve the building info from the database
:param request_values: Building names
:param city_id: City ID
:return: [CityObject]
"""
buildings = []
for name in request_values['names']:
buildings.append(self._city_object.get_by_name_or_alias_and_city(name, city_id))
return buildings
def results(self, user_id, application_id, request_values, result_names=None) -> Dict:
""" """
Retrieve the simulation results for the given cities from the database Retrieve the simulation results for the given cities from the database
:param user_id: the user id owning the results :param user_id: the user id owning the results
:param application_id: the application id owning the results :param application_id: the application id owning the results
:param cities: dictionary containing the city and building names for the results :param request_values: dictionary containing the scenario and building names to grab the results
:param result_names: if given, filter the results to the selected names :param result_names: if given, filter the results to the selected names
""" """
if result_names is None: if result_names is None:
result_names = [] result_names = []
results = {} results = {}
for city in cities['cities']: for scenario in request_values['scenarios']:
scenario_name = next(iter(city)) scenario_name = next(iter(scenario))
result_sets = self._city_repository.get_by_user_id_application_id_and_scenario( result_sets = self._city_repository.get_by_user_id_application_id_and_scenario(
user_id, user_id,
application_id, application_id,
@ -96,14 +108,13 @@ class DBControl:
continue continue
for result_set in result_sets: for result_set in result_sets:
city_id = result_set[0].id city_id = result_set[0].id
print('city ids', city_id)
results[scenario_name] = [] results[scenario_name] = []
for building_name in city[scenario_name]: for building_name in scenario[scenario_name]:
_building = self._city_object.get_by_name_or_alias_and_city(building_name, city_id) _building = self._city_object.get_by_name_or_alias_and_city(building_name, city_id)
if _building is None: if _building is None:
continue continue
city_object_id = _building.id city_object_id = _building.id
print('city object ids', city_object_id)
_ = self._simulation_results.get_simulation_results_by_city_id_city_object_id_and_names( _ = self._simulation_results.get_simulation_results_by_city_id_city_object_id_and_names(
city_id, city_id,
city_object_id, city_object_id,

View File

@ -104,15 +104,17 @@ class CityObject(Repository):
:return: [CityObject] with the provided name or alias belonging to the city with id city_id :return: [CityObject] with the provided name or alias belonging to the city with id city_id
""" """
try: try:
_city_objects = self.session.execute(select(Model).where( # search by name first
or_(Model.name == name, Model.aliases.contains(f'{name}')), Model.city_id == city_id city_object = self.session.execute(select(Model).where(Model.name == name, Model.city_id == city_id)).first()
)).all() if city_object is not None:
for city_object in _city_objects: return city_object[0]
if city_object[0].name == name: city_objects = self.session.execute(
return city_object[0] select(Model).where(Model.aliases.contains(name), Model.city_id == city_id)
).all()
# name not found, so search by alias instead
for city_object in city_objects:
aliases = city_object[0].aliases.replace('{', '').replace('}', '').split(',') aliases = city_object[0].aliases.replace('{', '').replace('}', '').split(',')
for alias in aliases: for alias in aliases:
print(alias, name)
if alias == name: if alias == name:
# force the name as the alias # force the name as the alias
city_object[0].name = name city_object[0].name = name

View File

@ -12,7 +12,6 @@ openpyxl
networkx networkx
parseidf==1.0.0 parseidf==1.0.0
ply ply
rhino3dm==7.7.0
scipy scipy
PyYAML PyYAML
pyecore==0.12.2 pyecore==0.12.2

View File

@ -4,35 +4,34 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Peter Yefi peteryefi@gmail.com Project Coder Peter Yefi peteryefi@gmail.com
""" """
import distutils.spawn
import glob import glob
import json import json
import logging import logging
import os import os
import subprocess import subprocess
import unittest import unittest
from unittest import TestCase
from pathlib import Path from pathlib import Path
import sqlalchemy.exc from unittest import TestCase
from hub.helpers.data.montreal_function_to_hub_function import MontrealFunctionToHubFunction import sqlalchemy.exc
from hub.imports.geometry_factory import GeometryFactory from sqlalchemy import create_engine
from hub.imports.construction_factory import ConstructionFactory from sqlalchemy.exc import ProgrammingError
from hub.imports.usage_factory import UsageFactory
from hub.imports.results_factory import ResultFactory import hub.helpers.constants as cte
from hub.imports.weather_factory import WeatherFactory
from hub.imports.energy_systems_factory import EnergySystemsFactory
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
from hub.exports.exports_factory import ExportsFactory from hub.exports.exports_factory import ExportsFactory
from hub.helpers.data.montreal_function_to_hub_function import MontrealFunctionToHubFunction
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.energy_systems_factory import EnergySystemsFactory
from hub.imports.geometry_factory import GeometryFactory
from hub.imports.results_factory import ResultFactory
from hub.imports.usage_factory import UsageFactory
from hub.imports.weather_factory import WeatherFactory
from hub.persistence.db_control import DBControl from hub.persistence.db_control import DBControl
from hub.persistence.repository import Repository
from sqlalchemy import create_engine
from hub.persistence.models import City, Application, CityObject, SimulationResults from hub.persistence.models import City, Application, CityObject, SimulationResults
from hub.persistence.models import User, UserRoles from hub.persistence.models import User, UserRoles
from hub.helpers.dictionaries import Dictionaries from hub.persistence.repository import Repository
from sqlalchemy.exc import ProgrammingError
import uuid
import hub.helpers.constants as cte
import distutils.spawn
class Control: class Control:
@ -65,13 +64,13 @@ class Control:
self._skip_test = True self._skip_test = True
self._skip_reason = f'{operational_error}' self._skip_reason = f'{operational_error}'
return return
"""
Application.__table__.create(bind=repository.engine, checkfirst=True) Application.__table__.create(bind=repository.engine, checkfirst=True)
User.__table__.create(bind=repository.engine, checkfirst=True) User.__table__.create(bind=repository.engine, checkfirst=True)
City.__table__.create(bind=repository.engine, checkfirst=True) City.__table__.create(bind=repository.engine, checkfirst=True)
CityObject.__table__.create(bind=repository.engine, checkfirst=True) CityObject.__table__.create(bind=repository.engine, checkfirst=True)
SimulationResults.__table__.create(bind=repository.engine, checkfirst=True) SimulationResults.__table__.create(bind=repository.engine, checkfirst=True)
"""
city_file = Path('tests_data/test.geojson').resolve() city_file = Path('tests_data/test.geojson').resolve()
output_path = Path('tests_outputs/').resolve() output_path = Path('tests_outputs/').resolve()
self._city = GeometryFactory('geojson', self._city = GeometryFactory('geojson',
@ -81,6 +80,7 @@ class Control:
aliases_field=['ID_UEV', 'CIVIQUE_DE', 'NOM_RUE'], aliases_field=['ID_UEV', 'CIVIQUE_DE', 'NOM_RUE'],
function_field='CODE_UTILI', function_field='CODE_UTILI',
function_to_hub=MontrealFunctionToHubFunction().dictionary).city function_to_hub=MontrealFunctionToHubFunction().dictionary).city
ConstructionFactory('nrcan', self._city).enrich() ConstructionFactory('nrcan', self._city).enrich()
UsageFactory('nrcan', self._city).enrich() UsageFactory('nrcan', self._city).enrich()
WeatherFactory('epw', self._city).enrich() WeatherFactory('epw', self._city).enrich()
@ -107,14 +107,13 @@ class Control:
self._application_id = 1 self._application_id = 1
self._user_id = 1 self._user_id = 1
"""
self._application_id = self._database.persist_application( self._application_id = self._database.persist_application(
'City_layers', 'City_layers',
'City layers test user', 'City layers test user',
self.application_uuid self.application_uuid
) )
self._user_id = self._database.create_user('city_layers', self._application_id, 'city_layers', UserRoles.Admin) self._user_id = self._database.create_user('city_layers', self._application_id, 'city_layers', UserRoles.Admin)
"""
self._pickle_path = 'tests_data/pickle_path.bz2' self._pickle_path = 'tests_data/pickle_path.bz2'
@property @property

View File

@ -110,15 +110,6 @@ class TestGeometryFactory(TestCase):
self._check_surfaces(building) self._check_surfaces(building)
city = ConstructionFactory('nrel', city).enrich() city = ConstructionFactory('nrel', city).enrich()
def test_import_rhino(self):
"""
Test rhino import
"""
file = 'dompark.3dm'
city = self._get_city(file, 'rhino')
self.assertIsNotNone(city, 'city is none')
self.assertTrue(len(city.buildings) == 36)
def test_import_obj(self): def test_import_obj(self):
""" """
Test obj import Test obj import

Binary file not shown.

Binary file not shown.