コード例 #1
0
class DateRangeSlider(_SliderBase):

    value = param.Tuple(default=(None, None), length=2)

    value_throttled = param.Tuple(default=None, length=2, constant=True)

    start = param.Date(default=None)

    end = param.Date(default=None)

    step = param.Number(default=1)

    _source_transforms = {'value': None, 'value_throttled': None,
                         'start': None, 'end': None, 'step': None}

    _widget_type = _BkDateRangeSlider

    def __init__(self, **params):
        if 'value' not in params:
            params['value'] = (params.get('start', self.start),
                               params.get('end', self.end))
        super(DateRangeSlider, self).__init__(**params)

    def _process_property_change(self, msg):
        msg = super(DateRangeSlider, self)._process_property_change(msg)
        if 'value' in msg:
            v1, v2 = msg['value']
            msg['value'] = (value_as_datetime(v1), value_as_datetime(v2))
        if 'value_throttled' in msg:
            v1, v2 = msg['value_throttled']
            msg['value_throttled'] = (value_as_datetime(v1), value_as_datetime(v2))
        return msg
コード例 #2
0
ファイル: slider.py プロジェクト: effect/panel
class DateSlider(_SliderBase):

    value = param.Date(default=None)

    value_throttled = param.Date(default=None, constant=True)

    start = param.Date(default=None)

    end = param.Date(default=None)

    _source_transforms = {
        'value': None,
        'value_throttled': None,
        'start': None,
        'end': None
    }

    _widget_type = _BkDateSlider

    def __init__(self, **params):
        if 'value' not in params:
            params['value'] = params.get('start', self.start)
        super(DateSlider, self).__init__(**params)

    def _process_property_change(self, msg):
        msg = super(_SliderBase, self)._process_property_change(msg)
        if 'value' in msg:
            msg['value'] = value_as_date(msg['value'])
        if 'value_throttled' in msg:
            msg['value_throttled'] = value_as_date(msg['value_throttled'])
        return msg
コード例 #3
0
class DatetimeRangeInput(CompositeWidget):

    value = param.Tuple(default=(None, None), length=2)

    start = param.Date(default=None)

    end = param.Date(default=None)

    format = param.String(default='%Y-%m-%d %H:%M:%S', doc="""
        Datetime format used for parsing and formatting the datetime.""")

    _composite_type = Column

    def __init__(self, **params):
        self._text = StaticText(margin=(5, 0, 0, 0), style={'white-space': 'nowrap'})
        self._start = DatetimeInput(sizing_mode='stretch_width', margin=(5, 0, 0, 0))
        self._end = DatetimeInput(sizing_mode='stretch_width', margin=(5, 0, 0, 0))
        if 'value' not in params:
            params['value'] = (params['start'], params['end'])
        super().__init__(**params)
        self._msg = ''
        self._composite.extend([self._text, self._start, self._end])
        self._updating = False
        self._update_widgets()
        self._update_label()

    @param.depends('name', '_start.name', '_end.name', watch=True)
    def _update_label(self):
        self._text.value = f'{self.name}{self._start.name}{self._end.name}{self._msg}'

    @param.depends('_start.value', '_end.value', watch=True)
    def _update(self):
        if self._updating:
            return
        if (self._start.value is not None and
            self._end.value is not None and
            self._start.value > self._end.value):
            self._msg = ' (start of range must be <= end)'
            self._update_label()
            return
        elif self._msg:
            self._msg = ''
            self._update_label()
        try:
            self._updating = True
            self.value = (self._start.value, self._end.value)
        finally:
            self._updating = False

    @param.depends('value', 'start', 'end', 'name', 'format', watch=True)
    def _update_widgets(self):
        if self._updating:
            return
        try:
            self._updating = True
            self._start.param.set_param(value=self.value[0], start=self.start, end=self.end, format=self.format)
            self._end.param.set_param(value=self.value[1], start=self.start, end=self.end, format=self.format)
        finally:
            self._updating = False
コード例 #4
0
ファイル: widgets.py プロジェクト: lancelot1969/panel
class DatetimeInput(LiteralInput):
    """
    DatetimeInput allows declaring Python literals using a text
    input widget. Optionally a type may be declared.
    """

    format = param.String(default='%Y-%m-%d %H:%M:%S', doc="""
        Datetime format used for parsing and formatting the datetime.""")

    value = param.Date(default=None)

    start = param.Date(default=None)

    end = param.Date(default=None)

    type = datetime

    def __init__(self, **params):
        super(DatetimeInput, self).__init__(**params)
        if self.value is not None and ((self.start is not None and self.start < self.value) or
                                       (self.end is not None and self.end < self.value)):
            value = datetime.strftime(self.value, self.format)
            start = datetime.strftime(self.start, self.format)
            end = datetime.strftime(self.end, self.format)
            raise ValueError('DatetimeInput value must be between {start} and {end}, '
                             'supplied value is {value}'.format(start=start, end=end, value=value))

    def _process_property_change(self, msg):
        msg = Widget._process_property_change(self, msg)
        new_state = ''
        if 'value' in msg:
            value = msg.pop('value')
            try:
                value = datetime.strptime(value, self.format)
            except:
                new_state = ' (invalid)'
                value = self.value
            else:
                if value is not None and ((self.start is not None and self.start > value) or
                                          (self.end is not None and self.end < value)):
                    new_state = ' (out of bounds)'
                    value = self.value
            msg['value'] = value
        msg['name'] = msg.get('title', self.name).replace(self._state, '') + new_state
        self._state = new_state
        return msg

    def _process_param_change(self, msg):
        msg = {k: v for k, v in msg.items() if k not in ('type', 'format', 'start', 'end')}
        if 'value' in msg:
            value = msg['value']
            if value is None:
                value = ''
            else:
                value = datetime.strftime(msg['value'], self.format)
            msg['value'] = value
        msg['title'] = self.name
        return msg
コード例 #5
0
ファイル: slider.py プロジェクト: zachlewis/panel
class DateSlider(_SliderBase):

    value = param.Date(default=None)

    start = param.Date(default=None)

    end = param.Date(default=None)

    _widget_type = _BkDateSlider
コード例 #6
0
class TimePeriodServiceBase(ServiceBase):
    start = param.Date(default=None, precedence=2, doc='start date')
    end = param.Date(default=None, precedence=3, doc='end date')

    @property
    def start_string(self):
        return self.start.strftime('%Y-%m-%d')

    @property
    def end_string(self):
        return self.end.strftime('%Y-%m-%d')
コード例 #7
0
class DateSlider(_SliderBase):

    value = param.Date(default=None)

    start = param.Date(default=None)

    end = param.Date(default=None)

    _widget_type = _BkDateSlider

    def _process_property_change(self, msg):
        msg = super(_SliderBase, self)._process_property_change(msg)
        if 'value' in msg:
            msg['value'] = value_as_date(msg['value'])
        return msg
