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.gpandas import GPandas
|
||||
from hub.imports.geometry.obj import Obj
|
||||
from hub.imports.geometry.rhino import Rhino
|
||||
|
||||
|
||||
class GeometryFactory:
|
||||
@ -80,14 +79,6 @@ class GeometryFactory:
|
||||
self._function_field,
|
||||
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
|
||||
def city(self) -> City:
|
||||
"""
|
||||
|
@ -74,19 +74,31 @@ class DBControl:
|
||||
"""
|
||||
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
|
||||
:param user_id: the user 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
|
||||
"""
|
||||
if result_names is None:
|
||||
result_names = []
|
||||
results = {}
|
||||
for city in cities['cities']:
|
||||
scenario_name = next(iter(city))
|
||||
for scenario in request_values['scenarios']:
|
||||
scenario_name = next(iter(scenario))
|
||||
result_sets = self._city_repository.get_by_user_id_application_id_and_scenario(
|
||||
user_id,
|
||||
application_id,
|
||||
@ -96,14 +108,13 @@ class DBControl:
|
||||
continue
|
||||
for result_set in result_sets:
|
||||
city_id = result_set[0].id
|
||||
print('city ids', city_id)
|
||||
|
||||
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)
|
||||
if _building is None:
|
||||
continue
|
||||
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(
|
||||
city_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
|
||||
"""
|
||||
try:
|
||||
_city_objects = self.session.execute(select(Model).where(
|
||||
or_(Model.name == name, Model.aliases.contains(f'{name}')), Model.city_id == city_id
|
||||
)).all()
|
||||
for city_object in _city_objects:
|
||||
if city_object[0].name == name:
|
||||
return city_object[0]
|
||||
# search by name first
|
||||
city_object = self.session.execute(select(Model).where(Model.name == name, Model.city_id == city_id)).first()
|
||||
if city_object is not None:
|
||||
return city_object[0]
|
||||
city_objects = self.session.execute(
|
||||
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(',')
|
||||
for alias in aliases:
|
||||
print(alias, name)
|
||||
if alias == name:
|
||||
# force the name as the alias
|
||||
city_object[0].name = name
|
||||
|
@ -12,7 +12,6 @@ openpyxl
|
||||
networkx
|
||||
parseidf==1.0.0
|
||||
ply
|
||||
rhino3dm==7.7.0
|
||||
scipy
|
||||
PyYAML
|
||||
pyecore==0.12.2
|
||||
|
@ -4,35 +4,34 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
||||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Peter Yefi peteryefi@gmail.com
|
||||
"""
|
||||
import distutils.spawn
|
||||
import glob
|
||||
import json
|
||||
import logging
|
||||
import os
|
||||
import subprocess
|
||||
import unittest
|
||||
from unittest import TestCase
|
||||
from pathlib import Path
|
||||
import sqlalchemy.exc
|
||||
from unittest import TestCase
|
||||
|
||||
from hub.helpers.data.montreal_function_to_hub_function import MontrealFunctionToHubFunction
|
||||
from hub.imports.geometry_factory import GeometryFactory
|
||||
from hub.imports.construction_factory import ConstructionFactory
|
||||
from hub.imports.usage_factory import UsageFactory
|
||||
from hub.imports.results_factory import ResultFactory
|
||||
from hub.imports.weather_factory import WeatherFactory
|
||||
from hub.imports.energy_systems_factory import EnergySystemsFactory
|
||||
import sqlalchemy.exc
|
||||
from sqlalchemy import create_engine
|
||||
from sqlalchemy.exc import ProgrammingError
|
||||
|
||||
import hub.helpers.constants as cte
|
||||
from hub.exports.energy_building_exports_factory import EnergyBuildingsExportsFactory
|
||||
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.repository import Repository
|
||||
from sqlalchemy import create_engine
|
||||
from hub.persistence.models import City, Application, CityObject, SimulationResults
|
||||
from hub.persistence.models import User, UserRoles
|
||||
from hub.helpers.dictionaries import Dictionaries
|
||||
from sqlalchemy.exc import ProgrammingError
|
||||
import uuid
|
||||
import hub.helpers.constants as cte
|
||||
import distutils.spawn
|
||||
from hub.persistence.repository import Repository
|
||||
|
||||
|
||||
class Control:
|
||||
@ -65,13 +64,13 @@ class Control:
|
||||
self._skip_test = True
|
||||
self._skip_reason = f'{operational_error}'
|
||||
return
|
||||
"""
|
||||
|
||||
Application.__table__.create(bind=repository.engine, checkfirst=True)
|
||||
User.__table__.create(bind=repository.engine, checkfirst=True)
|
||||
City.__table__.create(bind=repository.engine, checkfirst=True)
|
||||
CityObject.__table__.create(bind=repository.engine, checkfirst=True)
|
||||
SimulationResults.__table__.create(bind=repository.engine, checkfirst=True)
|
||||
"""
|
||||
|
||||
city_file = Path('tests_data/test.geojson').resolve()
|
||||
output_path = Path('tests_outputs/').resolve()
|
||||
self._city = GeometryFactory('geojson',
|
||||
@ -81,6 +80,7 @@ class Control:
|
||||
aliases_field=['ID_UEV', 'CIVIQUE_DE', 'NOM_RUE'],
|
||||
function_field='CODE_UTILI',
|
||||
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
|
||||
|
||||
ConstructionFactory('nrcan', self._city).enrich()
|
||||
UsageFactory('nrcan', self._city).enrich()
|
||||
WeatherFactory('epw', self._city).enrich()
|
||||
@ -107,14 +107,13 @@ class Control:
|
||||
self._application_id = 1
|
||||
self._user_id = 1
|
||||
|
||||
"""
|
||||
self._application_id = self._database.persist_application(
|
||||
'City_layers',
|
||||
'City layers test user',
|
||||
self.application_uuid
|
||||
)
|
||||
self._user_id = self._database.create_user('city_layers', self._application_id, 'city_layers', UserRoles.Admin)
|
||||
"""
|
||||
|
||||
self._pickle_path = 'tests_data/pickle_path.bz2'
|
||||
|
||||
@property
|
||||
|
@ -110,15 +110,6 @@ class TestGeometryFactory(TestCase):
|
||||
self._check_surfaces(building)
|
||||
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):
|
||||
"""
|
||||
Test obj import
|
||||
|
Binary file not shown.
Binary file not shown.
Loading…
Reference in New Issue
Block a user