oafuncs 0.0.98.19__py3-none-any.whl → 0.0.98.21__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.
@@ -1,18 +1,3 @@
1
- #!/usr/bin/env python
2
- # coding=utf-8
3
- """
4
- Author: Liu Kun && 16031215@qq.com
5
- Date: 2025-04-07 10:51:09
6
- LastEditors: Liu Kun && 16031215@qq.com
7
- LastEditTime: 2025-04-07 10:51:09
8
- FilePath: \\Python\\My_Funcs\\OAFuncs\\oafuncs\\oa_down\\hycom_3hourly copy.py
9
- Description:
10
- EditPlatform: vscode
11
- ComputerInfo: XPS 15 9510
12
- SystemInfo: Windows 11
13
- Python Version: 3.12
14
- """
15
-
16
1
  import asyncio
17
2
  import datetime
18
3
  import logging
@@ -23,7 +8,6 @@ import warnings
23
8
  from concurrent.futures import ThreadPoolExecutor, as_completed
24
9
  from pathlib import Path
25
10
  from threading import Lock
26
- from oafuncs.oa_tool import pbar
27
11
 
28
12
  import httpx
29
13
  import matplotlib.pyplot as plt
@@ -38,6 +22,7 @@ from oafuncs.oa_down.user_agent import get_ua
38
22
  from oafuncs.oa_file import file_size
39
23
  from oafuncs.oa_nc import check as check_nc
40
24
  from oafuncs.oa_nc import modify as modify_nc
25
+ from oafuncs.oa_tool import pbar
41
26
 
42
27
  logging.getLogger("httpx").setLevel(logging.WARNING) # 关闭 httpx 的 INFO 日志,只显示 WARNING 及以上
43
28
 
@@ -724,7 +709,6 @@ class _HycomDownloader:
724
709
  logging.info(f"{file_name}: {percent}% ({downloaded / 1024:.1f} KB / {total / 1024:.1f} KB)")
725
710
  last_percent = percent
726
711
 
727
-
728
712
  elapsed = datetime.datetime.now() - start
729
713
  # logging.info(f"File {file_name} downloaded, Time: {elapsed}")
730
714
  logging.info(f"Saving {file_name} ...")