コード例 #8
0
class StockScreener(param.Parameterized):

    df = param.DataFrame(precedence=-1)

    index = param.ListSelector()

    normalize = param.Boolean(default=True)

    start = param.Date()

    def __init__(self, df, **params):
        start = dt.date(year=df.index[0].year,
                        month=df.index[0].month,
                        day=df.index[0].day)
        end = dt.date(year=df.index[-1].year,
                      month=df.index[-1].month,
                      day=df.index[-1].day)
        super(StockScreener, self).__init__(df=df, start=start, **params)
        self.param.start.bounds = (start, end)
        columns = list(self.df.columns)
        self.param.index.objects = columns
        self.index = columns[:5]

    @param.depends('index', 'normalize', 'start')
    def update_plot(self):
        pos = self.df.index.get_loc(self.start, method='bfill')
        dfp = self.df.iloc[pos:][self.index]
        if self.normalize:
            dfp = 100 * dfp / dfp.iloc[0]
        return dfp.hvplot(group_label='Ticker')

    def panel(self):
        return pn.Row(
            pn.panel(self.param, widgets={'start': pn.widgets.DateSlider}),
            self.update_plot)
コード例 #9
0
class TestSet(param.Parameterized):

    numpy_params = ['r']
    pandas_params = ['s','t','u']
    conditionally_unsafe = ['f', 'o']

    a = param.Integer(default=5, doc='Example doc', bounds=(2,30), inclusive_bounds=(True, False))
    b = param.Number(default=4.3, allow_None=True)
    c = param.String(default='foo')
    d = param.Boolean(default=False)
    e = param.List([1,2,3], class_=int)
    f = param.List([1,2,3])
    g = param.Date(default=datetime.datetime.now())
    h = param.Tuple(default=(1,2,3), length=3)
    i = param.NumericTuple(default=(1,2,3,4))
    j = param.XYCoordinates(default=(32.1, 51.5))
    k = param.Integer(default=1)
    l = param.Range(default=(1.1,2.3), bounds=(1,3))
    m = param.String(default='baz', allow_None=True)
    n = param.ObjectSelector(default=3, objects=[3,'foo'], allow_None=False)
    o = param.ObjectSelector(default=simple_list, objects=[simple_list], allow_None=False)
    p = param.ListSelector(default=[1,4,5], objects=[1,2,3,4,5,6])
    q = param.CalendarDate(default=datetime.date.today())
    r = None if np is None else param.Array(default=ndarray)
    s = None if pd is None else param.DataFrame(default=df1, columns=2)
    t = None if pd is None else param.DataFrame(default=pd.DataFrame(
        {'A':[1,2,3], 'B':[1.1,2.2,3.3]}), columns=(1,4), rows=(2,5))
    u = None if pd is None else param.DataFrame(default=df2, columns=['A', 'B'])
    v = param.Dict({'1':2})
コード例 #10
0
 def test_get_soft_bounds(self):
     q = param.Date(dt.datetime(2017, 2, 25),
                    bounds=(dt.datetime(2017, 2,
                                        1), dt.datetime(2017, 2, 26)),
                    softbounds=(dt.datetime(2017, 2,
                                            1), dt.datetime(2017, 2, 25)))
     self.assertEqual(q.get_soft_bounds(),
                      (dt.datetime(2017, 2, 1), dt.datetime(2017, 2, 25)))
コード例 #11
0
class DateRangeSlider(_RangeSliderBase):

    value = param.Tuple(default=(None, None), length=2)

    value_start = param.Date(default=None, readonly=True)

    value_end = param.Date(default=None, readonly=True)

    value_throttled = param.Tuple(default=None, length=2, constant=True)

    start = param.Date(default=None)

    end = param.Date(default=None)

    step = param.Number(default=1)

    _source_transforms = {
        'value': None,
        'value_throttled': None,
        'start': None,
        'end': None,
        'step': None
    }

    _rename = {'name': 'title', 'value_start': None, 'value_end': None}

    _widget_type = _BkDateRangeSlider

    def _process_param_change(self, msg):
        msg = super()._process_param_change(msg)
        if msg.get('value') == (None, None):
            del msg['value']
        if msg.get('value_throttled') == (None, None):
            del msg['value_throttled']
        return msg

    def _process_property_change(self, msg):
        msg = super()._process_property_change(msg)
        if 'value' in msg:
            v1, v2 = msg['value']
            msg['value'] = (value_as_datetime(v1), value_as_datetime(v2))
        if 'value_throttled' in msg:
            v1, v2 = msg['value_throttled']
            msg['value_throttled'] = (value_as_datetime(v1),
                                      value_as_datetime(v2))
        return msg
コード例 #12
0
ファイル: input.py プロジェクト: zachlewis/panel
class DatePicker(Widget):

    value = param.Date(default=None)

    start = param.Date(default=None)

    end = param.Date(default=None)

    _widget_type = _BkDatePicker

    _rename = {'start': 'min_date', 'end': 'max_date', 'name': 'title'}

    def _process_property_change(self, msg):
        msg = super(DatePicker, self)._process_property_change(msg)
        if 'value' in msg:
            msg['value'] = datetime.strptime(msg['value'][4:], '%b %d %Y')
        return msg
コード例 #13
0
ファイル: slider.py プロジェクト: zachlewis/panel
class DateRangeSlider(_SliderBase):

    value = param.Tuple(default=None, length=2)

    start = param.Date(default=None)

    end = param.Date(default=None)

    step = param.Number(default=1)

    _widget_type = _BkDateRangeSlider

    def _process_property_change(self, msg):
        msg = super(DateRangeSlider, self)._process_property_change(msg)
        if 'value' in msg:
            v1, v2 = msg['value']
            msg['value'] = (value_as_datetime(v1), value_as_datetime(v2))
        return msg
コード例 #14
0
class DatePicker(Widget):

    value = param.Date(default=None)

    start = param.Date(default=None)

    end = param.Date(default=None)

    _widget_type = _BkDatePicker

    _rename = {'start': 'min_date', 'end': 'max_date', 'name': 'title'}

    def _process_property_change(self, msg):
        msg = super(DatePicker, self)._process_property_change(msg)
        if msg.get('value', False):
            if not isiterable(msg['value']):
                msg['value'] = datetime.combine(msg['value'],
                                                datetime.min.time())
        return msg
コード例 #15
0
class DatePicker(Widget):

    value = param.Date(default=None)

    start = param.Date(default=None)

    end = param.Date(default=None)

    _source_transforms = {'value': None, 'start': None, 'end': None}

    _rename = {'start': 'min_date', 'end': 'max_date', 'name': 'title'}

    _widget_type = _BkDatePicker

    def _process_property_change(self, msg):
        msg = super(DatePicker, self)._process_property_change(msg)
        if 'value' in msg:
            if isinstance(msg['value'], string_types):
                msg['value'] = datetime.strptime(msg['value'][4:], '%b %d %Y')
        return msg
