複数のシナリオを比較できる施設配置最適化のweb appを作ってください
To upload files, please first save the app
import streamlit as st
import pandas as pd
import numpy as np
import plotly.express as px
import folium
from streamlit_folium import folium_static
st.set_page_config(page_title="施設配置最適化シミュレーター", layout="wide")
# セッション状態の初期化
if 'scenarios' not in st.session_state:
st.session_state.scenarios = {}
st.title("施設配置最適化シミュレーター")
# サイドバーでシナリオを管理
with st.sidebar:
st.header("シナリオ管理")
scenario_name = st.text_input("新規シナリオ名")
# 新規シナリオの作成
if st.button("シナリオを追加"):
if scenario_name and scenario_name not in st.session_state.scenarios:
st.session_state.scenarios[scenario_name] = {
'facilities': [],
'demand_points': [],
'coverage_radius': 1.0
}
st.success(f"シナリオ '{scenario_name}' を作成しました")
elif not scenario_name:
st.error("シナリオ名を入力してください")
else:
st.error("同じ名前のシナリオが既に存在します")
# シナリオの選択
selected_scenario = st.selectbox(
"シナリオを選択",
options=list(st.session_state.scenarios.keys()),
index=0 if st.session_state.scenarios else None
)
# メインコンテンツ
if selected_scenario:
scenario_data = st.session_state.scenarios[selected_scenario]
col1, col2 = st.columns([2, 1])
with col1:
st.subheader("マップ表示")
# 地図の初期表示位置(東京)
m = folium.Map(location=[35.6812, 139.7671], zoom_start=12)
# 施設とカバレッジエリアの表示
for facility in scenario_data['facilities']:
folium.CircleMarker(
location=facility,
radius=8,
color='red',
fill=True
).add_to(m)
# カバレッジエリアを円で表示
folium.Circle(
location=facility,
radius=scenario_data['coverage_radius'] * 1000, # メートル単位に変換
color='blue',
fill=True,
opacity=0.2
).add_to(m)
# 需要点の表示
for point in scenario_data['demand_points']:
folium.CircleMarker(
location=point,
radius=5,
color='green',
fill=True
).add_to(m)
folium_static(m)
with col2:
st.subheader("設定")
# カバレッジ半径の設定
coverage_radius = st.slider(
"カバレッジ半径 (km)",
min_value=0.1,
max_value=5.0,
value=scenario_data['coverage_radius'],
step=0.1
)
scenario_data['coverage_radius'] = coverage_radius
st.subheader("施設の追加")
facility_lat = st.number_input("施設の緯度", value=35.6812, format="%.4f")
facility_lon = st.number_input("施設の経度", value=139.7671, format="%.4f")
if st.button("施設を追加"):
scenario_data['facilities'].append([facility_lat, facility_lon])
st.success("施設を追加しました")
st.subheader("需要点の追加")
demand_lat = st.number_input("需要点の緯度", value=35.6812, format="%.4f", key="demand_lat")
demand_lon = st.number_input("需要点の経度", value=139.7671, format="%.4f", key="demand_lon")
if st.button("需要点を追加"):
scenario_data['demand_points'].append([demand_lat, demand_lon])
st.success("需要点を追加しました")
# 施設・需要点のクリア
if st.button("全てクリア"):
scenario_data['facilities'] = []
scenario_data['demand_points'] = []
st.success("全てのポイントをクリアしました")
# 分析結果の表示
st.subheader("分析結果")
# カバー率の計算
if scenario_data['demand_points'] and scenario_data['facilities']:
covered_points = 0
total_points = len(scenario_data['demand_points'])
for demand_point in scenario_data['demand_points']:
for facility in scenario_data['facilities']:
# 簡易的な距離計算(実際はより精密な計算が必要)
distance = np.sqrt(
(demand_point[0] - facility[0])**2 +
(demand_point[1] - facility[1])**2
) * 111 # 度をkmに概算変換
if distance <= coverage_radius:
covered_points += 1
break
coverage_rate = (covered_points / total_points) * 100
col1, col2, col3 = st.columns(3)
with col1:
st.metric("施設数", len(scenario_data['facilities']))
with col2:
st.metric("需要点数", len(scenario_data['demand_points']))
with col3:
st.metric("カバー率", f"{coverage_rate:.1f}%")
else:
st.info("左のサイドバーで新しいシナリオを作成してください")
Hi! I can help you with any questions about Streamlit and Python. What would you like to know?