Merge remote-tracking branch 'origin/master' into nrcan

This commit is contained in:
Guille Gutierrez 2022-11-21 14:02:01 -05:00
commit ac53ce2f4d
22 changed files with 200 additions and 30 deletions

1
.gitignore vendored
View File

@ -5,3 +5,4 @@
/data/energy_systems/heat_pumps/*.csv /data/energy_systems/heat_pumps/*.csv
/data/energy_systems/heat_pumps/*.insel /data/energy_systems/heat_pumps/*.insel
.DS_Store .DS_Store
**/__pycache__/

View File

@ -164,7 +164,7 @@ When all the dependencies are satisfied, you are all set to start importing your
Add the following code to your main.py Add the following code to your main.py
from imports.geometry_factory import GeometryFactory from imports.geometry_factory import GeometryFactory
city = GeometryFactory('citygml', 'myfile.gml').city city = GeometryFactory('citygml', path='myfile.gml').city
Always remember to push your own project changes as the last thing you do before ending your working day! Always remember to push your own project changes as the last thing you do before ending your working day!
First, commit your changes by clicking on the green check in the top-right corner of Pycharm. Add a comment First, commit your changes by clicking on the green check in the top-right corner of Pycharm. Add a comment

View File

@ -237,7 +237,7 @@ Add the following code to your main.py
from imports.geometry_factory import GeometryFactory from imports.geometry_factory import GeometryFactory
city = GeometryFactory('citygml', 'myfile.gml').city city = GeometryFactory('citygml', path='myfile.gml').city
``` ```
9. Always remember to push your own project changes as the last thing you do before ending your working day! 9. Always remember to push your own project changes as the last thing you do before ending your working day!

View File

@ -12,7 +12,7 @@ Catalog = TypeVar('Catalog')
class GreeneryCatalogFactory: class GreeneryCatalogFactory:
""" """
GeometryFactory class GreeneryCatalogFactory class
""" """
def __init__(self, file_type, base_path=None): def __init__(self, file_type, base_path=None):
if base_path is None: if base_path is None:

View File

@ -26,7 +26,7 @@ class Obj(Triangular):
file_name_out = self._city.name + '_ground.' + self._triangular_format file_name_out = self._city.name + '_ground.' + self._triangular_format
file_path_in = (Path(self._path).resolve() / file_name_in).resolve() file_path_in = (Path(self._path).resolve() / file_name_in).resolve()
file_path_out = (Path(self._path).resolve() / file_name_out).resolve() file_path_out = (Path(self._path).resolve() / file_name_out).resolve()
scene = GeometryFactory('obj', file_path_in).scene scene = GeometryFactory('obj', path=file_path_in).scene
scene.rezero() scene.rezero()
obj_file = trimesh.exchange.obj.export_obj(scene) obj_file = trimesh.exchange.obj.export_obj(scene)
with open(file_path_out, 'w') as file: with open(file_path_out, 'w') as file:

115
imports/geometry/gpandas.py Normal file
View File

