[경로탐색] 경로 탐색 프론트-엔드 개발하기

2024. 11. 28. 08:40프로그래밍/GIS

728x90

 

 

[Windows] OSRM 경로 서버 구축 - 서울 탐색

서울 사람은 아니지만,  [Windows] OSRM 실행하기 - OSRM backend 서버 실행[Windows] OSRM 실행하기 - osrm-extract▶ [실패] OSRM 실행하기 - 오류 해결Chat gpt 와 함께 OSRM 빌드해보기 - 디버그모드저번 글에서

tobee.tistory.com

 

 

 

사실 여기서 끝내도 되지만 좀 코드가 아쉽죠? 그래서 교수님께 이렇게 여쭈어 보았습니다.

출발지와 목적지를 사용자 입력으로 받고 싶어요...

 

그랬더니, 두 개의 소스를 하사 하셨습니다.

 

1. 클릭으로 출발지 목적지를 정하기

다음과 같이 index.html 페이지를 고치면 된다고 합니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSRM Route Finder</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <style>
        #map {
            height: 600px;
        }
    </style>
</head>
<body>
    <div id="map"></div>
    <script>
        // 지도 설정
        const map = L.map('map').setView([37.5665, 126.9780], 13);  // 서울의 중심 좌표

        // OpenStreetMap 타일 레이어 추가
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        // OSRM 경로 탐색 API를 사용한 경로 설정
        const osrmUrl = 'http://localhost:5000/route/v1/driving/'; // OSRM 백엔드 서버의 경로 API

        let start = null; // 출발지
        let end = null;   // 목적지

        // 사용자가 클릭한 좌표에 출발지나 목적지를 설정
        map.on('click', function(e) {
            if (!start) {
                // 첫 번째 클릭: 출발지 설정
                start = { lat: e.latlng.lat, lng: e.latlng.lng };
                L.marker([start.lat, start.lng]).addTo(map).bindPopup("Start").openPopup();
                alert('출발지 설정 완료! 이제 목적지를 클릭하세요.');
            } else if (!end) {
                // 두 번째 클릭: 목적지 설정
                end = { lat: e.latlng.lat, lng: e.latlng.lng };
                L.marker([end.lat, end.lng]).addTo(map).bindPopup("End").openPopup();
                alert('목적지 설정 완료! 경로를 계산합니다.');
                findRoute(start, end); // 경로 탐색
            }
        });

        // 경로 탐색 함수
        function findRoute(start, end) {
            const url = osrmUrl + `${start.lng},${start.lat};${end.lng},${end.lat}?overview=full&geometries=geojson`;

            fetch(url)
                .then(response => response.json())
                .then(data => {
                    const route = data.routes[0].geometry.coordinates;
                    const latlngs = route.map(coord => [coord[1], coord[0]]); // 경로 좌표 변환

                    // 경로를 지도에 표시
                    L.polyline(latlngs, { color: 'blue', weight: 4 }).addTo(map);
                })
                .catch(error => {
                    console.error('Error fetching route:', error);
                });
        }
    </script>
</body>
</html>

이름은 거창하게 map_route.html 로 정했습니다.

http://localhost:8080/map_route.html

 

 

2. 텍스트로 입력 출발지/목적지 입력 받기

다음과 같이 index.html 페이지를 고치면 된다고 합니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSRM Route Finder</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <style>
        #map {
            height: 600px;
        }
        #input {
            margin: 20px;
        }
    </style>
