oafuncs 0.0.97.14__py3-none-any.whl → 0.0.97.16__py3-none-any.whl

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.
oafuncs/oa_date.py CHANGED
@@ -13,40 +13,39 @@ SystemInfo: Windows 11
13
13
  Python Version: 3.12
14
14
  """
15
15
 
16
-
17
-
18
16
  import calendar
19
17
  import datetime
18
+ from typing import List, Optional
20
19
 
21
- __all__ = ["get_days_in_month", "generate_hour_list", "adjust_time", "timeit"]
20
+ __all__ = ["month_days", "hour_range", "adjust_time", "timeit"]
22
21
 
23
22
 
24
- def get_days_in_month(year, month):
23
+ def month_days(year: int, month: int) -> int:
25
24
  return calendar.monthrange(year, month)[1]
26
25
 
27
26
 
28
- def generate_hour_list(start_date, end_date, interval_hours=6):
27
+ def hour_range(start: str, end: str, interval: int = 6) -> List[str]:
29
28
  """
30
29
  Generate a list of datetime strings with a specified interval in hours.
31
30
 
32
31
  Args:
33
- start_date (str): Start date in the format "%Y%m%d%H".
34
- end_date (str): End date in the format "%Y%m%d%H".
35
- interval_hours (int): Interval in hours between each datetime.
32
+ start (str): Start date in the format "%Y%m%d%H".
33
+ end (str): End date in the format "%Y%m%d%H".
34
+ interval (int): Interval in hours between each datetime.
36
35
 
37
36
  Returns:
38
37
  list: List of datetime strings in the format "%Y%m%d%H".
39
38
  """
40
- date_s = datetime.datetime.strptime(start_date, "%Y%m%d%H")
41
- date_e = datetime.datetime.strptime(end_date, "%Y%m%d%H")
39
+ date_s = datetime.datetime.strptime(start, "%Y%m%d%H")
40
+ date_e = datetime.datetime.strptime(end, "%Y%m%d%H")
42
41
  date_list = []
43
42
  while date_s <= date_e:
44
43
  date_list.append(date_s.strftime("%Y%m%d%H"))
45
- date_s += datetime.timedelta(hours=interval_hours)
44
+ date_s += datetime.timedelta(hours=interval)
46
45
  return date_list
47
46
 
48
47
 
49
- def adjust_time(initial_time, amount, time_unit="hours", output_format=None):
48
+ def adjust_time(initial_time: str, amount: int, time_unit: str = "hours", output_format: Optional[str] = None) -> str:
50
49
  """
51
50
  Adjust a given initial time by adding a specified amount of time.
52
51
 
@@ -91,22 +90,37 @@ def adjust_time(initial_time, amount, time_unit="hours", output_format=None):
91
90
  default_format = "%Y%m%d"
92
91
  return time_obj.strftime(default_format)
93
92
 
93
+
94
94
  class timeit:
95
95
  """
96
96
  A decorator to measure the execution time of a function.
97
97
 
98
98
  Usage:
99
- @timeit
99
+ @timeit(log=True, print_time=True)
100
100
  def my_function():
101
101
  # Function code here
102
+
103
+ Args:
104
+ log (bool): Whether to log the execution time to a file. Defaults to False.
105
+ print_time (bool): Whether to print the execution time to the console. Defaults to True.
102
106
  """
103
- def __init__(self, func):
107
+
108
+ def __init__(self, func, log: bool = False, print_time: bool = True):
104
109
  self.func = func
110
+ self.log = log
111
+ self.print_time = print_time
105
112
 
106
113
  def __call__(self, *args, **kwargs):
107
114
  start_time = datetime.datetime.now()
108
115
  result = self.func(*args, **kwargs)
109
116
  end_time = datetime.datetime.now()
110
117
  elapsed_time = (end_time - start_time).total_seconds()
111
- print(f"Function '{self.func.__name__}' executed in {elapsed_time:.2f} seconds.")
112
- return result
118
+
119
+ if self.print_time:
120
+ print(f"Function '{self.func.__name__}' executed in {elapsed_time:.2f} seconds.")
121
+
122
+ if self.log:
123
+ with open("execution_time.log", "a") as log_file:
124
+ log_file.write(f"{datetime.datetime.now()} - Function '{self.func.__name__}' executed in {elapsed_time:.2f} seconds.\n")
125
+
126
+ return result
@@ -40,7 +40,7 @@ from oafuncs.oa_nc import modify as modify_nc
40
40
 
