oafuncs 0.0.97.16__py3-none-any.whl → 0.0.97.17__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.
@@ -42,8 +42,13 @@ def _modify_var(nc_file_path, variable_name, new_value):
42
42
  raise ValueError(f"File '{nc_file_path}' is not a valid NetCDF file.")
43
43
  if not variable_name:
44
44
  raise ValueError("Variable name cannot be empty or None.")
45
+
46
+ # 自动尝试将 new_value 转换为 numpy.ndarray
45
47
  if not isinstance(new_value, np.ndarray):
46
- raise TypeError("New value must be a numpy.ndarray.")
48
+ try:
49
+ new_value = np.array(new_value)
50
+ except Exception:
51
+ raise TypeError("New value must be a numpy.ndarray or convertible to numpy.ndarray.")
47
52
 
48
53
  try:
49
54
  with nc.Dataset(nc_file_path, "r+") as dataset:
@@ -51,7 +56,10 @@ def _modify_var(nc_file_path, variable_name, new_value):
51
56
  raise ValueError(f"Variable '{variable_name}' not found in the NetCDF file.")
52
57
  variable = dataset.variables[variable_name]
53
58
  if variable.shape != new_value.shape:
54
- raise ValueError(f"Shape mismatch: Variable '{variable_name}' has shape {variable.shape}, but new value has shape {new_value.shape}.")
59
+ try:
60
+ new_value = new_value.reshape(variable.shape)
61
+ except ValueError:
62
+ raise ValueError(f"Shape mismatch: Variable '{variable_name}' has shape {variable.shape}, but new value has shape {new_value.shape}. Reshaping failed.")
55
63
  variable[:] = new_value
56
64
  print(f"[green]Successfully modified variable '{variable_name}' in '{nc_file_path}'.[/green]")
57
65
  return True
oafuncs/oa_cmap.py CHANGED
@@ -3,160 +3,276 @@ from typing import List, Optional, Union
3
3
  import matplotlib as mpl
4
4
  import matplotlib.pyplot as plt
5
5
  import numpy as np
6
- from rich import print
7
6
 
8
7
  __all__ = ["show", "to_color", "create", "get"]
9
8
 
10
9
 
11
10
  # ** 将cmap用填色图可视化(官网摘抄函数)
12
11
  def show(colormaps: Union[str, mpl.colors.Colormap, List[Union[str, mpl.colors.Colormap]]]) -> None:
12
+ """Helper function to plot data with associated colormap.
13
+
14
+ This function creates a visualization of one or more colormaps by applying them
15
+ to randomly generated data in a pcolormesh plot.
16
+
17
+ Parameters
18
+ ----------
19
+ colormaps : Union[str, mpl.colors.Colormap, List[Union[str, mpl.colors.Colormap]]]
20
+ List of colormaps, or a single colormap; can be a string name or a colormap object.
21
+
22
+ Returns
23
+ -------
24
+ None
25
+ This function displays the plot but does not return any value.
26
+
27
+ Examples
28
+ --------
29
+ >>> cmap = matplotlib.colors.ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])
30
+ >>> show([cmap])
31
+ >>> show("viridis")
32
+ >>> show(["viridis", "cividis"])
13
33
  """
