hurdat2py 0.3.5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) Andy McKeen
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,176 @@
1
+ Metadata-Version: 2.4
2
+ Name: hurdat2py
3
+ Version: 0.3.5
4
+ Summary: A custom HURDAT2 data parser.
5
+ Author-email: Andy McKeen <andymckeen648@gmail.com>
6
+ Requires-Python: >=3.7
7
+ Description-Content-Type: text/markdown
8
+ License-File: LICENSE
9
+ Requires-Dist: matplotlib
10
+ Requires-Dist: cartopy
11
+ Requires-Dist: numpy
12
+ Requires-Dist: pandas
13
+ Dynamic: license-file
14
+
15
+ <div align="center">
16
+ <img src="https://github.com/andy-theia/hurdat2py/blob/main/hurdat2py_logo_v1.png?raw=true" width="600" alt="hurdat2py logo">
17
+ </div>
18
+
19
+ [![PyPI version](https://img.shields.io/pypi/v/hurdat2py.svg)](https://pypi.org/project/hurdat2py/)
20
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
21
+ [![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/)
22
+
23
+ **hurdat2py** is a research-focused Python interface for the NOAA HURDAT2 Dataset. It automates data retrieval and parsing, giving you immediate access to clean, analysis-ready data.
24
+ * Note: Currently only the North Atlantic Basin is supported. See [Roadmap](#roadmap) for more info.
25
+
26
+ ## Table of Contents
27
+ 1. [Installation](#installation)
28
+ 2. [Quick Start](#quick-start)
29
+ 3. [API Reference](#api-reference)
30
+ - [The Database (Hurdat2)](#1-the-database-hurdat2)
31
+ - [The Storm Object](#2-the-storm-object)
32
+ - [The Season Object](#3-the-season-object)
33
+ 4. [Roadmap](#roadmap)
34
+ 5. [Changelog](#changelog)
35
+ 6. [Attribution](#attribution--data-sources)
36
+
37
+ ---
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install hurdat2py
43
+ ```
44
+ ## Quick Start
45
+
46
+ ### 1. Initialize the Database
47
+ * You can specify a path to a local download of the Hurdat2 dataset.
48
+ ```python
49
+ import hurdat2py
50
+
51
+ hd2 = hurdat2py.Hurdat2("path_to_file.txt")
52
+ ```
53
+ * If no path is specified, it will automatically download the latest data from [https://www.nhc.noaa.gov/data/hurdat/](https://www.nhc.noaa.gov/data/hurdat/) (~7MB) and save it locally.
54
+ * Note: The cached file will be overwritten after 30 days to ensure the latest file is available.
55
+
56
+ ```python
57
+ import hurdat2py
58
+
59
+ hd2 = hurdat2py.Hurdat2()
60
+ ```
61
+
62
+ ### 2. Create `Storm` or `Season` objects
63
+ * Allows for easy access to storm or season data.
64
+ ```python
65
+ # Storm obect:
66
+ storm = hd2['bob', 1991]
67
+ # or
68
+ storm = hd2['al031991']
69
+
70
+ # Season object:
71
+ season = hd2[1991]
72
+ ```
73
+ * See [The Storm Object](#2-the-storm-object) or [The Season Object](#3-the-season-object) for more info.
74
+
75
+ ## API Reference
76
+ ### 1. The Database (Hurdat2)
77
+ The main entry point. Handles downloading, caching, and parsing the raw text data.
78
+
79
+ Method | Description | Example
80
+ :--- | :--- | :---
81
+ `hurdat2py.Hurdat2()` | Initializes the database. Downloads latest data if local cache is missing/expired. | `hd2 = hurdat2py.Hurdat2()`
82
+ `db['name', year]`| **Get Storm (by Name)***. Returns a `Storm` object. Case-insensitive. <br> <br> *This method will not work for storms named UNNAMED, especially before 1950. | `storm = hd2['bob', 1991]`
83
+ `db[atcfid]` | **Get Storm (by ID)**. Returns a `Storm` object using ATCFID. | `storm = hd2['al031991']`
84
+ `db[year]` | **Get Season**. Returns a `Season` object for the specified year. | `season = hd2[1991]`
85
+ `rank_seasons_by_ace()` | Returns a sorted list of seasons by Accumulated Cyclone Energy (`float`). | `top5 = hd2.rank_seasons_by_ace()[:5]`
86
+
87
+ ### 2. The `Storm` Object
88
+ Represents a single tropical cyclone.
89
+
90
+ Method/Attribute | Description | Example
91
+ :--- | :--- | :---
92
+ `name` | Operational name (e.g., "Bob") (`str`). | `print(storm.name)`
93
+ `atcfid` | ATCF ID (e.g., "AL031991") (`str`). | `print(storm.atcfid)`
94
+ `year` | The year the storm formed (`int`). | `print(storm.year)`
95
+ `ace` | Accumulated Cyclone Energy (10^-4 kn^2) (`float`). | `print(storm.ace)`
96
+ `peak_wind` | Maximum sustained wind speed (knots) (`int`). | `print(storm.peak_wind)`
97
+ `peak_status` | Highest tropical classification the storm achieved (MH, HU, TS, SS, TD, SD) (`str`). | `print(storm.peak_status)`
98
+ `min_pressure` | Minimum central pressure (mb) (`int`). | `print(storm.min_pressure)`
99
+ `landfalls` | Number of landfalls recorded* (`int`). <br> <br>*Note: not every landfall is recorded in the Hurdat2 dataset. | `print(storm.landfalls)`
100
+ `lats`/`lons` | Raw list of latitude/longitude points. | `ax.plot(storm.lons, storm.lats)`
101
+ `duration_total` <br> `duration_tc` <br> `duration_ts` <br> `duration_hurricane` <br> `duration_major` | Duration of the system in hours, while at specified status (`float`) | `print(storm.duration_tc)`
102
+ `distance_total` <br> `distance_tc` <br> `distance_ts` <br> `distance_hurricane` <br> `distance_major` | Distance* the system travelled in Nautical Miles, while at specified status (`float`) <br> <br> *Note: Distances calculated using the [Haversine Formula](https://en.wikipedia.org/wiki/Haversine_formula). | `print(storm.distance_major)`
103
+ `info()` | Prints quick overview of the storm. | `storm.info()`
104
+ `stats()` | Prints statistics and detailed information about the storm. | `storm.stats()`
105
+ `plot()` | Plots storm track, colored by Saffir-Simpson intensity. Plot aesthetic is inspired by the tropycal package. | `storm.plot()`
106
+ `plot_intensity()` | Plots the storm's windspeed over time, colored by Saffir-Simpson intensity. <br> Kwargs: <br> zoom (`bool`) Crops into the intensity curve. <br> landfalls (`bool`) Plots landfall times on intensity curve. | storm.plot_intensity(zoom=True, landfalls=False)
107
+ `to_dataframe()` | Exports track data (date, time, lat, lon, wind, pressure) to a Pandas DataFrame. | `df = storm.to_dataframe()`
108
+
109
+ ### 3. The `Season` Object
110
+ Represents a full year of activity.
111
+
112
+ Method/Attribute | Description | Example
113
+ :--- | :--- | :---
114
+ `year` | Returns the year of the season (`int`) | `print(season.year)`
115
+ `storms` | List of `Storm` objects within the season. | `print(season.storms)`
116
+ `total_storms` | Number of storms in the season (`int`). Effectively `len(season.storms)` | `print(season_total_storms)`
117
+ `tropical_storms` | Number of tropical storms in the season (`int`). | `print(season.tropical_storms)`
118
+ `hurricanes` | Number of hurricanes in the season (`int`). | `print(season.hurricanes)`
119
+ `major_hurricanes` | Number of major hurricanes in the season (`int`). | `print(season.major_hurricanes)`
120
+ `ace` | Total Accumulated Cyclone Energy for the season (`float`). | `print(season.ace)`
121
+ `stats()` | Prints statistics and detailed information about the season. | `season.stats()`
122
+ `plot()` | Plots all storm tracks for the season, colored by Saffir-Simpson intensity. Plot aesthetic is inspired by the tropycal package. <br> Kwargs: <br> labels (`bool`) Adds labels for each storm track. | `season.plot(labels=True)`
123
+ `to_dataframe()` | Exports track data (date, time, lat, lon, wind, pressure) for all storms in the season to a Pandas DataFrame. | `df = season.to_dataframe()`
124
+
125
+ ## Roadmap
126
+ ### Future planned updates:
127
+ * **Northeast Pacific Support**: Add support for the NEPAC Hurdat2 dataset.
128
+ * Expected: Spring 2026
129
+ * **Statistics Improvements**: Implement new statistics functionality for `Storm` and `Season` objects.
130
+ * Expected: Summer 2026
131
+ * **Improved Plotting Functionality**: Add/improve plotting functions.
132
+ * Expected: Summer 2026
133
+ * **Wind Radius Implementation**: Add support for and methods utilizing wind radius data included with modern records in the Hurdat2 dataset.
134
+ * Expected: Summer 2027
135
+
136
+ ## Changelog
137
+ ### v0.3.5 (2026-01-22)
138
+ * Documentation: Add logo to README.
139
+
140
+ ### v0.3.4 (2026-01-15)
141
+ * Documentation: Minor updates to README.
142
+ * Launch: Initial release on [GitHub](https://github.com/andy-theia/hurdat2py).
143
+
144
+ ### v0.3.3 (2026-01-15)
145
+ * Documentation: Major overhaul of README to include API reference tables and additional information.
146
+
147
+ ### v0.3.2 (2026-01-12)
148
+ * Documentation: Updates to README.
149
+
150
+ ### v0.3.1 (2026-01-12)
151
+ * Launch: Initial release on [PyPI](https://pypi.org/project/hurdat2py/).
152
+ * Structure: Package structure overhaul for organization.
153
+
154
+ ## Attribution & Data Sources
155
+ ### **Data**
156
+ * This package processes data from the **National Hurricane Center (NHC) [HURDAT2 Database](https://www.nhc.noaa.gov/data/hurdat/)**.
157
+ * Landsea, C. W. and J. L. Franklin, 2013: Atlantic Hurricane Database Uncertainty and Presentation of a New Database Format. Mon. Wea. Rev., 141, 3576-3592.
158
+
159
+ ### Acknowledgements
160
+ * **Inspiration**: This work was inspired by the great [hurdat2parser](https://pypi.org/project/hurdat2parser/) package. There are many technical capabilities of hurdat2parser that we do not seek to replicate. We recommend you choose whichever package best suites your needs.
161
+
162
+ * **Plotting Style**: The map visualization aesthetic in this package was inspired by the excellent [tropycal](https://tropycal.github.io/tropycal/index.html) package. While `hurdat2py` is a standalone implementation, we aimed to match their clear, publication-ready visual style.
163
+
164
+ ## License
165
+ MIT License. See LICENSE file for details.
166
+
167
+ ## Disclaimer
168
+ `hurdat2py` is maintained for personal research and is not an official NOAA product.
169
+
170
+ Python® and the Python logo are registered trademarks of the Python Software Foundation. `hurdat2py` is an independent open-source project and is not affiliated with or endorsed by the Python Software Foundation.
171
+
172
+ ## Copyright
173
+ **hurdat2py** <br>
174
+ Copyright © 2026 Andy McKeen <br>
175
+ License: MIT <br>
176
+ GitHub: [https://github.com/andy-theia/hurdat2py](https://github.com/andy-theia/hurdat2py)
@@ -0,0 +1,162 @@
1
+ <div align="center">
2
+ <img src="https://github.com/andy-theia/hurdat2py/blob/main/hurdat2py_logo_v1.png?raw=true" width="600" alt="hurdat2py logo">
3
+ </div>
4
+
5
+ [![PyPI version](https://img.shields.io/pypi/v/hurdat2py.svg)](https://pypi.org/project/hurdat2py/)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+ [![Python 3.7+](https://img.shields.io/badge/python-3.7+-blue.svg)](https://www.python.org/downloads/)
8
+
9
+ **hurdat2py** is a research-focused Python interface for the NOAA HURDAT2 Dataset. It automates data retrieval and parsing, giving you immediate access to clean, analysis-ready data.
10
+ * Note: Currently only the North Atlantic Basin is supported. See [Roadmap](#roadmap) for more info.
11
+
12
+ ## Table of Contents
13
+ 1. [Installation](#installation)
14
+ 2. [Quick Start](#quick-start)
15
+ 3. [API Reference](#api-reference)
16
+ - [The Database (Hurdat2)](#1-the-database-hurdat2)
17
+ - [The Storm Object](#2-the-storm-object)
18
+ - [The Season Object](#3-the-season-object)
19
+ 4. [Roadmap](#roadmap)
20
+ 5. [Changelog](#changelog)
21
+ 6. [Attribution](#attribution--data-sources)
22
+
23
+ ---
24
+
25
+ ## Installation
26
+
27
+ ```bash
28
+ pip install hurdat2py
29
+ ```
30
+ ## Quick Start
31
+
32
+ ### 1. Initialize the Database
33
+ * You can specify a path to a local download of the Hurdat2 dataset.
34
+ ```python
35
+ import hurdat2py
36
+
37
+ hd2 = hurdat2py.Hurdat2("path_to_file.txt")
38
+ ```
39
+ * If no path is specified, it will automatically download the latest data from [https://www.nhc.noaa.gov/data/hurdat/](https://www.nhc.noaa.gov/data/hurdat/) (~7MB) and save it locally.
40
+ * Note: The cached file will be overwritten after 30 days to ensure the latest file is available.
41
+
42
+ ```python
43
+ import hurdat2py
44
+
45
+ hd2 = hurdat2py.Hurdat2()
46
+ ```
47
+
48
+ ### 2. Create `Storm` or `Season` objects
49
+ * Allows for easy access to storm or season data.
50
+ ```python
51
+ # Storm obect:
52
+ storm = hd2['bob', 1991]
53
+ # or
54
+ storm = hd2['al031991']
55
+
56
+ # Season object:
57
+ season = hd2[1991]
58
+ ```
59
+ * See [The Storm Object](#2-the-storm-object) or [The Season Object](#3-the-season-object) for more info.
60
+
61
+ ## API Reference
62
+ ### 1. The Database (Hurdat2)
63
+ The main entry point. Handles downloading, caching, and parsing the raw text data.
64
+
65
+ Method | Description | Example
66
+ :--- | :--- | :---
67
+ `hurdat2py.Hurdat2()` | Initializes the database. Downloads latest data if local cache is missing/expired. | `hd2 = hurdat2py.Hurdat2()`
68
+ `db['name', year]`| **Get Storm (by Name)***. Returns a `Storm` object. Case-insensitive. <br> <br> *This method will not work for storms named UNNAMED, especially before 1950. | `storm = hd2['bob', 1991]`
69
+ `db[atcfid]` | **Get Storm (by ID)**. Returns a `Storm` object using ATCFID. | `storm = hd2['al031991']`
70
+ `db[year]` | **Get Season**. Returns a `Season` object for the specified year. | `season = hd2[1991]`
71
+ `rank_seasons_by_ace()` | Returns a sorted list of seasons by Accumulated Cyclone Energy (`float`). | `top5 = hd2.rank_seasons_by_ace()[:5]`
72
+
73
+ ### 2. The `Storm` Object
74
+ Represents a single tropical cyclone.
75
+
76
+ Method/Attribute | Description | Example
77
+ :--- | :--- | :---
78
+ `name` | Operational name (e.g., "Bob") (`str`). | `print(storm.name)`
79
+ `atcfid` | ATCF ID (e.g., "AL031991") (`str`). | `print(storm.atcfid)`
80
+ `year` | The year the storm formed (`int`). | `print(storm.year)`
81
+ `ace` | Accumulated Cyclone Energy (10^-4 kn^2) (`float`). | `print(storm.ace)`
82
+ `peak_wind` | Maximum sustained wind speed (knots) (`int`). | `print(storm.peak_wind)`
83
+ `peak_status` | Highest tropical classification the storm achieved (MH, HU, TS, SS, TD, SD) (`str`). | `print(storm.peak_status)`
84
+ `min_pressure` | Minimum central pressure (mb) (`int`). | `print(storm.min_pressure)`
85
+ `landfalls` | Number of landfalls recorded* (`int`). <br> <br>*Note: not every landfall is recorded in the Hurdat2 dataset. | `print(storm.landfalls)`
86
+ `lats`/`lons` | Raw list of latitude/longitude points. | `ax.plot(storm.lons, storm.lats)`
87
+ `duration_total` <br> `duration_tc` <br> `duration_ts` <br> `duration_hurricane` <br> `duration_major` | Duration of the system in hours, while at specified status (`float`) | `print(storm.duration_tc)`
88
+ `distance_total` <br> `distance_tc` <br> `distance_ts` <br> `distance_hurricane` <br> `distance_major` | Distance* the system travelled in Nautical Miles, while at specified status (`float`) <br> <br> *Note: Distances calculated using the [Haversine Formula](https://en.wikipedia.org/wiki/Haversine_formula). | `print(storm.distance_major)`
89
+ `info()` | Prints quick overview of the storm. | `storm.info()`
90
+ `stats()` | Prints statistics and detailed information about the storm. | `storm.stats()`
91
+ `plot()` | Plots storm track, colored by Saffir-Simpson intensity. Plot aesthetic is inspired by the tropycal package. | `storm.plot()`
92
+ `plot_intensity()` | Plots the storm's windspeed over time, colored by Saffir-Simpson intensity. <br> Kwargs: <br> zoom (`bool`) Crops into the intensity curve. <br> landfalls (`bool`) Plots landfall times on intensity curve. | storm.plot_intensity(zoom=True, landfalls=False)
93
+ `to_dataframe()` | Exports track data (date, time, lat, lon, wind, pressure) to a Pandas DataFrame. | `df = storm.to_dataframe()`
94
+
95
+ ### 3. The `Season` Object
96
+ Represents a full year of activity.
97
+
98
+ Method/Attribute | Description | Example
99
+ :--- | :--- | :---
100
+ `year` | Returns the year of the season (`int`) | `print(season.year)`
101
+ `storms` | List of `Storm` objects within the season. | `print(season.storms)`
102
+ `total_storms` | Number of storms in the season (`int`). Effectively `len(season.storms)` | `print(season_total_storms)`
103
+ `tropical_storms` | Number of tropical storms in the season (`int`). | `print(season.tropical_storms)`
104
+ `hurricanes` | Number of hurricanes in the season (`int`). | `print(season.hurricanes)`
105
+ `major_hurricanes` | Number of major hurricanes in the season (`int`). | `print(season.major_hurricanes)`
106
+ `ace` | Total Accumulated Cyclone Energy for the season (`float`). | `print(season.ace)`
107
+ `stats()` | Prints statistics and detailed information about the season. | `season.stats()`
108
+ `plot()` | Plots all storm tracks for the season, colored by Saffir-Simpson intensity. Plot aesthetic is inspired by the tropycal package. <br> Kwargs: <br> labels (`bool`) Adds labels for each storm track. | `season.plot(labels=True)`
109
+ `to_dataframe()` | Exports track data (date, time, lat, lon, wind, pressure) for all storms in the season to a Pandas DataFrame. | `df = season.to_dataframe()`
110
+
111
+ ## Roadmap
112
+ ### Future planned updates:
113
+ * **Northeast Pacific Support**: Add support for the NEPAC Hurdat2 dataset.
114
+ * Expected: Spring 2026
115
+ * **Statistics Improvements**: Implement new statistics functionality for `Storm` and `Season` objects.
116
+ * Expected: Summer 2026
117
+ * **Improved Plotting Functionality**: Add/improve plotting functions.
118
+ * Expected: Summer 2026
119
+ * **Wind Radius Implementation**: Add support for and methods utilizing wind radius data included with modern records in the Hurdat2 dataset.
120
+ * Expected: Summer 2027
121
+
122
+ ## Changelog
123
+ ### v0.3.5 (2026-01-22)
124
+ * Documentation: Add logo to README.
125
+
126
+ ### v0.3.4 (2026-01-15)
127
+ * Documentation: Minor updates to README.
128
+ * Launch: Initial release on [GitHub](https://github.com/andy-theia/hurdat2py).
129
+
130
+ ### v0.3.3 (2026-01-15)
131
+ * Documentation: Major overhaul of README to include API reference tables and additional information.
132
+
133
+ ### v0.3.2 (2026-01-12)
134
+ * Documentation: Updates to README.
135
+
136
+ ### v0.3.1 (2026-01-12)
137
+ * Launch: Initial release on [PyPI](https://pypi.org/project/hurdat2py/).
138
+ * Structure: Package structure overhaul for organization.
139
+
140
+ ## Attribution & Data Sources
141
+ ### **Data**
142
+ * This package processes data from the **National Hurricane Center (NHC) [HURDAT2 Database](https://www.nhc.noaa.gov/data/hurdat/)**.
143
+ * Landsea, C. W. and J. L. Franklin, 2013: Atlantic Hurricane Database Uncertainty and Presentation of a New Database Format. Mon. Wea. Rev., 141, 3576-3592.
144
+
145
+ ### Acknowledgements
146
+ * **Inspiration**: This work was inspired by the great [hurdat2parser](https://pypi.org/project/hurdat2parser/) package. There are many technical capabilities of hurdat2parser that we do not seek to replicate. We recommend you choose whichever package best suites your needs.
147
+
148
+ * **Plotting Style**: The map visualization aesthetic in this package was inspired by the excellent [tropycal](https://tropycal.github.io/tropycal/index.html) package. While `hurdat2py` is a standalone implementation, we aimed to match their clear, publication-ready visual style.
149
+
150
+ ## License
151
+ MIT License. See LICENSE file for details.
152
+
153
+ ## Disclaimer
154
+ `hurdat2py` is maintained for personal research and is not an official NOAA product.
155
+
156
+ Python® and the Python logo are registered trademarks of the Python Software Foundation. `hurdat2py` is an independent open-source project and is not affiliated with or endorsed by the Python Software Foundation.
157
+
158
+ ## Copyright
159
+ **hurdat2py** <br>
160
+ Copyright © 2026 Andy McKeen <br>
161
+ License: MIT <br>
162
+ GitHub: [https://github.com/andy-theia/hurdat2py](https://github.com/andy-theia/hurdat2py)
@@ -0,0 +1,18 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "hurdat2py"
7
+ version = "0.3.5"
8
+ authors = [
9
+ { name="Andy McKeen", email="andymckeen648@gmail.com" },
10
+ ]
11
+ description = "A custom HURDAT2 data parser."
12
+ readme = "README.md"
13
+ requires-python = ">=3.7"
14
+ dependencies = [
15
+ "matplotlib",
16
+ "cartopy",
17
+ "numpy",
18
+ "pandas"]
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,4 @@
1
+ from setuptools import setup
2
+
3
+ if __name__ == "__main__":
4
+ setup()
@@ -0,0 +1,25 @@
1
+ # hurdat2py/__init__.py
2
+ from importlib.metadata import version, PackageNotFoundError
3
+
4
+ # Expose the main entry point
5
+ from .core import Hurdat2
6
+
7
+ # Expose the data objects
8
+ from .objects import TropicalCyclone, Season, Hurdat2Entry
9
+
10
+ # Expose errors
11
+ from .errors import (
12
+ Hurdat2Error,
13
+ StormNotFoundError,
14
+ DataDownloadError,
15
+ DataParseError
16
+ )
17
+
18
+ # Fetch version dynamically from pyproject.toml metadata
19
+ try:
20
+ __version__ = version("hurdat2py")
21
+ except PackageNotFoundError:
22
+ # If the package is not installed (e.g. just running local script), avoid crashing
23
+ __version__ = "unknown"
24
+
25
+ __author__ = "Andy McKeen"
@@ -0,0 +1,100 @@
1
+ import math
2
+ import datetime
3
+
4
+ # -----------------------------------------------------------------------------
5
+ # ACE Calculation
6
+ # -----------------------------------------------------------------------------
7
+
8
+ def calculate_ace(entries):
9
+ """
10
+ Calculates Accumulated Cyclone Energy (ACE).
11
+ Formula: Sum of (Wind_Speed / 10000)^2 for every 6 hours (00, 06, 12, 18Z)
12
+ where storm status is Tropical/Subtropical and wind >= 34kts.
13
+ """
14
+ ace_sum = 0
15
+ for entry in entries:
16
+ # Check for 6-hour synoptic times
17
+ if entry.entrytime.hour in [0, 6, 12, 18]:
18
+ # Check for Tropical/Subtropical status
19
+ if entry.status in ['SS', 'TS', 'HU', 'SD']:
20
+ # Check for TS intensity
21
+ if entry.wind >= 34:
22
+ ace_sum += (entry.wind ** 2)
23
+
24
+ return ace_sum / 10000
25
+
26
+
27
+ # -----------------------------------------------------------------------------
28
+ # Duration Calculation
29
+ # -----------------------------------------------------------------------------
30
+
31
+ def calculate_duration_days(entries, min_wind=0):
32
+ """
33
+ Calculates the duration (in days) where the storm maintained
34
+ winds >= min_wind.
35
+ """
36
+ duration = datetime.timedelta(0)
37
+
38
+ # Iterate through segments
39
+ for i in range(1, len(entries)):
40
+ prev_entry = entries[i - 1]
41
+ curr_entry = entries[i]
42
+
43
+ # We attribute the duration of the segment to the status of the START point
44
+ # We check is_TC() to ensure we aren't counting Extratropical duration
45
+ if prev_entry.is_TC() and prev_entry.wind >= min_wind:
46
+ duration += curr_entry.entrytime - prev_entry.entrytime
47
+
48
+ return duration.total_seconds() / (24 * 3600)
49
+
50
+
51
+ # -----------------------------------------------------------------------------
52
+ # Distance Calculation (Haversine)
53
+ # -----------------------------------------------------------------------------
54
+
55
+ def calculate_track_distance(entries, min_wind=0):
56
+ """
57
+ Calculates the track distance (in nautical miles) where the storm
58
+ maintained winds >= min_wind.
59
+ """
60
+ total_distance = 0
61
+ if len(entries) < 2:
62
+ return 0
63
+
64
+ for i in range(1, len(entries)):
65
+ prev_entry = entries[i - 1]
66
+ curr_entry = entries[i]
67
+
68
+ include_segment = False
69
+
70
+ # Logic: If looking for specific intensity, check the previous entry
71
+ if min_wind > 0:
72
+ if prev_entry.is_TC() and prev_entry.wind >= min_wind:
73
+ include_segment = True
74
+ else:
75
+ # If no min_wind (total distance), include everything
76
+ include_segment = True
77
+
78
+ if include_segment:
79
+ dist = haversine(prev_entry.latitude, prev_entry.longitude,
80
+ curr_entry.latitude, curr_entry.longitude)
81
+ total_distance += dist
82
+
83
+ return total_distance
84
+
85
+
86
+ def haversine(lat1, lon1, lat2, lon2):
87
+ """
88
+ Calculates the distance between two geographical points (nautical miles).
89
+ """
90
+ R = 3440.1 # Radius of Earth in Nautical Miles
91
+
92
+ lat1, lon1, lat2, lon2 = map(math.radians, [lat1, lon1, lat2, lon2])
93
+
94
+ dlat = lat2 - lat1
95
+ dlon = lon2 - lon1
96
+
97
+ a = math.sin(dlat/2)**2 + math.cos(lat1) * math.cos(lat2) * math.sin(dlon/2)**2
98
+ c = 2 * math.atan2(math.sqrt(a), math.sqrt(1-a))
99
+
100
+ return R * c