%PDF- %PDF-
| Direktori : /data/old/usr/local/lib/python3.6/site-packages/astral/ |
| Current File : //data/old/usr/local/lib/python3.6/site-packages/astral/__init__.py |
# -*- coding: utf-8 -*-
# Copyright 2009-2019, Simon Kennedy, sffjunkie+code@gmail.com
# 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.
"""Calculations for the position of the sun and moon.
The :mod:`astral` package provides the means to calculate the following times of the sun
* dawn
* sunrise
* noon
* midnight
* sunset
* dusk
* daylight
* night
* twilight
* blue hour
* golden hour
* rahukaalam
plus solar azimuth and elevation at a specific latitude/longitude.
It can also calculate the moon phase for a specific date.
The package also provides a self contained geocoder to turn a small set of
location names into timezone, latitude and longitude. The lookups
can be perfomed using the :func:`~astral.geocoder.lookup` function defined in
:mod:`astral.geocoder`
.. note::
The `Astral` and `GoogleGeocoder` classes from earlier versions have been
removed.
"""
import datetime
import re
from dataclasses import dataclass
from enum import Enum
from typing import Optional, Tuple, Union
try:
import pytz
except ImportError:
raise ImportError(("The astral module requires the pytz module to be available."))
__all__ = [
"Depression",
"SunDirection",
"Observer",
"LocationInfo",
"now",
"today",
"dms_to_float",
]
__version__ = "2.2"
__author__ = "Simon Kennedy <sffjunkie+code@gmail.com>"
Elevation = Union[float, Tuple[float, float]]
def now(tzinfo: datetime.tzinfo = pytz.utc) -> datetime.datetime:
"""Returns the current time in the specified time zone"""
return pytz.utc.localize(datetime.datetime.utcnow()).astimezone(tzinfo)
def today(tzinfo: datetime.tzinfo = pytz.utc) -> datetime.date:
"""Returns the current date in the specified time zone"""
return now(tzinfo).date()
def dms_to_float(dms: Union[str, float, Elevation], limit: Optional[float] = None) -> float:
"""Converts as string of the form `degrees°minutes'seconds"[N|S|E|W]`,
or a float encoded as a string, to a float
N and E return positive values
S and W return negative values
Args:
dms: string to convert
limit: Limit the value between ± `limit`
Returns:
The number of degrees as a float
"""
try:
res = float(dms) # type: ignore
except (ValueError, TypeError):
_dms_re = r"(?P<deg>\d{1,3})[°]((?P<min>\d{1,2})[′'])?((?P<sec>\d{1,2})[″\"])?(?P<dir>[NSEW])?"
m = re.match(_dms_re, str(dms), flags=re.IGNORECASE)
if m:
deg = m.group("deg") or 0.0
min_ = m.group("min") or 0.0
sec = m.group("sec") or 0.0
dir_ = m.group("dir") or "E"
res = float(deg)
if min_:
res += float(min_) / 60
if sec:
res += float(sec) / 3600
if dir_.upper() in ["S", "W"]:
res = -res
else:
raise ValueError("Unable to convert degrees/minutes/seconds to float")
if limit:
if res > limit:
res = limit
elif res < -limit:
res = -limit
return res
class Depression(Enum):
"""The depression angle in degrees for the dawn/dusk calculations"""
CIVIL: float = 6.0
NAUTICAL: float = 12.0
ASTRONOMICAL: float = 18.0
class SunDirection(Enum):
"""Direction of the sun either RISING or SETTING"""
RISING = 1
SETTING = -1
@dataclass
class Observer:
"""Defines the location of an observer on Earth.
Latitude and longitude can be set either as a float or as a string.
For strings they must be of the form
degrees°minutes'seconds"[N|S|E|W] e.g. 51°31'N
`minutes’` & `seconds”` are optional.
Elevations are either
* A float that is the elevation in metres above a location, if the nearest
obscuring feature is the horizon
* or a tuple of the elevation in metres and the distance in metres to the
nearest obscuring feature.
Args:
latitude: Latitude - Northern latitudes should be positive
longitude: Longitude - Eastern longitudes should be positive
elevation: Elevation and/or distance to nearest obscuring feature
in metres above/below the location.
"""
latitude: float = 51.4733
longitude: float = -0.0008333
elevation: Elevation = 0.0
def __setattr__(self, name: str, value: Union[str, float, Elevation]):
if name == "latitude":
value = dms_to_float(value, 90.0)
elif name == "longitude":
value = dms_to_float(value, 180.0)
elif name == "elevation":
if isinstance(value, tuple):
value = (float(value[0]), float(value[1]))
else:
value = float(value)
super().__setattr__(name, value)
@dataclass
class LocationInfo:
"""Defines a location on Earth.
Latitude and longitude can be set either as a float or as a string. For strings they must
be of the form
degrees°minutes'seconds"[N|S|E|W] e.g. 51°31'N
`minutes’` & `seconds”` are optional.
Args:
name: Location name (can be any string)
region: Region location is in (can be any string)
timezone: The location's time zone (a list of time zone names can be obtained from
`pytz.all_timezones`)
latitude: Latitude - Northern latitudes should be positive
longitude: Longitude - Eastern longitudes should be positive
"""
name: str = "Greenwich"
region: str = "England"
timezone: str = "Europe/London"
latitude: float = 51.4733
longitude: float = -0.0008333
def __setattr__(self, name: str, value: Union[float, str]):
if name == "latitude":
value = dms_to_float(value, 90.0)
elif name == "longitude":
value = dms_to_float(value, 180.0)
super().__setattr__(name, value)
@property
def observer(self):
"""Return an Observer at this location"""
return Observer(self.latitude, self.longitude, 0.0)
@property
def tzinfo(self):
"""Return a pytz timezone for this location"""
return pytz.timezone(self.timezone)
@property
def timezone_group(self):
return self.timezone.split("/")[0]