# coding=utf-8
# Copyright 2013-2021 The Salish Sea MEOPAR contributors
# and The University of British Columbia
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
# http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Salish Sea NEMO model unit conversion functions and constants.
"""
from __future__ import division
import numpy as np
# For convenience we import the TEOS-10 salinity conversion factors
# and functions so that they can be used from either here or the
# :py:mod:`salishsea_tools.teos_tools` module
from salishsea_tools.teos_tools import (
PSU_TEOS,
TEOS_PSU,
psu_teos,
teos_psu,
)
__all__ = [
'PSU_TEOS', 'TEOS_PSU', 'psu_teos', 'teos_psu',
'M_PER_S__KM_PER_HR', 'M_PER_S__KNOTS', 'mps_kph', 'mps_knots',
'wind_to_from', 'bearing_heading',
'humanize_time_of_day',
]
#: Conversion factor from m/s to km/hr
M_PER_S__KM_PER_HR = 3600 / 1000
#: Conversion factor from m/s to knots
M_PER_S__KNOTS = 3600 / 1852
#: Conversion factor from knots to m/s
KNOTS__M_PER_S = 1852 / 3600
[docs]
def mps_kph(m_per_s):
"""Convert speed from m/s to km/hr.
:kbd:`m_per_s` may be either a scalar number or a
:py:class:`numpy.ndarray` object,
and the return value will be of the same type.
:arg m_per_s: Speed in m/s to convert.
:returns: Speed in km/hr.
"""
return m_per_s * M_PER_S__KM_PER_HR
[docs]
def mps_knots(m_per_s):
"""Convert speed from m/s to knots.
:kbd:`m_per_s` may be either a scalar number or a
:py:class:`numpy.ndarray` object,
and the return value will be of the same type.
:arg m_per_s: Speed in m/s to convert.
:returns: Speed in knots.
"""
return m_per_s * M_PER_S__KNOTS
def knots_mps(knots):
"""Convert speed from knots to m/s.
:kbd:`knots` may be either a scalar number or a
:py:class:`numpy.ndarray` object,
and the return value will be of the same type.
:arg knots: Speed in knots to convert.
:returns: Speed in m/s.
"""
return knots * KNOTS__M_PER_S
[docs]
def wind_to_from(wind_to):
"""Convert wind bearing from "physics" compass "to" direction to
"human"compass "from" direction.
"Physics" compass has zero at the east and bearing angles increase
in the counter-clockwise direction.
"Human" compass has zero at the north and bearing angles increase in
the clockwise direction.
Example: 0° on the physics compass indicates air flow to the east
which is called a west (270°) wind on the human compass.
:kbd:`wind_to` may be either a scalar number or a
:py:class:`numpy.ndarray` object,
and the return value will be of the same type.
:arg wind_to: Bearing on physics (to) compass to convert.
:returns: Bearing on human (from) compass.
"""
try:
# Scalar value case
return 270 - wind_to if wind_to <= 270 else 270 - wind_to + 360
except ValueError:
# N-dimensional array case
mask_le_270, mask_gt_270 = wind_to <= 270, wind_to > 270
wind_from = np.array(wind_to, copy=True)
wind_from[mask_le_270] = 270 - wind_to[mask_le_270]
wind_from[mask_gt_270] = 270 - wind_to[mask_gt_270] + 360
return wind_from
[docs]
def bearing_heading(
bearing,
headings=(
'N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S',
'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW', 'N'),
):
"""Convert a compass bearing to a heading.
:arg float bearing: The compass bearing to convert.
:arg sequence headings: A sequence of headings to convert the bearing
to.
The default is the 16 compass points that
divide the compass into 22.5° segments.
:returns: Compass heading.
:rtype: str
"""
return headings[int(round(bearing * (len(headings) - 1) / 360, 0))]
[docs]
def humanize_time_of_day(date_time):
"""Return a "humanized" time of day string like "early Monday afternoon"
that corresponds to :kbd:`date_time`.
We use the same terminology as is used in the Environment Canada
weather forecasts:
https://www.ec.gc.ca/meteo-weather/default.asp?lang=En&n=10220A6B-1#c4
:arg date_time: Date/time to humanize.
:type date_time: :py:class:`arrow.Arrow`
:returns: Humanized time of day and day name;
e.g. early Monday afternoon
:rtype: str
"""
day_of_week = date_time.format('dddd')
if date_time.hour < 6:
part_of_day = ''
early_late = 'overnight'
elif date_time.hour < 12:
part_of_day = 'morning'
early_late = 'early' if date_time.hour < 9 else 'late'
elif 12 <= date_time.hour < 18:
part_of_day = 'afternoon'
early_late = 'early' if date_time.hour < 15 else 'late'
else:
part_of_day = 'evening'
early_late = 'early' if date_time.hour < 21 else 'late'
return ' '.join((early_late, day_of_week, part_of_day)).rstrip()