document.addEventListener('DOMContentLoaded', () => { // Leafletマップ初期化 (東京中心) const map = L.map('map').setView([35.6762, 139.6503], 13); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors' }).addTo(map); const gridSize = 10; let elevationGrid = []; let start = null; let goal = null; let pathLayer = null; let startMarker = null; let goalMarker = null; // 初期バウンディングボックス基準化 let initialBounds; function setInitialBounds() { initialBounds = map.getBounds(); } // ランダム標高生成 function generateElevation() { elevationGrid = []; for (let y = 0; y < gridSize; y++) { elevationGrid[y] = []; for (let x = 0; x < gridSize; x++) { elevationGrid[y][x] = Math.floor(Math.random() * 100); } } } // 緯度経度→グリッド function latLngToGrid(latLng) { const bounds = initialBounds; const north = bounds.getNorth(); const south = bounds.getSouth(); const east = bounds.getEast(); const west = bounds.getWest(); const x = Math.floor(((latLng.lng - west) / (east - west)) * gridSize); const y = Math.floor(((north - latLng.lat) / (north - south)) * gridSize); return { x: Math.max(0, Math.min(gridSize - 1, x)), y: Math.max(0, Math.min(gridSize - 1, y)) }; } // グリッド→緯度経度 function gridToLatLng(grid) { const bounds = initialBounds; const north = bounds.getNorth(); const south = bounds.getSouth(); const east = bounds.getEast(); const west = bounds.getWest(); const lat = north - (grid.y / gridSize) * (north - south); const lng = west + (grid.x / gridSize) * (east - west); return L.latLng(lat, lng); } // 坂が少ない道を選ぶA*アルゴリズム function findPath(startGrid, goalGrid) { const openSet = []; const openSetKeys = new Set(); const cameFrom = {}; const gScore = {}; const fScore = {}; const getKey = (node) => `${node.x},${node.y}`; const heuristic = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y); openSet.push(startGrid); openSetKeys.add(getKey(startGrid)); gScore[getKey(startGrid)] = 0; fScore[getKey(startGrid)] = heuristic(startGrid, goalGrid); while (openSet.length > 0) { openSet.sort((a, b) => fScore[getKey(a)] - fScore[getKey(b)]); const current = openSet.shift(); openSetKeys.delete(getKey(current)); if (current.x === goalGrid.x && current.y === goalGrid.y) { const path = []; let temp = current; while (temp) { path.push(temp); temp = cameFrom[getKey(temp)]; if (path.length > gridSize * gridSize) break; // 無限ループ対抗 } return path.reverse(); } const neighbors = [ { x: current.x + 1, y: current.y }, { x: current.x - 1, y: current.y }, { x: current.x, y: current.y + 1 }, { x: current.x, y: current.y - 1 } ]; for (let neighbor of neighbors) { if (neighbor.x < 0 || neighbor.x >= gridSize || neighbor.y < 0 || neighbor.y >= gridSize) continue; const elevDiff = Math.abs(elevationGrid[current.y][current.x] - elevationGrid[neighbor.y][neighbor.x]); // ★ 坂(標高差)を優先(10倍。必要なら値を大きく・小さくして調整してください) const tentativeG = gScore[getKey(current)] + 1 + elevDiff * 10; if (typeof gScore[getKey(neighbor)] === 'undefined' || tentativeG < gScore[getKey(neighbor)]) { cameFrom[getKey(neighbor)] = current; gScore[getKey(neighbor)] = tentativeG; fScore[getKey(neighbor)] = tentativeG + heuristic(neighbor, goalGrid); if (!openSetKeys.has(getKey(neighbor))) { openSet.push(neighbor); openSetKeys.add(getKey(neighbor)); } } } } return []; } // パス描画 function drawPath(path) { if (pathLayer) map.removeLayer(pathLayer); if (path.length === 0) return; const latLngs = path.map(grid => gridToLatLng(grid)); pathLayer = L.polyline(latLngs, { color: 'blue', weight: 3 }).addTo(map); } // クリックで start/goal 選択 map.on('click', (event) => { const grid = latLngToGrid(event.latlng); if (!start) { start = grid; if (startMarker) map.removeLayer(startMarker); startMarker = L.marker(event.latlng).addTo(map); } else if (!goal) { goal = grid; if (goalMarker) map.removeLayer(goalMarker); goalMarker = L.marker(event.latlng).addTo(map); const path = findPath(start, goal); drawPath(path); } }); document.getElementById('resetButton').addEventListener('click', resetMap); function resetMap() { start = null; goal = null; if (startMarker) { map.removeLayer(startMarker); startMarker = null; } if (goalMarker) { map.removeLayer(goalMarker); goalMarker = null; } if (pathLayer) map.removeLayer(pathLayer); generateElevation(); // 初期bounds取り直す場合は↓ // setInitialBounds(); } generateElevation(); setInitialBounds(); });