</head>
<body>
    <div>
        <label>출발지 좌표 (위도, 경도):</label>
        <input type="text" id="start-input" placeholder="예: 37.5665, 126.9780" />
        <label>목적지 좌표 (위도, 경도):</label>
        <input type="text" id="end-input" placeholder="예: 37.5550, 126.9705" />
        <button onclick="setRoute()">경로 탐색</button>
    </div>
    <div id="map"></div>
    
    <script>
        const map = L.map('map').setView([37.5665, 126.9780], 13);  // 서울의 중심 좌표

        // OpenStreetMap 타일 레이어 추가
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        const osrmUrl = 'http://localhost:5000/route/v1/driving/'; // OSRM 백엔드 서버의 경로 API

        // 경로 탐색 함수
        function setRoute() {
            const startInput = document.getElementById('start-input').value.split(',');
            const endInput = document.getElementById('end-input').value.split(',');

            const start = { lat: parseFloat(startInput[0]), lng: parseFloat(startInput[1]) };
            const end = { lat: parseFloat(endInput[0]), lng: parseFloat(endInput[1]) };

            if (isNaN(start.lat) || isNaN(start.lng) || isNaN(end.lat) || isNaN(end.lng)) {
                alert("유효한 좌표를 입력하세요.");
                return;
            }

            L.marker([start.lat, start.lng]).addTo(map).bindPopup("Start").openPopup();
            L.marker([end.lat, end.lng]).addTo(map).bindPopup("End").openPopup();

            findRoute(start, end); // 경로 탐색
        }

        // 경로 탐색 함수
        function findRoute(start, end) {
            const url = osrmUrl + `${start.lng},${start.lat};${end.lng},${end.lat}?overview=full&geometries=geojson`;

            fetch(url)
                .then(response => response.json())
                .then(data => {
                    const route = data.routes[0].geometry.coordinates;
                    const latlngs = route.map(coord => [coord[1], coord[0]]); // 경로 좌표 변환

                    // 경로를 지도에 표시
                    L.polyline(latlngs, { color: 'blue', weight: 4 }).addTo(map);
                })
                .catch(error => {
                    console.error('Error fetching route:', error);
                });
        }
    </script>
</body>
</html>

이건 map_route2.html 이라고 했습니다.

 

음... 그런데 이렇게 위경도를 집어 넣으면 누가 알고 집어넣을 수 있을까 하는 생각이 들었습니다.

그래서 이렇게 당당하게 요구 했죠...

사용자 입력 텍스트 필드를 통한 출발지와 목적지 설정에서 누가 위/경도를 알고 집어 넣을 수 있을까요?
지도에 클릭한 값을 가져오도록 고쳐주세요~

 

그랬더니... 또다시 코드를 하사해 주셨습니다. 이건 map_route3.html 이라고 할께요...

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSRM Route Finder</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <style>
        #map {
            height: 600px;
        }
        #input {
            margin: 20px;
        }
    </style>
</head>
<body>
    <div>
        <label>출발지 좌표 (위도, 경도):</label>
        <input type="text" id="start-input" placeholder="예: 37.5665, 126.9780" readonly />
        <label>목적지 좌표 (위도, 경도):</label>
        <input type="text" id="end-input" placeholder="예: 37.5550, 126.9705" readonly />
        <button onclick="setRoute()">경로 탐색</button>
    </div>
    <div id="map"></div>
    
    <script>
        const map = L.map('map').setView([37.5665, 126.9780], 13);  // 서울의 중심 좌표

        // OpenStreetMap 타일 레이어 추가
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        const osrmUrl = 'http://localhost:5000/route/v1/driving/'; // OSRM 백엔드 서버의 경로 API

        let start = null; // 출발지
        let end = null;   // 목적지

        // 사용자가 클릭한 좌표에 출발지나 목적지를 설정
        map.on('click', function(e) {
            if (!start) {
                // 첫 번째 클릭: 출발지 설정
                start = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('start-input').value = `${start.lat}, ${start.lng}`; // 입력 필드에 값 설정
                L.marker([start.lat, start.lng]).addTo(map).bindPopup("Start").openPopup();
                alert('출발지 설정 완료! 이제 목적지를 클릭하세요.');
            } else if (!end) {
                // 두 번째 클릭: 목적지 설정
                end = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('end-input').value = `${end.lat}, ${end.lng}`; // 입력 필드에 값 설정
                L.marker([end.lat, end.lng]).addTo(map).bindPopup("End").openPopup();
                alert('목적지 설정 완료! 경로를 계산합니다.');
                findRoute(start, end); // 경로 탐색
            }
        });

        // 경로 탐색 함수
        function findRoute(start, end) {
            const url = osrmUrl + `${start.lng},${start.lat};${end.lng},${end.lat}?overview=full&geometries=geojson`;

            fetch(url)
                .then(response => response.json())
                .then(data => {
                    const route = data.routes[0].geometry.coordinates;
                    const latlngs = route.map(coord => [coord[1], coord[0]]); // 경로 좌표 변환

                    // 경로를 지도에 표시
                    L.polyline(latlngs, { color: 'blue', weight: 4 }).addTo(map);
                })
                .catch(error => {
                    console.error('Error fetching route:', error);
                });
        }

        // 경로 탐색 버튼 클릭 시 호출될 함수
        function setRoute() {
            if (!start || !end) {
                alert("출발지와 목적지를 모두 설정해주세요.");
                return;
            }

            findRoute(start, end); // 경로 탐색
        }
    </script>