@ -0,0 +1,115 @@
"""
gpandas module parses geopandas input table and import the geometry into the city model structure
SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group
Project Coder: Milad Aghamohamadnia --- milad.aghamohamadnia@concordia.ca
"""
import trimesh
import trimesh.exchange.load
import trimesh.geometry
import trimesh.creation
import trimesh.repair
from shapely.geometry import Point
from shapely.geometry import Polygon as ShapelyPoly
from trimesh import Scene
from city_model_structure.attributes.polygon import Polygon
from city_model_structure.building import Building
from city_model_structure.building_demand.surface import Surface
from city_model_structure.city import City
import helpers.constants as cte
class GPandas:
"""
GeoPandas class
"""
def __init__(self, dataframe, srs_name='EPSG:26911'):
"""_summary_
Arguments:
dataframe {Geopandas.Dataframe} -- input geometry data in geopandas table
Keyword Arguments:
srs_name {str} -- coordinate system of coordinate system (default: {'EPSG:26911'})
"""
self._srs_name = srs_name
self._city = None
self._scene = dataframe
self._scene = self._scene.to_crs(self._srs_name)
min_x, min_y, max_x, max_y = self._scene.total_bounds
print(min_x)
self._lower_corner = [min_x, min_y, 0]
self._upper_corner = [max_x, max_y, 0]
@property
def scene(self) -> Scene:
"""
Get GeoPandas scene
"""
return self._scene
@property
def city(self) -> City:
"""
Get city out of a GeoPandas Table
"""
if self._city is None:
self._city = City(self._lower_corner, self._upper_corner, self._srs_name)
for scene_index, bldg in self._scene.iterrows():
geometry = bldg.geom
polygon = ShapelyPoly(geometry['coordinates'][0])
height = float(bldg['height'])
building_mesh = trimesh.creation.extrude_polygon(polygon, height)
trimesh.repair.fill_holes(building_mesh)
trimesh.repair.fix_winding(building_mesh)
year_of_construction = int(bldg['year_built'])
name = str(scene_index)
lod = 1
if year_of_construction > 2000:
function = cte.RESIDENTIAL
else:
function = cte.INDUSTRY
surfaces = []
for face_index, face in enumerate(building_mesh.faces):
points = []
for vertex_index in face:
points.append(building_mesh.vertices[vertex_index])
solid_polygon = Polygon(points)
perimeter_polygon = solid_polygon
surface = Surface(solid_polygon, perimeter_polygon)
surfaces.append(surface)
building = Building(name, lod, surfaces, year_of_construction, function, self._lower_corner, terrains=None)
self._city.add_city_object(building)
return self._city
@staticmethod
def resize_polygon(poly, factor=0.10, expand=False) -> ShapelyPoly:
"""
returns the shapely polygon which is smaller or bigger by passed factor.
Arguments:
poly {shapely.geometry.Polygon} -- an input geometry in shapely polygon format
Keyword Arguments:
factor {float} -- factor of expansion (default: {0.10})
expand {bool} -- If expand = True , then it returns bigger polygon, else smaller (default: {False})
Returns:
{shapely.geometry.Polygon} -- output geometry in shapely polygon format
"""
xs = list(poly.exterior.coords.xy[0])
ys = list(poly.exterior.coords.xy[1])
x_center = 0.5 * min(xs) + 0.5 * max(xs)
y_center = 0.5 * min(ys) + 0.5 * max(ys)
min_corner = Point(min(xs), min(ys))
center = Point(x_center, y_center)
shrink_distance = center.distance(min_corner) * factor
if expand:
poly_resized = poly.buffer(shrink_distance) # expand
else:
poly_resized = poly.buffer(-shrink_distance) # shrink
return poly_resized

View File

@ -10,15 +10,17 @@ from imports.geometry.citygml import CityGml
from imports.geometry.obj import Obj from imports.geometry.obj import Obj
from imports.geometry.osm_subway import OsmSubway from imports.geometry.osm_subway import OsmSubway
from imports.geometry.rhino import Rhino from imports.geometry.rhino import Rhino
from imports.geometry.gpandas import GPandas
import geopandas
class GeometryFactory: class GeometryFactory:
""" """
GeometryFactory class GeometryFactory class
""" """
def __init__(self, file_type, path): def __init__(self, file_type, path=None, data_frame=None):
self._file_type = '_' + file_type.lower() self._file_type = '_' + file_type.lower()
self._path = path self._path = path
self._data_frame = data_frame
@property @property
def _citygml(self) -> City: def _citygml(self) -> City:
@ -35,6 +37,16 @@ class GeometryFactory:
:return: City :return: City
""" """
return Obj(self._path).city return Obj(self._path).city
@property
def _gpandas(self) -> City:
"""
Enrich the city by using GeoPandas information as data source
:return: City
"""
if self._data_frame is None:
self._data_frame = geopandas.read_file(self._path)
return GPandas(self._data_frame).city
@property @property
def _osm_subway(self) -> City: def _osm_subway(self) -> City:
@ -66,4 +78,4 @@ class GeometryFactory:
Enrich the city given to the class using the class given handler Enrich the city given to the class using the class given handler
:return: City :return: City
""" """
return CityGml(self._path).city return GPandas(geopandas.read_file(self._path)).city

View File

@ -15,4 +15,7 @@ ply
rhino3dm==7.7.0 rhino3dm==7.7.0
scipy scipy
PyYAML PyYAML
pyecore==0.12.2 pyecore==0.12.2
shapely
geopandas
triangle

