Compare commits

..

1 Commits

Author SHA1 Message Date
9b5d002444 feat: add required changes to create city 2024-12-03 11:50:38 -05:00
15 changed files with 179 additions and 32 deletions

88
data/test.geojson Normal file
View File

@ -0,0 +1,88 @@
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[
-81.36226736610345,
42.96538327741903
],
[
-81.36226606408695,
42.96539751865368
],
[
-81.36169147743192,
42.96539971389088
],
[
-81.36169145437769,
42.96539577872095
],
[
-81.36160750096705,
42.965396296109915
],
[
-81.36167659355637,
42.96554865177412
],
[
-81.36143594626003,
42.96560572998295
],
[
-81.3612605719971,
42.9652212961923
],
[
-81.36153890966588,
42.96521432093572
],
[
-81.36172317324467,
42.9651651651121
],
[
-81.3617438333442,
42.965204767431615
],
[
-81.36220126106349,
42.96509245874135
],
[
-81.36231896772036,
42.96532755243104
],
[
-81.36230203685074,
42.96533218062799
],
[
-81.36232060244981,
42.9653700064552
],
[
-81.36226736610345,
42.96538327741903
]
]
]
},
"id": 1,
"properties": {
"name": "Shifton Head Office",
"address": "N/A",
"function": "1000",
"height": 14,
"year_of_construction": 2017,
"adjacency": "attached"
}
}
]
}

View File

@ -0,0 +1 @@
{"type": "FeatureCollection", "features": [{"type": "Feature", "geometry": {"type": "Polygon", "coordinates": [[[-81.36226736610345, 42.96538327741903], [-81.36232060244981, 42.9653700064552], [-81.36230203685074, 42.96533218062799], [-81.36231896772036, 42.96532755243104], [-81.36220126106349, 42.96509245874135], [-81.3617438333442, 42.965204767431615], [-81.36172317324467, 42.9651651651121], [-81.36153890966588, 42.96521432093572], [-81.3612605719971, 42.9652212961923], [-81.36143594626003, 42.96560572998295], [-81.36167659355637, 42.96554865177412], [-81.36160750096705, 42.965396296109915], [-81.36169145437769, 42.96539577872095], [-81.36169147743192, 42.96539971389088], [-81.36226606408695, 42.96539751865368], [-81.36226736610345, 42.96538327741903]]]}, "id": 1, "properties": {"name": "Shifton Head Office", "address": "N/A", "function": "1000", "height": 14, "year_of_construction": 2017, "adjacency": "attached"}}]}

View File

@ -10,6 +10,7 @@ import copy
import os
import shutil
import subprocess
from datetime import datetime
import hub.exports.building_energy.idf_helper as idf_cte
import hub.helpers.constants as cte
@ -47,6 +48,7 @@ class CercIdf(IdfBase):
_thermostat_added_to_idf = {}
def __init__(self, city, output_path, idf_file_path, idd_file_path, epw_file_path, target_buildings=None):
self._start = datetime.now()
super().__init__(city, output_path, idf_file_path, idd_file_path, epw_file_path, target_buildings)
self._add_surfaces = IdfSurfaces.add
self._add_file_schedule = IdfFileSchedule.add
@ -231,6 +233,7 @@ class CercIdf(IdfBase):
# Merge files
self._merge_files()
self._add_outputs()
print(f'{len(self._city.buildings)} buildings export completed in: {datetime.now() - self._start}')
@property
def _energy_plus(self):
@ -245,4 +248,5 @@ class CercIdf(IdfBase):
'--readvars',
'--output-prefix', f'{self._city.name}_',
self._output_file_path]
print(cmd)
subprocess.run(cmd, cwd=self._output_path)

View File

