-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapp.py
More file actions
128 lines (102 loc) · 4.44 KB
/
Copy pathapp.py
File metadata and controls
128 lines (102 loc) · 4.44 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
import streamlit as st
import geopandas as gpd
import pandas as pd
import folium
from streamlit_folium import st_folium
import os
# --- 1. 페이지 설정 ---
st.set_page_config(page_title="불법 건축물 탐지 대시보드", page_icon="🛰️", layout="wide")
# --- 2. 데이터 로드 함수 ---
@st.cache_data
def load_data(file_path):
if not os.path.exists(file_path):
return None
gdf = gpd.read_file(file_path)
# 날짜/시간 타입 문자열 변환 (오류 방지)
for col in gdf.columns:
if pd.api.types.is_datetime64_any_dtype(gdf[col]):
gdf[col] = gdf[col].astype(str)
return gdf
DATA_FILE = "final_analysis_complete.geojson"
st.title("🛰️ 위성 영상 기반 불법 증축 의심 건축물 탐지 시스템")
st.markdown("---")
with st.spinner("분석 데이터를 불러오는 중입니다..."):
gdf = load_data(DATA_FILE)
if gdf is None:
st.error(f"❌ 결과 파일('{DATA_FILE}')을 찾을 수 없습니다.")
st.stop()
# [수정] ID 컬럼 자동 탐지
possible_ids = ['sf16_bldgi', 'sf16_bldgId', 'building_id']
ID_COL = next((col for col in possible_ids if col in gdf.columns), None)
if ID_COL is None:
st.error(f"데이터에서 건물 ID 컬럼을 찾을 수 없습니다. (존재하는 컬럼: {list(gdf.columns)})")
st.stop()
# 의심 건물 필터링
suspicious_gdf = gdf[gdf['status'] != '정상']
total_count = len(gdf)
suspicious_count = len(suspicious_gdf)
normal_count = total_count - suspicious_count
detection_rate = (suspicious_count / total_count * 100) if total_count > 0 else 0
# (1) 요약 지표
col1, col2, col3 = st.columns(3)
col1.metric("🏢 전체 분석 건물", f"{total_count:,} 동")
col2.metric("🟢 정상 건물", f"{normal_count:,} 동")
col3.metric("🚨 불법 증축 의심", f"{suspicious_count:,} 동", delta=f"감지율 {detection_rate:.2f}%", delta_color="inverse")
st.markdown("---")
# (2) 지도 시각화
st.subheader("🗺️ 탐지 결과 지도")
st.caption("※ 속도 최적화를 위해 '의심 건물'만 지도에 표시합니다.")
if len(suspicious_gdf) > 0:
center_y = suspicious_gdf.geometry.centroid.y.mean()
center_x = suspicious_gdf.geometry.centroid.x.mean()
else:
center_y = gdf.geometry.centroid.y.mean()
center_x = gdf.geometry.centroid.x.mean()
m = folium.Map(location=[center_y, center_x], zoom_start=15, tiles='cartodbpositron')
def style_function(feature):
status = feature['properties'].get('status', 'Unknown')
if '복합' in status: return {'fillColor': 'red', 'color': 'red', 'weight': 2, 'fillOpacity': 0.7}
else: return {'fillColor': 'orange', 'color': 'orange', 'weight': 2, 'fillOpacity': 0.7}
# [ ★★★ 수정된 부분: 툴팁 컬럼명 변경 (diff_height -> diff_hgt) ★★★ ]
tooltip = folium.GeoJsonTooltip(
fields=[ID_COL, 'status', 'diff_area', 'diff_hgt'],
aliases=['ID:', '상태:', '면적 변화(㎡):', '높이 변화(m):'],
localize=True, sticky=False
)
popup = folium.GeoJsonPopup(
fields=[ID_COL, 'status', 'AI_area', 'AI_hgt'],
aliases=['ID', '판정', 'AI 예측 면적(㎡)', 'AI 예측 높이(m)']
)
# 의심 건물만 지도에 추가
folium.GeoJson(
suspicious_gdf,
name="🚨 의심 건축물",
style_function=style_function,
tooltip=tooltip,
popup=popup
).add_to(m)
st_folium(m, width='100%', height=700)
# (3) 상세 데이터 표
st.markdown("---")
st.subheader("📋 의심 건축물 상세 목록")
# [수정] 데이터프레임 표시 컬럼도 실제 컬럼명(diff_hgt)으로 변경
cols_to_display = [ID_COL, 'status', 'AI_area', 'diff_area', 'AI_hgt', 'diff_hgt']
# 만약 원본 데이터에 'hgt_maxcm'(허가 높이 cm)가 있다면 추가해서 보여줌
if 'hgt_maxcm' in suspicious_gdf.columns:
suspicious_gdf['허가 높이(m)'] = suspicious_gdf['hgt_maxcm'].astype(float) / 100.0
cols_to_display.insert(4, '허가 높이(m)')
display_df = suspicious_gdf[cols_to_display].copy()
# 컬럼명 한글화 (보기 좋게)
rename_dict = {
ID_COL: '건물 ID', 'status': '판정 상태',
'AI_area': 'AI 예측 면적(㎡)', 'diff_area': '면적 변화(㎡)',
'AI_hgt': 'AI 예측 높이(m)', 'diff_hgt': '높이 변화(m)'
}
display_df = display_df.rename(columns=rename_dict)
# 정렬 및 표시
sort_col = '면적 변화(㎡)' if '면적 변화(㎡)' in display_df.columns else display_df.columns[1]
st.dataframe(
display_df.sort_values(by=sort_col, ascending=False),
use_container_width=True,
hide_index=True
)