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/*.insel
.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
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!
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
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!

View File

@ -12,7 +12,7 @@ Catalog = TypeVar('Catalog')
class GreeneryCatalogFactory:
"""
GeometryFactory class
GreeneryCatalogFactory class
"""
def __init__(self, file_type, base_path=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_path_in = (Path(self._path).resolve() / file_name_in).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()
obj_file = trimesh.exchange.obj.export_obj(scene)
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.osm_subway import OsmSubway
from imports.geometry.rhino import Rhino
from imports.geometry.gpandas import GPandas
import geopandas
class GeometryFactory:
"""
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._path = path
self._data_frame = data_frame
@property
def _citygml(self) -> City:
@ -35,6 +37,16 @@ class GeometryFactory:
:return: 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
def _osm_subway(self) -> City:
@ -66,4 +78,4 @@ class GeometryFactory:
Enrich the city given to the class using the class given handler
: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
scipy
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):
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')
return city

View File

@ -26,7 +26,7 @@ class TestConstructionFactory(TestCase):
def _get_citygml(self, file):
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

View File

@ -27,7 +27,7 @@ class TestBuildings(TestCase):
def test_doe_idf(self):
city_file = "../unittests/tests_data/one_building_in_kelowna.gml"
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:
building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich()

View File

@ -25,7 +25,7 @@ class TestEnergySystemsFactory(TestCase):
"""
city_file = "../unittests/tests_data/C40_Final.gml"
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()
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"
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()
def test_water_to_water_heat_pump_import(self):

View File

@ -27,7 +27,7 @@ class TestGeometryFactory(TestCase):
def _get_citygml(self, file):
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
@ -93,9 +93,7 @@ class TestGeometryFactory(TestCase):
_construction_keys = ['nrel']
_usage_keys = ['comnet', 'hft']
for construction_key in _construction_keys:
print('construction key: ', construction_key)
for usage_key in _usage_keys:
print('usage key: ', usage_key)
# construction factory called first
city = self._get_citygml(file)
for building in city.buildings:

View File

@ -34,7 +34,7 @@ class TestExports(TestCase):
def _get_citygml(self, file):
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

View File

@ -8,9 +8,11 @@ from pathlib import Path
from unittest import TestCase
from numpy import inf
from pyproj import Proj, transform
from imports.geometry_factory import GeometryFactory
from imports.construction_factory import ConstructionFactory
import geopandas
class TestGeometryFactory(TestCase):
@ -28,20 +30,26 @@ class TestGeometryFactory(TestCase):
def _get_citygml(self, file):
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')
return self._city
def _get_obj(self, file):
# todo: solve the incongruities between city and city_debug
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')
return self._city
def _get_rhino(self, file):
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')
return self._city
@ -147,6 +155,21 @@ class TestGeometryFactory(TestCase):
for building in city.buildings:
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
def test_subway(self):
"""
@ -155,7 +178,7 @@ class TestGeometryFactory(TestCase):
"""
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.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
Copyright © 2022 Concordia CERC group
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"
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:
building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich()
@ -80,7 +80,7 @@ class GreeneryInIdf(TestCase):
print('With greenery')
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:
building.year_of_construction = 2006
ConstructionFactory('nrel', city).enrich()

View File

@ -24,28 +24,28 @@ class TestLifeCycleAssessment(TestCase):
def test_fuel(self):
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()
for fuel in city.fuels:
self.assertTrue(len(city.fuels) > 0)
def test_vehicle(self):
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()
for vehicle in city.vehicles:
self.assertTrue(len(city.vehicles) > 0)
def test_machine(self):
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()
for machine in city.machines:
self.assertTrue(len(city.machines) > 0)
def test_material(self):
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()
for material in city.lca_materials:
self.assertTrue(len(city.lca_materials) > 0)

View File

@ -28,7 +28,7 @@ class TestSchedulesFactory(TestCase):
def _get_citygml(self, file):
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:
building.year_of_construction = 2006
ConstructionFactory('nrel', _city).enrich()

View File

@ -26,7 +26,7 @@ class TestUsageFactory(TestCase):
def _get_citygml(self, file):
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

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
}
}
]
}