Source code for grafanacode.plugins.panel_plotly_windrose

# panel_plotly_windrose.py
# V0.5.0 LDO 19/10/2022: initial version
# V0.5.1 LDO 12/11/2022: refactor modules
# V0.6.1 LDO 17/12/2022: release candidate 1
'''
    grafanacode:
    class for generating ae3e-plotly windrose panels

    This class is derived from PanelPlotly.

    Mind the ``EXTRACTCHILDCLASS = True`` at the end of the file,
    This is needed for a derived class to prevent the base class method to be hidden while extracting panels.
'''

#******************************************************************************
# EXTERNAL MODULE REFERENCES
#******************************************************************************
import textwrap

from attrs           import define, field, Factory
from attr.validators import in_, instance_of, optional, deep_iterable

from grafanacode         import funcs
from grafanacode.plugins import panel_base
from grafanacode.plugins import panel_plotly

#******************************************************************************
# Panel Property classes: store panel property configuration parts
#******************************************************************************

#******************************************************************************
# Panel Class
#******************************************************************************
[docs]@define(slots=False) class PanelPlotlyWindrose(panel_plotly.PanelPlotly): ''' Dataclass encapsulating a windrose panel based on the ae3e plotly panel ''' # logging level of this item loglevel : int = 16 # attributes scriptpre = field( default='', validator=instance_of(str)) scriptpost = field( default='', validator=instance_of(str)) # message def __attrs_post_init__(self): ''' Attrs post init, print a message. Also subclass code ''' print (f' > Init Panel: {self.title} - type: PanelPlotlyWindrose ') self.script = textwrap.dedent(''' // some vars var i, j, k, dir, spd; // get data console.log(data); if (data.state!="Done") { // retuns 2 times, once "Loading", once "Done" return JSON.parse('[{"type": "barpolar"}, {"type": "barpolar"}]'); } //***** original data // direction var dir_raw = data.series[0].fields[1].values.buffer; var n_dir_raw = dir_raw.length; console.log("dir"); console.log(dir_raw); console.log(n_dir_raw); // speed var spd_raw = data.series[1].fields[1].values.buffer; var n_spd_raw = spd_raw.length; console.log("speed"); console.log(spd_raw); console.log(n_spd_raw); //***** some strings, arrays var spdtrips = [2, 4, 6, 10, 15, 20, 30, 40, 60, 80, 100]; var n_spdtrips = spdtrips.length; var dirtrips = [22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5, 361]; var n_dirtrips = dirtrips.length; let dirnames = '["Noord", "NO", "Oost", "ZO", "Zuid", "ZW", "West", "NW"]'; let spdcolors = ['"#0033ff"', '"#29b6f6"', '"#00ff00"', '"#bfff00"', '"#dce775"', '"#ffff00"', '"#ffb74d"', '"#ff8000"', '"#ff9999"', '"#ff0000"', '"#6a1b9a"']; let spdnames = ['"0<"', '"2<"', '"4<"', '"6<"', '"10<"', '"15<"', '"20<"', '"30<"', '"40<"', '"60<"', '"80<"', '"100<"']; //***** create bins from data var bins = []; // array [speed][dir] for (i = 0; i < n_spdtrips; i++){ bins.push([]); for (j = 0; j < n_dirtrips; j++){ bins[i].push(0); } } //***** Loop through data and add correct bin for (i = 0; i < n_dir_raw; i++) { dir = dir_raw[i]; spd = spd_raw[i]; loops: for (j = 0; j < n_spdtrips; j++) { if (spd < spdtrips[j]) { for (k = 0; k < n_dirtrips; k++) { if (dir < dirtrips[k]) { bins[j][k]++; break loops; } } } } } // merge first and last dir (0-22.5° and 337.5-0°) for (i = 0; i < n_spdtrips; i++) { bins[i][0] = bins[i][0] + bins[i][n_dirtrips-1]; bins[i].pop(); } // numbers to percent for (i = 0; i < n_spdtrips; i++) { for (j = 0; j < n_dirtrips - 1; j++) { bins[i][j] = bins[i][j] / n_dir_raw * 100; } } console.log("final"); console.log(n_dirtrips); console.log(bins); // format uitput var series = "["; for (i = 0; i < n_spdtrips; i++) { series = series + '{"r" : [' + bins[i] + '], "theta" : ' + dirnames + ', "name" : ' + spdnames[i] + ', "marker" : {"color" : ' + spdcolors[i] + '}, "type": "barpolar"}'; if (i < n_spdtrips-1) { series = series + ','; } else { series = series + ']'; } } var seriesjson = JSON.parse(series); console.log(seriesjson); return {data : seriesjson}; ''') self.clickscript='' self.configuration={'displayModeBar': False} self.layout={ 'font': {'color': 'white', 'size': 16}, 'legend': {'font': {'size': 16}}, 'margin': {'b': 30, 't': 30}, 'paper_bgcolor': '#141619', 'plot_bgcolor': '#141619', 'polar': { 'angularaxis': { 'color': 'lightgrey', 'direction': 'clockwise', 'rotation': 90, 'tickcolor': 'white', 'tickfont': {'color': 'white', 'size': 14}, 'ticklen': 10, 'ticks': 'outside', 'type': 'category' }, 'bargap': 3, 'barmode': 'stack', 'barnorm': 'percent', 'bgcolor': 'black', 'radialaxis': { 'angle': 45, 'autorange': True, 'color': 'lightgrey', 'dtick': 10, 'range': [0, 25.694001093848453], 'tick0': 0, 'tickangle': 90, 'tickcolor': 'white', 'tickfont': {'color': 'white', 'size': 10}, 'ticklen': 10, 'tickmode': 'linear', 'ticks': 'outside', 'ticksuffix': '%', 'type': 'linear' }, }, 'xaxis': {'autorange': True, 'type': 'date'}, 'type': 'scatter', }
[docs] def getJson(self, customjson={}, dashboard=None, panel=None): ''' Create the json to pack this panel. Parameters: customjson (dict): custom or child class json code to add Returns: dict: panel json code ''' # pylint: disable=unused-argument hjson = super().getJson() if self.scriptpre!= '': hjson['options']['script'] = self.scriptpre + '\r\n' + hjson['options']['script'] if self.scriptpost!= '': hjson['options']['script'] = hjson['options']['script'] + '\r\n' + self.scriptpre funcs.deepMerge(hjson, customjson) return hjson
#****************************************************************************** # EXTRACT FUNCTIONS : EXTRACT A CREATION SCRIPT FROM A JSON #****************************************************************************** # needed, otherwise base class method can be hidden EXTRACTCHILDCLASS = True