import os import json import datetime import streamlit as st import streamlit.components.v1 as components import re import warnings import traceback warnings.filterwarnings("ignore") # os.environ['SDK_CLIENT_HOST'] = 'https://pre-engine-aiearth.aliyun.com' import aie default_map_html = ''' IPyWidget export
''' label_text_tmpl='''
{LABEL_TEXT}
''' secondary_label_text_tmpl='''
{LABEL_TEXT}
''' page_title_text_tmpl='''
{TITLE_TEXT}
{DESC_TEXT}
''' label_text_component_height = 41 st.set_page_config(layout="wide") # AIE数据集stac code aie_dataset_category = [ "LANDSAT_LT05_T02_T1_L2" ,"LANDSAT_LE07_E02_T1_L2" ,"LANDSAT_LC08_C02_T1_L2" ,"LANDSAT_LC09_C02_T1_L2" ,"SENTINEL_MSIL2A" ] # L5:1984-2011 # L7:1999-至今 # L8:2014-至今 # L9:2021-至今 # S2:2018-至今 # 数据集时间范围 dataset_timestamp = { "LANDSAT_LT05_T02_T1_L2": {"startDate": "1984-01-01", "endDate": "2011-12-31", "sampleStart": "2011-11-01", "sampleEnd": "2011-11-30"}, "LANDSAT_LE07_E02_T1_L2": {"startDate": "1999-01-01", "endDate": "2021-12-31", "sampleStart": "2021-12-01", "sampleEnd": "2021-12-31"}, "LANDSAT_LC08_C02_T1_L2": {"startDate": "2014-01-01", "endDate": "__NOW__", "sampleStart": "2023-02-01", "sampleEnd": "2023-03-28"}, "LANDSAT_LC09_C02_T1_L2": {"startDate": "2021-01-01", "endDate": "__NOW__", "sampleStart": "2023-01-01", "sampleEnd": "2023-03-01"}, "SENTINEL_MSIL2A": {"startDate": "2018-01-01", "endDate": "__NOW__", "sampleStart": "2023-03-01", "sampleEnd": "2023-03-28"} } # aie_area_meta = {} image_collection_reduce_funcs = [ "median", "min", "max", "mosaic", "mean" ] def aie_init(): token = os.environ.get("SDK_TOKEN") #token = "81921ae3af932cf35f1e56de4a8b1ca4" aie.Authenticate(token=token) aie.Initialize() @st.cache_resource def load_area_select_options(): file_path = "./china-area.json" with open(file_path, 'r', encoding="utf-8") as reader: area_arr = json.loads(reader.read()) area_dic = {} for item in area_arr: province_name = item['levelOneAreaName'] city_name = item['levelTwoAreaName'] district_name = item['areaName'] if not province_name in area_dic: area_dic[province_name] = {} cities = area_dic[province_name] if not city_name in cities: cities[city_name] = {} districts = cities[city_name] if not district_name in districts: districts[district_name] = district_name print("tianxun area data init complete") return area_dic @st.cache_resource def load_dataset_info(): file_path = "./aie_dataset_meta.json" with open(file_path, 'r', encoding="utf-8") as reader: _dataset = json.loads(reader.read()) return _dataset def page_reset_callback(): st.session_state['dateset_picker'] = 'LANDSAT_LT05_T02_T1_L2' st.session_state['region_province_select'] = '北京市' st.session_state['region_city_select'] = '请选择城市' st.session_state['region_district_select'] = '请选择' st.session_state['time_picker'] = [datetime.date(2011, 1, 1), datetime.date(2011, 6, 30)] st.session_state['cloud_picker'] = '20%' st.session_state['reduce_picker'] = 'median' st.session_state['band_picker'] = [] st.session_state['min_input'] = '8000' st.session_state['max_input'] = '13000' st.session_state['render_map_html'] = None def page_components_render(ctr_panel, map_panel): with ctr_panel: hide_streamlit_menu = """ """ st.markdown(hide_streamlit_menu, unsafe_allow_html=True) area_selections = load_area_select_options() dataset_meta = load_dataset_info() # title components.html(page_title_text_tmpl.format(TITLE_TEXT="影像快速检索", DESC_TEXT="卫星影像数据获取是业务应用分析的第一步工作,基于平台现有Landsat和Sentinel系列卫星数据资源,根据用户选择的感兴趣行政区、时间范围、云量等参数,可快速获取覆盖区域内的已镶嵌和裁剪后的卫星数据。"), height=160) # 选择数据集类型 dataset_label, dataset_select = st.columns([1.1, 3.9]) # todo: 实在不行,label用html渲染 with dataset_label: # st.text("数据类型") components.html(label_text_tmpl.format(LABEL_TEXT="数据类型"), height=label_text_component_height) with dataset_select: dataset_empty = st.empty() dataset = dataset_empty.selectbox(label="请选择数据类型", options=aie_dataset_category, key="dateset_picker", label_visibility="collapsed") # 区域过滤 region_label, province_select, city_select = st.columns([1.65,2.8,2.75]) with region_label: # st.text("区域选择") components.html(label_text_tmpl.format(LABEL_TEXT="区域选择"), height=label_text_component_height) with province_select: region_province_options = [name for name in area_selections] region_province = st.selectbox(label="请选择省", options=region_province_options, key="region_province_select", label_visibility="collapsed") with city_select: region_city_options = [] if region_province: region_city_options = [name for name in area_selections[region_province]] region_city_options_update = ['请选择'] + region_city_options region_city = st.selectbox(label="请选择市", options=region_city_options_update, key="region_city_select", label_visibility="collapsed") # 选择时间区间 time_label, time_select = st.columns([1.1, 3.9]) with time_label: # st.text("检索日期") components.html(label_text_tmpl.format(LABEL_TEXT="检索日期"), height=label_text_component_height) with time_select: try: min_time = datetime.date(2011, 1, 1) max_time = datetime.date(2011, 6, 30) sample_start_time = datetime.date(2011, 1, 1) sample_end_time = datetime.date(2011, 6, 30) if dataset and dataset in dataset_timestamp: min_time = datetime.datetime.strptime(dataset_timestamp[dataset]['startDate'], '%Y-%m-%d') if dataset_timestamp[dataset]['endDate'] == '__NOW__': # max_time = datetime.date.today() max_time = datetime.datetime.strptime(str(datetime.date.today()), '%Y-%m-%d') else: max_time = datetime.datetime.strptime(dataset_timestamp[dataset]['endDate'], '%Y-%m-%d') # print(f"[DEBUG] {dataset} max_time = {max_time}, min_time = {min_time}") sample_start_time = datetime.datetime.strptime(dataset_timestamp[dataset]['sampleStart'], '%Y-%m-%d') sample_end_time = datetime.datetime.strptime(dataset_timestamp[dataset]['sampleEnd'], '%Y-%m-%d') start_date, end_date = st.date_input(label="Select image time range", min_value=min_time, max_value=max_time,value=[sample_start_time, sample_end_time], key="time_picker", label_visibility="collapsed") except Exception as e: print("date picker error. ignore") # 选择云量 cloud_label, cloud_select = st.columns([1.1, 3.9]) with cloud_label: # st.text("云量") components.html(label_text_tmpl.format(LABEL_TEXT="云 量"), height=label_text_component_height) with cloud_select: cloud_options = [ str(x) + "%" for x in range(0, 105, 5)] end_cloud = st.select_slider( '选择云量', options=cloud_options, key='cloud_picker', value='20%', label_visibility="collapsed") # 选择镶嵌方式 crop_label, crop_select = st.columns([1.1, 3.9]) with crop_label: #st.text("镶嵌方式") components.html(label_text_tmpl.format(LABEL_TEXT="镶嵌方式"), height=label_text_component_height) with crop_select: crop_type = st.selectbox(label="请选择镶嵌方式", options=image_collection_reduce_funcs, key="reduce_picker", label_visibility="collapsed") # 波段选择(默认填充前3个) band_label, band_select = st.columns([1.1, 3.9]) with band_label: # st.text("波段选择") components.html(label_text_tmpl.format(LABEL_TEXT="波段选择"), height=label_text_component_height) with band_select: bands_option = [] if dataset and dataset in dataset_meta: bands_option = [band_name for band_name in dataset_meta[dataset]['bands']] bands = st.multiselect("Select bands to display", options=bands_option, key="band_picker", label_visibility="collapsed", max_selections=3) minmax_label, min_label, min_select, max_label, max_select = st.columns([1.55, 1.22, 1.74, 1.23, 1.74]) with min_label: # st.text("最小值") components.html(secondary_label_text_tmpl.format(LABEL_TEXT="最小值"), height=label_text_component_height) with min_select: default_min = 100 if dataset and dataset in dataset_meta: default_min = dataset_meta[dataset]['min'] min = st.text_input(label="Enter image min",value=str(default_min), key="min_input", label_visibility="collapsed") with max_label: #st.text("最大值") components.html(secondary_label_text_tmpl.format(LABEL_TEXT="最大值"), height=label_text_component_height) with max_select: default_max = 10000 if dataset and dataset in dataset_meta: default_max = dataset_meta[dataset]['max'] max = st.text_input(label="Enter image max",value=str(default_max), key="max_input", label_visibility="collapsed") reset_btn, query_btn = st.columns([1, 2]) with reset_btn: page_reset = st.button("重置", type='secondary', key='reset_btn', use_container_width=True, on_click=page_reset_callback) with query_btn: submit = st.button("检索", type='primary', key='submit_btn', use_container_width=True) # date_debug = str(dataset) + ", max_time = " + str(max_time) + ", min_time = " + str(min_time) # st.write(date_debug) if page_reset: with map_panel: components.html(default_map_html, height=780, scrolling=False) return if submit: # 参数校验 errMsg = None if not start_date or not end_date: errMsg = "请选择时间" elif start_date >= max_time.date(): errMsg = "未检索到数据,请重新设置检索日期" elif not bands or len(bands) == 0: errMsg = "请选择波段" elif not min or not max: errMsg = "请输入最大最小值" else: try: ti = float(min) ta = float(max) except Exception as e: errMsg = "最大最小值只能为数字" if errMsg: aie_init() with ctr_panel: st.error(errMsg) with map_panel: components.html(default_map_html, height=780, scrolling=False) return if dataset: try: # 拼接AIE 数据查询SDK语句 aie_init() image_collection = aie.ImageCollection(dataset) if start_date and end_date: image_collection = image_collection.filterDate(start_date.strftime('%Y-%m-%d'), end_date.strftime('%Y-%m-%d')) if region_province or region_city: fc = aie.FeatureCollection('China_City') if region_province: fc = fc.filter(aie.Filter.eq('province', region_province)) if region_city and region_city != '请选择': fc = fc.filter(aie.Filter.eq('city', region_city)) # 用选择的数据重新初始化map组件 aie_map = aie.Map( center=fc.getCenter(), height=800, zoom=7 ) region_vis_params = { 'color': '#00FF00' } aie_map.addLayer( fc, region_vis_params, '行政区划边界', bounds=fc.getBounds() ) image_collection = image_collection.filterBounds(fc.geometry()) if end_cloud: end_cloud_val = end_cloud.replace('%','') image_collection = image_collection.filter(aie.Filter.lte('eo:cloud_cover', int(end_cloud_val))) if crop_type == 'median': image_collection = image_collection.median() elif crop_type == 'min': image_collection = image_collection.min() elif crop_type == 'max': image_collection = image_collection.max() elif crop_type == 'mosaic': image_collection = image_collection.mosaic() elif crop_type == 'mean': image_collection = image_collection.mean() if region_province or region_city: image_collection = image_collection.clipToCollection(fc) vis_params = {} if bands: vis_params['bands'] = list(bands) if min and max: vis_params['min'] = float(min) vis_params['max'] = float(max) aie_map.addLayer( image_collection, vis_params, dataset, bounds=image_collection.getBounds() ) except Exception as e: traceback.print_exc() with ctr_panel: # st.error(e) st.error("未检索到数据集信息, 请重新设置查询条件") with map_panel: components.html(default_map_html, height=780, scrolling=False) return with map_panel: # 对于未点击submit的情况, 直接读取静态html字符串来加快load速度 if submit: map_html_source = aie_map.to_html() st.session_state['render_map_html'] = map_html_source components.html(map_html_source, height=780, scrolling=False) else: display_template = default_map_html if 'render_map_html' in st.session_state and st.session_state['render_map_html']: display_template = st.session_state['render_map_html'] components.html(display_template, height=780, scrolling=False) # components.html(default_map_html, height=800, scrolling=False) row1_col1, row1_col2 = st.columns([0.8, 2.2]) page_components_render(row1_col1, row1_col2)