11from netCDF4 import Dataset , Variable
2+ import numpy as np
3+
24
35class HDF5 (Dataset ):
46 @staticmethod
@@ -50,3 +52,59 @@ def _jump(fp: Dataset, path="/"):
5052 else :
5153 return HDF5 ._jump (subnode_fp , subnode_path )
5254
55+
56+ @staticmethod
57+ def write (fp : Dataset , data :np .ndarray | np .ma .MaskedArray , varname :str , dimensions :tuple [str , ...], datatype :str = None , scale_factor = 1.0 , add_offset = 0.0 , ** kwargs ):
58+ # convert data to numpy.ma.MaskedArray
59+ if isinstance (data , np .ma .MaskedArray ):
60+ xm = data
61+ elif isinstance (data , np .ndarray ):
62+ xm = np .ma .masked_invalid (data )
63+ else :
64+ raise ValueError ("data must be a numpy.ndarray or numpy.ma.MaskedArray" )
65+
66+ # check dimensions
67+ shape = xm .shape
68+ if len (dimensions ) != len (shape ):
69+ raise ValueError ("dimensions (tuple) and array shape (tuple) must have the same length" )
70+ for i , dimension in enumerate (dimensions ):
71+ if dimension not in fp .dimensions :
72+ fp .createDimension (dimension , shape [i ])
73+ else :
74+ if (dsize := shape [i ]) != (fsize := fp .dimensions [dimension ].size ):
75+ raise ValueError (f"dimensions ({ dimension } : { dsize } ) must have the same size in the file ({ dimension } : { fsize } )" )
76+
77+ # check datatype
78+ if datatype is None :
79+ datatype = xm .dtype
80+ else :
81+ # 将 datatype (如 'i2', 'int16') 转换为 numpy dtype 对象
82+ dt = np .dtype (datatype )
83+
84+ # 只有目标类型是整数时,才需要考虑截断(防止 Overflow)
85+ if np .issubdtype (dt , np .integer ):
86+ # 1. 获取目标整数类型的理论最大/最小值
87+ info = np .iinfo (dt )
88+ i_min , i_max = info .min , info .max
89+ print (i_min , i_max )
90+
91+ # 3. 转换为物理值的范围
92+ # 公式: Physical = Integer * scale + offset
93+ lim1 = i_min * scale_factor + add_offset
94+ lim2 = i_max * scale_factor + add_offset
95+ xm = np .ma .masked_outside (xm , lim1 , lim2 )
96+
97+ # create variable
98+ v = fp .createVariable (varname = varname , datatype = datatype , dimensions = dimensions , ** kwargs )
99+
100+ # set scale_factor and add_offset
101+ v .scale_factor = scale_factor
102+ v .add_offset = add_offset
103+ v .set_auto_maskandscale (True )
104+
105+ # write data
106+ xm .data [xm .mask ] = 0 # fill invalid values with 0
107+ xm .fill_value = 0
108+ v [:] = xm
109+
110+ return v
0 commit comments