├── icon.ico
├── resources.qrc
├── download.py
├── README.md
├── query.py
├── cdse-data-download.py
├── main.ui
├── ui_main.py
├── resources_rc.py
└── main.py
/icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/fangdt/cdse-data-download/HEAD/icon.ico
--------------------------------------------------------------------------------
/resources.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | icon.ico
4 |
5 |
6 |
--------------------------------------------------------------------------------
/download.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import requests
4 |
5 |
6 | # 获取CDSE的Access token
7 | def get_access_token(username: str, password: str) -> str:
8 | data = {
9 | "client_id": "cdse-public",
10 | "username": username,
11 | "password": password,
12 | "grant_type": "password",
13 | }
14 | req = requests.post(
15 | "https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token",
16 | data=data,
17 | )
18 | req.raise_for_status()
19 | return req.json()["access_token"]
20 |
21 |
22 | # 获取产品响应
23 | def download_response(data_id, token):
24 | url = f"https://download.dataspace.copernicus.eu/odata/v1/Products({data_id})/$value"
25 | headers = {"Authorization": f"Bearer {token}"}
26 | session = requests.Session()
27 | session.headers.update(headers)
28 | response = session.get(url, headers=headers, stream=True)
29 | return response
30 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CDSE 数据下载工具 v2.0
2 |
3 | ## 项目背景
4 |
5 | 2023年5月1日,利用第三方 [sentinelsat][link1] 库,写了一个界面化的哨兵卫星数据下载工具。可好景不长,哥白尼开放数据访问中心([Copernicus Open Access Hub][link2])在2023年10月底关闭了,原来写的工具已经无法使用了。为了能够在新的网站——哥白尼数据空间生态系统([Copernicus Data Space Ecosystem][link3])批量下载哨兵卫星数据,2024年5月,利用网站提供的 [OData][link4] 的API,编写了这个——CDSE 数据下载工具。
6 |
7 | ## 使用说明
8 |
9 | **1. 如果您有Python环境:**
10 |
11 | 您只需要下载cdse-data-download.py这一个文件,安装pandas、requests、tqdm等第三方支持库,设置好您的CDSE账户、所需下载卫星平台、卫星名称包含文字、起止日期、GeoJSON文件、保存位置等,运行即可。
12 |
13 | **2. 如果您没有Python环境,且为windows 10及以上操作系统**
14 |
15 | 您可以clone项目,或者直接下载 [Releases][link5] 中已经编译好的windows界面化工具。
16 |
17 | ## 如何使用
18 |
19 | ### CDSE 账户
20 | 账户和密码需要在哥白尼数据空间生态系统([Copernicus Data Space Ecosystem][link3])注册。
21 |
22 | ### 查询设置
23 | 卫星平台和产品类型按需选择,起止日期和截止日期格式为:YYYY-MM-DD,例如:2024-05-01
24 |
25 | #### 卫星平台和产品类型
26 | + SENTINEL-1(哨兵一号)
27 | + Level 0
28 | + IW_RAW:原始影像
29 | + Level 1
30 | + IW_SLC:单视复数影像,Single Look Complex
31 | + IW_GRDH:地距多视影像,Ground Range Detected
32 | + IW_ETA:扩展时序注释数据集,The Extended Timing Annotation Dataset
33 | + 注:IW:Interferometric Wide swath
34 |
35 |
36 | + SENTINEL-2(哨兵二号)
37 | + Level 1C
38 | + MSIL1C:大气层顶部 (TOA) 反射率图像
39 | + Level 2A
40 | + MSIL2A:经大气校正的表面反射率 (SR) 产品
41 | + 注:MSI:MultiSpectral Instrument
42 |
43 |
44 | + SENTINEL-3(哨兵三号)
45 | + OLCI:Ocean and Land Colour Instrument
46 | + Level 1B
47 | + OL_1_EFR:全分辨率 TOA 反射率
48 | + OL_1_ERR:降低分辨率 TOA 反射率
49 | + Level 2
50 | + OL_2_LFR:全分辨率陆地和大气地球物理产品
51 | + OL_2_LRR:低分辨率陆地和大气地球物理产品
52 | + OL_2_WFR:全分辨率水和大气地球物理产品
53 | + OL_2_WRR:低分辨率水和大气地球物理产品
54 | + SLSTR:Sea and Land Surface Temperature Radiometer
55 | + Level 1
56 | + SL_1_RBT:亮度温度和辐射率
57 | + Level 2
58 | + SL_2_AOD:气溶胶光学深度
59 | + SL_2_FRP:火灾辐射功率
60 | + SL_2_LST:地表温度参数
61 | + SL_2_WST:海面温度参数
62 | + SYNERGY
63 | + Level 2
64 | + SY_2_AOD:超像素分辨率的陆地和海洋全球气溶胶参数(4.5 km x 4.5 km)
65 | + SY_2_SYN:陆地表面反射率和气溶胶参数
66 | + SY_2_VG1:1 km 类植物产品 TOA 反射率
67 | + SY_2_VGP:1 km 类植物产品 1 天合成表面反射率和 NDVI
68 |
69 |
70 | + SENTINEL-5P(哨兵五号)
71 | + Level 2
72 | + OFFL_L2__AER_AI:紫外线气溶胶指数
73 | + OFFL_L2__AER_LH:气溶胶层高度(中等气压)
74 | + OFFL_L2__CLOUD:云量、反照率、云顶大气压
75 | + OFFL_L2__CH4:甲烷(CH4)总柱含量
76 | + OFFL_L2__CO:一氧化碳(CO)总柱含量
77 | + OFFL_L2__HCHO:甲醛(HCHO)总柱含量
78 | + OFFL_L2__NO2:二氧化氮(NO2)总柱含量、对流层柱含量
79 | + OFFL_L2__O3:臭氧(O3)总柱含量
80 | + OFFL_L2__SO2:二氧化硫(SO2)总柱含量
81 | + 注:OFFL:OFFLINE,离线数据流
82 |
83 |
84 | + SENTINEL-6(哨兵六号)
85 | + Level 1
86 | + P4_1B_LR:1B 级低分辨率(LR)产品
87 | + Level 2
88 | + P4_2__LR:低分辨率(LR)产品
89 | + 注:P4:Poseidon-4,波塞冬-4 或 海神-4
90 |
91 |
92 | ### GeoJSON文件
93 | GeoJSON文件为任务区范围文件,可在网站 [geojson.io][link6] 上获取。
94 | 获取方法:进入网站后,划定任务区域,点击Save,保存为GeoJSON格式文件即可。
95 |
96 | ### 保存位置
97 | 填写下载数据的文件夹保存路径。
98 |
99 | ### 数据查询
100 | 用于显示数据查询结果。
101 |
102 | ### 控制面板
103 | #### 查询
104 | 可以查询覆盖任务区范围的数据产品,并将产品名称显示在"数据查询"框内。
105 | 1. 选择正确的卫星平台、产品类型;
106 | 2. 填写起始日期、截止日期;
107 | 3. 选择输入GeoJSON文件;
108 | 4. 点击"查询"按钮。
109 |
110 | #### 下载
111 | 下载查询到数据产品,将其保存在"保存位置"文件夹内。
112 | 注:点击"下载"按钮前,应先点击"查询"按钮查询数据,只有当"数据查询"框内有产品名称时,点击"下载"按钮才能工作。
113 | 1. 填写 CDSE 账号和密码;
114 | 2. 选择数据保存位置文件夹;
115 | 3. 点击"下载"按钮,开始下载数据。
116 |
117 | #### 清空
118 | 将"数据查询"框内显示文本清空。
119 |
120 | #### 导出
121 | 将"数据查询"框内显示文本导出TXT文件。
122 |
123 | #### 关于
124 | 显示工具的版本、开发、主页等信息。
125 |
126 | ### 下载状态
127 | 显示当前下载文件文件名、当前文件下载进度和所有文件下载进度等信息。
128 |
129 | ## 参考资料
130 | 过程参考:微信公众号——海研人
131 | https://mp.weixin.qq.com/s/8vWCMYy_pkwauVkwZd3rYQ
132 |
133 | 过程参考:微信公众号——小y只会写bug
134 | https://mp.weixin.qq.com/s/aEbsusU8FIrJTRorQR1oPQ
135 |
136 | 过程参考:CSDN——hyzhao_RS
137 | https://blog.csdn.net/mrzhy1/article/details/132921422
138 |
139 | OData API官方文档
140 | https://documentation.dataspace.copernicus.eu/APIs/OData.html
141 |
142 | Access token官方文档(已无python方法,参考“海研人”代码)
143 | https://documentation.dataspace.copernicus.eu/APIs/Token.html
144 |
145 | [link1]:https://sentinelsat.readthedocs.io/
146 | [link2]:https://scihub.copernicus.eu/
147 | [link3]:https://dataspace.copernicus.eu/
148 | [link4]:https://documentation.dataspace.copernicus.eu/APIs/OData.html
149 | [link5]:https://github.com/fangdt/cdse-data-download/releases
150 | [link6]:http://geojson.io/#map=2/0/20
--------------------------------------------------------------------------------
/query.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | import pandas as pd
4 | import requests
5 | import json
6 | import re
7 | from datetime import datetime
8 |
9 |
10 | # 获取ROI的坐标字符串
11 | # Disclaimers:
12 | # 1.MULTIPOLYGON is currently not supported.
13 | # 2.Polygon must start and end with the same point.
14 | # 3.Coordinates must be given in EPSG 4326
15 | def get_coordinates(geojson):
16 | with open(geojson, 'r') as f:
17 | data = f.read()
18 | geojson_data = json.loads(data)
19 | coordinates = geojson_data['features'][0]['geometry']['coordinates'][0]
20 | coordinates_string = ''
21 | for i in range(len(coordinates)):
22 | coordinates_string = coordinates_string + str(coordinates[i][0]) + ' ' + str(coordinates[i][1]) + ', '
23 | coordinates_string = coordinates_string[:-2]
24 | return coordinates_string
25 |
26 |
27 | # 检查输入起始日期和截止日期格式
28 | def check_date_range(start_date_str, end_date_str):
29 | # 定义日期格式的正则表达式
30 | date_pattern = re.compile(r'^\d{4}-\d{2}-\d{2}$')
31 | # 检查起始日期和截止日期的格式,
32 | # 起始日期格式错误,返回值1
33 | if not date_pattern.match(start_date_str):
34 | return 1
35 | # 截止日期格式错误,返回值2
36 | if not date_pattern.match(end_date_str):
37 | return 2
38 | # 尝试解析日期,并检查它们的有效性
39 | try:
40 | start_date = datetime.strptime(start_date_str, '%Y-%m-%d')
41 | end_date = datetime.strptime(end_date_str, '%Y-%m-%d')
42 | # 确保起始日期在截止日期之前或相同,如果起始日期在截止日期之后,返回值3
43 | if start_date > end_date:
44 | return 3
45 | # 如果一切正常,返回值9
46 | return 9
47 | except ValueError:
48 | # 如果解析日期时发生错误(例如,2月30日),返回值4
49 | return 4
50 |
51 |
52 | # 获取查询链接,检索条件之间要加 and
53 | def get_https_request(satellite, contains, start_date, end_date, geojson, expand):
54 | # 基础前缀
55 | base_prefix = "https://catalogue.dataspace.copernicus.eu/odata/v1/Products?$filter="
56 | # 检索条件
57 | collection = "Collection/Name eq '" + satellite + "' and contains(Name,'" + contains + "')"
58 | roi_coordinates = get_coordinates(geojson)
59 | geographic_criteria = "OData.CSC.Intersects(area=geography'SRID=4326;POLYGON((" + roi_coordinates + "))') "
60 | content_date = (
61 | "ContentDate/Start gt " + start_date + "T00:00:00.000Z and " +
62 | "ContentDate/Start lt " + end_date + "T00:00:00.000Z"
63 | )
64 | # Top option specifies the maximum number of items returned from a query.
65 | # The default value is set to 20.
66 | # The acceptable arguments for this option: Integer <0,1000>
67 | top_option = "&$top=1000"
68 | # The expand option enables users to see the full metadata of each returned result.
69 | # The acceptable arguments for this option: Attributes, Assets and Locations.
70 | # Expand assets allows to list additional assets of products, including quicklooks
71 | # Expand Locations allows users to see full list of available products’ forms (compressed/uncompressed)
72 | # and locations from which they can be downloaded
73 | # 暂时不开发expand_option功能,expand为空,但保留expand_option语句
74 | if not expand:
75 | # 最终检索链接
76 | https_request = (
77 | base_prefix + collection + " and " + geographic_criteria + " and " + content_date + top_option
78 | )
79 | else:
80 | expand_option = "&$expand=" + expand
81 | # 最终检索链接
82 | https_request = (
83 | base_prefix + collection + " and " + geographic_criteria + " and " + content_date + top_option +
84 | expand_option
85 | )
86 | return https_request
87 |
88 |
89 | # 更改文件名称后缀,将其均改为.zip
90 | def chang_extension(query_satellite, name):
91 | file_name = ''
92 | if query_satellite == 'SENTINEL-1':
93 | file_name = name.replace(".SAFE", ".zip")
94 | elif query_satellite == 'SENTINEL-2':
95 | file_name = name.replace(".SAFE", ".zip")
96 | elif query_satellite == 'SENTINEL-3':
97 | file_name = name.replace(".SEN3", ".zip")
98 | elif query_satellite == 'SENTINEL-5P':
99 | file_name = name.replace(".nc", ".zip")
100 | elif query_satellite == 'SENTINEL-6':
101 | file_name = name.replace(".SEN6", ".zip")
102 | return file_name
103 |
104 |
105 | # 获取数据ID、NAME和Length
106 | def get_datas(url):
107 | res = requests.get(url)
108 | data_json = res.json()
109 | res.close()
110 | if 'value' in data_json:
111 | df = pd.DataFrame.from_dict(data_json['value'])
112 | # 获取成功,但为查询到数据,返回值1
113 | if len(df) == 0:
114 | return 1
115 | # 获取成功,并查询到数据,返回原始数据id,name,length列表
116 | id_list = df.Id
117 | name_list = df.Name
118 | length_list = df.ContentLength
119 | return id_list, name_list, length_list
120 | else:
121 | # 获取失败,返回值2
122 | return 2
123 |
--------------------------------------------------------------------------------
/cdse-data-download.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | """
3 | Created on May 20, 2024
4 |
5 | 针对哥白尼数据空间生态系统 Copernicus Data Space Ecosystem(CDSE)
6 | 使用OData API批量下载数据
7 |
8 | 过程参考:微信公众号——海研人
9 | https://mp.weixin.qq.com/s/8vWCMYy_pkwauVkwZd3rYQ
10 |
11 | 过程参考:微信公众号——小y只会写bug
12 | https://mp.weixin.qq.com/s/aEbsusU8FIrJTRorQR1oPQ
13 |
14 | 过程参考:CSDN——hyzhao_RS
15 | https://blog.csdn.net/mrzhy1/article/details/132921422
16 |
17 | OData API官方文档
18 | https://documentation.dataspace.copernicus.eu/APIs/OData.html
19 |
20 | Access token官方文档(已无python方法,参考“海研人”代码)
21 | https://documentation.dataspace.copernicus.eu/APIs/Token.html
22 |
23 | @author:辽宁省自然资源卫星应用技术中心 技术组
24 | """
25 |
26 | import os
27 | import sys
28 | import pandas as pd
29 | import requests
30 | import json
31 | import datetime
32 | from tqdm import tqdm
33 |
34 | # 基础设置
35 | ############################################################################
36 | # 1 所需卫星数据
37 | query_satellite = 'SENTINEL-2'
38 |
39 | # 2 检索时文件名需包括的字符串,可以是产品类型,如:SLC;可以是产品级别,如:L2A;也可以是区块代码,如:RVQ
40 | query_contains = 'L2A'
41 |
42 | # 3 起始日期
43 | query_startDate = '2023-09-01'
44 | query_endDate = '2023-09-15'
45 |
46 | # 4 检索区域 在如下网站绘制geojson文件 https://geojson.io/#map=2/0/20
47 | map_geojson = 'map.geojson'
48 |
49 | # 5 expand option,暂时未开发此功能
50 | query_expand = ''
51 |
52 | # 6 哥白尼数据空间生态系统账号密码 在如下网站申请:https://dataspace.copernicus.eu/
53 | CDSE_email = '您的账号'
54 | CDSE_password = '您的密码'
55 |
56 | # 7 数据保存路径
57 | output_dir = 'D:/Data/'
58 | ############################################################################
59 |
60 |
61 | # 获取CDSE的Access token
62 | def get_access_token(username: str, password: str) -> str:
63 | data = {
64 | "client_id": "cdse-public",
65 | "username": username,
66 | "password": password,
67 | "grant_type": "password",
68 | }
69 | try:
70 | r = requests.post(
71 | "https://identity.dataspace.copernicus.eu/auth/realms/CDSE/protocol/openid-connect/token",
72 | data=data,
73 | )
74 | r.raise_for_status()
75 | except Exception as e:
76 | raise Exception(
77 | f"Access token creation failed. Reponse from the server was: {r.json()}"
78 | )
79 | return r.json()["access_token"]
80 |
81 |
82 | # 获取ROI的坐标字符串
83 | # Disclaimers:
84 | # 1.MULTIPOLYGON is currently not supported.
85 | # 2.Polygon must start and end with the same point.
86 | # 3.Coordinates must be given in EPSG 4326
87 | def get_coordinates(geojson):
88 | with open(geojson, 'r') as f:
89 | data = f.read()
90 | geojson_data = json.loads(data)
91 | coordinates = geojson_data['features'][0]['geometry']['coordinates'][0]
92 | coordinates_string = ''
93 | for i in range(len(coordinates)):
94 | coordinates_string = coordinates_string + str(coordinates[i][0]) + ' ' + str(coordinates[i][1]) + ', '
95 | coordinates_string = coordinates_string[:-2]
96 | return coordinates_string
97 |
98 |
99 | # 获取查询链接 检索条件之间要加 and
100 | def get_https_request(satellite, contains, start_date, end_date, geojson, expand):
101 | # 基础前缀
102 | base_prefix = "https://catalogue.dataspace.copernicus.eu/odata/v1/Products?$filter="
103 | # 检索条件
104 | collection = "Collection/Name eq '" + satellite + "' and contains(Name,'" + contains + "')"
105 | roi_coordinates = get_coordinates(geojson)
106 | geographic_criteria = "OData.CSC.Intersects(area=geography'SRID=4326;POLYGON((" + roi_coordinates + "))') "
107 | content_date = (
108 | "ContentDate/Start gt " + start_date + "T00:00:00.000Z and " +
109 | "ContentDate/Start lt " + end_date + "T00:00:00.000Z"
110 | )
111 | # Top option specifies the maximum number of items returned from a query.
112 | # The default value is set to 20.
113 | # The acceptable arguments for this option: Integer <0,1000>
114 | top_option = "&$top=1000"
115 | # The expand option enables users to see the full metadata of each returned result.
116 | # The acceptable arguments for this option: Attributes, Assets and Locations.
117 | # Expand assets allows to list additional assets of products, including quicklooks
118 | # Expand Locations allows users to see full list of available products’ forms (compressed/uncompressed)
119 | # and locations from which they can be downloaded
120 | # 暂时不开发expand_option功能,expand为空,但保留expand_option语句
121 | if not expand:
122 | # 最终检索链接
123 | https_request = (
124 | base_prefix + collection + " and " + geographic_criteria + " and " + content_date + top_option
125 | )
126 | else:
127 | expand_option = "&$expand=" + expand
128 | # 最终检索链接
129 | https_request = (
130 | base_prefix + collection + " and " + geographic_criteria + " and " + content_date + top_option +
131 | expand_option
132 | )
133 | return https_request
134 |
135 |
136 | # 下载数据
137 | def download_data(token, id, name, length, output):
138 | url = f"https://download.dataspace.copernicus.eu/odata/v1/Products({id})/$value"
139 | headers = {"Authorization": f"Bearer {token}"}
140 | session = requests.Session()
141 | session.headers.update(headers)
142 | response = session.get(url, headers=headers, stream=True)
143 | try:
144 | print('[', datetime.datetime.strftime(datetime.datetime.now(), '%H:%M:%S'), '] '+'开始下载: '+name)
145 | with open(output, "wb") as file:
146 | if length is not None:
147 | # 使用total参数设置进度条的总长度
148 | pbar = tqdm(total=length, unit="B", unit_scale=True, desc=name)
149 | for chunk in response.iter_content(chunk_size=8192):
150 | if chunk:
151 | file.write(chunk)
152 | # 更新进度条
153 | pbar.update(len(chunk))
154 | # 确保进度条完成
155 | pbar.close()
156 | print('[', datetime.datetime.strftime(datetime.datetime.now(), '%H:%M:%S'), '] '+'下载成功: '+name)
157 | response.close()
158 | except Exception as e:
159 | print('[', datetime.datetime.strftime(datetime.datetime.now(), '%H:%M:%S'), '] '+'下载失败: '+name)
160 | print(f"发生了一个异常: {e}")
161 |
162 |
163 | # 将文件名后缀改为.zip
164 | def get_file_name(name):
165 | file_name = ''
166 | if query_satellite == 'SENTINEL-1':
167 | file_name = name.replace(".SAFE", ".zip")
168 | elif query_satellite == 'SENTINEL-2':
169 | file_name = name.replace(".SAFE", ".zip")
170 | elif query_satellite == 'SENTINEL-3':
171 | file_name = name.replace(".SEN3", ".zip")
172 | elif query_satellite == 'SENTINEL-5P':
173 | file_name = name.replace(".nc", ".zip")
174 | elif query_satellite == 'SENTINEL-6':
175 | file_name = name.replace(".SEN6", ".zip")
176 | return file_name
177 |
178 |
179 | # 进行检索
180 | request_url = get_https_request(
181 | query_satellite, query_contains, query_startDate, query_endDate, map_geojson, query_expand
182 | )
183 | JSON = requests.get(request_url).json()
184 | if 'detail' in JSON:
185 | print(JSON['detail']['message'])
186 | sys.exit()
187 | elif 'value' in JSON:
188 | df = pd.DataFrame.from_dict(JSON['value'])
189 | # print(df.columns)
190 | if len(df) == 0:
191 | print('未查询到数据')
192 | sys.exit()
193 | # 原始数据id列表
194 | data_id_list = df.Id
195 | # 原始数据name列表
196 | data_name_list = df.Name
197 | # 原始数据length列表
198 | date_content_length = df.ContentLength
199 | else:
200 | print('存在未知查询错误')
201 | sys.exit()
202 |
203 | for i in range(len(data_id_list)):
204 | print(data_name_list[i])
205 | data_id = data_id_list[i]
206 | data_name = get_file_name(data_name_list[i])
207 | data_length = date_content_length[i]
208 | # 判断数据保存路径是否存在,如不存在则创建数据保存路径
209 | if not os.path.exists(output_dir):
210 | os.makedirs(output_dir)
211 | output_file = os.path.join(output_dir, data_name)
212 | # 判断文件是否已经下载,如已经下载则跳过,不再下载
213 | if os.path.exists(output_file) and os.path.getsize(output_file) == data_length:
214 | print(output_file + ' 已经存在,跳过下载')
215 | else:
216 | access_token = get_access_token(CDSE_email, CDSE_password)
217 | download_data(access_token, data_id, data_name, data_length, output_file)
218 |
--------------------------------------------------------------------------------
/main.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 900
10 | 600
11 |
12 |
13 |
14 | CDSE 数据下载工具 v2.0
15 |
16 |
17 |
18 | :/images/icon.ico:/images/icon.ico
19 |
20 |
21 |
22 | -
23 |
24 |
-
25 |
26 |
27 | CDSE 账号
28 |
29 |
30 |
-
31 |
32 |
33 | 账号:
34 |
35 |
36 |
37 | -
38 |
39 |
40 |
41 | 120
42 | 0
43 |
44 |
45 |
46 |
47 | -
48 |
49 |
50 | 密码:
51 |
52 |
53 |
54 | -
55 |
56 |
57 |
58 | 120
59 | 0
60 |
61 |
62 |
63 | QLineEdit::EchoMode::Password
64 |
65 |
66 |
67 |
68 |
69 |
70 | -
71 |
72 |
73 | 查询设置
74 |
75 |
76 |
-
77 |
78 |
79 | 卫星平台:
80 |
81 |
82 |
83 | -
84 |
85 |
86 |
87 | 160
88 | 0
89 |
90 |
91 |
92 |
93 | -
94 |
95 |
96 | 起始日期:
97 |
98 |
99 |
100 | -
101 |
102 |
103 |
104 | 120
105 | 0
106 |
107 |
108 |
109 |
110 | -
111 |
112 |
113 | 产品类型:
114 |
115 |
116 |
117 | -
118 |
119 |
120 |
121 | 160
122 | 0
123 |
124 |
125 |
126 |
127 | -
128 |
129 |
130 | 截止日期:
131 |
132 |
133 |
134 | -
135 |
136 |
137 |
138 | 120
139 | 0
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 | -
150 |
151 |
152 | GeoJSON文件
153 |
154 |
155 |
-
156 |
157 |
158 | -
159 |
160 |
161 | 选择
162 |
163 |
164 |
165 |
166 |
167 |
168 | -
169 |
170 |
171 | 保存位置
172 |
173 |
174 |
-
175 |
176 |
177 | -
178 |
179 |
180 | 选择
181 |
182 |
183 |
184 |
185 |
186 |
187 | -
188 |
189 |
-
190 |
191 |
192 | 数据查询
193 |
194 |
195 |
-
196 |
197 |
198 |
199 |
200 |
201 | -
202 |
203 |
204 | 控制面板
205 |
206 |
207 |
-
208 |
209 |
210 | 查询
211 |
212 |
213 |
214 | -
215 |
216 |
217 | 下载
218 |
219 |
220 |
221 | -
222 |
223 |
224 | 清空
225 |
226 |
227 |
228 | -
229 |
230 |
231 | 导出
232 |
233 |
234 |
235 | -
236 |
237 |
238 | 关于
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 | -
248 |
249 |
250 | 下载状态
251 |
252 |
253 |
-
254 |
255 |
256 | -
257 |
258 |
-
259 |
260 |
261 | 当前文件下载进度:
262 |
263 |
264 |
265 | -
266 |
267 |
268 | 0
269 |
270 |
271 |
272 | -
273 |
274 |
275 | 所有文件下载进度:
276 |
277 |
278 |
279 | -
280 |
281 |
282 | 0
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
--------------------------------------------------------------------------------
/ui_main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | ################################################################################
4 | ## Form generated from reading UI file 'main.ui'
5 | ##
6 | ## Created by: Qt User Interface Compiler version 6.7.0
7 | ##
8 | ## WARNING! All changes made in this file will be lost when recompiling UI file!
9 | ################################################################################
10 |
11 | from PySide6.QtCore import (QCoreApplication, QDate, QDateTime, QLocale,
12 | QMetaObject, QObject, QPoint, QRect,
13 | QSize, QTime, QUrl, Qt)
14 | from PySide6.QtGui import (QBrush, QColor, QConicalGradient, QCursor,
15 | QFont, QFontDatabase, QGradient, QIcon,
16 | QImage, QKeySequence, QLinearGradient, QPainter,
17 | QPalette, QPixmap, QRadialGradient, QTransform)
18 | from PySide6.QtWidgets import (QApplication, QComboBox, QGridLayout, QGroupBox,
19 | QHBoxLayout, QLabel, QLineEdit, QMainWindow,
20 | QProgressBar, QPushButton, QSizePolicy, QTextBrowser,
21 | QVBoxLayout, QWidget)
22 | import resources_rc
23 |
24 | class Ui_MainWindow(object):
25 | def setupUi(self, MainWindow):
26 | if not MainWindow.objectName():
27 | MainWindow.setObjectName(u"MainWindow")
28 | MainWindow.resize(900, 600)
29 | icon = QIcon()
30 | icon.addFile(u":/images/icon.ico", QSize(), QIcon.Normal, QIcon.Off)
31 | MainWindow.setWindowIcon(icon)
32 | self.centralwidget = QWidget(MainWindow)
33 | self.centralwidget.setObjectName(u"centralwidget")
34 | self.verticalLayout_3 = QVBoxLayout(self.centralwidget)
35 | self.verticalLayout_3.setObjectName(u"verticalLayout_3")
36 | self.horizontalLayout = QHBoxLayout()
37 | self.horizontalLayout.setObjectName(u"horizontalLayout")
38 | self.groupBox = QGroupBox(self.centralwidget)
39 | self.groupBox.setObjectName(u"groupBox")
40 | self.gridLayout = QGridLayout(self.groupBox)
41 | self.gridLayout.setObjectName(u"gridLayout")
42 | self.label = QLabel(self.groupBox)
43 | self.label.setObjectName(u"label")
44 |
45 | self.gridLayout.addWidget(self.label, 0, 0, 1, 1)
46 |
47 | self.lineEdit = QLineEdit(self.groupBox)
48 | self.lineEdit.setObjectName(u"lineEdit")
49 | self.lineEdit.setMinimumSize(QSize(120, 0))
50 |
51 | self.gridLayout.addWidget(self.lineEdit, 0, 1, 1, 1)
52 |
53 | self.label_2 = QLabel(self.groupBox)
54 | self.label_2.setObjectName(u"label_2")
55 |
56 | self.gridLayout.addWidget(self.label_2, 1, 0, 1, 1)
57 |
58 | self.lineEdit_2 = QLineEdit(self.groupBox)
59 | self.lineEdit_2.setObjectName(u"lineEdit_2")
60 | self.lineEdit_2.setMinimumSize(QSize(120, 0))
61 | self.lineEdit_2.setEchoMode(QLineEdit.EchoMode.Password)
62 |
63 | self.gridLayout.addWidget(self.lineEdit_2, 1, 1, 1, 1)
64 |
65 |
66 | self.horizontalLayout.addWidget(self.groupBox)
67 |
68 | self.groupBox_2 = QGroupBox(self.centralwidget)
69 | self.groupBox_2.setObjectName(u"groupBox_2")
70 | self.gridLayout_2 = QGridLayout(self.groupBox_2)
71 | self.gridLayout_2.setObjectName(u"gridLayout_2")
72 | self.label_3 = QLabel(self.groupBox_2)
73 | self.label_3.setObjectName(u"label_3")
74 |
75 | self.gridLayout_2.addWidget(self.label_3, 0, 0, 1, 1)
76 |
77 | self.comboBox = QComboBox(self.groupBox_2)
78 | self.comboBox.setObjectName(u"comboBox")
79 | self.comboBox.setMinimumSize(QSize(160, 0))
80 |
81 | self.gridLayout_2.addWidget(self.comboBox, 0, 1, 1, 1)
82 |
83 | self.label_5 = QLabel(self.groupBox_2)
84 | self.label_5.setObjectName(u"label_5")
85 |
86 | self.gridLayout_2.addWidget(self.label_5, 0, 2, 1, 1)
87 |
88 | self.lineEdit_3 = QLineEdit(self.groupBox_2)
89 | self.lineEdit_3.setObjectName(u"lineEdit_3")
90 | self.lineEdit_3.setMinimumSize(QSize(120, 0))
91 |
92 | self.gridLayout_2.addWidget(self.lineEdit_3, 0, 3, 1, 1)
93 |
94 | self.label_4 = QLabel(self.groupBox_2)
95 | self.label_4.setObjectName(u"label_4")
96 |
97 | self.gridLayout_2.addWidget(self.label_4, 1, 0, 1, 1)
98 |
99 | self.comboBox_2 = QComboBox(self.groupBox_2)
100 | self.comboBox_2.setObjectName(u"comboBox_2")
101 | self.comboBox_2.setMinimumSize(QSize(160, 0))
102 |
103 | self.gridLayout_2.addWidget(self.comboBox_2, 1, 1, 1, 1)
104 |
105 | self.label_6 = QLabel(self.groupBox_2)
106 | self.label_6.setObjectName(u"label_6")
107 |
108 | self.gridLayout_2.addWidget(self.label_6, 1, 2, 1, 1)
109 |
110 | self.lineEdit_4 = QLineEdit(self.groupBox_2)
111 | self.lineEdit_4.setObjectName(u"lineEdit_4")
112 | self.lineEdit_4.setMinimumSize(QSize(120, 0))
113 |
114 | self.gridLayout_2.addWidget(self.lineEdit_4, 1, 3, 1, 1)
115 |
116 |
117 | self.horizontalLayout.addWidget(self.groupBox_2)
118 |
119 | self.horizontalLayout.setStretch(0, 1)
120 | self.horizontalLayout.setStretch(1, 2)
121 |
122 | self.verticalLayout_3.addLayout(self.horizontalLayout)
123 |
124 | self.groupBox_3 = QGroupBox(self.centralwidget)
125 | self.groupBox_3.setObjectName(u"groupBox_3")
126 | self.gridLayout_3 = QGridLayout(self.groupBox_3)
127 | self.gridLayout_3.setObjectName(u"gridLayout_3")
128 | self.lineEdit_5 = QLineEdit(self.groupBox_3)
129 | self.lineEdit_5.setObjectName(u"lineEdit_5")
130 |
131 | self.gridLayout_3.addWidget(self.lineEdit_5, 0, 0, 1, 1)
132 |
133 | self.pushButton = QPushButton(self.groupBox_3)
134 | self.pushButton.setObjectName(u"pushButton")
135 |
136 | self.gridLayout_3.addWidget(self.pushButton, 0, 1, 1, 1)
137 |
138 |
139 | self.verticalLayout_3.addWidget(self.groupBox_3)
140 |
141 | self.groupBox_4 = QGroupBox(self.centralwidget)
142 | self.groupBox_4.setObjectName(u"groupBox_4")
143 | self.gridLayout_4 = QGridLayout(self.groupBox_4)
144 | self.gridLayout_4.setObjectName(u"gridLayout_4")
145 | self.lineEdit_6 = QLineEdit(self.groupBox_4)
146 | self.lineEdit_6.setObjectName(u"lineEdit_6")
147 |
148 | self.gridLayout_4.addWidget(self.lineEdit_6, 0, 0, 1, 1)
149 |
150 | self.pushButton_2 = QPushButton(self.groupBox_4)
151 | self.pushButton_2.setObjectName(u"pushButton_2")
152 |
153 | self.gridLayout_4.addWidget(self.pushButton_2, 0, 1, 1, 1)
154 |
155 |
156 | self.verticalLayout_3.addWidget(self.groupBox_4)
157 |
158 | self.horizontalLayout_2 = QHBoxLayout()
159 | self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
160 | self.groupBox_5 = QGroupBox(self.centralwidget)
161 | self.groupBox_5.setObjectName(u"groupBox_5")
162 | self.gridLayout_5 = QGridLayout(self.groupBox_5)
163 | self.gridLayout_5.setObjectName(u"gridLayout_5")
164 | self.textBrowser = QTextBrowser(self.groupBox_5)
165 | self.textBrowser.setObjectName(u"textBrowser")
166 |
167 | self.gridLayout_5.addWidget(self.textBrowser, 0, 0, 1, 1)
168 |
169 |
170 | self.horizontalLayout_2.addWidget(self.groupBox_5)
171 |
172 | self.groupBox_6 = QGroupBox(self.centralwidget)
173 | self.groupBox_6.setObjectName(u"groupBox_6")
174 | self.verticalLayout_2 = QVBoxLayout(self.groupBox_6)
175 | self.verticalLayout_2.setObjectName(u"verticalLayout_2")
176 | self.pushButton_3 = QPushButton(self.groupBox_6)
177 | self.pushButton_3.setObjectName(u"pushButton_3")
178 |
179 | self.verticalLayout_2.addWidget(self.pushButton_3)
180 |
181 | self.pushButton_4 = QPushButton(self.groupBox_6)
182 | self.pushButton_4.setObjectName(u"pushButton_4")
183 |
184 | self.verticalLayout_2.addWidget(self.pushButton_4)
185 |
186 | self.pushButton_5 = QPushButton(self.groupBox_6)
187 | self.pushButton_5.setObjectName(u"pushButton_5")
188 |
189 | self.verticalLayout_2.addWidget(self.pushButton_5)
190 |
191 | self.pushButton_6 = QPushButton(self.groupBox_6)
192 | self.pushButton_6.setObjectName(u"pushButton_6")
193 |
194 | self.verticalLayout_2.addWidget(self.pushButton_6)
195 |
196 | self.pushButton_7 = QPushButton(self.groupBox_6)
197 | self.pushButton_7.setObjectName(u"pushButton_7")
198 |
199 | self.verticalLayout_2.addWidget(self.pushButton_7)
200 |
201 |
202 | self.horizontalLayout_2.addWidget(self.groupBox_6)
203 |
204 |
205 | self.verticalLayout_3.addLayout(self.horizontalLayout_2)
206 |
207 | self.groupBox_7 = QGroupBox(self.centralwidget)
208 | self.groupBox_7.setObjectName(u"groupBox_7")
209 | self.verticalLayout = QVBoxLayout(self.groupBox_7)
210 | self.verticalLayout.setObjectName(u"verticalLayout")
211 | self.textBrowser_2 = QTextBrowser(self.groupBox_7)
212 | self.textBrowser_2.setObjectName(u"textBrowser_2")
213 |
214 | self.verticalLayout.addWidget(self.textBrowser_2)
215 |
216 | self.horizontalLayout_3 = QHBoxLayout()
217 | self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
218 | self.label_7 = QLabel(self.groupBox_7)
219 | self.label_7.setObjectName(u"label_7")
220 |
221 | self.horizontalLayout_3.addWidget(self.label_7)
222 |
223 | self.progressBar = QProgressBar(self.groupBox_7)
224 | self.progressBar.setObjectName(u"progressBar")
225 | self.progressBar.setValue(0)
226 |
227 | self.horizontalLayout_3.addWidget(self.progressBar)
228 |
229 | self.label_8 = QLabel(self.groupBox_7)
230 | self.label_8.setObjectName(u"label_8")
231 |
232 | self.horizontalLayout_3.addWidget(self.label_8)
233 |
234 | self.progressBar_2 = QProgressBar(self.groupBox_7)
235 | self.progressBar_2.setObjectName(u"progressBar_2")
236 | self.progressBar_2.setValue(0)
237 |
238 | self.horizontalLayout_3.addWidget(self.progressBar_2)
239 |
240 |
241 | self.verticalLayout.addLayout(self.horizontalLayout_3)
242 |
243 |
244 | self.verticalLayout_3.addWidget(self.groupBox_7)
245 |
246 | MainWindow.setCentralWidget(self.centralwidget)
247 |
248 | self.retranslateUi(MainWindow)
249 |
250 | QMetaObject.connectSlotsByName(MainWindow)
251 | # setupUi
252 |
253 | def retranslateUi(self, MainWindow):
254 | MainWindow.setWindowTitle(QCoreApplication.translate("MainWindow", u"CDSE \u6570\u636e\u4e0b\u8f7d\u5de5\u5177 v2.0", None))
255 | self.groupBox.setTitle(QCoreApplication.translate("MainWindow", u"CDSE \u8d26\u53f7", None))
256 | self.label.setText(QCoreApplication.translate("MainWindow", u"\u8d26\u53f7\uff1a", None))
257 | self.label_2.setText(QCoreApplication.translate("MainWindow", u"\u5bc6\u7801\uff1a", None))
258 | self.groupBox_2.setTitle(QCoreApplication.translate("MainWindow", u"\u67e5\u8be2\u8bbe\u7f6e", None))
259 | self.label_3.setText(QCoreApplication.translate("MainWindow", u"\u536b\u661f\u5e73\u53f0\uff1a", None))
260 | self.label_5.setText(QCoreApplication.translate("MainWindow", u"\u8d77\u59cb\u65e5\u671f\uff1a", None))
261 | self.label_4.setText(QCoreApplication.translate("MainWindow", u"\u4ea7\u54c1\u7c7b\u578b\uff1a", None))
262 | self.label_6.setText(QCoreApplication.translate("MainWindow", u"\u622a\u6b62\u65e5\u671f\uff1a", None))
263 | self.groupBox_3.setTitle(QCoreApplication.translate("MainWindow", u"GeoJSON\u6587\u4ef6", None))
264 | self.pushButton.setText(QCoreApplication.translate("MainWindow", u"\u9009\u62e9", None))
265 | self.groupBox_4.setTitle(QCoreApplication.translate("MainWindow", u"\u4fdd\u5b58\u4f4d\u7f6e", None))
266 | self.pushButton_2.setText(QCoreApplication.translate("MainWindow", u"\u9009\u62e9", None))
267 | self.groupBox_5.setTitle(QCoreApplication.translate("MainWindow", u"\u6570\u636e\u67e5\u8be2", None))
268 | self.groupBox_6.setTitle(QCoreApplication.translate("MainWindow", u"\u63a7\u5236\u9762\u677f", None))
269 | self.pushButton_3.setText(QCoreApplication.translate("MainWindow", u"\u67e5\u8be2", None))
270 | self.pushButton_4.setText(QCoreApplication.translate("MainWindow", u"\u4e0b\u8f7d", None))
271 | self.pushButton_5.setText(QCoreApplication.translate("MainWindow", u"\u6e05\u7a7a", None))
272 | self.pushButton_6.setText(QCoreApplication.translate("MainWindow", u"\u5bfc\u51fa", None))
273 | self.pushButton_7.setText(QCoreApplication.translate("MainWindow", u"\u5173\u4e8e", None))
274 | self.groupBox_7.setTitle(QCoreApplication.translate("MainWindow", u"\u4e0b\u8f7d\u72b6\u6001", None))
275 | self.label_7.setText(QCoreApplication.translate("MainWindow", u"\u5f53\u524d\u6587\u4ef6\u4e0b\u8f7d\u8fdb\u5ea6\uff1a", None))
276 | self.label_8.setText(QCoreApplication.translate("MainWindow", u"\u6240\u6709\u6587\u4ef6\u4e0b\u8f7d\u8fdb\u5ea6\uff1a", None))
277 | # retranslateUi
278 |
279 |
--------------------------------------------------------------------------------
/resources_rc.py:
--------------------------------------------------------------------------------
1 | # Resource object code (Python 3)
2 | # Created by: object code
3 | # Created by: The Resource Compiler for Qt version 6.7.0
4 | # WARNING! All changes made in this file will be lost!
5 |
6 | from PySide6 import QtCore
7 |
8 | qt_resource_data = b"\
9 | \x00\x00\x10\xe9\
10 | \x00\
11 | \x01\x08>x\xda\xed]\x07xV\xd5\x19\xce \x93l\
12 | B\x02\x19\x90@\x12\x08YB\x12\x02B a\x1a\x04\
13 | d5\x03\x11d\x04\xc2\x1e\xb2\x8cA\xa2\x80%\xa9\xa8\
14 | \x95J\xadZD\xb4 \x1b\xd9+\x01$\x10\x02\x04\xaa\
15 | u\x81\xd6Um+V\xd1\xa2OQy\xf8\xfa\xbf\x97\
16 | \x86\xfe\xf7\xdc\xf1\xaf\xfb\xcf\x9c\xe3\xf3\xf9\xf0\xfc\xb9\xf7\
17 | \xac\xf7\xdcs\xce\xb7\xdd\xdc\xdcu\xffUV\xba\xe9\xfe\
18 | \x1f\xe7\xd6\xd9\xd7\xdd-\xc2\xcd\xcd\xad\xb3\x8et?\xe1\
19 | G\xe1\xf7\xdb\xc5\xdd\xadc\xba\x9b@M\x85x\xe1\x85\
20 | \x17^x\xe1\x85\x17^x\xe1\x85\x17^x\xe1\x85\x97\
21 | f_~\xbeZO7>\xde\xc2\xc9\x09\x09\xd8YZ\
22 | n|\xb2\x9d~\xb8\xf8(''$`\xc7\xf1\xe7\xf8\
23 | s\xfc9\xfe\x1c\x7f\x8e?\xc7\x9f\xe3\xefj\xf8\x7fU\
24 | \xb7\x94^Z9\x92\x0a\x0bR\xa9S|8\x05\x05\xf8\
25 | \x90\xb7\x97'EG\x06QfJ\x14\x95\x15w\xa7\xcd\
26 | k\x0a\x85\xe78\xfe\xae\x83?\xf0,\x9f\xd6\x97\x02[\
27 | \xfa\x10\xd4\xd8\x86\xa8\xa5\xbf7=8\xb2+]\xdc9\
28 | \x83\xe3\xef\xe4\xf8\xd7l\x98D\xf11\xa1F\xe1\xceR\
29 | \x0bO\x0f*-\xcc\xa2\xcfj\x17r\xfc\x9d\x10\xff\xd7\
30 | \xaa\xc7\x90\x8f\xb7\xa7Y\xd8\xebSp\xa0\xafpnp\
31 | \xfc\x9d\x07\xff-O\x17\x91\xa7\x87\xbb\xec7=\xbc_\
32 | 2=_y\x1f\x1d\xdf8\x99\xcel\x9eJ\xbb\x9f\xbb\
33 | \x9f\x9e\x5c|\x0f\x0d\xcd\xeb\xa4\xba^\xc6\x0eMw\xd9\
34 | \xbb\x81+\xe1_\xb7\xa9\x94\xfc}\xbd$\xf8\xe5\xe7t\
35 | \xa0\xb7\xdf\x98\xa5\xfa\xee\xdf\x8e/\xa2e3\xf2\x85o\
36 | ^n\x0d$wlM\xef\x1f\x98\xcb\xf1wP\xfc\xbf\
37 | :\xfd0%\xb4o%\xc1m\xce\x03=\xe9\xdf\x17\x96\
38 | \x19]\xcf\xa75\x0bi\xfc}w\x91\xbb\xbbt\x0d\xb4\
39 | m\x1dH\xf5\xafO\xe3\xf8; \xfe\xb3\xc7\xf5\x90\xe0\
40 | \x85;\x9c\xb9\xf5\xed\xfa\xddX\x0a\x0f\xf5\x97\xd4\x09^\
41 | \xe2\xd8\xfa\x89\x1c\x7f\x07\xc2\xff\xfc\xb6\xe9\xc2\xf9\xae\x8f\
42 | S\xf7\xb4\x18\xbav\xae\xc2\xa2z\xaf\x1c\x9aO9\xe9\
43 | 1\x925\x10\xd0\xd2[\xb8Cp\xfc\x1d\x03\xff\x11\x03\
44 | \xba\x88\xf9x?/zw\xdf\x1cM\xea\xfe\xd7\xd9r\
45 | \xba\xaf\x7f\xb2d\x0d\x84\x04\xf9R\xc3\xd62\x8e\xbf\x9d\
46 | \xf1o\xdc1\x83<\x98\xfb\xfe\xd2\xd2>&\x9d\xf7o\
47 | \xe8\xf8\x805K\x0ahja\xb6pW\x84\x8c\xb0u\
48 | XKa\x1d\xe9\xf3\x0fr\xfc\xe1\xfaU#\x85:8\
49 | \xfe\xf6\xe9?0\xd3\xc7$\xa2UK\xfa\xe7)e^\
50 | \xed\xea\x99\x87i\xeb3\xc54cl\x0e\xa5&F\xca\
51 | \xde\xf3L%\xd4\x81\xbaP'\xeaF\x1b\x1c\x7f\xeb\xd3\
52 | \xd7\xf5\xe5\x12~M\xee\xdb\xc7\xfd\x1f\xbc\xfe\x98\xc1)\
53 | \xa2o\xdaZ\x846\xd0\x16\xda4\x85\xf7\xe0\xf8\x9b.\
54 | \xe7ce<\x1f\x1c\x9cw\xe7\xef\xd8\x07\xaa\x16\x0e\xa6\
55 | \xf6Q!V\xc7\x5c\x89\xd06\xfa\xa0\xb6'q\xfc\xcd\
56 | \xa3\xd1\x83RDs]\x90\x9btg_X1w\x00\
57 | \xb5\x0a\xf17\x0a\xa3\xc0\xc0@\xea\xdd\xbb7\x95\x95\x95\
58 | Quu5m\xda\xb4\x89\xf6\xef\xdfO'O\x9e\xa4\
59 | \xc6\xc6F\x81\xf0o\xfc\x86\xbf\x95\x97\x97\x93\x9f\x9f\x9f\
60 | I\xeb\x00}y|N\x7f\xa1o\x1c\x7f\xcb\xe9\xbb\xf3\
61 | \x15\x82\x1eW\x7f\x8e\x9f{t\x18\xbd\xfc\xc4(\x8am\
62 | \x13\xac\x8a\x85\xa7\xa7'\xf5\xeb\xd7O\xc0\xfa\xd2\xa5K\
63 | t\xf3\xe6M\x93\xc7\x5cSS#\xd4\xc3\xae#\xf67\
64 | \x96\xd07\xf4\x91\xe3o\xb9~O\x7f^=\xdc\xdd\xa9\
65 | OV\x9c\xea\xdcgdd\xd0\xda\xb5k\xe9\xea\xd5\xab\
66 | \x9a\xd8=WVVJ\xda\xa8\xa8\xa8\x10\xda@[j\
67 | }\x19\xd0\xb3\xa3f\xdfK?\x7f\xdd@7\xaf\x7f,|\x93\xb7\
83 | ~\xbaF\xbf|s\x91n|\xba\x93~|\xe7)\xb3\
84 | \xfb\x05}\x10+K\x5c2%W\xf1y\xf4\x15}f\
85 | \xc7\x11\x13\x19$\x8c\x91\xe3/%\xd8pDE\x04\xca\
86 | \xf2T\xb0\xe7\xb1\xf7]\x1avB\xec\x9aT\xb3\x19A\
87 | \x9f\xe5xU\x8c\xd1\x90\xbdJs\xc3\x1f:\x96\x0e\xb1\
88 | a\x92\xb9JK\x8a\xa4\x8f\x8e,p\x08^\x1a\xfab\
89 | \xd8\x10\xeb\xf7o\xc2\x88\xae\xaa\xef\xa0\xef\x18\x03;.\
90 | \x8c\xd5\x16z%g\xc0\x1fw\xe3\xec\xd4h\xc9\x1cu\
91 | \xeb\x12E_\x9cX\xecP\xf24\xe8\x1fX\x99\xf4[\
92 | \xbb\xd5\xbfe\x8c\x01ca\xc7\x871C\xff\xdc\xdc\xf1\
93 | \xc77\xc4\xce\x0d\xf4m\x8e\x86=\xe8\xefo.\x11l\
94 | \x03\xf4\xfb:nx\x86\xc1\xf70\x96\xa8\xf8 \xc98\
95 | a\x8b\xd6\x9c\xf1\x7fq\xc5\x08\x99\xf31\x88.\xdb\xf8\
96 | \x9el\x0a=:3_\xb2\x07\x18\xb2\x1d]W3\x82\
97 | \xba\xfd\xa1\x1dy\xb7\x92\xde\x091\x07\xcd\x11\x7f\xc8G\
98 | Y\xdf\x1d\xd8\xf7\x9e\xdd\xe2\xd8v7\xd0\xf5\x85\x05\x8b\
99 | eB3\xc7\xe6\xa8\xbe3~o6\xf5\xd8\xde\x81\xd2\
100 | \xd7\xc4\x90\x87\x8f\xbb\xc4\xe6\xf0\x9d\xbds\x9a\x1d\xfe\x90\
101 | \x91\xb3\xdf\xc2\xfaU\xa3\x1c\x1a\xfb&\xaa\x98\x9e'\xf1\
102 | -\x03\xef\xaf\xe8\xb7p\xa2D\xc0\x1f\x9407B2\
103 | \xee\xfe\xba\xb9hN\xf8CG&\xe1\xf3\xf2Bi\xd9\
104 | \xe1\x01\xb4\xe3\xe4\xfd\xf4\xd19\xdb\xdc\xf9\xbfi,\xa7\
105 | \x9a\xba\xc9\xb4\xfaH\x01\x8d~#\x83\x8e\xd5\x19g\xf7\
106 | \x09\xdf1\x96\x17X5o\xa0\xb2-\xcb\x85r\xca\xdf\
107 | \x99tg\x0d\x84\xf7\x0d\xb0\xc9\xdawD\xfca?\xc5\
108 | \xf2\xc5~a^\x94\xbd1\xee\xce\xfc\x80\xa6\xee\xebI\
109 | \xa7Oko\x8f\x7f\xb9a>\xbdv\xa2\x88\xe6\x1c\xe8\
110 | M}v$\x8a\xda\x1c\xb1;\x9d\xbem4NV\xcf\
111 | \xda\xa6aL\xdf\xab\xd8\x03\xad<2\xf8N;Y\xaf\
112 | \xc4\x91w\x98\xa7\xe4}\xadm\xcb\x1c\x11\x7f\xd8n\xb0\
113 | k?\xe9\xa1H\x11\x0e\xd6Z\x07\x07N=\xa8\xd8N\
114 | \x13\xe1\xaefL]\x17\xb6O\x97\xc8\x04\xb7\xff\xb6D\
115 | \xf1\xf9\x8b\xf5\xb3D\xed$.\x90\xca\x0507\xae\x8c\
116 | ?\xfc\xec\xd8\xbbSX\x9a\xbfAL\xb4X\x07_\x9c\
117 | _L\xf7\xec\xeab\xb0\x1d\xec\x09\x1f6\x18w\xfe\xf4\
118 | \xc9\x16\xdb$\x0c\xcb\xef\xac\xfa\xfc(\xdd\x19\xa3\xdfV\
119 | P*3\x17\xba\xb9\xd1\xd2\x17\xd1\xd1\xf0\x87?&\xbb\
120 | \xe6SVE\x19\x85\x7f\x13\xfd\xb9\xde<\xd9\xe9\xe2\x83\
121 | yF\xb71\xf7@\xaeQu\xe2\xccfy\xc1\xbf\x1e\
122 | U^;UG\x87\x88\xda\xc1\xd8\xd9\xf9\xa8^8\xd8\
123 | %\xf1\x87\xad,k\xab\x19\x92\xe6g\x12\xf6\xa0\xd5G\
124 | \x0b\xcc\xf3#\xd1\x9d\xed\xa6\xb4\xf3\xeeY\xc3\xfe\xa0\x90\
125 | \xdf\xb1\xfb\xd9c\xb3\xfb+>_[7E\xd2NP\
126 | \x9a\xf8\xfdvm\x835\xb3+v$\xfc\xe1s\xc7\xae\
127 | \xf5.\xcb\xdb\x9a\x8c\xff\xa0]\xc9\xf4]\xa3\xe9\xbe_\
128 | \xcb\x0f\x0f4\xa9\x1dc\xef\x01\xe0\xfdY_be~\
129 | \xe3\x11\xea\xcb\xdc9\x93us\xc0\xce\x0b\xe6\xca\xd5\xf0\
130 | g\xedy\xfd\xdby\x9b\x8c}\x13\x1d\xd4\xdd\xe3L^\
131 | \x7f'\xc7\x99\xd4F\xd1\x9e\xaeF\xd5{\xeaO\xa5\x12\
132 | \xfc\xce\xa9\xf8\x8e\xe1la\xdb\xf2\x8b\x15\xf3\x92\x98+\
133 | W\xc2\x1f\xfe\xdb\xacoF\xbb\x07\xc2\xcc\xc6\x7f\x91\xee\
134 | ,7\xb5\x0f\x90)\x98\xd2\xc6b\x13\xda\xe8\xd8.\xcc\
135 | h\x1f\xb5W\x8f\x17J\xda\xc2\x5c\xb0>&\x983W\
136 | \xc1\x1f\xf1\xb7D>U\x1e\xee\xd4\xed\x85\xf6f\xe3\x8f\
137 | =\xf4\xea\x05\xd3\xe7\x07\xb2\xa5\xbe;\x92\x0c\xd6\x0f>\
138 | \x01\xfc\x82\xb1\xf5\xc2\x16D\x7f|X\x0f\x8az\xe4\x86\
139 | \xf9\x92\xf60\x17\xee\x1e\xe2=\x04s\xe6*\xf8O\x1a\
140 | \xd5Ml\x1b\x9b\xe2g6\xf6M\xb4\xf7\xcd\xf1f\xf5\
141 | \xe5\xbd\x86\xb9T\xbc\xa7\x9bj\xdd\x07L<_\xe0+\
142 | \xcc\x9e\x01>\xde-\x04\xdd?\xf6\x026\xd6\x94\x5c\x9b\
143 | A)\xe2{\xe0\xe4\xd1\x99.\x83?k\xdb\x11[\x1c\
144 | \xaa<\xff\xdb\x8c\xc3\xbf\xf2\xf0@\x8b\xe4\xbe\xb8\x0f\x0e\
145 | \xd6}\xe7e\xfb{\xd2\x13G\xee\xa1\xcd'\x8a\x05\xf9\
146 | \xc2\x97\xe7\x97\x98\xb5\xbfy(\xd8\xab6\xd9\x82\xeb\x7f\
147 | \xcfr\xe3\x89-\x093z\x0fq&\xfc?9\xf6\x90\
148 | \xc5<\xbf\x1c\x81\x9fs\x04]\xd0+\xabG\x1b\xe5g\
149 | \x8cg^\x7f\xaaH\x11\x7f9Y\x00\xe6\xce\xd9\xf1g\
150 | \xf9>O_\x0f\xca\xd9\x12o1\xfe\xa0O\xce\xd9\xd7\
151 | 7\x1f\xfb:\xeb\xa7\xa6Fx\x16\xef\xc8\x8d\x05s\x82\
152 | \xb9\xd1\x7f~\xc7\xb3%N\x8f?\xe4!\xa2\xf8*\x09\
153 | >\x9a`o\xce9\xad5!\xfe\xa8\x08cw\xdd\x19\
154 | 0u\x16\xb9\x9d\xbax\x9b\xf0ow\xf1\xb9\x80w\x94\
155 | \xc6\x83\xb91V\x96\xe4,\xf8\xb3\xf6]\xe1}\x024\
156 | \xc3\xdf\x5cY\xa0VtWgFv3~2\xb9}\
157 | Ob\x9a2C\xf4\x0c\xdeQ\x1a\x0f\xab\x176d_\
158 | \xea\x0c\xf8\xf7\xcd\x8e\x17\xdf\xfd\xc6\x86i\x86?\xf4)\
159 | \xf6\xc4_\x12O\xf0X\xbd\x14\x7f\xfc\xa6\xaf\xeb\xf6i\
160 | \xa18\x1e\xcc\x8d\xc8\xd7,+\xce\xe9\xf1OI\x10\xdb\
161 | \xbb$\xce\x8f\xd0\x0c\x7fc\xe5\xf46\xc3\xbf\xee\x92\x14\
162 | \x7f\xfc\xa6\xef7\xa0\x82?k\x1b\x94\x14\x17\xee\xf4\xf8\
163 | \xb3:\x9fd3d\xfeZ\xc8\xe9\xadA]\x93M\xdf\
164 | \xffc\x13C\x14\xc7\xd2\xf9\xe16\xa2g\x11\xa7\xca\xd9\
165 | \xf1g\xf5ciU\xd1\x9a\xe2_\xb2\xa7\x9bc\xdd\xff\
166 | Jg\xfe\xff\xfe\x87\x7f3\xf7\xbf\xd4\x12\xe5\xf5\x9f\xb2\
167 | \x22JbW\xe8\xec\xf8#\x17\x83\xfe\x98\xd2\x9f\x8a\xd1\
168 | \x14\x7f\xd0\xd6\x93c\xed\x82\xff\xe7\xc7\x17\x99\xc4\xff\x05\
169 | \xea\x9e\xcdzYY\xee\x9d\xf6\x1b\xa9\x1f\x8c\xb3\xe3\xcf\
170 | \x8e\xc7\x1a\xf8\xf7\xde\x91@\x0dg\xa6\xdbe\x0dl\xac\
171 | \x1a\xa3*\xfb\xbb\x13\xc3D\xf7L\xd9\x8a\x1e\xaa\xe3\xc0\
172 | \xdc\xb8\x1a\xfe\x88\xa7*\xda\xffVGk\x8e?h\xc8\
173 | \xae\x14\xfa\xec\xfc\x22\xbb\xac\x01\xc8\xf5\x94b\x8b\x0b2\
174 | \x0f\xdd>\x8eg\xa0SV\x1b\x03\xceFW\xdb\xff[\
175 | 3\xfe\xdcF\xdf\xff\xb6\x99\xbe\x06&\xed\xcb1\xcb6\
176 | D+Y t=\xd0\xf9x0g\xfe\xca\xf9\x03h\
177 | \x85\x9e\xfd\xaf\x12\xb1\xb6 \xaep\xff\x03\x0fc\x16\xff\
178 | \xb7\xcd\xbc}\xe0\xf1\xc3\x83\xec\xae\x13`m]\x92\x87\
179 | D\x1a\xd5w\xcc\x8d\xab\xf1\x7f\x12\xf9OI\x98\xf1\xf8\
180 | \x9b\xb9\x06\xa0\xdf}\xa1v\x94\xcd\xfcH\x0c\xf9\x08\x06\
181 | u\xf15\xaa\xdf\xac\xfc\x07\xf6\xc5\x5c\xfek\x19M\xd8\
182 | \x9bM\x7f\xac\x1dM\xef7\xd8\xce\xa7\x94\xb5w\xf1\x0a\
183 | \xf54\xaa\xaf\xae(\xff\xb5\xa6\xfe\xc7TZx0O\
184 | \xb0\xc1\xd4\xfc\xec\xd7\xdd;\x8f\xd6M\x12dQ\xf3\x0e\
185 | \xe4R\xde\xbaD\xa9\xbf\xff\xabq\x06\xfb\xe7\x8a\xfa\x1f\
186 | V\xff\xdbBC\xfd\xaf\xb9k\xe0\xbaF\xb8\xc3Fl\
187 | \xc9\xc1|\xd9\xb3\xcb\xc3K|\x07\x04o\xaf\xd6/9\
188 | \xfd\xef\xce\xb5c\xb9\xfd\x87\x15H\x0b\x99\xf1\xfeS\x13\
189 | T\xfd\x89|\xa3\xc4\xf6\xae\x9d\x96\xa8\xdf\x01]\xd5\xfe\
190 | C\xce>\xb6}I\x98]\xf1\xef\xa9\xa37O\x97Z\
191 | \x96\x97\xc4\x80?Ip\x86X\xee\x1d7\xb1\x95\xfa\xdd\
192 | \xaf8\xd4%\xed\xbf@\xb0e\x14\xc5AK\x0b\xb1+\
193 | \xfe\xa0\xa1\xbbS\xe9\x1f\x17\xcc\xf7\xb53\xe4O\x12\xd1\
194 | _\x1c\xcb\xac\xed\xf0`\xd5\xe7]\xd9\xfeS\xde\xfe\xbb\
195 | \x9d\xdd\xd7\xc0\xd2C\xf9\xe6\xdfk\x0c\xf8\x93D\x8f\x16\
196 | \xeb=[\xe7\x05*>\x8b\xb9psw]\xfbo\xf8\
197 | \xb5\xb3\xfe\x1f\x9d\xc6G\xda\x1d\x7f\x90\xb9c2\xe4O\
198 | \x82\xfd^\xe4\xeb\xd8U\xd9\xcf\xb9\xdd8\xd7\xf6\xff\x00\
199 | !g\x8a\xfe\x18#\xe3\x02\x9c\x1a\x7f\x90\x9a<\x9f\xb5\
200 | \xe5\x08HT\xe6{]\xdd\xff\x0b\x84|9\x12\xff\xcf\
201 | \xc7\xda:5\xfe\xe0#\x94\xea\xed\xb4\xb4\x8dQ\xfe\x8e\
202 | \x98\x83\xe6\xe0\xff\x09\x9f\xe6\xb8h\xf1\x99\x18\x93\x1d\xe2\
203 | \xd4\xf8\xc3\xf6L\xd1\x96\xa7B\x8c\xabO\xa4\x97\xecs\
204 | \xa1\x99\xfe\x92\x9cB\xae\xe8\xff-\x17\xffA\xc8\xadf\
205 | %}\xb0-\xf0\x17\xe2\xfc\x9e\x99F\xd3\xf6\xf7\x94\xf2\
206 | \xf3+\xc5\xfc\xbcw\xb8\xd4\xee\x0fcg\xef}\x98#\
207 | W\x8e\xff\xc2\xe6m\x0aM\xf5\xb7\x0b\xee\xb9;\x12\x85\
208 | \xb8lZ\x8d\x8d]\x07i\xd5b]\xbe\xa7\xbf\x87\x94\
209 | \xe7kf\xf1_\x94\xe2?!\x16\x92-\xb1\xbfwW\
210 | \x8a\xd5\xec\x85\x9a\xd6\x01k\xcb\x03yps\x8f\xff\xa4\
211 | \x14\xff\x0d\xb1\xd0\x10\x13\xcd\x16\xd8O\xde\x97c\x13;\
212 | \xa1\xad[\x8b%\xf8\xf2\xf8o\xb7i\xc3\xafGK\xe6\
213 | \x06\xbaOkc\x8f\x18|\xd7\x1am\x93\x8b\x87\xf5\x09\
214 | \x87\xdfG\xd3\xdfJ\xeeM\x97\x8c\xdf\x1ay\xe3\x9c-\
215 | \xfek\xc2\x9c\x08\xcd1G\xdcM\xd8^]\xac\x9fe\
216 | S\x1b\x10\x16\x7f\xd8\x09\xe3\xf7\x97V\x8e\x94\xcd\x17\xd7\
217 | \xdc\xe2\xff\xbe\xb7\x7f\xae$\xfe3b##F\xb2\x16\
218 | \xb8\x8f\xdf\x9b%\xdc\xef\x10{\xd5\x1e6@\xb5\xafL\
219 | \x92\xdc\xed\xea_\x9f&\xc4\xb8f\xe3?c.\x9ac\
220 | \xfcw\xb9o\x011\xd2\x11+\xdd\x10\xbe\x03wv\xa6\
221 | q:\x8c\x17\x1c\xecK\xd5G\x87\xd0\xc6\xe3\x85t\xe4\
222 | \xd4Dz\xeb\xecl\xfa\xea\x82\xfdst\xb3v\x0f\xd1\
223 | \x91A\x02\xb1\xe3\xc5\x1c\xf0\xfc\x0f\x8c}@B\x84]\
224 | \xf2\xa5Y3\xc65\xfc\xfe\xd8qZj\xdf\xe5\x0a\xf8\
225 | \x7f\xdb\xa0\x9c\xffEm\x0d\x5cotl\xfc\x11\xc7S\
226 | \xcd\x1f\x04c\xb6v^Hg\xcf\xff\x84<0\x1f\x1e\
227 | \x9e\xef\x94\xf8\xcf\x9bp\xb7\x22\xf6<\xff\x93|\xfe7\
228 | \xb9\xf3\x119\xf3\x94\xf2\xbf9\xf2\x1a\x18xw\x82,\
229 | \xf6\x18#\xcf\xff\xa6\x9c\xffQ.\x87\xaaR\xfeG\xc4\
230 | \xdbw\xc45\x80\xbe\xb2>@\x82\x0d\x90nl<\xff\
231 | \xa3:!\x17N|L\xa8\xec\xb7\x03\xbd8\x9bg\xe5\
232 | {\x8dteZ\x10\xfa\xc6\xda9\xe8co\xad\
233 | \xae\x96\xff\x19v\xaf9\xe91\xb2\xf3\xd8&<\x80^\
234 | \xab\x1e#\xd2+;\x02\xf6\xe8\x93R\xfeg\x90-\xbf\
235 | {g\xc7\xbf)\xb6:\xf2\xe3)\xcd'b\xe3\xd4\xbc\
236 | <\xd1\xee{@\xcd\x86IB_\xd4\xee\xfaX\xb3\xf6\
237 | \xe8\x9b3\xe3\xaf/#b\xe5\x84\xac\x8f\xdc\xbe\xe7\x1f\
238 | \xb0\xf9=\x00m\xb2\xf9?\x94hh^'\x8e\xbf\x85\
239 | \xb2b\xa5\xfb\xb4~\xdc\xfd\xca\xd9\xfd\x14\xf9E\xad\xf8\
240 | \xd45K\x0a\x04\xbeT\xad/\xc1\x01\xe2X\x00\xcbg\
241 | \xf6\xe3\xf8k\xa47\x94\xcb\xa9\xcd\xc6\xd9\xe8\x9d\xd9\x9e\
242 | \x96\xcd\xc8\xa7c\xeb'\xd2w\xe7\xcd\x8f\x07\x80wQ\
243 | \x07\xea\xc2\xb7\xeei \xce\x07\xfa\xf6\xdc\xb2a\x92\xe7\
244 | pFp\xfc\xb5\xa1\xaf\xeb\xcbi\xe5\xdc\x01\x12;\x22\
245 | %B\x0c\x8d\x1ew\xc5\xd2\x941\x99\xc2{\x90\xcb\x22\
246 | \xae\xea\xa1\x17'\x08\xb9\x1b@\xf87~\xc3\xdf\xf0\x0c\
247 | \x9e\xc5;l\x8e?%B_`\xbb\x81\xbe\xb1:\x0d\
248 | \x9c]\x96\xacA\x8e\xbf\xb2-Y\xd5\xc2\xc1\x92\xf8r\
249 | \xb6$\xb4\x8d>\xe8\xdbl\x0d\xe9\x93d\x15[n\x8e\
250 | \xbf\xb2]1l\xcb\xc1w\xb3>&\xd6 \xb4\x81\xb6\
251 | \xd0&\xcb{\x22o5r\x80\xe9?\xaf\xcf\xabr\xfc\
252 | \xadK\xb0\x9f\xda\xfaL\xb1\x90\x93)4\xc8O\x13\xbc\
253 | a\xa3\x8c\x98>\xa8\x13u\xab\xd9h!\xfe\x0f\x1b\xf7\
254 | \x1fg\x02\xc7\xdf\xf6\x04\xddZS|N\xf7\xff\xe1\xd8\
255 | t\x1e#\xaeR\xd3\xd9\x8e\xf8\x84\xa1\xc1~\x14\x1f\x1d\
256 | *\xdc\xeb\xf3s:\x08\xb9]q\xcf\xdf\xb3n\x9c$\
257 | w\x87\xda\xda\x8bh%\x8eu\x85z\xec9\x07\xcd\x19\
258 | \x7f!\xd7\xce\xa1\xf9\xb2:\xa5\xa2!i\x9a\xcb\x8c\xc0\
259 | \xe3\xb1|\xc8\xc5\x9d38\xfev&\xe4b\x0b\x0f\x95\
260 | \xf2\x0a\x05\xb9I\x9a\xf8X\x0a~@\xfb\xe6Hx\x85\
261 | \x11\x03\xba\xd8}\xec\x1c\xff\xdbtz\xd3T\xd98\xad\
262 | \xb0\xc1\xc0\x1eaI\xdd\xe0\xed\xba\xa7\xc5H\xf2\xc0\x9e\
263 | \xdf6\x9d\xe3\xef@t|\xe3d\xd95\x80\xbd\xc1\x92\
264 | 8;l\x1ex\xd0\xf4\x92\xee\x0e1f\x8e\xbf\x98\xce\
265 | n\x99FQ\x11\x81\xb2w|\xe8\x9aL\x89\xb7\x03\xde\
266 | o\xf6\xb8\x1eR\x1b\xf6\xf6\xad4\xf5\xe1\xe2\xf8kK\
267 | \x1f\xe8x\xf4.LN\x0a}~\x0d\xb2^Cw\xfe\
268 | \xb7v\xcf\xa2\xbc\xee\xf1\x92\xf7a\xdb]\xb7\xa9\xd4a\
269 | \xc6\xca\xf1W\x96\x1d\xde?,C\x91\xe7\xf7\xf1\xf6\x14\
270 | tv\xf0\xc5\x85\x9c\x07\xf6g8?~_9\x9c\x06\
271 | \xf5J\x90\x8d\xf7\x0d\x99\xff\x96\xa7\x8b\x1cj\x9c\x1c\x7f\
272 | uZ\xbfj\x14\x85\x04\xf9Z,#\xc2z\xb1\xa7\x9c\
273 | \x8f\xe3oY\xdc\xee\xd2\xc2,\x89\xdc\xd6X\x82\xad\x1a\
274 | \xf6\x06G\x1c\x1b\xc7\xdfx\xc2\x99^V\xdc]\xd5\xd6\
275 | \x84\xf5\xe7C\xfe\x17\xadd\x08\x1c\x7f\xc7\xb9\x1b \xf6\
276 | \x1a\xd6BVj\xb4 ?\xc4\xfe\x8es\x026&\x85\
277 | \x05\xa9\x82\x8e\xd7Q\xee\xf8\x1c\x7fN\x1c\x7fN\x1c\x7f\
278 | N\x1c\x7fNZ\xe3\xff\xf3\xd5z\xba\xf1\xf1\x16NN\
279 | H\xc0\x8e\x17^x\xe1\x85\x17^x\xe1\x85\x17^x\
280 | \xe1\x85\x17^x\xe1\xc5\x8d\x17^x\xe1\x85\x17^x\
281 | iv\xe5\xbf}b#\xa3\
282 | "
283 |
284 | qt_resource_name = b"\
285 | \x00\x06\
286 | \x07\x03}\xc3\
287 | \x00i\
288 | \x00m\x00a\x00g\x00e\x00s\
289 | \x00\x08\
290 | \x0aaB\x7f\
291 | \x00i\
292 | \x00c\x00o\x00n\x00.\x00i\x00c\x00o\
293 | "
294 |
295 | qt_resource_struct = b"\
296 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x01\
297 | \x00\x00\x00\x00\x00\x00\x00\x00\
298 | \x00\x00\x00\x00\x00\x02\x00\x00\x00\x01\x00\x00\x00\x02\
299 | \x00\x00\x00\x00\x00\x00\x00\x00\
300 | \x00\x00\x00\x12\x00\x01\x00\x00\x00\x01\x00\x00\x00\x00\
301 | \x00\x00\x01\x8f\x8f\xe6=p\
302 | "
303 |
304 | def qInitResources():
305 | QtCore.qRegisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
306 |
307 | def qCleanupResources():
308 | QtCore.qUnregisterResourceData(0x03, qt_resource_struct, qt_resource_name, qt_resource_data)
309 |
310 | qInitResources()
311 |
--------------------------------------------------------------------------------
/main.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 |
3 | from PySide6.QtWidgets import QApplication, QMainWindow, QFileDialog, QMessageBox
4 | from PySide6.QtCore import QThread, Signal, Slot, QTimer
5 | import os
6 | import sys
7 | from datetime import datetime
8 | import ui_main
9 | import query
10 | import download
11 |
12 | # 基础设置
13 | ############################################################################
14 | # 1 所需卫星数据
15 | query_satellite = ''
16 |
17 | # 2 检索时文件名需包括的字符串,可以是产品类型,如:SLC;可以是产品级别,如:L2A;也可以是区块代码,如:RVQ
18 | query_contains = ''
19 |
20 | # 3 起始与截止日期
21 | query_startDate = ''
22 | query_endDate = ''
23 |
24 | # 4 检索区域 在如下网站绘制geojson文件 https://geojson.io/#map=2/0/20
25 | map_geojson = ''
26 |
27 | # 5 expand option,暂时未开发此功能
28 | query_expand = ''
29 |
30 | # 6 哥白尼数据空间生态系统账号密码 在如下网站申请:https://dataspace.copernicus.eu/
31 | CDSE_email = ''
32 | CDSE_password = ''
33 |
34 | # 7 数据保存路径
35 | output_dir = ''
36 | ############################################################################
37 |
38 | # 卫星平台与数据类型
39 | ############################################################################
40 | plat_list = ['SENTINEL-1', 'SENTINEL-2', 'SENTINEL-3', 'SENTINEL-5P', 'SENTINEL-6']
41 | S1_type_list = ['IW_RAW', 'IW_SLC', 'IW_GRDH', 'IW_ETA']
42 | S2_type_list = ['MSIL1C', 'MSIL2A']
43 | S3_type_list = ['OL_1_EFR', 'OL_1_ERR', 'OL_2_LFR', 'OL_2_LRR', 'OL_2_WFR', 'OL_2_WRR',
44 | 'SL_1_RBT', 'SL_2_AOD', 'SL_2_FRP', 'SL_2_LST', 'SL_2_WST',
45 | 'SY_2_AOD', 'SY_2_SYN', 'SY_2_VG1', 'SY_2_VGP']
46 | S5P_type_list = ['OFFL_L2__AER_AI', 'OFFL_L2__AER_LH', 'OFFL_L2__CLOUD',
47 | 'OFFL_L2__CH4', 'OFFL_L2__CO', 'OFFL_L2__HCHO',
48 | 'OFFL_L2__NO2', 'OFFL_L2__O3', 'OFFL_L2__SO2']
49 | S6_type_list = ['P4_1B_LR', 'P4_2__LR']
50 | ############################################################################
51 |
52 | # 等待下载数据列表
53 | ############################################################################
54 | data_id_list = []
55 | data_name_list = []
56 | date_content_length = []
57 | ############################################################################
58 |
59 | # 等待下载数据列表
60 | ############################################################################
61 | access_token = ''
62 | datas_len = 0
63 | single_progress = 0
64 | total_progress = 0
65 | ############################################################################
66 |
67 |
68 | class MyWindow(QMainWindow, ui_main.Ui_MainWindow):
69 | def __init__(self):
70 | super(MyWindow, self).__init__()
71 | self.setupUi(self)
72 | self.retranslateUi(self)
73 |
74 | self.comboBox.addItems(plat_list)
75 | self.comboBox_2.addItems(S1_type_list)
76 | self.comboBox.currentIndexChanged.connect(self.change_combo)
77 | self.pushButton.clicked.connect(self.get_geojson)
78 | self.pushButton_2.clicked.connect(self.get_save_path)
79 | self.pushButton_3.clicked.connect(self.get_query)
80 | self.pushButton_4.clicked.connect(self.get_download)
81 | self.pushButton_5.clicked.connect(self.text_clear)
82 | self.pushButton_6.clicked.connect(self.save_query_result)
83 | self.pushButton_7.clicked.connect(self.about_box)
84 | self.cmd = os.getcwd()
85 | # 创建查询工作线程实例
86 | self.query_thread = QueryThread(self)
87 | # 连接信号到槽函数
88 | self.query_thread.query_result_sig.connect(self.query_result_display)
89 | # 创建下载工作线程实例
90 | self.download_thread = DownloadThread(self)
91 | # 连接信号到槽函数
92 | self.download_thread.download_sig.connect(self.download_display)
93 | self.download_thread.download_complete_sig.connect(self.download_complete_message)
94 | # 创建单个文件下载定时器
95 | self.single_timer = QTimer(self)
96 | self.single_timer.timeout.connect(self.update_single_progress)
97 | # 创建所有文件下载定时器
98 | self.total_timer = QTimer(self)
99 | self.total_timer.timeout.connect(self.update_total_progress)
100 |
101 | @Slot() # 声明这个方法是一个槽函数
102 | def start_query(self):
103 | # 启动查询工作线程
104 | self.query_thread.start()
105 |
106 | @Slot(str) # 声明这个方法是一个槽函数
107 | def query_result_display(self, result):
108 | # 在QTextBrowser中显示结果
109 | self.textBrowser.append(result)
110 | # 停止线程
111 | # self.query_thread.quit()
112 | # 等待线程结束
113 | # self.query_thread.wait()
114 |
115 | @Slot() # 声明这个方法是一个槽函数
116 | def start_download(self):
117 | # 启动下载工作线程
118 | self.download_thread.start()
119 | # 每1秒更新一次进度
120 | self.single_timer.start(1000)
121 | self.total_timer.start(1000)
122 |
123 | @Slot(str) # 声明这个方法是一个槽函数
124 | def download_display(self, result):
125 | # 在QTextBrowser_2中显示结果
126 | self.textBrowser_2.append(result)
127 | # 停止线程
128 | # self.download_thread.quit()
129 | # 等待线程结束
130 | # self.download_thread.wait()
131 |
132 | @Slot() # 声明这个方法是一个槽函数
133 | def download_complete_message(self):
134 | # 创建一个QMessageBox实例
135 | download_complete_message = QMessageBox(self)
136 | # 设置消息框的标题和文本
137 | download_complete_message.setWindowTitle("任务完成")
138 | download_complete_message.setText("下载任务已完成,谢谢使用!")
139 | # 设置消息框的图标(这里使用Information图标)
140 | download_complete_message.setIcon(QMessageBox.Icon.Information)
141 | # 显示消息框,并等待用户关闭它
142 | download_complete_message.exec()
143 |
144 | # 更新当前文件下载进度
145 | def update_single_progress(self):
146 | progress = single_progress
147 | self.progressBar.setValue(progress)
148 | # 只有当所有文件下载完成时
149 | if total_progress >= 100:
150 | # 下载完成时停止定时器
151 | self.single_timer.stop()
152 |
153 | # 更新所有文件下载进度
154 | def update_total_progress(self):
155 | progress = total_progress
156 | self.progressBar_2.setValue(progress)
157 | if progress >= 100:
158 | # 下载完成时停止定时器
159 | self.total_timer.stop()
160 |
161 | # 设置comboBox_2随comboBox变化
162 | def change_combo(self):
163 | platform = self.comboBox.currentText()
164 | if platform == 'SENTINEL-1':
165 | self.comboBox_2.clear()
166 | self.comboBox_2.addItems(S1_type_list)
167 | elif platform == 'SENTINEL-2':
168 | self.comboBox_2.clear()
169 | self.comboBox_2.addItems(S2_type_list)
170 | elif platform == 'SENTINEL-3':
171 | self.comboBox_2.clear()
172 | self.comboBox_2.addItems(S3_type_list)
173 | elif platform == 'SENTINEL-5P':
174 | self.comboBox_2.clear()
175 | self.comboBox_2.addItems(S5P_type_list)
176 | elif platform == 'SENTINEL-6':
177 | self.comboBox_2.clear()
178 | self.comboBox_2.addItems(S6_type_list)
179 |
180 | # 获取ROI的GeoJSON文件
181 | def get_geojson(self):
182 | file = QFileDialog()
183 | file.setDirectory(self.cmd)
184 | a_file, _ = file.getOpenFileName(None, '选择文件', '.', 'GeoJSON文件(*.geojson)')
185 | self.lineEdit_5.setText(a_file)
186 |
187 | # 获取下载数据保存位置
188 | def get_save_path(self):
189 | fold = QFileDialog()
190 | fold.setDirectory(self.cmd)
191 | path = fold.getExistingDirectory()
192 | self.lineEdit_6.setText(path)
193 |
194 | # 数据查询
195 | def get_query(self):
196 | global query_satellite
197 | query_satellite = self.comboBox.currentText()
198 | global query_contains
199 | query_contains = self.comboBox_2.currentText()
200 | global query_startDate
201 | query_startDate = self.lineEdit_3.text()
202 | global query_endDate
203 | query_endDate = self.lineEdit_4.text()
204 | global map_geojson
205 | map_geojson = self.lineEdit_5.text()
206 | if not query_startDate:
207 | self.textBrowser.clear()
208 | self.textBrowser.append(f'WARNING: 请输入起始日期:格式为 2024-05-01')
209 | elif not query_endDate:
210 | self.textBrowser.clear()
211 | self.textBrowser.append(f'WARNING: 请输入截止日期:格式为 2024-05-01')
212 | elif not map_geojson:
213 | self.textBrowser.clear()
214 | self.textBrowser.append(f'WARNING: 请选择GeoJSON文件')
215 | else:
216 | self.textBrowser.clear()
217 | self.textBrowser.append(f'正在查询,请稍后……')
218 | # 开始查询
219 | self.start_query()
220 |
221 | # 数据下载
222 | def get_download(self):
223 | self.download_thread.quit()
224 | global single_progress, total_progress
225 | single_progress = 0
226 | total_progress = 0
227 | global CDSE_email
228 | CDSE_email = self.lineEdit.text()
229 | global CDSE_password
230 | CDSE_password = self.lineEdit_2.text()
231 | global output_dir
232 | output_dir = self.lineEdit_6.text()
233 | if datas_len == 0:
234 | self.textBrowser_2.clear()
235 | self.textBrowser_2.append(f'请先查询数据')
236 | elif not CDSE_email:
237 | self.textBrowser_2.clear()
238 | self.textBrowser_2.append(f'WARNING: 请输入 CDSE 账号')
239 | elif not CDSE_password:
240 | self.textBrowser_2.clear()
241 | self.textBrowser_2.append(f'WARNING: 请输入 CDSE 密码')
242 | elif not output_dir:
243 | self.textBrowser_2.clear()
244 | self.textBrowser_2.append(f'WARNING: 请输入保存位置')
245 | else:
246 | global access_token
247 | try:
248 | # 获取 access_token
249 | access_token = download.get_access_token(CDSE_email, CDSE_password)
250 | self.textBrowser_2.clear()
251 | # 开始下载
252 | self.start_download()
253 | except Exception as e:
254 | self.textBrowser_2.clear()
255 | self.textBrowser_2.append(f'WARNING: 发生了一个异常: {e}, 请检查账户或密码是否正确')
256 |
257 | # 清空数据查询窗口显示内容
258 | def text_clear(self):
259 | self.textBrowser.clear()
260 |
261 | # 保存数据查询窗口显示内容为txt文本
262 | def save_query_result(self):
263 | text = self.textBrowser.toPlainText()
264 | fold = QFileDialog()
265 | fold.setDirectory(self.cmd)
266 | filename, filetype = fold.getSaveFileName(None, '保存文件', '.txt', 'TXT(*.txt)')
267 | if filename != "":
268 | file = open(filename, 'w', encoding='utf-8')
269 | file.write(text)
270 |
271 | # 关于窗口
272 | def about_box(self):
273 | about_title = '关于 CDSE 数据下载工具'
274 | about_version = '版本:V2.0.20240520'
275 | about_copyright = '开发:辽宁省自然资源卫星应用技术中心 技术组'
276 | about_link = 'https://liaoning.sasclouds.com/web/home'
277 | about_website = "官网:辽宁省自然资源卫星遥感服务平台" % about_link
278 | about_tel = '电话:024-86586307'
279 | about_link2 = 'https://github.com/fangdt/cdse-data-download'
280 | about_page = "主页:cdse-data-download" % about_link2
281 | about_content = (
282 | about_version + '
' +
283 | about_copyright + '
' +
284 | about_website + '
' +
285 | about_tel + '
' +
286 | about_page
287 | )
288 | QMessageBox.about(self, about_title, about_content)
289 |
290 |
291 | # 查询线程
292 | class QueryThread(QThread):
293 | # 定义一个信号来传递查询结果
294 | query_result_sig = Signal(str)
295 |
296 | def run(self):
297 | if query.check_date_range(query_startDate, query_endDate) == 1:
298 | self.query_result_sig.emit(f'WARNING: 输入起始日期格式错误,格式为 2024-05-01')
299 | elif query.check_date_range(query_startDate, query_endDate) == 2:
300 | self.query_result_sig.emit(f'WARNING: 输入截止日期格式错误,格式为 2024-05-01')
301 | elif query.check_date_range(query_startDate, query_endDate) == 3:
302 | self.query_result_sig.emit(f'WARNING: 起始日期不能在截止日期之后')
303 | elif query.check_date_range(query_startDate, query_endDate) == 4:
304 | self.query_result_sig.emit(f'WARNING: 起始日期或截止日期无效')
305 | else:
306 | request_url = query.get_https_request(
307 | query_satellite, query_contains, query_startDate, query_endDate, map_geojson, query_expand
308 | )
309 | if query.get_datas(request_url) == 1:
310 | self.query_result_sig.emit(f'未查询到数据')
311 | elif query.get_datas(request_url) == 2:
312 | self.query_result_sig.emit(f'存在未知查询错误')
313 | else:
314 | global data_id_list, data_name_list, date_content_length
315 | data_id_list, data_name_list, date_content_length = query.get_datas(request_url)
316 | global datas_len
317 | datas_len = len(data_id_list)
318 | for i in range(datas_len):
319 | data_name = query.chang_extension(query_satellite, data_name_list[i])
320 | self.query_result_sig.emit(data_name)
321 | self.query_result_sig.emit(f'数据查询完成,共查询到 %d 条数据' % datas_len)
322 |
323 |
324 | # 下载线程
325 | class DownloadThread(QThread):
326 | # 定义一个信号来传递下载状态
327 | download_sig = Signal(str)
328 | # 定义一个信号来传递下载完成状态
329 | download_complete_sig = Signal()
330 |
331 | def run(self):
332 | total_length = sum(date_content_length)
333 | for i in range(len(data_id_list)):
334 | data_id = data_id_list[i]
335 | data_name = query.chang_extension(query_satellite, data_name_list[i])
336 | data_length = date_content_length[i]
337 | if not os.path.exists(output_dir):
338 | os.makedirs(output_dir)
339 | output_file = os.path.join(output_dir, data_name)
340 | if os.path.exists(output_file) and os.path.getsize(output_file) == data_length:
341 | self.download_sig.emit(data_name + ' 已经存在,跳过下载')
342 | else:
343 | token = download.get_access_token(CDSE_email, CDSE_password)
344 | j = i + 1
345 | response = download.download_response(data_id, token)
346 | try:
347 | start_time = datetime.now().strftime('%H:%M:%S')
348 | self.download_sig.emit(f'[ %s ] 正在下载第 %d 条数据:' % (start_time, j))
349 | self.download_sig.emit(data_name)
350 | data_loading_length = 0
351 | with open(output_file, "wb") as file:
352 | for chunk in response.iter_content(chunk_size=8192):
353 | if chunk:
354 | file.write(chunk)
355 | data_loading_length += len(chunk)
356 | global single_progress, total_progress
357 | single_progress = int((data_loading_length / data_length) * 100)
358 | total_loading_length = sum(date_content_length[:i]) + data_loading_length
359 | total_progress = int((total_loading_length / total_length) * 100)
360 | complete_time = datetime.now().strftime('%H:%M:%S')
361 | self.download_sig.emit(f'[ %s ] 第 %d 条数据下载完成' % (complete_time, j))
362 | response.close()
363 | except Exception as e:
364 | self.download_sig.emit(f'WARNING: 发生了一个异常: {e}')
365 | self.download_complete_sig.emit()
366 |
367 |
368 | if __name__ == "__main__":
369 | # 在 PySide6 中,通常不需要显式地设置任何属性来启用高DPI支持,因为 Qt 6 已经默认支持它。
370 | # QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
371 | app = QApplication(sys.argv)
372 | win = MyWindow()
373 | win.show()
374 | sys.exit(app.exec())
375 |
--------------------------------------------------------------------------------