@ -169,7 +169,7 @@ class EnergyAde:
def _building_geometry(self, building, building_dic, city):
building_dic['bldg:Building']['bldg:function'] = building.function
building_dic['bldg:Building']['bldg:usage'] = building.usages
building_dic['bldg:Building']['bldg:usage'] = building.usages_percentage
building_dic['bldg:Building']['bldg:yearOfConstruction'] = building.year_of_construction
building_dic['bldg:Building']['bldg:roofType'] = building.roof_type
building_dic['bldg:Building']['bldg:measuredHeight'] = {

View File

@ -8,12 +8,10 @@ Code contributors: Pilar Monsalvete Alvarez de Uribarri pilar.monsalvete@concord
"""
import copy
import datetime
import shutil
import subprocess
import glob
import os
from pathlib import Path
from geomeppy import IDF
import hub.helpers.constants as cte
from hub.city_model_structure.attributes.schedule import Schedule
from hub.city_model_structure.building_demand.thermal_zone import ThermalZone
@ -531,7 +529,6 @@ class Idf:
self._remove_sizing_periods()
self._rename_building(self._city.name)
self._lod = self._city.level_of_detail.geometry
is_target = False
for building in self._city.buildings:
is_target = building.name in self._target_buildings or building.name in self._adjacent_buildings
for internal_zone in building.internal_zones:
@ -659,20 +656,12 @@ class Idf:
self._add_surfaces(building, building.name)
return self._idf
@property
def _energy_plus(self):
return shutil.which('energyplus')
def run(self):
cmd = [self._energy_plus,
'--weather', self._epw_file_path,
'--output-directory', self._output_path,
'--idd', self._idd_file_path,
'--expandobjects',
'--readvars',
'--output-prefix', f'{self._city.name}_',
self._idf_file_path]
subprocess.run(cmd, cwd=self._output_path)
"""
Start the energy plus simulation
"""
self._idf.run(expandobjects=False, readvars=True, output_directory=self._output_path,
output_prefix=f'{self._city.name}_')
def _add_block(self, building):
_points = self._matrix_to_2d_list(building.foot_print.coordinates)

View File

@ -77,8 +77,8 @@ class CesiumjsTileset:
'function': {
'type': 'STRING'
},
'usages': {
'type': 'LIST'
'usages_percentage': {
'type': 'STRING'
}
}
}
@ -146,7 +146,7 @@ class CesiumjsTileset:
'max_height': building.max_height,
'year_of_construction': building.year_of_construction,
'function': building.function,
'usages': building.usages
'usages_percentage': building.usages
}
},
'content': {

View File

@ -58,7 +58,8 @@ class ConstructionHelper:
'Boucherville': '6',
'Mascouche': '6',
'Saint-Leonard': '6',
'La Prairie': '6'
'La Prairie': '6',
'London': '6'
}
_reference_city_to_israel_climate_zone = {

View File

@ -20,7 +20,7 @@ class EnergyPlus:
header_parts = header.split(':')
building_name = header_parts[0]
variable = ':'.join(header_parts[1:]).strip() # concat the rest and ensure that : it's reintroduced just in case
if variable == '':
if variable is '':
continue
if building_name not in self._summary_variables:
self._building_energy_demands[variable] = [] # initialize the list of variables

View File

@ -9,7 +9,6 @@ import datetime
import logging
from sqlalchemy import Column, Integer, String, Sequence, ForeignKey, Float
from sqlalchemy.dialects.postgresql import JSON
from sqlalchemy import DateTime
from hub.city_model_structure.building import Building
@ -28,7 +27,7 @@ class CityObject(Models):
type = Column(String, nullable=False)
year_of_construction = Column(Integer, nullable=True)
function = Column(String, nullable=True)
usage = Column(JSON, nullable=True)
usage = Column(String, nullable=True)
volume = Column(Float, nullable=False)
area = Column(Float, nullable=False)
total_heating_area = Column(Float, nullable=False)
@ -47,7 +46,7 @@ class CityObject(Models):
self.type = building.type
self.year_of_construction = building.year_of_construction
self.function = building.function
self.usage = building.usages
self.usage = building.usages_percentage
self.volume = building.volume
self.area = building.floor_area
self.roof_area = sum(roof.solid_polygon.area for roof in building.roofs)

View File

@ -1,4 +1,4 @@
"""
Hub version number
"""
__version__ = '0.3.0.5'
__version__ = '0.3.0.0'

21
main.py Normal file
View File

@ -0,0 +1,21 @@
from hub.imports.geometry_factory import GeometryFactory
from hub.helpers.dictionaries import Dictionaries
from hub.imports.construction_factory import ConstructionFactory
from hub.imports.results_factory import ResultFactory
from hub.exports.exports_factory import ExportsFactory
import subprocess
from pathlib import Path
from hub.imports.weather_factory import WeatherFactory
input_file = "data/test_updated.geojson"
city = GeometryFactory(
"geojson",
input_file,
height_field="height",
year_of_construction_field="year_of_construction",
function_field="function",
function_to_hub=Dictionaries().montreal_function_to_hub_function).city
ConstructionFactory('nrcan', city).enrich()
print('done')

View File

@ -1,5 +1,5 @@
xmltodict
numpy
numpy==1.26.4
trimesh[all]
pyproj
pandas

View File

@ -59,7 +59,6 @@ setup(
'hub.exports',
'hub.exports.building_energy',
'hub.exports.building_energy.idf_files',
'hub.exports.building_energy.idf_helper',
'hub.exports.building_energy.insel',
'hub.exports.energy_systems',
'hub.exports.formats',

46
shapely_test.py Normal file
View File

@ -0,0 +1,46 @@
import json
from shapely.geometry import shape, mapping, MultiPolygon, Polygon
from shapely.ops import unary_union
from pathlib import Path
def enforce_right_hand_rule(geojson):
for feature in geojson['features']:
geometry = shape(feature['geometry'])
if isinstance(geometry, Polygon):
if not geometry.exterior.is_ccw:
geometry = Polygon(geometry.exterior.coords[::-1], [interior.coords[::-1] for interior in geometry.interiors])
elif isinstance(geometry, MultiPolygon):
new_polygons = []
for polygon in geometry.geoms: # Use .geoms to iterate over the individual polygons
if not polygon.exterior.is_ccw:
polygon = Polygon(polygon.exterior.coords[::-1], [interior.coords[::-1] for interior in polygon.interiors])
new_polygons.append(polygon)
geometry = MultiPolygon(new_polygons)
feature['geometry'] = mapping(geometry)
return geojson
data_path = Path(__file__).parent.parent / 'hub/data' # Adjust this path as needed
# Load the GeoJSON file
input_filepath = data_path / 'test.geojson'
output_filepath = data_path / 'test_updated.geojson'
with open(input_filepath, 'r') as f:
geojson_data = json.load(f)
# Iterate through the features and convert MultiPolygons to Polygons
for feature in geojson_data['features']:
geom = shape(feature['geometry'])
if isinstance(geom, MultiPolygon):
# Merge the polygons into a single polygon
merged_polygon = unary_union(geom)
# Convert the merged polygon back to GeoJSON format
feature['geometry'] = mapping(merged_polygon)
# Enforce the right-hand rule
geojson_data = enforce_right_hand_rule(geojson_data)
# Save the updated GeoJSON file
with open(output_filepath, 'w') as f:
json.dump(geojson_data, f)

View File

@ -144,8 +144,7 @@ class TestExports(TestCase):
UsageFactory('nrcan', city).enrich()
WeatherFactory('epw', city).enrich()
try:
_idf = EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
_idf.run()
idf = EnergyBuildingsExportsFactory('idf', city, self._output_path).export()
except Exception:
self.fail("Idf ExportsFactory raised ExceptionType unexpectedly!")