diff --git a/app.py b/app.py index 8061c6c..bb9f406 100644 --- a/app.py +++ b/app.py @@ -15,179 +15,11 @@ st.set_page_config( initial_sidebar_state="expanded", ) -# ─── Custom CSS ────────────────────────────────────────────────────────────── +# ─── Minimal CSS ───────────────────────────────────────────────────────────── st.markdown(""" """, unsafe_allow_html=True) @@ -300,11 +132,11 @@ def rank_crops(ph, N, P, K, rainfall, temp, pesticide, area): # ─── Sidebar Inputs ────────────────────────────────────────────────────────── with st.sidebar: - st.markdown('', unsafe_allow_html=True) - st.markdown('', unsafe_allow_html=True) - st.markdown("
", unsafe_allow_html=True) + st.header("🌾 种植决策助手") + st.caption("根据土壤和气候,推荐适宜作物") + st.divider() - st.markdown('
🧪 土壤参数
', unsafe_allow_html=True) + st.subheader("🧪 土壤参数") col1, col2 = st.columns(2) with col1: ph = st.slider("pH 值", 4.0, 9.0, 6.5, 0.1) @@ -313,18 +145,18 @@ with st.sidebar: P = st.slider("磷 P (mg/kg)", 0, 100, 45, 5) K = st.slider("钾 K (mg/kg)", 0, 150, 60, 5) - st.markdown('
🌦 气象数据
', unsafe_allow_html=True) + st.subheader("🌦 气象数据") col3, col4 = st.columns(2) with col3: rainfall = st.slider("降雨量 (mm/月)", 0, 400, 120, 10) with col4: temp = st.slider("温度 (°C)", 0, 45, 22, 1) - st.markdown('
🌱 种植参数
', unsafe_allow_html=True) + st.subheader("🌱 种植参数") area = st.number_input("种植面积 (公顷)", 0.1, 10000.0, 100.0, 10.0) pesticide = st.slider("农药用量 (kg/ha)", 0, 200, 50, 5) - st.markdown('
🎯 目标作物
', unsafe_allow_html=True) + st.subheader("🎯 目标作物") selected_crop = st.selectbox( "选择分析作物", list(CROPS.keys()), @@ -339,54 +171,22 @@ best_crop = rankings[0] # ─── Main Layout ───────────────────────────────────────────────────────────── -st.markdown(f""" -
-
种植决策助手
-
-
输入土壤与气象条件,获得作物产量预测与种植建议
-""", unsafe_allow_html=True) - -st.markdown("
", unsafe_allow_html=True) +st.title("🌾 种植决策助手") +st.caption("输入土壤与气象条件,获得作物产量预测与种植建议") # KPI row +overall = np.mean(list(factors.values())) k1, k2, k3, k4 = st.columns(4) -with k1: - st.markdown(f""" -
-
{yph:,.0f}
-
kg / 公顷
-
{CROPS[selected_crop]['emoji']} {selected_crop} 单产
-
""", unsafe_allow_html=True) -with k2: - st.markdown(f""" -
-
{ytotal/1000:,.1f}
-
吨 / 总产量
-
📦 {area:.0f} 公顷总产
-
""", unsafe_allow_html=True) -with k3: - overall = np.mean(list(factors.values())) - st.markdown(f""" -
-
{overall*100:.1f}%
-
综合适宜度
-
🎯 环境匹配指数
-
""", unsafe_allow_html=True) -with k4: - st.markdown(f""" -
-
{best_crop['emoji']}
-
{best_crop['crop']} ({best_crop['score']*100:.0f}%)
-
🏆 最优推荐作物
-
""", unsafe_allow_html=True) - -st.markdown("
", unsafe_allow_html=True) +k1.metric(f"{CROPS[selected_crop]['emoji']} {selected_crop} 单产", f"{yph:,.0f} kg/ha") +k2.metric(f"📦 {area:.0f} 公顷总产", f"{ytotal/1000:,.1f} 吨") +k3.metric("🎯 环境匹配指数", f"{overall*100:.1f}%") +k4.metric("🏆 最优推荐作物", f"{best_crop['emoji']} {best_crop['crop']}", f"匹配度 {best_crop['score']*100:.0f}%") # ─── Charts Row ────────────────────────────────────────────────────────────── col_left, col_right = st.columns([3, 2]) with col_left: - st.markdown('
📊 影响因子雷达图
', unsafe_allow_html=True) + st.subheader("📊 影响因子雷达图") factor_names = list(factors.keys()) factor_vals = [round(v * 100, 1) for v in factors.values()] @@ -428,30 +228,17 @@ with col_left: st.plotly_chart(fig_radar, use_container_width=True) with col_right: - st.markdown('
🏅 作物推荐排行
', unsafe_allow_html=True) + st.subheader("🏅 作物推荐排行") for i, r in enumerate(rankings[:4]): rank_icons = ["🥇", "🥈", "🥉", "4️⃣"] - bar_width = int(r['score'] * 100) - bar_color = r['color'] - st.markdown(f""" -
-
-
- {rank_icons[i]} - {r['emoji']} {r['crop']} -
- {r['score']*100:.1f}% -
-
-
-
-
- {r['yield_ha']:,.0f} kg/ha · 总产 {r['total_yield']/1000:,.1f} 吨 -
-
""", unsafe_allow_html=True) + with st.container(border=True): + c1, c2 = st.columns([3, 1]) + c1.markdown(f"**{rank_icons[i]} {r['emoji']} {r['crop']}**") + c2.markdown(f"
{r['score']*100:.1f}%
", unsafe_allow_html=True) + st.progress(int(r['score'] * 100), text=f"{r['yield_ha']:,.0f} kg/ha · 总产 {r['total_yield']/1000:,.1f} 吨") # ─── Sensitivity Analysis ───────────────────────────────────────────────────── -st.markdown('
📈 产量敏感性分析
', unsafe_allow_html=True) +st.subheader("📈 产量敏感性分析") sa_col1, sa_col2 = st.columns(2) @@ -504,7 +291,7 @@ with sa_col2: st.plotly_chart(fig_R, use_container_width=True) # ─── All Crops Comparison Bar Chart ─────────────────────────────────────────── -st.markdown('
🌐 全作物产量对比
', unsafe_allow_html=True) +st.subheader("🌐 全作物产量对比") crop_names = [f"{r['emoji']} {r['crop']}" for r in rankings] crop_yields = [r['yield_ha'] for r in rankings] @@ -530,61 +317,42 @@ fig_bar.update_layout( st.plotly_chart(fig_bar, use_container_width=True) # ─── Advisory Panel ─────────────────────────────────────────────────────────── -st.markdown('
💡 种植建议
', unsafe_allow_html=True) +st.subheader("💡 种植建议") adv1, adv2 = st.columns(2) with adv1: crop_opt = CROPS[selected_crop]["optimal"] - advisories = [] if not (crop_opt["ph"][0] <= ph <= crop_opt["ph"][1]): - advisories.append(("warn", f"pH {ph} 偏离 {selected_crop} 适宜范围 {crop_opt['ph']},建议{'施石灰' if ph < crop_opt['ph'][0] else '施硫磺'}调节")) + st.warning(f"pH {ph} 偏离 {selected_crop} 适宜范围 {crop_opt['ph']},建议{'施石灰' if ph < crop_opt['ph'][0] else '施硫磺'}调节") else: - advisories.append(("good", f"土壤 pH {ph} 处于 {selected_crop} 适宜范围内 ✓")) + st.success(f"土壤 pH {ph} 处于 {selected_crop} 适宜范围内") if N < crop_opt["N"][0]: - advisories.append(("warn", f"氮肥不足({N} vs 建议 {crop_opt['N'][0]}-{crop_opt['N'][1]} mg/kg),建议追施尿素")) + st.warning(f"氮肥不足({N} vs 建议 {crop_opt['N'][0]}-{crop_opt['N'][1]} mg/kg),建议追施尿素") elif N > crop_opt["N"][1]: - advisories.append(("warn", f"氮肥过量({N} mg/kg),可能造成徒长,建议减施")) + st.warning(f"氮肥过量({N} mg/kg),可能造成徒长,建议减施") else: - advisories.append(("good", f"氮肥水平 {N} mg/kg 适宜 ✓")) + st.success(f"氮肥水平 {N} mg/kg 适宜") if rainfall < crop_opt["rainfall"][0]: - advisories.append(("warn", f"降雨量不足,建议增加灌溉(缺水 {crop_opt['rainfall'][0]-rainfall} mm)")) + st.warning(f"降雨量不足,建议增加灌溉(缺水 {crop_opt['rainfall'][0]-rainfall} mm)") elif rainfall > crop_opt["rainfall"][1]: - advisories.append(("warn", f"降雨量偏多,注意防涝排水")) + st.warning(f"降雨量偏多,注意防涝排水") else: - advisories.append(("good", f"降雨量 {rainfall}mm 适合 {selected_crop} 生长 ✓")) - - for typ, msg in advisories: - css_class = "alert-good" if typ == "good" else "alert-warn" - st.markdown(f'
{msg}
', unsafe_allow_html=True) + st.success(f"降雨量 {rainfall}mm 适合 {selected_crop} 生长") with adv2: - st.markdown(f""" -
-
- 当前环境参数下适宜种植: -
- """, unsafe_allow_html=True) - badges = "".join([ - f'{r["emoji"]} {r["crop"]} {r["score"]*100:.0f}%' - for r in rankings if r['score'] > 0.6 - ]) - st.markdown(f'{badges}
', unsafe_allow_html=True) + suitable = [r for r in rankings if r['score'] > 0.6] + if suitable: + st.info("当前环境参数下适宜种植:" + "、".join([f"{r['emoji']} {r['crop']} ({r['score']*100:.0f}%)" for r in suitable])) + else: + st.info("当前环境参数下暂无特别适宜的作物") - st.markdown(f""" -
- 最优方案:{best_crop['emoji']} {best_crop['crop']}
- 预期单产:{best_crop['yield_ha']:,.0f} kg/ha
- {area:.0f}公顷总产:{best_crop['total_yield']/1000:,.1f} 吨 -
- """, unsafe_allow_html=True) + with st.container(border=True): + st.markdown(f"**最优方案:{best_crop['emoji']} {best_crop['crop']}**") + st.markdown(f"- 预期单产:**{best_crop['yield_ha']:,.0f} kg/ha**") + st.markdown(f"- {area:.0f} 公顷总产:**{best_crop['total_yield']/1000:,.1f} 吨**") -# ─── Footer ─────────────────────────────────────────────────────────────────── -st.markdown("
", unsafe_allow_html=True) -st.markdown(""" -
- 种植决策助手 · 基于 Cobb-Douglas 多因子产量模型 · 仅供参考 -
-""", unsafe_allow_html=True) +st.divider() +st.caption("种植决策助手 · 基于 Cobb-Douglas 多因子产量模型 · 仅供参考")