oafuncs 0.0.98.41__tar.gz → 0.0.98.43__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.
Files changed (50) hide show
  1. {oafuncs-0.0.98.41/oafuncs.egg-info → oafuncs-0.0.98.43}/PKG-INFO +1 -1
  2. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/netcdf_write.py +0 -69
  3. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/plot_dataset.py +8 -3
  4. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_data.py +80 -2
  5. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_nc.py +6 -3
  6. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43/oafuncs.egg-info}/PKG-INFO +1 -1
  7. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/setup.py +1 -1
  8. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/LICENSE.txt +0 -0
  9. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/MANIFEST.in +0 -0
  10. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/README.md +0 -0
  11. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/__init__.py +0 -0
  12. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_data/hycom.png +0 -0
  13. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_data/oafuncs.png +0 -0
  14. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/cprogressbar.py +0 -0
  15. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/data_interp.py +0 -0
  16. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/email.py +0 -0
  17. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/netcdf_merge.py +0 -0
  18. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/netcdf_modify.py +0 -0
  19. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/parallel.py +0 -0
  20. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/parallel_bak.py +0 -0
  21. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/_script/replace_file_content.py +0 -0
  22. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_cmap.py +0 -0
  23. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_date.py +0 -0
  24. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_down/User_Agent-list.txt +0 -0
  25. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_down/__init__.py +0 -0
  26. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_down/hycom_3hourly.py +0 -0
  27. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_down/idm.py +0 -0
  28. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_down/literature.py +0 -0
  29. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_down/read_proxy.py +0 -0
  30. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_down/test_ua.py +0 -0
  31. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_down/user_agent.py +0 -0
  32. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_draw.py +0 -0
  33. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_file.py +0 -0
  34. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_help.py +0 -0
  35. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_model/__init__.py +0 -0
  36. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_model/roms/__init__.py +0 -0
  37. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_model/roms/test.py +0 -0
  38. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_model/wrf/__init__.py +0 -0
  39. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_model/wrf/little_r.py +0 -0
  40. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_python.py +0 -0
  41. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_sign/__init__.py +0 -0
  42. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_sign/meteorological.py +0 -0
  43. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_sign/ocean.py +0 -0
  44. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_sign/scientific.py +0 -0
  45. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs/oa_tool.py +0 -0
  46. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs.egg-info/SOURCES.txt +0 -0
  47. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs.egg-info/dependency_links.txt +0 -0
  48. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs.egg-info/requires.txt +0 -0
  49. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/oafuncs.egg-info/top_level.txt +0 -0
  50. {oafuncs-0.0.98.41 → oafuncs-0.0.98.43}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.41