@@ -966,7 +950,7 @@ def _download_hourly_func(var, time_s, time_e, lon_min=0, lon_max=359.92, lat_mi
966
950
  print("Downloading a series of files...")
967
951
  time_list = _get_time_list(ymdh_time_s, ymdh_time_e, interval_hour, "hour")
968
952
  # with Progress() as progress:
969
- # task = progress.add_task(f"[cyan]{bar_desc}", total=len(time_list))
953
+ # task = progress.add_task(f"[cyan]{bar_desc}", total=len(time_list))
970
954
  if num_workers is None or num_workers <= 1:
971
955
  for i, time_str in pbar(enumerate(time_list), description=f"{bar_desc}", total=len(time_list), next_line=True):
972
956
  _prepare_url_to_download(var, lon_min, lon_max, lat_min, lat_max, time_str, None, depth, level, store_path, dataset_name, version_name, cover)
@@ -976,7 +960,7 @@ def _download_hourly_func(var, time_s, time_e, lon_min=0, lon_max=359.92, lat_mi
976
960
  futures = [executor.submit(_download_task, var, time_str, None, lon_min, lon_max, lat_min, lat_max, depth, level, store_path, dataset_name, version_name, cover) for time_str in time_list]
977
961
  """ for feature in as_completed(futures):
978
962
  _done_callback(feature, progress, task, len(time_list), counter_lock) """
979
- for _ in pbar(as_completed(futures),description=f"{bar_desc}", total=len(futures),next_line=True):
963
+ for _ in pbar(as_completed(futures), description=f"{bar_desc}", total=len(futures), next_line=True):
980
964
  pass
981
965
  else:
982
966
  print("[bold red]Please ensure the time_s is no more than time_e")
@@ -1174,7 +1158,7 @@ def download(
1174
1158
 
1175
1159
  count_dict["total"] = count_dict["success"] + count_dict["fail"] + count_dict["skip"] + count_dict["no_data"]
1176
1160
  print("[bold #ecdbfe]=" * mark_len)
1177
- print(f"[bold #ff80ab]Total: {count_dict['total']}\nSuccess: {count_dict['success']}\nFail: {count_dict['fail']}\nSkip: {count_dict['skip']}\nNo data: {count_dict['no_data']}")
1161
+ print(f"[bold #ff80ab]Total : {count_dict['total']}\nSuccess: {count_dict['success']}\nFail : {count_dict['fail']}\nSkip : {count_dict['skip']}\nNo data: {count_dict['no_data']}")
1178
1162
  print("[bold #ecdbfe]=" * mark_len)
1179
1163
  if count_dict["fail"] > 0:
1180
1164
  print("[bold #be5528]Please try again to download the failed data later.")
oafuncs/oa_draw.py CHANGED
@@ -318,7 +318,7 @@ def add_gridlines(axes: plt.Axes, longitude_lines: list[float] = None, latitude_
318
318
  if latitude_lines is not None:
319
319
  gl.ylocator = mticker.FixedLocator(np.array(latitude_lines))
320
320
 
321
- print("[green]Gridlines added successfully.[/green]")
321
+ # print("[green]Gridlines added successfully.[/green]")
322
322
  return axes, gl
323
323
 
324
324
 
@@ -365,7 +365,7 @@ def add_cartopy(axes: plt.Axes, longitude_data: np.ndarray = None, latitude_data
365
365
  lat_min, lat_max = np.nanmin(latitude_data), np.nanmax(latitude_data)
366
366
  axes.set_extent([lon_min, lon_max, lat_min, lat_max], crs=map_projection)
367
367
 
368
- print("[green]Cartopy features added successfully.[/green]")
368
+ # print("[green]Cartopy features added successfully.[/green]")
369
369
  return axes
370
370
 
371
371
 
oafuncs/oa_nc.py CHANGED
@@ -6,7 +6,8 @@ import numpy as np
6
6
  import xarray as xr
7
7
  from rich import print
8
8
 
9
- __all__ = ["save", "merge", "modify", "rename", "check", "convert_longitude", "isel", "draw", "compress_netcdf", "unpack_netcdf"]
9
+ __all__ = ["save", "merge", "modify", "rename", "check", "convert_longitude", "isel", "draw", "compress", "unscale"]
10
+
10
11
 
11
12
 
12
13
  def save(
@@ -15,8 +16,10 @@ def save(
15
16
  variable_name: Optional[str] = None,
16
17
  coordinates: Optional[dict] = None,
17
18
  write_mode: str = "w",
19
+ convert_dtype: str = "int32",
18
20
  use_scale_offset: bool = True,
19
21
  use_compression: bool = True,
22
+ preserve_mask_values: bool = True,
20
23
  ) -> None:
21
24
  """
22
25
  Write data to a NetCDF file.
@@ -27,8 +30,10 @@ def save(
27
30
  variable_name (Optional[str]): Variable name for the data.
28
31
  coordinates (Optional[dict]): Coordinates, where keys are dimension names and values are coordinate data.
29
32
  write_mode (str): Write mode, 'w' for write, 'a' for append. Default is 'w'.
33
+ convert_dtype (str): Data type to convert to. Default is 'int32'.
30
34
  use_scale_offset (bool): Whether to use scale_factor and add_offset. Default is True.
31
35
  use_compression (bool): Whether to use compression parameters. Default is True.
36
+ preserve_mask_values (bool): Whether to preserve mask values. Default is True.
32
37
 
33
38
  Example:
34
39
  >>> save(r'test.nc', data, 'u', {'time': np.linspace(0, 120, 100), 'lev': np.linspace(0, 120, 50)}, 'a')
@@ -38,7 +43,7 @@ def save(
38
43
  """
39
44
  from ._script.netcdf_write import save_to_nc
40
45
 
41
- save_to_nc(file_path, data, variable_name, coordinates, write_mode, use_scale_offset, use_compression)
46
+ save_to_nc(file_path, data, variable_name, coordinates, write_mode, convert_dtype,use_scale_offset, use_compression, preserve_mask_values)
42
47
  print(f"[green]Data successfully saved to {file_path}[/green]")
43
48
 
44
49
 
@@ -278,7 +283,7 @@ def draw(
278
283
  print("[red]No dataset or file provided.[/red]")
279
284
 
280
285
 
281
- def compress_netcdf(src_path, dst_path=None):
286
+ def compress(src_path, dst_path=None,convert_dtype='int16'):
282
287
  """
283
288
  压缩 NetCDF 文件,使用 scale_factor/add_offset 压缩数据。
284
289
  若 dst_path 省略,则自动生成新文件名,写出后删除原文件并将新文件改回原名。
@@ -289,7 +294,7 @@ def compress_netcdf(src_path, dst_path=None):
289
294
  dst_path = src_path.replace(".nc", "_compress.nc")
290
295
 
291
296
  ds = xr.open_dataset(src_path)
292
- save(dst_path, ds)
297
+ save(dst_path, ds, convert_dtype=convert_dtype, use_scale_offset=True, use_compression=True)
293
298
  ds.close()
294
299
 
295
300
  if delete_orig:
@@ -298,26 +303,88 @@ def compress_netcdf(src_path, dst_path=None):
298
303
  pass
299
304
 
300
305
 
301
- def unpack_netcdf(src_path, dst_path=None):
306
+ def unscale(src_path, dst_path=None, compression_level=4):
302
307
  """解码 NetCDF 并移除 scale_factor/add_offset,写出真实值。
308
+ 保留压缩功能,但不使用比例因子和偏移量,以控制文件大小。
303
309
  若 dst_path 省略,则自动生成新文件名,写出后删除原文件并将新文件改回原名。
310
+
311
+ Args:
312
+ src_path: 源文件路径
313
+ dst_path: 目标文件路径,None则替换原文件
314
+ compression_level: 压缩级别(1-9),数值越大压缩比越高,速度越慢
304
315
  """
305
316
  # 判断是否要替换原文件
306
317
  delete_orig = dst_path is None
307
318
  if delete_orig:
308
319
  dst_path = src_path.replace(".nc", "_unpacked.nc")
309
320
 
321
+ # 打开原始文件,获取文件大小
322
+ orig_size = os.path.getsize(src_path) / (1024 * 1024) # MB
323
+
324
+ # 先以原始模式打开,查看哪些变量使用了scale_factor/add_offset
325
+ with xr.open_dataset(src_path, decode_cf=False) as ds_raw:
326
+ has_scaling = []
327
+ for var in ds_raw.data_vars:
328
+ if "scale_factor" in ds_raw[var].attrs or "add_offset" in ds_raw[var].attrs:
329
+ has_scaling.append(var)
330
+
331
+ print(f"[yellow]文件: {src_path} (原始大小: {orig_size:.2f} MB)[/yellow]")
332
+ if has_scaling:
333
+ print(f"[yellow]发现 {len(has_scaling)} 个变量使用了比例因子: {', '.join(has_scaling)}[/yellow]")
334
+ else:
335
+ print("[yellow]未发现任何变量使用比例因子,解包可能不必要[/yellow]")
336
+
337
+ # 解码模式打开
310
338
  ds = xr.open_dataset(src_path, decode_cf=True)
339
+ encoding = {}
340
+
311
341
  for var in ds.data_vars:
342
+ # 保存原始的_FillValue
343
+ fill_value = None
344
+ if "_FillValue" in ds[var].attrs:
345
+ fill_value = ds[var].attrs["_FillValue"]
346
+ elif "_FillValue" in ds[var].encoding:
347
+ fill_value = ds[var].encoding["_FillValue"]
348
+
349
+ # 清除scale_factor和add_offset属性
312
350
  ds[var].attrs.pop("scale_factor", None)
313
351
  ds[var].attrs.pop("add_offset", None)
314
352
  ds[var].encoding.clear()
315
- ds.to_netcdf(dst_path, mode="w", format="NETCDF4", engine="netcdf4")
353
+
354
+ # 仅对数值型变量处理
355
+ if np.issubdtype(ds[var].dtype, np.number):
356
+ # 强制转换为float32,避免float64导致文件暴涨
357
+ if np.issubdtype(ds[var].dtype, np.floating) and ds[var].dtype != np.float32:
358
+ ds[var] = ds[var].astype(np.float32)
359
+
360
+ # 设置压缩参数,但不使用scale_factor/add_offset
361
+ encoding[var] = {"zlib": True, "complevel": compression_level, "dtype": ds[var].dtype}
362
+ # 恢复_FillValue
363
+ if fill_value is not None:
364
+ encoding[var]["_FillValue"] = fill_value
365
+
366
+ # 使用save函数保存,传入encoding确保只压缩不使用scale_factor
367
+ ds.to_netcdf(dst_path, encoding=encoding)
316
368
  ds.close()
317
369
 
370
+ # 打印输出文件大小对比
371
+ if os.path.exists(dst_path):
372
+ new_size = os.path.getsize(dst_path) / (1024 * 1024) # MB
373
+ size_change = new_size - orig_size
374
+ change_percent = (size_change / orig_size) * 100
375
+
376
+ color = "green" if size_change <= 0 else "red"
377
+ print(f"[{color}]解包后文件大小: {new_size:.2f} MB ({change_percent:+.1f}%)[/{color}]")
378
+
379
+ if size_change > orig_size * 0.5 and new_size > 100: # 如果文件增长超过50%且大于100MB
380
+ print(f"[red]警告: 文件大小增长显著! 考虑增加压缩级别(当前:{compression_level})[/red]")
381
+
318
382
  if delete_orig:
319
383
  os.remove(src_path)
320
384
  os.rename(dst_path, src_path)
385
+ print(f"[green]已替换原文件: {src_path}[/green]")
386
+ else:
387
+ print(f"[green]已保存到: {dst_path}[/green]")
321
388
 
322
389
 
323
390
  if __name__ == "__main__":
oafuncs/oa_tool.py CHANGED
@@ -135,7 +135,7 @@ def email(title: str = "Title", content: Optional[str] = None, send_to: str = "1
135
135
 
136
136
  def pbar(
137
137
  iterable: Iterable = range(100),
138
- description: str = "Working...",
138
+ description: str = "Working",
139
139
  total: Optional[float] = None,
140
140
  completed: float = 0,
141
141
  color: Any = "None",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: oafuncs
3
- Version: 0.0.98.19
3
+ Version: 0.0.98.21
4
4
  Summary: Oceanic and Atmospheric Functions
5
5
  Home-page: https://github.com/Industry-Pays/OAFuncs
6
6
  Author: Kun Liu
@@ -1,29 +1,29 @@
1
1
  oafuncs/__init__.py,sha256=T_-VtnWWllV3Q91twT5Yt2sUapeA051QbPNnBxmg9nw,1456
2
- oafuncs/oa_cmap.py,sha256=DimWT4Bg7uE5Lx8hSw1REp7whpsR2pFRStAwk1cowEM,11494
3
- oafuncs/oa_data.py,sha256=y11xxaVNZ6_eveVjSG4PisRXYpKr_FFsBBh0mj_ss2g,8436
2
+ oafuncs/oa_cmap.py,sha256=NVKwEkmMUKXh9L1svx_WHqemLOx3evgX3_-UVDxc9ko,11498
3
+ oafuncs/oa_data.py,sha256=Aat9ktxxRGevaqQya3IJWfXeoEs-FCXGUcNE2pKnzfU,10931
4
4
  oafuncs/oa_date.py,sha256=WhM6cyD4G3IeghjLTHhAMtlvJbA7kwQG2sHnxdTgyso,6303
5
- oafuncs/oa_draw.py,sha256=Wj2QBgyIPpV_dxaDrH10jqj_puK9ZM9rd-si-3VrsrE,17631
5
+ oafuncs/oa_draw.py,sha256=IaBGDx-EOxyMM2IuJ4zLZt6ruHHV5qFStPItmUOXoWk,17635
6
6
  oafuncs/oa_file.py,sha256=j9gXJgPOJsliu4IOUc4bc-luW4yBvQyNCEmMyDVjUwQ,16404
7
7
  oafuncs/oa_help.py,sha256=_4AZgRDq5Or0vauNvq5IDDHIBoBfdOQtzak-mG1wwAw,4537
8
- oafuncs/oa_nc.py,sha256=lLEPjj4qgdEw1al0r1nKGZUnMP_ejT8A2NKD4lrs2kc,11936
8
+ oafuncs/oa_nc.py,sha256=UUXnBg2cO5XiJ8w0jNqCZJg83FVKqxlEHxOJG5o08Z8,15201
9
9
  oafuncs/oa_python.py,sha256=NkopwkYFGSEuVljnTBvXCl6o2CeyRNBqRXSsUl3euEE,5192
10
- oafuncs/oa_tool.py,sha256=rpPkLqWhqMmqlCc5wjL8qMTg3gThCkSrYJckbX_0iJc,8631
10
+ oafuncs/oa_tool.py,sha256=QBjJh3pf54yXVuOmu97rW6Tsr6uNMyZ5KqZbR4VQFTc,8628
11
11
  oafuncs/_data/hycom.png,sha256=MadKs6Gyj5n9-TOu7L4atQfTXtF9dvN9w-tdU9IfygI,10945710
12
12
  oafuncs/_data/oafuncs.png,sha256=o3VD7wm-kwDea5E98JqxXl04_78cBX7VcdUt7uQXGiU,3679898
13
13
  oafuncs/_script/cprogressbar.py,sha256=UIgGcLFs-6IgWlITuBLaQqrpt4OAK3Mst5RlCiNfZdQ,15772
14
- oafuncs/_script/data_interp.py,sha256=KJ-p-UN3Op1MmtCoN4KdjFVHFE3GNHrTD3vBjzaYSjQ,4688
15
- oafuncs/_script/data_interp_geo.py,sha256=X89KxLYhpltWi0Sf96gIhBL3r1M5aExd_JCmgBmmvUc,3742
14
+ oafuncs/_script/data_interp.py,sha256=EiZbt6n5BEaRKcng88UgX7TFPhKE6TLVZniS01awXjg,5146
15
+ oafuncs/_script/data_interp_geo.py,sha256=ZRFb3fKRiYQViZNHd19eW20C9i38BsiIU8w0fG5mbqM,7789
16
16
  oafuncs/_script/email.py,sha256=lL4HGKrr524-g0xLlgs-4u7x4-u7DtgNoD9AL8XJKj4,3058
17
- oafuncs/_script/netcdf_merge.py,sha256=9hCyxfeUHnBzs50_0v0jzVfxpMxTX4dNTo0pmsp_T6g,4226
17
+ oafuncs/_script/netcdf_merge.py,sha256=4mZLMcxBL4Rehi_eW2EX6vqbMJgZBOL4_ceaMzcuzio,5565
18
18
  oafuncs/_script/netcdf_modify.py,sha256=sGRUYNhfGgf9JV70rnBzw3bzuTRSXzBTL_RMDnDPeLQ,4552
19
- oafuncs/_script/netcdf_write.py,sha256=iO1Qv9bp6RLiw1D8Nrv7tX_8X-diUZaX3Nxhk6pJ5Nw,8556
20
- oafuncs/_script/parallel.py,sha256=T9Aie-e4LcbKlFTLZ0l4lhEN3SBVa84jRcrAsIm8s0I,8767
19
+ oafuncs/_script/netcdf_write.py,sha256=GvyUyUhzMonzSp3y4pT8ZAfbQrsh5J3dLnmINYJKhuE,21422
20
+ oafuncs/_script/parallel.py,sha256=07-BJVHxXJNlrOrhrSGt7qCZiKWq6dBvNDBA1AANYnI,8861
21
21
  oafuncs/_script/parallel_test.py,sha256=0GBqZOX7IaCOKF2t1y8N8YYu53GJ33OkfsWgpvZNqM4,372
22
22
  oafuncs/_script/plot_dataset.py,sha256=zkSEnO_-biyagorwWXPoihts_cwuvripzEt-l9bHJ2E,13989
23
23
  oafuncs/_script/replace_file_content.py,sha256=eCFZjnZcwyRvy6b4mmIfBna-kylSZTyJRfgXd6DdCjk,5982
24
24
  oafuncs/oa_down/User_Agent-list.txt,sha256=pHaMlElMvZ8TG4vf4BqkZYKqe0JIGkr4kCN0lM1Y9FQ,514295
25
25
  oafuncs/oa_down/__init__.py,sha256=kRX5eTUCbAiz3zTaQM1501paOYS_3fizDN4Pa0mtNUA,585
26
- oafuncs/oa_down/hycom_3hourly.py,sha256=VYxG9DPAnS1wk8gEXyqSIww4IvNRWOYGC4D6PTh5t3A,55450
26
+ oafuncs/oa_down/hycom_3hourly.py,sha256=R5fKfIcpNRuaQgPiov_hJRd8voWgAHVLWifAMzR6RQI,55075
27
27
  oafuncs/oa_down/hycom_3hourly_proxy.py,sha256=1eaoJGI_m-7w4ZZ3n7NGxkZaeFdsm0d3U-hyw8RFNbc,54563
28
28
  oafuncs/oa_down/idm.py,sha256=4z5IvgfTyIKEI1kOtqXZwN7Jnfjwp6qDBOIoVyOLp0I,1823
29
29
  oafuncs/oa_down/literature.py,sha256=2bF9gSKQbzcci9LcKE81j8JEjIJwON7jbwQB3gDDA3E,11331
@@ -39,8 +39,8 @@ oafuncs/oa_sign/__init__.py,sha256=QKqTFrJDFK40C5uvk48GlRRbGFzO40rgkYwu6dYxatM,5
39
39
  oafuncs/oa_sign/meteorological.py,sha256=8091SHo2L8kl4dCFmmSH5NGVHDku5i5lSiLEG5DLnOQ,6489
40
40
  oafuncs/oa_sign/ocean.py,sha256=xrW-rWD7xBWsB5PuCyEwQ1Q_RDKq2KCLz-LOONHgldU,5932
41
41
  oafuncs/oa_sign/scientific.py,sha256=a4JxOBgm9vzNZKpJ_GQIQf7cokkraV5nh23HGbmTYKw,5064
42
- oafuncs-0.0.98.19.dist-info/licenses/LICENSE.txt,sha256=rMtLpVg8sKiSlwClfR9w_Dd_5WubTQgoOzE2PDFxzs4,1074
43
- oafuncs-0.0.98.19.dist-info/METADATA,sha256=PJ_1BA6QOeA2QHSb61jwOFA2pDD2H4QT--m3tf_f44I,4273
44
- oafuncs-0.0.98.19.dist-info/WHEEL,sha256=SmOxYU7pzNKBqASvQJ7DjX3XGUF92lrGhMb3R6_iiqI,91
45
- oafuncs-0.0.98.19.dist-info/top_level.txt,sha256=bgC35QkXbN4EmPHEveg_xGIZ5i9NNPYWqtJqaKqTPsQ,8
46
- oafuncs-0.0.98.19.dist-info/RECORD,,
42
+ oafuncs-0.0.98.21.dist-info/licenses/LICENSE.txt,sha256=rMtLpVg8sKiSlwClfR9w_Dd_5WubTQgoOzE2PDFxzs4,1074
43
+ oafuncs-0.0.98.21.dist-info/METADATA,sha256=Lk9y2XVdDKb9HB5PP_Pm-_aII0tnGmaladuGW1Y9otM,4273
44
+ oafuncs-0.0.98.21.dist-info/WHEEL,sha256=ooBFpIzZCPdw3uqIQsOo4qqbA4ZRPxHnOH7peeONza0,91
45
+ oafuncs-0.0.98.21.dist-info/top_level.txt,sha256=bgC35QkXbN4EmPHEveg_xGIZ5i9NNPYWqtJqaKqTPsQ,8
46
+ oafuncs-0.0.98.21.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: setuptools (79.0.1)
2
+ Generator: setuptools (80.0.1)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5