Skip to content

Commit

Permalink
feat(visualize): Add component for monthly chart
Browse files Browse the repository at this point in the history
This commit also fixes several other bugs that were found in the process of implementing the chart.
  • Loading branch information
chriswmackey authored and Chris Mackey committed Jun 24, 2020
1 parent 708e35c commit 4043a29
Show file tree
Hide file tree
Showing 13 changed files with 175 additions and 12 deletions.
3 changes: 2 additions & 1 deletion ladybug_grasshopper/src/LB Arithmetic Operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@

ghenv.Component.Name = "LB Arithmetic Operation"
ghenv.Component.NickName = 'ArithOp'
ghenv.Component.Message = '0.1.0'
ghenv.Component.Message = '0.1.1'
ghenv.Component.Category = 'Ladybug'
ghenv.Component.SubCategory = '1 :: Analyze Weather Data'
ghenv.Component.AdditionalHelpFromDocStrings = '2'
Expand Down Expand Up @@ -74,6 +74,7 @@

# try to replace the data collection type
try:
result = result.duplicate()
if type_:
result.header.metadata['type'] = type_
elif 'type' in result.header.metadata:
Expand Down
6 changes: 3 additions & 3 deletions ladybug_grasshopper/src/LB Degree Days.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@

ghenv.Component.Name = 'LB Degree Days'
ghenv.Component.NickName = 'HDD_CDD'
ghenv.Component.Message = '0.1.0'
ghenv.Component.Message = '0.1.1'
ghenv.Component.Category = 'Ladybug'
ghenv.Component.SubCategory = '1 :: Analyze Weather Data'
ghenv.Component.AdditionalHelpFromDocStrings = '3'
Expand Down Expand Up @@ -70,5 +70,5 @@
CoolingDegreeTime(), 'degC-hours')
hourly_cool.convert_to_unit('degC-days')

heat_deg_days = hourly_heat.total / 24.
cool_deg_days = hourly_cool.total / 24.
heat_deg_days = hourly_heat.total
cool_deg_days = hourly_cool.total
11 changes: 8 additions & 3 deletions ladybug_grasshopper/src/LB Hourly Plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,13 @@
dimension according to the data. The value input here should usually be
several times larger than the x_dim or y_dim in order to be noticable
(e.g. 100). If 0, the colored_mesh3d will simply be flat. (Default: 0).
reverse_y_: Boolean to note whether the Y-axis of the chart is reversed
If True, time over the course of the day will flow from the top of
the chart to the bottom instead of the bottom to the top.
legend_par_: An optional LegendParameter object to change the display
of the HourlyPlot (Default: None).
statement_: A conditional statement as a string (e.g. a > 25).
.
.
The variable of the first data collection should always be named 'a'
(without quotations), the variable of the second list should be
named 'b', and so on.
Expand All @@ -53,7 +56,7 @@

ghenv.Component.Name = "LB Hourly Plot"
ghenv.Component.NickName = 'HourlyPlot'
ghenv.Component.Message = '0.1.0'
ghenv.Component.Message = '0.2.0'
ghenv.Component.Category = 'Ladybug'
ghenv.Component.SubCategory = '2 :: VisualizeWeatherData'
ghenv.Component.AdditionalHelpFromDocStrings = '1'
Expand Down Expand Up @@ -94,6 +97,7 @@
_x_dim_ = _x_dim_ if _x_dim_ is not None else 1
_y_dim_ = _y_dim_ if _y_dim_ is not None else 4
_z_dim_ = _z_dim_ if _z_dim_ is not None else 0
reverse_y_ = reverse_y_ if reverse_y_ is not None else False

# set up empty lists of objects to be filled
mesh = []
Expand All @@ -109,7 +113,8 @@
lpar = None if len(legend_par_) == 0 else legend_par_[-1]

# create the hourly plot object and get the main pieces of geometry
hour_plot = HourlyPlot(data_coll, lpar, _base_pt_, _x_dim_, _y_dim_, _z_dim_)
hour_plot = HourlyPlot(data_coll, lpar, _base_pt_,
_x_dim_, _y_dim_, _z_dim_, reverse_y_)
msh = from_mesh2d(hour_plot.colored_mesh2d, _base_pt_.z) if _z_dim_ == 0 else \
from_mesh3d(hour_plot.colored_mesh3d)
mesh.append(msh)
Expand Down
3 changes: 2 additions & 1 deletion ladybug_grasshopper/src/LB Mass Arithmetic Operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@

ghenv.Component.Name = "LB Mass Arithmetic Operation"
ghenv.Component.NickName = 'MassArithOp'
ghenv.Component.Message = '0.1.1'
ghenv.Component.Message = '0.1.2'
ghenv.Component.Category = 'Ladybug'
ghenv.Component.SubCategory = '1 :: Analyze Weather Data'
ghenv.Component.AdditionalHelpFromDocStrings = '2'
Expand All @@ -57,6 +57,7 @@

