본문 바로가기
정보기술

(7) 카카오 지도 API - 특정 주소의 좌표 확인하기

by 엘라트리니티 2022. 12. 17.

2022.12.17 - [정보기술] - (1) 카카오 지도 API - 지도 만들기

2022.12.17 - [정보기술] - (2) 카카오 지도 API - 단일 마커 표시하기

2022.12.17 - [정보기술] - (3) 카카오 지도 API - 다중 마커 표시하기

2022.12.17 - [정보기술] - (4) 카카오 지도 API - 정보창 표시하기

2022.12.17 - [정보기술] - (5) 카카오 지도 API - 커스텀 정보창 표시하기

2022.12.17 - [정보기술] - (6) 카카오 지도 API - 주소 검색하고 목록으로 나타내기

2022.12.17 - [정보기술] - (7) 카카오 지도 API - 특정 주소의 좌표 확인하기

2022.12.17 - [정보기술] - (8) 카카오 지도 API - 좌표 DB 등록하기

2022.12.17 - [정보기술] - (9) 카카오 지도 API - 검색 인터페이스 구현

2022.12.17 - [정보기술] - (10) 카카오 지도 API - 좌표 DB 불러오기 (xml 이용)

2022.12.17 - [정보기술] - (11) 카카오 지도 API – 위치기반 서비스 제공 (끝)

 

본 포스팅은 카카오 지도 API를 이용하여 자동완성검색 주소의 좌표를 확인하는 스크립트에 대한 내용입니다.

지난번 포스팅에서 이어지는 내용으로 장소명과 주소, 그리고 위도와 경도에 대한 내용을 확인하는 방법에 대해 설명합니다.

먼저 스크립트 전문과 실행결과를 확인하시기 바랍니다.

 

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@page import = "java.io.File"%>

<!DOCTYPE html>
<html lang="ko">
<head>
	<title> Place Information UI </title>
	<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">

	
<style>
body{
	font-family:"맑은 고딕", "고딕", "굴림";
}

html, body {
margin: 0px; 
padding: 0px;
}

#wrapper{
	width: 1200px;
	margin: 0 auto;	
}