</body>
</html>


위의 코드는 아래처럼 나옵니다. 

 

음... 다시 교수님께 여쭤보기 전에 생각해 보았죠. 경로 서버가 있다는 상황에서 이렇게 코드를 구성하려면 얼마나 걸릴까? 하고 그래도 한 두시간 이상 걸리는 일을 여기까지 좁혀 준것도 감사한 일이다 라는 생각을 드네요...

내가 고쳐도 되지만(??) 다시 여쭤 보았죠...

두 번이상 경로 탐색이 안되잖아요? 두번 이상 경로탐색이 되도록 해주세요~

 

그러자, 코드가 다음과 같이 내려 왔습니다. 스르륵__~~

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSRM Route Finder</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <style>
        #map {
            height: 600px;
        }
        #input {
            margin: 20px;
        }
    </style>
</head>
<body>
    <div>
        <label>출발지 좌표 (위도, 경도):</label>
        <input type="text" id="start-input" placeholder="예: 37.5665, 126.9780" readonly />
        <label>목적지 좌표 (위도, 경도):</label>
        <input type="text" id="end-input" placeholder="예: 37.5550, 126.9705" readonly />
        <button onclick="setRoute()">경로 탐색</button>
        <button onclick="resetRoute()">새로운 경로 설정</button>
    </div>
    <div id="map"></div>
    
    <script>
        const map = L.map('map').setView([37.5665, 126.9780], 13);  // 서울의 중심 좌표

        // OpenStreetMap 타일 레이어 추가
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        const osrmUrl = 'http://localhost:5000/route/v1/driving/'; // OSRM 백엔드 서버의 경로 API

        let start = null; // 출발지
        let end = null;   // 목적지

        // 사용자가 클릭한 좌표에 출발지나 목적지를 설정
        map.on('click', function(e) {
            if (!start) {
                // 첫 번째 클릭: 출발지 설정
                start = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('start-input').value = `${start.lat}, ${start.lng}`; // 입력 필드에 값 설정
                L.marker([start.lat, start.lng]).addTo(map).bindPopup("Start").openPopup();
                alert('출발지 설정 완료! 이제 목적지를 클릭하세요.');
            } else if (!end) {
                // 두 번째 클릭: 목적지 설정
                end = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('end-input').value = `${end.lat}, ${end.lng}`; // 입력 필드에 값 설정
                L.marker([end.lat, end.lng]).addTo(map).bindPopup("End").openPopup();
                alert('목적지 설정 완료! 경로를 계산합니다.');
                findRoute(start, end); // 경로 탐색
            }
        });

        // 경로 탐색 함수
        function findRoute(start, end) {
            const url = osrmUrl + `${start.lng},${start.lat};${end.lng},${end.lat}?overview=full&geometries=geojson`;

            fetch(url)
                .then(response => response.json())
                .then(data => {
                    const route = data.routes[0].geometry.coordinates;
                    const latlngs = route.map(coord => [coord[1], coord[0]]); // 경로 좌표 변환

                    // 경로를 지도에 표시
                    L.polyline(latlngs, { color: 'blue', weight: 4 }).addTo(map);
                })
                .catch(error => {
                    console.error('Error fetching route:', error);
                });
        }

        // 경로 탐색 버튼 클릭 시 호출될 함수
        function setRoute() {
            if (!start || !end) {
                alert("출발지와 목적지를 모두 설정해주세요.");
                return;
            }

            findRoute(start, end); // 경로 탐색
        }

        // 새로운 경로 설정 함수 (초기화)
        function resetRoute() {
            // 출발지와 목적지 초기화
            start = null;
            end = null;
            document.getElementById('start-input').value = '';
            document.getElementById('end-input').value = '';
            
            // 지도에서 마커를 삭제
            map.eachLayer(function(layer) {
                if (layer instanceof L.Marker) {
                    map.removeLayer(layer);
                }
            });

            alert('새로운 경로를 설정해주세요!');
        }
    </script>
</body>
</html>

 

탐색한 결과 입니다. 결과가 그닥 만족스럽지가 않네요.. 

 

그리고 프론트엔드 코드에 익숙하지 않는 저에게는 아직 코드도 익숙치가 않습니다.

그래서 이렇게 물어 보았죠

경로 탐색을 진행 할 때마다 기존 탐색 경로는 지워주시고,
목적지 표시만 움직여서 경로가 표시 되도록 만들어 주세요~

 

