New panel to manage solar analysis setup (location, date, and time)

This commit is contained in:
Dion Moult
2024-07-16 23:12:55 +10:00
parent 41065f77d1
commit 7fc7a73db8
7 changed files with 237 additions and 50 deletions
@@ -169,6 +169,7 @@ classes = [
# Services and systems
ui.BIM_PT_tab_services,
ui.BIM_PT_tab_zones,
ui.BIM_PT_tab_solar_analysis,
# Structural analysis
ui.BIM_PT_tab_structural,
# Construction scheduling
@@ -119,16 +119,11 @@ class GeoreferenceData:
def true_north(cls):
ifc = tool.Ifc.get()
true_north = {}
if ifc.schema == "IFC2X3":
return
for context in ifc.by_type("IfcGeometricRepresentationContext", include_subtypes=False):
if not context.TrueNorth:
continue
true_north = context.TrueNorth.DirectionRatios
break
return true_north
@classmethod
@@ -22,13 +22,19 @@ from . import ui, prop, operator
classes = (
operator.ExportOBJ,
operator.ImportLatLong,
operator.ImportTrueNorth,
operator.RadianceRender,
prop.BIMSolarProperties,
prop.RadianceExporterProperties,
ui.BIM_PT_radiance_exporter,
ui.BIM_PT_solar,
)
def register():
bpy.types.Scene.radiance_exporter_properties = bpy.props.PointerProperty(type=prop.RadianceExporterProperties)
bpy.types.Scene.BIMSolarProperties = bpy.props.PointerProperty(type=prop.BIMSolarProperties)
def unregister():
del bpy.types.Scene.radiance_exporter_properties
del bpy.types.Scene.radiance_exporter_properties
del bpy.types.Scene.BIMSolarProperties
@@ -17,17 +17,17 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import os
import pyradiance as pr
import bpy
from pathlib import Path
from typing import Union, Optional, Sequence
import json
import shutil
import multiprocessing
import pyradiance as pr
import ifcopenshell
import ifcopenshell.geom
import multiprocessing
import shutil
import blenderbim.tool as tool
from pathlib import Path
from typing import Union
from blenderbim.bim.module.light.data import SolarData
@@ -229,4 +229,43 @@ def save_obj2mesh_output(inp: Union[bytes, str, Path], output_file: str, **kwarg
with open(output_file, 'wb') as f:
f.write(output_bytes)
return output_file
return output_file
class ImportTrueNorth(bpy.types.Operator):
bl_idname = "bim.import_true_north"
bl_label = "Import True North"
bl_description = "Imports the True North from your IFC geometric context"
bl_options = {"REGISTER", "UNDO"}
@classmethod
def poll(cls, context):
if not tool.Ifc.get():
return False
if not SolarData.is_loaded:
SolarData.load()
return SolarData.data["true_north"] is not None
def execute(self, context):
props = context.scene.BIMSolarProperties
for context in tool.Ifc.get().by_type("IfcGeometricRepresentationContext", include_subtypes=False):
if not context.TrueNorth:
continue
value = context.TrueNorth.DirectionRatios
props.true_north = ifcopenshell.util.geolocation.yaxis2angle(*value[:2])
return {"FINISHED"}
class ImportLatLong(bpy.types.Operator):
bl_idname = "bim.import_lat_long"
bl_label = "Import Latitude / Longitude"
bl_description = "Imports the latitude / longitude from an IfcSite"
bl_options = {"REGISTER", "UNDO"}
def execute(self, context):
props = context.scene.BIMSolarProperties
site = tool.Ifc.get().by_id(int(props.sites))
if site.RefLatitude and site.RefLongitude:
props.latitude = ifcopenshell.util.geolocation.dms2dd(*site.RefLatitude)
props.longitude = ifcopenshell.util.geolocation.dms2dd(*site.RefLongitude)
return {"FINISHED"}
@@ -17,8 +17,53 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from bpy.props import IntProperty, StringProperty, EnumProperty
import pytz
import tzfpy
import datetime
from math import radians
from bpy.props import IntProperty, StringProperty, EnumProperty, FloatProperty
from bpy.types import PropertyGroup
from blenderbim.bim.module.light.data import SolarData
def get_sites(self, context):
if not SolarData.is_loaded:
SolarData.load()
return SolarData.data["sites"]
def update_latlong(self, context):
sun_props = context.scene.sun_pos_properties
sun_props.latitude = self.latitude
sun_props.longitude = self.longitude
update_sun_path()
def update_hourminute(self, context):
sun_props = context.scene.sun_pos_properties
sun_props.time = self.hour + (self.minute / 60)
update_sun_path()
def update_date(self, context):
update_sun_path()
def update_true_north(self, context):
sun_props = context.scene.sun_pos_properties
# Preserve IFC sign convention
sun_props.north_offset = radians(self.true_north * -1)
def update_sun_path():
props = bpy.context.scene.BIMSolarProperties
sun_props = bpy.context.scene.sun_pos_properties
timezone = pytz.timezone(tzfpy.get_tz(props.longitude, props.latitude))
dt = datetime.datetime(datetime.datetime.now(datetime.UTC).year, int(props.month), props.date, props.hour, props.minute)
local_time = timezone.localize(dt, is_dst=None)
sun_props.use_daylight_savings = bool(local_time.dst())
sun_props.UTC_zone = local_time.utcoffset().total_seconds() / 3600
class RadianceExporterProperties(PropertyGroup):
@@ -26,25 +71,14 @@ class RadianceExporterProperties(PropertyGroup):
if self.json_file_path:
self.json_file_path = bpy.path.abspath(self.json_file_path)
radiance_resolution_x: IntProperty(
name="X",
description="Horizontal resolution of the output image",
default=1920,
min=1
name="X", description="Horizontal resolution of the output image", default=1920, min=1
)
radiance_resolution_y: IntProperty(
name="Y",
description="Vertical resolution of the output image",
default=1080,
min=1
name="Y", description="Vertical resolution of the output image", default=1080, min=1
)
ifc_file_name: StringProperty(
name="IFC File Name",
description="Name of the IFC file to use (without .ifc extension)",
default=""
name="IFC File Name", description="Name of the IFC file to use (without .ifc extension)", default=""
)
json_file_path: StringProperty(
@@ -52,36 +86,61 @@ class RadianceExporterProperties(PropertyGroup):
description="Path to the JSON file",
default="",
subtype="FILE_PATH",
update=lambda self, context: self.update_json_file_path(context)
update=lambda self, context: self.update_json_file_path(context),
)
radiance_quality: EnumProperty(
name="Quality",
description="Radiance rendering quality",
items=[
('LOW', "Low", "Low quality"),
('MEDIUM', "Medium", "Medium quality"),
('HIGH', "High", "High quality")
],
default='MEDIUM'
items=[("LOW", "Low", "Low quality"), ("MEDIUM", "Medium", "Medium quality"), ("HIGH", "High", "High quality")],
default="MEDIUM",
)
radiance_detail: EnumProperty(
name="Detail",
description="Radiance rendering detail",
items=[
('LOW', "Low", "Low detail"),
('MEDIUM', "Medium", "Medium detail"),
('HIGH', "High", "High detail")
],
default='MEDIUM'
items=[("LOW", "Low", "Low detail"), ("MEDIUM", "Medium", "Medium detail"), ("HIGH", "High", "High detail")],
default="MEDIUM",
)
radiance_variability: EnumProperty(
name="Variability",
description="Radiance rendering variability",
items=[
('LOW', "Low", "Low variability"),
('MEDIUM', "Medium", "Medium variability"),
('HIGH', "High", "High variability")
("LOW", "Low", "Low variability"),
("MEDIUM", "Medium", "Medium variability"),
("HIGH", "High", "High variability"),
],
default='MEDIUM'
)
default="MEDIUM",
)
class BIMSolarProperties(PropertyGroup):
sites: EnumProperty(items=get_sites, name="Sites")
latitude: FloatProperty(name="Latitude", min=-90, max=90, update=update_latlong)
longitude: FloatProperty(name="Longitude", min=-180, max=180, update=update_latlong)
true_north: FloatProperty(name="True North", min=-180, max=180, update=update_true_north)
month: EnumProperty(
name="Month",
items=[
(str(i + 1), m, "")
for i, m in enumerate(
(
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December",
)
)
],
update=update_date
)
date: IntProperty(name="Date", min=1, max=31, default=1, update=update_date)
hour: IntProperty(name="Hour", min=0, max=23, update=update_hourminute)
minute: IntProperty(name="Minute", min=0, max=59, update=update_hourminute)
@@ -17,6 +17,9 @@
# along with BlenderBIM Add-on. If not, see <http://www.gnu.org/licenses/>.
import bpy
from blenderbim.bim.module.light.data import SolarData
from sun_position.sun_calc import format_time, format_hms, sun
class BIM_PT_radiance_exporter(bpy.types.Panel):
"""Creates a Panel in the render properties window"""
@@ -61,4 +64,75 @@ class BIM_PT_radiance_exporter(bpy.types.Panel):
row.operator("export_scene.radiance", text="Export to OBJ")
row = layout.row()
row.operator("render_scene.radiance", text="Radiance Render")
row.operator("render_scene.radiance", text="Radiance Render")
class BIM_PT_solar(bpy.types.Panel):
"""Creates a Panel in the render properties window"""
bl_label = "Solar Access / Shadow"
bl_idname = "BIM_PT_solar"
bl_space_type = 'PROPERTIES'
bl_region_type = 'WINDOW'
bl_context = "scene"
bl_parent_id = "BIM_PT_tab_solar_analysis"
def draw(self, context):
if not SolarData.is_loaded:
SolarData.load()
props = context.scene.BIMSolarProperties
if SolarData.data["sites"]:
row = self.layout.row(align=True)
row.prop(props, "sites")
row.operator("bim.import_lat_long", icon="IMPORT", text="")
else:
row = self.layout.row(align=True)
row.label(text="No Sites With Lat/Longs Found", icon="ERROR")
sun_props = context.scene.sun_pos_properties
row = self.layout.row()
row.prop(sun_props, "coordinates", icon='URL')
row = self.layout.row(align=True)
row.prop(props, "latitude")
row.prop(props, "longitude")
row = self.layout.row(align=True)
row.prop(props, "true_north")
row.prop(sun_props, "show_north", icon="HIDE_ON" if sun_props.show_north else "HIDE_OFF", text="")
if SolarData.data["true_north"] is not None:
row.operator("bim.import_true_north", icon="IMPORT", text="")
row = self.layout.row(align=True)
row.prop(props, "month", text="")
row.prop(props, "date")
row = self.layout.row(align=True)
row.prop(props, "hour")
row.prop(props, "minute")
row = self.layout.row(align=True)
box = row.box()
local_time = format_time(sun_props.time, sun_props.use_daylight_savings)
utc_time = format_time(sun_props.time, sun_props.use_daylight_savings, sun_props.UTC_zone)
row2 = box.row()
row2.label(text=f"Local Time: {local_time}")
row2 = box.row()
row2.label(text=f"UTC Time: {utc_time}")
box = row.box()
sunrise = format_hms(sun.sunrise)
sunset = format_hms(sun.sunset)
row2 = box.row()
row2.label(text=f"Sunrise: {sunrise}")
row2 = box.row()
row2.label(text=f"Sunset: {sunset}")
row = self.layout.row(align=True)
row.prop(sun_props, "sun_distance", text="Sun Path Size")
row.prop(sun_props, "show_analemmas", icon="HIDE_ON" if sun_props.show_analemmas else "HIDE_OFF", text="")
+14 -1
View File
@@ -373,7 +373,6 @@ class BIM_ADDON_preferences(bpy.types.AddonPreferences):
row.prop(context.scene.DocProperties, "magic_font_scale")
row = self.layout.row()
row.prop(context.scene.DocProperties, "imperial_precision")
# Scene panel groups
@@ -709,6 +708,20 @@ class BIM_PT_tab_zones(Panel):
pass
class BIM_PT_tab_solar_analysis(Panel):
bl_label = "Solar Analysis"
bl_space_type = "PROPERTIES"
bl_region_type = "WINDOW"
bl_context = "scene"
@classmethod
def poll(cls, context):
return tool.Blender.is_tab(context, "SERVICES") and tool.Ifc.get()
def draw(self, context):
pass
class BIM_PT_tab_quality_control(Panel):
bl_label = "Quality Control"
bl_space_type = "PROPERTIES"