# try to replace the data collection type
try:
data = data.duplicate()
if type_:
data.header.metadata['type'] = type_
elif 'type' in data.header.metadata:
Expand Down
153 changes: 153 additions & 0 deletions ladybug_grasshopper/src/LB Monthly Chart.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
# Ladybug: A Plugin for Environmental Analysis (GPL)
# This file is part of Ladybug.
#
# Copyright (c) 2020, Ladybug Tools.
# You should have received a copy of the GNU General Public License
# along with Ladybug; If not, see <http://www.gnu.org/licenses/>.
#
# @license GPL-3.0+ <http://spdx.org/licenses/GPL-3.0+>

"""
Create a chart in the Rhino scene with data organized by month.
_
Data will display as a bar chart if the input data is monthly or daily. If the
data is hourly or sub-hourly, it will be plotted with lines and/or a colored
mesh that shows the range of the data within specific percentiles.
-
Args:
_data: Data collections (eg. HourlyCollection, MonthlyCollection, or
DailyCollection), which will be used to generate the monthly chart.
_base_pt_: An optional Point3D to be used as a starting point to generate
the geometry of the chart (Default: (0, 0, 0)).
_x_dim_: An optional number to set the X dimension of each month of the
chart. (Default: 10).
_y_dim_: An optional number to set the Y dimension of the chart (Default: 40).
stack_: Boolean to note whether multiple connected monthly or daily input
_data with the same units should be stacked on top of each other.
Otherwise, all bars for monthly/daily data will be placed next to
each other. (Default: False).
percentile_: An optional number between 0 and 50 to be used for the percentile
difference from the mean that hourly data meshes display at. For example,
using 34 will generate hourly data meshes with a range of one standard
deviation from the mean. Note that this input only has significance when
the input data collections are hourly. (Default: 34)
global_title_: A text string to label the entire entire chart. It will be
displayed in the lower left of the output chart. The default will
display the metadata of the input _data.
y_axis_title_: A text string to label the Y-axis of the chart. This can
also be a list of 2 Y-axis titles if there are two different types
of data connected to _data and there are two axes labels on either
side of the chart. The default will display the data type and
units of the first (and possibly the second) data collection
connected to _data.
legend_par_: An optional LegendParameter object to change the display
of the chart (Default: None).
Returns:
report: ...
data_mesh: A list of colored meshes that represent the different input data.
These meshes will resemble a bar chart in the case of monthly or
daily data and will resemble a band between two ranges for hourly
and sub-hourly data. Multiple lists of meshes will be output for
several input data streams.
data_lines: A list of polylines that represent the different input data.
These will represent the average at each hour for hourly input data.
Multiple lists of polylines will be output for several input
data streams.
legend: Geometry representing the legend for the chart, noting which
colors correspond to which input data.
borders: A list of lines and polylines representing the axes and intervals
of the chart.
labels: A list of text objects that label the borders with month name
and the intervals of the Y-axis.
y_title: A text oject for the Y-axis title.
title: A text object for the global_title.
"""

ghenv.Component.Name = 'LB Monthly Chart'
ghenv.Component.NickName = 'MonthlyChart'
ghenv.Component.Message = '0.1.0'
ghenv.Component.Category = 'Ladybug'
ghenv.Component.SubCategory = '2 :: VisualizeWeatherData'
ghenv.Component.AdditionalHelpFromDocStrings = '1'

try:
from ladybug.monthlychart import MonthlyChart
except ImportError as e:
raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e))

try:
from ladybug_geometry.geometry2d.pointvector import Point2D
from ladybug_geometry.geometry3d.pointvector import Point3D
from ladybug_geometry.geometry3d.plane import Plane
except ImportError as e:
raise ImportError('\nFailed to import ladybug_geometry:\n\t{}'.format(e))

try:
from ladybug_rhino.config import tolerance
from ladybug_rhino.togeometry import to_point2d
from ladybug_rhino.fromgeometry import from_mesh3d, from_mesh2d, \
from_polyline2d, from_linesegment2d
from ladybug_rhino.text import text_objects
from ladybug_rhino.fromobjects import legend_objects
from ladybug_rhino.grasshopper import all_required_inputs
except ImportError as e:
raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e))


if all_required_inputs(ghenv.Component):
# set default values for the chart dimensions
z_val = _base_pt_.Z if _base_pt_ is not None else 0
z_val_tol = z_val + tolerance
_base_pt_ = to_point2d(_base_pt_) if _base_pt_ is not None else Point2D()
_x_dim_ = _x_dim_ if _x_dim_ is not None else 10
_y_dim_ = _y_dim_ if _y_dim_ is not None else 40
stack_ = stack_ if stack_ is not None else False
percentile_ = percentile_ if percentile_ is not None else 34.0
lpar = legend_par_[0] if len(legend_par_) != 0 else None

