forked from s_ranjbar/city_retrofit
Merge pull request 'optimization' (#10) from optimization into main
Reviewed-on: https://nextgenerations-cities.encs.concordia.ca/gitea/CERC/hub/pulls/10
This commit is contained in:
commit
053a866443
|
@ -26,8 +26,6 @@ class Polygon:
|
|||
"""
|
||||
Polygon class
|
||||
"""
|
||||
# todo: review with @Guille: Points, Coordinates, Vertices, Faces
|
||||
|
||||
def __init__(self, coordinates):
|
||||
self._area = None
|
||||
self._points = None
|
||||
|
|
|
@ -60,6 +60,7 @@ class City:
|
|||
self._stations = []
|
||||
self._lca_materials = None
|
||||
self._level_of_detail = LevelOfDetail()
|
||||
self._city_objects_dictionary = {}
|
||||
|
||||
@property
|
||||
def fuels(self) -> [Fuel]:
|
||||
|
@ -198,9 +199,8 @@ class City:
|
|||
:param name:str
|
||||
:return: None or CityObject
|
||||
"""
|
||||
for city_object in self.buildings:
|
||||
if str(city_object.name) == str(name):
|
||||
return city_object
|
||||
if name in self._city_objects_dictionary:
|
||||
return self.buildings[self._city_objects_dictionary[name]]
|
||||
return None
|
||||
|
||||
def add_city_object(self, new_city_object):
|
||||
|
@ -213,6 +213,7 @@ class City:
|
|||
if self._buildings is None:
|
||||
self._buildings = []
|
||||
self._buildings.append(new_city_object)
|
||||
self._city_objects_dictionary[new_city_object.name] = len(self._buildings) - 1
|
||||
elif new_city_object.type == 'energy_system':
|
||||
if self._energy_systems is None:
|
||||
self._energy_systems = []
|
||||
|
@ -233,6 +234,10 @@ class City:
|
|||
else:
|
||||
if city_object in self._buildings:
|
||||
self._buildings.remove(city_object)
|
||||
# regenerate hash map
|
||||
self._city_objects_dictionary.clear()
|
||||
for i, city_object in enumerate(self._buildings):
|
||||
self._city_objects_dictionary[city_object.name] = i
|
||||
|
||||
@property
|
||||
def srs_name(self) -> Union[None, str]:
|
||||
|
|
|
@ -6,9 +6,6 @@ Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca
|
|||
"""
|
||||
|
||||
from pathlib import Path
|
||||
import trimesh.exchange.obj
|
||||
from hub.exports.formats.triangular import Triangular
|
||||
from hub.imports.geometry_factory import GeometryFactory
|
||||
|
||||
|
||||
class Obj:
|
||||
|
@ -38,6 +35,8 @@ class Obj:
|
|||
faces = []
|
||||
for building in self._city.buildings:
|
||||
obj.write(f'# building {building.name}\n')
|
||||
obj.write(f'g {building.name}\n')
|
||||
obj.write('s off\n')
|
||||
for surface in building.surfaces:
|
||||
obj.write(f'# surface {surface.name}\n')
|
||||
face = 'f '
|
||||
|
@ -56,6 +55,3 @@ class Obj:
|
|||
faces.append(f'{face} {face.split(" ")[1]}\n')
|
||||
obj.writelines(faces)
|
||||
faces = []
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ import math
|
|||
|
||||
import numpy as np
|
||||
import requests
|
||||
from PIL import Image
|
||||
from trimesh import Trimesh
|
||||
from trimesh import intersections
|
||||
|
||||
|
@ -16,8 +17,6 @@ from hub.city_model_structure.attributes.polygon import Polygon
|
|||
from hub.city_model_structure.attributes.polyhedron import Polyhedron
|
||||
from hub.helpers.location import Location
|
||||
|
||||
from PIL import Image
|
||||
|
||||
|
||||
class MapPoint:
|
||||
def __init__(self, x, y):
|
||||
|
@ -88,6 +87,7 @@ class GeometryHelper:
|
|||
for ground in building.grounds:
|
||||
length = len(ground.perimeter_polygon.coordinates) - 1
|
||||
for i, coordinate in enumerate(ground.perimeter_polygon.coordinates):
|
||||
|
||||
j = i + 1
|
||||
if i == length:
|
||||
j = 0
|
||||
|
@ -167,10 +167,53 @@ class GeometryHelper:
|
|||
elif building not in neighbour.neighbours:
|
||||
neighbour.neighbours.append(building)
|
||||
line += 1
|
||||
|
||||
if plot:
|
||||
img.show()
|
||||
return lines_information
|
||||
|
||||
@staticmethod
|
||||
def fast_city_mapping(city, building_names=None):
|
||||
lines_information = {}
|
||||
if building_names is None:
|
||||
building_names = [b.name for b in city.buildings]
|
||||
x = int((city.upper_corner[0] - city.lower_corner[0]) * 0.5) + 1
|
||||
y = int((city.upper_corner[1] - city.lower_corner[1]) * 0.5) + 1
|
||||
city_map = [['' for _ in range(y + 1)] for _ in range(x + 1)]
|
||||
for building_name in building_names:
|
||||
building = city.city_object(building_name)
|
||||
line = 0
|
||||
for ground in building.grounds:
|
||||
length = len(ground.perimeter_polygon.coordinates) - 1
|
||||
for i, coordinate in enumerate(ground.perimeter_polygon.coordinates):
|
||||
j = i + 1
|
||||
if i == length:
|
||||
j = 0
|
||||
next_coordinate = ground.perimeter_polygon.coordinates[j]
|
||||
point = GeometryHelper.coordinate_to_map_point(coordinate, city)
|
||||
distance = int(GeometryHelper.distance_between_points(coordinate, next_coordinate))
|
||||
if distance == 0:
|
||||
continue
|
||||
delta_x = (coordinate[0] - next_coordinate[0]) / (distance / 0.5)
|
||||
delta_y = (coordinate[1] - next_coordinate[1]) / (distance / 0.5)
|
||||
for k in range(0, distance):
|
||||
x = MapPoint(point.x + (delta_x * k), point.y + (delta_y * k)).x
|
||||
y = MapPoint(point.x + (delta_x * k), point.y + (delta_y * k)).y
|
||||
if city_map[x][y] == '':
|
||||
city_map[x][y] = building.name
|
||||
elif city_map[x][y] != building.name:
|
||||
neighbour = city.city_object(city_map[x][y])
|
||||
if building.neighbours is None:
|
||||
building.neighbours = [neighbour]
|
||||
elif neighbour not in building.neighbours:
|
||||
building.neighbours.append(neighbour)
|
||||
if neighbour.neighbours is None:
|
||||
neighbour.neighbours = [building]
|
||||
elif building not in neighbour.neighbours:
|
||||
neighbour.neighbours.append(building)
|
||||
line += 1
|
||||
return lines_information
|
||||
|
||||
@staticmethod
|
||||
def segment_list_to_trimesh(lines) -> Trimesh:
|
||||
"""
|
||||
|
|
|
@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
import datetime
|
||||
import sys
|
||||
import math
|
||||
import numpy as np
|
||||
|
@ -39,7 +40,7 @@ class StoreysGeneration:
|
|||
number_of_storeys, height = self._calculate_number_storeys_and_height(self._building.average_storey_height,
|
||||
self._building.eave_height,
|
||||
self._building.storeys_above_ground)
|
||||
number_of_storeys = 1
|
||||
|
||||
if not self._divide_in_storeys or number_of_storeys == 1:
|
||||
storey = Storey('storey_0', self._building.surfaces, [None, None], self._internal_zone.volume,
|
||||
self._internal_zone, self._floor_area)
|
||||
|
@ -55,7 +56,6 @@ class StoreysGeneration:
|
|||
else:
|
||||
thermal_zones = [storey.neighbours[1], storey.thermal_zone]
|
||||
thermal_boundary.thermal_zones = thermal_zones
|
||||
|
||||
return [storey.thermal_zone]
|
||||
|
||||
if number_of_storeys == 0:
|
||||
|
|
|
@ -4,7 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
|
||||
import datetime
|
||||
import math
|
||||
import sys
|
||||
from hub.hub_logger import logger
|
||||
|
@ -37,17 +37,21 @@ class NrcanPhysicsParameters:
|
|||
for building in city.buildings:
|
||||
try:
|
||||
function = Dictionaries().hub_function_to_nrcan_construction_function[building.function]
|
||||
|
||||
archetype = self._search_archetype(nrcan_catalog, function, building.year_of_construction, self._climate_zone)
|
||||
|
||||
except KeyError:
|
||||
logger.error(f'Building {building.name} has unknown construction archetype for building function: '
|
||||
f'{building.function}, building year of construction: {building.year_of_construction} '
|
||||
f'{function} [{building.function}], building year of construction: {building.year_of_construction} '
|
||||
f'and climate zone {self._climate_zone}\n')
|
||||
sys.stderr.write(f'Building {building.name} has unknown construction archetype for building function: '
|
||||
f'{building.function}, building year of construction: {building.year_of_construction} '
|
||||
f'{function} [{building.function}], building year of construction: {building.year_of_construction} '
|
||||
f'and climate zone {self._climate_zone}\n')
|
||||
continue
|
||||
|
||||
# if building has no thermal zones defined from geometry, and the building will be divided in storeys,
|
||||
# one thermal zone per storey is assigned
|
||||
|
||||
if len(building.internal_zones) == 1:
|
||||
if building.internal_zones[0].thermal_zones is None:
|
||||
self._create_storeys(building, archetype, self._divide_in_storeys)
|
||||
|
@ -63,7 +67,6 @@ class NrcanPhysicsParameters:
|
|||
for internal_zone in building.internal_zones:
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
thermal_zone.total_floor_area = thermal_zone.footprint_area
|
||||
|
||||
for internal_zone in building.internal_zones:
|
||||
self._assign_values(internal_zone.thermal_zones, archetype)
|
||||
for thermal_zone in internal_zone.thermal_zones:
|
||||
|
@ -74,7 +77,7 @@ class NrcanPhysicsParameters:
|
|||
nrcan_archetypes = nrcan_catalog.entries('archetypes')
|
||||
for building_archetype in nrcan_archetypes:
|
||||
construction_period_limits = building_archetype.construction_period.split('_')
|
||||
if int(construction_period_limits[0]) <= year_of_construction < int(construction_period_limits[1]):
|
||||
if int(construction_period_limits[0]) <= year_of_construction <= int(construction_period_limits[1]):
|
||||
if (str(function) == str(building_archetype.function)) and \
|
||||
(climate_zone == str(building_archetype.climate_zone)):
|
||||
return building_archetype
|
||||
|
|
|
@ -4,6 +4,7 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Guillermo Gutierrez Guillermo.GutierrezMorote@concordia.ca
|
||||
"""
|
||||
import datetime
|
||||
import json
|
||||
|
||||
import numpy as np
|
||||
|
@ -67,7 +68,8 @@ class Geojson:
|
|||
points = igh.invert_points(points)
|
||||
polygon = Polygon(points)
|
||||
polygon.area = igh.ground_area(points)
|
||||
surfaces.append(Surface(polygon, polygon))
|
||||
surface = Surface(polygon, polygon)
|
||||
surfaces.append(surface)
|
||||
buildings.append(Building(f'{name}_zone_{zone}', surfaces, year_of_construction, function))
|
||||
return buildings
|
||||
|
||||
|
@ -79,6 +81,7 @@ class Geojson:
|
|||
|
||||
for zone, lod0_building in enumerate(lod0_buildings):
|
||||
for surface in lod0_building.grounds:
|
||||
|
||||
volume = surface.solid_polygon.area * height
|
||||
surfaces.append(surface)
|
||||
roof_coordinates = []
|
||||
|
@ -88,6 +91,7 @@ class Geojson:
|
|||
# insert the roof rotated already
|
||||
roof_coordinates.insert(0, roof_coordinate)
|
||||
polygon = Polygon(roof_coordinates)
|
||||
polygon.area = surface.solid_polygon.area
|
||||
roof = Surface(polygon, polygon)
|
||||
surfaces.append(roof)
|
||||
# adding a wall means add the point coordinates and the next point coordinates with Z's height and 0
|
||||
|
@ -177,6 +181,7 @@ class Geojson:
|
|||
Get city out of a Geojson file
|
||||
"""
|
||||
if self._city is None:
|
||||
start = datetime.datetime.now()
|
||||
missing_functions = []
|
||||
buildings = []
|
||||
building_id = 0
|
||||
|
@ -225,12 +230,18 @@ class Geojson:
|
|||
[polygon])
|
||||
|
||||
self._city = City([self._min_x, self._min_y, 0.0], [self._max_x, self._max_y, self._max_z], 'epsg:26911')
|
||||
print(f'features: {datetime.datetime.now()-start}')
|
||||
|
||||
for building in buildings:
|
||||
self._city.add_city_object(building)
|
||||
self._city.level_of_detail.geometry = lod
|
||||
if lod == 1:
|
||||
lines_information = GeometryHelper.city_mapping(self._city)
|
||||
start = datetime.datetime.now()
|
||||
lines_information = GeometryHelper.city_mapping(self._city, plot=False)
|
||||
print(f'mapping: {datetime.datetime.now() - start}')
|
||||
start = datetime.datetime.now()
|
||||
self._store_shared_percentage_to_walls(self._city, lines_information)
|
||||
print(f'shared_walls: {datetime.datetime.now() - start}')
|
||||
if len(missing_functions) > 0:
|
||||
print(f'There are unknown functions {missing_functions}')
|
||||
return self._city
|
||||
|
|
|
@ -105,11 +105,3 @@ class GeometryHelper:
|
|||
cosine = -1
|
||||
alpha = math.acos(cosine)
|
||||
return alpha
|
||||
|
||||
|
||||
@staticmethod
|
||||
def invert_points(points):
|
||||
res = []
|
||||
for point in points:
|
||||
res.insert(0,point)
|
||||
return res
|
||||
|
|
|
@ -4,14 +4,12 @@ SPDX - License - Identifier: LGPL - 3.0 - or -later
|
|||
Copyright © 2022 Concordia CERC group
|
||||
Project Coder Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concordia.ca
|
||||
"""
|
||||
import datetime
|
||||
from pathlib import Path
|
||||
from unittest import TestCase
|
||||
from hub.helpers.geometry_helper import GeometryHelper
|
||||
|
||||
from numpy import inf
|
||||
|
||||
import hub.exports.exports_factory
|
||||
from hub.helpers.dictionaries import MontrealFunctionToHubFunction
|
||||
from hub.helpers.geometry_helper import GeometryHelper
|
||||
from hub.imports.construction_factory import ConstructionFactory
|
||||
from hub.imports.geometry_factory import GeometryFactory
|
||||
|
||||
|
@ -21,6 +19,7 @@ class TestGeometryFactory(TestCase):
|
|||
Non-functional TestGeometryFactory
|
||||
Load testing
|
||||
"""
|
||||
|
||||
def setUp(self) -> None:
|
||||
"""
|
||||
Test setup
|
||||
|
@ -36,7 +35,8 @@ class TestGeometryFactory(TestCase):
|
|||
path=file_path,
|
||||
height_field=height_field,
|
||||
year_of_construction_field=year_of_construction_field,
|
||||
function_field=function_field).city
|
||||
function_field=function_field,
|
||||
).city
|
||||
self.assertIsNotNone(self._city, 'city is none')
|
||||
return self._city
|
||||
|
||||
|
@ -135,19 +135,13 @@ class TestGeometryFactory(TestCase):
|
|||
Test geojson import
|
||||
"""
|
||||
file = '2000_buildings.geojson'
|
||||
start = datetime.datetime.now()
|
||||
city = self._get_city(file, 'geojson',
|
||||
height_field='building_height',
|
||||
year_of_construction_field='ANNEE_CONS',
|
||||
function_field='CODE_UTILI')
|
||||
end = datetime.datetime.now()
|
||||
print(f'geometry load in {end-start} s')
|
||||
start = datetime.datetime.now()
|
||||
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
|
||||
end = datetime.datetime.now()
|
||||
print(f'geometry export in {end - start} s')
|
||||
city = GeometryFactory('geojson',
|
||||
path=(self._example_path / file).resolve(),
|
||||
height_field='building_height',
|
||||
year_of_construction_field='ANNEE_CONS',
|
||||
function_field='CODE_UTILI',
|
||||
function_to_hub=MontrealFunctionToHubFunction().dictionary).city
|
||||
self.assertEqual(2356, len(city.buildings), 'wrong number of buildings')
|
||||
self._check_buildings(city)
|
||||
|
||||
def test_map_neighbours(self):
|
||||
"""
|
||||
|
@ -162,15 +156,16 @@ class TestGeometryFactory(TestCase):
|
|||
city = self._get_city(file, 'geojson',
|
||||
year_of_construction_field='ANNEE_CONS',
|
||||
function_field='LIBELLE_UT')
|
||||
|
||||
info_lod0 = GeometryHelper.city_mapping(city, plot=False)
|
||||
hub.exports.exports_factory.ExportsFactory('obj', city, self._output_path).export()
|
||||
self.assertEqual(info_lod0, info_lod1)
|
||||
for building in city.buildings:
|
||||
self.assertEqual(2, len(building.neighbours))
|
||||
|
||||
self.assertEqual('2_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[0].name)
|
||||
self.assertEqual('3_part_0_zone_0',city.city_object('1_part_0_zone_0').neighbours[1].name)
|
||||
self.assertEqual('1_part_0_zone_0',city.city_object('2_part_0_zone_0').neighbours[0].name)
|
||||
self.assertEqual('3_part_0_zone_0',city.city_object('2_part_0_zone_0').neighbours[1].name)
|
||||
self.assertEqual('2_part_0_zone_0', city.city_object('1_part_0_zone_0').neighbours[0].name)
|
||||
self.assertEqual('3_part_0_zone_0', city.city_object('1_part_0_zone_0').neighbours[1].name)
|
||||
self.assertEqual('1_part_0_zone_0', city.city_object('2_part_0_zone_0').neighbours[0].name)
|
||||
self.assertEqual('3_part_0_zone_0', city.city_object('2_part_0_zone_0').neighbours[1].name)
|
||||
self.assertEqual('1_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[0].name)
|
||||
self.assertEqual('2_part_0_zone_0', city.city_object('3_part_0_zone_0').neighbours[1].name)
|
||||
|
|
File diff suppressed because one or more lines are too long
Loading…
Reference in New Issue
Block a user