From 35a2e4fbfad0a9282980c6f13dc96009c1906b5b Mon Sep 17 00:00:00 2001 From: Aki Date: Sun, 2 Apr 2023 17:16:39 +0200 Subject: Exploded naive prototype into modularized package --- waterspout_radar/__main__.py | 8 +++++++ waterspout_radar/_config.py | 2 +- waterspout_radar/_radar.py | 52 ++++++++++++++++++++++++++++++++++++++++++++ waterspout_radar/_storage.py | 18 +++++++++++++++ waterspout_radar/cli.py | 19 ++++++++++++++++ 5 files changed, 98 insertions(+), 1 deletion(-) create mode 100644 waterspout_radar/__main__.py create mode 100644 waterspout_radar/_radar.py create mode 100644 waterspout_radar/_storage.py create mode 100644 waterspout_radar/cli.py (limited to 'waterspout_radar') diff --git a/waterspout_radar/__main__.py b/waterspout_radar/__main__.py new file mode 100644 index 0000000..ec840b5 --- /dev/null +++ b/waterspout_radar/__main__.py @@ -0,0 +1,8 @@ +import sys + +from . import cli + +if __name__ == "__main__": + if "__main__" in sys.arg[0]: + sys.argv[0] = "waterspout_radar" + cli.main() diff --git a/waterspout_radar/_config.py b/waterspout_radar/_config.py index 36d9c0b..eaa6c58 100644 --- a/waterspout_radar/_config.py +++ b/waterspout_radar/_config.py @@ -39,7 +39,7 @@ class ConfigError(Exception): "Raised when configuration could not be loaded or was malformed." -def load(pathname: str="radar.conf") -> Config: +def load(pathname: str) -> Config: "Loads configuration from a file at *pathname*. May raise ConfigError when content does not meet expectations." config = configparser.ConfigParser(allow_no_value=True) config.read(pathname) diff --git a/waterspout_radar/_radar.py b/waterspout_radar/_radar.py new file mode 100644 index 0000000..629ec55 --- /dev/null +++ b/waterspout_radar/_radar.py @@ -0,0 +1,52 @@ +import dataclasses +import datetime +import typing + +import szilagyi +from geopy import geocoders +from metpy import calc +from metpy import units as metunits +from windy import point_forecast, Windy + +_L = point_forecast.Level + + +@dataclasses.dataclass +class Prediction: + time: datetime.datetime + swi: float + + +def calculate(config) -> typing.List[Prediction]: + units = metunits.units + windy = Windy(units) + + def _calculate(latitude, longitude): + forecasts = windy.point_forecast( + config.key, + latitude, longitude, + point_forecast.Model.ICONEU, + ("temp", "dewpoint", "wind", "pressure"), + tuple(_L)) + for cast in forecasts: + dt = abs(cast.at("temp", _L.H850) - cast.at("temp", _L.SURFACE)) + pressure, _ = calc.lcl( + cast.at("pressure", _L.SURFACE), + cast.at("temp", _L.SURFACE), + cast.at("dewpoint", _L.SURFACE)) + lcl = calc.pressure_to_height_std(pressure) + pressure, _ = calc.el(cast["pressure"], cast["temp"], cast["dewpoint"]) + el = calc.pressure_to_height_std(pressure) + ccd = (el - lcl).to(units.ft) + try: + swi = szilagyi.calculate_swi(dt, ccd) + except ValueError: + swi = -10 + yield Prediction(time=cast.timestamp, swi=swi) + + predictions = [] + locator = geocoders.Nominatim(user_agent="waterspout-radar") + for location in config.locations: + found = locator.geocode(location) + predictions.extend(_calculate(found.latitude, found.longitude)) + return predictions diff --git a/waterspout_radar/_storage.py b/waterspout_radar/_storage.py new file mode 100644 index 0000000..a410259 --- /dev/null +++ b/waterspout_radar/_storage.py @@ -0,0 +1,18 @@ +import datetime +import typing + +from . import _radar + +Period = typing.Tuple[datetime.datetime, datetime.datetime] + + +class Storage: + def __init__(self, pathname): + pass + + def show(self, period: Period=None): + return [] + + def extend(self, predictions: typing.Iterable[_radar.Prediction]): + for prediction in predictions: + print(prediction) diff --git a/waterspout_radar/cli.py b/waterspout_radar/cli.py new file mode 100644 index 0000000..ecf79d8 --- /dev/null +++ b/waterspout_radar/cli.py @@ -0,0 +1,19 @@ +import argparse + +from . import _config, _radar, _storage + + +def main(): + parser = argparse.ArgumentParser() + parser.add_argument( + "-c", "--config", default="radar.conf", + help="Pathname of the instance configuration file (default: %(default)s)") + args = parser.parse_args() + config = _config.load(args.config) + storage = _storage.Storage(config.db) + predictions = _radar.calculate(config) + storage.extend(predictions) + + +if __name__ == "__main__": + main() -- cgit v1.1