# create the monthly chart object and get the main pieces of geometry
month_chart = MonthlyChart(_data, lpar, _base_pt_, _x_dim_, _y_dim_,
stack_, percentile_)
if len(legend_par_) > 1:
if legend_par_[1].min is not None:
month_chart.set_minimum_by_index(legend_par_[1].min, 1)
if legend_par_[1].max is not None:
month_chart.set_maximum_by_index(legend_par_[1].max, 1)

# get the main pieces of geometry
d_meshes = month_chart.data_meshes
if d_meshes is not None:
data_mesh = [from_mesh2d(msh, z_val_tol) for msh in d_meshes]
d_lines = month_chart.data_polylines
if d_lines is not None:
data_lines = [from_polyline2d(lin, z_val_tol) for lin in d_lines]
borders = [from_polyline2d(month_chart.chart_border, z_val)] + \
[from_linesegment2d(line, z_val) for line in month_chart.y_axis_lines] + \
[from_linesegment2d(line, z_val_tol) for line in month_chart.month_lines]
legend = legend_objects(month_chart.legend)

# process all of the text-related outputs
title_txt = month_chart.title_text if global_title_ is None else global_title_
txt_hgt = month_chart.legend_parameters.text_height
font = month_chart.legend_parameters.font
title = text_objects(title_txt, month_chart.lower_title_location, txt_hgt, font)

# process the first y axis
y1_txt = month_chart.y_axis_title_text1 if len(y_axis_title_) == 0 else y_axis_title_[0]
y_title = text_objects(y1_txt, month_chart.y_axis_title_location1, txt_hgt, font)
label1 = [text_objects(txt, Plane(o=Point3D(pt.x, pt.y, z_val)), txt_hgt, font, 1, 0)
for txt, pt in zip(month_chart.month_labels, month_chart.month_label_points)]
label2 = [text_objects(txt, Plane(o=Point3D(pt.x, pt.y, z_val)), txt_hgt, font, 2, 3)
for txt, pt in zip(month_chart.y_axis_labels1, month_chart.y_axis_label_points1)]
labels = label1 + label2

# process the second y axis if it exists
if month_chart.y_axis_title_text2 is not None:
y2_txt = month_chart.y_axis_title_text2 if len(y_axis_title_) <= 1 else y_axis_title_[1]
y_title2 = text_objects(y2_txt, month_chart.y_axis_title_location2, txt_hgt, font)
y_title = [y_title, y_title2]
label3 = [text_objects(txt, Plane(o=Point3D(pt.x, pt.y, z_val)), txt_hgt, font, 0, 3)
for txt, pt in zip(month_chart.y_axis_labels2, month_chart.y_axis_label_points2)]
labels = labels + label3
11 changes: 7 additions & 4 deletions ladybug_grasshopper/src/LB Time Interval Operation.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@
day/month/hour and inputting 100 will give the max value of each
day/month/hour.
.
Default is set to 'average'.
Default is 'average' if the input data type is not cumulative and
'total' if the data type is not cumulative.
Returns:
daily: Daily data collection derived from the input _data and _operation_.
monthly: Monthly data collection derived from the input _data and _operation_.
Expand All @@ -48,7 +49,7 @@

ghenv.Component.Name = 'LB Time Interval Operation'
ghenv.Component.NickName = 'TimeOp'
ghenv.Component.Message = '0.1.0'
ghenv.Component.Message = '0.1.1'
ghenv.Component.Category = 'Ladybug'
ghenv.Component.SubCategory = '1 :: Analyze Weather Data'
ghenv.Component.AdditionalHelpFromDocStrings = '2'
Expand All @@ -68,8 +69,10 @@
assert isinstance(_data, HourlyDiscontinuousCollection), \
'_data must be an Hourly Data Collection.' \
' Got {}.'.format(type(_data))

if _operation_ is None or _operation_.lower() == 'average':
if _operation_ is None:
_operation_ = 'total' if _data.header.data_type.cumulative else 'average'

if _operation_.lower() == 'average':
daily = _data.average_daily()
monthly = _data.average_monthly()
mon_per_hr = _data.average_monthly_per_hour()
Expand Down
Binary file modified ladybug_grasshopper/user_objects/LB Arithmetic Operation.ghuser
Binary file not shown.
Binary file modified ladybug_grasshopper/user_objects/LB Degree Days.ghuser
Binary file not shown.
Binary file modified ladybug_grasshopper/user_objects/LB Hourly Plot.ghuser
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file modified ladybug_grasshopper/user_objects/LB Time Interval Operation.ghuser
Binary file not shown.
Binary file modified samples/outdoor_comfort.gh
Binary file not shown.

0 comments on commit 4043a29

Please sign in to comment.