"""Data json schema and validation for the config file."""
from __future__ import annotations
from typing import List, Union
from pydantic import BaseModel, validator, Field, constr, conint
from pydantic.types import confloat, conlist
from .types import DataSetNames
from .legend_parameter import ColorSets, DecimalCount, Orientation
from ladybug.dt import DateTime
[docs]class Autocalculate(BaseModel):
type: constr(regex='^Autocalculate$') = 'Autocalculate'
[docs]class TextConfig(BaseModel):
"""Config for the text to be used in a legend.
This object applies to text for legend title and legend labels as well.
"""
[docs] class Config:
validate_all = True
validate_assignment = True
color: List[conint(ge=0, le=255)] = Field(
[0, 0, 0],
description='An array of three integer values representing R, G, and B values'
' for the color of text. Values from 0 to 255 are accepted.'
)
size: conint(ge=0) = Field(
0,
description='Text size in points.'
)
bold: bool = Field(
False,
description='Boolean value to indicate whether to make the text bold or not.'
)
[docs]class LegendConfig(BaseModel):
"""Config for the legend to be created from a dataset."""
[docs] class Config:
validate_all = True
validate_assignment = True
color_set: ColorSets = Field(
ColorSets.ecotect,
description='Color set to be used on data and legend. Currently, this field'
' only uses Ladybug color sets. Defaults to using ecotect colorset.'
)
reverse_color_set: bool = Field(
False,
description='Boolean value to indicate whether to reverse the color set.'
)
min: Union[Autocalculate, float] = Field(
Autocalculate(),
description='Minimum value for the legend. Also known as the lower end of the'
' legend. If min and max values are not specified, autocalculated min and max'
' values will be used from data.'
)
max: Union[Autocalculate, float] = Field(
Autocalculate(),
description='Maximum value for the legend. Also known as the higher end of the'
' legend. If min and max values are not specified, autocalculated min and max'
' values will be used from data.'
)
hide_legend: bool = Field(
False,
description='A bool value to indicate whether to show legend in the exported'
' images or not.'
)
orientation: Orientation = Field(
Orientation.vertical,
description='Choose between horizontal and vertical orientation of legend.'
)
width: confloat(ge=0.05, le=0.95) = Field(
0.05,
description=' A decimal number representing the fraction of viewport width'
' that will be used to define the width of the legend.'
)
height: confloat(ge=0.05, le=0.95) = Field(
0.45,
description='A decimal number representing the fraction of viewport height'
'that will be used to define the height of the legend.'
)
position: conlist(confloat(ge=0.05, le=0.95), min_items=2, max_items=2) = Field(
[0.9, 0.5],
description='A tuple of two decimal values. The values represent the fraction'
' of viewport width and the fraction of viewport height.'
)
color_count: Union[Autocalculate, int] = Field(
Autocalculate(),
description='An integer representing the number of colors in a legend. If not'
' specified, it defaults to the number of colors in a Ladybug color set.'
)
label_count: Union[Autocalculate, int] = Field(
Autocalculate(),
description='An integer representing the number of text labels on a legend.'
' Label count will have to be less than or equal to color count. It defaults'
' to vtk scalarbar default setting.'
)
decimal_count: DecimalCount = Field(
DecimalCount.default,
description='Controlling the number of decimals on each label of the legend.'
'Accepted values are "default", "integer", "decimal_two", and "decimal_three".'
'Defaults to VTKs default settings.'
)
preceding_labels: bool = Field(
False,
description='Boolean value to decide whether the legend title and the'
' legend labels will precede the legend or not.'
)
label_parameters: TextConfig = Field(
TextConfig(),
description='Text parameters for the labels on the legend.'
)
title_parameters: TextConfig = Field(
TextConfig(bold=True),
description='Text parameters for the title of the legend.'
)
[docs]class DataConfig(BaseModel):
"""data-config for simulation results you'd like to load on a honeybee-vtk model."""
identifier: str = Field(
description='identifier to be given to data. Example, "Daylight-Factor".'
' This identifier needs to be unique in each of the DataConfig objects'
' one introduces using a config file. Having multiple DataConfig objects with'
' same modifier will raise an error.'
)
object_type: DataSetNames = Field(
description='The name of the model object on which you would like to map this'
' data.'
)
unit: str = Field(
description=' The unit of the data being loaded.'
)
path: str = Field(
description='Valid path to the folder with result files and the json file that'
' catalogues the results.'
)
hide: bool = Field(
False,
description='Boolean value to indicate if this data should be visible in the'
' exported images or not.'
)
lower_threshold: Union[Autocalculate, float] = Field(
Autocalculate(),
description='Lower end of the threshold range for the data. Data beyond this'
' threshold will be filtered. This value is included in the thresholding'
' operation. For example, if you want the lower threshold to filter all the'
' values greater than 0 then set the value to 0.1. If not specified, the lower'
' threshold will be infinite. Defaults to None.'
)
upper_threshold: Union[Autocalculate, float] = Field(
Autocalculate(),
description='Upper end of the threshold range for the data. Data beyond this'
' threshold will be filtered. This value is included in the thresholding'
' operation. For example, if you want the threshold to filter all the Values'
' less than 1 then set the value to 0.99. If not specified, the upper threshold'
' will be infinite. Defaults to None.'
)
legend_parameters: LegendConfig = Field(
LegendConfig(),
description='Legend parameters to create legend out of the this dataset.'
)
grid_colors: List = Field(
None,
description='A list of RGB values for colors to be used to color the grids.'
' this is useful when you do not want to use the ladybug colorset defined in the'
' config.'
)
[docs] @validator('grid_colors')
def set_default_value(cls, v):
return v if v else []
[docs] @validator('legend_parameters')
def check_pos_against_width_height(cls, v: LegendConfig, values) -> LegendConfig:
id = values['identifier']
if v.width >= 0.5 and v.position[0] >= 0.5 and v.width > v.position[0]:
raise ValueError(
f'Width of legend {id}, {v.width}'
f' cannot be greater than the position of legend in X direction'
f' {v.position[0]}.'
' Either update the position in X direction or update the'
' width of the legend. \n'
)
if v.height >= 0.5 and v.position[1] >= 0.5 and v.height > v.position[1]:
raise ValueError(
f'Height of legend {id}, {v.height}'
f' cannot be greater than the position of legend in Y direction'
f' {v.position[1]}.'
' Either update the position in Y direction or update the'
' height of the legend. \n'
)
return v
[docs]class Config(BaseModel):
"""Config for simulation results you'd like to load on a honeybee-vtk model."""
data: List[DataConfig] = Field(
description='List of data-config objects that define the data to be loaded on'
' the model.'
)
[docs]class DateTimeConfig(BaseModel):
"""A DateTime object."""
month: conint(ge=1, le=12) = Field(
description='Month of the year. Accepted values are between 1 and 12 inclusive.'
)
day: conint(ge=1, le=31) = Field(
description='Month of the year. Accepted values are between 1 and 31 inclusive.'
)
hour: conint(ge=0, le=23) = Field(
description='Hour of the day. Accepted values are between 0 and 23 inclusive.'
)
[docs]class Period(BaseModel):
"""A period of time for which to generate time step images."""
date_time: conlist(DateTimeConfig, min_items=2, max_items=2) = Field(
description='A list of two DateTimConfig objects that define the start and end'
' of the period. The first DateTime object is the start of the period and'
' the second DateTime object is the end of the period.'
)
color: conlist((conint(ge=0, le=255)), min_items=3, max_items=3) = Field(
[0, 0, 0],
description='An array of three integer values representing R, G, and B values'
' for the color of text. Values from 0 to 255 are accepted.'
)
[docs]class Periods(BaseModel):
"""Config for the peridos to be used in generating time step images."""
periods: List[Period] = Field(
description='A list of Period objects that define the periods to be used in'
' generating time step images.'
)
[docs]class TimeStepConfig(BaseModel):
"""Data to be used in generating an image for the time step."""
index: int = Field(
description='The index of the time step in the time step file.'
' Example of such a file is sun-up-hours.txt'
)
hoy: float = Field(
description='The hour of the year for the time step.'
)
color: conlist((conint(ge=0, le=255)), min_items=3, max_items=3) = Field(
description='An array of three integer values representing R, G, and B values'
' for the color that will be used to color the grid in expoted time step image.'
' Values from 0 to 255 are accepted.'
)
[docs]class TimeStepDataConfig(BaseModel):
"""A list of TimeStepData objects."""
time_step_data: List[TimeStepConfig] = Field(
description='A list of TimeStepData objects that define data to be used in'
' generating an image for that timestep.'
)