コード例 #16
0
 class _BigDumbParams(param.Parameterized):
     action = param.Action(default_action, allow_None=True)
     array = param.Array(np.array([1.0, 2.0]))
     boolean = param.Boolean(True, allow_None=True)
     callable = param.Callable(default_action, allow_None=True)
     class_selector = param.ClassSelector(int, is_instance=False, allow_None=True)
     color = param.Color("#FFFFFF", allow_None=True)
     composite = param.Composite(["action", "array"], allow_None=True)
     try:
         data_frame = param.DataFrame(
             pd.DataFrame({"A": 1.0, "B": np.arange(5)}), allow_None=True
         )
     except TypeError:
         data_frame = param.DataFrame(pd.DataFrame({"A": 1.0, "B": np.arange(5)}))
     date = param.Date(datetime.now(), allow_None=True)
     date_range = param.DateRange((datetime.min, datetime.max), allow_None=True)
     dict_ = param.Dict({"foo": "bar"}, allow_None=True, doc="dict means dictionary")
     dynamic = param.Dynamic(default=default_action, allow_None=True)
     file_selector = param.FileSelector(
         os.path.join(FILE_DIR_DIR, "LICENSE"),
         path=os.path.join(FILE_DIR_DIR, "*"),
         allow_None=True,
     )
     filename = param.Filename(
         os.path.join(FILE_DIR_DIR, "LICENSE"), allow_None=True
     )
     foldername = param.Foldername(os.path.join(FILE_DIR_DIR), allow_None=True)
     hook_list = param.HookList(
         [CallableObject(), CallableObject()], class_=CallableObject, allow_None=True
     )
     integer = param.Integer(10, allow_None=True)
     list_ = param.List([1, 2, 3], allow_None=True, class_=int)
     list_selector = param.ListSelector([2, 2], objects=[1, 2, 3], allow_None=True)
     magnitude = param.Magnitude(0.5, allow_None=True)
     multi_file_selector = param.MultiFileSelector(
         [],
         path=os.path.join(FILE_DIR_DIR, "*"),
         allow_None=True,
         check_on_set=True,
     )
     number = param.Number(-10.0, allow_None=True, doc="here is a number")
     numeric_tuple = param.NumericTuple((5.0, 10.0), allow_None=True)
     object_selector = param.ObjectSelector(
         False, objects={"False": False, "True": 1}, allow_None=True
     )
     path = param.Path(os.path.join(FILE_DIR_DIR, "LICENSE"), allow_None=True)
     range_ = param.Range((-1.0, 2.0), allow_None=True)
     series = param.Series(pd.Series(range(5)), allow_None=True)
     string = param.String("foo", allow_None=True, doc="this is a string")
     tuple_ = param.Tuple((3, 4, "fi"), allow_None=True)
     x_y_coordinates = param.XYCoordinates((1.0, 2.0), allow_None=True)
コード例 #17
0
    class Example(BaseClass):
        """An example Parameterized class"""

        timestamps = []

        boolean = param.Boolean(True, doc="A sample Boolean parameter")
        color = param.Color(default="#FFFFFF")
        date = param.Date(dt.date(2017, 1, 1), bounds=wired.DATE_BOUNDS)
        dataframe = param.DataFrame(pd.util.testing.makeDataFrame().iloc[:3])
        select_string = param.ObjectSelector(default="yellow", objects=["red", "yellow", "green"])
        select_fn = param.ObjectSelector(default=list, objects=[list, set, dict])
        int_list = param.ListSelector(default=[3, 5], objects=[1, 3, 5, 7, 9], precedence=0.5)
        single_file = param.FileSelector(path="../../*/*.py*", precedence=0.5)
        multiple_files = param.MultiFileSelector(path="../../*/*.py?", precedence=0.5)
        record_timestamp = param.Action(
            lambda x: x.timestamps.append(dt.datetime.utcnow()),
            doc="""Record timestamp.""",
            precedence=0.7,
        )
コード例 #18
0
ファイル: __init__.py プロジェクト: toddrme2178/EarthSim
class Simulation(param.Parameterized):
    """Basic example of wrapping a GSSHA-based rainfall simulation."""

    elevation_service = param.Parameter(default='svc://usgs-ned:13-arc-second',
                                        precedence=0.1)

    land_use_service = param.Parameter(default='svc://usgs-nlcd:2011',
                                       precedence=0.2)

    land_use_grid_id = param.Parameter(default='nlcd', precedence=0.3)

    model_creator = param.ClassSelector(CreateModel,
                                        default=CreateGSSHAModel(),
                                        precedence=-1)

    # TODO: switch to date range...
    simulation_start = param.Date(default=datetime(2017, 5, 9), precedence=0.5)
    # ...or at least a timedelta parameter type
    simulation_duration = param.Integer(default=120,
                                        bounds=(0, None),
                                        softbounds=(0, 1000000),
                                        precedence=0.51,
                                        doc="""
        Simulated-time duration to run the simulator, in seconds.""")

    rain_intensity = param.Integer(default=24,
                                   bounds=(0, None),
                                   softbounds=(0, 75),
                                   precedence=0.6,
                                   doc="""
        Intensity for simulated rain, in mm/hr.""")

    rain_duration = param.Integer(default=60,
                                  bounds=(0, None),
                                  softbounds=(0, 1000000),
                                  precedence=0.61,
                                  doc="""
        Simulated-time duration for simulated rain, in seconds.""")
コード例 #19
0
class Athlete(param.Parameterized):
    """A model of an Athlete"""

    name_ = param.String("P.A. Nelson")
    birthday = param.Date(
        datetime.date(
            1976,
            9,
            17,
        ),
        bounds=DATE_BOUNDS,
    )
    weight = param.Number(
        default=82,
        bounds=(
            20,
            300,
        ),
    )
    power_curve = param.ClassSelector(
        class_=PowerCurve,
        default=PowerCurve(),
    )
コード例 #20
0
ファイル: input.py プロジェクト: syamajala/panel
class DatetimePicker(_DatetimePickerBase):

    value = param.Date(default=None)

    mode = param.String('single', constant=True)

    def _serialize_value(self, value):
        if isinstance(value, string_types) and value:
            value = datetime.strptime(value, r'%Y-%m-%d %H:%M:%S')

            # Hour, minute and seconds can be increased after end is reached.
            # This forces the hours, minute and second to be 0.
            end = self._date_to_datetime(self.end)
            if end is not None and value > end:
                value = end

        return value

    def _deserialize_value(self, value):
        if isinstance(value, (datetime, date)):
            value = value.strftime(r'%Y-%m-%d %H:%M:%S')

        return value
コード例 #21
0
    class MyParameterized(param.Parameterized):
        enable = param.Boolean(True,
                               doc="A sample Boolean parameter",
                               allow_None=True)
        what_proportion = param.Magnitude(default=0.9)
        age = param.Number(49,
                           bounds=(0, 100),
                           doc="Any Number between 0 to 100")
        how_many = param.Integer()
        favorite_quote = param.String(default="Hello, world!")

        choose_file_or_folder = param.Path(search_paths='./')
        choose_folder = param.Foldername(search_paths="./")
        choose_file = param.Filename(search_paths="./")
        select_a_file = param.FileSelector(path='./*')
        select_multiple_files = param.MultiFileSelector(path='./*')

        favorite_color = param.ObjectSelector(
            default="green", objects=["red", "yellow", "green"])
        favorite_fruit = param.Selector(default="Apple",
                                        objects=["Orange", "Apple", "Mango"])
        select_multiple = param.ListSelector(default=[3, 5],
                                             objects=[1, 2, 3, 4, 5])

        birthday = param.CalendarDate(dt.date(2017, 1, 1),
                                      bounds=(dt.date(2017, 1,
                                                      1), dt.date(2017, 2, 1)))
        appointment = param.Date(dt.datetime(2017, 1, 1),
                                 bounds=(dt.datetime(2017, 1, 1),
                                         dt.datetime(2017, 2, 1)))
        least_favorite_color = param.Color(default='#FF0000')
        dataset = param.DataFrame(pd.util.testing.makeDataFrame().iloc[:3])

        this_strange_thing = param.Tuple(default=(False, ), allow_None=True)
        some_numbers = param.NumericTuple(default=(1, 2, 3.0, 4.0))
        home_city = param.XYCoordinates(default=(-111.65, 40.23))
        bounds = param.Range(default=(-10, 10))