41
41
  warnings.filterwarnings("ignore", category=RuntimeWarning, message="Engine '.*' loading failed:.*")
42
42
 
43
- __all__ = ["draw_time_range", "download", "how_to_use", "get_time_list"]
43
+ __all__ = ["draw_time_range", "download"]
44
44
 
45
45
 
46
46
  def _get_initial_data():
@@ -278,7 +278,7 @@ def draw_time_range(pic_save_folder=None):
278
278
  plt.close()
279
279
 
280
280
 
281
- def get_time_list(time_s, time_e, delta, interval_type="hour"):
281
+ def _get_time_list(time_s, time_e, delta, interval_type="hour"):
282
282
  """
283
283
  Description: get a list of time strings from time_s to time_e with a specified interval
284
284
  Args:
@@ -1029,7 +1029,7 @@ def _download_hourly_func(var, time_s, time_e, lon_min=0, lon_max=359.92, lat_mi
1029
1029
  _prepare_url_to_download(var, lon_min, lon_max, lat_min, lat_max, ymdh_time_s, None, depth, level, store_path, dataset_name, version_name, check)
1030
1030
  elif int(ymdh_time_s) < int(ymdh_time_e):
1031
1031
  print("Downloading a series of files...")
1032
- time_list = get_time_list(ymdh_time_s, ymdh_time_e, interval_hour, "hour")
1032
+ time_list = _get_time_list(ymdh_time_s, ymdh_time_e, interval_hour, "hour")
1033
1033
  with Progress() as progress:
1034
1034
  task = progress.add_task(f"[cyan]{bar_desc}", total=len(time_list))
1035
1035
  if ftimes == 1:
@@ -1048,7 +1048,7 @@ def _download_hourly_func(var, time_s, time_e, lon_min=0, lon_max=359.92, lat_mi
1048
1048
  _done_callback(feature, progress, task, len(time_list), counter_lock)
1049
1049
  else:
1050
1050
  # new_time_list = get_time_list(ymdh_time_s, ymdh_time_e, 3 * ftimes, "hour")
1051
- new_time_list = get_time_list(ymdh_time_s, ymdh_time_e, interval_hour * ftimes, "hour")
1051
+ new_time_list = _get_time_list(ymdh_time_s, ymdh_time_e, interval_hour * ftimes, "hour")
1052
1052
  total_num = len(new_time_list)
1053
1053
  if num_workers is None or num_workers <= 1:
1054
1054
  # 串行方式
@@ -1097,7 +1097,7 @@ def download(var, time_s, time_e=None, lon_min=0, lon_max=359.92, lat_min=-80, l
1097
1097
  Returns:
1098
1098
  None
1099
1099
  """
1100
- from oafuncs.oa_data import pbar
1100
+ from oafuncs.oa_tool import pbar
1101
1101
  from oafuncs.oa_cmap import get as get_cmap
1102
1102
 
1103
1103
  _get_initial_data()
