Source code for ladybug.datatype

# coding=utf-8
"""Ladybug data types."""
# from abc import ABCMeta, abstractmethod
import math
from .euclid import Vector3
from .dt import DateTime

PI = math.pi


[docs]class DataTypeBase(object): """Base type for data. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = ('_standard', '_value', 'datetime', 'nickname') minimum = float('-inf') maximum = float('+inf') value_type = None unitSI = None unitIP = None missing = None mute = False # print warnings for missing data def __init__(self, value, datetime=None, standard=None, nickname=None): """Init DataType.""" self.nickname = nickname self.standard = standard self.datetime = datetime self.value = value # TODO: Add support for type
[docs] @classmethod def from_json(cls, data): """Create a data point from a dictionary. Args: json_data: Data as a dictionary. { "value": A number or a string, "standard": SI/IP, "datetime": {}, // A ladybug datetime schema "nickname": A string for nickname } """ # check for value to be available assert 'value' in data, 'Required keyword "value" is missing!' if 'datetime' not in data: data['dateTime'] = {} if 'standard' not in data: data['standard'] = 'SI' if 'nickname' not in data: data['nickname'] = None datetime = DateTime.from_json(data['datetime']) return cls(data['value'], datetime, data['standard'], data['nickname'])
@property def value(self): """Get/set value.""" return self._value @value.setter def value(self, v): """Set value.""" if not self.value_type: self._value = v else: try: if self.value_type is str: _v = str(v) else: _v = self.value_type(v) except Exception: raise ValueError( "Failed to convert {} to {}".format(v, self.value_type)) else: self._value = _v self.is_in_range(_v, True) @property def standard(self): """standard SI/IP""" return self._standard @standard.setter def standard(self, value): value = value or 'SI' if value not in ('SI', 'IP'): raise ValueError('Invalid standard: {}. Choose SI or IP.'.format(value)) self._standard = value @property def unit(self): """Return current Unit.""" if not self.standard: return None return self.unitSI if self.standard == 'SI' else \ self.unitIP @property def to_ip(self): """Write a static method that converts a value from SI to IP.""" raise NotImplementedError( 'to_ip is not implemented to %s' % self.__class__.__name__ ) @property def to_si(self): """Write a static method that converts a value from IP to SI.""" raise NotImplementedError( 'to_si is not implemented to %s' % self.__class__.__name__ )
[docs] def convert_to_si(self): """Change value to SI. To only get the value in SI use to_si property. """ if not self.standard: raise Exception("Failed to convert to SI. " "Current system is unknown.") if self.standard == 'SI': return else: self.standard = 'SI' self.value = self.to_si
[docs] def convert_to_ip(self): """change value to IP. To only get the value in IP use to_ip property. """ if not self.standard: raise Exception("Failed to convert to IP. " "Current system is unknown.") if self.standard == 'IP': return else: self.standard = 'IP' self.value = self.to_ip
def _is_missed_data(self, v): """Check if the value is missed data.""" _isMissed = v == self.missing if not self.mute and _isMissed: print("{} value is missing!".format( self.__class__.__name__ if not self.nickname else self.nickname )) return _isMissed
[docs] def is_in_range(self, value, raise_exception=False): """check if the value is in range.""" if not self.standard: return True if self._is_missed_data(self): return True _isInRange = self.minimum <= value <= self.maximum \ if self.standard == 'SI' \ else self.to_ip(self.minimum) <= value <= self.to_ip(self.maximum) if _isInRange or not raise_exception: return _isInRange else: raise ValueError( '{0} should be between {1} and {2}'.format( self.__class__.__name__, self.minimum, self.maximum ) )
[docs] def to_json(self): "Get data point as a json object" return { 'value': self.value, 'datetime': self.datetime.to_json() if self.datetime else {}, 'standard': self.standard, 'nickname': self.nickname, 'type': self.__class__.__name__ }
[docs] def ToString(self): """Overwrite .NET representation.""" return self.__repr__()
def __str__(self): """Return full information.""" # Temperature: 21C return "{}{}{}".format( self.__repr__(), self.unit if self.unit else "", " at %s" % self.datetime if self.datetime else "") def __repr__(self): """Return string representation.""" return str(self.value) def __int__(self): """Return integer value.""" try: return int(self.value) except ValueError: raise ValueError( "Failed to convert {} to an integer.".format(self.value) ) def __float__(self): """Return float value.""" return float(self.value) def __eq__(self, other): return self.value == float(other) def __ne__(self, other): return self.value != float(other) def __lt__(self, other): return self.value < other def __gt__(self, other): return self.value > other def __le__(self, other): return self.value <= other def __ge__(self, other): return self.value >= other def __add__(self, other): return self.value + other def __sub__(self, other): return self.value - other def __mul__(self, other): return self.value * other def __floordiv__(self, other): return self.value // other def __div__(self, other): return self.value / other def __mod__(self, other): return self.value % other def __pow__(self, other): return self.value ** other def __radd__(self, other): return self.__add__(other) def __rsub__(self, other): return other - self.value def __rmul__(self, other): return self.__mul__(other) def __rfloordiv__(self, other): return other // self.value def __rdiv__(self, other): return other / self.value def __rmod__(self, other): return other % self.value def __rpow__(self, other): return other ** self.value
[docs]class DataPoint(DataTypeBase): """A single Ladybug data point. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = float('-inf') maximum = float('+inf') value_type = None unitSI = None unitIP = None missing = None def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataTypeBase.__init__(self, value, datetime, standard, nickname)
[docs] @classmethod def from_data(cls, value): """Try to create a DataPoint from input data.""" if hasattr(value, 'isData'): return value try: return cls(value) except Exception as e: raise ValueError( "Failed to create a DataPoint from %s!\n%s" % (value, e))
@property def isDataPoint(self): """Return True if Ladybug data point.""" return True
# TODO: Add methods for toKelvin for temperature # TODO: Add toAtmospheres, toBars, toPsi, toInWater for pressure
[docs]class Temperature(DataPoint): """Base type for temperature. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = float('-inf') maximum = float('inf') missing = 99.9 value_type = float unitSI = 'C' unitIP = 'F' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname) @property def to_ip(self): """Return the value in F assuming input value is in C.""" return self.value * 9 / 5 + 32 @property def to_si(self): """Return the value in C assuming input value is in F.""" return (self.value - 32) * 5 / 9
[docs]class DryBulbTemperature(Temperature): """Dry bulb temperature. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = -70 maximum = 70
[docs]class DewPointTemperature(Temperature): """Dew point temperature. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = -70 maximum = 70
[docs]class RelativeHumidity(DataPoint): """Relative humidity. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 maximum = 100 missing = 999 value_type = int unitSI = '%' unitIP = '%' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname) @property def to_ip(self): """Return the value in IP.""" return self.value @property def to_si(self): """Return the value in SI.""" return self.value
[docs]class Pressure(DataPoint): """Atmospheric Pressure. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 31000 maximum = 120000 missing = 999999 value_type = int unitSI = 'Pa' unitIP = 'in' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname) @property def to_ip(self): """Return the value in IP.""" return self.value * 0.0002953 @property def to_si(self): """Return the value in SI.""" return self.value / 0.0002953
[docs]class Radiation(DataPoint): """Radiation. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 missing = 9999 value_type = int unitSI = 'Wh/m2' unitIP = 'BTU/ft2' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname) @property def to_ip(self): """Return the value in IP assuming input value is in SI.""" return self.value * 0.316998331 @property def to_si(self): """Return the value in SI assuming input value is in IP.""" return self.value / 0.316998331
[docs]class Illuminance(DataPoint): """Illuminance. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 missing = 999999 value_type = int unitSI = 'lux' unitIP = 'fc' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname) @property def to_ip(self): """Return the value in IP assuming input value is in SI.""" return self.value * 0.09290304 @property def to_si(self): """Return the value in SI assuming input value is in IP.""" return self.value / 0.09290304
[docs]class Luminance(Illuminance): """Luminance. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 missing = 9999 value_type = int unitSI = 'Cd/m2' unitIP = 'Cd/ft2' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" Illuminance.__init__(self, value, datetime, standard, nickname)
[docs]class Angle(DataPoint): """Angle. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 maximum = 360 missing = 999 value_type = int unitSI = 'degrees' unitIP = 'radians' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname) @property def to_ip(self): """Return the value in IP assuming input value is in SI.""" return (self.value * PI) / 360 @property def to_si(self): """Return the value in SI assuming input value is in IP.""" return (self.value / PI) * 360
[docs]class Speed(DataPoint): """Speed. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 missing = 999 value_type = float unitSI = 'm/s' unitIP = 'mph' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname) @property def to_ip(self): """Return the value in IP assuming input value is in SI.""" return self.value * 2.23694 # m/s to mph @property def to_si(self): """Return the value in SI assuming input value is in IP.""" return self.value / 2.23694 # mph to m/s
[docs]class WindSpeed(Speed): """Wind Speed. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () maximum = 40
[docs]class Time(DataPoint): """Time. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 missing = 99 * 3600 value_type = int unitSI = 'second' unitIP = 'second' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname)
[docs]class Tenth(DataPoint): """Tenth. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 maximum = 10 missing = 99 value_type = int unitSI = None unitIP = None def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname)
[docs]class Thousandths(DataPoint): """Thousandths. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 missing = 999 value_type = float unitSI = 'thousandths' unitIP = 'thousandths' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname)
[docs]class Distance(DataPoint): """Distance. Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) conversion: Optional value for conversion to meters or foot before creating the object. """ __slots__ = () minimum = 0 value_type = float unitSI = 'm' unitIP = 'foot' def __init__(self, value, datetime=None, standard='SI', nickname=None, conversion=1): """Init class.""" DataPoint.__init__(self, value * conversion, datetime, standard, nickname) @property def to_ip(self): """Return the value in IP assuming input value is in SI.""" return self.value * 3.28084 @property def to_si(self): """Return the value in SI assuming input value is in IP.""" return self.value / 3.28084
[docs]class SkyPatch(DataPoint): """SkyPatch. Attributes: value: Input value vector: Sky vector as a tuple id: patch number """ __slots__ = ('vector',) minimum = 0 value_type = float unitSI = 'steradian' unitIP = 'steradian' def __init__(self, value, vector, id=None): """Init class.""" DataPoint.__init__(self, value, datetime=None, standard=None, nickname=id) self.vector = Vector3(*vector) @property def id(self): """Sky patch number.""" return self.nickname
[docs]class PredictedMeanVote(DataPoint): """Predicted Mean Vote (PMV). Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = -50 maximum = 50 value_type = float unitSI = 'PMV' unitIP = 'PMV' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname)
[docs]class PercentagePeopleDissatisfied(DataPoint): """Percentage of People Dissatisfied (PPD). Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 maximum = 100 value_type = float unitSI = '%' unitIP = '%' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname)
[docs]class MetabolicRate(DataPoint): """Metabolic Rate (met). Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 value_type = float unitSI = 'met' unitIP = 'met' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname)
[docs]class Clothing(DataPoint): """Clothing Level (clo). Attributes: value: Input value datetime: Date time data for this value (Default: None) standard: 'SI' or 'IP' (Default: 'SI') nickname: Optional nickname for data (e.g. Dew Point Temperature) """ __slots__ = () minimum = 0 value_type = float unitSI = 'clo' unitIP = 'clo' def __init__(self, value, datetime=None, standard='SI', nickname=None): """Init class.""" DataPoint.__init__(self, value, datetime, standard, nickname)