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.
- {alaea-2.3.4 → alaea-2.3.6}/.DS_Store +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/.gitattributes +2 -0
- {alaea-2.3.4 → alaea-2.3.6}/.gitignore +1 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/Blogging.py +13 -12
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/Standard/xr_attrs.py +166 -52
- alaea-2.3.6/Alaea/baysalt/Forage/__init__.py +11 -0
- alaea-2.3.6/Alaea/baysalt/Forage/erosion_data/__init__.py +11 -0
- alaea-2.3.6/Alaea/baysalt/__init__.py +11 -0
- alaea-2.3.6/Alaea/baysalt/erosion_coastline.py +405 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/processBar.py +25 -1
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/Read.py +25 -1
- {alaea-2.3.4 → alaea-2.3.6/Alaea.egg-info}/PKG-INFO +1 -1
- {alaea-2.3.4 → alaea-2.3.6}/Alaea.egg-info/SOURCES.txt +4 -0
- {alaea-2.3.4/Alaea.egg-info → alaea-2.3.6}/PKG-INFO +1 -1
- {alaea-2.3.4 → alaea-2.3.6}/setup.py +1 -1
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/Pyshell/RS_File.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/Pyshell/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/S_DateTime.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/SameAsMATLAB/CoorTrans.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/SameAsMATLAB/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/SameAsMATLAB/calculatefuns.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/Standard/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/Standard/xr_ice.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/Standard/xr_wave.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/commonCode.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/cprintfs.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/exutils/Interp/Interp_g2s.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/exutils/Interp/Interp_s2g.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/exutils/Interp/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/exutils/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/fvcom_class.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/fvcom_image.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/fvcom_load.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/fvcom_tools.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/pFVCOM/writeFile.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/read_conf.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/server_info.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/P_params.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/download_check.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/geo_distance.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/tools/kml2shp.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea/warningTools/__init__.py +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea.egg-info/dependency_links.txt +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea.egg-info/requires.txt +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/Alaea.egg-info/top_level.txt +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/MANIFEST.in +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/README.md +0 -0
- {alaea-2.3.4 → alaea-2.3.6}/run_twine.py +0 -0
- {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
|
|
@@ -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,
|
|
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(
|
|
26
|
+
def add_coors_attrs(_ds):
|
|
23
27
|
"""
|
|
24
28
|
给xarray.Dataset的坐标添加属性
|
|
25
|
-
:param
|
|
29
|
+
:param _ds: xarray.Dataset
|
|
26
30
|
:return: xarray.Dataset
|
|
27
31
|
"""
|
|
28
|
-
lon_name
|
|
29
|
-
for
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
|
124
|
-
for
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
for
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
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,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__ = [
|
|
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
|
|
@@ -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
|
|
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
|
|
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
|