728x90
"카카오 맵 키워드로 장소검색하고 목록으로 표출하기" 로 검색된 결과 URL 이다.
https://apis.map.kakao.com/web/sample/keywordList/
위 UTL 예시에 나온 코드로는 부족하므로 클러스터링 등 내용을 더 찾아서 살을 붙여야 한다.
화면 구성을 위해서 아래와 같이 HTML 을 변경한다.
아래 HTML 코드에서 카카오맵 API Key는 삭제했다.
<html>
<head>
<meta charset="UTF-8">
<meta name="robots" content="noindex,nofollow"/>
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta http-equiv="x-ua-compatible" content="ie=edge">
<title>카카오지도</title>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/mdb.lite.css">
<link rel="stylesheet" href="/css/jquery-ui.css">
<link rel="stylesheet" href="/css/kakaomap.css"/>
</head>
<body>
<div class="card">
<div class="my-0 mx-2">
<div class="form-check form-check-inline">
<button type="button" class="btn btn-outline-primary btn-sm px-2" id="searchToggle">OFF</button>
</div>
<div class="form-check form-check-inline">
<p class="fs-4">반경</p>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="radius" id="dis1" value="0.5" checked />
<label class="form-check-label" for="dis1">0.5</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="radius" id="dis2" value="1.0" />
<label class="form-check-label" for="dis2">1</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="radius" id="dis3" value="1.5" />
<label class="form-check-label" for="dis3">1.5</label>
</div>
<div class="form-check form-check-inline">
<input class="form-check-input" type="radio" name="radius" id="dis4" value="2.0" />
<label class="form-check-label" for="dis4">2</label>
</div>
<div class="form-check form-check-inline">
<button type="button" class="btn btn-outline-primary btn-sm px-2" id="curLoc">현위치</button>
</div>
</div>
<div class="mt-1 mb-2 mx-2">
<div class="map_wrap">
<div id="map" style="width:100%;height:800px;position:relative;overflow:hidden;"></div>
<div id="menu_wrap" class="bg_white">
<div class="option">
<div class="form-check form-check-inline">
<form onsubmit="searchPlaces(); return false;">
<input type="text" value="" id="keyword" size="15">
<button type="submit"> 검색</button>
</form>
<img src="./marker/icon_eraser.png" alt="clear" height="30" width="30" style="margin-left: 10px;" id="eraser">
</div>
</div>
<div id="datacount"></div>
<hr>
<ul id="placesList"></ul>
<div id="pagination"></div>
</div>
<div id="dialog" title="지도 Infowindow"></div>
</div>
</div>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
<script type="text/javascript" src="/js/popper.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/js/mdb.lite.min.js"></script>
<script type="text/javascript" src="/js/jquery-ui.js"></script>
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=&libraries=services,clusterer,drawing"></script>
<script type="text/javascript" src="Map.js"></script>
<script>
$("#searchToggle").click(function(){
// 지도 검색창 ON/OFF
if($("#menu_wrap").css("display")=="none"){
$("#menu_wrap").show();
$("#searchToggle").text('OFF');
} else {
$("#menu_wrap").hide();
$("#searchToggle").text('ON');
}
});
$("input[name='radius']").change(function(){
searchPlaces();
});
$("#curLoc").click(function(){
// 현재 지도의 중심좌표 구하기
searchPlaces();
});
$("#eraser").click(function(){
$("#keyword").val('');
});
</script>
</body>
</html>
|
Map.js
변수는 let 과 const 로 전부 변경하고, Ajax 로 PHP와 연동된 DB 자료를 가져다가 출력하는 걸 만든다.
// 마커를 담을 배열입니다
let markers = [];
const mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(37.566826, 126.9786567), // 지도의 중심좌표
level: 12 // 지도의 확대 레벨
};
// 지도를 생성합니다
const map = new kakao.maps.Map(mapContainer, mapOption);
// 마커 클러스터러를 생성합니다
const clusterer = new kakao.maps.MarkerClusterer({
map: map, // 마커들을 클러스터로 관리하고 표시할 지도 객체
averageCenter: true, // 클러스터에 포함된 마커들의 평균 위치를 클러스터 마커 위치로 설정
minLevel: 6 // 클러스터 할 최소 지도 레벨
});
// 일반 지도와 스카이뷰로 지도 타입을 전환할 수 있는 지도타입 컨트롤을 생성합니다
const mapTypeControl = new kakao.maps.MapTypeControl();
// 지도에 컨트롤을 추가해야 지도위에 표시됩니다
// kakao.maps.ControlPosition은 컨트롤이 표시될 위치를 정의하는데 TOPRIGHT는 오른쪽 위를 의미합니다
map.addControl(mapTypeControl, kakao.maps.ControlPosition.TOPRIGHT);
// 지도 확대 축소를 제어할 수 있는 줌 컨트롤을 생성합니다
const zoomControl = new kakao.maps.ZoomControl();
map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHTBOTTOM);
// 지도가 이동, 확대, 축소로 인해 중심좌표가 변경되면 마지막 파라미터로 넘어온 함수를 호출하도록 이벤트를 등록합니다
let getLat = null;
let getLng = null;
kakao.maps.event.addListener(map, 'center_changed', function() {
// 지도의 레벨을 얻어옵니다
const level = map.getLevel();
// 지도의 중심좌표를 얻어옵니다
const latlng = map.getCenter();
console.log('중심 위도: '+latlng.getLat() + ', 경도: ' + latlng.getLng());
getLat = latlng.getLat();
getLng = latlng.getLng();
});
// 장소 검색 객체를 생성합니다
const ps = new kakao.maps.services.Places();
// 검색 결과 목록이나 마커를 클릭했을 때 장소명을 표출할 인포윈도우를 생성합니다
const infowindow = new kakao.maps.InfoWindow({zIndex:1});
// 키워드로 장소를 검색합니다
searchPlaces();
// 키워드 검색을 요청하는 함수입니다
function searchPlaces() {
let keyword = document.getElementById('keyword').value;
let radius = $("input[name='radius']:checked").val();
$.ajax({
url : 'ajaxMap.php',
type : 'GET',
data : { 'msg': keyword, 'radius':radius, 'lat':getLat, 'lng':getLng },
dataType: 'json',
success : function(json) {
if(json.length == 0) {
alert('검색 데이터가 없습니다.');
return;
}
console.log('데이터 갯수 :: ' + json.length);
displayPlaces(json);
// 클러스터에 마커들을 추가합니다
clusterer.addMarkers(markers);
var pagination = [ '1', '2', '3' ];
displayPagination(pagination);
},
error : function(xhr, status, error){
alert("에러코드 : " + xhr.status); //요청에 실패하면 에러코드 출력
},
complete : function() {
}
});
}
// 장소검색이 완료됐을 때 호출되는 콜백함수 입니다
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) {
let listEl = document.getElementById('placesList'),
menuEl = document.getElementById('menu_wrap'),
fragment = document.createDocumentFragment(),
bounds = new kakao.maps.LatLngBounds(),
listStr = '';
// 검색 결과 목록에 추가된 항목들을 제거합니다
removeAllChildNods(listEl);
// 지도에 표시되고 있는 마커를 제거합니다
removeMarker();
const cntEl = document.getElementById('datacount');
cntEl.innerText = '검색 개수 : ' + places.length;
for (let i=0; i<places.length; i++) {
let color_marker = "marker-icon-blue.png";
if (places[i].yn == 0) { // 없으면 => 빨간색
color_marker = "marker-icon-red.png";
if(places[i].isbiz > 0) {
color_marker = "marker-icon-green.png";
}
} else {
color_marker = "marker-icon-blue.png";
if(places[i].isbiz > 0) {
color_marker = "marker-icon-orange.png";
}
}
// 마커를 생성하고 지도에 표시합니다.
let idx = places[i].idx; // 절대 var 변수를 선언하면 안된다. 반드시 let 변수를 선언해야 제대로 동작되더라. 개삽질을 엄청했다.
let title = places[i].idx;
let placePosition = new kakao.maps.LatLng(places[i].latitude, places[i].longitude);
let marker = addMarkers(placePosition, color_marker, title);
let itemEl = getListItem(i, places[i], color_marker); // 검색 결과 항목 Element를 생성합니다
// 검색된 장소 위치를 기준으로 지도 범위를 재설정하기위해 LatLngBounds 객체에 좌표를 추가합니다
bounds.extend(placePosition);
// 마커와 검색결과
// 항목에 mouseover 했을때 해당 장소에 인포윈도우에 장소명을 표시합니다. mouseout 했을 때는 인포윈도우를 닫습니다
(function(marker, title) {
kakao.maps.event.addListener(marker, 'mouseover', function() {
displayInfowindow(marker, title);
});
kakao.maps.event.addListener(marker, 'mouseout', function() {
infowindow.close();
});
itemEl.onmouseover = function () {
displayInfowindow(marker, title);
};
itemEl.onmouseout = function () {
infowindow.close();
};
// 마커에 클릭 이벤트를 등록한다.
kakao.maps.event.addListener(marker, 'click', function() {
//map.panTo(placePosition); // 지도 중심을 부드럽게 이동시킨다.
console.log('idx:' + idx);
custom_infowindow(idx);
});
})(marker, places[i].bldNM);
fragment.appendChild(itemEl);
}
// 검색결과 항목들을 검색결과 목록 Elemnet에 추가합니다
listEl.appendChild(fragment);
menuEl.scrollTop = 0;
// 검색된 장소 위치를 기준으로 지도 범위를 재설정합니다
map.setBounds(bounds);
}
function custom_infowindow(idx){
$('#dialog').load('infowindow.php?do='+idx, function() {
$(this).dialog({
autoOpen : true,
height : 'auto',
width : 'auto',
position: { my: "center", at: "center", of: window },
title : 'XX정보',
buttons : {
"닫기" : function() {
$(this).dialog("close");
}
}
});
});
}
// 검색결과 항목을 Element로 반환하는 함수입니다. 위도, 경도, 번호, 건물명, 도로명주소, 지번주소
function getListItem(index, places, _color) {
let el = document.createElement('li'),
itemStr = '<span style="float:left;position:absolute;width:33px; height:31px;margin:10px 0 0 10px;"><img width="31" height="31" src="./marker/' + _color + '"></span>' +
'<div class="info">' +
' <h5>' + places.bldNM + '</h5>';
if (places.stAddress) {
itemStr += ' <span>' + places.stAddress + '</span>' +
' <span class="jibun gray">' + places.jiAddress + '</span>';
} else {
itemStr += ' <span>' + places.jiAddress + '</span>';
}
itemStr += ' <span class="tel">' + places.idx + '</span>' +
'</div>';
el.innerHTML = itemStr;
el.className = 'item';
return el;
}
function addMarkers(position, _color, title) {
const imageSrc = './marker/' + _color;
const imageSize = new kakao.maps.Size(33, 33);
const imageOptions = {
offset: new kakao.maps.Point(27, 69)
};
const markerImage = new kakao.maps.MarkerImage(imageSrc, imageSize, imageOptions)
// 마커를 생성합니다
const marker = new kakao.maps.Marker({
position: position,
//title: title,
image: markerImage
});
marker.setMap(map); // 마커가 지도 위에 표시되도록 설정합니다
markers.push(marker); // 생성된 마커를 배열에 추가합니다
return marker;
}
// 지도 위에 표시되고 있는 마커를 모두 제거합니다
function removeMarker() {
//clusterer.removeMarkers(markers);
clusterer.clear(); // 추가된 모든 마커 삭제
for ( let i = 0; i < markers.length; i++ ) {
markers[i].setMap(null);
}
markers = [];
}
// 검색결과 목록 하단에 페이지번호를 표시는 함수입니다
function displayPagination(pagination) {
let paginationEl = document.getElementById('pagination'),
fragment = document.createDocumentFragment(),
i;
// 기존에 추가된 페이지번호를 삭제합니다
while (paginationEl.hasChildNodes()) {
paginationEl.removeChild (paginationEl.lastChild);
}
for (i=1; i<=pagination.last; i++) {
let el = document.createElement('a');
el.href = "#";
el.innerHTML = i;
if (i===pagination.current) {
el.className = 'on';
} else {
el.onclick = (function(i) {
return function() {
pagination.gotoPage(i);
}
})(i);
}
fragment.appendChild(el);
}
paginationEl.appendChild(fragment);
}
// 검색결과 목록 또는 마커를 클릭했을 때 호출되는 함수. 인포윈도우에 장소명을 표시합니다
function displayInfowindow(marker, title) {
const 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);
}
}
|
백엔드 PHP 코드는 여기에는 적지 않는다.
화면에 뿌리는 marker 의 개수와 성능은 백엔드 Query를 어떻게 구현하느냐에 따라 달라진다.
가능하면 SELECT 문의 결과를 최소로 하여 원하는 결과를 찾을 수 있게 구현한다.
728x90
'React > morden javascript' 카테고리의 다른 글
Javascript Array.filter 사용 예제 (0) | 2022.12.27 |
---|---|
Javascript Array.map 사용 예제 (2) | 2022.12.27 |
[Javascript] 정규표현식 (0) | 2022.10.08 |
[Javascript] _cloneDeep() (0) | 2022.10.05 |
[ES6] Spread 연산자 (0) | 2022.10.02 |