Source code for ladybug_display.extension.adaptivechart

"""Method to draw an AdaptiveChart as a VisualizationSet."""
from ladybug_geometry.geometry2d import Polyline2D
from ladybug_geometry.geometry3d import Vector3D, Point3D, Plane, LineSegment3D, \
    Polyline3D, Mesh3D
from ladybug.datatype.time import Time
from ladybug.legend import LegendParameters

from ladybug_display.geometry3d import DisplayLineSegment3D, DisplayPolyline3D, \
    DisplayText3D
from ladybug_display.visualization import VisualizationSet, AnalysisGeometry, \
    VisualizationData, ContextGeometry


[docs]def adaptive_chart_to_vis_set( adaptive_chart, data=None, legend_parameters=None, z=0): """Get a Ladybug AdaptiveChart represented as a VisualizationSet. Args: adaptive_chart: A Ladybug AdaptiveChart object. data: An optional list of data collection objects, which are aligned with the prevailing and operative temperature values of the chart. and will generate additional colored AnalysisGeometries on the chart. legend_parameters: An optional LegendParameter object or list of LegendParameter objects to customize the display of the data on the adaptive chart. Note that this relates only to the data supplied as input for this method and, to customize the display of the time/frequency mesh, the AdaptiveChart's native legend_parameters should be edited. If a list is used here, this should align with the input data (one legend parameter per data collection). z: A number for the Z-coordinate to be used in translation. (Default: 0). Returns: A VisualizationSet with the adaptive chart represented several ContextGeometries and an AnalysisGeometry. This includes these objects in the following order. - Title -- A ContextGeometry for the title and border around the adaptive chart. - Prevailing_Axis -- A ContextGeometry with lines and text for the Prevailing Outdoor Temperature (X) axis of the adaptive chart. - Operative_Axis -- A ContextGeometry with lines and text for the Indoor Operative Temperature (Y) axis of the adaptive chart. - Comfort_Polygon -- A ContextGeometry with lines for the comfort polygon and neutral temperature of the adaptive chart. - Analysis_Data -- An AnalysisGeometry for the data on the adaptive chart. This will include multiple data sets if the data input is provided. """ # establish the VisualizationSet object vis_set = VisualizationSet('Adaptive_Chart', ()) vis_set.display_name = 'Adaptive Chart' # get values used throughout the translation txt_hgt = adaptive_chart.legend_parameters.text_height font = adaptive_chart.legend_parameters.font bp = Plane(o=Point3D(0, 0, z)) # add the title and border meta_i = adaptive_chart.operative_temperature.header.metadata.items() title_items = ['Adaptive Chart', 'Time [hr]'] + \ ['{}: {}'.format(k, v) for k, v in meta_i] ttl_pl = adaptive_chart.container.lower_title_location.move( Vector3D(0, -txt_hgt * 3)) if z != 0: ttl_pl = Plane(n=ttl_pl.n, o=Point3D(ttl_pl.o.x, ttl_pl.o.y, z), x=ttl_pl.x) ttl_txt = DisplayText3D( '\n'.join(title_items), ttl_pl, txt_hgt * 1.5, None, font, 'Left', 'Top') border_geo = Polyline3D.from_polyline2d( Polyline2D.from_polygon(adaptive_chart.chart_border), bp) title_objs = [ttl_txt, DisplayPolyline3D(border_geo, line_width=2)] title = ContextGeometry('Title', title_objs) vis_set.add_geometry(title) # add the prevailing temperature axis tm_pl = _plane_from_point(adaptive_chart.x_axis_location, z) temp_txt = DisplayText3D( adaptive_chart.x_axis_text, tm_pl, txt_hgt * 1.5, None, font, 'Center', 'Top') temp_geo = [temp_txt] for tl in adaptive_chart.prevailing_lines: tl_geo = LineSegment3D.from_line_segment2d(tl, z) temp_geo.append(DisplayLineSegment3D(tl_geo, line_type='Dotted')) tl_pts = adaptive_chart.prevailing_label_points for txt, pt in zip(adaptive_chart.prevailing_labels, tl_pts): t_pln = Plane(o=Point3D(pt.x, pt.y, z)) txt_obj = DisplayText3D(txt, t_pln, txt_hgt, None, font, 'Center', 'Top') temp_geo.append(txt_obj) temp_axis = ContextGeometry('Prevailing_Axis', temp_geo) temp_axis.display_name = 'Prevailing Axis' vis_set.add_geometry(temp_axis) # add the operative temperature axis op_pl = _plane_from_point(adaptive_chart.y_axis_location, z, Vector3D(0, 1)) op_txt = DisplayText3D( adaptive_chart.y_axis_text, op_pl, txt_hgt * 1.5, None, font, 'Center', 'Top') op_geo = [op_txt] for hl in adaptive_chart.operative_lines: hl_geo = LineSegment3D.from_line_segment2d(hl, z) op_geo.append(DisplayLineSegment3D(hl_geo, line_type='Dotted')) op_pts = adaptive_chart.operative_label_points for txt, pt in zip(adaptive_chart.operative_labels, op_pts): t_pln = Plane(o=Point3D(pt.x, pt.y, z)) txt_obj = DisplayText3D(txt, t_pln, txt_hgt, None, font, 'Left', 'Middle') op_geo.append(txt_obj) op_axis = ContextGeometry('Operative_Axis', op_geo) op_axis.display_name = 'Operative Axis' vis_set.add_geometry(op_axis) # add the comfort polygon poly_geo = [] neutral_geo = Polyline3D.from_polyline2d( Polyline2D.from_polygon(adaptive_chart.comfort_polygon), bp) poly_geo.append(DisplayPolyline3D(neutral_geo, line_width=3)) neutral_geo = Polyline3D.from_polyline2d(adaptive_chart.neutral_polyline, bp) poly_geo.append(DisplayPolyline3D(neutral_geo, line_width=1)) comf_poly = ContextGeometry('Comfort_Polygon', poly_geo) comf_poly.display_name = 'Comfort Polygon' vis_set.add_geometry(comf_poly) # add the analysis geometry # ensure 3D legend defaults are overridden to make the data readable l_par = adaptive_chart.legend.legend_parameters.duplicate() l_par.base_plane = l_par.base_plane l_par.segment_height = l_par.segment_height l_par.segment_width = l_par.segment_width # gather all of the visualization data sets vis_data = [VisualizationData(adaptive_chart.hour_values, l_par, Time(), 'hr')] if data is not None and len(data) != 0: if legend_parameters is None: l_pars = [LegendParameters()] * len(data) elif isinstance(legend_parameters, LegendParameters): l_pars = [legend_parameters] * len(data) else: # assume it's a list that aligns with the data l_pars = legend_parameters for dat, lp in zip(data, l_pars): # process the legend parameters lp = lp.duplicate() if lp.is_base_plane_default: lp.base_plane = l_par.base_plane if lp.is_segment_height_default: lp.segment_height = l_par.segment_height if lp.is_segment_width_default: lp.segment_width = l_par.segment_width # check to be sure the data collection aligns d_vals = dat.values _tp_values = adaptive_chart.prevailing_outdoor_temperature.values _to_values = adaptive_chart.operative_temperature.values # create a matrix with a tally of the hours for all the data base_mtx = [[[] for val in adaptive_chart._tp_category] for rh in adaptive_chart._to_category] for tp, to, val in zip(_tp_values, _to_values, d_vals): if tp < adaptive_chart._min_prevailing or \ tp > adaptive_chart._max_prevailing: continue # temperature value does not currently fit on the chart if to < adaptive_chart._min_operative or \ to > adaptive_chart._max_operative: continue # temperature value does not currently fit on the chart for y, to_cat in enumerate(adaptive_chart._to_category): if to < to_cat: break for x, tp_cat in enumerate(adaptive_chart._tp_category): if tp < tp_cat: break base_mtx[y][x].append(val) # compute average values avg_values = [sum(val_list) / len(val_list) for rh_l in base_mtx for val_list in rh_l if len(val_list) != 0] hd = dat.header vd = VisualizationData(avg_values, lp, hd.data_type, hd.unit) vis_data.append(vd) # create the analysis geometry mesh_3d = Mesh3D.from_mesh2d(adaptive_chart.colored_mesh, bp) mesh_geo = AnalysisGeometry( 'Analysis_Data', [mesh_3d], vis_data, active_data=len(vis_data) - 1) mesh_geo.display_name = 'Analysis Data' mesh_geo.display_mode = 'Surface' vis_set.add_geometry(mesh_geo) return vis_set
def _plane_from_point(point_2d, z, align_vec=Vector3D(1, 0, 0)): """Get a Plane from a Point2D. Args: point_2d: A Point2D to serve as the origin of the plane. z: The Z value for the plane origin. align_vec: A Vector3D to serve as the X-Axis of the plane. """ return Plane(o=Point3D(point_2d.x, point_2d.y, z), x=align_vec)