이번에 내려 온 코드 입니다. - map_route5.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSRM Route Finder</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <style>
        #map {
            height: 600px;
        }
        #input {
            margin: 20px;
        }
    </style>
</head>
<body>
    <div>
        <label>출발지 좌표 (위도, 경도):</label>
        <input type="text" id="start-input" placeholder="예: 37.5665, 126.9780" readonly />
        <label>목적지 좌표 (위도, 경도):</label>
        <input type="text" id="end-input" placeholder="예: 37.5550, 126.9705" readonly />
        <button onclick="setRoute()">경로 탐색</button>
        <button onclick="resetRoute()">새로운 경로 설정</button>
    </div>
    <div id="map"></div>
    
    <script>
        const map = L.map('map').setView([37.5665, 126.9780], 13);  // 서울의 중심 좌표

        // OpenStreetMap 타일 레이어 추가
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        const osrmUrl = 'http://localhost:5000/route/v1/driving/'; // OSRM 백엔드 서버의 경로 API

        let start = null; // 출발지
        let end = null;   // 목적지
        let routeLayer = null; // 경로 레이어
        let endMarker = null; // 목적지 마커

        // 사용자가 클릭한 좌표에 출발지나 목적지를 설정
        map.on('click', function(e) {
            if (!start) {
                // 첫 번째 클릭: 출발지 설정
                start = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('start-input').value = `${start.lat}, ${start.lng}`; // 입력 필드에 값 설정
                L.marker([start.lat, start.lng]).addTo(map).bindPopup("Start").openPopup();
                alert('출발지 설정 완료! 이제 목적지를 클릭하세요.');
            } else if (!end) {
                // 두 번째 클릭: 목적지 설정
                end = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('end-input').value = `${end.lat}, ${end.lng}`; // 입력 필드에 값 설정
                // 기존의 목적지 마커가 있으면 지우고 새로운 마커를 추가
                if (endMarker) {
                    endMarker.setLatLng([end.lat, end.lng]);
                } else {
                    endMarker = L.marker([end.lat, end.lng]).addTo(map).bindPopup("End").openPopup();
                }
                alert('목적지 설정 완료! 경로를 계산합니다.');
                findRoute(start, end); // 경로 탐색
            }
        });

        // 경로 탐색 함수
        function findRoute(start, end) {
            const url = osrmUrl + `${start.lng},${start.lat};${end.lng},${end.lat}?overview=full&geometries=geojson`;

            // 기존 경로가 있으면 삭제
            if (routeLayer) {
                map.removeLayer(routeLayer);
            }

            fetch(url)
                .then(response => response.json())
                .then(data => {
                    const route = data.routes[0].geometry.coordinates;
                    const latlngs = route.map(coord => [coord[1], coord[0]]); // 경로 좌표 변환

                    // 경로를 지도에 표시
                    routeLayer = L.polyline(latlngs, { color: 'blue', weight: 4 }).addTo(map);
                })
                .catch(error => {
                    console.error('Error fetching route:', error);
                });
        }

        // 경로 탐색 버튼 클릭 시 호출될 함수
        function setRoute() {
            if (!start || !end) {
                alert("출발지와 목적지를 모두 설정해주세요.");
                return;
            }

            findRoute(start, end); // 경로 탐색
        }

        // 새로운 경로 설정 함수 (초기화)
        function resetRoute() {
            // 출발지와 목적지 초기화
            start = null;
            end = null;
            document.getElementById('start-input').value = '';
            document.getElementById('end-input').value = '';
            
            // 지도에서 경로와 마커를 삭제
            if (routeLayer) {
                map.removeLayer(routeLayer);
                routeLayer = null;
            }
            if (endMarker) {
                map.removeLayer(endMarker);
                endMarker = null;
            }

            alert('새로운 경로를 설정해주세요!');
        }
    </script>
</body>
</html>

 

결과를 한 번 보도록 할까요?

 

목적지를 빼고 세개의 표시는 출발점입니다. 기존 경로는 지워졌구요....

뭔가 잘 못 물어봤을까요? 질문이 제대로 되지 못했다고 느껴야 할까요? 

다시 한번 질문을 해보도록 했습니다.

출발지를 움직이면 경로탐색을 하지 않습니다.
출발지를 움직일 때마다 기존 탐색 경로와 목적지를 지웁니다.
목적지를 움직일 때마다 경로 탐색을 하고 싶습니다.

 

