oafuncs 0.0.81__py2.py3-none-any.whl → 0.0.83__py2.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.
Files changed (44) hide show
  1. oafuncs/data_store/OAFuncs.png +0 -0
  2. oafuncs/oa_cmap.py +1 -0
  3. oafuncs/oa_data.py +107 -28
  4. oafuncs/oa_down/__init__.py +5 -4
  5. oafuncs/oa_down/hycom_3hourly.py +152 -35
  6. oafuncs/oa_down/user_agent.py +34 -0
  7. oafuncs/oa_draw.py +165 -103
  8. oafuncs/oa_file.py +66 -53
  9. oafuncs/oa_help.py +19 -16
  10. oafuncs/oa_nc.py +82 -114
  11. oafuncs-0.0.83.dist-info/METADATA +91 -0
  12. oafuncs-0.0.83.dist-info/RECORD +26 -0
  13. oafuncs/oa_down/test.py +0 -151
  14. oafuncs/oa_s/__init__.py +0 -23
  15. oafuncs/oa_s/oa_cmap.py +0 -163
  16. oafuncs/oa_s/oa_data.py +0 -187
  17. oafuncs/oa_s/oa_draw.py +0 -451
  18. oafuncs/oa_s/oa_file.py +0 -332
  19. oafuncs/oa_s/oa_help.py +0 -39
  20. oafuncs/oa_s/oa_nc.py +0 -410
  21. oafuncs/oa_s/oa_python.py +0 -107
  22. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/__init__.py" +0 -26
  23. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_cmap.py" +0 -163
  24. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_data.py" +0 -187
  25. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/__init__.py" +0 -20
  26. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/hycom_3hourly.py" +0 -1176
  27. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/literature.py" +0 -332
  28. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/test_ua.py" +0 -151
  29. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_draw.py" +0 -451
  30. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_file.py" +0 -332
  31. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_help.py" +0 -39
  32. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_nc.py" +0 -410
  33. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_python.py" +0 -107
  34. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/__init__.py" +0 -21
  35. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/meteorological.py" +0 -168
  36. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/ocean.py" +0 -158
  37. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/scientific.py" +0 -139
  38. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/__init__.py" +0 -18
  39. oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/email.py" +0 -114
  40. oafuncs-0.0.81.dist-info/METADATA +0 -918
  41. oafuncs-0.0.81.dist-info/RECORD +0 -51
  42. {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/LICENSE.txt +0 -0
  43. {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/WHEEL +0 -0
  44. {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/top_level.txt +0 -0
Binary file
oafuncs/oa_cmap.py CHANGED
@@ -100,6 +100,7 @@ def create_cmap_rgbtxt(rgbtxt_file,split_mark=','): # 根据RGB的txt文档制
100
100
  return icmap
101
101
 
102
102
 
103
+ # ** 选择cmap
103
104
  def choose_cmap(cmap_name=None, query=False):
104
105
  """
105
106
  description: Choosing a colormap from the list of available colormaps or a custom colormap
oafuncs/oa_data.py CHANGED
@@ -1,31 +1,97 @@
1
1
  #!/usr/bin/env python
2
2
  # coding=utf-8
3
- '''
3
+ """
4
4
  Author: Liu Kun && 16031215@qq.com
5
5
  Date: 2024-09-17 17:12:47
6
6
  LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2024-11-21 13:13:20
7
+ LastEditTime: 2024-12-13 19:11:08
8
8
  FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_data.py
9
- Description:
9
+ Description:
10
10
  EditPlatform: vscode
11
11
  ComputerInfo: XPS 15 9510
12
12
  SystemInfo: Windows 11
13
13
  Python Version: 3.11
14
- '''
15
-
14
+ """
16
15
 
16
+ import itertools
17
17
  import multiprocessing as mp
18
18
  from concurrent.futures import ThreadPoolExecutor
19
19
 
20
20
  import numpy as np
21
21
  from scipy.interpolate import griddata
22
22
 
23
- __all__ = ['interp_2d', 'interp_2d_parallel']
23
+ __all__ = ["interp_2d"]
24
24
 
25
- # ** 高维插值函数,插值最后两个维度
26
25
 
26
+ def interp_2d(target_x, target_y, origin_x, origin_y, data, method="linear", parallel=True):
27
+ """
28
+ Perform 2D interpolation on the last two dimensions of a multi-dimensional array.
29
+
30
+ Parameters:
31
+ - target_x (array-like): 1D array of target grid's x-coordinates.
32
+ - target_y (array-like): 1D array of target grid's y-coordinates.
33
+ - origin_x (array-like): 1D array of original grid's x-coordinates.
34
+ - origin_y (array-like): 1D array of original grid's y-coordinates.
35
+ - data (numpy.ndarray): Multi-dimensional array where the last two dimensions correspond to the original grid.
36
+ - method (str, optional): Interpolation method, default is 'linear'. Other options include 'nearest', 'cubic', etc.
37
+ - parallel (bool, optional): Flag to enable parallel processing. Default is True.
38
+
39
+ Returns:
40
+ - interpolated_data (numpy.ndarray): Interpolated data with the same leading dimensions as the input data, but with the last two dimensions corresponding to the target grid.
41
+
42
+ Raises:
43
+ - ValueError: If the shape of the data does not match the shape of the origin_x or origin_y grids.
44
+
45
+ Usage:
46
+ - Interpolate a 2D array:
47
+ result = interp_2d(target_x, target_y, origin_x, origin_y, data_2d)
48
+ - Interpolate a 3D array (where the last two dimensions are spatial):
49
+ result = interp_2d(target_x, target_y, origin_x, origin_y, data_3d)
50
+ - Interpolate a 4D array (where the last two dimensions are spatial):
51
+ result = interp_2d(target_x, target_y, origin_x, origin_y, data_4d)
52
+ """
53
+
54
+ def interp_single(data_slice, target_points, origin_points, method):
55
+ return griddata(origin_points, data_slice.ravel(), target_points, method=method).reshape(target_y.shape)
56
+
57
+ # 确保目标网格和初始网格都是二维的
58
+ if len(target_y.shape) == 1:
59
+ target_x, target_y = np.meshgrid(target_x, target_y)
60
+ if len(origin_y.shape) == 1:
61
+ origin_x, origin_y = np.meshgrid(origin_x, origin_y)
62
+
63
+ # 根据经纬度网格判断输入数据的形状是否匹配
64
+ if origin_x.shape != data.shape[-2:] or origin_y.shape != data.shape[-2:]:
65
+ raise ValueError("Shape of data does not match shape of origin_x or origin_y.")
66
+
67
+ # 创建网格和展平数据
68
+ target_points = np.column_stack((target_y.ravel(), target_x.ravel()))
69
+ origin_points = np.column_stack((origin_y.ravel(), origin_x.ravel()))
70
+
71
+ # 根据是否并行选择不同的执行方式
72
+ if parallel:
73
+ with ThreadPoolExecutor(max_workers=mp.cpu_count() - 2) as executor:
74
+ if len(data.shape) == 2:
75
+ interpolated_data = list(executor.map(interp_single, [data], [target_points], [origin_points], [method]))
76
+ elif len(data.shape) == 3:
77
+ interpolated_data = list(executor.map(interp_single, [data[i] for i in range(data.shape[0])], [target_points] * data.shape[0], [origin_points] * data.shape[0], [method] * data.shape[0]))
78
+ elif len(data.shape) == 4:
79
+ index_combinations = list(itertools.product(range(data.shape[0]), range(data.shape[1])))
80
+ interpolated_data = list(executor.map(interp_single, [data[i, j] for i, j in index_combinations], [target_points] * len(index_combinations), [origin_points] * len(index_combinations), [method] * len(index_combinations)))
81
+ interpolated_data = np.array(interpolated_data).reshape(data.shape[0], data.shape[1], *target_y.shape)
82
+ else:
83
+ if len(data.shape) == 2:
84
+ interpolated_data = interp_single(data, target_points, origin_points, method)
85
+ elif len(data.shape) == 3:
86
+ interpolated_data = np.stack([interp_single(data[i], target_points, origin_points, method) for i in range(data.shape[0])])
87
+ elif len(data.shape) == 4:
88
+ interpolated_data = np.stack([np.stack([interp_single(data[i, j], target_points, origin_points, method) for j in range(data.shape[1])]) for i in range(data.shape[0])])
89
+
90
+ return np.array(interpolated_data)
27
91
 
28
- def interp_2d(target_x, target_y, origin_x, origin_y, data, method='linear'):
92
+
93
+ # ** 高维插值函数,插值最后两个维度
94
+ def interp_2d_20241213(target_x, target_y, origin_x, origin_y, data, method="linear"):
29
95
  """
30
96
  高维插值函数,默认插值最后两个维度,传输数据前请确保数据的维度正确
31
97
  参数:
@@ -52,7 +118,7 @@ def interp_2d(target_x, target_y, origin_x, origin_y, data, method='linear'):
52
118
 
53
119
  if origin_x.shape != dims[-2:] or origin_y.shape != dims[-2:]:
54
120
  print(origin_x.shape, dims[-2:])
55
- raise ValueError('Shape of data does not match shape of origin_x or origin_y.')
121
+ raise ValueError("Shape of data does not match shape of origin_x or origin_y.")
56
122
 
57
123
  # 将目标网格展平成一维数组
58
124
  target_points = np.column_stack((np.ravel(target_y), np.ravel(target_x)))
@@ -69,7 +135,7 @@ def interp_2d(target_x, target_y, origin_x, origin_y, data, method='linear'):
69
135
  for i in range(dims[0]):
70
136
  dt = griddata(origin_points, np.ravel(data[i, :, :]), target_points, method=method)
71
137
  interpolated_data.append(np.reshape(dt, target_y.shape))
72
- print(f'Interpolating {i+1}/{dims[0]}...')
138
+ print(f"Interpolating {i+1}/{dims[0]}...")
73
139
  interpolated_data = np.array(interpolated_data)
74
140
  elif len_dims == 4:
75
141
  interpolated_data = []
@@ -78,8 +144,8 @@ def interp_2d(target_x, target_y, origin_x, origin_y, data, method='linear'):
78
144
  for j in range(dims[1]):
79
145
  dt = griddata(origin_points, np.ravel(data[i, j, :, :]), target_points, method=method)
80
146
  interpolated_data[i].append(np.reshape(dt, target_y.shape))
81
- print(f'\rInterpolating {i*dims[1]+j+1}/{dims[0]*dims[1]}...', end='')
82
- print('\n')
147
+ print(f"\rInterpolating {i*dims[1]+j+1}/{dims[0]*dims[1]}...", end="")
148
+ print("\n")
83
149
  interpolated_data = np.array(interpolated_data)
84
150
 
85
151
  return interpolated_data
@@ -87,8 +153,8 @@ def interp_2d(target_x, target_y, origin_x, origin_y, data, method='linear'):
87
153
 
88
154
  # ** 高维插值函数,插值最后两个维度,使用多线程进行插值
89
155
  # 在本地电脑上可以提速三倍左右,超算上暂时无法加速
90
- def interp_2d_parallel(target_x, target_y, origin_x, origin_y, data, method='linear'):
91
- '''
156
+ def interp_2d_parallel_20241213(target_x, target_y, origin_x, origin_y, data, method="linear"):
157
+ """
92
158
  param {*} target_x 目标经度网格 1D 或 2D
93
159
  param {*} target_y 目标纬度网格 1D 或 2D
94
160
  param {*} origin_x 初始经度网格 1D 或 2D
@@ -98,22 +164,23 @@ def interp_2d_parallel(target_x, target_y, origin_x, origin_y, data, method='lin
98
164
  return {*} 插值结果
99
165
  description : 高维插值函数,默认插值最后两个维度,传输数据前请确保数据的维度正确
100
166
  example : interpolated_data = interp_2d_parallel(target_x, target_y, origin_x, origin_y, data, method='linear')
101
- '''
102
- def interp_single2d(target_y, target_x, origin_y, origin_x, data, method='linear'):
167
+ """
168
+
169
+ def interp_single2d(target_y, target_x, origin_y, origin_x, data, method="linear"):
103
170
  target_points = np.column_stack((np.ravel(target_y), np.ravel(target_x)))
104
171
  origin_points = np.column_stack((np.ravel(origin_y), np.ravel(origin_x)))
105
172
 
106
173
  dt = griddata(origin_points, np.ravel(data[:, :]), target_points, method=method)
107
174
  return np.reshape(dt, target_y.shape)
108
175
 
109
- def interp_single3d(i, target_y, target_x, origin_y, origin_x, data, method='linear'):
176
+ def interp_single3d(i, target_y, target_x, origin_y, origin_x, data, method="linear"):
110
177
  target_points = np.column_stack((np.ravel(target_y), np.ravel(target_x)))
111
178
  origin_points = np.column_stack((np.ravel(origin_y), np.ravel(origin_x)))
112
179
 
113
180
  dt = griddata(origin_points, np.ravel(data[i, :, :]), target_points, method=method)
114
181
  return np.reshape(dt, target_y.shape)
115
182
 
116
- def interp_single4d(i, j, target_y, target_x, origin_y, origin_x, data, method='linear'):
183
+ def interp_single4d(i, j, target_y, target_x, origin_y, origin_x, data, method="linear"):
117
184
  target_points = np.column_stack((np.ravel(target_y), np.ravel(target_x)))
118
185
  origin_points = np.column_stack((np.ravel(origin_y), np.ravel(origin_x)))
119
186
 
@@ -129,19 +196,31 @@ def interp_2d_parallel(target_x, target_y, origin_x, origin_y, data, method='lin
129
196
  len_dims = len(dims)
130
197
 
131
198
  if origin_x.shape != dims[-2:] or origin_y.shape != dims[-2:]:
132
- raise ValueError('数据形状与 origin_x 或 origin_y 的形状不匹配.')
199
+ raise ValueError("数据形状与 origin_x 或 origin_y 的形状不匹配.")
133
200
 
134
201
  interpolated_data = []
135
202
 
136
203
  # 使用多线程进行插值
137
- with ThreadPoolExecutor(max_workers=mp.cpu_count()-2) as executor:
138
- print(f'Using {mp.cpu_count()-2} threads...')
204
+ with ThreadPoolExecutor(max_workers=mp.cpu_count() - 2) as executor:
205
+ print(f"Using {mp.cpu_count()-2} threads...")
139
206
  if len_dims == 2:
140
207
  interpolated_data = list(executor.map(interp_single2d, [target_y], [target_x], [origin_y], [origin_x], [data], [method]))
141
208
  elif len_dims == 3:
142
- interpolated_data = list(executor.map(interp_single3d, [i for i in range(dims[0])], [target_y]*dims[0], [target_x]*dims[0], [origin_y]*dims[0], [origin_x]*dims[0], [data]*dims[0], [method]*dims[0]))
209
+ interpolated_data = list(executor.map(interp_single3d, [i for i in range(dims[0])], [target_y] * dims[0], [target_x] * dims[0], [origin_y] * dims[0], [origin_x] * dims[0], [data] * dims[0], [method] * dims[0]))
143
210
  elif len_dims == 4:
144
- interpolated_data = list(executor.map(interp_single4d, [i for i in range(dims[0]) for j in range(dims[1])], [j for i in range(dims[0]) for j in range(dims[1])], [target_y]*dims[0]*dims[1], [target_x]*dims[0]*dims[1], [origin_y]*dims[0]*dims[1], [origin_x]*dims[0]*dims[1], [data]*dims[0]*dims[1], [method]*dims[0]*dims[1]))
211
+ interpolated_data = list(
212
+ executor.map(
213
+ interp_single4d,
214
+ [i for i in range(dims[0]) for j in range(dims[1])],
215
+ [j for i in range(dims[0]) for j in range(dims[1])],
216
+ [target_y] * dims[0] * dims[1],
217
+ [target_x] * dims[0] * dims[1],
218
+ [origin_y] * dims[0] * dims[1],
219
+ [origin_x] * dims[0] * dims[1],
220
+ [data] * dims[0] * dims[1],
221
+ [method] * dims[0] * dims[1],
222
+ )
223
+ )
145
224
  interpolated_data = np.array(interpolated_data).reshape(dims[0], dims[1], target_y.shape[0], target_x.shape[1])
146
225
 
147
226
  interpolated_data = np.array(interpolated_data)
@@ -149,7 +228,7 @@ def interp_2d_parallel(target_x, target_y, origin_x, origin_y, data, method='lin
149
228
  return interpolated_data
150
229
 
151
230
 
152
- if __name__ == '__main__':
231
+ if __name__ == "__main__":
153
232
  import time
154
233
 
155
234
  import matplotlib.pyplot as plt
@@ -169,15 +248,15 @@ if __name__ == '__main__':
169
248
  data = np.random.rand(10, 10, 11, 11)
170
249
 
171
250
  start = time.time()
172
- interpolated_data = interp_2d(target_x, target_y, origin_x, origin_y, data)
173
- print(f'Interpolation time: {time.time()-start:.2f}s')
251
+ interpolated_data = interp_2d(target_x, target_y, origin_x, origin_y, data, parallel=False)
252
+ print(f"Interpolation time: {time.time()-start:.2f}s")
174
253
 
175
254
  print(interpolated_data.shape)
176
255
 
177
256
  # 高维插值多线程
178
257
  start = time.time()
179
- interpolated_data = interp_2d_parallel(target_x, target_y, origin_x, origin_y, data)
180
- print(f'Interpolation time: {time.time()-start:.2f}s')
258
+ interpolated_data = interp_2d(target_x, target_y, origin_x, origin_y, data)
259
+ print(f"Interpolation time: {time.time()-start:.2f}s")
181
260
 
182
261
  print(interpolated_data.shape)
183
262
  print(interpolated_data[0, 0, :, :].shape)
@@ -1,20 +1,21 @@
1
1
  #!/usr/bin/env python
2
2
  # coding=utf-8
3
- '''
3
+ """
4
4
  Author: Liu Kun && 16031215@qq.com
5
5
  Date: 2024-09-17 16:09:20
6
6
  LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2024-12-01 19:49:40
7
+ LastEditTime: 2024-12-26 08:09:01
8
8
  FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_down\\__init__.py
9
- Description:
9
+ Description:
10
10
  EditPlatform: vscode
11
11
  ComputerInfo: XPS 15 9510
12
12
  SystemInfo: Windows 11
13
13
  Python Version: 3.11
14
- '''
14
+ """
15
15
 
16
16
  # from .love_ocean import sign as love_ocean
17
17
  # from .meteorological_home import sign as meteorological_home
18
18
 
19
19
  from .hycom_3hourly import *
20
20
  from .literature import *
21
+ from .user_agent import *
@@ -21,6 +21,7 @@ import warnings
21
21
  from concurrent.futures import ThreadPoolExecutor, as_completed
22
22
  from pathlib import Path
23
23
  from threading import Lock
24
+ import re
24
25
 
25
26
  import matplotlib.pyplot as plt
26
27
  import numpy as np
@@ -29,17 +30,18 @@ import requests
29
30
  from bs4 import BeautifulSoup
30
31
  from rich import print
31
32
  from rich.progress import Progress
33
+ import glob
32
34
 
33
35
  warnings.filterwarnings("ignore", category=RuntimeWarning, message="Engine '.*' loading failed:.*")
34
36
 
35
- __all__ = ["draw_time_range", "download", "how_to_use", "get_time_list", "get_ua"]
37
+ __all__ = ["draw_time_range", "download", "how_to_use", "get_time_list"]
36
38
 
37
39
  # time resolution
38
40
  data_info = {"yearly": {}, "monthly": {}, "daily": {}, "hourly": {}}
39
41
 
40
42
  # hourly data
41
43
  # dataset: GLBv0.08, GLBu0.08, GLBy0.08
42
- data_info["hourly"]["dataset"] = {"GLBv0.08": {}, "GLBu0.08": {}, "GLBy0.08": {}}
44
+ data_info["hourly"]["dataset"] = {"GLBv0.08": {}, "GLBu0.08": {}, "GLBy0.08": {}, "ESPC_D": {}}
43
45
 
44
46
  # version
45
47
  # version of GLBv0.08: 53.X, 56.3, 57.2, 92.8, 57.7, 92.9, 93.0
@@ -48,6 +50,8 @@ data_info["hourly"]["dataset"]["GLBv0.08"]["version"] = {"53.X": {}, "56.3": {},
48
50
  data_info["hourly"]["dataset"]["GLBu0.08"]["version"] = {"93.0": {}}
49
51
  # version of GLBy0.08: 93.0
50
52
  data_info["hourly"]["dataset"]["GLBy0.08"]["version"] = {"93.0": {}}
53
+ # version of ESPC_D: V02
54
+ data_info["hourly"]["dataset"]["ESPC_D"]["version"] = {"V02": {}}
51
55
 
52
56
  # info details
53
57
  # time range
@@ -65,7 +69,9 @@ data_info["hourly"]["dataset"]["GLBv0.08"]["version"]["93.0"]["time_range"] = {"
65
69
  # GLBu0.08
66
70
  data_info["hourly"]["dataset"]["GLBu0.08"]["version"]["93.0"]["time_range"] = {"time_start": "2018091912", "time_end": "2018120909"}
67
71
  # GLBy0.08
68
- data_info["hourly"]["dataset"]["GLBy0.08"]["version"]["93.0"]["time_range"] = {"time_start": "2018120412", "time_end": "20300904"}
72
+ data_info["hourly"]["dataset"]["GLBy0.08"]["version"]["93.0"]["time_range"] = {"time_start": "2018120412", "time_end": "2024090509"}
73
+ # ESPC-D
74
+ data_info["hourly"]["dataset"]["ESPC_D"]["version"]["V02"]["time_range"] = {"time_start": "2024081012", "time_end": "2030010100"}
69
75
 
70
76
  # variable
71
77
  variable_info = {
@@ -94,6 +100,7 @@ data_info["hourly"]["dataset"]["GLBv0.08"]["version"]["92.9"]["classification"]
94
100
  data_info["hourly"]["dataset"]["GLBv0.08"]["version"]["93.0"]["classification"] = "var_different"
95
101
  data_info["hourly"]["dataset"]["GLBu0.08"]["version"]["93.0"]["classification"] = "var_different"
96
102
  data_info["hourly"]["dataset"]["GLBy0.08"]["version"]["93.0"]["classification"] = "var_year_different"
103
+ data_info["hourly"]["dataset"]["ESPC_D"]["version"]["V02"]["classification"] = "single_var_year_different"
97
104
 
98
105
  # download info
99
106
  # base url
@@ -141,7 +148,7 @@ data_info["hourly"]["dataset"]["GLBu0.08"]["version"]["93.0"]["url"] = url_930_u
141
148
  uv3z_930_y = {}
142
149
  ts3z_930_y = {}
143
150
  ssh_930_y = {}
144
- for y_930_y in range(2018, 2030):
151
+ for y_930_y in range(2018, 2025):
145
152
  uv3z_930_y[str(y_930_y)] = rf"https://ncss.hycom.org/thredds/ncss/GLBy0.08/expt_93.0/uv3z/{y_930_y}?"
146
153
  ts3z_930_y[str(y_930_y)] = rf"https://ncss.hycom.org/thredds/ncss/GLBy0.08/expt_93.0/ts3z/{y_930_y}?"
147
154
  ssh_930_y[str(y_930_y)] = rf"https://ncss.hycom.org/thredds/ncss/GLBy0.08/expt_93.0/ssh/{y_930_y}?"
@@ -152,12 +159,39 @@ url_930_y = {
152
159
  "ssh": ssh_930_y,
153
160
  }
154
161
  data_info["hourly"]["dataset"]["GLBy0.08"]["version"]["93.0"]["url"] = url_930_y
162
+ # ESPC-D-V02
163
+ u3z_espc_d_v02_y = {}
164
+ v3z_espc_d_v02_y = {}
165
+ t3z_espc_d_v02_y = {}
166
+ s3z_espc_d_v02_y = {}
167
+ ssh_espc_d_v02_y = {}
168
+ for y_espc_d_v02 in range(2024, 2030):
169
+ u3z_espc_d_v02_y[str(y_espc_d_v02)] = rf"https://ncss.hycom.org/thredds/ncss/ESPC-D-V02/u3z/{y_espc_d_v02}?"
170
+ v3z_espc_d_v02_y[str(y_espc_d_v02)] = rf"https://ncss.hycom.org/thredds/ncss/ESPC-D-V02/v3z/{y_espc_d_v02}?"
171
+ t3z_espc_d_v02_y[str(y_espc_d_v02)] = rf"https://ncss.hycom.org/thredds/ncss/ESPC-D-V02/t3z/{y_espc_d_v02}?"
172
+ s3z_espc_d_v02_y[str(y_espc_d_v02)] = rf"https://ncss.hycom.org/thredds/ncss/ESPC-D-V02/s3z/{y_espc_d_v02}?"
173
+ ssh_espc_d_v02_y[str(y_espc_d_v02)] = rf"https://ncss.hycom.org/thredds/ncss/ESPC-D-V02/ssh/{y_espc_d_v02}?"
174
+ url_espc_d_v02_y = {
175
+ "u3z": u3z_espc_d_v02_y,
176
+ "v3z": v3z_espc_d_v02_y,
177
+ "t3z": t3z_espc_d_v02_y,
178
+ "s3z": s3z_espc_d_v02_y,
179
+ "ssh": ssh_espc_d_v02_y,
180
+ }
181
+ data_info['hourly']['dataset']['ESPC_D']['version']['V02']['url'] = url_espc_d_v02_y
155
182
 
156
183
  var_group = {
157
184
  "uv3z": ["u", "v", "u_b", "v_b"],
158
185
  "ts3z": ["temp", "salt", "temp_b", "salt_b"],
159
186
  "ssh": ["ssh"],
160
187
  }
188
+ single_var_group = {
189
+ "u3z": ["u"],
190
+ "v3z": ["v"],
191
+ "t3z": ["temp"],
192
+ "s3z": ["salt"],
193
+ "ssh": ["ssh"],
194
+ }
161
195
 
162
196
 
163
197
  def draw_time_range(pic_save_folder=None):
@@ -205,7 +239,8 @@ def draw_time_range(pic_save_folder=None):
205
239
  # f"{row['version']}", ha='right', color='black')
206
240
  ymdh_s = row["start_date"].strftime("%Y-%m-%d %H")
207
241
  ymdh_e = row["end_date"].strftime("%Y-%m-%d %H")
208
- if k == 1 or k == len(combined_labels):
242
+ # if k == 1 or k == len(combined_labels):
243
+ if k == 1:
209
244
  plt.text(row["start_date"], k + 0.125, f"{ymdh_s}", ha="left", color="black")
210
245
  plt.text(row["end_date"], k + 0.125, f"{ymdh_e}", ha="right", color="black")
211
246
  else:
@@ -496,6 +531,14 @@ def get_base_url(dataset_name, version_name, var, ymdh_str):
496
531
  break
497
532
  if base_url is None:
498
533
  print("Please ensure the var is in [u,v,temp,salt,ssh,u_b,v_b,temp_b,salt_b]")
534
+ elif classification_method == "single_var_year_different":
535
+ base_url = None
536
+ for key, value in single_var_group.items():
537
+ if var in value:
538
+ base_url = url_dict[key][str(year_str)]
539
+ break
540
+ if base_url is None:
541
+ print("Please ensure the var is in [u,v,temp,salt,ssh]")
499
542
  return base_url
500
543
 
501
544
 
@@ -513,7 +556,44 @@ def clear_existing_file(file_full_path):
513
556
  print(f"{file_full_path} has been removed")
514
557
 
515
558
 
516
- def _get_file_size(file_path, unit="KB"):
559
+ def find_file(parent_path, fname, mode="path"):
560
+ """
561
+ description:
562
+ param {*} parent_path: The parent path where the files are located
563
+ param {*} fname: The file name pattern to search for
564
+ param {*} mode: 'path' to return the full path of the files, 'file' to return only the file names
565
+ return {*} A list of file paths or file names if files are found, None otherwise
566
+ """
567
+
568
+ def natural_sort_key(s):
569
+ """生成一个用于自然排序的键"""
570
+ return [int(text) if text.isdigit() else text.lower() for text in re.split("([0-9]+)", s)]
571
+
572
+ # 将parent_path和fname结合成完整的搜索路径
573
+ search_pattern = os.path.join(str(parent_path), fname)
574
+
575
+ # 使用glob模块查找所有匹配的文件
576
+ matched_files = glob.glob(search_pattern)
577
+
578
+ # 如果没有找到任何文件,则返回False
579
+ if not matched_files:
580
+ return None
581
+
582
+ # 在find_files函数中替换natsorted调用
583
+ matched_files = sorted(matched_files, key=natural_sort_key)
584
+
585
+ # 根据mode参数决定返回的内容
586
+ if mode == "file":
587
+ # 只返回文件名
588
+ result = [os.path.basename(file) for file in matched_files]
589
+ else: # 默认为'path'
590
+ # 返回文件的绝对路径
591
+ result = [os.path.abspath(file) for file in matched_files]
592
+
593
+ return result
594
+
595
+
596
+ def file_size(file_path, unit="KB"):
517
597
  # 检查文件是否存在
518
598
  if not os.path.exists(file_path):
519
599
  return "文件不存在"
@@ -534,12 +614,34 @@ def _get_file_size(file_path, unit="KB"):
534
614
  return converted_size
535
615
 
536
616
 
537
- def check_existing_file(file_full_path):
617
+ # ** 计算文件夹下指定相关文件的平均大小
618
+ def mean_size(parent_path, fname):
619
+ flist = find_file(parent_path, fname)
620
+ if flist:
621
+ size_list = [file_size(f) for f in flist if file_size(f) != 0]
622
+ else:
623
+ size_list = []
624
+ if size_list:
625
+ min_size, max_size = min(size_list), max(size_list)
626
+ mean_size = sum(size_list) / len(size_list)
627
+ else:
628
+ mean_size, min_size, max_size = 0, 0, 0
629
+ return mean_size, min_size, max_size
630
+
631
+
632
+ def check_existing_file(file_full_path, min_size):
538
633
  if os.path.exists(file_full_path):
539
634
  print(f"[bold #FFA54F]{file_full_path} exists")
540
- fsize = _get_file_size(file_full_path)
635
+ fsize = file_size(file_full_path)
636
+ if min_size:
637
+ if fsize < min_size:
638
+ print(f"[bold #FFA54F]{file_full_path} ({fsize:.2f} KB) may be incomplete")
639
+ # clear_existing_file(file_full_path)
640
+ return False
641
+ else:
642
+ return True
541
643
  if fsize < 5:
542
- print(f"[bold #FFA54F]{file_full_path} may be incomplete\nFile size: {fsize:.2f} KB")
644
+ print(f"[bold #FFA54F]{file_full_path} ({fsize:.2f} KB) may be incomplete")
543
645
  # clear_existing_file(file_full_path)
544
646
  return False
545
647
  else:
@@ -623,8 +725,13 @@ def get_proxy():
623
725
  def download_file(target_url, store_path, file_name, check=False):
624
726
  # Check if the file exists
625
727
  fname = Path(store_path) / file_name
728
+ file_name_split = file_name.split("_")
729
+ same_file = f'{file_name_split[0]}_{file_name_split[1]}*nc'
730
+ fsize_mean, fsize_min, fsize_max = mean_size(store_path, same_file)
731
+ set_min_size = fsize_mean - 0.5 * (fsize_max - fsize_min)
732
+ set_min_size = set_min_size if set_min_size > 0 else fsize_min
626
733
  if check:
627
- if check_existing_file(fname):
734
+ if check_existing_file(fname, set_min_size*0.8):
628
735
  count_dict["skip"] += 1
629
736
  return
630
737
  clear_existing_file(fname)
@@ -819,29 +926,37 @@ def prepare_url_to_download(var, lon_min=0, lon_max=359.92, lat_min=-80, lat_max
819
926
  file_name = f"HYCOM_{variable_info[var]['var_name']}_{download_time}_{download_time_end}.nc"
820
927
  download_file(submit_url, store_path, file_name, check)
821
928
  else:
822
- varlist = [_ for _ in var]
823
- for key, value in var_group.items():
824
- current_group = []
825
- for v in varlist:
826
- if v in value:
827
- current_group.append(v)
828
- if len(current_group) == 0:
829
- continue
830
-
831
- var = current_group[0]
832
- submit_url = get_submit_url_var(var, depth, level_num, lon_min, lon_max, lat_min, lat_max, dataset_name, version_name, download_time, download_time_end)
833
- file_name = f"HYCOM_{variable_info[var]['var_name']}_{download_time}.nc"
834
- old_str = f'var={variable_info[var]["var_name"]}'
835
- new_str = f'var={variable_info[var]["var_name"]}'
836
- if len(current_group) > 1:
837
- for v in current_group[1:]:
838
- new_str = f'{new_str}&var={variable_info[v]["var_name"]}'
839
- submit_url = submit_url.replace(old_str, new_str)
840
- # file_name = f'HYCOM_{'-'.join([variable_info[v]["var_name"] for v in current_group])}_{download_time}.nc'
841
- file_name = f"HYCOM_{key}_{download_time}.nc"
929
+ if download_time < "2024081012":
930
+ varlist = [_ for _ in var]
931
+ for key, value in var_group.items():
932
+ current_group = []
933
+ for v in varlist:
934
+ if v in value:
935
+ current_group.append(v)
936
+ if len(current_group) == 0:
937
+ continue
938
+
939
+ var = current_group[0]
940
+ submit_url = get_submit_url_var(var, depth, level_num, lon_min, lon_max, lat_min, lat_max, dataset_name, version_name, download_time, download_time_end)
941
+ file_name = f"HYCOM_{variable_info[var]['var_name']}_{download_time}.nc"
942
+ old_str = f'var={variable_info[var]["var_name"]}'
943
+ new_str = f'var={variable_info[var]["var_name"]}'
944
+ if len(current_group) > 1:
945
+ for v in current_group[1:]:
946
+ new_str = f'{new_str}&var={variable_info[v]["var_name"]}'
947
+ submit_url = submit_url.replace(old_str, new_str)
948
+ # file_name = f'HYCOM_{'-'.join([variable_info[v]["var_name"] for v in current_group])}_{download_time}.nc'
949
+ file_name = f"HYCOM_{key}_{download_time}.nc"
950
+ if download_time_end is not None:
951
+ file_name = f"HYCOM_{key}_{download_time}_{download_time_end}.nc"
952
+ download_file(submit_url, store_path, file_name, check)
953
+ else:
954
+ for v in var:
955
+ submit_url = get_submit_url_var(v, depth, level_num, lon_min, lon_max, lat_min, lat_max, dataset_name, version_name, download_time, download_time_end)
956
+ file_name = f"HYCOM_{variable_info[v]['var_name']}_{download_time}.nc"
842
957
  if download_time_end is not None:
843
- file_name = f"HYCOM_{key}_{download_time}_{download_time_end}.nc"
844
- download_file(submit_url, store_path, file_name, check)
958
+ file_name = f"HYCOM_{variable_info[v]['var_name']}_{download_time}_{download_time_end}.nc"
959
+ download_file(submit_url, store_path, file_name, check)
845
960
 
846
961
 
847
962
  def convert_full_name_to_short_name(full_name):
@@ -913,7 +1028,7 @@ def download_hourly_func(var, time_s, time_e, lon_min=0, lon_max=359.92, lat_min
913
1028
  parallel_counter = 0
914
1029
  counter_lock = Lock() # 创建一个锁,线程安全的计数器
915
1030
  if ymdh_time_s == ymdh_time_e:
916
- 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)
1031
+ 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)
917
1032
  elif int(ymdh_time_s) < int(ymdh_time_e):
918
1033
  print("Downloading a series of files...")
919
1034
  time_list = get_time_list(ymdh_time_s, ymdh_time_e, 3, "hour")
@@ -1111,9 +1226,9 @@ def how_to_use():
1111
1226
 
1112
1227
  if __name__ == "__main__":
1113
1228
  # help(hycom3h.download)
1114
- time_s, time_e = "2023010100", "2023123121"
1229
+ time_s, time_e = "2024081012", "2024081115"
1115
1230
  merge_name = f"{time_s}_{time_e}" # 合并后的文件名
1116
- root_path = r"G:\Data\HYCOM\3hourly"
1231
+ root_path = r"G:\Data\HYCOM\3hourly_test"
1117
1232
  location_dict = {"west": 105, "east": 130, "south": 15, "north": 45}
1118
1233
  download_dict = {
1119
1234
  "water_u": {"simple_name": "u", "download": 1},
@@ -1141,6 +1256,8 @@ if __name__ == "__main__":
1141
1256
  download_switch, single_var = True, False
1142
1257
  combine_switch = False
1143
1258
  copy_switch, copy_dir = False, r"G:\Data\HYCOM\3hourly"
1259
+
1260
+ # draw_time_range(pic_save_folder=r'I:\Delete')
1144
1261
 
1145
1262
  if download_switch:
1146
1263
  if single_var:
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env python
2
+ # coding=utf-8
3
+ """
4
+ Author: Liu Kun && 16031215@qq.com
5
+ Date: 2024-12-26 08:06:34
6
+ LastEditors: Liu Kun && 16031215@qq.com
7
+ LastEditTime: 2024-12-26 08:06:34
8
+ FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_down\\user_agent.py
9
+ Description:
10
+ EditPlatform: vscode
11
+ ComputerInfo: XPS 15 9510
12
+ SystemInfo: Windows 11
13
+ Python Version: 3.12
14
+ """
15
+ import os
16
+ import random
17
+
18
+
19
+ __all__ = ["get_ua"]
20
+
21
+
22
+ def get_ua():
23
+ current_dir = os.path.dirname(os.path.abspath(__file__))
24
+ ua_file_txt = os.path.join(current_dir, "User_Agent-list.txt")
25
+
26
+ with open(ua_file_txt, "r") as f:
27
+ ua_list = f.readlines()
28
+ # 去掉换行符和空行
29
+ ua_list = [line.strip() for line in ua_list if line.strip()]
30
+
31
+ # if current_platform == 'Linux':
32
+ # ua_list = [line for line in ua_list if 'Linux' in line]
33
+
34
+ return random.choice(ua_list)