shancx 1.9.33.216__py3-none-any.whl → 1.9.33.226__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.
- shancx/Hug/__init__.py +0 -3
- shancx/NN/chainMul.py +175 -0
- shancx/Plot/plotGlobal.py +134 -53
- shancx/Read.py +72 -33
- shancx/npz.py +15 -0
- shancx/sendM.py +6 -7
- {shancx-1.9.33.216.dist-info → shancx-1.9.33.226.dist-info}/METADATA +1 -1
- {shancx-1.9.33.216.dist-info → shancx-1.9.33.226.dist-info}/RECORD +10 -8
- {shancx-1.9.33.216.dist-info → shancx-1.9.33.226.dist-info}/WHEEL +0 -0
- {shancx-1.9.33.216.dist-info → shancx-1.9.33.226.dist-info}/top_level.txt +0 -0
shancx/Hug/__init__.py
CHANGED
shancx/NN/chainMul.py
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
import subprocess
|
|
2
|
+
import time
|
|
3
|
+
import logging
|
|
4
|
+
import os
|
|
5
|
+
import argparse
|
|
6
|
+
import datetime
|
|
7
|
+
import traceback
|
|
8
|
+
from dateutil.relativedelta import relativedelta
|
|
9
|
+
import pandas as pd
|
|
10
|
+
from shancx.NN import Mul_TH
|
|
11
|
+
from shancx.NN import _loggersPlus
|
|
12
|
+
from shancx.NN import cleanupLogs
|
|
13
|
+
from shancx.sendM import sendMES
|
|
14
|
+
BASE_DIR = '/mnt/wtx_weather_forecast/scx/sever7/product_log_scx/code06_re2_cp1/exam/mk_trainsatRadarMSG1'
|
|
15
|
+
PYTHON_ENV = '/home/scx/miniconda3/envs/mqpf/bin/python'
|
|
16
|
+
DEFAULT_GPU = '0'
|
|
17
|
+
DEFAULT_TIMEOUT = 2400
|
|
18
|
+
SCRIPT_DELAY = 2
|
|
19
|
+
# minutedict = {"20":8,"30":6,"40":4,"50":2,"0":0}
|
|
20
|
+
minutedict = {"0": 0 }
|
|
21
|
+
class PipelineExecutor:
|
|
22
|
+
def __init__(self, UTC, sat_cd, gpu=DEFAULT_GPU):
|
|
23
|
+
self.UTCstr = UTC.strftime("%Y%m%d%H%M")
|
|
24
|
+
self.sat_cd = sat_cd
|
|
25
|
+
self.gpu = gpu
|
|
26
|
+
self.produce_time= self.producetime(UTC)
|
|
27
|
+
self.scripts = self._initialize_scripts()
|
|
28
|
+
def producetime(self,UTC):
|
|
29
|
+
_minute = UTC.minute
|
|
30
|
+
_minute = minutedict.get(str(_minute),None)
|
|
31
|
+
if _minute is not None :
|
|
32
|
+
start_time = UTC - relativedelta(minutes=_minute) # "20":8 indicates that the step 2 command is executed at the 12-minute mark, which is between 20 and 10 minutes.
|
|
33
|
+
return start_time
|
|
34
|
+
else:
|
|
35
|
+
return None
|
|
36
|
+
def _initialize_scripts(self):
|
|
37
|
+
#step 1 command
|
|
38
|
+
scripts = [
|
|
39
|
+
{
|
|
40
|
+
'name': 'MSGsat',
|
|
41
|
+
'cmd': f"cd {os.path.join(BASE_DIR, 'makeMSGnc')} && {PYTHON_ENV} MSGsat.py --times {self.UTCstr[:12]}",
|
|
42
|
+
'timeout': DEFAULT_TIMEOUT
|
|
43
|
+
}
|
|
44
|
+
]
|
|
45
|
+
#step 2 command
|
|
46
|
+
if self.produce_time is not None:
|
|
47
|
+
times_str = self.produce_time.strftime("%Y%m%d%H%M")[:12]
|
|
48
|
+
scripts.extend([
|
|
49
|
+
# {
|
|
50
|
+
# 'name': 'mainSAT',
|
|
51
|
+
# 'cmd': f"cd {os.path.join('/mnt/wtx_weather_forecast/scx', 'mqpf_GEOSH9SEAStestCal')} && {PYTHON_ENV} mainSAT.py --times {times_str[:12]} --satcd H9SEAS --sepSec 360 --gpu '{self.gpu}' --isOverwrite --classer 'WT_RTH9SEAS'",
|
|
52
|
+
# 'timeout': DEFAULT_TIMEOUT
|
|
53
|
+
# },
|
|
54
|
+
# {
|
|
55
|
+
# 'name': 'mainSAT',
|
|
56
|
+
# 'cmd': f"cd {os.path.join('/mnt/wtx_weather_forecast/scx', 'mqpf_GEOSH9SEAStestCal')} && {PYTHON_ENV} mainSAT.py --times {times_str[:12]} --satcd H9CHNNEAS --sepSec 360 --gpu '{self.gpu}' --isOverwrite --classer 'WT_RTH9OC'",
|
|
57
|
+
# 'timeout': DEFAULT_TIMEOUT
|
|
58
|
+
# },
|
|
59
|
+
# {
|
|
60
|
+
# 'name': 'mainSAT',
|
|
61
|
+
# 'cmd': f"cd {os.path.join('/mnt/wtx_weather_forecast/scx', 'mqpf_GEOSH9SEAStestCal')} && {PYTHON_ENV} mainSAT.py --times {times_str[:12]} --satcd H9OC --sepSec 360 --gpu '{self.gpu}' --isOverwrite --classer 'WT_RTH9CHNNEAS'",
|
|
62
|
+
# 'timeout': DEFAULT_TIMEOUT
|
|
63
|
+
# },
|
|
64
|
+
# # [
|
|
65
|
+
# # # {
|
|
66
|
+
# # # 'name': 'satPzlSATREF',
|
|
67
|
+
# # # 'cmd': f"cd {os.path.join(BASE_DIR, 'SawPuz')} && {PYTHON_ENV} satPzlSAT.py --times {times_str[:12]} --satcd '{self.sat_cd}' --mqpf_cd 'REF'",
|
|
68
|
+
# # # 'timeout': DEFAULT_TIMEOUT
|
|
69
|
+
# # # },
|
|
70
|
+
# # # {
|
|
71
|
+
# # # 'name': 'satPzlSATQPF',
|
|
72
|
+
# # # 'cmd': f"cd {os.path.join(BASE_DIR, 'SawPuz')} && {PYTHON_ENV} satPzlSAT.py --times {times_str[:12]} --satcd '{self.sat_cd}' --mqpf_cd 'QPF' --isUpload ",
|
|
73
|
+
# # # 'timeout': DEFAULT_TIMEOUT
|
|
74
|
+
# # # }
|
|
75
|
+
# # ]
|
|
76
|
+
])
|
|
77
|
+
else:
|
|
78
|
+
logger.warning("produce_time is None, skipping mainSAT, satPzlSATREF and satPzlSATQPF command execution")
|
|
79
|
+
return scripts
|
|
80
|
+
def execute_script(self, conf):
|
|
81
|
+
script_info = conf[0]
|
|
82
|
+
script_name = script_info['name']
|
|
83
|
+
timeout = script_info['timeout']
|
|
84
|
+
logger.info(f"Starting to execute script: {script_name}")
|
|
85
|
+
try:
|
|
86
|
+
result = subprocess.run(
|
|
87
|
+
script_info['cmd'],
|
|
88
|
+
shell=True,
|
|
89
|
+
timeout=timeout,
|
|
90
|
+
check=False,
|
|
91
|
+
stdout=subprocess.PIPE,
|
|
92
|
+
stderr=subprocess.PIPE,
|
|
93
|
+
text=True
|
|
94
|
+
)
|
|
95
|
+
if result.returncode != 0:
|
|
96
|
+
logger.error(f"Script {script_name} execution failed, return code: {result.returncode}")
|
|
97
|
+
if result.stdout.strip():
|
|
98
|
+
logger.error(f"Output before failure:\n{result.stdout}")
|
|
99
|
+
if result.stderr.strip():
|
|
100
|
+
logger.error(f"Error output:\n{result.stderr}")
|
|
101
|
+
logger.error(f"Traceback:\n{traceback.format_exc()}")
|
|
102
|
+
return False
|
|
103
|
+
# Success case
|
|
104
|
+
if result.stdout.strip():
|
|
105
|
+
logger.debug(f"[{script_name}] Output:\n{result.stdout}")
|
|
106
|
+
if result.stderr.strip():
|
|
107
|
+
logger.warning(f"[{script_name}] Warning output:\n{result.stderr}")
|
|
108
|
+
logger.info(f"Script {script_name} executed successfully")
|
|
109
|
+
return True
|
|
110
|
+
except Exception as e:
|
|
111
|
+
logger.error(f"Script {script_name} execution exception: {str(e)}")
|
|
112
|
+
logger.error(f"Traceback:\n{traceback.format_exc()}")
|
|
113
|
+
sendMES(f"{traceback.format_exc()} ",NN="MT1")
|
|
114
|
+
return False
|
|
115
|
+
def run_pipeline(self):
|
|
116
|
+
"""Execute complete script pipeline"""
|
|
117
|
+
logger.info(f"Starting to process time slot: {self.UTCstr}, satellite: {self.sat_cd}")
|
|
118
|
+
for i, script in enumerate(self.scripts, 1):
|
|
119
|
+
if isinstance(script, list):
|
|
120
|
+
logger.info(f"Executing step {i}/{len(self.scripts)}: satPzlSAT")
|
|
121
|
+
Mul_TH(self.execute_script,[script])
|
|
122
|
+
else:
|
|
123
|
+
logger.info(f"Executing step {i}/{len(self.scripts)}: {script['name']}")
|
|
124
|
+
if not self.execute_script((script,)):
|
|
125
|
+
logger.error(f"Pipeline interrupted at {script['name']}, processing failed")
|
|
126
|
+
return False
|
|
127
|
+
if i < len(self.scripts):
|
|
128
|
+
time.sleep(SCRIPT_DELAY)
|
|
129
|
+
logger.info(f"Time slot {self.UTCstr} processing completed")
|
|
130
|
+
return True
|
|
131
|
+
def main(conf):
|
|
132
|
+
utc,sat_cd,gpu = conf[0],conf[1],conf[2]
|
|
133
|
+
executor = PipelineExecutor(utc, sat_cd, gpu)
|
|
134
|
+
return executor.run_pipeline()
|
|
135
|
+
def options():
|
|
136
|
+
parser = argparse.ArgumentParser(description='Pipeline Cascade time command execution')
|
|
137
|
+
parser.add_argument('--times', type=str, default='202412010000,202508010000')
|
|
138
|
+
parser.add_argument('--sat_cd', type=str, default='MSG')
|
|
139
|
+
parser.add_argument('--gpu', type=str, default='0')
|
|
140
|
+
config= parser.parse_args()
|
|
141
|
+
print(config)
|
|
142
|
+
config.times = config.times.split(",")
|
|
143
|
+
if len(config.times) == 1:
|
|
144
|
+
config.times = [config.times[0], config.times[0]]
|
|
145
|
+
config.times = [datetime.datetime.strptime(config.times[0], "%Y%m%d%H%M"),
|
|
146
|
+
datetime.datetime.strptime(config.times[1], "%Y%m%d%H%M")]
|
|
147
|
+
return config
|
|
148
|
+
if __name__ == "__main__":
|
|
149
|
+
cfg = options()
|
|
150
|
+
sUTC = cfg.times[0]
|
|
151
|
+
eUTC = cfg.times[-1]
|
|
152
|
+
sat_cd = cfg.sat_cd
|
|
153
|
+
gpu =cfg.gpu
|
|
154
|
+
sUTCstr = sUTC.strftime("%Y%m%d%H%M")
|
|
155
|
+
dir_ = f"./logs/{sat_cd}"
|
|
156
|
+
logger = _loggersPlus(root = dir_ , phase=f"{sUTCstr}_Pipeline")
|
|
157
|
+
from shancx.NN import cleanupLogs
|
|
158
|
+
stats = cleanupLogs(dir_,30, '*.log', False, False)
|
|
159
|
+
logging.info(f"clean={stats['total_dirs']}, cleanfile={stats['deleted_files']}, error={len(stats['errors'])}")
|
|
160
|
+
|
|
161
|
+
timeList = pd.date_range(sUTC, eUTC, freq='30min')
|
|
162
|
+
# filtered_times = [t for t in timeList if t.hour % 4 == 0 and t.minute == 0] #t.mintues
|
|
163
|
+
# result_times = []
|
|
164
|
+
# for t in filtered_times:
|
|
165
|
+
# result_times.extend(pd.date_range(t - pd.Timedelta(minutes=30), t, freq='10T'))
|
|
166
|
+
# for UTC in result_times:
|
|
167
|
+
# main((UTC,sat_cd,gpu))
|
|
168
|
+
Mul_TH(main,[timeList,[sat_cd],[gpu]],2)
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
|
shancx/Plot/plotGlobal.py
CHANGED
|
@@ -11,7 +11,7 @@ import cartopy.feature as cfeature
|
|
|
11
11
|
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
|
|
12
12
|
from hjnwtx.colormap import cmp_hjnwtx
|
|
13
13
|
import os
|
|
14
|
-
def plotGlobal(b, latArr1, lonArr1, cmap='summer', title='Global QPF Data Visualization',saveDir = "./plotGlobal",ty=None ):
|
|
14
|
+
def plotGlobal(b, latArr1, lonArr1, cmap='summer', title='Global QPF Data Visualization',saveDir = "./plotGlobal",ty=None,name="plotGlobal" ):
|
|
15
15
|
now_str = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
16
16
|
plt.figure(figsize=(20, 10), dpi=100)
|
|
17
17
|
ax = plt.axes(projection=ccrs.PlateCarree())
|
|
@@ -28,14 +28,29 @@ def plotGlobal(b, latArr1, lonArr1, cmap='summer', title='Global QPF Data Visual
|
|
|
28
28
|
"pre": cmp_hjnwtx["pre_tqw"],
|
|
29
29
|
None: 'summer'
|
|
30
30
|
}.get(ty)
|
|
31
|
+
|
|
32
|
+
minmax = {
|
|
33
|
+
"radar": [0,80],
|
|
34
|
+
"pre": [0,20],
|
|
35
|
+
None: [np.nanmin(b),np.nanmax(b)]
|
|
36
|
+
}.get(ty)
|
|
37
|
+
|
|
38
|
+
setlabel = {
|
|
39
|
+
"radar": 'Composite Reflectivity (Radar)\nUnit: (dBZ)',
|
|
40
|
+
"pre": 'Precipitation\nUnit: (mm)',
|
|
41
|
+
None: 'Data'
|
|
42
|
+
}.get(ty)
|
|
43
|
+
|
|
31
44
|
img = ax.pcolormesh(lon_grid[::stride, ::stride],
|
|
32
45
|
lat_grid[::stride, ::stride],
|
|
33
46
|
b[::stride, ::stride],
|
|
34
47
|
cmap=cmap,
|
|
35
48
|
shading='auto',
|
|
49
|
+
vmin=minmax[0],
|
|
50
|
+
vmax=minmax[1],
|
|
36
51
|
transform=ccrs.PlateCarree())
|
|
37
52
|
cbar = plt.colorbar(img, ax=ax, orientation='vertical', pad=0.05, shrink=0.6)
|
|
38
|
-
cbar.set_label(
|
|
53
|
+
cbar.set_label(setlabel, fontsize=12)
|
|
39
54
|
ax.set_xticks(np.arange(-180, 181, 30), crs=ccrs.PlateCarree())
|
|
40
55
|
ax.set_yticks(np.arange(-90, 91, 15), crs=ccrs.PlateCarree())
|
|
41
56
|
ax.xaxis.set_major_formatter(LongitudeFormatter(zero_direction_label=True))
|
|
@@ -45,15 +60,108 @@ def plotGlobal(b, latArr1, lonArr1, cmap='summer', title='Global QPF Data Visual
|
|
|
45
60
|
ax.set_global()
|
|
46
61
|
plt.tight_layout()
|
|
47
62
|
os.makedirs(saveDir, exist_ok=True)
|
|
48
|
-
plt.savefig(f"./{saveDir}/
|
|
63
|
+
plt.savefig(f"./{saveDir}/plotGlobal_glob{now_str}_{name}.png", dpi=300, bbox_inches="tight")
|
|
49
64
|
plt.close()
|
|
50
65
|
|
|
51
66
|
"""
|
|
52
67
|
plotGlobal(b, latArr1, lonArr1,
|
|
53
68
|
title='Global Meteorological Data',ty="radar")
|
|
54
|
-
|
|
55
69
|
"""
|
|
56
70
|
|
|
71
|
+
import ssl
|
|
72
|
+
import urllib.request
|
|
73
|
+
ssl._create_default_https_context = ssl._create_unverified_context
|
|
74
|
+
import datetime
|
|
75
|
+
import numpy as np
|
|
76
|
+
import matplotlib.pyplot as plt
|
|
77
|
+
import cartopy.crs as ccrs
|
|
78
|
+
import cartopy.feature as cfeature
|
|
79
|
+
from cartopy.mpl.ticker import LongitudeFormatter, LatitudeFormatter
|
|
80
|
+
from hjnwtx.colormap import cmp_hjnwtx
|
|
81
|
+
import os
|
|
82
|
+
def plotGlobalTtf(b, latArr1, lonArr1, cmap='summer', title='Global QPF Data Visualization', saveDir="./plotGlobal", ty=None, name=1,font_path=None):
|
|
83
|
+
now_str = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
84
|
+
import matplotlib.pyplot as plt
|
|
85
|
+
import matplotlib
|
|
86
|
+
import matplotlib.font_manager as fm
|
|
87
|
+
font_path = "/mnt/wtx_weather_forecast/scx/sever7/微软雅黑.ttf" if font_path is None else font_path
|
|
88
|
+
font_kwargs = {}
|
|
89
|
+
if os.path.exists(font_path):
|
|
90
|
+
try:
|
|
91
|
+
fm.fontManager.addfont(font_path)
|
|
92
|
+
font_prop = fm.FontProperties(fname=font_path)
|
|
93
|
+
font_name = font_prop.get_name()
|
|
94
|
+
plt.rcParams['font.family'] = 'sans-serif'
|
|
95
|
+
plt.rcParams['font.sans-serif'] = [font_name]
|
|
96
|
+
plt.rcParams['axes.unicode_minus'] = False
|
|
97
|
+
font_kwargs = {'fontproperties': font_prop}
|
|
98
|
+
print(f"成功加载字体: {font_name}")
|
|
99
|
+
except Exception as e:
|
|
100
|
+
print(f"加载指定字体失败: {e}")
|
|
101
|
+
# 尝试使用系统字体
|
|
102
|
+
try:
|
|
103
|
+
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
|
|
104
|
+
plt.rcParams['axes.unicode_minus'] = False
|
|
105
|
+
except:
|
|
106
|
+
pass
|
|
107
|
+
else:
|
|
108
|
+
print(f"警告: 字体文件不存在: {font_path}")
|
|
109
|
+
try:
|
|
110
|
+
plt.rcParams['font.sans-serif'] = ['SimHei', 'Microsoft YaHei', 'DejaVu Sans']
|
|
111
|
+
plt.rcParams['axes.unicode_minus'] = False
|
|
112
|
+
except:
|
|
113
|
+
pass
|
|
114
|
+
plt.figure(figsize=(20, 10), dpi=100)
|
|
115
|
+
ax = plt.axes(projection=ccrs.PlateCarree())
|
|
116
|
+
ax.add_feature(cfeature.LAND, facecolor='none') # 陆地无色
|
|
117
|
+
ax.add_feature(cfeature.OCEAN, facecolor='none') # 海洋无色
|
|
118
|
+
ax.add_feature(cfeature.COASTLINE, linewidth=0.8, edgecolor='black') # 海岸线黑色
|
|
119
|
+
ax.add_feature(cfeature.BORDERS, linestyle='-', linewidth=0.5, edgecolor='black') # 国界线黑色
|
|
120
|
+
ax.add_feature(cfeature.LAKES, alpha=0.3, facecolor='none', edgecolor='gray') # 湖泊无色,灰色边界
|
|
121
|
+
ax.add_feature(cfeature.RIVERS, edgecolor='gray', linewidth=0.5) # 河流灰色
|
|
122
|
+
lon_grid, lat_grid = np.meshgrid(lonArr1, latArr1)
|
|
123
|
+
stride = 10 # 每10个点取1个
|
|
124
|
+
cmap = {
|
|
125
|
+
"radar": cmp_hjnwtx["radar_nmc"],
|
|
126
|
+
"pre": cmp_hjnwtx["pre_tqw"],
|
|
127
|
+
None: 'summer'
|
|
128
|
+
}.get(ty)
|
|
129
|
+
minmax = {
|
|
130
|
+
"radar": [0,80],
|
|
131
|
+
"pre": [0,20],
|
|
132
|
+
None: [np.nanmin(b),np.nanmax(b)]
|
|
133
|
+
}.get(ty)
|
|
134
|
+
|
|
135
|
+
setlabel = {
|
|
136
|
+
"radar": '雷达组合反射率\n单位:(dbz)',
|
|
137
|
+
"pre": '降雨量\n单位:(mm)',
|
|
138
|
+
None: '数据'
|
|
139
|
+
}.get(ty)
|
|
140
|
+
img = ax.pcolormesh(lon_grid[::stride, ::stride],
|
|
141
|
+
lat_grid[::stride, ::stride],
|
|
142
|
+
b[::stride, ::stride],
|
|
143
|
+
cmap=cmap,
|
|
144
|
+
shading='auto',
|
|
145
|
+
transform=ccrs.PlateCarree(), vmin=minmax[0], vmax=minmax[1])
|
|
146
|
+
cbar = plt.colorbar(img, ax=ax, orientation='vertical', pad=0.05, shrink=0.6)
|
|
147
|
+
if font_kwargs:
|
|
148
|
+
cbar.set_label(setlabel, fontsize=12, **font_kwargs)
|
|
149
|
+
ax.set_xticks(np.arange(-180, 181, 30), crs=ccrs.PlateCarree())
|
|
150
|
+
ax.set_yticks(np.arange(-90, 91, 15), crs=ccrs.PlateCarree())
|
|
151
|
+
ax.xaxis.set_major_formatter(LongitudeFormatter(zero_direction_label=True))
|
|
152
|
+
ax.yaxis.set_major_formatter(LatitudeFormatter())
|
|
153
|
+
ax.gridlines(color='gray', linestyle=':', alpha=0.5)
|
|
154
|
+
ax.set_title(title, fontsize=16, pad=20)
|
|
155
|
+
ax.set_global()
|
|
156
|
+
plt.tight_layout()
|
|
157
|
+
os.makedirs(saveDir, exist_ok=True)
|
|
158
|
+
plt.savefig(f"./{saveDir}/plotScatter_glob{now_str}_{ty}_{name}.png", dpi=300, bbox_inches="tight")
|
|
159
|
+
plt.close()
|
|
160
|
+
|
|
161
|
+
'''
|
|
162
|
+
plotGlobal(satH9[0], latArrsatH9, lonArrsatH9, title=f'全球气象雷达组合反射率分布图',saveDir="./plotGlobal", ty="radar",name = "")
|
|
163
|
+
'''
|
|
164
|
+
|
|
57
165
|
import datetime
|
|
58
166
|
import numpy as np
|
|
59
167
|
import matplotlib.pyplot as plt
|
|
@@ -89,16 +197,31 @@ def plotGlobalPlus(b, latArr1, lonArr1, cmap='summer', title='Global QPF Data Vi
|
|
|
89
197
|
cmap = {
|
|
90
198
|
"radar": cmp_hjnwtx["radar_nmc"],
|
|
91
199
|
"pre": cmp_hjnwtx["pre_tqw"],
|
|
92
|
-
None:
|
|
93
|
-
}.get(ty
|
|
200
|
+
None: 'summer'
|
|
201
|
+
}.get(ty)
|
|
202
|
+
|
|
203
|
+
minmax = {
|
|
204
|
+
"radar": [0,80],
|
|
205
|
+
"pre": [0,20],
|
|
206
|
+
None: [np.nanmin(b),np.nanmax(b)]
|
|
207
|
+
}.get(ty)
|
|
208
|
+
|
|
209
|
+
setlabel = {
|
|
210
|
+
"radar": 'Composite Reflectivity (Radar)\nUnit: (dBZ)',
|
|
211
|
+
"pre": 'Precipitation\nUnit: (mm)',
|
|
212
|
+
None: 'Data'
|
|
213
|
+
}.get(ty)
|
|
214
|
+
|
|
94
215
|
img = ax.pcolormesh(lon_grid[::stride, ::stride],
|
|
95
216
|
lat_grid[::stride, ::stride],
|
|
96
217
|
b[::stride, ::stride],
|
|
97
218
|
cmap=cmap,
|
|
98
219
|
shading='auto',
|
|
220
|
+
vmin = minmax[0],
|
|
221
|
+
vmax = minmax[1],
|
|
99
222
|
transform=ccrs.PlateCarree())
|
|
100
223
|
cbar = plt.colorbar(img, ax=ax, orientation='vertical', pad=0.05, shrink=0.6)
|
|
101
|
-
cbar.set_label(
|
|
224
|
+
cbar.set_label(setlabel , fontsize=12)
|
|
102
225
|
ax.set_xticks(np.arange(-180, 181, 30), crs=ccrs.PlateCarree())
|
|
103
226
|
ax.set_yticks(np.arange(-90, 91, 15), crs=ccrs.PlateCarree())
|
|
104
227
|
ax.xaxis.set_major_formatter(LongitudeFormatter(zero_direction_label=True))
|
|
@@ -113,6 +236,7 @@ def plotGlobalPlus(b, latArr1, lonArr1, cmap='summer', title='Global QPF Data Vi
|
|
|
113
236
|
plt.close()
|
|
114
237
|
print(f"图像已保存到: {save_path}")
|
|
115
238
|
return save_path
|
|
239
|
+
|
|
116
240
|
"""
|
|
117
241
|
if __name__ == "__main__":
|
|
118
242
|
plotGlobal(
|
|
@@ -126,6 +250,7 @@ if __name__ == "__main__":
|
|
|
126
250
|
)
|
|
127
251
|
"""
|
|
128
252
|
|
|
253
|
+
|
|
129
254
|
import ssl
|
|
130
255
|
import urllib.request
|
|
131
256
|
ssl._create_default_https_context = ssl._create_unverified_context
|
|
@@ -240,10 +365,10 @@ def plotBorder(UTCstr, nclon, nclat, cr, fig_title,savepath="./plotBorder", data
|
|
|
240
365
|
cb.set_ticklabels([str(level) for level in clevels[:-1]], fontproperties=myfont)
|
|
241
366
|
for label in ax.get_xticklabels() + ax.get_yticklabels():
|
|
242
367
|
label.set_fontproperties(myfont)
|
|
243
|
-
mkDir(figpath)
|
|
244
368
|
plt.savefig(figpath, dpi=300, bbox_inches='tight')
|
|
245
369
|
print(f"{fig_title.split('_')[0]}绘制完成: {figpath}")
|
|
246
370
|
plt.close()
|
|
371
|
+
|
|
247
372
|
"""
|
|
248
373
|
latArr = np.linspace(27, -13, 2000 )
|
|
249
374
|
lonArr = np.linspace(70, 150, 4000)
|
|
@@ -322,48 +447,4 @@ fig_title = f"卫星反演雷达回波_{UTCstr}"
|
|
|
322
447
|
# base[20:1070,75:1625] = satCR
|
|
323
448
|
plot_fig(data,result['lats'],result['lons'],fig_title,datatype="radar")
|
|
324
449
|
"""
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
import numpy as np
|
|
328
|
-
import matplotlib.pyplot as plt
|
|
329
|
-
import cartopy.crs as ccrs
|
|
330
|
-
import cartopy.feature as cfeature
|
|
331
|
-
from mpl_toolkits.axes_grid1 import make_axes_locatable
|
|
332
|
-
import datetime
|
|
333
|
-
from shancx import crDir
|
|
334
|
-
def plotBorder(matrix,name='plotBorder',saveDir="plotBorder",extent=None,title='Matrix Plot', xlabel='X-axis', ylabel='Y-axis', color_label='Value', cmap='viridis'):
|
|
335
|
-
if extent is None:
|
|
336
|
-
lat_min, lat_max = -3, 13
|
|
337
|
-
lon_min, lon_max = -0, 28
|
|
338
|
-
else:
|
|
339
|
-
lat_min, lat_max, lon_min, lon_max = extent
|
|
340
|
-
now_str = datetime.datetime.now().strftime("%Y%m%d%H%M%S")
|
|
341
|
-
fig = plt.figure(figsize=(12, 8))
|
|
342
|
-
ax = fig.add_subplot(1, 1, 1, projection=ccrs.PlateCarree())
|
|
343
|
-
ax.set_extent([lon_min, lon_max, lat_min, lat_max], crs=ccrs.PlateCarree())
|
|
344
|
-
im = ax.imshow(
|
|
345
|
-
matrix,
|
|
346
|
-
extent=[lon_min, lon_max, lat_min, lat_max],
|
|
347
|
-
origin='upper',
|
|
348
|
-
cmap='viridis',
|
|
349
|
-
transform=ccrs.PlateCarree()
|
|
350
|
-
)
|
|
351
|
-
ax.add_feature(cfeature.COASTLINE, linewidth=0.5)
|
|
352
|
-
ax.add_feature(cfeature.BORDERS, linestyle=':', linewidth=0.5)
|
|
353
|
-
states = cfeature.NaturalEarthFeature(
|
|
354
|
-
category='cultural',
|
|
355
|
-
name='admin_1_states_provinces_lines',
|
|
356
|
-
scale='50m',
|
|
357
|
-
facecolor='none'
|
|
358
|
-
)
|
|
359
|
-
ax.add_feature(states, edgecolor='red', linewidth=0.5)
|
|
360
|
-
divider = make_axes_locatable(ax)
|
|
361
|
-
cax = divider.append_axes("right", size="5%", pad=0.1, axes_class=plt.Axes)
|
|
362
|
-
cbar = plt.colorbar(im, cax=cax, label='Data Values')
|
|
363
|
-
ax.set_title('Sat data Boundaries', fontsize=14)
|
|
364
|
-
plt.tight_layout()
|
|
365
|
-
outpath = f'./{saveDir}/{name}_{now_str}.png' if name=="plotBorder" else f"./{saveDir}/{name}.png"
|
|
366
|
-
crDir(outpath)
|
|
367
|
-
plt.savefig(outpath)
|
|
368
|
-
plt.close()
|
|
369
|
-
|
|
450
|
+
|
shancx/Read.py
CHANGED
|
@@ -1,9 +1,77 @@
|
|
|
1
|
+
import xarray as xr
|
|
2
|
+
import numpy as np
|
|
3
|
+
import gzip
|
|
4
|
+
import tempfile
|
|
5
|
+
import os
|
|
6
|
+
def getGrib(filepath):
|
|
7
|
+
tmp_path = filepath
|
|
8
|
+
if filepath.endswith('.gz'):
|
|
9
|
+
with gzip.open(filepath, 'rb') as f:
|
|
10
|
+
with tempfile.NamedTemporaryFile(suffix='.grib2', delete=False) as tmp:
|
|
11
|
+
tmp.write(f.read())
|
|
12
|
+
tmp_path = tmp.name
|
|
13
|
+
try:
|
|
14
|
+
ds = xr.open_dataset(tmp_path, engine='cfgrib')
|
|
15
|
+
var_name = list(ds.data_vars)[0]
|
|
16
|
+
data_var = ds[var_name]
|
|
17
|
+
data_values = np.where(data_var.values < -900, np.nan, data_var.values)
|
|
18
|
+
if 'latitude' in ds.coords and 'longitude' in ds.coords:
|
|
19
|
+
lats = ds.latitude.values
|
|
20
|
+
lons = ds.longitude.values
|
|
21
|
+
elif 'lat' in ds.coords and 'lon' in ds.coords:
|
|
22
|
+
lats = ds.lat.values
|
|
23
|
+
lons = ds.lon.values
|
|
24
|
+
elif hasattr(ds, 'latitude') and hasattr(ds, 'longitude'):
|
|
25
|
+
lats = ds.latitude.values
|
|
26
|
+
lons = ds.longitude.values
|
|
27
|
+
else:
|
|
28
|
+
for var_name in ds.data_vars:
|
|
29
|
+
var = ds[var_name]
|
|
30
|
+
if hasattr(var, 'latitude') and hasattr(var, 'longitude'):
|
|
31
|
+
lats = var.latitude.values
|
|
32
|
+
lons = var.longitude.values
|
|
33
|
+
break
|
|
34
|
+
else:
|
|
35
|
+
raise ValueError("未找到经纬度坐标")
|
|
36
|
+
lat_range = (float(np.nanmin(lats)), float(np.nanmax(lats)))
|
|
37
|
+
lon_range = (float(np.nanmin(lons)), float(np.nanmax(lons)))
|
|
38
|
+
return {
|
|
39
|
+
'data': data,
|
|
40
|
+
'lats': lats,
|
|
41
|
+
'lons': lons
|
|
42
|
+
}
|
|
43
|
+
finally:
|
|
44
|
+
os.unlink(tmp_path)
|
|
45
|
+
"""
|
|
46
|
+
input_Path = './Composite_00.50_20250701-010044.grib2.gz'
|
|
47
|
+
result = getGrib(input_Path)
|
|
48
|
+
"""
|
|
49
|
+
|
|
1
50
|
import pygrib
|
|
2
51
|
import numpy as np
|
|
3
52
|
import pandas as pd
|
|
4
53
|
def readGrib(file_path, target_param=None):
|
|
5
54
|
try:
|
|
6
|
-
with pygrib.open(file_path) as grbs:
|
|
55
|
+
with pygrib.open(file_path) as grbs:
|
|
56
|
+
file_path= file_path
|
|
57
|
+
if filepath.endswith('.gz'):
|
|
58
|
+
with gzip.open(filepath, 'rb') as f:
|
|
59
|
+
with tempfile.NamedTemporaryFile(suffix='.grib2', delete=False) as tmp:
|
|
60
|
+
tmp.write(f.read())
|
|
61
|
+
file_path = tmp.name
|
|
62
|
+
if target_param:
|
|
63
|
+
try:
|
|
64
|
+
grb = grbs.select(shortName=target_param)[0]
|
|
65
|
+
except:
|
|
66
|
+
try:
|
|
67
|
+
grb = grbs.select(parameterName=target_param)[0]
|
|
68
|
+
except:
|
|
69
|
+
raise ValueError(f"未找到参数: {target_param}")
|
|
70
|
+
else:
|
|
71
|
+
grb = grbs[1]
|
|
72
|
+
data = grb.values
|
|
73
|
+
lats, lons = grb.latlons()
|
|
74
|
+
#data2
|
|
7
75
|
field_info = []
|
|
8
76
|
for grb in grbs:
|
|
9
77
|
field_info.append({
|
|
@@ -16,18 +84,6 @@ def readGrib(file_path, target_param=None):
|
|
|
16
84
|
'units': getattr(grb, 'units', 'N/A'),
|
|
17
85
|
'shape': grb.values.shape
|
|
18
86
|
})
|
|
19
|
-
if target_param:
|
|
20
|
-
try:
|
|
21
|
-
grb = grbs.select(shortName=target_param)[0]
|
|
22
|
-
except:
|
|
23
|
-
try:
|
|
24
|
-
grb = grbs.select(parameterName=target_param)[0]
|
|
25
|
-
except:
|
|
26
|
-
raise ValueError(f"未找到参数: {target_param}")
|
|
27
|
-
else:
|
|
28
|
-
grb = grbs[1]
|
|
29
|
-
data = grb.values
|
|
30
|
-
lats, lons = grb.latlons()
|
|
31
87
|
return {
|
|
32
88
|
'data': data,
|
|
33
89
|
'lats': lats,
|
|
@@ -36,7 +92,8 @@ def readGrib(file_path, target_param=None):
|
|
|
36
92
|
'parameterName': grb.parameterName,
|
|
37
93
|
'level': grb.level,
|
|
38
94
|
'validDate': grb.validDate,
|
|
39
|
-
'units': grb.units
|
|
95
|
+
'units': grb.units,
|
|
96
|
+
"field_info":field_info
|
|
40
97
|
}
|
|
41
98
|
}
|
|
42
99
|
except Exception as e:
|
|
@@ -50,23 +107,5 @@ if __name__ == "__main__":
|
|
|
50
107
|
print("经度范围:", np.min(result['lons']), "~", np.max(result['lons']))
|
|
51
108
|
print("纬度范围:", np.min(result['lats']), "~", np.max(result['lats']))
|
|
52
109
|
print("参数单位:", result['metadata']['units'])
|
|
53
|
-
|
|
54
|
-
latArr = latMat[:,0]
|
|
55
|
-
lonArr = lonMat[0]
|
|
56
|
-
"""
|
|
110
|
+
|
|
57
111
|
|
|
58
|
-
import numpy as np
|
|
59
|
-
from pathlib import Path
|
|
60
|
-
def npsavez(output, data):
|
|
61
|
-
output = output.replace('.npy', '.npz')
|
|
62
|
-
output_path = Path(output).with_suffix('.npz')
|
|
63
|
-
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
64
|
-
np.savez_compressed(output_path, data=data)
|
|
65
|
-
print(f"{output_path} done")
|
|
66
|
-
def nploadz(output_path):
|
|
67
|
-
return np.load(Path(output_path).with_suffix('.npz'))['data']
|
|
68
|
-
|
|
69
|
-
"""
|
|
70
|
-
savez(output, data)
|
|
71
|
-
nploadz(output_path)
|
|
72
|
-
"""
|
shancx/npz.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
def npzSave(output, data):
|
|
4
|
+
output = output.replace('.npy', '.npz')
|
|
5
|
+
output_path = Path(output).with_suffix('.npz')
|
|
6
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
7
|
+
np.savez_compressed(output_path, data=data)
|
|
8
|
+
print(f"{output_path} done")
|
|
9
|
+
def npzLoad(output_path):
|
|
10
|
+
return np.load(Path(output_path).with_suffix('.npz'))['data']
|
|
11
|
+
|
|
12
|
+
"""
|
|
13
|
+
npzSave(output, data)
|
|
14
|
+
npzLoad(output_path)
|
|
15
|
+
"""
|
shancx/sendM.py
CHANGED
|
@@ -41,14 +41,13 @@ def sendMES(message,key='0f313-17b2-4e3d-84b8-3f9c290fa596',NN = None):
|
|
|
41
41
|
elif NN=="MSGAF":
|
|
42
42
|
webHookUrl ="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=5797fbe9-1c19-4a67-a6fc-83a85cb464a7"
|
|
43
43
|
elif NN=="GOES18C":
|
|
44
|
-
webHookUrl ="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4ddac32b-692b-4aa0-9d69-0e655552ff17"
|
|
45
|
-
|
|
46
|
-
|
|
44
|
+
webHookUrl ="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=4ddac32b-692b-4aa0-9d69-0e655552ff17"
|
|
47
45
|
elif NN=="GFS":
|
|
48
|
-
webHookUrl ="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=13f39c2f-e191-4100-b1ee-7316ac9c2451"
|
|
49
|
-
else:
|
|
50
46
|
webHookUrl ="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=13f39c2f-e191-4100-b1ee-7316ac9c2451"
|
|
51
|
-
|
|
47
|
+
elif NN=="WTX":
|
|
48
|
+
webHookUrl ="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=44869d2b-ab46-4bad-9621-4cda03470908"
|
|
49
|
+
else:
|
|
50
|
+
webHookUrl ="https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=13f39c2f-e191-4100-b1ee-7316ac9c2451"
|
|
52
51
|
try:
|
|
53
52
|
url=webHookUrl
|
|
54
53
|
headers = {"Content-Type":"application/json"}
|
|
@@ -56,4 +55,4 @@ def sendMES(message,key='0f313-17b2-4e3d-84b8-3f9c290fa596',NN = None):
|
|
|
56
55
|
res = requests.post(url,json=data,headers=headers)
|
|
57
56
|
except Exception as e:
|
|
58
57
|
print(e)
|
|
59
|
-
|
|
58
|
+
#sendMES(f" {sat_cd} {UTC}",NN="WTX")
|
|
@@ -5,7 +5,7 @@ shancx/Lib.py,sha256=GUAspllSxk39mvj-F1Q8Ys0EcY_lQfZPRGPE7L3x4SE,977
|
|
|
5
5
|
shancx/Path1.py,sha256=vX4A5RgdwVyIVVNZRocl18rOu1Z8YMLfDb0B92-0bE8,5334
|
|
6
6
|
shancx/Point.py,sha256=gyIomOVbNoz6SOcAhhTS26NHBeJ0TOwB-ljNsBWN1ZE,1909
|
|
7
7
|
shancx/QC.py,sha256=MFxbPMEwrnk5l0sTFMrx13wPlXJ5WI5TovzaiBoEr0E,9848
|
|
8
|
-
shancx/Read.py,sha256=
|
|
8
|
+
shancx/Read.py,sha256=g6UWxK0iEVyBU3cOpvw9fiTYvOrC3uHRPFR1WTlWWRU,4583
|
|
9
9
|
shancx/Resize.py,sha256=uwiOhD-aWPDLvIwOYEX-XXN_suOXv6hPDcaGyr4JLC4,4044
|
|
10
10
|
shancx/ZR.py,sha256=5APchqVoI1G2MkrN9YJSWCwfLUygcSFIsUXSo9aq1Qg,341
|
|
11
11
|
shancx/__init__.py,sha256=6sfiHElECqfy5eEEGqaCAw4YiKxgKfJ5IFFh9yvtewk,11442
|
|
@@ -17,7 +17,8 @@ shancx/geosProj.py,sha256=JMmtj3kV8rtFkKbmwvTKuDWlC8AkgLG8JvCPzkn8Dfs,2685
|
|
|
17
17
|
shancx/getResponse.py,sha256=QmJfa4czGCOiKb8RuCLXKE7AAKVADAptNiwn7v1tfbM,1055
|
|
18
18
|
shancx/info.py,sha256=0rk_L8Z5uj9RliaZrzT-CArH9ZObmdmcp-1RxAItL08,1122
|
|
19
19
|
shancx/netdfJU.py,sha256=96KR9NMLr2Kcs_OOMpX7QPujdWUj0GCEUIZ7_4_pBxw,7741
|
|
20
|
-
shancx/
|
|
20
|
+
shancx/npz.py,sha256=ZydULlx1ZhgwxrdIrVDm9lGa8KY4q1MJ9dZo_wQriso,462
|
|
21
|
+
shancx/sendM.py,sha256=dcqGNPPy1n0NxrRgOrHZEpaGtLy15FS_lCYe6_TkvSs,3510
|
|
21
22
|
shancx/wait.py,sha256=OgT9JmEgMH7d3ir3TEksx0_Kyl_S4bkOKl50jvVpdFA,8575
|
|
22
23
|
shancx/3D/__init__.py,sha256=pDFvW1OOEW-ueXH50NJIYdWlySHZU6sr5htzHUSeROo,1000
|
|
23
24
|
shancx/Algo/Class.py,sha256=bxJ9JhIjoxl0hJfjO126YrnyHWDpxCEAZ7qcPs0Y0ic,377
|
|
@@ -62,15 +63,16 @@ shancx/H9/__init__.py,sha256=FCarcXfU2tVD2KrCGKNNUuL51zAWZYHV7lrP7gntvaI,4911
|
|
|
62
63
|
shancx/H9/ahi_read_hsd.py,sha256=bt9oOOARcXijmyGpmHYXj0NKnuTntZjqx0_tu6Vp2vs,33522
|
|
63
64
|
shancx/H9/ahisearchtable.py,sha256=e2kpz-P5npgL4gzNxn8igERJuWWIysvTNLkptr5_Zcc,9579
|
|
64
65
|
shancx/H9/geometry.py,sha256=ZoCfgP07ANqDxyLf8uFN016DPl7QqW9pAV3sr2ekCng,95893
|
|
65
|
-
shancx/Hug/__init__.py,sha256=
|
|
66
|
+
shancx/Hug/__init__.py,sha256=uSPle6I70G6C6UzOs3OABiGqvlhxG3cr3xmP4kFStiE,3037
|
|
66
67
|
shancx/Mos/__init__.py,sha256=IDlCLNN6a_mguYmRcAPcevHSDaud5ZhvYNSuASqztcE,1563
|
|
67
68
|
shancx/NN/__init__.py,sha256=ENgZxIzUJzkuxArkcH-1JlxQA08Urq2ppFkGCbIURZk,9219
|
|
69
|
+
shancx/NN/chainMul.py,sha256=p7q_jrgFQkkfAYr_ZJFirwZOYoyn31QOVYERwhBGH4k,8649
|
|
68
70
|
shancx/Plot/GlobMap.py,sha256=WRvZOf5bo8IfECk1-sN4g6K0CrcJhSoEJAiLvZAWDwg,13021
|
|
69
71
|
shancx/Plot/Gray2RGB.py,sha256=yDXu1xWVi7OtrS8_ExxK_ev1988kj1ZxoNCWf-kl4Zc,2734
|
|
70
72
|
shancx/Plot/__init__.py,sha256=_fmDFZfh9HzDPBPKCMPrDT2DsblLVfrO-uFvUg76a74,19194
|
|
71
73
|
shancx/Plot/draw_day_CR_PNG.py,sha256=ftOYdk80Xmry_H1uaypjrTUA0mJZBV7EGt5T7JfVg0E,7267
|
|
72
74
|
shancx/Plot/exam.py,sha256=XXyBQp1Eb1siNiu7FY8ACiWH0g43ZSLTOGLqs3aKomE,3966
|
|
73
|
-
shancx/Plot/plotGlobal.py,sha256=
|
|
75
|
+
shancx/Plot/plotGlobal.py,sha256=X06RvuTDqX9S1WUx_mZ_u7RJ96w1HWk_ca5N47YZfo0,20367
|
|
74
76
|
shancx/Plot/radarNmc.py,sha256=ixmoDaoT6whmmrv4wD4p7H_DiNsyOEb2tUoz2KQgDeM,1273
|
|
75
77
|
shancx/Plot/single_china_map.py,sha256=cRmZW6TtLBwLb-Y3syim_K1jM350CmB5zkK1nrAcaRQ,2116
|
|
76
78
|
shancx/RdPzl/__init__.py,sha256=I5Isz3KfDgFZAdlRk4D-Hvp-8xH4F2Dl0XGxYGzUcKc,1166
|
|
@@ -85,7 +87,7 @@ shancx/Train/multiGpu.py,sha256=D_oZeiSc7VWktpnVDwrFOC1CYZSt9rxOKY5lngE5vFg,820
|
|
|
85
87
|
shancx/Train/prepare.py,sha256=vL_8UOA66oZCBIwCICtihsGibivtNgaVJGulJxfNdn8,6793
|
|
86
88
|
shancx/Train/renet50.py,sha256=wEhYk1X96WE5zuqHqVxWLJa-A5jDNkz4z6edORNufnA,6428
|
|
87
89
|
shancx/tensBoard/__init__.py,sha256=ga2C5YyJITvvQA1ocpxna_KNFnNRJVwkTjLoIglLZUQ,993
|
|
88
|
-
shancx-1.9.33.
|
|
89
|
-
shancx-1.9.33.
|
|
90
|
-
shancx-1.9.33.
|
|
91
|
-
shancx-1.9.33.
|
|
90
|
+
shancx-1.9.33.226.dist-info/METADATA,sha256=sZuxCrfePOwXAfvoPyNJNqhaHW3MQcQAh-JDl-bn2Lo,850
|
|
91
|
+
shancx-1.9.33.226.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
92
|
+
shancx-1.9.33.226.dist-info/top_level.txt,sha256=akfCS1vKWz3pNmEN_yN9ZiGp-60IQY5ET38mRx_i_-4,7
|
|
93
|
+
shancx-1.9.33.226.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|