두둥~ map_route6.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSRM Route Finder</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <style>
        #map {
            height: 600px;
        }
        #input {
            margin: 20px;
        }
    </style>
</head>
<body>
    <div>
        <label>출발지 좌표 (위도, 경도):</label>
        <input type="text" id="start-input" placeholder="예: 37.5665, 126.9780" readonly />
        <label>목적지 좌표 (위도, 경도):</label>
        <input type="text" id="end-input" placeholder="예: 37.5550, 126.9705" readonly />
        <button onclick="setRoute()">경로 탐색</button>
        <button onclick="resetRoute()">새로운 경로 설정</button>
    </div>
    <div id="map"></div>
    
    <script>
        const map = L.map('map').setView([37.5665, 126.9780], 13);  // 서울의 중심 좌표

        // OpenStreetMap 타일 레이어 추가
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        const osrmUrl = 'http://localhost:5000/route/v1/driving/'; // OSRM 백엔드 서버의 경로 API

        let start = null; // 출발지
        let end = null;   // 목적지
        let routeLayer = null; // 경로 레이어
        let startMarker = null; // 출발지 마커
        let endMarker = null; // 목적지 마커

        // 사용자가 클릭한 좌표에 출발지나 목적지를 설정
        map.on('click', function(e) {
            if (!start) {
                // 첫 번째 클릭: 출발지 설정
                start = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('start-input').value = `${start.lat}, ${start.lng}`; // 입력 필드에 값 설정
                startMarker = L.marker([start.lat, start.lng], { draggable: true }).addTo(map).bindPopup("Start").openPopup();
                startMarker.on('dragend', onStartMarkerDrag); // 출발지 마커가 드래그되었을 때 처리
                alert('출발지 설정 완료! 이제 목적지를 클릭하세요.');
            } else if (!end) {
                // 두 번째 클릭: 목적지 설정
                end = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('end-input').value = `${end.lat}, ${end.lng}`; // 입력 필드에 값 설정
                endMarker = L.marker([end.lat, end.lng], { draggable: true }).addTo(map).bindPopup("End").openPopup();
                endMarker.on('dragend', onEndMarkerDrag); // 목적지 마커가 드래그되었을 때 처리
                alert('목적지 설정 완료! 경로를 계산합니다.');
                findRoute(start, end); // 경로 탐색
            }
        });

        // 출발지 마커가 드래그될 때 호출되는 함수
        function onStartMarkerDrag() {
            // 출발지 마커가 이동되면 경로와 목적지 마커를 모두 지우고 새로 계산하지 않음
            start = { lat: startMarker.getLatLng().lat, lng: startMarker.getLatLng().lng };
            document.getElementById('start-input').value = `${start.lat}, ${start.lng}`;
            resetRoute(); // 경로를 초기화
        }

        // 목적지 마커가 드래그될 때 호출되는 함수
        function onEndMarkerDrag() {
            // 목적지 마커가 이동되면 경로를 다시 계산
            end = { lat: endMarker.getLatLng().lat, lng: endMarker.getLatLng().lng };
            document.getElementById('end-input').value = `${end.lat}, ${end.lng}`;
            findRoute(start, end); // 경로 탐색
        }

        // 경로 탐색 함수
        function findRoute(start, end) {
            const url = osrmUrl + `${start.lng},${start.lat};${end.lng},${end.lat}?overview=full&geometries=geojson`;

            // 기존 경로가 있으면 삭제
            if (routeLayer) {
                map.removeLayer(routeLayer);
            }

            fetch(url)
                .then(response => response.json())
                .then(data => {
                    const route = data.routes[0].geometry.coordinates;
                    const latlngs = route.map(coord => [coord[1], coord[0]]); // 경로 좌표 변환

                    // 경로를 지도에 표시
                    routeLayer = L.polyline(latlngs, { color: 'blue', weight: 4 }).addTo(map);
                })
                .catch(error => {
                    console.error('Error fetching route:', error);
                });
        }

        // 경로 탐색 버튼 클릭 시 호출될 함수
        function setRoute() {
            if (!start || !end) {
                alert("출발지와 목적지를 모두 설정해주세요.");
                return;
            }

            findRoute(start, end); // 경로 탐색
        }

        // 새로운 경로 설정 함수 (초기화)
        function resetRoute() {
            // 출발지와 목적지 초기화
            start = null;
            end = null;
            document.getElementById('start-input').value = '';
            document.getElementById('end-input').value = '';
            
            // 지도에서 경로와 마커를 삭제
            if (routeLayer) {
                map.removeLayer(routeLayer);
                routeLayer = null;
            }
            if (endMarker) {
                map.removeLayer(endMarker);
                endMarker = null;
            }

            alert('새로운 경로를 설정해주세요!');
        }
    </script>