14
- Description:
15
- Helper function to plot data with associated colormap.
16
- Parameters:
17
- colormaps : list of colormaps, or a single colormap; can be a string or a colormap object.
18
- Example:
19
- cmap = ListedColormap(["darkorange", "gold", "lawngreen", "lightseagreen"])
20
- show([cmap]); show("viridis"); show(["viridis", "cividis"])
21
- """
34
+ # Convert single colormap to list for uniform processing
22
35
  if not isinstance(colormaps, list):
23
36
  colormaps = [colormaps]
37
+
38
+ # Create a formatted list of colormap names for display
39
+ cmap_names = []
40
+ for cmap in colormaps:
41
+ if isinstance(cmap, str):
42
+ cmap_names.append(cmap)
43
+ elif hasattr(cmap, "name"):
44
+ cmap_names.append(cmap.name)
45
+ else:
46
+ cmap_names.append("unnamed_colormap")
47
+
48
+ print(f"Visualizing {len(colormaps)} colormap(s): {', '.join(cmap_names)}")
49
+
50
+ # Generate random data with fixed seed for reproducibility
24
51
  np.random.seed(19680801)
25
52
  data = np.random.randn(30, 30)
53
+
54
+ # Create subplots based on number of colormaps
26
55
  n = len(colormaps)
27
56
  fig, axs = plt.subplots(1, n, figsize=(n * 2 + 2, 3), constrained_layout=True, squeeze=False)
57
+
58
+ # Plot each colormap
28
59
  for ax, cmap in zip(axs.flat, colormaps):
29
60
  psm = ax.pcolormesh(data, cmap=cmap, rasterized=True, vmin=-4, vmax=4)
30
61
  fig.colorbar(psm, ax=ax)
62
+
63
+ # Set title if colormap has a name
64
+ if isinstance(cmap, str):
65
+ ax.set_title(cmap)
66
+ elif hasattr(cmap, "name") and cmap.name:
67
+ ax.set_title(cmap.name)
68
+
69
+ print("Displaying colormap visualization...")
31
70
  plt.show()
32
71
 
33
72
 
34
73
  # ** 将cmap转为list,即多个颜色的列表
35
- def to_color(cmap_name: str, n: int = 256) -> List[tuple]:
36
- """
37
- Description:
38
- Convert a colormap to a list of colors
39
- Parameters:
40
- cmap_name : str; the name of the colormap
41
- n : int, optional; the number of colors
42
- Return:
43
- out_colors : list of colors
44
- Example:
45
- out_colors = to_color('viridis', 256)
74
+ def to_color(colormap_name: str, num_colors: int = 256) -> List[tuple]:
75
+ """Convert a colormap to a list of colors.
76
+
77
+ Args:
78
+ colormap_name (str): The name of the colormap.
79
+ num_colors (int, optional): The number of colors. Defaults to 256.
80
+
81
+ Returns:
82
+ List[tuple]: List of RGBA colors.
83
+
84
+ Raises:
85
+ ValueError: If the colormap name is not recognized.
86
+
87
+ Examples:
88
+ >>> out_colors = to_color('viridis', 256)
46
89
  """
47
- cmap = mpl.colormaps.get_cmap(cmap_name)
48
- return [cmap(i) for i in np.linspace(0, 1, n)]
90
+ try:
91
+ cmap = mpl.colormaps.get_cmap(colormap_name)
92
+ return [cmap(i) for i in np.linspace(0, 1, num_colors)]
93
+ except (ValueError, TypeError):
94
+ error_msg = f"Invalid colormap name: {colormap_name}"
95
+ print(error_msg)
96
+ raise ValueError(error_msg)
49
97
 
50
98
 
51
99
  # ** 自制cmap,多色,可带位置