.map_wrap, .map_wrap * {margin:0;padding:0;font-family:'Malgun Gothic',dotum,'돋움',sans-serif;font-size:12px;}
.map_wrap a, .map_wrap a:hover, .map_wrap a:active{color:#000;text-decoration: none;}
.map_wrap {position:relative;width:100%;height:480px;}
#menu_wrap {position:absolute;top:0;left:0;bottom:0;width:250px;margin:10px 0 30px 10px;padding:5px;overflow-y:auto;background:rgba(255, 255, 255, 0.7);z-index: 1;font-size:12px;border-radius: 10px;}
.bg_white {background:#fff;}
#menu_wrap hr {display: block; height: 1px;border: 0; border-top: 2px solid #5F5F5F;margin:3px 0;}
#menu_wrap .option{text-align: center;}
#menu_wrap .option p {margin:10px 0;}  
#menu_wrap .option button {margin-left:5px;}
#placesList li {list-style: none;}
#placesList .item {position:relative;border-bottom:1px solid #888;overflow: hidden;cursor: pointer;min-height: 65px;}
#placesList .item span {display: block;margin-top:4px;}
#placesList .item h5, #placesList .item .info {text-overflow: ellipsis;overflow: hidden;white-space: nowrap;}
#placesList .item .info{padding:10px 0 10px 55px;}
#placesList .info .gray {color:#8a8a8a;}
#placesList .info .jibun {padding-left:26px;background:url(https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/places_jibun.png) no-repeat;}
#placesList .info .tel {color:#009900;}
#placesList .info .placey {color:#009900;}
#placesList .info .placex {color:#009900;}
#placesList .item .markerbg {float:left;position:absolute;width:36px; height:37px;margin:10px 0 0 10px;background:url(https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png) no-repeat;}
#placesList .item .marker_1 {background-position: 0 -10px;}
#placesList .item .marker_2 {background-position: 0 -56px;}
#placesList .item .marker_3 {background-position: 0 -102px}
#placesList .item .marker_4 {background-position: 0 -148px;}
#placesList .item .marker_5 {background-position: 0 -194px;}
#placesList .item .marker_6 {background-position: 0 -240px;}
#placesList .item .marker_7 {background-position: 0 -286px;}
#placesList .item .marker_8 {background-position: 0 -332px;}
#placesList .item .marker_9 {background-position: 0 -378px;}
#placesList .item .marker_10 {background-position: 0 -423px;}
#placesList .item .marker_11 {background-position: 0 -470px;}
#placesList .item .marker_12 {background-position: 0 -516px;}
#placesList .item .marker_13 {background-position: 0 -562px;}
#placesList .item .marker_14 {background-position: 0 -608px;}
#placesList .item .marker_15 {background-position: 0 -654px;}
#pagination {margin:10px auto;text-align: center;}
#pagination a {display:inline-block;margin-right:10px;}
#pagination .on {font-weight: bold; cursor: default;color:#777;}

</style>
	
</head>
<body>
<div id="wrapper">

<div class="map_wrap">
    <div id="map" style="width:100%;height:100%;position:relative;overflow:hidden;"></div>

    <div id="menu_wrap" class="bg_white">
        <div class="option">
            <div>
                    키워드 : <input type="text" value="서울역" id="keyword" size="15"> 
            <button onclick="searchPlaces(); return false;">검색하기</button> 
            </div>
        </div>
        <hr>
        <ul id="placesList"></ul>
        <div id="pagination"></div>
    </div>
	</div>
	
	<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=YOUR_API_KEY&libraries=services"></script>
	<script>
// 마커를 담을 배열입니다
var markers = [];

var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
    mapOption = {
        center: new kakao.maps.LatLng(37.566826, 126.9786567), // 지도의 중심좌표
        level: 3 // 지도의 확대 레벨
    };  

// 지도를 생성합니다    
var map = new kakao.maps.Map(mapContainer, mapOption); 

// 장소 검색 객체를 생성합니다
var ps = new kakao.maps.services.Places();  

// 검색 결과 목록이나 마커를 클릭했을 때 장소명을 표출할 인포윈도우를 생성합니다
var infowindow = new kakao.maps.InfoWindow({zIndex:1});

// 키워드로 장소를 검색합니다
searchPlaces();

// 키워드 검색을 요청하는 함수입니다
function searchPlaces() {

    var keyword = document.getElementById('keyword').value;

    if (!keyword.replace(/^\s+|\s+$/g, '')) {
        alert('키워드를 입력해주세요!');
        return false;
    }

    // 장소검색 객체를 통해 키워드로 장소검색을 요청합니다
    ps.keywordSearch( keyword, placesSearchCB); 
}

// 장소검색이 완료됐을 때 호출되는 콜백함수 입니다
function placesSearchCB(data, status, pagination) {
    if (status === kakao.maps.services.Status.OK) {

        // 정상적으로 검색이 완료됐으면
        // 검색 목록과 마커를 표출합니다
        displayPlaces(data);

        // 페이지 번호를 표출합니다
        displayPagination(pagination);

    } else if (status === kakao.maps.services.Status.ZERO_RESULT) {

        alert('검색 결과가 존재하지 않습니다.');
        return;

    } else if (status === kakao.maps.services.Status.ERROR) {

        alert('검색 결과 중 오류가 발생했습니다.');
        return;

    }
}

// 검색 결과 목록과 마커를 표출하는 함수입니다
function displayPlaces(places) {

    var listEl = document.getElementById('placesList'), 
    menuEl = document.getElementById('menu_wrap'),
    fragment = document.createDocumentFragment(), 
    bounds = new kakao.maps.LatLngBounds(), 
    listStr = '';
    
    // 검색 결과 목록에 추가된 항목들을 제거합니다
    removeAllChildNods(listEl);

    // 지도에 표시되고 있는 마커를 제거합니다
    removeMarker();
    
    for ( var i=0; i<places.length; i++ ) {

        // 마커를 생성하고 지도에 표시합니다
        var placePosition = new kakao.maps.LatLng(places[i].y, places[i].x),
            marker = addMarker(placePosition, i), 
            itemEl = getListItem(i, places[i]); // 검색 결과 항목 Element를 생성합니다

        // 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해
        // LatLngBounds 객체에 좌표를 추가합니다
        bounds.extend(placePosition);

        // 마커와 검색결과 항목에 mouseover 했을때
        // 해당 장소에 인포윈도우에 장소명을 표시합니다
        // mouseout 했을 때는 인포윈도우를 닫습니다
        (function(marker, pname, praddress, paddress, plat, plng) {
            kakao.maps.event.addListener(marker, 'mouseover', function() {
                displayInfowindow(marker, pname);
            });

            kakao.maps.event.addListener(map, 'mouseout', function() {
                infowindow.close();
            });
            
            // 리스트의 아이템을 클릭하면 정보들을 text 영역으로 전송 (hidden 사용가능)
            itemEl.onclick = function() {
                if (praddress) {
                document.getElementById('fulladdress').value = "[" + pname + "]" + praddress;
                } else {
                document.getElementById('fulladdress').value = "[" + pname + "]" + paddress;
                }
            	
            	document.getElementById('pname').value = pname;            	
                if (praddress) {
                	document.getElementById('paddress').value = praddress;
                } else {
                	document.getElementById('paddress').value = paddress; 
                }
            	document.getElementById('latclick').value = plat;
            	document.getElementById('lngclick').value = plng;
            };

            itemEl.onmouseover =  function () {
                displayInfowindow(marker, pname);
            };

            itemEl.onmouseout =  function () {
                infowindow.close();
            };
        })(marker, places[i].place_name, places[i].road_address_name, places[i].address_name, places[i].y, places[i].x);

        fragment.appendChild(itemEl);
    }

    // 검색결과 항목들을 검색결과 목록 Elemnet에 추가합니다
    listEl.appendChild(fragment);
    menuEl.scrollTop = 0;

    // 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
    map.setBounds(bounds);
}

// 검색결과 항목을 Element로 반환하는 함수입니다
function getListItem(index, places) {

    var el = document.createElement('li'),
    itemStr = '<span class="markerbg marker_' + (index+1) + '"></span>' +
                '<div class="info" style="cursor:pointer;">' +
                '   <h5>' + places.place_name + '</h5>';

    if (places.road_address_name) {
        itemStr += '    <span>' + places.road_address_name + '</span>' +
                    '   <span class="jibun gray">' +  places.address_name  + '</span>';
    } else {
        itemStr += '    <span>' +  places.address_name  + '</span>'; 
    }
      itemStr += '  <span class="tel">' + places.phone + '</span>'; +
                '</div>';           

    el.innerHTML = itemStr;
    el.className = 'item';

    return el;
}


// 마커를 생성하고 지도 위에 마커를 표시하는 함수입니다
function addMarker(position, idx, title) {
    var imageSrc = 'https://t1.daumcdn.net/localimg/localimages/07/mapapidoc/marker_number_blue.png', // 마커 이미지 url, 스프라이트 이미지를 씁니다
        imageSize = new kakao.maps.Size(36, 37),  // 마커 이미지의 크기
        imgOptions =  {
            spriteSize : new kakao.maps.Size(36, 691), // 스프라이트 이미지의 크기
            spriteOrigin : new kakao.maps.Point(0, (idx*46)+10), // 스프라이트 이미지 중 사용할 영역의 좌상단 좌표
            offset: new kakao.maps.Point(13, 37) // 마커 좌표에 일치시킬 이미지 내에서의 좌표
        },
        markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imgOptions),
            marker = new kakao.maps.Marker({
            position: position, // 마커의 위치
            image: markerImage 
        });

    marker.setMap(map); // 지도 위에 마커를 표출합니다
    markers.push(marker);  // 배열에 생성된 마커를 추가합니다

    return marker;
}

// 지도 위에 표시되고 있는 마커를 모두 제거합니다
function removeMarker() {
    for ( var i = 0; i < markers.length; i++ ) {
        markers[i].setMap(null);
    }   
    markers = [];
}

// 검색결과 목록 또는 마커를 클릭했을 때 호출되는 함수입니다
// 인포윈도우에 장소명을 표시합니다
function displayInfowindow(marker, title) {
    var content = '<div style="padding:5px;z-index:1;">' + title + '</div>';

    infowindow.setContent(content);
    infowindow.open(map, marker);
}

 // 검색결과 목록의 자식 Element를 제거하는 함수입니다
function removeAllChildNods(el) {   
    while (el.hasChildNodes()) {
        el.removeChild (el.lastChild);
    }
}
</script>
	
    <div>
    <!-- 위도 및 경도 좌표 및 위치정보 -->
    <input type="text" id="fulladdress" name="fulladdress" style="width:90%;" disabled> 
    <input type="text" id="pname" name="pname" value="">   
    <input type="text" id="paddress" name="paddress" value="">  
    <input type="text" id="latclick" name="latclick" value=""> 
    <input type="text" id="lngclick" name="lngclick" value=""> 
    </div>
    </div>
</body>
</html>

 

지난번 포스팅인 “주소 검색하고 목록으로 나타내기” 스크립트와 대부분 동일합니다. 단지 주의 깊게 봐야 할 부분은 다음 스크립트입니다.

 

<script>
	(function(marker, pname, praddress, paddress, plat, plng) {
            kakao.maps.event.addListener(marker, 'mouseover', function() {
                displayInfowindow(marker, pname);
            });

            kakao.maps.event.addListener(map, 'mouseout', function() {
                infowindow.close();
            });
            
            // 리스트의 아이템을 클릭하면 정보들을 hidden 영역으로 전송
            itemEl.onclick = function() {
                if (praddress) {
                document.getElementById('fulladdress').value = "[" + pname + "]" + praddress;
                } else {
                document.getElementById('fulladdress').value = "[" + pname + "]" + paddress;
                }
            	
            	document.getElementById('pname').value = pname;            	
                if (praddress) {
                	document.getElementById('paddress').value = praddress;
                } else {
                	document.getElementById('paddress').value = paddress; 
                }
            	document.getElementById('latclick').value = plat;
            	document.getElementById('lngclick').value = plng;
            };

            itemEl.onmouseover =  function () {
                displayInfowindow(marker, pname);
            };

            itemEl.onmouseout =  function () {
                infowindow.close();
            };
        })(marker, places[i].place_name, places[i].road_address_name, places[i].address_name, places[i].y, places[i].x);

        fragment.appendChild(itemEl);
    }
 </script>

 