コード例 #22
0
class DatetimePicker(_DatetimePickerBase):
    """
    The `DatetimePicker` allows selecting selecting a `datetime` value using a
    textbox and a datetime-picking utility.
    
    Reference: https://panel.holoviz.org/reference/widgets/DatetimePicker.html

    :Example:

    >>> DatetimePicker(
    ...    value=datetime(2025,1,1,22,0),
    ...    start=date(2025,1,1), end=date(2025,12,31),
    ...    military_time=True, name='Date and time'
    ... )
    """

    value = param.Date(default=None)

    mode = param.String('single', constant=True)

    def _serialize_value(self, value):
        if isinstance(value, str) and value:
            value = datetime.strptime(value, r'%Y-%m-%d %H:%M:%S')

            # Hour, minute and seconds can be increased after end is reached.
            # This forces the hours, minute and second to be 0.
            end = self._date_to_datetime(self.end)
            if end is not None and value > end:
                value = end

        return value

    def _deserialize_value(self, value):
        if isinstance(value, (datetime, date)):
            value = value.strftime(r'%Y-%m-%d %H:%M:%S')

        return value
コード例 #23
0
class DateRangeSlider(_RangeSliderBase):
    """
    The DateRangeSlider widget allows selecting a date range using a
    slider with two handles. Supports datetime.datetime, datetime.data
    and np.datetime64 ranges.

    Reference: https://panel.holoviz.org/reference/widgets/DateRangeSlider.html

    :Example:

    >>> import datetime as dt
    >>> DateRangeSlider(
    ...     value=(dt.datetime(2025, 1, 9), dt.datetime(2025, 1, 16)),
    ...     start=dt.datetime(2025, 1, 1),
    ...     end=dt.datetime(2025, 1, 31),
    ...     step=2,
    ...     name="A tuple of datetimes"
    ... )
    """

    value = param.Tuple(
        default=(None, None),
        length=2,
        doc=
        """The selected range as a tuple of values. Updated when one of the handles is
        dragged. Supports datetime.datetime, datetime.date, and np.datetime64 ranges."""
    )

    value_start = param.Date(default=None,
                             readonly=True,
                             doc="""
        The lower value of the selected range.""")

    value_end = param.Date(default=None,
                           readonly=True,
                           doc="""
        The upper value of the selected range.""")

    value_throttled = param.Tuple(default=None,
                                  length=2,
                                  constant=True,
                                  doc="""
        The selected range as a tuple of values. Updated one of the handles is released. Supports 
        datetime.datetime, datetime.date and np.datetime64 ranges""")

    start = param.Date(default=None, doc="""
        The lower bound.""")

    end = param.Date(default=None, doc="""
        The upper bound.""")

    step = param.Number(default=1,
                        doc="""
        The step size. Default is 1 (day).""")

    _source_transforms = {
        'value': None,
        'value_throttled': None,
        'start': None,
        'end': None,
        'step': None
    }

    _rename = {'name': 'title', 'value_start': None, 'value_end': None}

    _widget_type = _BkDateRangeSlider

    def _process_param_change(self, msg):
        msg = super()._process_param_change(msg)
        if msg.get('value') == (None, None):
            del msg['value']
        elif 'value' in msg:
            v1, v2 = msg['value']
            if isinstance(v1, dt.datetime):
                v1 = datetime_as_utctimestamp(v1)
            if isinstance(v2, dt.datetime):
                v2 = datetime_as_utctimestamp(v2)
            msg['value'] = (v1, v2)
        if msg.get('value_throttled') == (None, None):
            del msg['value_throttled']
        return msg

    def _process_property_change(self, msg):
        msg = super()._process_property_change(msg)
        if 'value' in msg:
            v1, v2 = msg['value']
            msg['value'] = (value_as_datetime(v1), value_as_datetime(v2))
        if 'value_throttled' in msg:
            v1, v2 = msg['value_throttled']
            msg['value_throttled'] = (value_as_datetime(v1),
                                      value_as_datetime(v2))
        return msg
コード例 #24
0
class DateSlider(_SliderBase):
    """
    The DateSlider widget allows selecting a value within a set of
    bounds using a slider.  Supports datetime.datetime, datetime.date
    and np.datetime64 values. The step size is fixed at 1 day.

    Reference: https://panel.holoviz.org/reference/widgets/DateSlider.html

    :Example:

    >>> import datetime as dt
    >>> DateSlider(
    ...     value=dt.datetime(2025, 1, 1),
    ...     start=dt.datetime(2025, 1, 1),
    ...     end=dt.datetime(2025, 1, 7),
    ...     name="A datetime value"
    ... )
    """

    value = param.Date(default=None,
                       doc="""
        The selected date value of the slider. Updated when the slider
        handle is dragged. Supports datetime.datetime, datetime.date
        or np.datetime64 types.""")

    value_throttled = param.Date(default=None,
                                 constant=True,
                                 doc="""
        The value of the slider. Updated when the slider handle is released."""
                                 )

    start = param.Date(default=None, doc="""
        The lower bound.""")

    end = param.Date(default=None, doc="""
        The upper bound.""")

    as_datetime = param.Boolean(default=False,
                                doc="""
        Whether to store the date as a datetime.""")

    _rename = {'name': 'title', 'as_datetime': None}

    _source_transforms = {
        'value': None,
        'value_throttled': None,
        'start': None,
        'end': None
    }

    _widget_type = _BkDateSlider

    def __init__(self, **params):
        if 'value' not in params:
            params['value'] = params.get('start', self.start)
        super().__init__(**params)

    def _process_param_change(self, msg):
        msg = super()._process_param_change(msg)
        if 'value' in msg:
            value = msg['value']
            if isinstance(value, dt.datetime):
                value = datetime_as_utctimestamp(value)
            msg['value'] = value
        return msg

    def _process_property_change(self, msg):
        msg = super()._process_property_change(msg)
        transform = value_as_datetime if self.as_datetime else value_as_date
        if 'value' in msg:
            msg['value'] = transform(msg['value'])
        if 'value_throttled' in msg:
            msg['value_throttled'] = transform(msg['value_throttled'])
        return msg
コード例 #25
0
 class Q(param.Parameterized):
     q = param.Date(bounds=(dt.date(2017, 2, 1), dt.date(2017, 2, 26)))