52
- def create(color_list: Optional[List[Union[str, tuple]]] = None, rgb_file_path: Optional[str] = None, positions: Optional[List[float]] = None, under_color: Optional[Union[str, tuple]] = None, over_color: Optional[Union[str, tuple]] = None, delimiter: str = ",") -> mpl.colors.Colormap:
53
- """
54
- Description:
55
- Create a custom colormap from a list of colors or an RGB txt document.
56
- Parameters:
57
- color_list : list of colors (optional, required if rgb_file_path is None)
58
- rgb_file_path : str, the path of txt file (optional, required if color_list is None)
59
- positions : list of positions (optional, for color_list)
60
- under_color : color (optional)
61
- over_color : color (optional)
62
- delimiter : str, optional, default is ','; the delimiter of RGB values in txt file
63
- Return:
64
- cmap : colormap
65
- Example:
66
- cmap = create(color_list=['#C2B7F3','#B3BBF2','#B0CBF1','#ACDCF0','#A8EEED'])
67
- cmap = create(color_list=['aliceblue','skyblue','deepskyblue'], positions=[0.0,0.5,1.0])
68
- cmap = create(rgb_file_path='path/to/file.txt', delimiter=',')
100
+ def create(color_list: Optional[List[Union[str, tuple]]] = None, rgb_file: Optional[str] = None, color_positions: Optional[List[float]] = None, below_range_color: Optional[Union[str, tuple]] = None, above_range_color: Optional[Union[str, tuple]] = None, value_delimiter: str = ",") -> mpl.colors.Colormap:
101
+ """Create a custom colormap from a list of colors or an RGB txt document.
102
+
103
+ Args:
104
+ color_list (Optional[List[Union[str, tuple]]]): List of colors. Required if rgb_file is None.
105
+ rgb_file (Optional[str]): The path of txt file. Required if color_list is None.
106
+ color_positions (Optional[List[float]]): List of positions for color_list. Must have same length as color_list.
107
+ below_range_color (Optional[Union[str, tuple]]): Color for values below the colormap range.
108
+ above_range_color (Optional[Union[str, tuple]]): Color for values above the colormap range.
109
+ value_delimiter (str, optional): The delimiter of RGB values in txt file. Defaults to ",".
110
+
111
+ Returns:
112
+ mpl.colors.Colormap: Created colormap.
113
+
114
+ Raises:
115
+ ValueError: If neither color_list nor rgb_file is provided.
116
+ ValueError: If color_positions is provided but has different length than color_list.
117
+ FileNotFoundError: If rgb_file does not exist.
118
+ ValueError: If the RGB file format is invalid.
119
+
120
+ Examples:
121
+ >>> cmap = create(color_list=['#C2B7F3','#B3BBF2','#B0CBF1','#ACDCF0','#A8EEED'])
122
+ >>> cmap = create(color_list=['aliceblue','skyblue','deepskyblue'], color_positions=[0.0,0.5,1.0])
123
+ >>> cmap = create(rgb_file='path/to/file.txt', value_delimiter=',')
69
124
  """
70
- if rgb_file_path:
71
- with open(rgb_file_path) as fid:
72
- data = fid.readlines()
73
- n = len(data)
74
- rgb = np.zeros((n, 3))
75
- for i in np.arange(n):
76
- rgb[i][0] = data[i].split(delimiter)[0]
77
- rgb[i][1] = data[i].split(delimiter)[1]
78
- rgb[i][2] = data[i].split(delimiter)[2]
79
- max_rgb = np.max(rgb)
80
- if max_rgb > 2: # if the value is greater than 2, it is normalized to 0-1
81
- rgb = rgb / 255.0
82
- cmap_color = mpl.colors.ListedColormap(rgb, name="my_color")
83
- elif color_list:
84
- if positions is None: # 自动分配比例
85
- cmap_color = mpl.colors.LinearSegmentedColormap.from_list("mycmap", color_list)
86
- else: # 按提供比例分配
87
- cmap_color = mpl.colors.LinearSegmentedColormap.from_list("mycmap", list(zip(positions, color_list)))
125
+ # Input validation
126
+ if rgb_file is None and color_list is None:
127
+ error_msg = "Either 'color_list' or 'rgb_file' must be provided."
128
+ print(error_msg)
129
+ raise ValueError(error_msg)
130
+
131
+ if color_positions is not None and color_list is not None:
132
+ if len(color_positions) != len(color_list):
133
+ error_msg = f"'color_positions' must have the same length as 'color_list' (positions: {len(color_positions)}, colors: {len(color_list)})"
134
+ print(error_msg)
135
+ raise ValueError(error_msg)
136
+ if not all(0 <= pos <= 1 for pos in color_positions):
137
+ error_msg = "All position values must be between 0 and 1"
138
+ print(error_msg)
139
+ raise ValueError(error_msg)
140
+ if color_positions != sorted(color_positions):
141
+ error_msg = f"Position values must be in ascending order: {color_positions}"
142
+ print(error_msg)
143
+ raise ValueError(error_msg)
144
+
145
+ if rgb_file:
146
+ try:
147
+ print(f"Reading RGB data from {rgb_file}...")
148
+
149
+ with open(rgb_file) as fid:
150
+ data = [line.strip() for line in fid if line.strip() and not line.strip().startswith("#")]
151
+
152
+ if not data:
153
+ error_msg = f"RGB file is empty or contains only comments: {rgb_file}"
154
+ print(error_msg)
155
+ raise ValueError(error_msg)
156
+
157
+ n = len(data)
158
+ rgb = np.zeros((n, 3))
159
+
160
+ for i in np.arange(n):
161
+ try:
162
+ parts = data[i].split(value_delimiter)
163
+ if len(parts) < 3:
164
+ error_msg = f"Line {i + 1}: Expected at least 3 values, got {len(parts)}"
165
+ print(error_msg)
166
+ raise ValueError(error_msg)
167
+
168
+ rgb[i][0] = float(parts[0])
169
+ rgb[i][1] = float(parts[1])
170
+ rgb[i][2] = float(parts[2])
171
+ except (ValueError, IndexError) as e:
172
+ error_msg = f"Error parsing RGB values at line {i + 1}: {e}"
173
+ print(error_msg)
174
+ raise ValueError(error_msg)
175
+
176
+ max_rgb = np.max(rgb)
177
+ # Normalize RGB values if they are in 0-255 range
178
+ if max_rgb > 2:
179
+ rgb = rgb / 255.0
180
+ cmap_color = mpl.colors.ListedColormap(rgb, name="my_color")
181
+ print(f"Successfully created colormap from {rgb_file}")
182
+ except FileNotFoundError:
183
+ error_msg = f"RGB file not found: {rgb_file}"
184
+ print(error_msg)
185
+ raise FileNotFoundError(error_msg)
88
186
  else:
89
- raise ValueError("Either 'color_list' or 'rgb_file_path' must be provided.")
187
+ # Create colormap from color list
188
+ if color_positions is None:
189
+ cmap_color = mpl.colors.LinearSegmentedColormap.from_list("mycmap", color_list)
190
+ else:
191
+ cmap_color = mpl.colors.LinearSegmentedColormap.from_list("mycmap", list(zip(color_positions, color_list)))
192
+ print(f"Successfully created colormap from {len(color_list)} colors")
193
+
194
+ # Set below/above range colors if provided
195
+ if below_range_color is not None:
196
+ cmap_color.set_under(below_range_color)
197
+ print(f"Set below-range color to {below_range_color}")
198
+ if above_range_color is not None:
199
+ cmap_color.set_over(above_range_color)
200
+ print(f"Set above-range color to {above_range_color}")
90
201
 
91
- if under_color is not None:
92
- cmap_color.set_under(under_color)
93
- if over_color is not None:
94
- cmap_color.set_over(over_color)
95
202
  return cmap_color
96
203
 
97
204
 
98
205
  # ** 选择cmap
99
- def get(cmap_name: Optional[str] = None, query: bool = False) -> Optional[mpl.colors.Colormap]:
100
- """
101
- Description:
102
- Choosing a colormap from the list of available colormaps or a custom colormap
103
- Parameters:
104
- cmap_name : str, optional; the name of the colormap
105
- query : bool, optional; whether to query the available colormap names
106
- Return:
107
- cmap : colormap
108
- Example:
109
- cmap = get('viridis')
110
- cmap = get('diverging_1')
111
- cmap = get('cool_1')
112
- cmap = get('warm_1')
113
- cmap = get('colorful_1')
206
+ def get(colormap_name: Optional[str] = None, show_available: bool = False) -> Optional[mpl.colors.Colormap]:
207
+ """Choose a colormap from the list of available colormaps or a custom colormap.
208
+
209
+ Args:
210
+ colormap_name (Optional[str], optional): The name of the colormap. Defaults to None.
211
+ show_available (bool, optional): Whether to query the available colormap names. Defaults to False.
212
+
213
+ Returns:
214
+ Optional[mpl.colors.Colormap]: Selected colormap or None if show_available is True or colormap_name is None.
215
+
216
+ Examples:
217
+ >>> cmap = get('viridis')
218
+ >>> cmap = get('diverging_1')
219
+ >>> cmap = get('cool_1')
220
+ >>> cmap = get('warm_1')
221
+ >>> cmap = get('colorful_1')
114
222
  """