해당 스크립트는 카카오 맵에서 마커의 움직임에 따라 (mouseover, mouseout)과 마커의 정보창이 나타나고 지번주소와 도로명 주소가 if문에 따라 분기가 됩니다.

사실 이는 카카오 개발자 센터에서 공개하는 내용은 아닙니다만 지역의 행정구분시 개정된 도로명 주소가 존재하는 곳이 있지만 여전히 지번주소를 사용하는 곳이 있어서

(1) 도로명 주소가 존재한다면 먼저 그것을 표기하고

(2) 만약 도로명 주소가 존재하지 않는다면 지번 주소를 출력하도록 되어있습니다.

해당 값들은 장소이름 (pname), 주소(praddress=도로명주소, paddress=지번주소), 위도(plat), 경도(plng)으로 정의되어 body의 input에 표시되게 되고

이를 type=”hidden”으로 숨길 수 있으며 input 태그에 의해 id가 결정되었기 때문에 request.getParameter 등의 구문을 통해 Form 제출시 DB나 다른 JavaScript 문서로 넘기는 것이 가능해집니다.

이 구문을 응용하면 DB에 업로드 하기위한 특정장소의 정보등을 추출할 수 있으며 반대로, 위치정보 서비스를 제공할 때 기준점이 되는 위치 등을 설정할 수 있게됩니다.

이처럼 API를 살펴볼때는 행정정책에 대한 개연성 있는 논리가 로직에 반영된 것을 발견하는 경우도 있습니다.

 

2022.12.17 - [정보기술] - (8) 카카오 지도 API - 좌표 DB 등록하기

 

(8) 카카오 지도 API - 좌표 DB 등록하기

2022.12.17 - [정보기술] - (1) 카카오 지도 API - 지도 만들기 2022.12.17 - [정보기술] - (2) 카카오 지도 API - 단일 마커 표시하기 2022.12.17 - [정보기술] - (3) 카카오 지도 API - 다중 마커 표시하기 2022.12.17 - [

iftraveler.tistory.com

 

댓글