Spaces:
Sleeping
Sleeping
| // 初始化地圖 | |
| var map = L.map('map').setView([23.5, 121], 8); | |
| console.log('地圖初始化成功'); // 調試代碼 | |
| L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { | |
| maxZoom: 18, | |
| errorTileUrl: 'no-image-icon-23494.png' // 可選:設置錯誤時的替代圖像 | |
| }).addTo(map).on('tileerror', function(error) { | |
| console.error('地圖圖層加載失敗', error); // 調試代碼 | |
| }); | |
| console.log('圖層添加成功'); // 調試代碼 | |
| // 測試地圖圖層加載 | |
| fetch('https://a.tile.openstreetmap.org/0/0/0.png') | |
| .then(response => { | |
| if (!response.ok) { | |
| throw new Error('Network response was not ok'); | |
| } | |
| return response.blob(); | |
| }) | |
| .then(blob => { | |
| console.log('地圖圖層加載成功'); | |
| }) | |
| .catch(error => { | |
| console.error('地圖圖層加載失敗', error); | |
| }); | |
| // 定義顏色對應的poi_type | |
| var poiTypeColors = { | |
| "mrt": "red", | |
| "school": "blue", | |
| "landfill": "green", | |
| "hospital": "yellow", | |
| "collage": "purple", | |
| "park": "orange", | |
| "financial_industry": "cyan", | |
| "entertainment": "magenta", | |
| "shopping": "lime", | |
| "temple": "brown", | |
| "funeral_industry": "navy", | |
| "night_market": "olive", | |
| "hsr": "teal", | |
| "gas_station": "pink", | |
| "station": "coral", | |
| "bus_station": "gold" | |
| }; | |
| // 創建標記圖層 | |
| var markersLayer = L.layerGroup().addTo(map); | |
| var poiMarkers = {}; | |
| var selectedMarker = null; // 用於保存所選位置的標記 | |
| // 動態生成下拉選單選項 | |
| var select = document.getElementById('poiTypeSelect'); | |
| var latInput = document.getElementById('latInput'); | |
| var lngInput = document.getElementById('lngInput'); | |
| var searchButton = document.getElementById('searchButton'); | |
| var uploadButton = document.getElementById('uploadButton'); | |
| var clearButton = document.getElementById('clearButton'); | |
| var fileInput = document.getElementById('fileInput'); | |
| var confirmationModal = $('#confirmationModal'); | |
| var confirmationMessage = document.getElementById('confirmationMessage'); | |
| var confirmButton = document.getElementById('confirmButton'); | |
| // 監聽POI類型選擇改變事件 | |
| select.addEventListener('change', function(e) { | |
| var selectedType = e.target.value; | |
| // 使用者選擇POI類型後,自動取得當前地圖中心點的經緯度進行查詢 | |
| var center = map.getCenter(); | |
| searchNearestPOIs(center.lat, center.lng, selectedType); | |
| }); | |
| searchButton.addEventListener('click', function() { | |
| var lat = parseFloat(latInput.value); | |
| var lng = parseFloat(lngInput.value); | |
| var poiType = select.value; | |
| if (!isNaN(lat) && !isNaN(lng)) { | |
| setMarker(lat, lng); | |
| searchNearestPOIs(lat, lng, poiType); | |
| } else { | |
| alert('請輸入有效的經緯度'); | |
| } | |
| }); | |
| // 上傳POI數據 | |
| uploadButton.addEventListener('click', function() { | |
| fileInput.click(); | |
| }); | |
| fileInput.addEventListener('change', function() { | |
| var file = fileInput.files[0]; | |
| if (file) { | |
| confirmationMessage.textContent = '確定要上傳這個POI數據文件嗎?'; | |
| confirmButton.onclick = function() { | |
| var formData = new FormData(); | |
| formData.append("file", file); | |
| fetch('https://chienweichang-poi-data.hf.space/upload-poi', { | |
| method: 'POST', | |
| body: formData | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| confirmationModal.modal('hide'); | |
| }) | |
| .catch(error => { | |
| console.error('Error uploading POI data:', error); | |
| confirmationModal.modal('hide'); | |
| alert('上傳POI數據失敗'); | |
| }); | |
| }; | |
| confirmationModal.modal('show'); | |
| } | |
| }); | |
| // 清除POI | |
| clearButton.addEventListener('click', function() { | |
| confirmationMessage.textContent = '確定要清除所有POI數據嗎?'; | |
| confirmButton.onclick = function() { | |
| fetch('https://chienweichang-poi-data.hf.space/clear-kdtrees', { | |
| method: 'POST' | |
| }) | |
| .then(response => response.json()) | |
| .then(data => { | |
| confirmationModal.modal('hide'); | |
| }) | |
| .catch(error => { | |
| console.error('Error clearing POI:', error); | |
| confirmationModal.modal('hide'); | |
| alert('清除POI失敗'); | |
| }); | |
| }; | |
| confirmationModal.modal('show'); | |
| }); | |
| // 查詢最近的POI | |
| function searchNearestPOIs(lat, lng, poiType) { | |
| // 清空現有標記 | |
| markersLayer.clearLayers(); | |
| poiMarkers = {}; | |
| // 調用後端API獲取最近的POI | |
| fetch(`https://chienweichang-poi-data.hf.space/poi/nearest?lat=${lat}&lng=${lng}&poi_type=${poiType}`) | |
| .then(response => response.json()) | |
| .then(data => { | |
| if (data && data.length > 0) { | |
| let infoHtml = generatePOITable(data); | |
| document.getElementById('info').innerHTML = infoHtml; | |
| // 更新地圖圖例 | |
| updateLegend(new Set(data.map(poi => poi.poi_type))); | |
| // 為每個表格行添加點擊事件 | |
| data.forEach((poi, index) => { | |
| document.getElementById(`poi-${index}`).addEventListener('click', () => { | |
| var marker = poiMarkers[index]; | |
| map.setView(marker.getLatLng(), 15); // 跳轉到POI位置並放大地圖 | |
| marker.openPopup(); // 開啟POI的彈出訊息 | |
| // 取消之前選中的標記樣式 | |
| Object.values(poiMarkers).forEach(m => m.getElement().classList.remove('highlight')); | |
| // 添加選中標記的樣式 | |
| marker.getElement().classList.add('highlight'); | |
| }); | |
| }); | |
| } else { | |
| document.getElementById('info').innerHTML = `沒有找到附近的 POI`; | |
| } | |
| }) | |
| .catch(error => { | |
| console.error('Error fetching nearest POI:', error); | |
| document.getElementById('info').innerHTML = `錯誤:無法獲取最近的POI`; | |
| }); | |
| } | |
| function generatePOITable(data) { | |
| let infoHtml = `<table class="table table-bordered"> | |
| <thead> | |
| <tr> | |
| <th scope="col">名稱</th> | |
| <th scope="col">類型</th> | |
| <th scope="col">距離 (米)</th> | |
| </tr> | |
| </thead> | |
| <tbody>`; | |
| data.forEach((poi, index) => { | |
| var customIcon = L.divIcon({ | |
| className: 'custom-icon', | |
| html: `<div style="background-color: ${poiTypeColors[poi.poi_type] || 'gray'}; width: 12px; height: 12px;"></div>` | |
| }); | |
| var marker = L.marker([poi.latitude, poi.longitude], { icon: customIcon }) | |
| .bindPopup(`<b>${poi.name}</b><br>距離:${poi.distance} 米`) | |
| .bindTooltip(`<b>${poi.name}</b><br>距離:${poi.distance} 米`, { permanent: false, direction: 'top' }) | |
| .addTo(markersLayer); | |
| // 保存marker以便後續使用 | |
| poiMarkers[index] = marker; | |
| infoHtml += `<tr id="poi-${index}" class="poi-row"> | |
| <td>${poi.name}</td> | |
| <td>${poi.poi_type}</td> | |
| <td>${poi.distance}</td> | |
| </tr>`; | |
| }); | |
| infoHtml += `</tbody></table>`; | |
| return infoHtml; | |
| } | |
| // 點擊地圖事件 | |
| map.on('click', function(e) { | |
| var lat = e.latlng.lat; | |
| var lng = e.latlng.lng; | |
| var poiType = select.value; | |
| latInput.value = lat.toFixed(6); | |
| lngInput.value = lng.toFixed(6); | |
| setMarker(lat, lng); | |
| searchNearestPOIs(lat, lng, poiType); | |
| }); | |
| // 在地圖上設置或更新標記 | |
| function setMarker(lat, lng) { | |
| if (selectedMarker) { | |
| map.removeLayer(selectedMarker); | |
| } | |
| selectedMarker = L.marker([lat, lng], { | |
| icon: L.icon({ | |
| iconUrl: 'https://unpkg.com/[email protected]/dist/images/marker-icon.png', | |
| shadowUrl: 'https://unpkg.com/[email protected]/dist/images/marker-shadow.png', | |
| iconAnchor: [12, 41], // 設置圖標的錨點為圖標底部中心 | |
| shadowAnchor: [13, 41], // 設置陰影的錨點為陰影底部中心 | |
| popupAnchor: [0, -41] // 設置彈出框的錨點為圖標頂部 | |
| }) | |
| }).addTo(map); | |
| map.setView([lat, lng], 15); // 跳轉到所選位置並放大地圖 | |
| } | |
| // 更新地圖圖例 | |
| function updateLegend(displayedTypes) { | |
| var legendHtml = '<b>圖例</b><br>'; | |
| displayedTypes.forEach(type => { | |
| legendHtml += `<i style="background:${poiTypeColors[type]}; width: 12px; height: 12px; display: inline-block; margin-right: 5px;"></i> ${type}<br>`; | |
| }); | |
| document.getElementById('legend').innerHTML = legendHtml; | |
| } | |
| // 初始加載圖例 | |
| updateLegend(new Set()); | |