// 메인 JavaScript 파일 document.addEventListener('DOMContentLoaded', function() { console.log('Flask 앱이 로드되었습니다.'); // 고흥 지역 좌표 const GOHEUNG_LAT = 34.6047; const GOHEUNG_LNG = 127.2855; // 지도 객체 저장 var maps = {}; // 지도 초기화 함수 function initMap(containerId, lat, lng, zoom) { if (maps[containerId]) { maps[containerId].invalidateSize(); return maps[containerId]; } var container = document.getElementById(containerId); if (!container) return null; var map = L.map(containerId).setView([lat, lng], zoom); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© OpenStreetMap contributors' }).addTo(map); // 고흥 마커 추가 L.marker([lat, lng]).addTo(map) .bindPopup('고흥') .openPopup(); maps[containerId] = map; return map; } // 메뉴 아이템과 페이지 콘텐츠 요소 가져오기 const menuItems = document.querySelectorAll('.menu-item'); const submenuItems = document.querySelectorAll('.submenu-item'); const pageContents = document.querySelectorAll('.page-content'); const hasSubmenuItems = document.querySelectorAll('.has-submenu'); // 페이지 전환 함수 function switchPage(pageId) { // 모든 페이지 콘텐츠 숨기기 pageContents.forEach(function(content) { content.classList.remove('active'); }); // 모든 메뉴 아이템 비활성화 menuItems.forEach(function(item) { item.classList.remove('active'); }); // 모든 서브메뉴 아이템 비활성화 submenuItems.forEach(function(item) { item.classList.remove('active'); }); // 선택된 페이지 콘텐츠 표시 const targetPage = document.getElementById(pageId); if (targetPage) { targetPage.classList.add('active'); } // 선택된 메뉴 아이템 활성화 const activeMenuItem = document.querySelector('.menu-item[data-page="' + pageId + '"]'); if (activeMenuItem) { activeMenuItem.classList.add('active'); } // 선택된 서브메뉴 아이템 활성화 const activeSubmenuItem = document.querySelector('.submenu-item[data-page="' + pageId + '"]'); if (activeSubmenuItem) { activeSubmenuItem.classList.add('active'); // 부모 메뉴도 활성화 const parentMenu = activeSubmenuItem.closest('.has-submenu'); if (parentMenu) { parentMenu.querySelector('.menu-item').classList.add('active'); } } // 페이지별 지도 초기화 setTimeout(function() { if (pageId === 'intro') { initMap('map-intro', GOHEUNG_LAT, GOHEUNG_LNG, 11); } else if (pageId === 'flood-simulation') { Terrain3D.init('terrain-flood-3d'); loadFlood3DChart(); } else if (pageId === 'flood-monitoring') { initMap('flood-heatmap', GOHEUNG_LAT, GOHEUNG_LNG, 12); initMap('flood-mini-map', GOHEUNG_LAT, GOHEUNG_LNG, 13); loadFloodMonitoringData(); } else if (pageId === 'drought-simulation') { Terrain3D.init('terrain-drought-3d'); loadDrought3DChart(); } else if (pageId === 'drought-monitoring') { initGridMap('drought-heatmap'); initMap('drought-mini-map', GOHEUNG_LAT, GOHEUNG_LNG, 13); loadDroughtMonitoringData(); } else if (pageId === 'kma-test') { loadKmaForecast(); } }, 100); } // 침수 3D 시뮬레이션 차트 로드 function loadFlood3DChart() { fetch('/api/terrain/simulation-chart') .then(function(response) { return response.json(); }) .then(function(data) { if (data.status === 'success' && data.image) { var chartImg = document.getElementById('flood-simulation-chart'); if (chartImg) { chartImg.src = 'data:image/png;base64,' + data.image; } } }); } // 가뭄 3D 시뮬레이션 차트 로드 function loadDrought3DChart() { fetch('/api/terrain/simulation-chart') .then(function(response) { return response.json(); }) .then(function(data) { if (data.status === 'success' && data.image) { var chartImg = document.getElementById('drought-simulation-chart'); if (chartImg) { chartImg.src = 'data:image/png;base64,' + data.image; } } }); } // 침수 모니터링 데이터 로드 function loadFloodMonitoringData() { // 히트맵 이미지 로드 fetch('/api/flood/monitoring-heatmap') .then(function(response) { return response.json(); }) .then(function(data) { if (data.status === 'success') { var heatmapOverlay = document.getElementById('flood-heatmap-overlay'); if (heatmapOverlay && data.heatmap_image) { heatmapOverlay.src = 'data:image/png;base64,' + data.heatmap_image; } var miniHeatmap = document.getElementById('flood-mini-heatmap'); if (miniHeatmap && data.mini_heatmap_image) { miniHeatmap.src = 'data:image/png;base64,' + data.mini_heatmap_image; } // Risk Score 업데이트 var riskScoreEl = document.getElementById('flood-risk-score'); if (riskScoreEl && data.risk_score) { riskScoreEl.textContent = data.risk_score; } } }); // 월별 강수량 차트 로드 fetch('/api/flood/monthly-chart') .then(function(response) { return response.json(); }) .then(function(data) { if (data.status === 'success' && data.image) { var chartImg = document.getElementById('flood-monthly-chart'); if (chartImg) { chartImg.src = 'data:image/png;base64,' + data.image; } } }); } // 침수 모니터링 필터 변경 이벤트 var floodMapType = document.getElementById('flood-map-type'); var floodPeriod = document.getElementById('flood-period'); var floodViewType = document.getElementById('flood-view-type'); [floodMapType, floodPeriod, floodViewType].forEach(function(select) { if (select) { select.addEventListener('change', function() { loadFloodMonitoringData(); }); } }); // 침수 모니터링 버튼 이벤트 var floodDetailBtn = document.getElementById('btn-flood-detail'); if (floodDetailBtn) { floodDetailBtn.addEventListener('click', function() { // 시뮬레이션 페이지로 이동 switchPage('flood-simulation'); // 하위 메뉴 열기 var floodSubmenu = document.querySelector('.has-submenu:nth-child(3)'); if (floodSubmenu) { floodSubmenu.classList.add('open'); } }); } var floodReportBtn = document.getElementById('btn-flood-report'); if (floodReportBtn) { floodReportBtn.addEventListener('click', function() { alert('보고서 출력 기능은 추후 업데이트 예정입니다.'); }); } // 침수 수위 곡선 그래프 로드 function loadFloodGraph() { var graphType = document.getElementById('flood-graph-type'); var graphPeriod = document.getElementById('flood-graph-period'); var graphImage = document.getElementById('flood-graph-image'); var graphPlaceholder = document.getElementById('flood-graph-placeholder'); var graphLoading = document.getElementById('flood-graph-loading'); if (!graphType || !graphPeriod) return; var type = graphType.value; var period = graphPeriod.value; // 로딩 표시 if (graphLoading) graphLoading.style.display = 'block'; if (graphPlaceholder) graphPlaceholder.style.display = 'none'; fetch('/api/flood/graph?type=' + type + '&period=' + period) .then(function(response) { return response.json(); }) .then(function(data) { if (graphLoading) graphLoading.style.display = 'none'; if (data.status === 'success' && data.image) { if (graphImage) { graphImage.src = 'data:image/png;base64,' + data.image; graphImage.style.display = 'block'; } if (graphPlaceholder) graphPlaceholder.style.display = 'none'; // 통계 정보 업데이트 if (data.stats) { var currentLevel = document.getElementById('flood-current-level'); var avgLevel = document.getElementById('flood-avg-level'); var maxLevel = document.getElementById('flood-max-level'); if (currentLevel) currentLevel.textContent = data.stats.current; if (avgLevel) avgLevel.textContent = data.stats.avg; if (maxLevel) maxLevel.textContent = data.stats.max; } } }) .catch(function(error) { if (graphLoading) graphLoading.style.display = 'none'; if (graphPlaceholder) { graphPlaceholder.style.display = 'block'; graphPlaceholder.querySelector('span').textContent = '그래프 로드 실패'; } }); } // 침수 수위 곡선 버튼 이벤트 var floodGraphLoadBtn = document.getElementById('btn-flood-graph-load'); if (floodGraphLoadBtn) { floodGraphLoadBtn.addEventListener('click', loadFloodGraph); } // 가뭄 시계열 변화 그래프 로드 function loadDroughtGraph() { var graphType = document.getElementById('drought-graph-type'); var graphPeriod = document.getElementById('drought-graph-period'); var graphImage = document.getElementById('drought-graph-image'); var graphPlaceholder = document.getElementById('drought-graph-placeholder'); var graphLoading = document.getElementById('drought-graph-loading'); if (!graphType || !graphPeriod) return; var type = graphType.value; var period = graphPeriod.value; // 로딩 표시 if (graphLoading) graphLoading.style.display = 'block'; if (graphPlaceholder) graphPlaceholder.style.display = 'none'; fetch('/api/drought/graph?type=' + type + '&period=' + period) .then(function(response) { return response.json(); }) .then(function(data) { if (graphLoading) graphLoading.style.display = 'none'; if (data.status === 'success' && data.image) { if (graphImage) { graphImage.src = 'data:image/png;base64,' + data.image; graphImage.style.display = 'block'; } if (graphPlaceholder) graphPlaceholder.style.display = 'none'; // 통계 정보 업데이트 if (data.stats) { var currentValue = document.getElementById('drought-current-value'); var avgValue = document.getElementById('drought-avg-value'); var minValue = document.getElementById('drought-min-value'); var gradeValue = document.getElementById('drought-grade'); if (currentValue) currentValue.textContent = data.stats.current; if (avgValue) avgValue.textContent = data.stats.avg; if (minValue) minValue.textContent = data.stats.min; if (gradeValue) gradeValue.textContent = data.stats.grade; } } }) .catch(function(error) { if (graphLoading) graphLoading.style.display = 'none'; if (graphPlaceholder) { graphPlaceholder.style.display = 'block'; graphPlaceholder.querySelector('span').textContent = '그래프 로드 실패'; } }); } // 가뭄 시계열 변화 그래프 버튼 이벤트 var droughtGraphLoadBtn = document.getElementById('btn-drought-graph-load'); if (droughtGraphLoadBtn) { droughtGraphLoadBtn.addEventListener('click', loadDroughtGraph); } // SHP 파일을 JPG로 렌더링하여 지도 영역에 표시 function initGridMap(containerId, gridSize) { gridSize = gridSize || 500; var container = document.getElementById(containerId); if (!container) return; // 기존 Leaflet 지도 제거 (이미지로 대체) if (maps[containerId]) { maps[containerId].remove(); delete maps[containerId]; } // 로딩 표시 container.innerHTML = '
SHP 렌더링 중...
'; fetch('/api/drought/shp-render?grid=' + gridSize) .then(function(response) { return response.json(); }) .then(function(data) { if (data.status === 'success' && data.image) { container.innerHTML = ''; var img = document.createElement('img'); img.src = 'data:image/jpeg;base64,' + data.image; img.style.width = '100%'; img.style.height = '100%'; img.style.objectFit = 'contain'; img.alt = '가뭄 SHP 렌더링'; container.appendChild(img); } else { container.innerHTML = '
' + (data.message || '렌더링 실패') + '
'; } }) .catch(function(error) { container.innerHTML = '
오류: ' + error.message + '
'; }); } // 격자 단위 버튼 이벤트 document.querySelectorAll('.grid-btn').forEach(function(btn) { btn.addEventListener('click', function() { document.querySelectorAll('.grid-btn').forEach(function(b) { b.classList.remove('active'); }); btn.classList.add('active'); initGridMap('drought-heatmap', parseInt(btn.dataset.grid)); }); }); // 가뭄 모니터링 데이터 로드 function loadDroughtMonitoringData() { // 히트맵 이미지 로드 fetch('/api/drought/monitoring-heatmap') .then(function(response) { return response.json(); }) .then(function(data) { if (data.status === 'success') { var heatmapOverlay = document.getElementById('drought-heatmap-overlay'); if (heatmapOverlay && data.heatmap_image) { heatmapOverlay.src = 'data:image/png;base64,' + data.heatmap_image; } var miniHeatmap = document.getElementById('drought-mini-heatmap'); if (miniHeatmap && data.mini_heatmap_image) { miniHeatmap.src = 'data:image/png;base64,' + data.mini_heatmap_image; } // Risk Score 업데이트 var riskScoreEl = document.getElementById('drought-risk-score'); if (riskScoreEl && data.risk_score) { riskScoreEl.textContent = data.risk_score; } } }); // 월별 가뭄 지수 차트 로드 fetch('/api/drought/monthly-chart') .then(function(response) { return response.json(); }) .then(function(data) { if (data.status === 'success' && data.image) { var chartImg = document.getElementById('drought-monthly-chart'); if (chartImg) { chartImg.src = 'data:image/png;base64,' + data.image; } } }); } // 가뭄 모니터링 필터 변경 이벤트 var droughtMapType = document.getElementById('drought-map-type'); var droughtPeriod = document.getElementById('drought-period'); var droughtViewType = document.getElementById('drought-view-type'); [droughtMapType, droughtPeriod, droughtViewType].forEach(function(select) { if (select) { select.addEventListener('change', function() { loadDroughtMonitoringData(); }); } }); // 가뭄 모니터링 버튼 이벤트 var droughtDetailBtn = document.getElementById('btn-drought-detail'); if (droughtDetailBtn) { droughtDetailBtn.addEventListener('click', function() { // 시뮬레이션 페이지로 이동 switchPage('drought-simulation'); // 하위 메뉴 열기 var droughtSubmenu = document.querySelector('.has-submenu:nth-child(4)'); if (droughtSubmenu) { droughtSubmenu.classList.add('open'); } }); } var droughtReportBtn = document.getElementById('btn-drought-report'); if (droughtReportBtn) { droughtReportBtn.addEventListener('click', function() { alert('보고서 출력 기능은 추후 업데이트 예정입니다.'); }); } // 가뭄 탭 버튼 이벤트 (탭 전환만, 페이지 이동 없음) document.querySelectorAll('.drought-tab-btn').forEach(function(btn) { btn.addEventListener('click', function() { document.querySelectorAll('.drought-tab-btn').forEach(function(b) { b.classList.remove('active'); }); btn.classList.add('active'); }); }); // 메뉴 클릭 이벤트 리스너 등록 menuItems.forEach(function(item) { item.addEventListener('click', function(e) { e.preventDefault(); const parentLi = this.parentElement; // 하위 메뉴가 있는 경우 토글 if (parentLi.classList.contains('has-submenu')) { parentLi.classList.toggle('open'); } const pageId = this.getAttribute('data-page'); switchPage(pageId); }); }); // 서브메뉴 클릭 이벤트 리스너 등록 submenuItems.forEach(function(item) { item.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); const pageId = this.getAttribute('data-page'); switchPage(pageId); }); }); // API 호출 함수 function callApi(apiUrl, container) { const loading = container.querySelector('.loading'); const plotImage = container.querySelector('.plot-image'); const plotMessage = container.querySelector('.plot-message'); // 로딩 표시 loading.style.display = 'block'; plotImage.style.display = 'none'; plotMessage.style.display = 'none'; fetch(apiUrl) .then(function(response) { return response.json(); }) .then(function(data) { loading.style.display = 'none'; if (data.status === 'success' && data.image) { plotImage.src = 'data:image/png;base64,' + data.image; plotImage.style.display = 'block'; } else { plotMessage.textContent = data.message || '결과를 불러올 수 없습니다.'; plotMessage.style.display = 'block'; } }) .catch(function(error) { loading.style.display = 'none'; plotMessage.textContent = '오류가 발생했습니다: ' + error.message; plotMessage.style.display = 'block'; }); } // API 버튼 클릭 이벤트 리스너 등록 const apiBtns = document.querySelectorAll('.api-btn'); apiBtns.forEach(function(btn) { btn.addEventListener('click', function() { const apiUrl = this.getAttribute('data-api'); const container = this.parentElement.querySelector('.plot-container'); callApi(apiUrl, container); }); }); // 침수 3D 시뮬레이션 버튼 클릭 이벤트 var floodSimBtn = document.getElementById('btn-flood-simulation'); if (floodSimBtn) { floodSimBtn.addEventListener('click', function() { loadFlood3DChart(); // 통계 업데이트 (랜덤 시뮬레이션) var areaEl = document.querySelector('.flood-stat-area'); var buildingEl = document.querySelector('.flood-stat-building'); var costEl = document.querySelector('.flood-stat-cost'); if (areaEl) { var area = Math.floor(Math.random() * 3000) + 3000; areaEl.innerHTML = area.toLocaleString() + ' '; } if (buildingEl) { var building = Math.floor(Math.random() * 100) + 80; buildingEl.innerHTML = building.toLocaleString() + ' '; } if (costEl) { var cost = (Math.random() * 15 + 10).toFixed(1); costEl.innerHTML = cost + ' 억원'; } }); } // 가뭄 3D 시뮬레이션 버튼 클릭 이벤트 var droughtSimBtn = document.getElementById('btn-drought-simulation'); if (droughtSimBtn) { droughtSimBtn.addEventListener('click', function() { loadDrought3DChart(); // 통계 업데이트 (랜덤 시뮬레이션) var areaEl = document.querySelector('.drought-stat-area'); var farmEl = document.querySelector('.drought-stat-farm'); var costEl = document.querySelector('.drought-stat-cost'); if (areaEl) { var area = Math.floor(Math.random() * 5000) + 5000; areaEl.innerHTML = area.toLocaleString() + ' '; } if (farmEl) { var farm = Math.floor(Math.random() * 2000) + 2000; farmEl.innerHTML = farm.toLocaleString() + ' '; } if (costEl) { var cost = (Math.random() * 10 + 8).toFixed(1); costEl.innerHTML = cost + ' 억원'; } }); } // ========== Water Body 추출 기능 (Drag & Drop) ========== var wbDropzone = document.getElementById('wb-dropzone'); var wbFileInput = document.getElementById('wb-file-input'); var wbBrowseBtn = document.getElementById('btn-wb-browse'); var wbRemoveBtn = document.getElementById('btn-wb-remove'); var wbExtractBtn = document.getElementById('btn-waterbody-extract'); var wbDropzoneContent = document.getElementById('wb-dropzone-content'); var wbPreview = document.getElementById('wb-preview'); var wbInputImage = document.getElementById('wb-input-image'); var wbOutputImage = document.getElementById('wb-output-image'); var wbResultPlaceholder = document.getElementById('wb-result-placeholder'); var wbResultContainer = document.getElementById('wb-result-container'); var wbExtractLoading = document.getElementById('wb-extract-loading'); var wbResultInfo = document.getElementById('wb-result-info'); // 업로드된 파일 저장 var uploadedFile = null; // 파일 선택 버튼 클릭 if (wbBrowseBtn) { wbBrowseBtn.addEventListener('click', function(e) { e.preventDefault(); wbFileInput.click(); }); } // 파일 선택 이벤트 if (wbFileInput) { wbFileInput.addEventListener('change', function(e) { var file = e.target.files[0]; if (file) { handleFileUpload(file); } }); } // 드래그 이벤트 if (wbDropzone) { // 드래그 오버 wbDropzone.addEventListener('dragover', function(e) { e.preventDefault(); e.stopPropagation(); wbDropzone.classList.add('dragover'); }); // 드래그 리브 wbDropzone.addEventListener('dragleave', function(e) { e.preventDefault(); e.stopPropagation(); wbDropzone.classList.remove('dragover'); }); // 드롭 wbDropzone.addEventListener('drop', function(e) { e.preventDefault(); e.stopPropagation(); wbDropzone.classList.remove('dragover'); var file = e.dataTransfer.files[0]; if (file && file.type.startsWith('image/')) { handleFileUpload(file); } else { alert('이미지 파일만 업로드 가능합니다.'); } }); } // 파일 업로드 처리 function handleFileUpload(file) { uploadedFile = file; // 이미지 미리보기 var reader = new FileReader(); reader.onload = function(e) { wbInputImage.src = e.target.result; wbDropzoneContent.style.display = 'none'; wbPreview.style.display = 'block'; // 추출 버튼 활성화 if (wbExtractBtn) { wbExtractBtn.disabled = false; } }; reader.readAsDataURL(file); // 결과 영역 초기화 resetOutputArea(); } // 이미지 제거 버튼 if (wbRemoveBtn) { wbRemoveBtn.addEventListener('click', function(e) { e.preventDefault(); e.stopPropagation(); resetInputArea(); resetOutputArea(); }); } // 입력 영역 초기화 function resetInputArea() { uploadedFile = null; wbInputImage.src = ''; wbDropzoneContent.style.display = 'flex'; wbPreview.style.display = 'none'; wbFileInput.value = ''; if (wbExtractBtn) { wbExtractBtn.disabled = true; } } // 출력 영역 초기화 function resetOutputArea() { wbOutputImage.src = ''; wbOutputImage.style.display = 'none'; wbResultPlaceholder.style.display = 'flex'; wbResultInfo.style.display = 'none'; } // 추출 버튼 클릭 if (wbExtractBtn) { wbExtractBtn.addEventListener('click', function() { if (!uploadedFile) { alert('이미지를 먼저 업로드해주세요.'); return; } // 로딩 표시 wbExtractBtn.style.display = 'none'; wbExtractLoading.style.display = 'flex'; wbResultPlaceholder.style.display = 'none'; // FormData로 이미지 전송 var formData = new FormData(); formData.append('image', uploadedFile); fetch('/api/waterbody/predict', { method: 'POST', body: formData }) .then(function(response) { return response.json(); }) .then(function(data) { wbExtractBtn.style.display = 'flex'; wbExtractLoading.style.display = 'none'; if (data.status === 'success') { // 결과 이미지 표시 wbOutputImage.src = 'data:image/png;base64,' + data.output_image; wbOutputImage.style.display = 'block'; wbResultPlaceholder.style.display = 'none'; // 결과 정보 표시 if (data.data) { document.getElementById('wb-area').textContent = data.data.area || '-'; document.getElementById('wb-ratio').textContent = data.data.ratio || '-'; document.getElementById('wb-count').textContent = data.data.count ? data.data.count + '개' : '-'; document.getElementById('wb-confidence').textContent = data.data.confidence || '-'; } wbResultInfo.style.display = 'flex'; } else { alert('추출 실패: ' + (data.message || '알 수 없는 오류')); wbResultPlaceholder.style.display = 'flex'; } }) .catch(function(error) { wbExtractBtn.style.display = 'flex'; wbExtractLoading.style.display = 'none'; wbResultPlaceholder.style.display = 'flex'; alert('오류가 발생했습니다: ' + error.message); }); }); } // ========== Intro 대시보드 기능 ========== // 현재 날짜/시간 표시 function updateCurrentDateTime() { var now = new Date(); var days = ['일', '월', '화', '수', '목', '금', '토']; var dateStr = now.getFullYear() + '.' + String(now.getMonth() + 1).padStart(2, '0') + '.' + String(now.getDate()).padStart(2, '0') + ' (' + days[now.getDay()] + ')'; var dateTimeEl = document.getElementById('current-datetime'); if (dateTimeEl) { dateTimeEl.textContent = dateStr; } } // 날짜 셀렉터 초기화 function initDateSelector() { var now = new Date(); var yearSelect = document.getElementById('select-year'); var monthSelect = document.getElementById('select-month'); var daySelect = document.getElementById('select-day'); if (!yearSelect || !monthSelect || !daySelect) return; // 현재 날짜로 설정 yearSelect.value = now.getFullYear(); monthSelect.value = now.getMonth() + 1; // 일 옵션 업데이트 updateDayOptions(); daySelect.value = now.getDate(); // 월 변경 시 일 옵션 업데이트 yearSelect.addEventListener('change', updateDayOptions); monthSelect.addEventListener('change', updateDayOptions); } // 일 옵션 업데이트 function updateDayOptions() { var yearSelect = document.getElementById('select-year'); var monthSelect = document.getElementById('select-month'); var daySelect = document.getElementById('select-day'); if (!yearSelect || !monthSelect || !daySelect) return; var year = parseInt(yearSelect.value); var month = parseInt(monthSelect.value); var daysInMonth = new Date(year, month, 0).getDate(); var currentDay = parseInt(daySelect.value) || 1; daySelect.innerHTML = ''; for (var i = 1; i <= daysInMonth; i++) { var option = document.createElement('option'); option.value = i; option.textContent = i + '일'; daySelect.appendChild(option); } daySelect.value = Math.min(currentDay, daysInMonth); } // 모니터링 데이터 업데이트 (시뮬레이션) function updateMonitoringData() { // 침수 예측 확률 업데이트 var floodProb = Math.floor(Math.random() * 40) + 10; var floodProbEl = document.getElementById('flood-prob'); var floodFill = document.querySelector('.flood-fill'); var floodStatus = document.querySelector('.monitoring-card.flood .prob-status'); if (floodProbEl) floodProbEl.textContent = floodProb; if (floodFill) floodFill.style.width = floodProb + '%'; if (floodStatus) { if (floodProb < 30) { floodStatus.className = 'prob-status safe'; floodStatus.textContent = '낮음'; } else if (floodProb < 60) { floodStatus.className = 'prob-status warning'; floodStatus.textContent = '주의'; } else { floodStatus.className = 'prob-status danger'; floodStatus.textContent = '위험'; } } // 가뭄 예측 확률 업데이트 var droughtProb = Math.floor(Math.random() * 50) + 30; var droughtProbEl = document.getElementById('drought-prob'); var droughtFill = document.querySelector('.drought-fill'); var droughtStatus = document.querySelector('.monitoring-card.drought .prob-status'); if (droughtProbEl) droughtProbEl.textContent = droughtProb; if (droughtFill) droughtFill.style.width = droughtProb + '%'; if (droughtStatus) { if (droughtProb < 30) { droughtStatus.className = 'prob-status safe'; droughtStatus.textContent = '낮음'; } else if (droughtProb < 60) { droughtStatus.className = 'prob-status warning'; droughtStatus.textContent = '주의'; } else { droughtStatus.className = 'prob-status danger'; droughtStatus.textContent = '위험'; } } // 종합 위험도 업데이트 var overallRisk = Math.floor((floodProb + droughtProb) / 2); var gaugeValue = document.querySelector('.gauge-value'); var gaugeLabel = document.querySelector('.gauge-label'); var gaugeFill = document.querySelector('.gauge-fill'); if (gaugeValue) gaugeValue.textContent = overallRisk; if (gaugeFill) { gaugeFill.setAttribute('stroke-dasharray', overallRisk + ', 100'); if (overallRisk < 30) { gaugeFill.className.baseVal = 'gauge-fill safe'; if (gaugeLabel) { gaugeLabel.textContent = '안전'; gaugeLabel.style.color = '#27ae60'; } } else if (overallRisk < 60) { gaugeFill.className.baseVal = 'gauge-fill warning'; if (gaugeLabel) { gaugeLabel.textContent = '보통'; gaugeLabel.style.color = '#f39c12'; } } else { gaugeFill.className.baseVal = 'gauge-fill danger'; if (gaugeLabel) { gaugeLabel.textContent = '위험'; gaugeLabel.style.color = '#e74c3c'; } } } // 예보 미니 업데이트 var floodForecasts = document.querySelectorAll('.monitoring-card.flood .fc-value'); floodForecasts.forEach(function(el) { el.textContent = (floodProb + Math.floor(Math.random() * 10) - 5) + '%'; }); var droughtForecasts = document.querySelectorAll('.monitoring-card.drought .fc-value'); droughtForecasts.forEach(function(el) { el.textContent = (droughtProb + Math.floor(Math.random() * 10) - 5) + '%'; }); } // 날짜 조회 버튼 클릭 var dateSearchBtn = document.getElementById('btn-date-search'); if (dateSearchBtn) { dateSearchBtn.addEventListener('click', function() { updateMonitoringData(); var year = document.getElementById('select-year').value; var month = document.getElementById('select-month').value; var day = document.getElementById('select-day').value; var dateTimeEl = document.getElementById('current-datetime'); var selectedDate = new Date(year, month - 1, day); var days = ['일', '월', '화', '수', '목', '금', '토']; var dateStr = year + '.' + String(month).padStart(2, '0') + '.' + String(day).padStart(2, '0') + ' (' + days[selectedDate.getDay()] + ')'; if (dateTimeEl) { dateTimeEl.textContent = dateStr; } }); } // ========== 테스트용 화면 (기상청 단기예보 - 고흥) ========== var KMA_DISPLAY_COLS = [ { key: 'numEf', label: '예보차수' }, { key: 'wf', label: '날씨' }, { key: 'ta', label: '예상기온(℃)' }, { key: 'rnSt', label: '강수확률(%)' }, { key: 'rnYn', label: '강수형태' }, { key: 'wd1', label: '풍향(1)' }, { key: 'wd2', label: '풍향(2)' }, { key: 'wsIt', label: '풍속강도' }, { key: 'wfCd', label: '하늘상태' } ]; var KMA_RNYN_NAMES = { '0': '없음', '1': '비', '2': '비/눈', '3': '눈', '4': '소나기' }; var KMA_WSLT_NAMES = { '1': '약', '2': '약간강', '3': '강', '4': '매우강' }; var kmaCache = null; function loadKmaForecast() { var placeholder = document.getElementById('kma-placeholder'); var loading = document.getElementById('kma-loading'); var dataContainer = document.getElementById('kma-data-container'); var summary = document.getElementById('kma-summary'); // 캐시가 있으면 재사용 if (kmaCache) { renderKmaTable(kmaCache); return; } if (placeholder) placeholder.style.display = 'none'; if (summary) summary.style.display = 'none'; loading.style.display = 'flex'; dataContainer.style.display = 'none'; fetch('/api/kma/forecast') .then(function(res) { return res.json(); }) .then(function(result) { loading.style.display = 'none'; if (!result.success) { if (placeholder) placeholder.style.display = 'block'; return; } kmaCache = result; renderKmaTable(result); }) .catch(function(err) { loading.style.display = 'none'; if (placeholder) placeholder.style.display = 'block'; }); } function renderKmaTable(result) { var summary = document.getElementById('kma-summary'); var dataContainer = document.getElementById('kma-data-container'); document.getElementById('kma-count').textContent = result.count + '건'; // 발표시간 표시 var rows = result.rows || []; if (rows.length > 0 && rows[0].announceTime) { var s = String(rows[0].announceTime); var formatted = s.length >= 10 ? s.substring(0,4) + '.' + s.substring(4,6) + '.' + s.substring(6,8) + ' ' + s.substring(8,10) + ':00' : s; document.getElementById('kma-announce-time').textContent = formatted; } if (summary) summary.style.display = 'flex'; var rows = result.rows || []; var tableHead = document.getElementById('kma-table-head'); var tableBody = document.getElementById('kma-table-body'); tableHead.innerHTML = ''; tableBody.innerHTML = ''; if (rows.length > 0) { var headRow = ''; KMA_DISPLAY_COLS.forEach(function(col) { headRow += '' + col.label + ''; }); headRow += ''; tableHead.innerHTML = headRow; rows.forEach(function(row) { var tr = ''; KMA_DISPLAY_COLS.forEach(function(col) { var val = row[col.key]; if (col.key === 'rnYn' && KMA_RNYN_NAMES[String(val)] !== undefined) { val = KMA_RNYN_NAMES[String(val)]; } if (col.key === 'wsIt' && KMA_WSLT_NAMES[String(val)] !== undefined) { val = KMA_WSLT_NAMES[String(val)]; } if (col.key === 'numEf') { var efNames = {0:'오늘밤', 1:'내일오전', 2:'내일오후', 3:'모레오전', 4:'모레오후', 5:'+3일오전', 6:'+3일오후', 7:'+4일오전', 8:'+4일오후'}; if (efNames[val] !== undefined) val = efNames[val]; } tr += '' + (val !== null && val !== undefined && val !== '' ? val : '-') + ''; }); tr += ''; tableBody.innerHTML += tr; }); dataContainer.style.display = 'block'; } } var btnKmaFetch = document.getElementById('btn-kma-fetch'); if (btnKmaFetch) { btnKmaFetch.addEventListener('click', function() { kmaCache = null; // 수동 조회 시 캐시 초기화 loadKmaForecast(); }); } // 초기화 updateCurrentDateTime(); initDateSelector(); // 초기 페이지 지도 로드 setTimeout(function() { initMap('map-intro', GOHEUNG_LAT, GOHEUNG_LNG, 11); }, 200); });