3
+ Version: 0.0.98.43
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -374,75 +374,6 @@ def save_to_nc(file, data, varname=None, coords=None, mode="w", convert_dtype='i
374
374
  raise RuntimeError(f"netCDF4 保存失败: {str(e)}") from e
375
375
 
376
376
 
377
- def _compress_netcdf(src_path, dst_path=None, tolerance=1e-10, preserve_mask_values=True):
378
- """
379
- 压缩 NetCDF 文件,使用 scale_factor/add_offset 压缩数据。
380
- 若 dst_path 省略,则自动生成新文件名,写出后删除原文件并将新文件改回原名。
381
- 压缩后验证数据是否失真。
382
-
383
- 参数:
384
- - src_path: 原始 NetCDF 文件路径
385
- - dst_path: 压缩后的文件路径(可选)
386
- - tolerance: 数据验证的允许误差范围(默认 1e-10)
387
- - preserve_mask_values: 是否保留掩码区域的原始值(True)或将其替换为缺省值(False)
388
- """
389
- # 判断是否要替换原文件
390
- delete_orig = dst_path is None
391
- if delete_orig:
392
- dst_path = src_path.replace(".nc", "_compress.nc")
393
- # 打开原始文件并保存压缩文件
394
- ds = xr.open_dataset(src_path)
395
- save_to_nc(dst_path, ds, convert_dtype='int32',scale_offset_switch=True, compile_switch=True, preserve_mask_values=preserve_mask_values)
396
- ds.close()
397
-
398
- # 验证压缩后的数据是否失真
399
- original_ds = xr.open_dataset(src_path)
400
- compressed_ds = xr.open_dataset(dst_path)
401
- # 更详细地验证数据
402
- for var in original_ds.data_vars:
403
- original_data = original_ds[var].values
404
- compressed_data = compressed_ds[var].values
405
- # 跳过非数值类型变量
406
- if not np.issubdtype(original_data.dtype, np.number):
407
- continue
408
- # 获取掩码(如果存在)
409
- original_mask = None
410
- if hasattr(original_data, "mask") and np.ma.is_masked(original_data): # 修正:确保是有效的掩码数组
411
- original_mask = original_data.mask.copy()
412
- # 检查有效数据是否在允许误差范围内
413
- valid_mask = np.isfinite(original_data)
414
- if original_mask is not None:
415
- valid_mask &= ~original_mask
416
- if np.any(valid_mask):
417
- if np.issubdtype(original_data.dtype, np.floating):
418
- diff = np.abs(original_data[valid_mask] - compressed_data[valid_mask])
419
- max_diff = np.max(diff)
420
- if max_diff > tolerance:
421
- print(f"警告: 变量 {var} 的压缩误差 {max_diff} 超出容许范围 {tolerance}")
422
- if max_diff > tolerance * 10: # 严重偏差时抛出错误
423
- raise ValueError(f"变量 {var} 的数据在压缩后严重失真 (max_diff={max_diff})")
424
- elif np.issubdtype(original_data.dtype, np.integer):
425
- # 整数类型应该完全相等
426
- if not np.array_equal(original_data[valid_mask], compressed_data[valid_mask]):
427
- raise ValueError(f"变量 {var} 的整数数据在压缩后不一致")
428
- # 如果需要保留掩码区域值,检查掩码区域的值
429
- if preserve_mask_values and original_mask is not None and np.any(original_mask):
430
- # 确保掩码区域的原始值被正确保留
431
- # 修正:掩码数组可能存在数据类型不匹配问题,添加安全检查
432
- try:
433
- mask_diff = np.abs(original_data[original_mask] - compressed_data[original_mask])
434
- if np.any(mask_diff > tolerance):
435
- print(f"警告: 变量 {var} 的掩码区域数据在压缩后发生变化")
436
- except Exception as e:
437
- print(f"警告: 变量 {var} 的掩码区域数据比较失败: {str(e)}")
438
- original_ds.close()
439
- compressed_ds.close()
440
-
441
- # 替换原文件
442
- if delete_orig:
443
- os.remove(src_path)
444
- os.rename(dst_path, src_path)
445
-
446
377
 
447
378
  # 测试用例
448
379
  if __name__ == "__main__":
@@ -197,12 +197,12 @@ def select_colormap_and_levels(data_range: Tuple[float, float], plot_type: str)
197
197
  num_levels = 128
198
198
 
199
199
  if data_range[0] * data_range[1] < 0:
200
- cmap = oafuncs.oa_cmap.get("diverging_1")
200
+ cmap = oafuncs.oa_cmap.get(diverging_cmap)
201
201
  bdy = max(abs(data_range[0]), abs(data_range[1]))
202
202
  norm = mpl.colors.TwoSlopeNorm(vmin=-bdy, vcenter=0, vmax=bdy)
203
203
  levels = np.linspace(-bdy, bdy, num_levels)
204
204
  else:
205
- cmap = oafuncs.oa_cmap.get("cool_1") if data_range[0] < 0 else oafuncs.oa_cmap.get("warm_1")
205
+ cmap = oafuncs.oa_cmap.get(negative_cmap) if data_range[0] < 0 else oafuncs.oa_cmap.get(positive_cmap)
206
206
  norm = mpl.colors.Normalize(vmin=data_range[0], vmax=data_range[1])
207
207
  levels = np.linspace(data_range[0], data_range[1], num_levels)
208
208
 
@@ -320,9 +320,14 @@ def get_xyzt_names(ds_in, xyzt_dims):
320
320
  return x_dim, y_dim, z_dim, t_dim
321
321
 
322
322
 
323
- def func_plot_dataset(ds_in: Union[xr.Dataset, xr.DataArray], output_dir: str, xyzt_dims: Tuple[str, str, str, str] = None, plot_type: str = "contourf", fixed_colorscale: bool = False) -> None:
323
+ def func_plot_dataset(ds_in: Union[xr.Dataset, xr.DataArray], output_dir: str, cmap='diverging_3', pcmap='warm_3', ncmap='cool_3', xyzt_dims: Tuple[str, str, str, str] = None, plot_type: str = "contourf", fixed_colorscale: bool = False) -> None:
324
324
  """Plot variables from a NetCDF file and save the plots to the specified directory."""
325
325
  os.makedirs(output_dir, exist_ok=True)
326
+
327
+ global diverging_cmap, positive_cmap, negative_cmap
328
+ diverging_cmap = cmap
329
+ positive_cmap = pcmap
330
+ negative_cmap = ncmap
326
331
 
327
332
  # Main processing function
328
333
  try:
@@ -1,11 +1,11 @@
1
- from typing import Any, List, Union
1
+ from typing import Any, List, Union, Literal
2
2
 
3
3
  import numpy as np
4
4
  import xarray as xr
5
5
  from rich import print
6
6
 
7
7
 
8
- __all__ = ["interp_along_dim", "interp_2d", "ensure_list", "mask_shapefile"]
8
+ __all__ = ["interp_along_dim", "interp_2d", "ensure_list", "mask_shapefile", "mask_land_ocean"]
9
9
 
10
10
 
11
11
  def ensure_list(input_value: Any) -> List[str]:
@@ -188,5 +188,83 @@ def mask_shapefile(
188
188
  return None
189
189
 
190
190
 
191
+
192
+ def _normalize_lon(lon: np.ndarray) -> np.ndarray:
193
+ """将经度转换到 [-180, 180)。"""
194
+ lon = np.asarray(lon, dtype=float)
195
+ return np.where(lon >= 180, lon - 360, lon)
196
+
197
+
198
+ def _land_sea_mask(
199
+ lon: np.ndarray,
200
+ lat: np.ndarray,
201
+ keep: Literal["land", "ocean"],
202
+ ) -> np.ndarray:
203
+ """
204
+ 根据 1-D 或 2-D 经纬度返回布尔掩膜。
205
+ True 表示该位置 *保留*,False 表示该位置将被掩掉。
206
+ """
207
+ from global_land_mask import globe
208
+
209
+ lon = _normalize_lon(lon)
210
+ lat = np.asarray(lat, dtype=float)
211
+
212
+ # 如果输入是 1-D,则网格化;2-D 则直接使用
213
+ if lon.ndim == 1 and lat.ndim == 1:
214
+ lon_2d, lat_2d = np.meshgrid(lon, lat)
215
+ elif lon.ndim == 2 and lat.ndim == 2:
216
+ lon_2d, lat_2d = lon, lat
217
+ else:
218
+ raise ValueError("经纬度必须是同维度的 1-D 或 2-D 数组")
219
+
220
+ is_ocean = globe.is_ocean(lat_2d, lon_2d)
221
+
222
+ if keep == "land":
223
+ mask = ~is_ocean
224
+ elif keep == "ocean":
225
+ mask = is_ocean
226
+ else:
227
+ raise ValueError("keep 只能是 'land' 或 'ocean'")
228
+
229
+ return mask
230
+
231
+
232
+ def mask_land_ocean(
233
+ data: xr.DataArray | xr.Dataset,
234
+ lon: np.ndarray,
235
+ lat: np.ndarray,
236
+ *, # 强制关键字参数
237
+ keep: Literal["land", "ocean"] = "land",
238
+ ) -> xr.DataArray | xr.Dataset:
239
+ """
240
+ 根据海陆分布掩膜 xarray 对象。
241
+
242
+ Parameters
243
+ ----------
244
+ data : xr.DataArray 或 xr.Dataset
245
+ 至少包含 'lat' 和 'lon' 维度/坐标的数组。
246
+ lon : array_like
247
+ 经度,可以是 1-D 或 2-D。
248
+ lat : array_like
249
+ 纬度,可以是 1-D 或 2-D。
250
+ keep : {'land', 'ocean'}, optional
251
+ 指定要保留的部分,默认为 'land'。
252
+
253
+ Returns
254
+ -------
255
+ 掩膜后的 xr.DataArray / xr.Dataset
256
+ """
257
+ mask = _land_sea_mask(lon, lat, keep)
258
+
259
+ # 用 apply_ufunc 自动对齐并广播掩膜
260
+ return xr.apply_ufunc(
261
+ lambda x, m: x.where(m),
262
+ data,
263
+ xr.DataArray(mask, dims=("lat", "lon")),
264
+ dask="parallelized",
265
+ keep_attrs=True,
266
+ )
267
+
268
+
191
269
  if __name__ == "__main__":
192
270
  pass
@@ -270,6 +270,9 @@ def draw(
270
270
  output_directory: Optional[str] = None,
271
271
  dataset: Optional[xr.Dataset] = None,
272
272
  file_path: Optional[str] = None,
273
+ cmap='diverging_3',
274
+ pcmap='warm_3',
275
+ ncmap='cool_3',
273
276
  dims_xyzt: Union[List[str], Tuple[str, str, str, str]] = None,
274
277
  plot_style: str = "contourf",
275
278
  use_fixed_colorscale: bool = False,
@@ -296,18 +299,18 @@ def draw(
296
299
  raise ValueError("dimensions must be a list or tuple")
297
300
 
298
301
  if dataset is not None:
299
- func_plot_dataset(dataset, output_directory, dims_xyzt, plot_style, use_fixed_colorscale)
302
+ func_plot_dataset(dataset, output_directory, cmap, pcmap, ncmap, dims_xyzt, plot_style, use_fixed_colorscale)
300
303
  elif file_path is not None:
301
304
  if check(file_path):
302
305
  ds = xr.open_dataset(file_path)
303
- func_plot_dataset(ds, output_directory, dims_xyzt, plot_style, use_fixed_colorscale)
306
+ func_plot_dataset(ds, output_directory, cmap, pcmap, ncmap, dims_xyzt, plot_style, use_fixed_colorscale)
304
307
  else:
305
308
  print(f"[red]Invalid file: {file_path}[/red]")
306
309
  else:
307
310
  print("[red]No dataset or file provided.[/red]")
308
311
 
309
312
 
310
- def compress(src_path, dst_path=None,convert_dtype='int16'):
313
+ def compress(src_path, dst_path=None,convert_dtype='int32'):
311
314
  """
312
315
  压缩 NetCDF 文件,使用 scale_factor/add_offset 压缩数据。
313
316
  若 dst_path 省略,则自动生成新文件名,写出后删除原文件并将新文件改回原名。
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.41
3
+ Version: 0.0.98.43
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -18,7 +18,7 @@ URL = "https://github.com/Industry-Pays/OAFuncs"
18
18
  EMAIL = "liukun0312@stu.ouc.edu.cn"
19
19
  AUTHOR = "Kun Liu"
20
20
  REQUIRES_PYTHON = ">=3.10.0" # 2025/03/13
21
- VERSION = "0.0.98.41"
21
+ VERSION = "0.0.98.43"
22
22
 
23
23
  # What packages are required for this module to be executed?
24
24
  REQUIRED = [
File without changes
File without changes
File without changes
File without changes