@@ -1248,55 +1248,6 @@ def download(var, time_s, time_e=None, lon_min=0, lon_max=359.92, lat_min=-80, l
1248
1248
  print("[bold #ecdbfe]=" * mark_len)
1249
1249
 
1250
1250
 
1251
- def how_to_use():
1252
- print("""
1253
- # 1. Choose the dataset and version according to the time:
1254
- # 1.1 Use function to query
1255
- You can use the function check_time_in_dataset_and_version(time_input=20241101) to find the dataset and version according to the time.
1256
- Then, you can see the dataset and version in the output.
1257
- # 1.2 Draw a picture to see
1258
- You can draw a picture to see the time range of each dataset and version.
1259
- Using the function draw_time_range(pic_save_folder=None) to draw the picture.
1260
-
1261
- # 2. Get the base url according to the dataset, version, var and year:
1262
- # 2.1 Dataset and version were found in step 1
1263
- # 2.2 Var: u, v, temp, salt, ssh, u_b, v_b, temp_b, salt_b
1264
- # 2.3 Year: 1994-2024(current year)
1265
-
1266
- # 3. Get the query_dict according to the var, lon_min, lon_max, lat_min, lat_max, depth, level_num, time_str_ymdh:
1267
- # 3.1 Var: u, v, temp, salt, ssh, u_b, v_b, temp_b, salt_b
1268
- # 3.2 Lon_min, lon_max, lat_min, lat_max: float
1269
- # 3.3 Depth: 0-5000m, if you wanna get single depth data, you can set the depth
1270
- # 3.4 Level_num: 1-40, if you wanna get single level data, you can set the level_num
1271
- # 3.5 Time_str_ymdh: '2024110112', the hour normally is 00, 03, 06, 09, 12, 15, 18, 21, besides 1 hourly data
1272
- # 3.6 Use the function to get the query_dict
1273
- # 3.7 Note: If you wanna get the full depth or full level data, you can needn't set the depth or level_num
1274
-
1275
- # 4. Get the submit url according to the dataset, version, var, year, query_dict:
1276
- # 4.1 Use the function to get the submit url
1277
- # 4.2 You can use the submit url to download the data
1278
-
1279
- # 5. Download the data according to the submit url:
1280
- # 5.1 Use the function to download the data
1281
- # 5.2 You can download the data of single time or a series of time
1282
- # 5.3 Note: If you wanna download a series of data, you can set the ymdh_time_s and ymdh_time_e different
1283
- # 5.4 Note: The time resolution is 3 hours
1284
-
1285
- # 6. Direct download the data:
1286
- # 6.1 Use the function to direct download the data
1287
- # 6.2 You can set the dataset_name and version_name by yourself
1288
- # 6.3 Note: If you do not set the dataset_name and version_name, the dataset and version will be chosen according to the download_time
1289
- # 6.4 Note: If you set the dataset_name and version_name, please ensure the dataset_name and version_name are correct
1290
- # 6.5 Note: If you just set one of the dataset_name and version_name, both the dataset and version will be chosen according to the download_time
1291
-
1292
- # 7. Simple use:
1293
- # 7.1 You can use the function: download(var, ymdh_time_s, ymdh_time_e, lon_min=0, lon_max=359.92, lat_min=-80, lat_max=90, depth=None, level_num=None, store_path=None, dataset_name=None, version_name=None)
1294
- # 7.2 You can download the data of single time or a series of time
1295
- # 7.3 The parameters you must set are var, ymdh_time_s, ymdh_time_e
1296
- # 7.4 Example: download('u', '2024110112', '2024110212', lon_min=0, lon_max=359.92, lat_min=-80, lat_max=90, depth=None, level_num=None, store_path=None, dataset_name=None, version_name=None)
1297
- """)
1298
-
1299
-
1300
1251
  if __name__ == "__main__":
1301
1252
  download_dict = {
1302
1253
  "water_u": {"simple_name": "u", "download": 1},
oafuncs/oa_draw.py CHANGED
@@ -13,7 +13,6 @@ SystemInfo: Windows 11
13
13
  Python Version: 3.11
14
14
  """
15
15
 
16
-
17
16
  import warnings
18
17
 
19
18
  import cartopy.crs as ccrs
@@ -21,16 +20,15 @@ import cartopy.feature as cfeature
21
20
  import matplotlib as mpl
22
21
  import matplotlib.pyplot as plt
23
22
  import numpy as np
24
- import xarray as xr
25
23
  from cartopy.mpl.ticker import LatitudeFormatter, LongitudeFormatter
26
24
  from rich import print
27
25
 
28
- __all__ = ["fig_minus", "gif", "add_cartopy", "add_gridlines", "MidpointNormalize", "add_lonlat_unit", "contour", "contourf", "quiver"]
26
+ __all__ = ["fig_minus", "gif", "add_cartopy", "add_gridlines", "MidpointNormalize", "add_lonlat_unit"]
29
27
 
30
28
  warnings.filterwarnings("ignore")
31
29
 
32
30
 
33
- def fig_minus(ax_x=None, ax_y=None, cbar=None, decimal=None, add_space=False):
31
+ def fig_minus(ax_x: plt.Axes = None, ax_y: plt.Axes = None, cbar: mpl.colorbar.Colorbar = None, decimal: int = None, add_space: bool = False) -> plt.Axes | mpl.colorbar.Colorbar | None:
34
32
  """
35
33
  Description: 将坐标轴刻度中的负号替换为减号
36
34
 
@@ -76,7 +74,7 @@ def fig_minus(ax_x=None, ax_y=None, cbar=None, decimal=None, add_space=False):
76
74
 
77
75
 
78
76
  # ** 将生成图片/已有图片制作成动图
79
- def gif(image_list: list, gif_name: str, duration=200, resize=None): # 制作动图,默认间隔0.2
77
+ def gif(image_list: list[str], gif_name: str, duration: float = 200, resize: tuple[int, int] = None) -> None: # 制作动图,默认间隔0.2
80
78
  """
81
79
  Description
82
80
  Make gif from images
@@ -125,7 +123,7 @@ def gif(image_list: list, gif_name: str, duration=200, resize=None): # 制作
125
123
 
126
124
 
127
125
  # ** 转化经/纬度刻度
128
- def add_lonlat_unit(lon=None, lat=None, decimal=2):
126
+ def add_lonlat_unit(lon: list[float] = None, lat: list[float] = None, decimal: int = 2) -> tuple[list[str], list[str]] | list[str]:
129
127
  """
130
128
  param {*} lon : 经度列表
131
129
  param {*} lat : 纬度列表
@@ -165,15 +163,16 @@ def add_lonlat_unit(lon=None, lat=None, decimal=2):
165
163
 
166
164
 
167
165
  # ** 添加网格线
168
- def add_gridlines(ax, xline=None, yline=None, projection=ccrs.PlateCarree(), color="k", alpha=0.5, linestyle="--", linewidth=0.5):
166
+ def add_gridlines(ax: plt.Axes, xline: list[float] = None, yline: list[float] = None, projection: ccrs.Projection = ccrs.PlateCarree(), color: str = "k", alpha: float = 0.5, linestyle: str = "--", linewidth: float = 0.5) -> tuple[plt.Axes, mpl.ticker.Locator]:
169
167
  from matplotlib import ticker as mticker
168
+
170
169
  # add gridlines
171
170
  gl = ax.gridlines(crs=projection, draw_labels=True, linewidth=linewidth, color=color, alpha=alpha, linestyle=linestyle)
172
171
  gl.right_labels = False
173
172
  gl.top_labels = False
174
173
  gl.xformatter = LongitudeFormatter(zero_direction_label=False)
175
174
  gl.yformatter = LatitudeFormatter()
176
-
175
+
177
176
  if xline is not None:
178
177
  gl.xlocator = mticker.FixedLocator(np.array(xline))
179
178
  if yline is not None:
@@ -183,7 +182,7 @@ def add_gridlines(ax, xline=None, yline=None, projection=ccrs.PlateCarree(), col
183
182
 
184
183
 
185
184
  # ** 添加地图
186
- def add_cartopy(ax, lon=None, lat=None, projection=ccrs.PlateCarree(), gridlines=True, landcolor="lightgrey", oceancolor="lightblue", cartopy_linewidth=0.5):
185
+ def add_cartopy(ax: plt.Axes, lon: np.ndarray = None, lat: np.ndarray = None, projection: ccrs.Projection = ccrs.PlateCarree(), gridlines: bool = True, landcolor: str = "lightgrey", oceancolor: str = "lightblue", cartopy_linewidth: float = 0.5) -> None:
187
186
  # add coastlines
188
187
  ax.add_feature(cfeature.LAND, facecolor=landcolor)
189
188
  ax.add_feature(cfeature.OCEAN, facecolor=oceancolor)
@@ -219,139 +218,19 @@ class MidpointNormalize(mpl.colors.Normalize):
219
218
  nrom = MidpointNormalize(vmin=-2, vmax=1, vcenter=0)
220
219
  """
221
220
 
222
- def __init__(self, vmin=None, vmax=None, vcenter=None, clip=False):
221
+ def __init__(self, vmin: float = None, vmax: float = None, vcenter: float = None, clip: bool = False) -> None:
223
222
  self.vcenter = vcenter
224
223
  super().__init__(vmin, vmax, clip)
225
224
 
226
- def __call__(self, value, clip=None):
225
+ def __call__(self, value: np.ndarray, clip: bool = None) -> np.ma.MaskedArray:
227
226
  x, y = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1.0]
228
227
  return np.ma.masked_array(np.interp(value, x, y, left=-np.inf, right=np.inf))
229
228
 
230
- def inverse(self, value):
229
+ def inverse(self, value: np.ndarray) -> np.ndarray:
231
230
  y, x = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1]
232
231
  return np.interp(value, x, y, left=-np.inf, right=np.inf)
233
232
 
234
233
 
235
- # -----------------------------------------------------------------------------------------------------------------------------------------------------------------
236
-
237
- # ** 绘制填色图
238
- def contourf(data,x=None,y=None,cmap='coolwarm',show=True,store=None,cartopy=False):
239
- """
240
- Description: 绘制填色图
241
-
242
- param {*} data : 二维数据
243
- param {*} x : x轴坐标
244
- param {*} y : y轴坐标
245
- param {*} cmap : 颜色映射
246
- param {*} show : 是否显示
247
- param {*} store : 是否保存
248
- param {*} cartopy : 是否使用cartopy
249
-
250
- return {*}
251
- """
252
- data = np.array(data)
253
- if x is None or y is None:
254
- x = np.arange(data.shape[1])
255
- y = np.arange(data.shape[0])
256
- if cartopy:
257
- fig, ax = plt.subplots(subplot_kw={'projection': ccrs.PlateCarree()})
258
- add_cartopy(ax, lon=x, lat=y)
259
- ax.contourf(x, y, data, transform=ccrs.PlateCarree(), cmap=cmap)
260
- else:
261
- plt.contourf(x, y, data, cmap=cmap)
262
- plt.colorbar()
263
- plt.savefig(store, dpi=600, bbox_inches="tight") if store else plt.show()
264
- plt.close()
265
-
266
-
267
- # ** 绘制等值线图
268
- def contour(data, x=None, y=None, cmap="coolwarm", show=True, store=None, cartopy=False):
269
- """
270
- Description: 绘制等值线图
271
-
272
- param {*} data : 二维数据
273
- param {*} x : x轴坐标
274
- param {*} y : y轴坐标
275
- param {*} cmap : 颜色映射
276
- param {*} show : 是否显示
277
- param {*} store : 是否保存
278
- param {*} cartopy : 是否使用cartopy
279
-
280
- return {*}
281
- """
282
- data = np.array(data)
283
- if x is None or y is None:
284
- x = np.arange(data.shape[1])
285
- y = np.arange(data.shape[0])
286
- if cartopy:
287
- fig, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()})
288
- add_cartopy(ax, lon=x, lat=y)
289
- cr = ax.contour(x, y, data, transform=ccrs.PlateCarree(), cmap=cmap)
290
- else:
291
- cr = plt.contour(x, y, data, cmap=cmap)
292
- plt.clabel(cr, inline=True, fontsize=10)
293
- plt.savefig(store, dpi=600, bbox_inches="tight") if store else plt.show()
294
- plt.close()
295
-
296
-
297
- # ** 绘制矢量场
298
- def quiver(u, v, lon, lat, picname=None, cmap="coolwarm", scale=0.25, width=0.002, x_space=5, y_space=5):
299
- """
300
- param {*} u : 二维数据
301
- param {*} v : 二维数据
302
- param {*} lon : 经度, 1D or 2D
303
- param {*} lat : 纬度, 1D or 2D
304
- param {*} picname : 图片保存的文件名(含路径)
305
- param {*} cmap : 颜色映射,默认coolwarm
306
- param {*} scale : 箭头的大小 / 缩小程度
307
- param {*} width : 箭头的宽度
308
- param {*} x_space : x轴间隔
309
- param {*} y_space : y轴间隔
310
- return {*} 无返回值
311
- """
312
- # 创建新的网格位置变量(lat_c, lon_c)
313
- if len(lon.shape) == 1 and len(lat.shape) == 1:
314
- lon_c, lat_c = np.meshgrid(lon, lat)
315
- else:
316
- lon_c, lat_c = lon, lat
317
-
318
- # 设置箭头的比例、颜色、宽度等参数
319
- # scale = 0.25 # 箭头的大小 / 缩小程度
320
- # color = '#E5D1FA'
321
- # width = 0.002 # 箭头的宽度
322
- # x_space = 1
323
- # y_space = 1
324
-
325
- # 计算矢量的大小
326
- S = xr.DataArray(np.hypot(np.array(u), np.array(v)))
327
-
328
- mean_S = S.nanmean()
329
-
330
- # 使用 plt.quiver 函数绘制矢量图
331
- # 通过设置 quiver 函数的 pivot 参数来指定箭头的位置
332
- quiver_plot = plt.quiver(
333
- lon_c[::y_space, ::x_space],
334
- lat_c[::y_space, ::x_space],
335
- u[::y_space, ::x_space],
336
- v[::y_space, ::x_space],
337
- S[::y_space, ::x_space], # 矢量的大小,可以不要
338
- pivot="middle",
339
- scale=scale,
340
- # color=color, # 矢量的颜色,单色
341
- cmap=cmap, # 矢量的颜色,多色
342
- width=width,
343
- )
344
- # plt.quiverkey(quiver_plot, X=0.90, Y=0.975, U=1, label='1 m/s', labelpos='E', fontproperties={'size': 10})
345
- plt.quiverkey(quiver_plot, X=0.87, Y=0.975, U=mean_S, label=f"{mean_S:.2f} m/s", labelpos="E", fontproperties={"size": 10})
346
- plt.colorbar(quiver_plot)
347
- plt.xlabel("X")
348
- plt.ylabel("Y")
349
-
350
- plt.savefig(picname, bbox_inches="tight") if picname is not None else plt.show()
351
- plt.clf()
352
- plt.close()
353
-
354
-
355
234
 
356
235
  if __name__ == "__main__":
357
236
  pass
oafuncs/oa_file.py CHANGED
@@ -21,7 +21,7 @@ import shutil
21
21
 
22
22
  from rich import print
23
23
 
24
- __all__ = ["find_file", "link_file", "copy_file", "rename_file", "make_folder", "clear_folder", "remove_empty_folder", "remove", "file_size", "mean_size", "make_dir", "replace_content", "move_file"]
24
+ __all__ = ["find_file", "link_file", "copy_file", "rename_file", "move_file", "clear_folder", "remove_empty_folder", "remove", "file_size", "mean_size", "make_dir", "replace_content"]
25
25
 
26
26
 
27
27
  # ** 查找文件,支持通配符
@@ -157,7 +157,6 @@ def copy_file(src_pattern, dst):
157
157
  print(f"Copy and rename file or directory: {src_file} -> {dst_file}")
158
158
 
159
159
 
160
-
161
160
  # ** 移动文件或目录,支持通配符
162
161
  def move_file(src_pattern, dst):
163
162
  """
@@ -205,7 +204,6 @@ def move_file(src_pattern, dst):
205
204
  print(f"Move and rename file or directory: {src_file} -> {dst_file}")
206
205
 
207
206
 
208
-
209
207
  # ** 重命名文件,支持通配符
210
208
  def rename_file(directory, old_str, new_str):
211
209
  """
@@ -243,26 +241,6 @@ def rename_file(directory, old_str, new_str):
243
241
  print(f"Rename file: {old_path} -> {new_path}")
244
242
 
245
243
 
246
- # ** 创建子文件夹(可选清空)
247
- def make_folder(rootpath=None, folder_name=None, clear=False) -> str:
248
- """
249
- # 描述:创建子文件夹(可选清空)
250
- # 使用示例
251
- rootpath = r'E:\\Data\\2024\\09\\17'
252
- folder_name = 'var1'
253
- newpath = make_folder(rootpath, folder_name, clear=1)
254
- param {*} rootpath # 根目录
255
- param {*} folder_name # 文件夹名称
256
- """
257
- if rootpath is None:
258
- rootpath = os.getcwd()
259
- folder_path = os.path.join(str(rootpath), str(folder_name))
260
- if clear:
261
- shutil.rmtree(folder_path, ignore_errors=True)
262
- os.makedirs(folder_path, exist_ok=True)
263
- return folder_path
264
-
265
-
266
244
  # ** 创建路径
267
245
  def make_dir(directory):
268
246
  """