Map(WEB)

vworld 지도 만들기(12) - 주소에 대한 좌표 구하기(2)

별동산 2025. 2. 17. 08:41
반응형

1. 구현 영상

왼쪽 위에 주소를 입력할 수 있는 칸이 있어서 도로명 주소 또는 지번 주소를 입력하고 검색 버튼 또는 엔터 키를 누르면 해당 위치로 이동한 후 해당 지점에 대한 좌표를 표시합니다.

도로명 또는 지번 주소에 대한 좌표 구하기

 

2. 전체 코드

<html>
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no, shrink-to-fit=no">
	<title>주소에 대한 좌표 구하기</title>
	
	<!-- 헤드 태그 안에 style 지정 -->
	<style type="text/css">
		#wrap .buttons { position:absolute;top:20;left:20;z-index:1000;padding:5px; }
		#wrap .buttons .control-btn { margin:0 5px 5px 0; }
	</style>
	<script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
	<script  type="text/javascript">
		
$(document).ready(function () {
	let pop = null ;
    // 검색 실행 함수
    function get_lat_lng(type = "road") {
		if (pop == null) {
			pop = new vw.ol3.popup.Popup(); 
		}
        const address = $('#addressInput').val().trim(); // inputbox내 입력 값 가져오기

        if (address === "") {
            alert("주소를 입력하세요.");
            return;
        }

        $.ajax({
            url: "https://api.vworld.kr/req/address?",
            type: "GET",
            dataType: "jsonp",
            data: {
                service: "address",
                request: "GetCoord",
                version: "2.0",
                crs: "EPSG:4326",
                type: type,
                address: address,
                format: "json",
                errorformat: "json",
                key: "CEB52025-E065-364C-9DBA-44880E3B02B8"
            },
            success: function (result) {
				if (result.response && result.response.result && result.response.result.point) {
					console.log(result);

					const arr = [result.response.result.point.x, result.response.result.point.y];
					console.log(arr);
					const coordinate = arr.map(Number);
					console.log(coordinate);

					// 좌표 변환 (EPSG:4326 -> EPSG:3857)
					const transformedCoord = ol.proj.transform(coordinate, "EPSG:4326", "EPSG:3857");

					// 지도 중심 이동
					vmap.getView().setCenter(transformedCoord);

					// 0.5초 후 pop 표시(비동기 처리하여 충돌 방지)
					setTimeout(() => {
						pop.title = "<b>주소에 대한 좌표</b><br><br>";
						const content = address + " 의 좌표는 <br><br>" + result.response.result.point.x + ',<br>' + result.response.result.point.y + " 입니다.";
						vmap.addOverlay(pop);
						pop.show(content, transformedCoord);
					}, 500);
				} else if (type === "road") {
					// road 검색 결과가 없으면 parcel로 재요청
					console.log("검색 결과 없음. 'parcel' 타입으로 재검색합니다.");
					get_lat_lng("parcel");
				} else {
					console.log("검색 결과를 찾을 수 없습니다.");
				}
			},
			error: function () {
				console.log("API 요청 실패");
			}
        });
    }

    // 버튼 클릭 시 검색 실행
    $('#searchBtn').click(function () {
        get_lat_lng();
    });

    // Enter 키 입력 시 검색 실행
    $('#addressInput').keydown(function (event) {
        if (event.key === "Enter") {
            event.preventDefault(); // 폼이 제출되지 않도록 방지
            get_lat_lng();
        }
    });
});
	</script> 
</head>

<body>

	<!-- div 태그의 순서는 영향 없음 -->
	<div id="vmap">
	<div id="wrap">
	
	<script type="text/javascript" src="https://map.vworld.kr/js/vworldMapInit.js.do?version=2.0&apiKey=CEB52025-E065-364C-9DBA-44880E3B02B8&domain=localhost:8080"></script>
	
	<div class="buttons">
	<input type="text" size="30" id="addressInput" value="">
	<button id="searchBtn" type="button" >검색</button>
	</div>
	
	<script type="text/javascript">		
		vw.ol3.MapOptions = {
			basemapType: vw.ol3.BasemapType.GRAPHIC
			, controlDensity: vw.ol3.DensityType.EMPTY
			, interactionDensity: vw.ol3.DensityType.BASIC
			, controlsAutoArrange: true
			, homePosition: vw.ol3.CameraPosition
			, initPosition: vw.ol3.CameraPosition
		};

		let vmap = new vw.ol3.Map("vmap",  vw.ol3.MapOptions); 		 

		vmap.getView().setCenter([14129132.729661161, 4512073.703926638]);
		vmap.getView().setZoom(18);

	</script>
	</div>
	</div>
</body>
</html>

 

 

3. 코드 살펴보기

가. JQuery 선언 및 사용

    <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script>
    <script  type="text/javascript">

