climatewatches 1.0.1__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) 2025 Andriamihaja R.
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,47 @@
1
+ Metadata-Version: 2.4
2
+ Name: climatewatches
3
+ Version: 1.0.1
4
+ Summary: Climate data monitoring and visualization for temporal and spatial dataset
5
+ Author-email: "Andriamihaja R." <andriamihajaranaivoson@yahoo.fr>
6
+ License-Expression: MIT
7
+ Project-URL: Homepage, https://github.com/Dihj/climatewatches
8
+ Project-URL: Repository, https://github.com/Dihj/climatewatches
9
+ Project-URL: Issues, https://github.com/Dihj/climatewatches/issues
10
+ Keywords: climate,meteorology,rainfall,temperature,cyclone,climatewatches
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: Programming Language :: Python :: 3 :: Only
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: Scientific/Engineering :: Atmospheric Science
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: cartopy
19
+ Requires-Dist: geopandas
20
+ Requires-Dist: geopy
21
+ Requires-Dist: matplotlib<3.11,>=3.8
22
+ Requires-Dist: netCDF4
23
+ Requires-Dist: numpy
24
+ Requires-Dist: pandas
25
+ Requires-Dist: rasterio
26
+ Requires-Dist: rioxarray
27
+ Requires-Dist: scipy
28
+ Requires-Dist: shapely
29
+ Requires-Dist: xarray
30
+ Dynamic: license-file
31
+
32
+ # climatewatches
33
+
34
+ `climatewatches` is a small Python toolkit for climate bulletin monitoring and
35
+ visualization. It includes station monitoring plots, rainfall and temperature
36
+ maps, climatology maps, tropical cyclone monitoring, and large-scale climate
37
+ driver diagnostics.
38
+
39
+ ## Installation
40
+
41
+ ```bash
42
+ pip install climatewatches
43
+ ```
44
+
45
+ ## Documentation
46
+
47
+ See on the wiki page on this repository.
@@ -0,0 +1,16 @@
1
+ # climatewatches
2
+
3
+ `climatewatches` is a small Python toolkit for climate bulletin monitoring and
4
+ visualization. It includes station monitoring plots, rainfall and temperature
5
+ maps, climatology maps, tropical cyclone monitoring, and large-scale climate
6
+ driver diagnostics.
7
+
8
+ ## Installation
9
+
10
+ ```bash
11
+ pip install climatewatches
12
+ ```
13
+
14
+ ## Documentation
15
+
16
+ See on the wiki page on this repository.
@@ -0,0 +1,100 @@
1
+ """ClimateWatches is a Python toolkit for climate bulletin monitoring and visualization."""
2
+
3
+ __version__ = "1.0.1"
4
+
5
+ _EXPORTS = {
6
+ "plot_precip_interannual_variability": (
7
+ "interannual_variability",
8
+ "plot_precip_interannual_variability",
9
+ ),
10
+ "plot_temp_interannual_variability": (
11
+ "interannual_variability",
12
+ "plot_temp_interannual_variability",
13
+ ),
14
+ "read_stn_data": ("interannual_variability", "read_stn_data"),
15
+ "assign_season_year": ("interannual_variability", "assign_season_year"),
16
+ "plot_precip_map": ("spatial_map", "plot_precip_map"),
17
+ "plot_precip_anomaly": ("spatial_map", "plot_precip_anomaly"),
18
+ "plot_temp_map": ("spatial_map", "plot_temp_map"),
19
+ "plot_temp_anomaly": ("spatial_map", "plot_temp_anomaly"),
20
+ "plot_precip_climatology_map": (
21
+ "climatology",
22
+ "plot_precip_climatology_map",
23
+ ),
24
+ "plot_temperature_climatology_map": (
25
+ "climatology",
26
+ "plot_temperature_climatology_map",
27
+ ),
28
+ "plot_climatology_spatial_mean": (
29
+ "climatology",
30
+ "plot_climatology_spatial_mean",
31
+ ),
32
+ "plot_diagram_ombro_one_point": (
33
+ "climatology",
34
+ "plot_diagram_ombro_one_point",
35
+ ),
36
+ "plot_composite_map": ("climatology", "plot_composite_map"),
37
+ "plot_stn_temp_monitoring": (
38
+ "monitoring_stn",
39
+ "plot_stn_temp_monitoring",
40
+ ),
41
+ "plot_stn_precip_monitoring": (
42
+ "monitoring_stn",
43
+ "plot_stn_precip_monitoring",
44
+ ),
45
+ "plot_stn_monthly_monitoring": (
46
+ "monitoring_stn",
47
+ "plot_stn_monthly_monitoring",
48
+ ),
49
+ "plot_monthly_interannual_variability": (
50
+ "monitoring_stn",
51
+ "plot_monthly_interannual_variability",
52
+ ),
53
+ "plot_track_tc": ("monitor_tc", "plot_track_tc"),
54
+ "temporal_evolution_tc": ("monitor_tc", "temporal_evolution_tc"),
55
+ "plot_all_tc_season": ("monitor_tc", "plot_all_tc_season"),
56
+ "plot_map_latest_sst": (
57
+ "monitoring_large_scale_param",
58
+ "plot_map_latest_sst",
59
+ ),
60
+ "plot_map_latest_mslp": (
61
+ "monitoring_large_scale_param",
62
+ "plot_map_latest_mslp",
63
+ ),
64
+ "plot_climate_indices": (
65
+ "monitoring_climate_driver",
66
+ "plot_climate_indices",
67
+ ),
68
+ "plot_sst_map_variability": (
69
+ "monitoring_climate_driver",
70
+ "plot_sst_map_variability",
71
+ ),
72
+ "blend_stn_cdt_data": ("preparation_data", "blend_stn_cdt_data"),
73
+ "download_sst_mean_data": ("preparation_data", "download_sst_mean_data"),
74
+ "normalize_lat_lon": ("preparation_data", "normalize_lat_lon"),
75
+ "resolve_variable": ("preparation_data", "resolve_variable"),
76
+ "is_precip_variable": ("preparation_data", "is_precip_variable"),
77
+ "set_plot_defaults": ("preparation_data", "set_plot_defaults"),
78
+ "set_title": ("preparation_data", "set_title"),
79
+ "set_grid": ("preparation_data", "set_grid"),
80
+ "set_map_grid": ("preparation_data", "set_map_grid"),
81
+ "annual_trend": ("annual_analysis", "annual_trend"),
82
+ "disaster_impact": ("disaster_analysis", "disaster_impact"),
83
+ "plot_disaster_impact": ("plot_disaster_impact", "plot_disaster_impact"),
84
+ }
85
+
86
+ __all__ = sorted(_EXPORTS)
87
+
88
+
89
+ def __getattr__(name):
90
+ if name not in _EXPORTS:
91
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
92
+
93
+ module_name, object_name = _EXPORTS[name]
94
+ module = __import__(
95
+ f"{__name__}.{module_name}",
96
+ fromlist=[object_name],
97
+ )
98
+ value = getattr(module, object_name)
99
+ globals()[name] = value
100
+ return value
@@ -0,0 +1,191 @@
1
+ """Annual climate analysis."""
2
+
3
+ import pandas as pd
4
+ import matplotlib.pyplot as plt
5
+
6
+
7
+ DEFAULT_MISSING_VALUES = [
8
+ -999,
9
+ -99.9,
10
+ -99,
11
+ "NA",
12
+ "N/A",
13
+ "",
14
+ "missing",
15
+ "Missing",
16
+ "MISSING",
17
+ "nan",
18
+ "NaN",
19
+ "NULL",
20
+ None,
21
+ ]
22
+
23
+
24
+ def annual_trend(
25
+ file,
26
+ date_col="Date",
27
+ temp_col="Tmean",
28
+ rainfall_col="Rainfall",
29
+ date_format="%Y%m%d",
30
+ missing_values=-99.9,
31
+ smooth=True,
32
+ smooth_year=5,
33
+ title="Climate trends",
34
+ output_file="annual_valueTrend.png",
35
+ show=True,
36
+ ):
37
+ """
38
+ Plot annual mean temperature and annual total rainfall trends.
39
+
40
+ Parameters
41
+ ----------
42
+ file : str or path-like
43
+ Path to the station CSV file.
44
+ date_col : str, default "Date"
45
+ Date column name.
46
+ temp_col : str, default "Tmean"
47
+ Mean temperature column name.
48
+ rainfall_col : str, default "Rainfall"
49
+ Rainfall column name.
50
+ date_format : str or None, default "%Y%m%d"
51
+ Date format used by ``pandas.to_datetime``. Use None to infer dates.
52
+ missing_values : list-like or scalar, optional
53
+ Extra missing-value markers. Common markers are handled by default.
54
+ smooth : bool, default True
55
+ If True, add rolling average lines.
56
+ smooth_year : int, default 5
57
+ Rolling average window size in years.
58
+ title : str, default "Climate trends"
59
+ Main title displayed above the plot.
60
+ output_file : str or path-like or None, default "annual_valueTrend.png"
61
+ Path where the plot is saved. Use None to skip saving.
62
+ show : bool, default True
63
+ If True, display the plot.
64
+
65
+ Returns
66
+ -------
67
+ None
68
+ """
69
+ if smooth_year < 1:
70
+ raise ValueError("smooth_year must be at least 1.")
71
+
72
+ na_values = list(DEFAULT_MISSING_VALUES)
73
+ if missing_values is not None:
74
+ if isinstance(missing_values, (list, tuple, set)):
75
+ na_values.extend(missing_values)
76
+ else:
77
+ na_values.append(missing_values)
78
+
79
+ df = pd.read_csv(file, na_values=na_values)
80
+
81
+ required_columns = {date_col, temp_col, rainfall_col}
82
+ missing_columns = required_columns.difference(df.columns)
83
+ if missing_columns:
84
+ missing_text = ", ".join(sorted(missing_columns))
85
+ raise ValueError(f"Missing required column(s): {missing_text}")
86
+
87
+ df = df[[date_col, temp_col, rainfall_col]].copy()
88
+ df[date_col] = pd.to_datetime(
89
+ df[date_col].astype("string"),
90
+ format=date_format,
91
+ errors="coerce",
92
+ )
93
+ df[temp_col] = pd.to_numeric(df[temp_col], errors="coerce")
94
+ df[rainfall_col] = pd.to_numeric(df[rainfall_col], errors="coerce")
95
+ df = df.dropna(subset=[date_col])
96
+
97
+ if df.empty:
98
+ raise ValueError("No valid dates found after removing missing values.")
99
+
100
+ df["Year"] = df[date_col].dt.year
101
+
102
+ annual_df = (
103
+ df.groupby("Year", as_index=False)
104
+ .agg(
105
+ Mean_Temp=(temp_col, "mean"),
106
+ Total_Rainfall=(rainfall_col, lambda values: values.sum(min_count=1)),
107
+ )
108
+ .sort_values("Year")
109
+ .reset_index(drop=True)
110
+ )
111
+
112
+ if annual_df[["Mean_Temp", "Total_Rainfall"]].isna().all().all():
113
+ raise ValueError("No valid temperature or rainfall values found.")
114
+
115
+ if smooth:
116
+ annual_df["Temp_Rolling_Avg"] = (
117
+ annual_df["Mean_Temp"].rolling(window=smooth_year, min_periods=1).mean()
118
+ )
119
+ annual_df["Rain_Rolling_Avg"] = (
120
+ annual_df["Total_Rainfall"].rolling(window=smooth_year, min_periods=1).mean()
121
+ )
122
+
123
+ fig, axes = plt.subplots(1, 2, figsize=(14, 6), sharex=True)
124
+ fig.suptitle(
125
+ title,
126
+ fontsize=16,
127
+ fontweight="bold",
128
+ y=0.98,
129
+ color="#1e293b",
130
+ )
131
+
132
+ for ax in axes:
133
+ ax.spines["top"].set_visible(False)
134
+ ax.spines["right"].set_visible(False)
135
+ ax.grid(axis="y", linestyle="-", alpha=0.3, color="#cbd5e1")
136
+ ax.tick_params(colors="#475569")
137
+
138
+ axes[0].plot(
139
+ annual_df["Year"],
140
+ annual_df["Mean_Temp"],
141
+ color="#93c5fd",
142
+ linewidth=1,
143
+ alpha=0.75,
144
+ label="Annual mean",
145
+ )
146
+ if smooth:
147
+ axes[0].plot(
148
+ annual_df["Year"],
149
+ annual_df["Temp_Rolling_Avg"],
150
+ color="#1e3a8a",
151
+ linewidth=2.5,
152
+ label=f"{smooth_year}-yr rolling avg",
153
+ )
154
+
155
+ axes[0].set_title("Mean annual temperature (deg C)", color="#1e293b", pad=12)
156
+ axes[0].set_ylabel("deg C", fontweight="bold", color="#475569")
157
+ axes[0].legend(frameon=False, loc="upper right")
158
+
159
+ axes[1].plot(
160
+ annual_df["Year"],
161
+ annual_df["Total_Rainfall"],
162
+ color="#fde68a",
163
+ linewidth=1,
164
+ alpha=0.85,
165
+ label="Annual total",
166
+ )
167
+ if smooth:
168
+ axes[1].plot(
169
+ annual_df["Year"],
170
+ annual_df["Rain_Rolling_Avg"],
171
+ color="#78350f",
172
+ linewidth=2.5,
173
+ label=f"{smooth_year}-yr rolling avg",
174
+ )
175
+
176
+ axes[1].set_title("Total annual precipitation (mm)", color="#1e293b", pad=12)
177
+ axes[1].set_ylabel("mm", fontweight="bold", color="#475569")
178
+ axes[1].legend(frameon=False, loc="upper left")
179
+
180
+ plt.tight_layout()
181
+
182
+ if output_file is not None:
183
+ plt.savefig(output_file, dpi=300)
184
+ if show:
185
+ plt.show()
186
+ else:
187
+ plt.close(fig)
188
+ if output_file is not None:
189
+ print(f"Plot created and saved: {output_file}")
190
+
191
+ return None