115
223
  my_cmap_dict = {
116
- "diverging_1": create(["#4e00b3", "#0000FF", "#00c0ff", "#a1d3ff", "#DCDCDC", "#FFD39B", "#FF8247", "#FF0000", "#FF5F9E"]),
117
- "cool_1": create(["#4e00b3", "#0000FF", "#00c0ff", "#a1d3ff", "#DCDCDC"]),
118
- "warm_1": create(["#DCDCDC", "#FFD39B", "#FF8247", "#FF0000", "#FF5F9E"]),
119
- "colorful_1": create(["#6d00db", "#9800cb", "#F2003C", "#ff4500", "#ff7f00", "#FE28A2", "#FFC0CB", "#DDA0DD", "#40E0D0", "#1a66f2", "#00f7fb", "#8fff88", "#E3FF00"]),
224
+ "diverging_1": ["#4e00b3", "#0000FF", "#00c0ff", "#a1d3ff", "#DCDCDC", "#FFD39B", "#FF8247", "#FF0000", "#FF5F9E"],
225
+ "cool_1": ["#4e00b3", "#0000FF", "#00c0ff", "#a1d3ff", "#DCDCDC"],
226
+ "warm_1": ["#DCDCDC", "#FFD39B", "#FF8247", "#FF0000", "#FF5F9E"],
227
+ "colorful_1": ["#6d00db", "#9800cb", "#F2003C", "#ff4500", "#ff7f00", "#FE28A2", "#FFC0CB", "#DDA0DD", "#40E0D0", "#1a66f2", "#00f7fb", "#8fff88", "#E3FF00"],
120
228
  }
