Additional changes, the db_control building info need to be changed
This commit is contained in:
parent
f95c45660e
commit
eb246a18e6
|
@ -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
|
|
|
@ -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:
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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.
Loading…
Reference in New Issue
Block a user