コード例 #26
0
class HistoryPage(Page):
    """Provides an illustration of the `Ticker.history` method"""

    period_type = param.ObjectSelector(
        default="Period",
        objects=[
            "Dates",
            "Period",
        ],
    )
    # pylint: disable=protected-access
    interval = param.ObjectSelector(
        default="1d",
        objects=Ticker._INTERVALS,
    )
    period = param.ObjectSelector(
        default="1y",
        objects=Ticker._PERIODS,
    )
    # pylint: enable=protected-access
    start = param.Date(
        default=PERIOD_START_DATE,
        bounds=DATE_BOUNDS,
    )
    end = param.Date(
        default=PERIOD_END_DATE,
        bounds=DATE_BOUNDS,
    )

    @staticmethod
    def _help():
        return pnx_help(Ticker.history)

    @param.depends("period_type")
    def _param_view(
        self,
    ):
        if self.period_type == "Period":
            return pn.Param(
                self,
                parameters=[
                    "period_type",
                    "interval",
                    "period",
                ],
                default_layout=GridBoxWithTwoColumns,
                width=400,
                show_name=False,
            )
        return pn.Param(
            self,
            parameters=[
                "period_type",
                "interval",
                "start",
                "end",
            ],
            default_layout=GridBoxWithTwoColumns,
            widgets={
                "start": pn.widgets.DatePicker,
                "end": pn.widgets.DatePicker,
            },
            width=600,
            show_name=False,
        )

    @param.depends(
        "symbols",
        "period_type",
        "interval",
        "period",
        "start",
        "end",
    )
    def _code(
        self,
    ):
        code = f"""Ticker("{self.symbols}").history({', '.join(self._args_string)})"""
        return code_card(code=code)

    @property
    def _history_args(
        self,
    ):
        history_args = {}
        if self.period_type == "Period":
            history_args["period"] = self.period
            history_args["start"] = None
            history_args["end"] = None
        else:
            history_args["period"] = None
            history_args["start"] = self.start
            history_args["end"] = self.end
        history_args["interval"] = self.interval
        return history_args

    @property
    def _args_string(
        self,
    ):
        return [
            str(k) + "='" + str(v) + "'" for k, v in self._history_args.items() if v is not None
        ]

    # I cannot make the chart responsive. There is some starting information here
    # See https://stackoverflow.com/questions/55169344/how-to-make-altair-plots-responsive
    @staticmethod
    def _history_plot(
        dataframe: pd.DataFrame,
    ):
        if "symbol" in dataframe.columns:
            chart = (
                alt.Chart(dataframe.reset_index())
                .mark_line()
                .encode(
                    alt.Y(
                        "close:Q",
                        scale=alt.Scale(zero=False),
                    ),
                    x="dates",
                    color="symbol",
                    tooltip=["dates", "close", "symbol"],
                )
            )
        else:
            chart = (
                alt.Chart(dataframe.reset_index())
                .mark_line()
                .encode(
                    alt.Y(
                        "close:Q",
                        scale=alt.Scale(zero=False),
                    ),
                    x="dates:T",
                    tooltip=["dates", "close"],
                )
            )

        chart = chart.properties(
            width="container",
            height=300,
        )
        return chart

    @param.depends("symbols")
    @PROGRESS.report(message="Requesting Price History from Yahoo Finance")
    def _data(
        self,
    ):
        tickers = YahooQueryService.to_ticker(self.symbols)
        data = tickers.history(**self._history_args)
        if isinstance(
            data,
            pd.DataFrame,
        ):
            return pn.Column(
                pnx.SubHeader(" Response"),
                pn.pane.Vega(
                    self._history_plot(data),
                    sizing_mode="stretch_width",
                    height=325,
                ),
                sizing_mode="stretch_width",
            )
        return pnx_json(data)

    def view(
        self,
    ) -> pn.viewable.Viewable:
        """The main view of the OptionsPage

        Returns:
            pn.viewable.Viewable: The main view of the OptionsPage
        """
        return pn.Column(
            self._param_view,
            self._data,
            self._code,
            self._help,
            sizing_mode="stretch_width",
        )
コード例 #27
0
 class Q(param.Parameterized):
     q = param.Date(bounds=(dt.date(2017, 2, 1), dt.date(2017, 2, 26)),
                    inclusive_bounds=(True, False))
コード例 #28
0
class DatetimeInput(LiteralInput):
    """
    DatetimeInput allows declaring Python literals using a text
    input widget. Optionally a type may be declared.
    """

    format = param.String(default='%Y-%m-%d %H:%M:%S',
                          doc="""
        Datetime format used for parsing and formatting the datetime.""")

    value = param.Date(default=None)

    start = param.Date(default=None)

    end = param.Date(default=None)

    type = datetime

    _source_transforms = {'value': None, 'start': None, 'end': None}

    _rename = {
        'format': None,
        'type': None,
        'name': 'title',
        'start': None,
        'end': None,
        'serializer': None
    }

    def __init__(self, **params):
        super(DatetimeInput, self).__init__(**params)
        self.param.watch(self._validate, 'value')
        self._validate(None)

    def _validate(self, event):
        new = self.value
        if new is not None and ((self.start is not None and self.start > new)
                                or (self.end is not None and self.end < new)):
            value = datetime.strftime(new, self.format)
            start = datetime.strftime(self.start, self.format)
            end = datetime.strftime(self.end, self.format)
            if event:
                self.value = event.old
            raise ValueError(
                'DatetimeInput value must be between {start} and {end}, '
                'supplied value is {value}'.format(start=start,
                                                   end=end,
                                                   value=value))

    def _process_property_change(self, msg):
        msg = Widget._process_property_change(self, msg)
        new_state = ''
        if 'value' in msg:
            value = msg.pop('value')
            try:
                value = datetime.strptime(value, self.format)
            except Exception:
                new_state = ' (invalid)'
                value = self.value
            else:
                if value is not None and (
                    (self.start is not None and self.start > value) or
                    (self.end is not None and self.end < value)):
                    new_state = ' (out of bounds)'
                    value = self.value
            msg['value'] = value
        msg['name'] = msg.get('title', self.name).replace(self._state,
                                                          '') + new_state
        self._state = new_state
        return msg

    def _process_param_change(self, msg):
        msg = Widget._process_param_change(self, msg)
        if 'value' in msg:
            value = msg['value']
            if value is None:
                value = ''
            else:
                value = datetime.strftime(msg['value'], self.format)
            msg['value'] = value
        msg['title'] = self.name
        return msg
コード例 #29
0
class PowerCurve(param.Parameterized):
    """A Model of a Power Curve of an Athlete

    The PowerCurve is a recording of the athletes maximum power output in Watt per kg for fixed
    durations.
    """

    ten_sec = param.Number(1079)
    ten_sec_date = param.Date(
        datetime.date(
            2018,
            8,
            21,
        ),
        bounds=DATE_BOUNDS,
    )
    one_min = param.Number(684)
    one_min_date = param.Date(
        datetime.date(
            2017,
            8,
            31,
        ),
        bounds=DATE_BOUNDS,
    )
    ten_min = param.Number(419)
    ten_min_date = param.Date(
        datetime.date(
            2017,
            9,
            22,
        ),
        bounds=DATE_BOUNDS,
    )
    twenty_min = param.Number(398)
    twenty_min_date = param.Date(
        datetime.date(
            2017,
            9,
            22,
        ),
        bounds=DATE_BOUNDS,
    )
    one_hour = param.Number(319)
    one_hour_date = param.Date(
        datetime.date(
            2017,
            8,
            6,
        ),
        bounds=DATE_BOUNDS,
    )

    @param.depends(
        "ten_sec",
        "one_min",
        "ten_min",
        "twenty_min",
        "one_hour",
    )
    def plot(self, ):
        """A plot of the power curve: duration vs power"""
        data = {
            "duration": [
                10 / 60,
                1,
                10,
                20,
                60,
            ],
            "power": [
                self.ten_sec,
                self.one_min,
                self.ten_min,
                self.twenty_min,
                self.one_hour,
            ],
        }
        dataframe = pd.DataFrame(data)
        line_plot = dataframe.hvplot.line(
            x="duration",
            y="power",
            width=300,
            line_color="#007BFF",
            line_width=3,
        )
        scatter_plot = dataframe.hvplot.scatter(
            x="duration",
            y="power",
            width=300,
        ).opts(
            marker="o",
            size=6,
            color="#007BFF",
        )
        fig = line_plot * scatter_plot
        gridstyle = {
            "grid_line_color": "black",
            "grid_line_width": 0.1,
        }
        fig = fig.opts(
            responsive=True,
            toolbar=None,
            yticks=list(range(
                0,
                1600,
                200,
            )),
            ylim=(
                0,
                1500,
            ),
            gridstyle=gridstyle,
            show_grid=True,
        )
        return fig
