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.
- oafuncs/data_store/OAFuncs.png +0 -0
- oafuncs/oa_cmap.py +1 -0
- oafuncs/oa_data.py +107 -28
- oafuncs/oa_down/__init__.py +5 -4
- oafuncs/oa_down/hycom_3hourly.py +152 -35
- oafuncs/oa_down/user_agent.py +34 -0
- oafuncs/oa_draw.py +165 -103
- oafuncs/oa_file.py +66 -53
- oafuncs/oa_help.py +19 -16
- oafuncs/oa_nc.py +82 -114
- oafuncs-0.0.83.dist-info/METADATA +91 -0
- oafuncs-0.0.83.dist-info/RECORD +26 -0
- oafuncs/oa_down/test.py +0 -151
- oafuncs/oa_s/__init__.py +0 -23
- oafuncs/oa_s/oa_cmap.py +0 -163
- oafuncs/oa_s/oa_data.py +0 -187
- oafuncs/oa_s/oa_draw.py +0 -451
- oafuncs/oa_s/oa_file.py +0 -332
- oafuncs/oa_s/oa_help.py +0 -39
- oafuncs/oa_s/oa_nc.py +0 -410
- oafuncs/oa_s/oa_python.py +0 -107
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/__init__.py" +0 -26
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_cmap.py" +0 -163
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_data.py" +0 -187
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/__init__.py" +0 -20
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/hycom_3hourly.py" +0 -1176
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/literature.py" +0 -332
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_down/test_ua.py" +0 -151
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_draw.py" +0 -451
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_file.py" +0 -332
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_help.py" +0 -39
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_nc.py" +0 -410
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_python.py" +0 -107
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/__init__.py" +0 -21
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/meteorological.py" +0 -168
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/ocean.py" +0 -158
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_sign/scientific.py" +0 -139
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/__init__.py" +0 -18
- oafuncs - /321/205/320/231/320/277/321/206/320/254/320/274/oa_tool/email.py" +0 -114
- oafuncs-0.0.81.dist-info/METADATA +0 -918
- oafuncs-0.0.81.dist-info/RECORD +0 -51
- {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/LICENSE.txt +0 -0
- {oafuncs-0.0.81.dist-info → oafuncs-0.0.83.dist-info}/WHEEL +0 -0
- {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-
|
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__ = [
|
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
|
-
|
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(
|
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
|
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
|
82
|
-
print(
|
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
|
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
|
-
|
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=
|
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=
|
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(
|
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
|
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(
|
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__ ==
|
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
|
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 =
|
180
|
-
print(f
|
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)
|
oafuncs/oa_down/__init__.py
CHANGED
@@ -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-
|
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 *
|
oafuncs/oa_down/hycom_3hourly.py
CHANGED
@@ -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"
|
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": "
|
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,
|
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
|
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
|
-
|
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 =
|
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}
|
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
|
-
|
823
|
-
|
824
|
-
|
825
|
-
|
826
|
-
|
827
|
-
|
828
|
-
|
829
|
-
|
830
|
-
|
831
|
-
|
832
|
-
|
833
|
-
|
834
|
-
|
835
|
-
|
836
|
-
|
837
|
-
|
838
|
-
|
839
|
-
|
840
|
-
|
841
|
-
|
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_{
|
844
|
-
|
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 = "
|
1229
|
+
time_s, time_e = "2024081012", "2024081115"
|
1115
1230
|
merge_name = f"{time_s}_{time_e}" # 合并后的文件名
|
1116
|
-
root_path = r"G:\Data\HYCOM\
|
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)
|