Ok, I finally got around to editing this. Below is the result, with my custom changes marked.
import math
try:
from ladybug_geometry.geometry2d import Vector2D
from ladybug_geometry.geometry3d import Point3D
except ImportError as e:
raise ImportError('\nFailed to import ladybug_geometry:\n\t{}'.format(e))
try:
from ladybug.datatype.speed import WindSpeed
from ladybug.datacollection import BaseCollection
from ladybug.graphic import GraphicContainer
from ladybug.windprofile import WindProfile
from ladybug.windrose import WindRose
except ImportError as e:
raise ImportError('\nFailed to import ladybug:\n\t{}'.format(e))
try:
from ladybug_rhino.togeometry import to_point3d, to_vector2d
from ladybug_rhino.fromgeometry import from_point3d, from_vector3d, \
from_mesh3d, from_linesegment3d, from_polyline3d
from ladybug_rhino.text import text_objects
from ladybug_rhino.fromobjects import legend_objects
from ladybug_rhino.grasshopper import all_required_inputs, objectify_output
from ladybug_rhino.config import conversion_to_meters, units_system
except ImportError as e:
raise ImportError('\nFailed to import ladybug_rhino:\n\t{}'.format(e))
# dictionary to map integers to terrain types
TERRAIN_TYPES = {
'0': 'city',
'1': 'suburban',
'2': 'country',
'3': 'water',
'city': 'city',
'suburban': 'suburban',
'country': 'country',
'water': 'water'
}
# dictionary to map integers to cardinal directions
### ------ Custom modification (Start) ------ ###
DIR_TEXT = {
'0': 'N', '1': 'NNE', '2': 'NE', '3': 'ENE',
'4': 'E', '5': 'ESE', '6': 'SE', '7': 'SSE',
'8': 'S', '9': 'SSW', '10': 'SW', '11': 'WSW',
'12': 'W', '13': 'WNW','14': 'NW', '15': 'NNW',
'N': 'N', 'NNE': 'NNE', 'NE': 'NE', 'ENE': 'ENE',
'E': 'E', 'ESE': 'ESE', 'SE': 'SE', 'SSE': 'SSE',
'S': 'S', 'SSW': 'SSW', 'SW': 'SW', 'WSW': 'WSW',
'W': 'W', 'WNW': 'WNW', 'NW': 'NW', 'NNW': 'NNW'
}
DIR_RANGE = {
'N': (348.75, 11.25), 'NNE': (11.25, 33.75), 'NE': (33.75, 56.25), 'ENE': (56.25, 78.75),
'E': (78.75, 101.25), 'ESE': (101.25, 123.75), 'SE': (123.75, 146.25), 'SSE': (146.25, 168.75),
'S': (168.75, 191.25), 'SSW': (191.25, 213.75), 'SW': (213.75, 236.25), 'WSW': (236.25, 258.75),
'W': (258.75, 281.25), 'WNW': (281.25, 303.75), 'NW': (303.75, 326.25), 'NNW': (326.25, 348.75)
}
### ------ Custom modification (End) ------ ###
if all_required_inputs(ghenv.Component):
# interpret the model units
scale_fac = 1 / conversion_to_meters()
unit_sys = units_system()
# set default values
if north_ is not None: # process the north_
try:
north_ = math.degrees(
to_vector2d(north_).angle_clockwise(Vector2D(0, 1)))
except AttributeError: # north angle instead of vector
north_ = float(north_)
else:
north_ = 0
_terrain_ = 'city' if _terrain_ is None else TERRAIN_TYPES[_terrain_.lower()]
_met_height_ = 10 if _met_height_ is None else _met_height_
_met_terrain_ = 'country' if _met_terrain_ is None \
else TERRAIN_TYPES[_met_terrain_.lower()]
log_law_ = False if log_law_ is None else log_law_
bp = Point3D(0, 0, 0) if _base_pt_ is None else to_point3d(_base_pt_)
if unit_sys in ('Feet', 'Inches'):
_profile_height_ = 30.48 if _profile_height_ is None else _profile_height_
_vec_spacing_ = 3.048 if _vec_spacing_ is None else _vec_spacing_
feet_labels = True
else:
_profile_height_ = 30 if _profile_height_ is None else _profile_height_
_vec_spacing_ = 2 if _vec_spacing_ is None else _vec_spacing_
feet_labels = False
_vec_scale_ = 5 if _vec_scale_ is None else _vec_scale_
len_d, height_d = _vec_scale_, _vec_scale_ / 5
# process the data collections and wind direction if reuqested
if isinstance(met_wind_dir_, BaseCollection):
if profile_dir_ is not None:
dir_label = DIR_TEXT[profile_dir_]
dir_txt = '\nWind Direction = {}'.format(dir_label)
else: # get the prevailing wind direction
### ------ Custom modification (Start) ------ ###
prev_dir = WindRose.prevailing_direction_from_data(met_wind_dir_, 16)[0]
dir_label = DIR_TEXT[str(int(prev_dir / 22.5))]
dir_txt = '\nPrevailing Wind Direction = {}'.format(dir_label)
dir_range = DIR_RANGE[dir_label]
met_wd = sum(dir_range) / 2 if dir_range != (348.75, 11.25) else 0
if isinstance(_met_wind_vel, BaseCollection):
lw, hg = dir_range
if dir_range == (348.75, 11.25):
pattern = [lw < v or v < hg for v in met_wind_dir_]
else:
pattern = [lw < v < hg for v in met_wind_dir_]
### ------ Custom modification (End) ------ ###
_met_wind_vel = _met_wind_vel.filter_by_pattern(pattern)
else:
met_wd = float(met_wind_dir_) if met_wind_dir_ is not None else None
dir_txt = '\nWind Direction = {} degrees'.format(int(met_wd)) \
if met_wind_dir_ is not None else ''
if isinstance(_met_wind_vel, BaseCollection):
met_ws = _met_wind_vel.average
head = _met_wind_vel.header
loc_txt = '{} Terrain'.format(_terrain_.title()) if 'city' not in head.metadata \
else '{} - {} Terrain'.format(head.metadata['city'], _terrain_.title())
title_txt = '{}{}\nAverage Met Wind Speed = {} m/s'.format(
loc_txt, dir_txt, round(met_ws, 2))
else:
met_ws = float(_met_wind_vel)
title_txt = '{} Terrain{}\nMeteorological Speed = {} m/s'.format(
_terrain_.title(), dir_txt, round(met_ws, 2))
if met_wd is not None and north_ != 0:
met_wd = met_wd - north_
### ------ Custom modification (Start) ------ ###
# Force flat 2D mode (ignore wind direction)
if _flat_2d_:
met_wd = None
### ------ Custom modification (End) ------ ###
# create the wind profile and the graphic container
profile = WindProfile(_terrain_, _met_terrain_, _met_height_, log_law_)
_, mesh_ars, wind_speeds, wind_vectors, anchor_pts = \
profile.mesh_arrow_profile(
met_ws, _profile_height_, _vec_spacing_, met_wd, bp,
len_d, height_d, scale_fac)
profile_polyline, _, _ = profile.profile_polyline3d(
met_ws, _profile_height_, 0.1,
met_wd, bp, len_d, scale_fac)
max_speed = round(wind_speeds[-1]) if _max_speed_ is None else _max_speed_
max_pt = Point3D(bp.x + ((max_speed + 2) * len_d * scale_fac),
bp.y + (30 * scale_fac), bp.z)
graphic = GraphicContainer(
wind_speeds, bp, max_pt, legend_par_, WindSpeed(), 'm/s')
# draw profile geometry and mesh arrows in the scene
mesh_arrows = []
for mesh, col in zip(mesh_ars, graphic.value_colors):
mesh.colors = [col] * len(mesh)
mesh_arrows.append(from_mesh3d(mesh))
profile_curve = from_polyline3d(profile_polyline)
# draw axes and legend in the scene
txt_h = graphic.legend_parameters.text_height
axis_line, axis_arrow, axis_ticks, text_planes, text = \
profile.speed_axis(max_speed, met_wd, bp, len_d, scale_fac, txt_h)
speed_axis = [from_linesegment3d(axis_line), from_mesh3d(axis_arrow)]
for tic in axis_ticks:
speed_axis.append(from_linesegment3d(tic))
for i, (pl, txt) in enumerate(zip(text_planes, text)):
txt_i_h = txt_h if i != len(text) - 1 else txt_h * 1.25
txt_obj = text_objects(txt, pl, txt_i_h, graphic.legend_parameters.font, 1, 0)
speed_axis.append(txt_obj)
axis_line, axis_arrow, axis_ticks, text_planes, text = \
profile.height_axis(_profile_height_, _vec_spacing_ * 2, met_wd, bp,
scale_fac, txt_h, feet_labels)
height_axis = [from_linesegment3d(axis_line), from_mesh3d(axis_arrow)]
for tic in axis_ticks:
height_axis.append(from_linesegment3d(tic))
for i, (pl, txt) in enumerate(zip(text_planes, text)):
if i != len(text) - 1:
txt_i_h, ha, va = txt_h, 2, 3
else:
txt_i_h, ha, va = txt_h * 1.25, 1, 5
txt_obj = text_objects(txt, pl, txt_i_h, graphic.legend_parameters.font, ha, va)
height_axis.append(txt_obj)
# draw the legend and the title
if graphic.legend_parameters.is_base_plane_default:
graphic.legend_parameters.base_plane = \
profile.legend_plane(max_speed, met_wd, bp, len_d, scale_fac)
legend = legend_objects(graphic.legend)
title_pl = profile.title_plane(met_wd, bp, len_d, scale_fac, txt_h)
title = text_objects(title_txt, title_pl, txt_h, graphic.legend_parameters.font, 0, 0)
# process the output lists of data
anchor_pts = [from_point3d(pt) for pt in anchor_pts]
wind_vectors = [from_vector3d(vec) for vec in wind_vectors]
wind_speeds.insert(0, 0) # insert 0 wind speed for bottom of curve
# create the output VisualizationSet arguments
vis_set = [profile, met_ws, met_wd, legend_par_, bp, _profile_height_,
_vec_spacing_, len_d, height_d, max_speed, scale_fac, feet_labels]
vis_set = objectify_output('VisualizationSet Aruments [WindProfile]', vis_set)