├── 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\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 | --------------------------------------------------------------------------------