</body>
</html>

 

자 결과는?

 

목적지를 움직일 때마다 경로는 탐색되었으나, 출발지를 움직이면 목적지와 경로만 지워주네요.

출발지가 이미 있는 경우면 그냥 탐색 해야 하는데...

출발지를 움직이면 경로탐색을 하지 않습니다.
출발지를 움직일 때마다 기존 탐색 경로와 목적지를 지웁니다.
출발지 한 번 그려지면 새로운 출발지 그리지 않습니다.
목적지를 움직일 때마다 경로 탐색을 하고 싶습니다.

답변이 내가 원한 것이 아니네요

출발지를 움직이면 경로탐색을 하지 않습니다.
출발지를 움직일 때마다 기존 탐색 경로와 목적지를 지웁니다.
출발지 아이콘은 한번만 그립니다.
목적지를 움직일 때마다 경로 탐색을 하고 싶습니다.
출발지 아이콘이 있을 때, 목적지는 여러개가 될 수 있습니다.

 

위에 질문은 어떨까요? 답변이 좀 안 맞는 것 같네요

 

그래서 질문을 수정 했습니다. 이렇게요...

출발지 아이콘은 한번만 그리고 이동이 가능합니다.
목적지 아이콘은 한개 또는 여러개 일 수 있습니다.
출발지를 움직이면 경로탐색을 하지 않습니다.
출발지를 움직일 때마다 기존 탐색 경로와 목적지아이콘은 지웁니다.
출발지 아이콘이 그려져 있을 때, 클릭하면 목적지 아이콘을 그립니다.
하나의 목적지 아이콘을 드래그 할 때마다 경로 탐색을 하고 싶습니다.
목적지는 여러개가 될 수 있습니다.
경로 초기화로 지도 상의 모든 아이콘과 경로가 초기화가 됩니다.

 

답변의 요구 사항이 약간 그럴싸 한 듯 합니다.

 

코드는 ? 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSRM Route Finder</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <style>
        #map {
            height: 600px;
        }
        #input {
            margin: 20px;
        }
    </style>
