Source code for ladybug_display.extension.sunpath

"""Method to draw a Sunpath as a VisualizationSet."""
from ladybug_geometry.geometry2d.pointvector import Point2D
from ladybug_geometry.geometry3d import Point3D, Plane, Polyline3D, Sphere
from ladybug.dt import Date
from ladybug.color import Color
from ladybug.legend import LegendParameters
from ladybug.compass import Compass

from ..geometry3d import DisplayPoint3D, DisplayArc3D, DisplayPolyline3D, DisplaySphere
from ..visualization import VisualizationSet, \
    ContextGeometry, AnalysisGeometry, VisualizationData
from .compass import compass_to_vis_set


[docs]def sunpath_to_vis_set( sunpath, hoys=None, data=None, legend_parameters=None, radius=100, center_point=Point3D(0, 0, 0), solar_time=False, daily=False, projection=None, sun_spheres=False): """Get a Ladybug Sunpath represented as a VisualizationSet. Args: sunpath: A Ladybug Sunpath object. hoys: An optional list of numbers between 0 and 8760 that represent the hours of the year at which the sun position will be displayed. The Ladybug AnalysisPeriod class can output a list of HOYs within a certain hour or date range. (Default: None). data: An optional list of hourly data collection objects, which will generate colored sun positions for each of the hoys. legend_parameters: An optional LegendParameter object or list of LegendParameter objects to customize the display of the data on the sun path. If a list is used, these should align with the input data (one legend parameter per data collection). radius: Number for the radius of the sun path. (Default: 100). center_point: Point3D for the center of the sun path. (Default: (0, 0, 0)). solar_time: A boolean to indicate if the sunpath should be drawn with solar time hours instead of standard or daylight time. (Default: False) daily: Boolean to note whether the sunpath should display only one daily arc for each unique day in the input hoys_ (True) or whether the output sun path geometry should be for the entire year, complete with analemmas for all sun-up hours and a daily arc for each month (False). (Default: False) projection: Optional text for the name of a projection to use from the sky dome hemisphere to the 2D plane. If None, a 3D sun path will be drawn instead of a 2D one. (Default: None) Choose from the following: * Orthographic * Stereographic sun_spheres: Boolean to note whether sun positions should be drawn as points or as fully-detailed spheres. Note that this option should only be used when there are relatively few hoys input. Anything more than 100 hoys can make the display very slow. (Default: False). Returns: A VisualizationSet with the Sunpath represented several ContextGeometries (and optionally an AnalysisGeometry if data is input). This includes these objects in the following order. - Compass -- A ContextGeometry for the Compass at the base of the sunpath. - Analemmas -- A ContextGeometry for the analemmas of the sunpath (if the daily input is False). - Daily_Arcs -- A ContextGeometry for the daily arcs across the sunpath. - Sun_Positions -- Either a ContextGeometry or an AnalysisGeometry for the sun positions (if hoys are input). The object will be an AnalysisGeometry if data is input, indicating that suns are colored with this data. """ # establish the VisualizationSet object vis_set = VisualizationSet( 'Sunpath_{}_{}'.format(int(sunpath.latitude), int(sunpath.longitude)), ()) vis_set.display_name = 'Sunpath' # add the compass to the bottom of the path center_2d = Point2D(center_point.x, center_point.y) compass = Compass(radius, center_2d, sunpath.north_angle) compass_vis = compass_to_vis_set(compass, z=center_point.z, projection=projection) vis_set.add_geometry(compass_vis[0]) # create a intersection of the input hoys and the data hoys (if provided) if data is not None and len(data) > 0 and hoys is not None and len(hoys) > 0: all_aligned = all(data[0].is_collection_aligned(d) for d in data[1:]) assert all_aligned, 'All collections input to data must be aligned for ' \ 'each Sunpath.\nGrafting the data and supplying multiple grafted ' \ '_center_pt_ can be used to view each data on its own path.' data_hoys = set(dt.hoy for dt in data[0].datetimes) hoys = list(data_hoys.intersection(set(hoys))) # get the relevant sus and datetimes suns, datetimes, moys = [], [], [] if hoys is not None and len(hoys) > 0: for hoy in hoys: sun = sunpath.calculate_sun_from_hoy(hoy, solar_time) if sun.is_during_day: suns.append(sun) datetimes.append(sun.datetime) moys.append(sun.datetime.moy) # add the daily arcs and analemmas to the visualization set original_dls = sunpath.daylight_saving_period sunpath.daylight_saving_period = None # set here so analemmas aren't messed up center_pt, z = Point2D(center_point.x, center_point.y), center_point.z if not daily: if projection is None: # draw arcs and analemmas in 3D ana_plin_1 = sunpath.hourly_analemma_polyline3d( center_point, radius, True, solar_time, 1, 6, 4) ana_plin_2 = sunpath.hourly_analemma_polyline3d( center_point, radius, True, solar_time, 7, 12, 4) analemma = [DisplayPolyline3D(pl, line_width=1) for pl in ana_plin_1] + \ [DisplayPolyline3D(pl, line_width=1, line_type='Dashed') for pl in ana_plin_2] daily_arc = sunpath.monthly_day_arc3d(center_point, radius) daily = [] for i, arc in enumerate(daily_arc): lw = 2 if (i + 1) % 6 == 0 else 1 lt = 'Continuous' if i <= 5 else 'Dashed' daily.append(DisplayArc3D(arc, line_width=lw, line_type=lt)) else: # draw arcs and analemmas in the requested projection bp = Plane(o=Point3D(0, 0, z)) ana_plin_1 = sunpath.hourly_analemma_polyline2d( projection, center_point, radius, True, solar_time, 1, 6, 4) ana_plin_2 = sunpath.hourly_analemma_polyline2d( projection, center_point, radius, True, solar_time, 7, 12, 4) analemma = \ [DisplayPolyline3D(Polyline3D.from_polyline2d(p, bp), line_width=1) for p in ana_plin_1] + \ [DisplayPolyline3D( Polyline3D.from_polyline2d(p, bp), line_width=1, line_type='Dashed') for p in ana_plin_2] daily_arc = sunpath.monthly_day_polyline2d( projection, center_point, radius, divisions=30) daily = [] for i, arc in enumerate(daily_arc): lw = 2 if (i + 1) % 6 == 0 else 1 lt = 'Continuous' if i <= 5 else 'Dashed' pline = Polyline3D.from_polyline2d(arc, bp) daily.append(DisplayPolyline3D(pline, line_width=lw, line_type=lt)) analemma_geo = ContextGeometry('Analemmas', analemma) vis_set.add_geometry(analemma_geo) else: # just draw daily arcs without the analemmas doys = set(dt.doy for dt in datetimes) dates = [Date.from_doy(doy) for doy in doys] if projection is None: daily = [] for dat in dates: d_arc = sunpath.day_arc3d(dat.month, dat.day, center_point, radius) daily.append(DisplayArc3D(d_arc, line_width=1)) else: bp = Plane(o=Point3D(0, 0, z)) daily = [] for dat in dates: d_arc = sunpath.day_polyline2d( dat.month, dat.day, projection, center_pt, radius, divisions=30) daily.append(DisplayPolyline3D( Polyline3D.from_polyline2d(d_arc, bp), line_width=1)) if len(daily) != 0: daily_geo = ContextGeometry('Daily_Arcs', daily) daily_geo.display_name = 'Daily Arcs' vis_set.add_geometry(daily_geo) sunpath.daylight_saving_period = original_dls # put back to avoid mutation # plot the sun positions as points on the sunpath if hoys is not None and len(hoys) > 0: # get Point3Ds for all of the sun positions if projection is None: sun_pts = [sun.position_3d(center_point, radius) for sun in suns] else: sun_pts = [] for sun in suns: pt2d = sun.position_2d(projection, center_point, radius) sun_pts.append(Point3D.from_point2d(pt2d, z)) if sun_spheres: sun_pts = [Sphere(pt, radius / 30) for pt in sun_pts] # plot the sun positions as either context or analysis geometry if data is not None and len(data) > 0: # plot points as context or analysis geometry (if data is connected) if isinstance(legend_parameters, LegendParameters): legend_parameters = [legend_parameters] * len(data) all_data = [] for i, dat_c in enumerate(data): l_par = legend_parameters[i] if legend_parameters is not None else None n_data = dat_c.filter_by_moys(moys) # filter data by sun-up hours v_data = VisualizationData( n_data.values, l_par, dat_c.header.data_type, dat_c.header.unit) all_data.append(v_data) sun_geo = AnalysisGeometry('Sun_Positions', sun_pts, all_data) else: # otherwise, plot the suns as context geometry orange = Color(255, 165, 0) if sun_spheres: dis_pts = [DisplaySphere(pt, color=orange) for pt in sun_pts] else: dis_pts = [DisplayPoint3D(pt, color=orange, radius=5) for pt in sun_pts] sun_geo = ContextGeometry('Sun_Positions', dis_pts) sun_geo.display_name = 'Sun Positions' vis_set.add_geometry(sun_geo) return vis_set