Alaea 2.3.4__tar.gz → 2.3.6__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 (52) hide show
  1. {alaea-2.3.4 → alaea-2.3.6}/.DS_Store +0 -0
  2. {alaea-2.3.4 → alaea-2.3.6}/.gitattributes +2 -0
  3. {alaea-2.3.4 → alaea-2.3.6}/.gitignore +1 -0
  4. {alaea-2.3.4 → alaea-2.3.6}/Alaea/Blogging.py +13 -12
  5. {alaea-2.3.4 → alaea-2.3.6}/Alaea/Standard/xr_attrs.py +166 -52
  6. alaea-2.3.6/Alaea/baysalt/Forage/__init__.py +11 -0
  7. alaea-2.3.6/Alaea/baysalt/Forage/erosion_data/__init__.py +11 -0
  8. alaea-2.3.6/Alaea/baysalt/__init__.py +11 -0
  9. alaea-2.3.6/Alaea/baysalt/erosion_coastline.py +405 -0
  10. {alaea-2.3.4 → alaea-2.3.6}/Alaea/processBar.py +25 -1
  11. {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/Read.py +25 -1
  12. {alaea-2.3.4 → alaea-2.3.6/Alaea.egg-info}/PKG-INFO +1 -1
  13. {alaea-2.3.4 → alaea-2.3.6}/Alaea.egg-info/SOURCES.txt +4 -0
  14. {alaea-2.3.4/Alaea.egg-info → alaea-2.3.6}/PKG-INFO +1 -1
  15. {alaea-2.3.4 → alaea-2.3.6}/setup.py +1 -1
  16. {alaea-2.3.4 → alaea-2.3.6}/Alaea/Pyshell/RS_File.py +0 -0
  17. {alaea-2.3.4 → alaea-2.3.6}/Alaea/Pyshell/__init__.py +0 -0
  18. {alaea-2.3.4 → alaea-2.3.6}/Alaea/S_DateTime.py +0 -0
  19. {alaea-2.3.4 → alaea-2.3.6}/Alaea/SameAsMATLAB/CoorTrans.py +0 -0
  20. {alaea-2.3.4 → alaea-2.3.6}/Alaea/SameAsMATLAB/__init__.py +0 -0
  21. {alaea-2.3.4 → alaea-2.3.6}/Alaea/SameAsMATLAB/calculatefuns.py +0 -0
  22. {alaea-2.3.4 → alaea-2.3.6}/Alaea/Standard/__init__.py +0 -0
  23. {alaea-2.3.4 → alaea-2.3.6}/Alaea/Standard/xr_ice.py +0 -0
  24. {alaea-2.3.4 → alaea-2.3.6}/Alaea/Standard/xr_wave.py +0 -0
  25. {alaea-2.3.4 → alaea-2.3.6}/Alaea/__init__.py +0 -0
  26. {alaea-2.3.4 → alaea-2.3.6}/Alaea/commonCode.py +0 -0
  27. {alaea-2.3.4 → alaea-2.3.6}/Alaea/cprintfs.py +0 -0
  28. {alaea-2.3.4 → alaea-2.3.6}/Alaea/exutils/Interp/Interp_g2s.py +0 -0
  29. {alaea-2.3.4 → alaea-2.3.6}/Alaea/exutils/Interp/Interp_s2g.py +0 -0
  30. {alaea-2.3.4 → alaea-2.3.6}/Alaea/exutils/Interp/__init__.py +0 -0
  31. {alaea-2.3.4 → alaea-2.3.6}/Alaea/exutils/__init__.py +0 -0
  32. {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/__init__.py +0 -0
  33. {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/fvcom_class.py +0 -0
  34. {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/fvcom_image.py +0 -0
  35. {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/fvcom_load.py +0 -0
  36. {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/fvcom_tools.py +0 -0
  37. {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/writeFile.py +0 -0
  38. {alaea-2.3.4 → alaea-2.3.6}/Alaea/read_conf.py +0 -0
  39. {alaea-2.3.4 → alaea-2.3.6}/Alaea/server_info.py +0 -0
  40. {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/P_params.py +0 -0
  41. {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/__init__.py +0 -0
  42. {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/download_check.py +0 -0
  43. {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/geo_distance.py +0 -0
  44. {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/kml2shp.py +0 -0
  45. {alaea-2.3.4 → alaea-2.3.6}/Alaea/warningTools/__init__.py +0 -0
  46. {alaea-2.3.4 → alaea-2.3.6}/Alaea.egg-info/dependency_links.txt +0 -0
  47. {alaea-2.3.4 → alaea-2.3.6}/Alaea.egg-info/requires.txt +0 -0
  48. {alaea-2.3.4 → alaea-2.3.6}/Alaea.egg-info/top_level.txt +0 -0
  49. {alaea-2.3.4 → alaea-2.3.6}/MANIFEST.in +0 -0
  50. {alaea-2.3.4 → alaea-2.3.6}/README.md +0 -0
  51. {alaea-2.3.4 → alaea-2.3.6}/run_twine.py +0 -0
  52. {alaea-2.3.4 → alaea-2.3.6}/setup.cfg +0 -0
Binary file
@@ -15,6 +15,8 @@
15
15
  **/*.jpg filter=lfs diff=lfs merge=lfs -binary
16
16
  **/*.nml filter=lfs diff=lfs merge=lfs -binary
17
17
  **/*.xlsx filter=lfs diff=lfs merge=lfs -binary
18
+ **/*.nc filter=lfs diff=lfs merge=lfs -binary
19
+ **/*.mat filter=lfs diff=lfs merge=lfs -binary
18
20
  **/*.mhtml filter=lfs diff=lfs merge=lfs -binary linguist-language=HTML
19
21
  **/*.mlx filter=lfs diff=lfs merge=lfs -binary linguist-language=MATLAB
20
22
  **/*.md linguist-language=Markdown
@@ -1,2 +1,3 @@
1
1
  ../../Project
2
2
  **/__pycache__
3
+ **/.idea
@@ -402,31 +402,31 @@ class Blog:
402
402
  return self._logger
403
403
 
404
404
  def __write_all_log(self):
405
- all_logger_handler = self.__init_logger_handler(self.all_log_filename, Rotating=self.Rotating, when=self.when,
406
- interval=self.interval) # 收集所有日志文件
405
+ all_logger_handler = self.__init_logger_handler(self.all_log_filename, Rotating=self.Rotating,
406
+ when=self.when, interval=self.interval) # 收集所有日志文件
407
407
  self.__set_log_formatter(all_logger_handler) # 设置日志输出格式-all日志文件
408
408
  self.__set_log_handler(all_logger_handler, level=logging.DEBUG) # 设置handler级别并添加到logger收集器
409
- self.__close_log_handler(all_logger_handler) # 关闭handler
409
+ # self.__close_log_handler(all_logger_handler) # 关闭handler
410
410
 
411
411
  def __write_error_plus_log(self):
412
412
  error_logger_handler = self.__init_logger_handler(self.bug_log_filename, Rotating=self.Rotating,
413
413
  when=self.when, interval=self.interval) # 收集错误日志信息文件
414
414
  self.__set_log_formatter(error_logger_handler) # 设置日志输出格式-error日志文件
415
415
  self.__set_log_handler(error_logger_handler, level=logging.ERROR) # 设置handler级别并添加到logger收集器
416
- self.__close_log_handler(error_logger_handler) # 关闭handler
416
+ # self.__close_log_handler(error_logger_handler) # 关闭handler
417
+
418
+ def __write_single_log(self, level=logging.ERROR, _level_log_name='all.log'): # 日志单独输出
419
+ single_logger_handler = self.__init_logger_handler(_level_log_name, Rotating=self.Rotating,
420
+ when=self.when, interval=self.interval) # 收集随机日志信息文件
421
+ self.__set_log_formatter(single_logger_handler) # 设置日志输出格式-single日志文件
422
+ self.__set_log_Filter(single_logger_handler, level) # 设置过滤器
423
+ # self.__close_log_handler(single_logger_handler) # 关闭handler
417
424
 
418
425
  def __print_log(self):
419
426
  console_handle = colorlog.StreamHandler() # 创建终端日志记录器handler,用于输出到控制台
420
427
  self.__set_color_formatter(console_handle, self.colors_config) # 设置输出格式-控制台
421
428
  self.__set_log_handler(console_handle, level=self.log_level) # 设置handler级别并添加到终端logger收集器
422
- self.__close_log_handler(console_handle) # 关闭handler
423
-
424
- def __write_single_log(self, level=logging.ERROR, _level_log_name='all.log'): # 日志单独输出
425
- single_logger_handler = self.__init_logger_handler(_level_log_name, Rotating=self.Rotating, when=self.when,
426
- interval=self.interval) # 收集随机日志信息文件
427
- self.__set_log_formatter(single_logger_handler) # 设置日志输出格式-single日志文件
428
- self.__set_log_Filter(single_logger_handler, level) # 设置过滤器
429
- self.__close_log_handler(single_logger_handler) # 关闭handler
429
+ # self.__close_log_handler(console_handle) # 关闭handler
430
430
 
431
431
  def __rm_single_FileHandler(self): # 删除日志文件并关闭日志记录器
432
432
  handlers = self._logger.handlers
@@ -434,6 +434,7 @@ class Blog:
434
434
  if isinstance(handler, logging.FileHandler): # 找到文件处理器,进行删除操作
435
435
  self._logger.removeHandler(handler)
436
436
  handler.close() # 关闭文件处理器,释放资源
437
+ # 注意:handler 只能在移除/销毁时 close;不要在 addHandler 之后立刻 close,否则会导致日志不写入/轮换不稳定
437
438
 
438
439
  @property
439
440
  def logger(self) -> _BlogLogger:
@@ -9,33 +9,36 @@
9
9
  """
10
10
 
11
11
  """
12
+ from typing import Union
13
+
12
14
  import numpy as np
15
+ import xarray as xr
13
16
 
14
17
  from .. import new_filename
15
18
 
16
19
  __all__ = ['add_coors_attrs', 'add_lon_attrs', 'add_lat_attrs', 'add_time_attrs', 'add_TIME_attrs',
17
20
  'add_wind_attrs', 'add_temperature_attrs', 'add_precipitation_attrs', 'add_slp_attrs',
18
- 'add_wave_attrs', 'add_ice_attrs',
19
- 'fixed_encoding']
21
+ 'add_wave_attrs', 'add_ice_attrs', 'add_visibility_attrs',
22
+ 'fixed_encoding', 'calc_scale_offset', 'compress_scale_offset',
23
+ ]
20
24
 
21
25
 
22
- def add_coors_attrs(ds):
26
+ def add_coors_attrs(_ds):
23
27
  """
24
28
  给xarray.Dataset的坐标添加属性
25
- :param ds: xarray.Dataset
29
+ :param _ds: xarray.Dataset
26
30
  :return: xarray.Dataset
27
31
  """
28
- lon_name, lat_name, time_name, TIME_name = 'longitude', 'latitude', 'time', 'TIME'
29
- for i in ds.sizes.keys(): # ds.dims.keys(): --> FutureWarning
30
- lon_name = i if i in ['lon', 'longitude'] else 'longitude'
31
- lat_name = i if i in ['lat', 'latitude'] else 'latitude'
32
- time_name = i if i in ['time'] else 'time'
33
- TIME_name = i if i in ['TIME'] else 'TIME'
34
- add_lon_attrs(ds, lon_name)
35
- add_lat_attrs(ds, lat_name)
36
- add_time_attrs(ds, time_name)
37
- add_TIME_attrs(ds, TIME_name, dim_name='DateStr')
38
- return ds
32
+ lon_name = next((x for x in ['lon', 'longitude'] if x in _ds.dims), None)
33
+ lat_name = next((x for x in ['lat', 'latitude'] if x in _ds.dims), None)
34
+ time_name = next((x for x in ['time'] if x in _ds.dims), None)
35
+ TIME_name = next((x for x in ['TIME'] if x in _ds.dims), None)
36
+
37
+ add_lon_attrs(_ds, lon_name)
38
+ add_lat_attrs(_ds, lat_name)
39
+ add_time_attrs(_ds, time_name)
40
+ add_TIME_attrs(_ds, TIME_name, dim_name='DateStr')
41
+ return _ds
39
42
 
40
43
 
41
44
  def add_lon_attrs(_ds, lon_name):
@@ -120,10 +123,8 @@ def add_wind_attrs(_ds):
120
123
  :param _ds: xarray.Dataset
121
124
  :return: xarray.Dataset
122
125
  """
123
- U10_name, V10_name = 'U10', 'V10'
124
- for i in list(_ds.data_vars.keys()):
125
- U10_name = i if i in ['U10', 'u10'] else 'U10'
126
- V10_name = i if i in ['V10', 'v10'] else 'V10'
126
+ U10_name = next((x for x in ['wind_U10', 'U10', 'u10'] if x in _ds.data_vars), None)
127
+ V10_name = next((x for x in ['wind_V10', 'V10', 'v10'] if x in _ds.data_vars), None)
127
128
  _ds[U10_name].attrs = {
128
129
  'units': 'm s-1',
129
130
  'long_name': '10 meter U wind component',
@@ -136,6 +137,59 @@ def add_wind_attrs(_ds):
136
137
  }
137
138
  _ds[U10_name].encoding = dict(_FillValue=9.962100e+36, dtype='single', zlib=True)
138
139
  _ds[V10_name].encoding = dict(_FillValue=9.962100e+36, dtype='single', zlib=True)
140
+
141
+ if 'AGL_heights' in _ds.variables:
142
+ _ds['AGL_heights'].attrs = {
143
+ 'units': 'm',
144
+ 'long_name': 'Above Ground Level Height',
145
+ 'standard_name': 'height',
146
+ }
147
+ _ds['AGL_heights'].encoding = {'_FillValue': None}
148
+
149
+ if 'U_AGL' in _ds.variables and 'V_AGL' in _ds.variables:
150
+ _ds['U_AGL'].attrs = {
151
+ 'units': 'm s-1',
152
+ 'long_name': 'U-wind at AGL',
153
+ 'standard_name': 'eastward_wind',
154
+ }
155
+ _ds['V_AGL'].attrs = {
156
+ 'units': 'm s-1',
157
+ 'long_name': 'V-wind at AGL',
158
+ 'standard_name': 'northward_wind',
159
+ }
160
+ _ds['U_AGL'].encoding = dict(_FillValue=9.962100e+36, dtype='single', zlib=True)
161
+ _ds['V_AGL'].encoding = dict(_FillValue=9.962100e+36, dtype='single', zlib=True)
162
+
163
+ if 'MSL_heights' in _ds.variables:
164
+ _ds['MSL_heights'].attrs = {
165
+ 'units': 'm',
166
+ 'long_name': 'Mean Sea Level Height',
167
+ 'standard_name': 'height',
168
+ }
169
+ _ds['MSL_heights'].encoding = {'_FillValue': None}
170
+
171
+ if 'U_MSL' in _ds.variables and 'V_MSL' in _ds.variables:
172
+ _ds['U_MSL'].attrs = {
173
+ 'units': 'm s-1',
174
+ 'long_name': 'U-wind at MSL',
175
+ 'standard_name': 'eastward_wind',
176
+ }
177
+ _ds['V_MSL'].attrs = {
178
+ 'units': 'm s-1',
179
+ 'long_name': 'V-wind at MSL',
180
+ 'standard_name': 'northward_wind',
181
+ }
182
+ _ds['U_MSL'].encoding = dict(_FillValue=9.962100e+36, dtype='single', zlib=True)
183
+ _ds['V_MSL'].encoding = dict(_FillValue=9.962100e+36, dtype='single', zlib=True)
184
+
185
+ if 'WSPD10MAX' in _ds.variables:
186
+ _ds['WSPD10MAX'].attrs = {
187
+ 'units': 'm s-1',
188
+ 'long_name': 'Maximum 10m wind speed',
189
+ 'standard_name': 'maximum_10m_wind_speed',
190
+ }
191
+ _ds['WSPD10MAX'].encoding = dict(_FillValue=9.962100e+36, dtype='single', zlib=True)
192
+
139
193
  return _ds
140
194
 
141
195
 
@@ -145,9 +199,8 @@ def add_temperature_attrs(_ds):
145
199
  :param _ds: xarray.Dataset
146
200
  :return: xarray.Dataset
147
201
  """
148
- T2_name = 'temperature'
149
- for i in list(_ds.data_vars.keys()):
150
- T2_name = i if i in ['T2', 't2'] else T2_name
202
+ T2_name = next((x for x in ['temperature', 'T2', 't2'] if x in _ds.data_vars), None)
203
+
151
204
  _ds[T2_name].attrs = {
152
205
  'units': 'K',
153
206
  'long_name': 'temperature at 2m',
@@ -157,15 +210,31 @@ def add_temperature_attrs(_ds):
157
210
  return _ds
158
211
 
159
212
 
213
+ def add_visibility_attrs(_ds):
214
+ """
215
+ 给xarray.Dataset的能见度添加属性
216
+ :param _ds: xarray.Dataset
217
+ :return: xarray.Dataset
218
+ """
219
+ visibility_name = next((x for x in ['visibility', 'AFWA_VIS', 'VISIBILITY'] if x in _ds.data_vars), None)
220
+
221
+ _ds[visibility_name].attrs = {
222
+ 'units': 'm',
223
+ 'long_name': 'visibility',
224
+ 'standard_name': 'visibility'
225
+ }
226
+ _ds[visibility_name].encoding = dict(_FillValue=9.962100e+36, dtype='single', zlib=True)
227
+ return _ds
228
+
229
+
160
230
  def add_precipitation_attrs(_ds):
161
231
  """
162
232
  给xarray.Dataset的降水添加属性
163
233
  :param _ds: xarray.Dataset
164
234
  :return: xarray.Dataset
165
235
  """
166
- precipitation_name = 'precipitation'
167
- for i in list(_ds.data_vars.keys()):
168
- precipitation_name = i if i in ['tp', 'TP'] else precipitation_name
236
+ precipitation_name = next((x for x in ['precipitation', 'tp', 'TP', 'Precipitation'] if x in _ds.data_vars), None)
237
+
169
238
  _ds[precipitation_name].attrs = {
170
239
  'units': 'mm',
171
240
  'long_name': 'precipitation, positive for ocean gaining water',
@@ -181,9 +250,8 @@ def add_slp_attrs(_ds):
181
250
  :param _ds: xarray.Dataset
182
251
  :return: xarray.Dataset
183
252
  """
184
- slp_name = 'slp'
185
- for i in list(_ds.data_vars.keys()):
186
- slp_name = i if i in ['slp', 'SLP'] else slp_name
253
+ slp_name = next((x for x in ['slp', 'SLP'] if x in _ds.data_vars), None)
254
+
187
255
  _ds[slp_name].attrs = {
188
256
  'units': 'Pa',
189
257
  'long_name': 'sea level pressure',
@@ -199,24 +267,20 @@ def add_wave_attrs(_ds):
199
267
  :param _ds: xarray.Dataset
200
268
  :return: xarray.Dataset
201
269
  """
202
- swh_name, mwp_name, mwd_name, mwl_name = 'swh', 'mwp', 'mwd', 'mwl'
203
- pp1d_name = 'pp1d'
204
- shww_name, shts_name, mdww_name, mdts_name = 'shww', 'shts', 'mdww', 'mdts'
205
- mpww_name, mpts_name, hmax_name = 'mpww', 'mpts', 'hmax'
206
-
207
- for i in list(_ds.data_vars.keys()):
208
- swh_name = i if i in ['swh'] else swh_name
209
- mwp_name = i if i in ['mwp'] else mwp_name
210
- mwd_name = i if i in ['mwd'] else mwd_name
211
- mwl_name = i if i in ['mwl'] else mwl_name
212
- pp1d_name = i if i in ['pp1d'] else pp1d_name
213
- shww_name = i if i in ['shww'] else shww_name
214
- shts_name = i if i in ['shts'] else shts_name
215
- mdww_name = i if i in ['mdww'] else mdww_name
216
- mdts_name = i if i in ['mdts'] else mdts_name
217
- mpww_name = i if i in ['mpww'] else mpww_name
218
- mpts_name = i if i in ['mpts'] else mpts_name
219
- hmax_name = i if i in ['hmax'] else hmax_name
270
+ swh_name = next((x for x in ['swh', 'SWH'] if x in _ds.data_vars), None) # 有效波高
271
+ mwp_name = next((x for x in ['mwp', 'MWP'] if x in _ds.data_vars), None) # 平均波周期
272
+ mwd_name = next((x for x in ['mwd', 'MWD'] if x in _ds.data_vars), None) # 平均波向
273
+ mwl_name = next((x for x in ['mwl', 'MWL'] if x in _ds.data_vars), None) # 平均波长
274
+
275
+ shww_name = next((x for x in ['shww', 'SHWW'] if x in _ds.data_vars), None) # 风浪有效波高
276
+ shts_name = next((x for x in ['shts', 'SHTS'] if x in _ds.data_vars), None) # 涌浪有效波高
277
+ mdww_name = next((x for x in ['mdww', 'MDWW'] if x in _ds.data_vars), None) # 风浪平均波向
278
+ mdts_name = next((x for x in ['mdts', 'MDTS'] if x in _ds.data_vars), None) # 涌浪平均波向
279
+ mpww_name = next((x for x in ['mpww', 'MPWW'] if x in _ds.data_vars), None) # 风浪平均波周期
280
+ mpts_name = next((x for x in ['mpts', 'MPTS'] if x in _ds.data_vars), None) # 涌浪平均波周期
281
+
282
+ pp1d_name = next((x for x in ['pp1d', 'PP1D'] if x in _ds.data_vars), None) # 峰值周期
283
+ hmax_name = next((x for x in ['hmax', 'HMAX'] if x in _ds.data_vars), None) # 最大波高
220
284
 
221
285
  if swh_name in _ds:
222
286
  _ds[swh_name].attrs = {
@@ -329,12 +393,8 @@ def add_ice_attrs(_ds):
329
393
  :param _ds: xarray.Dataset
330
394
  :return: xarray.Dataset
331
395
  """
332
- aice_name = 'aice'
333
- tice_name = 'tice'
334
-
335
- for i in list(_ds.data_vars.keys()):
336
- aice_name = i if i in ['aice'] else aice_name
337
- tice_name = i if i in ['tice'] else tice_name
396
+ aice_name = next((x for x in ['aice'] if x in _ds.data_vars), None)
397
+ tice_name = next((x for x in ['tice'] if x in _ds.data_vars), None)
338
398
 
339
399
  if aice_name in _ds:
340
400
  _ds[aice_name].attrs = {
@@ -370,3 +430,57 @@ def fixed_encoding(_ds, **kwargs):
370
430
  _ds.encoding['contiguous'] = kwargs.get('contiguous', True)
371
431
  _ds.encoding.update(kwargs)
372
432
  return _ds
433
+
434
+
435
+ def calc_scale_offset(
436
+ _data: np.ndarray,
437
+ _dtype=np.int16
438
+ ) -> tuple[float, float]:
439
+ """
440
+ 计算scale_factor和add_offset
441
+ :param _data: 数据数组
442
+ :param _dtype: 压缩后的数据类型,默认为int16
443
+ :return: scale_factor, add_offset
444
+ """
445
+ valid_min = np.nanmin(_data)
446
+ valid_max = np.nanmax(_data)
447
+
448
+ if valid_min == valid_max:
449
+ scale_factor = 1.0
450
+ add_offset = valid_min
451
+ else:
452
+ scale_factor = (valid_max - valid_min) / (np.iinfo(_dtype).max - np.iinfo(_dtype).min)
453
+ add_offset = valid_min - np.iinfo(_dtype).min * scale_factor
454
+
455
+ return scale_factor, add_offset
456
+
457
+
458
+ def compress_scale_offset(
459
+ _ds: xr.Dataset,
460
+ _var_name: Union[str, list[str]] = None,
461
+ _dtype=np.int16
462
+ ) -> xr.Dataset:
463
+ """
464
+ 给xarray.Dataset的变量添加压缩属性
465
+ :param _ds: xarray.Dataset
466
+ :param _var_name: 变量名或变量名列表
467
+ :param _dtype: 压缩后的数据类型,默认为int16
468
+ :return: xarray.Dataset
469
+ """
470
+
471
+ if _var_name is None:
472
+ _var_name = list(_ds.data_vars.keys())
473
+ elif isinstance(_var_name, str):
474
+ _var_name = [_var_name]
475
+
476
+ for var in _var_name:
477
+ if var in _ds.data_vars:
478
+ data = _ds[var].data
479
+ scale_factor, add_offset = calc_scale_offset(data, _dtype=_dtype)
480
+
481
+ _ds[var].encoding['dtype'] = _dtype
482
+ _ds[var].encoding['scale_factor'] = scale_factor
483
+ _ds[var].encoding['add_offset'] = add_offset
484
+ _ds[var].encoding['_FillValue'] = np.iinfo(_dtype).min
485
+
486
+ return _ds
@@ -0,0 +1,11 @@
1
+ #!/Users/christmas/opt/anaconda3/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ # 日期 : 2023/4/7 23:14
4
+ # 作者 : Christmas
5
+ # 邮箱 : 273519355@qq.com
6
+ # 项目 : Project
7
+ # 版本 : python 3
8
+ # 摘要 :
9
+ """
10
+
11
+ """
@@ -0,0 +1,11 @@
1
+ #!/Users/christmas/opt/anaconda3/bin/python3
2
+ # -*- coding: utf-8 -*-
3
+ # 日期 : 2023/4/7 23:52
4
+ # 作者 : Christmas
5
+ # 邮箱 : 273519355@qq.com
6
+ # 项目 : Project
7
+ # 版本 : python 3
8
+ # 摘要 :
9
+ """
10
+
11
+ """
@@ -0,0 +1,11 @@
1
+ #!/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # 日期 : 2026/3/9 14:43
4
+ # 作者 : Christmas
5
+ # 邮箱 : 273519355@qq.com
6
+ # 项目 : Project
7
+ # 版本 : python 3
8
+ # 摘要 :
9
+ """
10
+
11
+ """
@@ -0,0 +1,405 @@
1
+ #!/usr/bin/env python3
2
+ # -*- coding: utf-8 -*-
3
+ # 日期 : 2026/3/9 14:44
4
+ # 作者 : Christmas
5
+ # 邮箱 : 273519355@qq.com
6
+ # 项目 : Project
7
+ # 版本 : python 3
8
+ # 摘要 :
9
+ """
10
+ 1. 先找出“需要被填补”的点(通常是海陆交界处的水点、空值点、mask 点)
11
+ 2. 再为这些点找到周围最近的 K 个邻居
12
+ 3. 过滤掉“周围有效邻居太少”的点
13
+ 4. 把可用邻居的值做平均,回填到目标点中
14
+
15
+ 这里的“erosion(侵蚀)”更接近一种海岸线附近的掩膜修正 / 邻域填补:
16
+ """
17
+
18
+ import itertools
19
+ import time
20
+ from collections import deque
21
+
22
+ import matplotlib.pyplot as plt
23
+ import numpy as np
24
+ import scipy.spatial as spt
25
+ import xarray as xr
26
+ from netCDF4 import Dataset
27
+ from scipy.io import loadmat
28
+
29
+ from Alaea.Blogging import Blog
30
+ from Alaea.tools.Read import read_json, write_json
31
+
32
+ __all__ = [
33
+ 'erosion_cal_id',
34
+ 'erosion_via_id',
35
+ 'LOGGER',
36
+ ]
37
+
38
+ BLOG = Blog('erosion_coastline')
39
+ BLOG.setup_Blog(log_level=BLOG.DEBUG)
40
+ LOGGER = BLOG.logger
41
+
42
+
43
+ def erosion_cal_id(
44
+ g_lon,
45
+ g_lat,
46
+ value,
47
+ k,
48
+ judge_num,
49
+ method='np.nan',
50
+ ):
51
+ """
52
+ 计算“哪些点需要被回填”,以及“每个点应该参考哪些邻居点”。
53
+
54
+ 这个函数不直接修改 value,而是先生成一个索引说明书 `_weight`:
55
+ - change_lat / change_lon:哪些点要被修改
56
+ - value_lat / value_lon:每个待修改点应当使用哪些邻居点的值
57
+
58
+ 整体流程:
59
+ 1. 把规则网格展开成二维坐标点云
60
+ 2. 用 KDTree 为每个格点查找最近的 k 个邻居
61
+ 3. 根据 method 判断哪些点是“待处理点”
62
+ 4. 再检查它们的 k 个邻居里是否有足够多的有效点
63
+ 5. 只保留满足 judge_num 条件的点,返回后续填补所需索引
64
+
65
+ 参数说明:
66
+ - k:每个目标点向周围找多少个最近邻
67
+ - judge_num:最少需要多少个“有效邻居”才允许回填
68
+ - method='np.nan':把 NaN 看成待处理点
69
+ - method='nc.mask':把 masked array 中 mask=True 的点看成待处理点
70
+ - method='txt.mask':把值为 0 的点看成待处理点(常用于陆海 mask)
71
+
72
+ :param g_lon: 一维格点经度
73
+ :param g_lat: 一维格点纬度
74
+ :param value: 二维格点值
75
+ :param k: 寻找近邻点的个数
76
+ :param judge_num: 至少需要多少个有效邻居点才保留该目标点
77
+ :param method: 判断目标点类型的方法
78
+ np.nan:直接在 value 中用 NaN 表示无效点
79
+ nc.mask:NetCDF masked array,用 mask 属性判断无效点
80
+ txt.mask:文本/掩膜型数据,0 表示水点,非 0 表示陆点
81
+ :return: 字典,描述待修改点及其邻居索引
82
+ """
83
+ # 网格大小(这里只是记录下来,后面真正高频使用的是 lon_number)
84
+ lat_number = len(g_lat)
85
+ # 一行有多少个经度格点,用于后面把一维展开索引还原成二维行列索引
86
+ lon_number = len(g_lon)
87
+ # 例如 g_lon.shape=(nlon,), g_lat.shape=(nlat,)
88
+ # meshgrid 后得到两个 (nlat, nlon) 的二维数组,分别存放每个格点的经纬度
89
+ LOGGER.debug(f'开始建立Kdtree,格点数量约为 {lat_number} * {lon_number},每个点找 {k} 个邻居,至少需要 {judge_num} 个有效邻居')
90
+ lon_mesh, lat_mesh = np.meshgrid(g_lon, g_lat)
91
+ # 后面 KDTree 就是在这个二维坐标集合上做最近邻搜索
92
+ point = np.column_stack((lon_mesh.ravel(), lat_mesh.ravel()))
93
+ # 下面两个 deque 用来保存所有“候选邻居点”的二维索引
94
+ fp_lon = deque([])
95
+ fp_lat = deque([])
96
+
97
+ # 建立 KDTree:适合大量最近邻查询
98
+ # 这里 point 里装的是所有格点坐标,所以相当于“让每个格点去找离自己最近的 k 个格点”
99
+ ckt = spt.cKDTree(point) # 用C写的查找类,执行速度更快
100
+ # 这里不再逐点循环,而是一次性把所有格点的近邻都查出来
101
+ LOGGER.debug('Kdtree建立完毕,开始查找每个格点的近邻点')
102
+
103
+ # 对所有格点一次性查询最近邻:
104
+ # distances.shape 约为 (n_points, k)
105
+ # sequences.shape 约为 (n_points, k)
106
+ # sequences 里保存的是最近邻在 point 数组中的一维编号
107
+ distances, sequences = ckt.query(point, k)
108
+
109
+ # point 中每个元素对应一个展开后的一维索引 [0, 1, 2, ...]
110
+ idx_all = np.arange(point.shape[0])
111
+ # 把展开索引还原回二维索引:
112
+ # 行号 = index // 每行列数
113
+ lat_i_all = idx_all // lon_number
114
+ # 列号 = index % 每行列数
115
+ lon_i_all = idx_all % lon_number
116
+
117
+ # 先找出哪些格点属于“待处理点”
118
+ # 不同数据源中,水点/无效点/边界点的表达方式不同,所以这里分三种判断方法
119
+ if method == 'np.nan':
120
+ mask = np.isnan(value[lat_i_all, lon_i_all])
121
+ elif method == 'nc.mask':
122
+ mask = value.mask[lat_i_all, lon_i_all]
123
+ elif method == 'txt.mask':
124
+ mask = (value[lat_i_all, lon_i_all] == 0)
125
+ else:
126
+ mask = np.zeros_like(lat_i_all, dtype=bool)
127
+
128
+ # 只保留那些“需要处理”的点在 point 里的编号
129
+ valid_idx = idx_all[mask]
130
+
131
+ # 取出这些目标点各自对应的 k 个最近邻编号
132
+ neigh = sequences[valid_idx]
133
+
134
+ # 展平后,后面统一换算成二维索引
135
+ neigh = neigh.reshape(-1)
136
+
137
+ # 邻居点的一维编号 -> 二维行列编号
138
+ lat_j = neigh // lon_number
139
+ lon_j = neigh % lon_number
140
+
141
+ # 这里会把所有目标点的 k 个邻居顺序拼在一起
142
+ # 因此后面需要再按 k reshape 回去,恢复“每 k 个属于同一个目标点”的结构
143
+ fp_lat.extend(lat_j.tolist())
144
+ fp_lon.extend(lon_j.tolist())
145
+ LOGGER.debug('所有格点的近邻点查找完毕,开始获取行列索引')
146
+
147
+ # 这里 fp_lat / fp_lon 已经是索引,不再需要 find_index
148
+ lat_id = list(fp_lat)
149
+ lon_id = list(fp_lon)
150
+
151
+ LOGGER.debug('行列索引获取完毕,开始判断每个目标点的邻居里有效点数量')
152
+
153
+ # 根据不同类型的数据,进一步判断:
154
+ # 某个目标点的 k 个邻居里,是否有足够多的有效点可以拿来回填
155
+ lonli_arr = np.array([0])
156
+ latli_arr = np.array([0])
157
+ if method == 'np.nan':
158
+ latli_arr, lonli_arr = judge_nan_npnan(lat_id, lon_id, value, k, judge_num)
159
+ elif method =='nc.mask':
160
+ latli_arr, lonli_arr = judge_nan_ncmask(lat_id, lon_id, value, k, judge_num)
161
+ elif method == 'txt.mask':
162
+ latli_arr, lonli_arr = judge_nan_txtmask(lat_id, lon_id, value, k, judge_num)
163
+
164
+ LOGGER.debug('有效邻居点数量判断完毕,开始生成索引')
165
+
166
+ # reshape 之后,每一行对应“一个目标点 + 它的 k-1 或 k 个邻居”
167
+ # 约定每行第 1 列是目标点本身,后面各列是参考邻居
168
+ _change_lat = latli_arr[:, 0].tolist()
169
+ _change_lon = lonli_arr[:, 0].tolist()
170
+ # 每行剩下的就是用来赋值的
171
+ _value_lat = latli_arr[:, 1:].tolist()
172
+ _value_lon = lonli_arr[:, 1:].tolist()
173
+
174
+ # 组织成统一的索引字典,供 erosion_via_id 直接使用
175
+ _weight = {
176
+ 'change_lat': _change_lat,
177
+ 'change_lon': _change_lon,
178
+ 'value_lat': _value_lat,
179
+ 'value_lon': _value_lon,
180
+ }
181
+ return _weight
182
+
183
+
184
+ def judge_nan_ncmask(latli, lonli, value, k, judge_num):
185
+ """
186
+ 针对 masked array(mask=True 表示无效点)筛选可参与回填的目标点。
187
+
188
+ 思路:
189
+ - latli / lonli 当前是展平后的邻居索引序列
190
+ - 每 k 个索引对应 1 个目标点及其 k 个最近邻
191
+ - 如果这 k 个点里“未被 mask 的有效点”数量不少于 judge_num,
192
+ 就保留这一组索引
193
+ """
194
+ # 先转成数组,便于 reshape 成 [目标点数, k] 的二维结构
195
+ latli_1 = np.array(latli)
196
+ lonli_1 = np.array(lonli)
197
+ # 每一行表示一个目标点对应的 k 个最近邻索引
198
+ latli_2 = np.reshape(latli_1, [-1, k])
199
+ lonli_2 = np.reshape(lonli_1, [-1, k])
200
+ # mask=True 说明该位置是无效/被遮罩的点
201
+ mask_arr = value.mask
202
+ # 取出每组 k 个邻居的 mask 情况
203
+ test_arr = mask_arr[latli_2.tolist(), lonli_2.tolist()] # 判断找出来的点里有哪些需要处理
204
+
205
+ # 统计每组中“无效点”的数量
206
+ sum_arr = np.sum(test_arr, axis=1) #
207
+
208
+ # 无效点数 <= k - judge_num,等价于有效点数 >= judge_num
209
+ select_id = np.where(sum_arr <= k-judge_num)[0].tolist()
210
+
211
+ latli_r = latli_2[select_id[:]]
212
+ lonli_r = lonli_2[select_id[:]]
213
+
214
+ return latli_r, lonli_r
215
+
216
+
217
+ def judge_nan_npnan(latli, lonli, value, k, judge_num):
218
+ """
219
+ 针对普通 ndarray 中 NaN 表示无效值的情况,筛选可参与回填的目标点。
220
+
221
+ 逻辑与 judge_nan_ncmask 一样,只不过这里通过 np.isnan(value)
222
+ 判断某个点是否无效。
223
+ """
224
+ # 每 k 个索引是一组,后面要恢复成二维结构
225
+ latli_1 = np.array(latli)
226
+ lonli_1 = np.array(lonli)
227
+ # reshape 后每一行对应 1 个目标点的 k 个邻居
228
+ latli_2 = np.reshape(latli_1, [-1, k])
229
+ lonli_2 = np.reshape(lonli_1, [-1, k])
230
+ # NaN 为 True,表示该点不能直接作为有效值参与平均
231
+ mask_arr = np.isnan(value)
232
+
233
+ # 检查每组 k 个邻居中哪些位置是 NaN
234
+ test_arr = mask_arr[latli_2.tolist(), lonli_2.tolist()] # 判断找出来的点里有哪些需要处理
235
+ sum_arr = np.sum(test_arr, axis=1) #
236
+
237
+ # 只有有效值数量 >= judge_num 的目标点,才保留下来
238
+ select_id = np.where(sum_arr <= k-judge_num)[0].tolist()
239
+
240
+ latli_r = latli_2[select_id[:]]
241
+ lonli_r = lonli_2[select_id[:]]
242
+
243
+ return latli_r, lonli_r
244
+
245
+
246
+ def judge_nan_txtmask(latli, lonli, value, k, judge_num):
247
+ """
248
+ 针对文本/掩膜型数据(0 表示水点,非 0 表示陆点)筛选可参与回填的目标点。
249
+
250
+ 这里与前两个函数不同:
251
+ - 前两个函数统计的是“无效点个数”
252
+ - 这里直接统计邻居值之和,本质上是在数一组邻居里有多少个陆点(值为 1)
253
+
254
+ 当一组邻居中陆点数量 >= judge_num 时,保留该组。
255
+ """
256
+ # 同样先恢复成 [目标点数, k] 的结构
257
+ latli_1 = np.array(latli)
258
+ lonli_1 = np.array(lonli)
259
+ latli_2 = np.reshape(latli_1, [-1, k])
260
+ lonli_2 = np.reshape(lonli_1, [-1, k])
261
+
262
+ # 取出每组邻居的实际值;在 mask 中通常 0=水,1=陆
263
+ test_arr = value[latli_2.tolist(), lonli_2.tolist()] # 判断找出来的点里有哪些需要处理
264
+
265
+ # 每组求和,相当于统计这一组里有多少个陆点
266
+ sum_arr = np.sum(test_arr, axis=1) #
267
+
268
+ # 只有陆点数达到阈值,才把该目标点纳入后续腐蚀/填补
269
+ select_id = np.where(sum_arr >= judge_num)[0].tolist()
270
+
271
+ latli_r = latli_2[select_id[:]]
272
+ lonli_r = lonli_2[select_id[:]]
273
+
274
+ return latli_r, lonli_r
275
+
276
+
277
+ def erosion_via_id(_weight, value):
278
+ """
279
+ 根据 erosion_cal_id 生成的索引说明书,对目标点执行回填。
280
+
281
+ 核心思想:
282
+ - 对每个待修改点,取出它对应邻居点的值
283
+ - 对这些邻居值做平均(np.nanmean 会自动跳过 NaN)
284
+ - 再把平均值写回目标点
285
+
286
+ 支持两种输入:
287
+ - 2D: [lat, lon]
288
+ - 3D: [time, lat, lon]
289
+
290
+ :param _weight: erosion_cal_id 返回的字典
291
+ :param value: 二维或三维格点值
292
+ :return: 回填后的新数组
293
+ """
294
+
295
+ # 要被修改的目标点索引
296
+ _change_lat = _weight['change_lat']
297
+ _change_lon = _weight['change_lon']
298
+ _value_lat = _weight['value_lat']
299
+ _value_lon = _weight['value_lon']
300
+
301
+ # 拷贝一份,避免直接改原数组
302
+ new = value.copy()
303
+ # 3D 情况:常见于 [time, lat, lon]
304
+ if value.ndim == 3:
305
+ for i in range(value.shape[0]):
306
+ # 取出当前时刻下,每个目标点对应的一组邻居值
307
+ replace = value[i, _value_lat, _value_lon]
308
+ # 对每组邻居求平均,得到每个目标点的新值
309
+ replace = np.nanmean(replace, axis=1)
310
+ # 写回当前时刻的目标点位置
311
+ new[i, _change_lat, _change_lon] = replace[:]
312
+
313
+ # 2D 情况:直接对一个平面进行处理
314
+ elif value.ndim == 2:
315
+ # 取出每个目标点对应的一组邻居值
316
+ replace = value[_value_lat, _value_lon]
317
+ # 每组邻居求平均
318
+ replace = np.nanmean(replace, axis=1)
319
+ # 把平均值写回目标点
320
+ new[_change_lat, _change_lon] = replace[:]
321
+
322
+ return new
323
+
324
+
325
+ def example_cal_weight(methoood): # sourcery skip: extract-duplicate-method
326
+ """
327
+ 示例函数:演示如何针对不同类型的数据源计算 weight,并执行一次回填。
328
+
329
+ methoood 含义:
330
+ 1 -> txt.mask:外部 mask 文件中 0/1 表示海陆
331
+ 2 -> nc.mask:NetCDF masked array,用 mask 属性判断无效点
332
+ 3 -> np.nan:直接在数据中用 NaN 表示无效点
333
+ """
334
+
335
+ if methoood == 1:
336
+ mask_wrf = loadmat('/Users/christmas/Documents/Code/Project/A-pypi/Alaea/Alaea/baysalt/Forage/erosion_data/mask_wrf.mat')['mask_wrf']
337
+ ds = xr.open_dataset('/Users/christmas/Documents/Code/Project/A-pypi/Alaea/Alaea/baysalt/Forage/erosion_data/erosion_ww3.nc') # ww3直接输出的nc,由于海浪可能算出来nan,所以用地形文件判断nan
338
+ lons = ds['longitude'][:].data
339
+ lats = ds['latitude'][:].data
340
+ viss = ds['hs'][:].data
341
+ ds.close()
342
+ weight_1 = erosion_cal_id(lons, lats, mask_wrf, 16, 3, method='txt.mask')
343
+ mask_wrf_1 = erosion_via_id(weight_1, mask_wrf)
344
+ weight_2 = erosion_cal_id(lons, lats, mask_wrf_1, 24, 3, method='txt.mask')
345
+ new_hs = erosion_via_id(weight_1, viss)
346
+ new_hs_2 = erosion_via_id(weight_2, new_hs)
347
+
348
+ plt.contourf(viss[0, 500:800, 1200:1700], cmap='jet')
349
+ plt.colorbar()
350
+ plt.show()
351
+ plt.contourf(new_hs[0, 500:800, 1200:1700], cmap='jet')
352
+ plt.colorbar()
353
+ plt.show()
354
+ plt.contourf(new_hs_2[0, 500:800, 1200:1700], cmap='jet')
355
+ plt.colorbar()
356
+ plt.show()
357
+
358
+ elif methoood == 2:
359
+ f = Dataset('/Users/christmas/Documents/Code/Project/A-pypi/Alaea/Alaea/baysalt/Forage/erosion_data/erosion_xujie.nc') # 杰哥写的nc,用mask的属性判断nan,data里是--,mask里是True
360
+ lons = f.variables['lon'][:]
361
+ lats = f.variables['lat'][:]
362
+ viss = f.variables['hs'][:]
363
+ f.close()
364
+ weight = erosion_cal_id(lons, lats, viss, 16, 3, method='nc.mask')
365
+
366
+ new_hs = erosion_via_id(weight, viss)
367
+
368
+ plt.contourf(viss[:, :], cmap='jet')
369
+ plt.colorbar()
370
+ plt.show()
371
+ plt.contourf(new_hs[:, :], cmap='jet')
372
+ plt.colorbar()
373
+ plt.show()
374
+
375
+ elif methoood == 3:
376
+ # f = Dataset('/Users/christmas/Documents/Code/Project/A-pypi/Alaea/Alaea/baysalt/Forage/erosion_data/erosion_Post_ww3.nc') # Postprocess_WW3产生的nc,mask为False,data里是nan
377
+ ds = xr.open_dataset('/Users/christmas/Documents/Code/Project/A-pypi/Alaea/Alaea/baysalt/Forage/erosion_data/erosion_Post_ww3.nc')
378
+ lons = ds['longitude'][900:1200].data
379
+ lats = ds['latitude'][300:500].data
380
+ viss = ds['swh'][:, 300:500, 900:1200].data
381
+ ds.close()
382
+ # f.close()
383
+ weight = erosion_cal_id(lons, lats, viss[0, :, :], 16, 3, method='np.nan')
384
+
385
+ new_hs = erosion_via_id(weight, viss)
386
+
387
+ plt.contourf(viss[0, :, :], cmap='jet')
388
+ plt.colorbar()
389
+ plt.show()
390
+ plt.contourf(new_hs[0, :, :], cmap='jet')
391
+ plt.colorbar()
392
+ plt.show()
393
+
394
+ else:
395
+ raise ValueError()
396
+
397
+
398
+ if __name__ == '__main__':
399
+ # 简单计时,看看一次示例运行要多久
400
+ start = time.perf_counter()
401
+ example_cal_weight(1)
402
+ example_cal_weight(2)
403
+ example_cal_weight(3)
404
+ print(f'耗时: {time.perf_counter() - start}秒')
405
+ # test()
@@ -11,10 +11,18 @@ processBar such as: ftp sftp
11
11
  """
12
12
  import datetime
13
13
  import time
14
+ from typing import Union
14
15
 
15
16
  import numpy as np
16
17
 
17
- __all__ = ['translate_byte', 'LowSpeed_Error', 'SftpProcessbar', 'FtpProcessbar', 'FtpHtmlProcessbar']
18
+ __all__ = [
19
+ 'translate_byte',
20
+ 'translate_seconds',
21
+ 'LowSpeed_Error',
22
+ 'SftpProcessbar',
23
+ 'FtpProcessbar',
24
+ 'FtpHtmlProcessbar',
25
+ ]
18
26
 
19
27
 
20
28
  def translate_byte(B):
@@ -39,6 +47,22 @@ def translate_byte(B):
39
47
  return '{:.2f} TB'.format(B / TB)
40
48
 
41
49
 
50
+ def translate_seconds(_seconds: Union[int, float]) -> str:
51
+ """
52
+ :param _seconds: 秒数
53
+ :return: 转换后的时间字符串
54
+ """
55
+ _seconds = float(_seconds)
56
+ if _seconds < 60:
57
+ return f"{_seconds:.2f} seconds"
58
+ elif 60 <= _seconds < 3600:
59
+ return f"{_seconds / 60:.2f} minutes"
60
+ elif 3600 <= _seconds < 86400:
61
+ return f"{_seconds / 3600:.2f} hours"
62
+ else:
63
+ return f"{_seconds / 86400:.2f} days"
64
+
65
+
42
66
  class LowSpeed_Error(Exception):
43
67
  """
44
68
  :param value: 低速异常
@@ -11,6 +11,12 @@
11
11
  """
12
12
  import json
13
13
 
14
+ __all__ = [
15
+ 'read_json',
16
+ 'write_json',
17
+ 'unique_list'
18
+ ]
19
+
14
20
 
15
21
  def read_json(_filepath):
16
22
  """
@@ -22,6 +28,24 @@ def read_json(_filepath):
22
28
  data = json.load(f)
23
29
  return data
24
30
 
31
+ def write_json(
32
+ _filepath,
33
+ _data,
34
+ _encoding='utf-8',
35
+ _ensure_ascii=False,
36
+ _indent=4
37
+ ):
38
+ """
39
+ 写入JSON文件
40
+ :param _filepath: JSON文件路径
41
+ :param _data: 要写入的数据
42
+ :param _encoding: 文件编码,默认为'utf-8'
43
+ :param _ensure_ascii: 是否确保ASCII编码,默认为False
44
+ :param _indent: 缩进级别,默认为4
45
+ """
46
+ with open(_filepath, 'w', encoding=_encoding) as f:
47
+ json.dump(_data, f, ensure_ascii=_ensure_ascii, indent=_indent)
48
+
25
49
 
26
50
  # 列表去重
27
51
  def unique_list(_lst):
@@ -38,4 +62,4 @@ def unique_list(_lst):
38
62
  _set.add(item)
39
63
  result.append(item)
40
64
 
41
- return result
65
+ return result
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Alaea
3
- Version: 2.3.4
3
+ Version: 2.3.6
4
4
  Summary: Some simple tools by Christmas
5
5
  Author: Christmas
6
6
  Author-email: 273519355@qq.com
@@ -27,6 +27,10 @@ Alaea/Standard/__init__.py
27
27
  Alaea/Standard/xr_attrs.py
28
28
  Alaea/Standard/xr_ice.py
29
29
  Alaea/Standard/xr_wave.py
30
+ Alaea/baysalt/__init__.py
31
+ Alaea/baysalt/erosion_coastline.py
32
+ Alaea/baysalt/Forage/__init__.py
33
+ Alaea/baysalt/Forage/erosion_data/__init__.py
30
34
  Alaea/exutils/__init__.py
31
35
  Alaea/exutils/Interp/Interp_g2s.py
32
36
  Alaea/exutils/Interp/Interp_s2g.py
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Alaea
3
- Version: 2.3.4
3
+ Version: 2.3.6
4
4
  Summary: Some simple tools by Christmas
5
5
  Author: Christmas
6
6
  Author-email: 273519355@qq.com
@@ -8,7 +8,7 @@ with open("README.md", "r") as fh:
8
8
 
9
9
  setuptools.setup(
10
10
  name="Alaea",
11
- version="2.3.4",
11
+ version="2.3.6",
12
12
  author="Christmas",
13
13
  author_email="273519355@qq.com",
14
14
  description="Some simple tools by Christmas",
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes