본문 바로가기
Project/내비게이션

[OSM]OpenStreetMap을 이용한 내비게이션 만들기 - 4

by 알파쿼카 2024. 9. 20.

저번 포스팅에서 이어서 이번에는 현재 위치를 실시간으로 표현해보자


 현재 위치 실시간으로 표시하기 

        //================================================= 현위치 실시간 업데이트
        function current_update() {
            $.ajax({
                url: '/h/get/current_point.php',
                method: 'GET',
                dataType: 'json',
                success: function(data) {

                    current_value = data;

                    // 문자열을 배열로 변환 후 숫자로 변환
                    current_point = current_value.split(',').map(Number);

                    current_lat = current_point[0];
                    current_lng = current_point[1];

                    // 기존 마커 제거
                    if (currentMarker) {
                        map.removeLayer(currentMarker);
                    }

                    currentMarker = L.marker([current_lat, current_lng], {
                            icon: currentIcon
                        })
                        .addTo(map)
                        .bindPopup("현재 위치");
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    console.error('오류:', textStatus, errorThrown);
                }
            });
        }

 

 

너무 뒤죽박죽인 것 같아서...

코드 가독성을 위해 싹 정리했다.

 

추가)

1) 현위치 실시간 마커로 받아오기

2) 출발지와 도착지 바꾸기

3) 현위치 버튼 누르면 지도 뷰 다시 조정하고 출발지도 다시 설정하기

전체 코드 

[index.html]

<?php
include($_SERVER['DOCUMENT_ROOT'] . "/h/db/db.php");

$user_id = $_SESSION['id'];
$sql = "SELECT * FROM `route` WHERE `id` = " . $user_id . "";
$user_id;
$res = mysqli_query($conn, $sql);
$row = mysqli_fetch_array($res);

($row['current_point'] != null) ? $current_point = $row['current_point'] : $current_point = '1';
?>
<!DOCTYPE html>
<html lang="ko">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>e-navi</title>
    <link rel="stylesheet" href="/h/css/index.css?css=3">
    <link rel="stylesheet" href="https://unpkg.com/leaflet/dist/leaflet.css" />
    <link rel="stylesheet" href="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.css" />
</head>

<body>
    <div id="popup">
        <div class="search_left">
            <button class="btn btn_change" onclick="position_change()">
                <div>
                    <span>▲</span>
                    <span>▼</span>
                </div>
            </button>
        </div>
        <div class="search">
            <input type="text" id="current" class="current" value='<?php echo $current_point; ?>'>
            <input type="text" id="start" class="start" placeholder="출발지 입력">
            <input type="text" id="end" class="end" placeholder="도착지 입력">
        </div>
        <div class="search_right">
            <button class="btn btn_current" onclick="set_current()">현위치</button>
            <button class="btn btn_search" onclick="get_route()">찾기</button>
        </div>
    </div>
    <div id="map"></div>
    <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
    <script src="https://unpkg.com/leaflet-routing-machine/dist/leaflet-routing-machine.js"></script>
    <script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
    <script src="/h/plugins/jquery/jquery.min.js"></script>
    <script>
        //================================================= 변수 정의
        var current_value; // 위도, 경도 (문자열)
        var current_point; // [위도, 경도] (배열로 변환)
        var current_lat; // 위도
        var current_lng; // 경도
        var map; // 맵
        var currentIcon; // 현재 아이콘 설정
        var currentMarker; // 현재 위치 마커 띄우기
        var startMarker; // 출발지 마커 띄우기
        var endMarker; // 도착지 마커 띄우기
        var control; // 경로 탐색 기능

        //================================================= 초기 설정
        $(document).ready(function() {
            $.ajax({
                url: '/h/get/current_point.php',
                method: 'GET',
                dataType: 'json',
                success: function(data) {
                    //----------------------------------------------- 초기 위치 1회 가져오기
                    current_value = data;

                    // 문자열을 배열로 변환 후 숫자로 변환
                    current_point = current_value.split(',').map(Number);

                    current_lat = current_point[0];
                    current_lng = current_point[1];

                    //----------------------------------------------- 지도 생성 # 코드 위치 옮기지 말 것
                    // 지도의 맨 처음 위치를 현위치로 설정
                    map = L.map('map').setView(current_point, 16);

                    // OpenStreetMap 지도 타일 레이어 추가
                    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
                        maxZoom: 19,
                    }).addTo(map);

                    //----------------------------------------------- 역지오코딩한 현재 위치 출발지에 넣기 (1회)
                    nearbuilding(current_lat, current_lng);

                    //----------------------------------------------- 현재 위치 마커 찍기 (1회)
                    currentIcon = L.icon({
                        iconUrl: '/h/img/current.png',
                        iconSize: [15, 15],
                        iconAnchor: [7.5, 7.5],
                    });
                    currentMarker = L.marker([current_lat, current_lng], {
                            icon: currentIcon
                        })
                        .addTo(map)
                        .bindPopup("현재 위치");

                    //----------------------------------------------- 경로 탐색 기능 불러오기
                    control = L.Routing.control({
                        waypoints: [],
                        routeWhileDragging: true,
                        geocoder: L.Control.Geocoder.nominatim(),
                        createMarker: function() {
                            return null;
                        } // 기본 마커 비활성화
                    }).addTo(map);

                    //----------------------------------------------- 길찾기 팝업 숨기기
                    control.getContainer().style.display = 'none';

                    //----------------------------------------------- 확대/축소 컨트롤 숨기기
                    var zoomControl = map.zoomControl;
                    if (zoomControl) {
                        zoomControl.remove();
                    }

                    //현위치 업데이트
                    setInterval(current_update, 1000);
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    console.error('오류:', textStatus, errorThrown);
                }
            });
        });


        /* function 모음 */
        //================================================= 현위치 실시간 업데이트
        function current_update() {
            $.ajax({
                url: '/h/get/current_point.php',
                method: 'GET',
                dataType: 'json',
                success: function(data) {

                    current_value = data;

                    // 문자열을 배열로 변환 후 숫자로 변환
                    current_point = current_value.split(',').map(Number);

                    current_lat = current_point[0];
                    current_lng = current_point[1];

                    // 기존 마커 제거
                    if (currentMarker) {
                        map.removeLayer(currentMarker);
                    }

                    currentMarker = L.marker([current_lat, current_lng], {
                            icon: currentIcon
                        })
                        .addTo(map)
                        .bindPopup("현재 위치");
                },
                error: function(jqXHR, textStatus, errorThrown) {
                    console.error('오류:', textStatus, errorThrown);
                }
            });
        }

        //================================================= 역지오코딩으로 가까운 건물 찾기
        function nearbuilding(lat, lng) {
            var url = `https://nominatim.openstreetmap.org/reverse?lat=${lat}&lon=${lng}&format=json`;

            fetch(url)
                .then(response => {
                    if (!response.ok) {
                        throw new Error('Network response was not ok ' + response.statusText);
                    }
                    return response.json();
                })
                .then(data => {
                    if (data && data.display_name) {
                        var city = data.address.city;
                        var borough = data.address.borough;
                        var road = data.address.road;
                        var address = city + ' ' + borough + ' ' + road

                        document.getElementById('start').value = address; // 출발지 입력란에 설정
                    } else {
                        alert('가장 가까운 위치를 찾을 수 없습니다.');
                    }
                })
                .catch(error => {
                    console.error('Error:', error);
                    alert('위치를 찾는 중 오류가 발생했습니다.');
                });
        }

        //================================================= 경로 찾기 함수
        function get_route() {
            var start = document.getElementById('start').value;
            var end = document.getElementById('end').value;

            if (start && end) {
                L.Control.Geocoder.nominatim().geocode(start, function(results) {
                    if (results.length > 0) {
                        var startLatLng = results[0].center;

                        L.Control.Geocoder.nominatim().geocode(end, function(results) {
                            if (results.length > 0) {
                                var endLatLng = results[0].center;

                                // 출발지와 도착지 설정
                                control.setWaypoints([
                                    L.latLng(startLatLng.lat, startLatLng.lng),
                                    L.latLng(endLatLng.lat, endLatLng.lng)
                                ]);

                                // 기존 마커 제거
                                if (startMarker) {
                                    map.removeLayer(startMarker);
                                }
                                if (endMarker) {
                                    map.removeLayer(endMarker);
                                }

                                // 출발지 마커 추가
                                var startIcon = L.icon({
                                    iconUrl: '/h/img/start.png', // start.png의 경로
                                    iconSize: [30, 41], // 아이콘 크기
                                    iconAnchor: [15, 41], // 아이콘 기준점
                                });
                                startMarker = L.marker(startLatLng, {
                                        icon: startIcon
                                    })
                                    .addTo(map)
                                    .bindPopup("출발지: " + start);

                                // 도착지 마커 추가
                                var endIcon = L.icon({
                                    iconUrl: '/h/img/end.png', // end.png의 경로
                                    iconSize: [30, 41], // 아이콘 크기
                                    iconAnchor: [15, 41], // 아이콘 기준점
                                });
                                endMarker = L.marker(endLatLng, {
                                    icon: endIcon
                                }).addTo(map).bindPopup("도착지: " + end);

                                // 경로 정보 요청
                                // 이벤트 리스너가 중복 추가되지 않도록 설정
                                control.once('routesfound', function(e) {
                                    var routes = e.routes;

                                    // 경로 정보를 가져온 후 지도 뷰를 설정
                                    var firstRoute = routes[0];
                                    var waypoints = firstRoute.coordinates;
                                    var bounds = L.latLngBounds(waypoints); // 모든 경로의 경계 계산
                                    map.fitBounds(bounds); // 경로에 맞게 지도를 조정

                                    var all = waypoints.map(coord => `"${coord.lat}, ${coord.lng}"`).join(',');
                                    var distance = firstRoute.summary.totalDistance;
                                    var routeTime = firstRoute.summary.totalTime;

                                    var index = firstRoute.instructions.map(instruction => instruction.index);
                                    var locations = index.map(index => {
                                        var coord = waypoints[index];
                                        return coord ? `${coord.lat}, ${coord.lng}` : null;
                                    }).filter(coord => coord !== null);

                                    // AJAX 요청으로 데이터베이스 업데이트
                                    update_db(routes, start, end, locations, distance, routeTime, index, all);
                                });
                            } else {
                                alert('도착지를 찾을 수 없습니다.');
                            }
                        });
                    } else {
                        alert('출발지를 찾을 수 없습니다.');
                    }
                });
            } else {
                alert('출발지와 도착지를 모두 입력하세요.');
            }
        }

        //================================================= 데이터베이스 업데이트 함수
        function update_db(routes, start, end, locations, distance, routeTime, index, all) {
            var xhr = new XMLHttpRequest();
            xhr.open("POST", "update_route.php", true);
            xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4) {
                    if (xhr.status == 200) {
                        // console.log('데이터베이스 업데이트 성공:', xhr.responseText);
                    } else {
                        console.error('업데이트 실패:', xhr.status, xhr.statusText);
                    }
                }
            };
            xhr.send(JSON.stringify({
                start_point: start,
                end_point: end,
                locations: JSON.stringify(locations).slice(1, -1),
                distance: distance,
                route_time: routeTime,
                all: all
            }));

            // console.log(routes);
            // console.log(index);
            // console.log(all);
            // console.log(JSON.stringify(locations).slice(1, -1));
        }

        //================================================= 출발지와 도착지 서로 변경
        function position_change() {
            // 값 잃어버릴 수 있으니 저장함
            var startValue = $('#start').val();
            var endValue = $('#end').val();
            
            $('#start').val(endValue);
            $('#end').val(startValue);
        }
        
        //================================================= 출발지 현위치로 설정
        function set_current() {
            nearbuilding(current_lat, current_lng);

            //지도 뷰를 현위치로 돌려놓기!!!!!!!!!!!!!!
            map.setView([current_lat, current_lng], 16);
        }
    </script>
</body>

</html>

 

[current_point.php] (현 위치 위도,경도 반환)

<?php
include($_SERVER['DOCUMENT_ROOT'] . "/h/db/db.php");

$user_id = $_SESSION['id'];

$sql = "SELECT current_point FROM `route` WHERE `id` = $user_id";
$res = mysqli_query($conn, $sql);
$row = mysqli_fetch_array($res);

($row['current_point'] != null) ? $current_point = $row['current_point'] : $current_point = '1';
echo json_encode($current_point);
?>

 

[update_route.php] (DB에 검색된 경로 저장)

<?php
include($_SERVER['DOCUMENT_ROOT'] . "/h/db/db.php");

// JSON 형식의 POST 데이터 읽기
$data = json_decode(file_get_contents("php://input"), true);

// 로그 파일에 데이터 기록
file_put_contents('log.txt', print_r($data, true), FILE_APPEND);

// POST 데이터 받기
$user_id = $_SESSION['id'];
$current_point = $data['current_point'];
$start_point = $data['start_point'];
$end_point = $data['end_point'];
$locations = $data['locations'];
$distance = $data['distance'];
$route_time = (int)$data['route_time'];
$all = $data['all'];

// 먼저 ID가 1인 레코드가 존재하는지 확인
$sql_check = "SELECT * FROM `route` WHERE `id` = '" . $user_id . "'";
$result = $conn->query($sql_check);

if ($result->num_rows > 0) {
    $sql_up = "UPDATE `route` set
            `user` = '" . $user_id . "',
            `start_point` = '" . $start_point . "',
            `end_point` = '" . $end_point . "',
            `locations` = '" . $locations . "',
            `distance` = '" . $distance . "',
            `route_time` = '" . $route_time . "',
            `all` = '" . $all . "'
            WHERE id= '" . $user_id . "'
            ";
    $res_up = mysqli_query($conn, $sql_up);

    if ($res_up) {
        echo "업데이트 성공";
    } else {
        echo "업데이트 실패: " . mysqli_error($conn);
    }
} else {
    // ID가 1인 레코드가 없을 경우 새 레코드 생성
    $sql_in = "INSERT INTO `route`
    (
    `user`,
    `start_point`,
    `end_point`,
    `locations`,
    `distance`,
    `route_time`,
    `all`
    )
    VALUES 
    (
    '" . $user_id . "',
    '" . $start_point . "',
    '" . $end_point . "',
    '" . $locations . "',
    '" . $distance . "',
    '" . $route_time . "',
    '" . $all . "'
        )
    ';
    ";
    $res_in = mysqli_query($conn, $sql_in);

    if ($res_in) {
        echo "생성 성공";
    } else {
        echo "생성 실패: " . mysqli_error($conn);
    }
}

$conn->close();

 

제대로 따라오셨다면 폴더구조는 다음과 같다.

 

[결과 화면]

 

1) 현위치 실시간 마커로 받아오기

2) 출발지와 도착지 바꾸기

3) 현위치 버튼 누르면 지도 뷰 다시 조정하고 출발지도 다시 설정하기

 

세가지 기능 모두 잘 작동한다 :)