oafuncs 0.0.84__tar.gz → 0.0.86__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.
- {oafuncs-0.0.84/oafuncs.egg-info → oafuncs-0.0.86}/PKG-INFO +1 -1
- oafuncs-0.0.86/oafuncs/data_store/OAFuncs.png +0 -0
- oafuncs-0.0.86/oafuncs/oa_cmap.py +213 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_data.py +88 -4
- oafuncs-0.0.86/oafuncs/oa_draw.py +325 -0
- oafuncs-0.0.86/oafuncs/oa_help.py +123 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_nc.py +50 -19
- {oafuncs-0.0.84 → oafuncs-0.0.86/oafuncs.egg-info}/PKG-INFO +1 -1
- {oafuncs-0.0.84 → oafuncs-0.0.86}/setup.py +1 -8
- oafuncs-0.0.84/oafuncs/data_store/OAFuncs.png +0 -0
- oafuncs-0.0.84/oafuncs/oa_cmap.py +0 -164
- oafuncs-0.0.84/oafuncs/oa_draw.py +0 -513
- oafuncs-0.0.84/oafuncs/oa_help.py +0 -42
- {oafuncs-0.0.84 → oafuncs-0.0.86}/LICENSE.txt +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/MANIFEST.in +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/README.md +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/__init__.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_down/User_Agent-list.txt +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_down/__init__.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_down/hycom_3hourly.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_down/literature.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_down/test_ua.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_down/user_agent.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_file.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_python.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_sign/__init__.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_sign/meteorological.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_sign/ocean.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_sign/scientific.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_tool/__init__.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs/oa_tool/email.py +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs.egg-info/SOURCES.txt +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs.egg-info/dependency_links.txt +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs.egg-info/requires.txt +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/oafuncs.egg-info/top_level.txt +0 -0
- {oafuncs-0.0.84 → oafuncs-0.0.86}/setup.cfg +0 -0
Binary file
|
@@ -0,0 +1,213 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# coding=utf-8
|
3
|
+
"""
|
4
|
+
Author: Liu Kun && 16031215@qq.com
|
5
|
+
Date: 2024-09-17 16:55:11
|
6
|
+
LastEditors: Liu Kun && 16031215@qq.com
|
7
|
+
LastEditTime: 2024-11-21 13:14:24
|
8
|
+
FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_cmap.py
|
9
|
+
Description:
|
10
|
+
EditPlatform: vscode
|
11
|
+
ComputerInfo: XPS 15 9510
|
12
|
+
SystemInfo: Windows 11
|
13
|
+
Python Version: 3.11
|
14
|
+
"""
|
15
|
+
|
16
|
+
import matplotlib as mpl
|
17
|
+
import matplotlib.pyplot as plt
|
18
|
+
import numpy as np
|
19
|
+
from rich import print
|
20
|
+
|
21
|
+
__all__ = ["show", "to_color", "create", "create_rgbtxt", "get"]
|
22
|
+
|
23
|
+
# ** 将cmap用填色图可视化(官网摘抄函数)
|
24
|
+
def show(colormaps):
|
25
|
+
"""
|
26
|
+
Description:
|
27
|
+
Helper function to plot data with associated colormap.
|
28
|
+
Parameters:
|
29
|
+
colormaps : list of colormaps, or a single colormap; can be a string or a colormap object.
|
30
|
+
Example:
|
31
|
+
cmap = ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])
|
32
|
+
show([cmap]); show("viridis"); show(["viridis", "cividis"])
|
33
|
+
"""
|
34
|
+
if isinstance(colormaps, str) or isinstance(colormaps, mpl.colors.Colormap):
|
35
|
+
colormaps = [colormaps]
|
36
|
+
np.random.seed(19680801)
|
37
|
+
data = np.random.randn(30, 30)
|
38
|
+
n = len(colormaps)
|
39
|
+
fig, axs = plt.subplots(1, n, figsize=(n * 2 + 2, 3), constrained_layout=True, squeeze=False)
|
40
|
+
for [ax, cmap] in zip(axs.flat, colormaps):
|
41
|
+
psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)
|
42
|
+
fig.colorbar(psm, ax=ax)
|
43
|
+
plt.show()
|
44
|
+
|
45
|
+
|
46
|
+
# ** 将cmap转为list,即多个颜色的列表
|
47
|
+
def to_color(cmap, n=256):
|
48
|
+
"""
|
49
|
+
Description:
|
50
|
+
Convert a colormap to a list of colors
|
51
|
+
Parameters:
|
52
|
+
cmap : str; the name of the colormap
|
53
|
+
n : int, optional; the number of colors
|
54
|
+
Return:
|
55
|
+
out_colors : list of colors
|
56
|
+
Example:
|
57
|
+
out_colors = to_color('viridis', 256)
|
58
|
+
"""
|
59
|
+
c_map = mpl.colormaps.get_cmap(cmap)
|
60
|
+
out_colors = [c_map(i) for i in np.linspace(0, 1, n)]
|
61
|
+
return out_colors
|
62
|
+
|
63
|
+
|
64
|
+
# ** 自制cmap,多色,可带位置
|
65
|
+
def create(colors: list, nodes=None, under=None, over=None): # 利用颜色快速配色
|
66
|
+
"""
|
67
|
+
Description:
|
68
|
+
Create a custom colormap
|
69
|
+
Parameters:
|
70
|
+
colors : list of colors
|
71
|
+
nodes : list of positions
|
72
|
+
under : color
|
73
|
+
over : color
|
74
|
+
Return:
|
75
|
+
cmap : colormap
|
76
|
+
Example:
|
77
|
+
cmap = create(['#C2B7F3','#B3BBF2','#B0CBF1','#ACDCF0','#A8EEED'])
|
78
|
+
cmap = create(['aliceblue','skyblue','deepskyblue'],[0.0,0.5,1.0])
|
79
|
+
"""
|
80
|
+
|
81
|
+
if nodes is None: # 采取自动分配比例
|
82
|
+
cmap_color = mpl.colors.LinearSegmentedColormap.from_list("mycmap", colors)
|
83
|
+
else: # 按照提供比例分配
|
84
|
+
cmap_color = mpl.colors.LinearSegmentedColormap.from_list("mycmap", list(zip(nodes, colors)))
|
85
|
+
if under is not None:
|
86
|
+
cmap_color.set_under(under)
|
87
|
+
if over is not None:
|
88
|
+
cmap_color.set_over(over)
|
89
|
+
return cmap_color
|
90
|
+
|
91
|
+
|
92
|
+
# ** 根据RGB的txt文档制作色卡(利用Grads调色盘)
|
93
|
+
def create_rgbtxt(rgbtxt_file,split_mark=','): # 根据RGB的txt文档制作色卡/根据rgb值制作
|
94
|
+
"""
|
95
|
+
Description
|
96
|
+
-----------
|
97
|
+
Make a color card according to the RGB txt document, each line in the txt file is an RGB value, separated by commas, such as: 251,251,253
|
98
|
+
|
99
|
+
Parameters
|
100
|
+
----------
|
101
|
+
rgbtxt_file : str, the path of txt file
|
102
|
+
split_mark : str, optional, default is ','; the split mark of rgb value
|
103
|
+
|
104
|
+
Returns
|
105
|
+
-------
|
106
|
+
cmap : colormap
|
107
|
+
|
108
|
+
Example
|
109
|
+
-------
|
110
|
+
cmap=create_rgbtxt(path,split_mark=',')
|
111
|
+
|
112
|
+
txt example
|
113
|
+
-----------
|
114
|
+
251,251,253
|
115
|
+
225,125,25
|
116
|
+
250,205,255
|
117
|
+
"""
|
118
|
+
with open(rgbtxt_file) as fid:
|
119
|
+
data = fid.readlines()
|
120
|
+
n = len(data)
|
121
|
+
rgb = np.zeros((n, 3))
|
122
|
+
for i in np.arange(n):
|
123
|
+
rgb[i][0] = data[i].split(split_mark)[0]
|
124
|
+
rgb[i][1] = data[i].split(split_mark)[1]
|
125
|
+
rgb[i][2] = data[i].split(split_mark)[2]
|
126
|
+
max_rgb = np.max(rgb)
|
127
|
+
if max_rgb > 2: # if the value is greater than 2, it is normalized to 0-1
|
128
|
+
rgb = rgb / 255.0
|
129
|
+
my_cmap = mpl.colors.ListedColormap(rgb, name="my_color")
|
130
|
+
return my_cmap
|
131
|
+
|
132
|
+
|
133
|
+
# ** 选择cmap
|
134
|
+
def get(cmap_name=None, query=False):
|
135
|
+
"""
|
136
|
+
Description:
|
137
|
+
Choosing a colormap from the list of available colormaps or a custom colormap
|
138
|
+
Parameters:
|
139
|
+
cmap_name : str, optional; the name of the colormap
|
140
|
+
query : bool, optional; whether to query the available colormap names
|
141
|
+
Return:
|
142
|
+
cmap : colormap
|
143
|
+
Example:
|
144
|
+
cmap = get('viridis')
|
145
|
+
cmap = get('diverging_1')
|
146
|
+
cmap = get('cold_1')
|
147
|
+
cmap = get('warm_1')
|
148
|
+
cmap = get('colorful_1')
|
149
|
+
"""
|
150
|
+
|
151
|
+
my_cmap_dict = {
|
152
|
+
"diverging_1": create(["#4e00b3", "#0000FF", "#00c0ff", "#a1d3ff", "#DCDCDC", "#FFD39B", "#FF8247", "#FF0000", "#FF5F9E"]),
|
153
|
+
"cold_1": create(["#4e00b3", "#0000FF", "#00c0ff", "#a1d3ff", "#DCDCDC"]),
|
154
|
+
"warm_1": create(["#DCDCDC", "#FFD39B", "#FF8247", "#FF0000", "#FF5F9E"]),
|
155
|
+
# "land_1": create_custom(["#3E6436", "#678A59", "#91A176", "#B8A87D", "#D9CBB2"], under="#A6CEE3", over="#FFFFFF"),
|
156
|
+
# "ocean_1": create_custom(["#126697", "#2D88B3", "#4EA1C9", "#78B9D8", "#A6CEE3"], under="#8470FF", over="#3E6436"),
|
157
|
+
# "ocean_land_1": create_custom(
|
158
|
+
# [
|
159
|
+
# "#126697", # 深蓝(深海)
|
160
|
+
# "#2D88B3", # 蓝
|
161
|
+
# "#4EA1C9", # 蓝绿
|
162
|
+
# "#78B9D8", # 浅蓝(浅海)
|
163
|
+
# "#A6CEE3", # 浅蓝(近岸)
|
164
|
+
# "#AAAAAA", # 灰色(0值,海平面)
|
165
|
+
# "#D9CBB2", # 沙质土壤色(陆地开始)
|
166
|
+
# "#B8A87D", # 浅棕
|
167
|
+
# "#91A176", # 浅绿
|
168
|
+
# "#678A59", # 中绿
|
169
|
+
# "#3E6436", # 深绿(高山)
|
170
|
+
# ]
|
171
|
+
# ),
|
172
|
+
"colorful_1": create(["#6d00db", "#9800cb", "#F2003C", "#ff4500", "#ff7f00", "#FE28A2", "#FFC0CB", "#DDA0DD", "#40E0D0", "#1a66f2", "#00f7fb", "#8fff88", "#E3FF00"]),
|
173
|
+
}
|
174
|
+
if query:
|
175
|
+
print("Available cmap names:")
|
176
|
+
print('-' * 20)
|
177
|
+
print('Defined by myself:')
|
178
|
+
for key, _ in my_cmap_dict.items():
|
179
|
+
print(key)
|
180
|
+
print('-' * 20)
|
181
|
+
print('Matplotlib built-in:')
|
182
|
+
print(mpl.colormaps())
|
183
|
+
print("-" * 20)
|
184
|
+
|
185
|
+
if cmap_name is None:
|
186
|
+
return
|
187
|
+
|
188
|
+
if cmap_name in my_cmap_dict:
|
189
|
+
return my_cmap_dict[cmap_name]
|
190
|
+
else:
|
191
|
+
try:
|
192
|
+
return mpl.colormaps.get_cmap(cmap_name)
|
193
|
+
except ValueError:
|
194
|
+
raise ValueError(f"Unknown cmap name: {cmap_name}")
|
195
|
+
|
196
|
+
|
197
|
+
if __name__ == "__main__":
|
198
|
+
# ** 测试自制cmap
|
199
|
+
colors = ["#C2B7F3", "#B3BBF2", "#B0CBF1", "#ACDCF0", "#A8EEED"]
|
200
|
+
nodes = [0.0, 0.2, 0.4, 0.6, 1.0]
|
201
|
+
c_map = create(colors, nodes)
|
202
|
+
show([c_map])
|
203
|
+
|
204
|
+
# ** 测试自制diverging型cmap
|
205
|
+
diverging_cmap = create(["#4e00b3", "#0000FF", "#00c0ff", "#a1d3ff", "#DCDCDC", "#FFD39B", "#FF8247", "#FF0000", "#FF5F9E"])
|
206
|
+
show([diverging_cmap])
|
207
|
+
|
208
|
+
# ** 测试根据RGB的txt文档制作色卡
|
209
|
+
file_path = "E:/python/colorbar/test.txt"
|
210
|
+
cmap_rgb = create_rgbtxt(file_path)
|
211
|
+
|
212
|
+
# ** 测试将cmap转为list
|
213
|
+
out_colors = to_color("viridis", 256)
|
@@ -15,12 +15,12 @@ Python Version: 3.11
|
|
15
15
|
|
16
16
|
import itertools
|
17
17
|
import multiprocessing as mp
|
18
|
-
from concurrent.futures import ThreadPoolExecutor
|
18
|
+
from concurrent.futures import ThreadPoolExecutor, ProcessPoolExecutor
|
19
19
|
|
20
20
|
import numpy as np
|
21
21
|
from scipy.interpolate import griddata
|
22
22
|
|
23
|
-
__all__ = ["interp_2d"]
|
23
|
+
__all__ = ["interp_2d","ParallelExecutor"]
|
24
24
|
|
25
25
|
|
26
26
|
def interp_2d(target_x, target_y, origin_x, origin_y, data, method="linear", parallel=True):
|
@@ -90,6 +90,73 @@ def interp_2d(target_x, target_y, origin_x, origin_y, data, method="linear", par
|
|
90
90
|
return np.array(interpolated_data)
|
91
91
|
|
92
92
|
|
93
|
+
class ParallelExecutor:
|
94
|
+
"""
|
95
|
+
通用并行计算类,支持多进程和多线程模式。
|
96
|
+
|
97
|
+
使用说明:
|
98
|
+
1. 创建实例时选择模式:
|
99
|
+
- mode="process" 使用多进程(适合 CPU 密集型任务)。
|
100
|
+
- mode="thread" 使用多线程(适合 IO 密集型任务)。
|
101
|
+
|
102
|
+
2. 调用 run 方法:
|
103
|
+
- 参数 func:需要并行执行的函数。
|
104
|
+
- 参数 param_list:参数列表,每个元素是传递给 func 的参数元组。
|
105
|
+
|
106
|
+
示例:
|
107
|
+
# 示例 1:计算平方
|
108
|
+
def compute_square(x):
|
109
|
+
return x * x
|
110
|
+
|
111
|
+
params = [(i,) for i in range(10)]
|
112
|
+
executor = ParallelExecutor(mode="process", max_workers=4)
|
113
|
+
results = executor.run(compute_square, params)
|
114
|
+
print("Results:", results)
|
115
|
+
|
116
|
+
# 示例 2:计算两数之和
|
117
|
+
def compute_sum(a, b):
|
118
|
+
return a + b
|
119
|
+
|
120
|
+
params = [(1, 2), (3, 4), (5, 6)]
|
121
|
+
executor = ParallelExecutor(mode="thread", max_workers=2)
|
122
|
+
results = executor.run(compute_sum, params)
|
123
|
+
print("Results:", results)
|
124
|
+
|
125
|
+
参数:
|
126
|
+
mode (str): 并行模式,"process" 表示多进程,"thread" 表示多线程。
|
127
|
+
max_workers (int): 最大并行工作数,默认为 CPU 核心数减 2。
|
128
|
+
"""
|
129
|
+
|
130
|
+
def __init__(self, mode="process", max_workers=mp.cpu_count() - 2):
|
131
|
+
self.mode = mode
|
132
|
+
self.max_workers = max_workers
|
133
|
+
self.executor = ProcessPoolExecutor if mode == "process" else ThreadPoolExecutor
|
134
|
+
|
135
|
+
def run(self, func, param_list):
|
136
|
+
"""
|
137
|
+
并行运行指定函数,并确保结果顺序与输入参数顺序一致。
|
138
|
+
|
139
|
+
参数:
|
140
|
+
func (callable): 需要并行执行的函数。
|
141
|
+
param_list (list): 参数列表,每个元素是传递给 func 的参数元组。
|
142
|
+
|
143
|
+
返回:
|
144
|
+
results (list): 按输入顺序返回的结果。
|
145
|
+
"""
|
146
|
+
results = [None] * len(param_list) # 预分配结果数组
|
147
|
+
|
148
|
+
with self.executor(max_workers=self.max_workers) as executor:
|
149
|
+
# 提交任务并保存其索引
|
150
|
+
future_to_index = {executor.submit(func, *params): idx for idx, params in enumerate(param_list)}
|
151
|
+
|
152
|
+
for future in future_to_index:
|
153
|
+
idx = future_to_index[future] # 获取原始索引
|
154
|
+
results[idx] = future.result() # 将结果存放到对应位置
|
155
|
+
|
156
|
+
return results
|
157
|
+
|
158
|
+
|
159
|
+
# ---------------------------------------------------------------------------------- not used below ----------------------------------------------------------------------------------
|
93
160
|
# ** 高维插值函数,插值最后两个维度
|
94
161
|
def interp_2d_20241213(target_x, target_y, origin_x, origin_y, data, method="linear"):
|
95
162
|
"""
|
@@ -228,8 +295,25 @@ def interp_2d_parallel_20241213(target_x, target_y, origin_x, origin_y, data, me
|
|
228
295
|
return interpolated_data
|
229
296
|
|
230
297
|
|
298
|
+
def _test_sum(a,b):
|
299
|
+
return a+b
|
300
|
+
|
301
|
+
|
231
302
|
if __name__ == "__main__":
|
232
|
-
|
303
|
+
# 参数列表:每个参数是元组
|
304
|
+
params_list = [(1, 2), (3, 4), (5, 6), (7, 8), (9, 10)]
|
305
|
+
|
306
|
+
# 创建并行执行器
|
307
|
+
executor = ParallelExecutor()
|
308
|
+
|
309
|
+
# 并行运行
|
310
|
+
results = executor.run(_test_sum, params_list)
|
311
|
+
|
312
|
+
# 验证结果顺序
|
313
|
+
print("Params:", params_list)
|
314
|
+
print("Results:", results)
|
315
|
+
pass
|
316
|
+
""" import time
|
233
317
|
|
234
318
|
import matplotlib.pyplot as plt
|
235
319
|
|
@@ -263,4 +347,4 @@ if __name__ == "__main__":
|
|
263
347
|
plt.figure()
|
264
348
|
plt.contourf(target_x, target_y, interpolated_data[0, 0, :, :])
|
265
349
|
plt.colorbar()
|
266
|
-
plt.show()
|
350
|
+
plt.show() """
|
@@ -0,0 +1,325 @@
|
|
1
|
+
#!/usr/bin/env python
|
2
|
+
# coding=utf-8
|
3
|
+
"""
|
4
|
+
Author: Liu Kun && 16031215@qq.com
|
5
|
+
Date: 2024-09-17 17:26:11
|
6
|
+
LastEditors: Liu Kun && 16031215@qq.com
|
7
|
+
LastEditTime: 2024-11-21 13:10:47
|
8
|
+
FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_draw.py
|
9
|
+
Description:
|
10
|
+
EditPlatform: vscode
|
11
|
+
ComputerInfo: XPS 15 9510
|
12
|
+
SystemInfo: Windows 11
|
13
|
+
Python Version: 3.11
|
14
|
+
"""
|
15
|
+
|
16
|
+
|
17
|
+
import warnings
|
18
|
+
|
19
|
+
import cartopy.crs as ccrs
|
20
|
+
import cartopy.feature as cfeature
|
21
|
+
import matplotlib as mpl
|
22
|
+
import matplotlib.pyplot as plt
|
23
|
+
import numpy as np
|
24
|
+
import xarray as xr
|
25
|
+
from cartopy.mpl.ticker import LatitudeFormatter, LongitudeFormatter
|
26
|
+
|
27
|
+
__all__ = ["fig_minus", "gif", "add_cartopy", "add_gridlines", "MidpointNormalize", "add_lonlat_unit", "contour", "contourf", "quiver"]
|
28
|
+
|
29
|
+
warnings.filterwarnings("ignore")
|
30
|
+
|
31
|
+
|
32
|
+
def fig_minus(ax_x=None, ax_y=None, cbar=None, decimal=None, add_space=False):
|
33
|
+
"""
|
34
|
+
Description: 将坐标轴刻度中的负号替换为减号
|
35
|
+
|
36
|
+
param {*} ax_x : x轴
|
37
|
+
param {*} ax_y : y轴
|
38
|
+
param {*} cbar : colorbar
|
39
|
+
param {*} decimal : 小数位数
|
40
|
+
param {*} add_space : 是否在非负数前面加空格
|
41
|
+
|
42
|
+
return {*} ax_x or ax_y or cbar
|
43
|
+
"""
|
44
|
+
if ax_x is not None:
|
45
|
+
current_ticks = ax_x.get_xticks()
|
46
|
+
if ax_y is not None:
|
47
|
+
current_ticks = ax_y.get_yticks()
|
48
|
+
if cbar is not None:
|
49
|
+
current_ticks = cbar.get_ticks()
|
50
|
+
# 先判断是否需要加空格,如果要,先获取需要加的索引
|
51
|
+
if add_space:
|
52
|
+
index = 0
|
53
|
+
for _, tick in enumerate(current_ticks):
|
54
|
+
if tick >= 0:
|
55
|
+
index = _
|
56
|
+
break
|
57
|
+
if decimal is not None:
|
58
|
+
# my_ticks = [(round(float(iii), decimal)) for iii in my_ticks]
|
59
|
+
current_ticks = [f"{val:.{decimal}f}" if val != 0 else "0" for val in current_ticks]
|
60
|
+
|
61
|
+
out_ticks = [f"{val}".replace("-", "\u2212") for val in current_ticks]
|
62
|
+
if add_space:
|
63
|
+
# 在非负数前面加两个空格
|
64
|
+
out_ticks[index:] = [" " + m for m in out_ticks[index:]]
|
65
|
+
|
66
|
+
if ax_x is not None:
|
67
|
+
ax_x.set_xticklabels(out_ticks)
|
68
|
+
return ax_x
|
69
|
+
if ax_y is not None:
|
70
|
+
ax_y.set_yticklabels(out_ticks)
|
71
|
+
return ax_y
|
72
|
+
if cbar is not None:
|
73
|
+
cbar.set_ticklabels(out_ticks)
|
74
|
+
return cbar
|
75
|
+
|
76
|
+
|
77
|
+
# ** 将生成图片/已有图片制作成动图
|
78
|
+
def gif(image_list: list, gif_name: str, duration=0.2): # 制作动图,默认间隔0.2
|
79
|
+
"""
|
80
|
+
Description
|
81
|
+
Make gif from images
|
82
|
+
Parameters
|
83
|
+
image_list : list, list of images
|
84
|
+
gif_name : str, name of gif
|
85
|
+
duration : float, duration of each frame
|
86
|
+
Returns
|
87
|
+
None
|
88
|
+
Example
|
89
|
+
gif(["1.png", "2.png"], "test.gif", duration=0.2)
|
90
|
+
"""
|
91
|
+
import imageio.v2 as imageio
|
92
|
+
|
93
|
+
frames = []
|
94
|
+
for image_name in image_list:
|
95
|
+
frames.append(imageio.imread(image_name))
|
96
|
+
imageio.mimsave(gif_name, frames, format="GIF", duration=duration)
|
97
|
+
print("Gif制作完成!")
|
98
|
+
return
|
99
|
+
|
100
|
+
|
101
|
+
# ** 转化经/纬度刻度
|
102
|
+
def add_lonlat_unit(lon=None, lat=None, decimal=2):
|
103
|
+
"""
|
104
|
+
param {*} lon : 经度列表
|
105
|
+
param {*} lat : 纬度列表
|
106
|
+
param {*} decimal : 小数位数
|
107
|
+
return {*} 转化后的经/纬度列表
|
108
|
+
example : add_lonlat_unit(lon=lon, lat=lat, decimal=2)
|
109
|
+
"""
|
110
|
+
|
111
|
+
def _format_longitude(x_list):
|
112
|
+
out_list = []
|
113
|
+
for x in x_list:
|
114
|
+
if x > 180:
|
115
|
+
x -= 360
|
116
|
+
# degrees = int(abs(x))
|
117
|
+
degrees = round(abs(x), decimal)
|
118
|
+
direction = "E" if x >= 0 else "W"
|
119
|
+
out_list.append(f"{degrees:.{decimal}f}°{direction}" if x != 0 and x != 180 else f"{degrees}°")
|
120
|
+
return out_list if len(out_list) > 1 else out_list[0]
|
121
|
+
|
122
|
+
def _format_latitude(y_list):
|
123
|
+
out_list = []
|
124
|
+
for y in y_list:
|
125
|
+
if y > 90:
|
126
|
+
y -= 180
|
127
|
+
# degrees = int(abs(y))
|
128
|
+
degrees = round(abs(y), decimal)
|
129
|
+
direction = "N" if y >= 0 else "S"
|
130
|
+
out_list.append(f"{degrees:.{decimal}f}°{direction}" if y != 0 else f"{degrees}°")
|
131
|
+
return out_list if len(out_list) > 1 else out_list[0]
|
132
|
+
|
133
|
+
if lon and lat:
|
134
|
+
return _format_longitude(lon), _format_latitude(lat)
|
135
|
+
elif lon:
|
136
|
+
return _format_longitude(lon)
|
137
|
+
elif lat:
|
138
|
+
return _format_latitude(lat)
|
139
|
+
|
140
|
+
|
141
|
+
# ** 添加网格线
|
142
|
+
def add_gridlines(ax, projection=ccrs.PlateCarree(), color="k", alpha=0.5, linestyle="--", linewidth=0.5):
|
143
|
+
# add gridlines
|
144
|
+
gl = ax.gridlines(crs=projection, draw_labels=True, linewidth=linewidth, color=color, alpha=alpha, linestyle=linestyle)
|
145
|
+
gl.right_labels = False
|
146
|
+
gl.top_labels = False
|
147
|
+
gl.xformatter = LongitudeFormatter(zero_direction_label=False)
|
148
|
+
gl.yformatter = LatitudeFormatter()
|
149
|
+
|
150
|
+
return ax, gl
|
151
|
+
|
152
|
+
|
153
|
+
# ** 添加地图
|
154
|
+
def add_cartopy(ax, lon=None, lat=None, projection=ccrs.PlateCarree(), gridlines=True, landcolor="lightgrey", oceancolor="lightblue", cartopy_linewidth=0.5):
|
155
|
+
# add coastlines
|
156
|
+
ax.add_feature(cfeature.LAND, facecolor=landcolor)
|
157
|
+
ax.add_feature(cfeature.OCEAN, facecolor=oceancolor)
|
158
|
+
ax.add_feature(cfeature.COASTLINE, linewidth=cartopy_linewidth)
|
159
|
+
# ax.add_feature(cfeature.BORDERS, linewidth=cartopy_linewidth, linestyle=":")
|
160
|
+
|
161
|
+
# add gridlines
|
162
|
+
if gridlines:
|
163
|
+
ax, gl = add_gridlines(ax, projection)
|
164
|
+
|
165
|
+
# set longitude and latitude format
|
166
|
+
lon_formatter = LongitudeFormatter(zero_direction_label=False)
|
167
|
+
lat_formatter = LatitudeFormatter()
|
168
|
+
ax.xaxis.set_major_formatter(lon_formatter)
|
169
|
+
ax.yaxis.set_major_formatter(lat_formatter)
|
170
|
+
|
171
|
+
# set extent
|
172
|
+
if lon is not None and lat is not None:
|
173
|
+
lon_min, lon_max = lon.min(), lon.max()
|
174
|
+
lat_min, lat_max = lat.min(), lat.max()
|
175
|
+
ax.set_extent([lon_min, lon_max, lat_min, lat_max], crs=projection)
|
176
|
+
|
177
|
+
|
178
|
+
# ** 自定义归一化类,使得0值处为中心点
|
179
|
+
class MidpointNormalize(mpl.colors.Normalize):
|
180
|
+
"""
|
181
|
+
Description: 自定义归一化类,使得0值处为中心点
|
182
|
+
|
183
|
+
param {*} mpl.colors.Normalize : 继承Normalize类
|
184
|
+
return {*}
|
185
|
+
|
186
|
+
Example:
|
187
|
+
nrom = MidpointNormalize(vmin=-2, vmax=1, vcenter=0)
|
188
|
+
"""
|
189
|
+
|
190
|
+
def __init__(self, vmin=None, vmax=None, vcenter=None, clip=False):
|
191
|
+
self.vcenter = vcenter
|
192
|
+
super().__init__(vmin, vmax, clip)
|
193
|
+
|
194
|
+
def __call__(self, value, clip=None):
|
195
|
+
x, y = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1.0]
|
196
|
+
return np.ma.masked_array(np.interp(value, x, y, left=-np.inf, right=np.inf))
|
197
|
+
|
198
|
+
def inverse(self, value):
|
199
|
+
y, x = [self.vmin, self.vcenter, self.vmax], [0, 0.5, 1]
|
200
|
+
return np.interp(value, x, y, left=-np.inf, right=np.inf)
|
201
|
+
|
202
|
+
|
203
|
+
# -----------------------------------------------------------------------------------------------------------------------------------------------------------------
|
204
|
+
|
205
|
+
# ** 绘制填色图
|
206
|
+
def contourf(data,x=None,y=None,cmap='coolwarm',show=True,store=None,cartopy=False):
|
207
|
+
"""
|
208
|
+
Description: 绘制填色图
|
209
|
+
|
210
|
+
param {*} data : 二维数据
|
211
|
+
param {*} x : x轴坐标
|
212
|
+
param {*} y : y轴坐标
|
213
|
+
param {*} cmap : 颜色映射
|
214
|
+
param {*} show : 是否显示
|
215
|
+
param {*} store : 是否保存
|
216
|
+
param {*} cartopy : 是否使用cartopy
|
217
|
+
|
218
|
+
return {*}
|
219
|
+
"""
|
220
|
+
data = np.array(data)
|
221
|
+
if x is None or y is None:
|
222
|
+
x = np.arange(data.shape[1])
|
223
|
+
y = np.arange(data.shape[0])
|
224
|
+
if cartopy:
|
225
|
+
fig, ax = plt.subplots(subplot_kw={'projection': ccrs.PlateCarree()})
|
226
|
+
add_cartopy(ax, lon=x, lat=y)
|
227
|
+
ax.contourf(x, y, data, transform=ccrs.PlateCarree(), cmap=cmap)
|
228
|
+
else:
|
229
|
+
plt.contourf(x, y, data, cmap=cmap)
|
230
|
+
plt.colorbar()
|
231
|
+
plt.savefig(store, dpi=600, bbox_inches="tight") if store else plt.show()
|
232
|
+
plt.close()
|
233
|
+
|
234
|
+
|
235
|
+
# ** 绘制等值线图
|
236
|
+
def contour(data, x=None, y=None, cmap="coolwarm", show=True, store=None, cartopy=False):
|
237
|
+
"""
|
238
|
+
Description: 绘制等值线图
|
239
|
+
|
240
|
+
param {*} data : 二维数据
|
241
|
+
param {*} x : x轴坐标
|
242
|
+
param {*} y : y轴坐标
|
243
|
+
param {*} cmap : 颜色映射
|
244
|
+
param {*} show : 是否显示
|
245
|
+
param {*} store : 是否保存
|
246
|
+
param {*} cartopy : 是否使用cartopy
|
247
|
+
|
248
|
+
return {*}
|
249
|
+
"""
|
250
|
+
data = np.array(data)
|
251
|
+
if x is None or y is None:
|
252
|
+
x = np.arange(data.shape[1])
|
253
|
+
y = np.arange(data.shape[0])
|
254
|
+
if cartopy:
|
255
|
+
fig, ax = plt.subplots(subplot_kw={"projection": ccrs.PlateCarree()})
|
256
|
+
add_cartopy(ax, lon=x, lat=y)
|
257
|
+
cr = ax.contour(x, y, data, transform=ccrs.PlateCarree(), cmap=cmap)
|
258
|
+
else:
|
259
|
+
cr = plt.contour(x, y, data, cmap=cmap)
|
260
|
+
plt.clabel(cr, inline=True, fontsize=10)
|
261
|
+
plt.savefig(store, dpi=600, bbox_inches="tight") if store else plt.show()
|
262
|
+
plt.close()
|
263
|
+
|
264
|
+
|
265
|
+
# ** 绘制矢量场
|
266
|
+
def quiver(u, v, lon, lat, picname=None, cmap="coolwarm", scale=0.25, width=0.002, x_space=5, y_space=5):
|
267
|
+
"""
|
268
|
+
param {*} u : 二维数据
|
269
|
+
param {*} v : 二维数据
|
270
|
+
param {*} lon : 经度, 1D or 2D
|
271
|
+
param {*} lat : 纬度, 1D or 2D
|
272
|
+
param {*} picname : 图片保存的文件名(含路径)
|
273
|
+
param {*} cmap : 颜色映射,默认coolwarm
|
274
|
+
param {*} scale : 箭头的大小 / 缩小程度
|
275
|
+
param {*} width : 箭头的宽度
|
276
|
+
param {*} x_space : x轴间隔
|
277
|
+
param {*} y_space : y轴间隔
|
278
|
+
return {*} 无返回值
|
279
|
+
"""
|
280
|
+
# 创建新的网格位置变量(lat_c, lon_c)
|
281
|
+
if len(lon.shape) == 1 and len(lat.shape) == 1:
|
282
|
+
lon_c, lat_c = np.meshgrid(lon, lat)
|
283
|
+
else:
|
284
|
+
lon_c, lat_c = lon, lat
|
285
|
+
|
286
|
+
# 设置箭头的比例、颜色、宽度等参数
|
287
|
+
# scale = 0.25 # 箭头的大小 / 缩小程度
|
288
|
+
# color = '#E5D1FA'
|
289
|
+
# width = 0.002 # 箭头的宽度
|
290
|
+
# x_space = 1
|
291
|
+
# y_space = 1
|
292
|
+
|
293
|
+
# 计算矢量的大小
|
294
|
+
S = xr.DataArray(np.hypot(np.array(u), np.array(v)))
|
295
|
+
|
296
|
+
mean_S = S.nanmean()
|
297
|
+
|
298
|
+
# 使用 plt.quiver 函数绘制矢量图
|
299
|
+
# 通过设置 quiver 函数的 pivot 参数来指定箭头的位置
|
300
|
+
quiver_plot = plt.quiver(
|
301
|
+
lon_c[::y_space, ::x_space],
|
302
|
+
lat_c[::y_space, ::x_space],
|
303
|
+
u[::y_space, ::x_space],
|
304
|
+
v[::y_space, ::x_space],
|
305
|
+
S[::y_space, ::x_space], # 矢量的大小,可以不要
|
306
|
+
pivot="middle",
|
307
|
+
scale=scale,
|
308
|
+
# color=color, # 矢量的颜色,单色
|
309
|
+
cmap=cmap, # 矢量的颜色,多色
|
310
|
+
width=width,
|
311
|
+
)
|
312
|
+
# plt.quiverkey(quiver_plot, X=0.90, Y=0.975, U=1, label='1 m/s', labelpos='E', fontproperties={'size': 10})
|
313
|
+
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})
|
314
|
+
plt.colorbar(quiver_plot)
|
315
|
+
plt.xlabel("X")
|
316
|
+
plt.ylabel("Y")
|
317
|
+
|
318
|
+
plt.savefig(picname, bbox_inches="tight") if picname is not None else plt.show()
|
319
|
+
plt.clf()
|
320
|
+
plt.close()
|
321
|
+
|
322
|
+
|
323
|
+
|
324
|
+
if __name__ == "__main__":
|
325
|
+
pass
|