$(document).ready(function () {

 

head 태그 안에서 

https://code.jquery.com/jquery-3.6.0.min.js">를 이용해 jquery를 참고하도록 하고,

$로 시작하는 각종 구문을 사용합니다.

 

그 중 첫번째로 나오는

$(document).ready(function () {

    ...

});

은 'html 문서가 준비되면, 다시 말해 문서 로딩이 끝나면 안에 있는 구문을 실행하라는 의미입니다.

 

나. type(검색 주소 유형)을 '도로명 주소'로 지정

function get_lat_lng(type = "road") {

주소 형태의 기본값을 도로명 주소로 지정했습니다. 

 

다. 도로명 주소로 검색 후 에러 시 지번 주소로 검색

success: function (result) {
    if (result.response && result.response.result && result.response.result.point) {
        ...
    } else if (type === "road") {
        // road 검색 결과가 없으면 parcel로 재요청
        console.log("검색 결과 없음. 'parcel' 타입으로 재검색합니다.");
        get_lat_lng("parcel");
    } else {
        console.log("검색 결과를 찾을 수 없습니다.");
    }

 

result.response와 result.response.result 가 있고, result.response.result.point가 있을 경우만 도로명 주소에 대한 좌표가 있는 것이므로 그때는 그대로 좌표값을 표시하면 되는데,

 

그렇지 않고 type이 'road(도로명 주소)'라면

console 창(F12키 클릭 또는 마우스 오른쪽 버튼 클릭 후 검사 메뉴 클릭)에

"검색 결과 없음. 'parcel' 타입으로 재검색합니다."라고 표시하고,

'parcel(지번 주소)' type으로 검색을 실시합니다.

 

라. 좌표의 작은따옴표 제거

const arr = [result.response.result.point.x, result.response.result.point.y];
console.log(arr);
const coordinate = arr.map(Number);
console.log(coordinate);

 

[result.response.result.point.x, result.response.result.point.y]의 배열을 확인하면

아래와 같이 숫자 좌우에 작은따옴표가 있어서

 

EPSG:4326 기준 좌표를 "EPSG:3857"로 바꿀 때 에러가 발생하기 때문에
  -> const transformedCoord = ol.proj.transform(arr, "EPSG:4326", "EPSG:3857");

 

좌표만 표시되고, 지도는 표시되지 않으므로 

 

const coordinate = arr.map(Number);를 사용해서 좌표를 문자가 아닌 숫자 형식으로 바꿔야 합니다.

console.log를 이용해 확인하면 아래와 같이 작은따옴표가 제외되었습니다.

 

마. 지도 중심 이동

위에서 구한 transformedCoord를 이용해 주소에 해당하는 좌표로 이동합니다.

// 지도 중심 이동
vmap.getView().setCenter(transformedCoord);

 

바. 지도가 제대로 이동하지 않고 popup은 표시되지 않는 문제 해결

아래와 같이 popup 창의 제목(title)과 내용(content)을 설정하고 실행하면

pop.title = "<b>주소에 대한 좌표</b><br><br>";
const content = address + " 의 좌표는 <br><br>" + result.response.result.point.x + ',<br>' + result.response.result.point.y + " 입니다.";
vmap.addOverlay(pop);
pop.show(content, transformedCoord);

 

좌표를 표시한 후 

 

지도가 제대로 이동하지 못하는 문제점이 있으므로

 

setTimeout 함수를 이용해 지도를 이동할 수 있는 시간(5초)을 확보한 후 popup 창을 표시하도록 해야 합니다. 그러면 지도도 잘 이동하고, 팝업 창도 잘 표시됩니다.

// 0.5초 후 pop 표시(비동기 처리하여 충돌 방지)
setTimeout(() => {
pop.title = "<b>주소에 대한 좌표</b><br><br>";
const content = address + " 의 좌표는 <br><br>" + result.response.result.point.x + ',<br>' + result.response.result.point.y + " 입니다.";
vmap.addOverlay(pop);
pop.show(content, transformedCoord);
}, 500);

 

사. 검색 버튼뿐만 아니라 엔터 키를 눌러도 되도록 코드 작성

    // 버튼 클릭 시 검색 실행
    $('#searchBtn').click(function () {
        get_lat_lng();
    });

    // Enter 키 입력 시 검색 실행
    $('#addressInput').keydown(function (event) {
        if (event.key === "Enter") {
            event.preventDefault(); // 폼이 제출되지 않도록 방지
            get_lat_lng();
        }
    });

 

아래를 보면 검색 버튼의 id는 searchBtn이고, inputbox의 id는 addressInput라 모두 #을 앞에 붙여서 위 함수를 작성했습니다.

<div class="buttons">
<input type="text" size="30" id="addressInput" value="">
<button id="searchBtn" type="button" >검색</button>
</div>

 

아. 지도 위에 inputbox 및 검색 버튼 배치

<!-- 헤드 태그 안에 style 지정 -->
<style type="text/css">
#wrap .buttons { position:absolute;top:20;left:20;z-index:1000;padding:5px; }
#wrap .buttons .control-btn { margin:0 5px 5px 0; }
</style>

<!-- div 태그의 순서는 영향 없음 -->
<div id="vmap">
<div id="wrap">

 

style로 id가 wrap인 div와 class가 buttons인 button의 설정을 하고,

body안에서 적용합니다.

반응형