View File

@ -23,7 +23,7 @@ class TestCityMerge(TestCase):
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
city = GeometryFactory('citygml', file_path).city city = GeometryFactory('citygml', path=file_path).city
self.assertIsNotNone(city, 'city is none') self.assertIsNotNone(city, 'city is none')
return city return city

View File

@ -26,7 +26,7 @@ class TestConstructionFactory(TestCase):
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('citygml', file_path).city self._city = GeometryFactory('citygml', path=file_path).city
self.assertIsNotNone(self._city, 'city is none') self.assertIsNotNone(self._city, 'city is none')
return self._city return self._city

View File

@ -27,7 +27,7 @@ class TestBuildings(TestCase):
def test_doe_idf(self): def test_doe_idf(self):
city_file = "../unittests/tests_data/one_building_in_kelowna.gml" city_file = "../unittests/tests_data/one_building_in_kelowna.gml"
output_path = Path('../unittests/tests_outputs/').resolve() output_path = Path('../unittests/tests_outputs/').resolve()
city = GeometryFactory('citygml', city_file).city city = GeometryFactory('citygml', path=city_file).city
for building in city.buildings: for building in city.buildings:
building.year_of_construction = 2006 building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich() ConstructionFactory('nrel', city).enrich()

View File

@ -25,7 +25,7 @@ class TestEnergySystemsFactory(TestCase):
""" """
city_file = "../unittests/tests_data/C40_Final.gml" city_file = "../unittests/tests_data/C40_Final.gml"
self._output_path = "../unittests/tests_data/as_user_output.csv" self._output_path = "../unittests/tests_data/as_user_output.csv"
self._city = GeometryFactory('citygml', city_file).city self._city = GeometryFactory('citygml', path=city_file).city
EnergySystemsFactory('air source hp', self._city).enrich() EnergySystemsFactory('air source hp', self._city).enrich()
def test_air_source_heat_pump_import(self): def test_air_source_heat_pump_import(self):

View File

@ -26,7 +26,7 @@ class TestEnergySystemsFactory(TestCase):
""" """
city_file = "../unittests/tests_data/C40_Final.gml" city_file = "../unittests/tests_data/C40_Final.gml"
self._output_path = "../unittests/tests_data/w2w_user_output.csv" self._output_path = "../unittests/tests_data/w2w_user_output.csv"
self._city = GeometryFactory('citygml', city_file).city self._city = GeometryFactory('citygml', path=city_file).city
EnergySystemsFactory('water to water hp', self._city).enrich() EnergySystemsFactory('water to water hp', self._city).enrich()
def test_water_to_water_heat_pump_import(self): def test_water_to_water_heat_pump_import(self):

View File

@ -27,7 +27,7 @@ class TestGeometryFactory(TestCase):
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('citygml', file_path).city self._city = GeometryFactory('citygml', path=file_path).city
self.assertIsNotNone(self._city, 'city is none') self.assertIsNotNone(self._city, 'city is none')
return self._city return self._city
@ -93,9 +93,7 @@ class TestGeometryFactory(TestCase):
_construction_keys = ['nrel'] _construction_keys = ['nrel']
_usage_keys = ['comnet', 'hft'] _usage_keys = ['comnet', 'hft']
for construction_key in _construction_keys: for construction_key in _construction_keys:
print('construction key: ', construction_key)
for usage_key in _usage_keys: for usage_key in _usage_keys:
print('usage key: ', usage_key)
# construction factory called first # construction factory called first
city = self._get_citygml(file) city = self._get_citygml(file)
for building in city.buildings: for building in city.buildings:

View File

@ -34,7 +34,7 @@ class TestExports(TestCase):
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('citygml', file_path).city self._city = GeometryFactory('citygml', path=file_path).city
self.assertIsNotNone(self._city, 'city is none') self.assertIsNotNone(self._city, 'city is none')
return self._city return self._city

View File