121
229
 
122
- if query:
230
+ if show_available:
123
231
  print("Available cmap names:")
124
232
  print("-" * 20)
125
233
  print("Defined by myself:")
126
- print("\n".join(my_cmap_dict.keys()))
234
+ for name in my_cmap_dict.keys():
235
+ print(f" • {name}")
127
236
  print("-" * 20)
128
237
  print("Matplotlib built-in:")
129
- print("\n".join(mpl.colormaps.keys()))
238
+ # 将Matplotlib内置cmap分批次打印,每行5个
239
+ built_in_cmaps = list(mpl.colormaps.keys())
240
+ for i in range(0, len(built_in_cmaps), 5):
241
+ print(" • " + ", ".join(built_in_cmaps[i : i + 5]))
130
242
  print("-" * 20)
131
243
  return None
132
244
 
133
- if cmap_name is None:
245
+ if colormap_name is None:
134
246
  return None
135
247
 
136
- if cmap_name in my_cmap_dict:
137
- return my_cmap_dict[cmap_name]
248
+ if colormap_name in my_cmap_dict:
249
+ print(f"Using custom colormap: {colormap_name}")
250
+ return create(my_cmap_dict[colormap_name])
138
251
  else:
139
252
  try:
140
- return mpl.colormaps.get_cmap(cmap_name)
253
+ cmap = mpl.colormaps.get_cmap(colormap_name)
254
+ print(f"Using matplotlib colormap: {colormap_name}")
255
+ return cmap
141
256
  except ValueError:
142
- print(f"Unknown cmap name: {cmap_name}\nNow return 'rainbow' as default.")
257
+ print(f"Warning: Unknown cmap name: {colormap_name}")
258
+ print("Using rainbow as default.")
143
259
  return mpl.colormaps.get_cmap("rainbow") # 默认返回 'rainbow'
144
260
 
145
261
 
146
262
  if __name__ == "__main__":
147
263
  # ** 测试自制cmap
148
264
  colors = ["#C2B7F3", "#B3BBF2", "#B0CBF1", "#ACDCF0", "#A8EEED"]
149
- nodes = [0.0, 0.2, 0.4, 0.6, 1.0]
150
- c_map = create(colors, nodes)
151
- show([c_map])
265
+ color_nodes = [0.0, 0.2, 0.4, 0.6, 1.0]
266
+ custom_cmap = create(colors, color_nodes)
267
+ show([custom_cmap])
152
268
 
153
269
  # ** 测试自制diverging型cmap
154
270
  diverging_cmap = create(["#4e00b3", "#0000FF", "#00c0ff", "#a1d3ff", "#DCDCDC", "#FFD39B", "#FF8247", "#FF0000", "#FF5F9E"])
155
271
  show([diverging_cmap])
156
272
 
157
273
  # ** 测试根据RGB的txt文档制作色卡
158
- file_path = "E:/python/colorbar/test.txt"
159
- cmap_rgb = create(rgb_file_path=file_path)
274
+ rgb_file_path = "E:/python/colorbar/test.txt"
275
+ cmap_from_rgb = create(rgb_file=rgb_file_path)
160
276
 
161
277
  # ** 测试将cmap转为list
162
- out_colors = to_color("viridis", 256)
278
+ viridis_colors = to_color("viridis", 256)