</head>
<body>
    <div>
        <label>출발지 좌표 (위도, 경도):</label>
        <input type="text" id="start-input" placeholder="예: 37.5665, 126.9780" readonly />
        <label>목적지 좌표 (위도, 경도):</label>
        <input type="text" id="end-input" placeholder="예: 37.5550, 126.9705" readonly />
        <button onclick="resetRoute()">경로 초기화</button>
    </div>
    <div id="map"></div>
    
    <script>
        const map = L.map('map').setView([37.5665, 126.9780], 13);  // 서울의 중심 좌표

        // OpenStreetMap 타일 레이어 추가
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        const osrmUrl = 'http://localhost:5000/route/v1/driving/'; // OSRM 백엔드 서버의 경로 API

        let start = null; // 출발지
        let endMarkers = []; // 목적지 마커들
        let routeLayer = null; // 경로 레이어
        let startMarker = null; // 출발지 마커

        // 사용자가 클릭한 좌표에 출발지나 목적지를 설정
        map.on('click', function(e) {
            if (!start) {
                // 첫 번째 클릭: 출발지 설정
                start = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('start-input').value = `${start.lat}, ${start.lng}`; // 입력 필드에 값 설정
                startMarker = L.marker([start.lat, start.lng], { draggable: true }).addTo(map).bindPopup("Start").openPopup();
                startMarker.on('dragend', onStartMarkerDrag); // 출발지 드래그 이벤트
                alert('출발지 설정 완료! 이제 목적지를 클릭하세요.');
            } else {
                // 두 번째 클릭: 목적지 추가
                const end = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('end-input').value = `${end.lat}, ${end.lng}`; // 입력 필드에 값 설정
                const endMarker = L.marker([end.lat, end.lng], { draggable: true }).addTo(map).bindPopup("End").openPopup();
                endMarker.on('dragend', function() { onEndMarkerDrag(endMarker); }); // 목적지 드래그 이벤트
                endMarkers.push(endMarker); // 목적지 마커 배열에 추가
                alert('목적지 추가 완료! 경로를 계산합니다.');
                findRoute(start, endMarkers); // 경로 탐색
            }
        });

        // 출발지 마커가 드래그되었을 때 호출되는 함수
        function onStartMarkerDrag() {
            // 출발지 이동 시 기존 경로와 목적지 지우기
            if (routeLayer) {
                map.removeLayer(routeLayer);
            }
            endMarkers.forEach(marker => map.removeLayer(marker)); // 기존 목적지 마커 제거
            endMarkers = []; // 목적지 마커 배열 초기화
        }

        // 목적지 마커가 드래그될 때 호출되는 함수
        function onEndMarkerDrag(marker) {
            // 마커 이동 후 경로 재계산
            const newEnd = { lat: marker.getLatLng().lat, lng: marker.getLatLng().lng };
            document.getElementById('end-input').value = `${newEnd.lat}, ${newEnd.lng}`;
            findRoute(start, endMarkers); // 경로 재계산
        }

        // 경로 탐색 함수
        function findRoute(start, endMarkers) {
            // 기존 경로가 있으면 삭제
            if (routeLayer) {
                map.removeLayer(routeLayer);
            }

            // 여러 목적지에 대해 경로 탐색
            const coordinates = endMarkers.map(marker => [marker.getLatLng().lng, marker.getLatLng().lat]);
            const url = osrmUrl + `${start.lng},${start.lat};` + coordinates.map(coord => `${coord[0]},${coord[1]}`).join(';') + `?overview=full&geometries=geojson`;

            fetch(url)
                .then(response => response.json())
                .then(data => {
                    const route = data.routes[0].geometry.coordinates;
                    const latlngs = route.map(coord => [coord[1], coord[0]]); // 경로 좌표 변환

                    // 경로를 지도에 표시
                    routeLayer = L.polyline(latlngs, { color: 'blue', weight: 4 }).addTo(map);
                })
                .catch(error => {
                    console.error('Error fetching route:', error);
                });
        }

        // 경로 초기화 함수 (모든 마커와 경로 삭제)
        function resetRoute() {
            // 출발지와 목적지 초기화
            start = null;
            endMarkers = [];
            document.getElementById('start-input').value = '';
            document.getElementById('end-input').value = '';
            
            // 지도에서 경로와 마커를 삭제
            if (routeLayer) {
                map.removeLayer(routeLayer);
                routeLayer = null;
            }
            if (startMarker) {
                map.removeLayer(startMarker);
                startMarker = null;
            }
            endMarkers.forEach(marker => {
                map.removeLayer(marker);
            });
            endMarkers = [];

            alert('모든 경로와 마커가 초기화되었습니다.');
        }
    </script>
</body>
</html>

 

결과는 좋았을까요?

 

 

QA 가 한 번 되어도 될 것 같아서 이렇게 물어 봤죠

다음이 오류입니다 고쳐주세요
경로 초기화 인데 아이콘이 지워지지 않는 경우 발생,
시작 아이콘을 움직이면 그 위치가 출발지가 되지 않음.
목적지 아이콘을 움직여도 새로운 목적지 아이콘이 계속 생김.
목적지 아이콘이 드래그 되지 않음.

 

코드 입니다.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>OSRM Route Finder</title>
    <link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css" />
    <script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <style>
        #map {
            height: 600px;
        }
        #input {
            margin: 20px;
        }
    </style>