@ -8,9 +8,11 @@ from pathlib import Path
from unittest import TestCase from unittest import TestCase
from numpy import inf from numpy import inf
from pyproj import Proj, transform
from imports.geometry_factory import GeometryFactory from imports.geometry_factory import GeometryFactory
from imports.construction_factory import ConstructionFactory from imports.construction_factory import ConstructionFactory
import geopandas
class TestGeometryFactory(TestCase): class TestGeometryFactory(TestCase):
@ -28,20 +30,26 @@ class TestGeometryFactory(TestCase):
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('citygml', file_path).city self._city = GeometryFactory('citygml', path=file_path).city
self.assertIsNotNone(self._city, 'city is none')
return self._city
def _get_geojson(self, file):
file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('gpandas', path=file_path).city
self.assertIsNotNone(self._city, 'city is none') self.assertIsNotNone(self._city, 'city is none')
return self._city return self._city
def _get_obj(self, file): def _get_obj(self, file):
# todo: solve the incongruities between city and city_debug # todo: solve the incongruities between city and city_debug
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('obj', file_path).city self._city = GeometryFactory('obj', path=file_path).city
self.assertIsNotNone(self._city, 'city is none') self.assertIsNotNone(self._city, 'city is none')
return self._city return self._city
def _get_rhino(self, file): def _get_rhino(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('rhino', file_path).city self._city = GeometryFactory('rhino', path=file_path).city
self.assertIsNotNone(self._city, 'city is none') self.assertIsNotNone(self._city, 'city is none')
return self._city return self._city
@ -147,6 +155,21 @@ class TestGeometryFactory(TestCase):
for building in city.buildings: for building in city.buildings:
self._check_surfaces(building) self._check_surfaces(building)
def test_import_geopandas(self):
"""
Test geopandas import
"""
file = 'sample.geojson'
city = self._get_geojson(file)
self.assertIsNotNone(city, 'city is none')
self.assertTrue(len(city.buildings) == 1)
self._check_buildings(city)
for building in city.buildings:
self._check_surfaces(building)
self.assertEqual(1912.0898135701814, building.volume)
self.assertEqual(146.19493345171213, building.floor_area)
# osm # osm
def test_subway(self): def test_subway(self):
""" """
@ -155,7 +178,7 @@ class TestGeometryFactory(TestCase):
""" """
file_path = (self._example_path / 'subway.osm').resolve() file_path = (self._example_path / 'subway.osm').resolve()
city = GeometryFactory('osm_subway', file_path).city city = GeometryFactory('osm_subway', path=file_path).city
self.assertIsNotNone(city, 'subway entrances is none') self.assertIsNotNone(city, 'subway entrances is none')
self.assertEqual(len(city.city_objects), 20, 'Wrong number of subway entrances') self.assertEqual(len(city.city_objects), 20, 'Wrong number of subway entrances')

View File

@ -1,5 +1,5 @@
""" """
TestGeometryFactory test and validate the city model structure geometric parameters Test greenery factory test and validate the greenery construction
SPDX - License - Identifier: LGPL - 3.0 - or -later SPDX - License - Identifier: LGPL - 3.0 - or -later
Copyright © 2022 Concordia CERC group Copyright © 2022 Concordia CERC group
Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca Project Coder Guille Gutierrez guillermo.gutierrezmorote@concordia.ca

View File

@ -27,7 +27,7 @@ class GreeneryInIdf(TestCase):
city_file = "../unittests/tests_data/one_building_in_kelowna.gml" city_file = "../unittests/tests_data/one_building_in_kelowna.gml"
output_path = Path('../unittests/tests_outputs/').resolve() output_path = Path('../unittests/tests_outputs/').resolve()
city = GeometryFactory('citygml', city_file).city city = GeometryFactory('citygml', path=city_file).city
for building in city.buildings: for building in city.buildings:
building.year_of_construction = 2006 building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich() ConstructionFactory('nrel', city).enrich()
@ -80,7 +80,7 @@ class GreeneryInIdf(TestCase):
print('With greenery') print('With greenery')
print(f'heating: {heating} MWh/yr, cooling: {cooling} MWh/yr') print(f'heating: {heating} MWh/yr, cooling: {cooling} MWh/yr')
city = GeometryFactory('citygml', city_file).city city = GeometryFactory('citygml', path=city_file).city
for building in city.buildings: for building in city.buildings:
building.year_of_construction = 2006 building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich() ConstructionFactory('nrel', city).enrich()

View File

@ -24,28 +24,28 @@ class TestLifeCycleAssessment(TestCase):
def test_fuel(self): def test_fuel(self):
city_file = "../unittests/tests_data/C40_Final.gml" city_file = "../unittests/tests_data/C40_Final.gml"
city = GeometryFactory('citygml', city_file).city city = GeometryFactory('citygml', path=city_file).city
LifeCycleAssessment('fuel', city).enrich() LifeCycleAssessment('fuel', city).enrich()
for fuel in city.fuels: for fuel in city.fuels:
self.assertTrue(len(city.fuels) > 0) self.assertTrue(len(city.fuels) > 0)
def test_vehicle(self): def test_vehicle(self):
city_file = "../unittests/tests_data/C40_Final.gml" city_file = "../unittests/tests_data/C40_Final.gml"
city = GeometryFactory('citygml', city_file).city city = GeometryFactory('citygml', path=city_file).city
LifeCycleAssessment('vehicle', city).enrich() LifeCycleAssessment('vehicle', city).enrich()
for vehicle in city.vehicles: for vehicle in city.vehicles:
self.assertTrue(len(city.vehicles) > 0) self.assertTrue(len(city.vehicles) > 0)
def test_machine(self): def test_machine(self):
city_file = "../unittests/tests_data/C40_Final.gml" city_file = "../unittests/tests_data/C40_Final.gml"
city = GeometryFactory('citygml', city_file).city city = GeometryFactory('citygml', path=city_file).city
LifeCycleAssessment('machine', city).enrich() LifeCycleAssessment('machine', city).enrich()
for machine in city.machines: for machine in city.machines:
self.assertTrue(len(city.machines) > 0) self.assertTrue(len(city.machines) > 0)
def test_material(self): def test_material(self):
city_file = "../unittests/tests_data/C40_Final.gml" city_file = "../unittests/tests_data/C40_Final.gml"
city = GeometryFactory('citygml', city_file).city city = GeometryFactory('citygml', path=city_file).city
LifeCycleAssessment('material', city).enrich() LifeCycleAssessment('material', city).enrich()
for material in city.lca_materials: for material in city.lca_materials:
self.assertTrue(len(city.lca_materials) > 0) self.assertTrue(len(city.lca_materials) > 0)

View File

@ -28,7 +28,7 @@ class TestSchedulesFactory(TestCase):
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
_city = GeometryFactory('citygml', file_path).city _city = GeometryFactory('citygml', path=file_path).city
for building in _city.buildings: for building in _city.buildings:
building.year_of_construction = 2006 building.year_of_construction = 2006
ConstructionFactory('nrel', _city).enrich() ConstructionFactory('nrel', _city).enrich()

View File

@ -26,7 +26,7 @@ class TestUsageFactory(TestCase):
def _get_citygml(self, file): def _get_citygml(self, file):
file_path = (self._example_path / file).resolve() file_path = (self._example_path / file).resolve()
self._city = GeometryFactory('citygml', file_path).city self._city = GeometryFactory('citygml', path=file_path).city
self.assertIsNotNone(self._city, 'city is none') self.assertIsNotNone(self._city, 'city is none')
return self._city return self._city

View File

@ -0,0 +1,18 @@
{ "type": "FeatureCollection",
"features": [
{ "type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[ [[-73.5027962600162, 45.6572759731914], [-73.5027463586105, 45.6572669735158], [-73.5027513584185, 45.6572530729948], [-73.5026715592026, 45.6572412737672], [-73.5026410593539, 45.6573430727752], [-73.5027703584728, 45.6573621728624], [-73.5027962600162, 45.6572759731914]] ]
]
},
"properties": {
"geom": {"type": "Polygon", "crs": {"type": "name", "properties": {"name": "urn:ogc:def:crs:EPSG::4326"}}, "coordinates": [[[3849322.0855625975, 6060583.24800576], [3849326.3956304314, 6060584.796717078], [3849327.0180495544, 6060583.089519385], [3849333.725799462, 6060585.837955164], [3849328.71788522, 6060598.03498192], [3849317.850609142, 6060593.57976506], [3849322.0855625975, 6060583.24800576]]]},
"height": 13.0790429485,
"year_built": 2000
}
}
]
}