コード例 #30
0
class OWI(param.Parameterized):
    grid = None
    field1 = pd.DataFrame()
    field2 = pd.DataFrame()
    delta = None
    number_fields = param.Integer(2, bounds=(1, 2), precedence=2)
    ilat = param.Integer(0, precedence=2)
    ilon = param.Integer(0, precedence=2)
    dx = param.Number(0.25, precedence=2)
    dy = param.Number(0.25, precedence=2)
    swlat = param.Number(5.8, precedence=2)
    swlon = param.Number(5.8, precedence=2)
    time_range = param.DateRange(default=None,
                                 doc="""
                    Start and end datetime""")
    date_start = param.Date(default=datetime.datetime.now(), precedence=-1)
    date_stop = param.Date(default=datetime.datetime.now() +
                           timedelta(hours=5),
                           precedence=-1)

    def get_file_metadata(self, path, filename):
        """
        Method to get extract only time between snaps from an OWI file using
        the first two snaps.

        Parameters
        ----------
        path - str
            directory where the file is located
        filename - str
            filename of the owi file

        Returns
        -------
        delta - datetime.timedelta
            time between snaps
        """
        # construct full path
        full_path = os.path.join(path, filename)
        # open the file
        fp = open(full_path)
        # get the first line
        line1 = fp.readline().split()
        # date_start = datetime.datetime.strptime(line1[-2], '%Y%m%d%H')
        # date_stop = datetime.datetime.strptime(line1[-1], '%Y%m%d%H')
        # get the second line
        line2 = fp.readline().rstrip('\n')
        # get the first snap time
        start_time = datetime.datetime.strptime(line2[68:80], '%Y%m%d%H')

        for line in fp:
            # if this is a header line
            if line[0:2] == 'iL':
                line.rstrip('\n')
                # get the second snap time
                snap_time = datetime.datetime.strptime(
                    line.rstrip('\n')[68:80], '%Y%m%d%H')
                # calculate the time between snaps
                delta = snap_time - start_time

                fp.close()

                return delta

        fp.close()

        print(
            'An accurate delta could not be calculated because only one snap '
            'was found in the file. Default of 0.0 will be used')

        delta = datetime.timedelta(seconds=0.0)
        return delta

    def read_owi_wind(self, path, filename, fields):
        """ Method to read owi wind field from file

        Parameters
        ----------
        path - str
            Directory where the file is located
        filename - str
            Filename to be read (with extension)
        delta - datetime.timedelta
            time between time snaps
        fields - int
            Number of fields in this file (1 for single data, 2 for x and y data)

        """
        if self.delta is None:
            self.delta = self.get_file_metadata(path, filename)

        # construct full path
        full_path = os.path.join(path, filename)

        # # convert the provided delta to a timedelta interval
        # interval = datetime.timedelta(minutes=delta)

        # set the nodes per line (fixed OWI format)
        nodes_per_line = 8

        # instantiate variables
        snaps = []
        data = []
        next_header = 0
        header_index = 0
        field1_df = pd.DataFrame()
        field2_df = pd.DataFrame()

        # open the file
        fp = open(full_path)
        # loop over all the lines in the file (with an index)
        for i, line in enumerate(fp):
            # first line has file header information
            if i == 0:
                # parse the line
                spar = line.split()
                # record the start time
                time_start = datetime.datetime.strptime(spar[3], '%Y%m%d%H')
                # record the stop time
                time_stop = datetime.datetime.strptime(spar[4], '%Y%m%d%H')
                # calculate the number of snaps in the file
                (number_snaps, remainder) = divmod(
                    (time_stop - time_start).total_seconds(),
                    self.delta.total_seconds())
                # check for even division
                if remainder != 0.0:
                    raise UserWarning(
                        'Warning: The time delta did not evenly match with the start and '
                        'stop time in the file. The results may contain bad data.'
                    )

                # calculate the datetime of each snap
                snaps = [
                    time_start + self.delta * x
                    for x in range(0,
                                   int(number_snaps) + 1, 1)
                ]

            # the first data header line
            elif i == 1:
                # parse the line
                # get number of latitude nodes
                ilat = int(line[5:9])
                # get number of longitude nodes
                ilon = int(line[15:19])
                # get dx
                dx = float(line[22:28])
                # get dy
                dy = float(line[31:37])
                # get the SW corner of the grid latitude
                swlat = float(line[43:51])
                # get the sW corner of the grid longitude
                swlon = float(line[57:65])
                # get the snap time as a string
                snap_time_str = line[68:80]

                # calculate the number of nodes
                number_nodes = ilat * ilon
                # calculate the number of lines per snap (per field)
                lines_per_snap = int(np.ceil(number_nodes / nodes_per_line))
                # calculate the indices for the header lines
                header_lines = [
                    x for x in range(
                        1, 1 + (len(snaps) * (1 + lines_per_snap * fields)), (
                            1 + lines_per_snap * fields))
                ]

                # increment the header line index
                header_index += 1
                # get the next header line
                next_header = header_lines[header_index]

            # stop at the next header line
            elif i == next_header:
                # flatten the data list
                column_data = list(chain.from_iterable(data))
                if fields == 1:
                    # store the previous data for field1
                    field1_df[snap_time_str] = column_data[0:len(column_data)]

                # if there are two fields in the file
                else:
                    # store the previous data for field1
                    field1_df[snap_time_str] = column_data[
                        0:int(len(column_data) / 2)]
                    # store the field2 data
                    field2_df[snap_time_str] = column_data[
                        int(len(column_data) / 2):len(column_data)]

                # record the snap time for the next interval
                snap_time_str = line[68:80]

                # clear the data list
                data = []

                # increment the header flag
                header_index += 1
                # if we've just moved beyond the last header in the list, stop
                if header_index == len(header_lines):
                    # break
                    next_header = -1
                else:
                    # get the next header line
                    next_header = header_lines[header_index]

            # if this is not a header line, it is data, append to the data list
            else:
                data.append([float(x) for x in line.split()])

        # Store the data from the last timestep
        # flatten the data list
        column_data = list(chain.from_iterable(data))
        if fields == 1:
            # store the previous data for field1
            field1_df[snap_time_str] = column_data[0:len(column_data)]

        # if there are two fields in the file
        else:
            # store the previous data for field1
            field1_df[snap_time_str] = column_data[0:int(len(column_data) / 2)]
            # store the field2 data
            field2_df[snap_time_str] = column_data[int(len(column_data) /
                                                       2):len(column_data)]

        # close the file
        fp.close()

        # store data in the object
        # self.delta = delta
        self.number_fields = fields
        self.ilat = ilat
        self.ilon = ilon
        self.dx = dx
        self.dy = dy
        self.swlat = swlat
        self.swlon = swlon
        # self.start_time = time_start
        # self.stop_time = time_stop
        self.time_range = (time_start, time_stop)  # todo

        # if there are two fields, return both
        if fields == 2:
            self.field1 = field1_df
            self.field2 = field2_df
        # otherwise return field1
        else:
            self.field1 = field1_df

    def set_grid_parameters(self, swLat, swLon, neLat, neLon, dx, dy):
        """Method to set the actual grid values based on a bounding box.
        dx and dy default to 0.25. If the number of cells cannot be
        evenly divided, an extra grid cell is added. Values are set into self.
        Latitude = y
        Longitude = x
        """

        # calculate the estimated grid size
        delta_lat = abs(swLat - neLat)
        delta_lon = abs(swLon - neLon)

        # calculate the number of cells required
        ilat, rem = divmod(delta_lat, dy)
        # if the delta cannot be evenly divided, add a cell
        if rem != 0:
            ilat += 1
        # if the ilat is even, add a cell
        if ilat % 2 == 0:
            ilat += 1
        # calculate the number of cells required
        ilon, rem = divmod(delta_lon, dx)
        # if the delta cannot be evenly divided, add a cell
        if rem != 0:
            ilon += 1
        # if the ilon is even, add a cell
        if ilon % 2 == 0:
            ilon += 1

        # set values into self
        self.ilat = int(ilat)
        self.ilon = int(ilon)
        self.dx = dx
        self.dy = dy
        self.swlat = swLat
        self.swlon = swLon

    def make_owi_grid(self):
        """
        Method to construct the list of grid nodes (latitude and longitude) from
        the construction parameters. The indexes of these grid nodes correspond to
        the indexing of the data in the OWI file.

        Parameters
        ----------
        swlon - float
            Longitude of the SouthWestern Corner of the grid
        swlat - float
            Latitude of the SouthWestern Corner of the grid
        dx - float
            grid spacing in the x direction (Longitude)
        dy - float
            grid spacing in the y direction (Latitude)
        ilon - int
            number of grid cells in the x direction (Longitude)
        ilat - int
            number of grid cells in the y direction (Latitude)

        Returns
        -------
        self.grid - df
            DataFrame containing the Latitude and Longitude lists for all the grid
            points. The indexing corresponds to the indexing of the data arrays from
            the file.
        """
        # ensure that all the required values have been set
        if self.ilat is None or self.ilon is None or self.dx is None or self.dy is None or \
                        self.swlat is None or self.swlon is None:
            raise IOError(
                'Parmeters defining the grid must be set into the OWI object (ilat, '
                'ilon, dx, dy, swlat, swlon)')

        # create array of x values
        xs = np.arange(self.swlon, self.swlon + self.ilon * self.dx, self.dx)

        # create array of y values
        ys = np.arange(self.swlat, self.swlat + self.ilat * self.dy, self.dy)

        # construct grid
        data = np.stack([[x, y] for y in ys for x in xs], axis=1)

        # construct the dataframe for the grid
        self.grid = pd.DataFrame({'Longitude': data[0], 'Latitude': data[1]})

        # ensure all grid values are valid
        self.check_grid()

    def check_grid(self):
        """Method to check that the grid does not cross a pole or an
        antimeridian with invalid values. Corrects as needed and sets
        values back into self.grid."""

        if self.grid.Longitude.min() < -180.0:
            bool = self.grid.Longitude < -180.0
            self.grid.Longitude[bool] = self.grid.Longitude[bool] + 180.0

        if self.grid.Longitude.max() > 180.0:
            bool = self.grid.Longitude > 180.0
            self.grid.Longitude[bool] = self.grid.Longitude[bool] - 180

        if self.grid.Latitude.min() < -90.0:
            bool = self.grid.Latitude < -90.0
            self.grid.Latitude[bool] = self.grid.Latitude[bool] + 90.0

        if self.grid.Latitude.min() > 90.0:
            bool = self.grid.Latitude > 90.0
            self.grid.Latitude[bool] = self.grid.Latitude[bool] - 90.0

    def create_wind_field(self, wind_df, directory, filename):
        """Method to generate wind field. Derived from Holland 1980"""
        #ensure data is present
        if wind_df.empty:
            raise IOError('No data provided. Unable to create wind field.')

        # Create the grid for this wind field (from the dx, dy, ilat, ilon, swlat, swlon)
        if self.grid is None:
            self.make_owi_grid()

        for index in wind_df.index:
            # date = wind_df['Datetime'][index]
            # lat = wind_df['Latitude'][index]
            # lon = wind_df['Longitude'][index]
            # sf_kt = wind_df['sf_kt'][index]
            # Rw_nm = wind_df['Rw_nm'][index]
            # pc_mb = wind_df['pc_mb'][index]
            # deg = wind_df['deg'][index]
            # v_kt = wind_df['v_kt'][index]

            date = wind_df['Datetime'][index]
            lat = wind_df['Latitude'][index]
            lon = wind_df['Longitude'][index]
            sf_kt = wind_df['Max_Wind_Speed'][index]
            Rw_nm = wind_df['Radius'][index]
            pc_mb = wind_df['Pressure'][index]
            deg = wind_df['Direction_Degrees'][index]
            v_kt = wind_df['Direction_Speed'][index]

            # convert grid to np array format # todo there is probably a better way to do this
            gridarray = np.array(
                list(zip(self.grid.Longitude, self.grid.Latitude)))

            # set the hurricane center point
            pt = np.array([lon, lat])
            # force the second axis for matrix multiplication
            pt = pt[np.newaxis, ...]
            # calculate the distances from the hurricane point
            degrees = distance.cdist(gridarray, pt)
            degrees = degrees.reshape(len(degrees))
            # set the avg radius of the earth
            earth_radius = 6371 * 1000  # meters
            # convert degrees to radius
            r_array = (np.pi * earth_radius) * degrees / 180
            delta_lon = (np.pi * earth_radius) * (self.grid.Longitude.values -
                                                  lon) / 180  # todo check this
            delta_lat = (np.pi * earth_radius) * (self.grid.Latitude.values -
                                                  lat) / 180
            # ignore div by zero errors
            np.seterr(divide='ignore')
            # calculate thea
            theta_array = np.arctan2(delta_lat, delta_lon)

            #######################################
            # set standard assumptions
            # boundary layer adjustment factor
            beta = 0.9
            # density of air
            den_air = 1.15  # kgm^-3
            # density of water
            den_water = 997.  # kgm^-3
            # ambient atmospheric pressure https://www.britannica.com/science/atmospheric-pressure
            pn = 101325
            # Euler's number
            e = 2.718281
            # gravity
            grav = 9.80665  # ms^2
            # rotation rate of the earth
            omega = 7.2921e-5  # rad/s (2*pi/day)
            # sampling adjustment (converts 1 min winds to 10 min winds)
            ct = 0.88
            ########################################################
            # apply conversions
            # convert knots to mps
            sf = sf_kt * 0.5144444
            v = v_kt * 0.5144444
            # convert nm to meters
            Rw = Rw_nm * 1852
            # convert millibars to pascal
            pc = pc_mb * 100
            # convert translational speed to
            # velocity east
            vte = v * np.sin(deg * np.pi / 180)
            # velocity west
            vtn = v * np.cos(deg * np.pi / 180)
            ######################################################

            # calculate maximum storm wind speed at 10m
            sm = sf - np.sqrt(np.power(vte, 2) + np.power(vtn, 2))
            # calculate maximum velocity at the top of the atmospheric boundary layer
            Vm = sm / beta
            # calculate Holland's B parameter
            holland_b = den_air * e * np.power(Vm, 2) / (pn - pc)
            # ensure Holland's B parameter is within reasonable limits 1 < B < 2.5
            if holland_b < 1:
                # set to lower limit of 1
                holland_b = 1
            elif holland_b > 2.5:
                # set to upper limit of 2.5
                holland_b = 2.5

            # coriolis parameter for the storm's current location
            f = 2 * omega * np.sin(
                lat * np.pi / 180
            )  # todo replace with array # typical values about 10^-4 rads/s

            # for each node from the center of storm and it's radial angle

            # calculate pressure at each node
            # pr_array = (pc + (pn - pc) * np.exp(-np.power(Rw / r_array, holland_b))) / (den_water * grav)

            # calculate the raw gradient wid speed at each node
            # Vg = np.sqrt((np.power(Rw / r, holland_b) - np.exp(1 - np.power(Rw / r, holland_b)) * np.power(Vm, 2) +
            #               (np.power(float(r), 2) * np.power(f, 2) / 4))
            #              ) g- (r * f / 2) # orig - suspected typo
            Vg = np.sqrt((np.power(Rw / r_array, holland_b) *
                          np.exp(1 - np.power(Rw / r_array, holland_b)) *
                          np.power(Vm, 2) +
                          (np.power(r_array, 2) * np.power(f, 2) / 4))) - (
                              r_array * f / 2)  # derived from Holland 1980

            # translational adjustments to final wind speed in north and east directions
            vtan = np.abs(Vg / Vm) * vtn
            vtae = np.abs(Vg / Vm) * vte

            # for each node location, i, separate the velocity into components and apply beta
            Vei = -Vg * beta * np.sin(
                theta_array
            )  # todo: does this need to be adjusted for southern hemi?
            Vni = Vg * beta * np.cos(theta_array)

            # multiply by sampling time adjustment to convert 1min winds to 10min winds and add translation velocity
            Vfei = ct * Vei + vtae
            Vfni = ct * Vni + vtan

            date_string = datetime.datetime.strftime(date, '%Y%m%d%H%M')
            self.field1[date_string] = Vfei
            self.field2[date_string] = Vfni

        # set start and stop dates
        self.date_start = wind_df['Datetime'].min()
        self.date_stop = wind_df['Datetime'].max()

        self.write_owi_file(directory, filename)

    def write_owi_file(self, path, filename):
        """ Method to write owi formatted wind file from values in the owi object.

        Parameters
        ----------
        path - str
          Output directory
        filename - str
          Output filename (with extension)

        Returns
        -------


        """
        # construct full path
        full_path = os.path.join(path, filename)

        # ensure that all the required values have been set
        if self.ilat is None or self.ilon is None or self.dx is None or self.dy is None or \
                        self.swlat is None or self.swlon is None:
            raise IOError(
                'Parmeters defining the grid must be set into the OWI object (ilat, '
                'ilon, dx, dy, swlat, swlon)')
        if self.grid is None:
            raise IOError(
                'Grid must be constructed and set in the OWI object.')
        if self.field1 is None:
            raise IOError(
                'At least one field of data must be set in the OWI object')
        if self.number_fields == 2 and self.field2 is None:
            raise IOError(
                'Number of fields specified as 2, but no field2 data was found in the OWI object'
            )
        # if self.start_time is None and self.stop_time is None:
        #     raise IOError('Start time and stop time must be specified as %Y%m%d%H')
        # set the number of columns in the file (fixed OWI format)
        columns = 8

        # start_time = self.time_range[0]
        # stop_time = self.time_range[1]
        start_time = self.date_start  # todo
        stop_time = self.date_stop  # todo

        # open the file
        fp = open(full_path, 'w+', newline='')

        # construct file header
        file_header = 'Oceanweather WIN/PRE Format                            {0}     {1}\n'.\
            format(datetime.datetime.strftime(start_time, '%Y%m%d%H'),
                   datetime.datetime.strftime(stop_time, '%Y%m%d%H'))
        # write the file header
        fp.write(file_header)

        # loop over the timesteps in the data
        for time_string in self.field1.__iter__():
            # construct the timestep header
            timestep_header = 'iLat={:>4}iLong={:>4}DX={:01.4f}DY={:01.4f}SWLat={:08.5f}SWLon={:02.4f}DT={}\n'.format(
                self.ilat, self.ilon, self.dx, self.dy, self.swlat, self.swlon,
                time_string)
            # write the timestep header
            fp.write(timestep_header)

            # loop over the data fields
            for data in [self.field1[time_string].values, self.field2[time_string].values] if self.number_fields == 2 \
                    else [self.field1[time_string].values]:

                # determine the number of rows required per field and the remaining extra data columns
                (num_rows, rem_columns) = divmod(len(data), columns)

                # reformat into fixed width strings and construct the columns for the file
                fullrows = [[
                    '{:>10.4f}'.format(data[i + j]) for j in range(columns)
                ] for i in range(0,
                                 len(data) - rem_columns, 8)]
                # print each full row
                for row in fullrows:
                    row = ''.join(row)
                    fp.write(row + '\n')
                # print the last row of data (doesn't contain all the columns)
                lastrow = [
                    '{:>10.4f}'.format(data[num_rows * 8 + j])
                    for j in range(rem_columns)
                ]
                fp.write(''.join(lastrow) + '\n')

    def write_grid_xy(self, path, filename):
        """ Method to write owi grid file as XY from values in the owi object.

        Parameters
        ----------
        path - str
          Output directory
        filename - str
          Output filename (with extension)

        Returns
        -------


        """
        # construct full path
        full_path = os.path.join(path, filename)

        if self.grid is None:
            raise IOError(
                'Grid must be constructed and set in the OWI object.')

        self.grid.to_csv(full_path, sep='\t', index=False)

    def view(self):
        def time_field(time):
            vx = self.field1[time].values
            vy = self.field2[time].values
            xs = self.grid.Longitude.values
            ys = self.grid.Latitude.values
            with np.errstate(divide='ignore', invalid='ignore'):
                angle = np.arctan2(vy, vx)
            mag = np.sqrt(vx**2 + vy**2)
            return gv.VectorField(
                (xs, ys, angle, mag),
                vdims=['Angle', 'Magnitude'],
                crs=ccrs.PlateCarree()).redim.range(Magnitude=(0, 50))

        # tiles = gv.WMTS('https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{Z}/{Y}/{X}')
        vectors = hv.DynamicMap(
            time_field,
            kdims='Time').redim.values(Time=sorted(self.field1.keys()))

        return vectors.opts(size_index='Magnitude',
                            color_index='Magnitude',
                            pivot='tail',
                            width=700,
                            height=500,
                            colorbar=True,
                            scale=1,
                            cmap='OrRd')