</head>
<body>
    <div>
        <label>출발지 좌표 (위도, 경도):</label>
        <input type="text" id="start-input" placeholder="예: 37.5665, 126.9780" readonly />
        <label>목적지 좌표 (위도, 경도):</label>
        <input type="text" id="end-input" placeholder="예: 37.5550, 126.9705" readonly />
        <button onclick="resetRoute()">경로 초기화</button>
    </div>
    <div id="map"></div>
    
    <script>
        const map = L.map('map').setView([37.5665, 126.9780], 13);  // 서울의 중심 좌표

        // OpenStreetMap 타일 레이어 추가
        L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
            attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
        }).addTo(map);

        const osrmUrl = 'http://localhost:5000/route/v1/driving/'; // OSRM 백엔드 서버의 경로 API

        let start = null; // 출발지
        let endMarkers = []; // 목적지 마커들
        let routeLayer = null; // 경로 레이어
        let startMarker = null; // 출발지 마커

        // 사용자가 클릭한 좌표에 출발지나 목적지를 설정
        map.on('click', function(e) {
            if (!start) {
                // 첫 번째 클릭: 출발지 설정
                start = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('start-input').value = `${start.lat}, ${start.lng}`; // 입력 필드에 값 설정
                startMarker = L.marker([start.lat, start.lng], { draggable: true }).addTo(map).bindPopup("Start").openPopup();
                startMarker.on('dragend', onStartMarkerDrag); // 출발지 드래그 이벤트
                alert('출발지 설정 완료! 이제 목적지를 클릭하세요.');
            } else {
                // 두 번째 클릭: 목적지 추가
                const end = { lat: e.latlng.lat, lng: e.latlng.lng };
                document.getElementById('end-input').value = `${end.lat}, ${end.lng}`; // 입력 필드에 값 설정
                const endMarker = L.marker([end.lat, end.lng], { draggable: true }).addTo(map).bindPopup("End").openPopup();
                endMarker.on('dragend', function() { onEndMarkerDrag(endMarker); }); // 목적지 드래그 이벤트
                endMarkers.push(endMarker); // 목적지 마커 배열에 추가
                alert('목적지 추가 완료! 경로를 계산합니다.');
                findRoute(start, endMarkers); // 경로 탐색
            }
        });

        // 출발지 마커가 드래그되었을 때 호출되는 함수
        function onStartMarkerDrag() {
            // 출발지 이동 시 기존 경로와 목적지 지우기
            if (routeLayer) {
                map.removeLayer(routeLayer);
            }
            endMarkers.forEach(marker => map.removeLayer(marker)); // 기존 목적지 마커 제거
            endMarkers = []; // 목적지 마커 배열 초기화

            // 새로운 출발지 위치로 경로 계산
            start = { lat: startMarker.getLatLng().lat, lng: startMarker.getLatLng().lng };
            document.getElementById('start-input').value = `${start.lat}, ${start.lng}`;
        }

        // 목적지 마커가 드래그될 때 호출되는 함수
        function onEndMarkerDrag(marker) {
            // 마커 이동 후 경로 재계산
            const newEnd = { lat: marker.getLatLng().lat, lng: marker.getLatLng().lng };
            document.getElementById('end-input').value = `${newEnd.lat}, ${newEnd.lng}`;
            findRoute(start, endMarkers); // 경로 재계산
        }

        // 경로 탐색 함수
        function findRoute(start, endMarkers) {
            // 기존 경로가 있으면 삭제
            if (routeLayer) {
                map.removeLayer(routeLayer);
            }

            // 여러 목적지에 대해 경로 탐색
            const coordinates = endMarkers.map(marker => [marker.getLatLng().lng, marker.getLatLng().lat]);
            const url = osrmUrl + `${start.lng},${start.lat};` + coordinates.map(coord => `${coord[0]},${coord[1]}`).join(';') + `?overview=full&geometries=geojson`;

            fetch(url)
                .then(response => response.json())
                .then(data => {
                    const route = data.routes[0].geometry.coordinates;
                    const latlngs = route.map(coord => [coord[1], coord[0]]); // 경로 좌표 변환

                    // 경로를 지도에 표시
                    routeLayer = L.polyline(latlngs, { color: 'blue', weight: 4 }).addTo(map);
                })
                .catch(error => {
                    console.error('Error fetching route:', error);
                });
        }

        // 경로 초기화 함수 (모든 마커와 경로 삭제)
        function resetRoute() {
            // 출발지와 목적지 초기화
            start = null;
            endMarkers = [];
            document.getElementById('start-input').value = '';
            document.getElementById('end-input').value = '';
            
            // 지도에서 경로와 마커를 삭제
            if (routeLayer) {
                map.removeLayer(routeLayer);
                routeLayer = null;
            }
            if (startMarker) {
                map.removeLayer(startMarker);
                startMarker = null;
            }
            endMarkers.forEach(marker => {
                map.removeLayer(marker);
            });
            endMarkers = [];

            alert('모든 경로와 마커가 초기화되었습니다.');
        }
    </script>
</body>
</html>

결과는?

 

 

마치면서...

이렇게 하면,

 

로직이 구성 되면 코드를 최단시간 작성 할 수 있을 것 같습니다.

단, 원하는 로직이 나올 때까지 무한 반복 할 수도 있겠다라는 생각도 드네요.

그런다음 결과물로 QA 가 되면 될 것 같습니다.

마지막으로 지금은 어느 순간에 가서는 약간의 터치(?)가 필요 할 듯 보이네요...

728x90