지난 이야기
2023.03.17 - [프로젝트 정리/애완동물 종합 솔루션(CatDogForest)] - Kakao Map API with 애완동물병원 #6
원하는 대로 dragend, zoom_changed 이벤트가 있을때 마다
지도의 남서와 북동쪽의 위경도를 기준으로 데이터를 가져오도록 만들었다.
map.html
<!DOCTYPE html>
<html lang="ko" itemscope itemtype="http://schema.org/WebPage" xmlns:th="http://www.thymeleaf.org"
xmlns:layout="http://www.ultraq.net.nz/thymeleaf/layout" layout:decorate="~{layout/default_layout.html}">
<div layout:fragment="content">
<div class="col-12 d-flex justify-content-center py-2 px-4">
<div id="map" style="width:100%;height:250px;"></div>
</div>
<hr class="border border-primary border-1 opacity-75">
<div class="col-12 d-flex justify-content-center py-2 px-4">
<div class="col-4">
<img class="img border-radius-lg max-width-100 w-100" src="/assets/img/hospital.png" alt="bruce">
</div>
<div class="col-8">
<div class="d-flex justify-content-between align-items-center mb-1 text-sm">
<h4 class="mb-0">불타는 지옥 병원</h4>
<div class="d-block">
<a href="#"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-star-fill text-primary text-gradient" viewBox="0 0 16 16">
<path d="M3.612 15.443c-.386.198-.824-.149-.746-.592l.83-4.73L.173 6.765c-.329-.314-.158-.888.283-.95l4.898-.696L7.538.792c.197-.39.73-.39.927 0l2.184 4.327 4.898.696c.441.062.612.636.282.95l-3.522 3.356.83 4.73c.078.443-.36.79-.746.592L8 13.187l-4.389 2.256z"/>
</svg></a>
</div>
</div>
<div class="row mb-1 text-xs">
<div class="col-auto">
<span class="h6">777</span>
<span>Posts</span>
</div>
<div class="col-auto">
<span class="h6">7.7Mk</span>
<span>Followers</span>
</div>
</div>
<p class="text-xs mb-0">
Decisions: 우리 고양이는 물어요. <br>
<a href="javascript:;" class="text-info icon-move-right">More about me
<i class="fas fa-arrow-right text-sm ms-1" aria-hidden="true"></i>
</a>
</p>
</div>
</div>
<script type="text/javascript" src="//dapi.kakao.com/v2/maps/sdk.js?appkey=fd70fd57d8ee6b2f63d1997fc16c9cf5&libraries=services&libraries=clusterer"></script>
<script>
// navigator.geolocation logic -> 현재 위.경도를 가져옴 -> 못가져오면 기본 값으로 map을 생성함
if(navigator.geolocation){
console.log('지오로케이션 먹음')
navigator.geolocation.getCurrentPosition((position) => {
var currentPosition = new kakao.maps.LatLng(position.coords.latitude, position.coords.longitude);
map.setCenter(currentPosition);
});
}else{
console.log('지오로케이션 안먹음')
}
let markers = [];
let mapContainer = document.getElementById('map'), // 지도를 표시할 div
mapOption = {
center: new kakao.maps.LatLng(36.49334, 127.27856), // 지도의 중심좌표
level: 8, // 지도의 확대 레벨
mapTypeId : kakao.maps.MapTypeId.ROADMAP // 지도종류
};
// 지도를 생성한다
let map = new kakao.maps.Map(mapContainer, mapOption);
// 마커 클러스터러를 생성합니다
let clusterer = new kakao.maps.MarkerClusterer({
map: map, // 마커들을 클러스터로 관리하고 표시할 지도 객체
averageCenter: true, // 클러스터에 포함된 마커들의 평균 위치를 클러스터 마커 위치로 설정
minLevel: 9 // 클러스터 할 최소 지도 레벨
});
// 지도 타입 변경 컨트롤을 생성한다
const mapTypeControl = new kakao.maps.MapTypeControl();
// 지도의 상단 우측에 지도 타입 변경 컨트롤을 추가한다
map.addControl(mapTypeControl, kakao.maps.ControlPosition.TOPRIGHT);
// 지도에 확대 축소 컨트롤을 생성한다
const zoomControl = new kakao.maps.ZoomControl();
// 지도의 우측에 확대 축소 컨트롤을 추가한다
map.addControl(zoomControl, kakao.maps.ControlPosition.RIGHT);
function getBounds() {
let center = map.getCenter();
let bounds = map.getBounds();
let sw = bounds.getSouthWest();
let ne = bounds.getNorthEast();
let swLat = sw.getLat();
let swLng = sw.getLng();
let neLat = ne.getLat();
let neLng = ne.getLng();
let centerLat = center.getLat();
let centerLng = center.getLng();
let position = [swLat, swLng, neLat, neLng, centerLat, centerLng];
return position;
}
function getData() {
let position = getBounds();
fetch('/api/user/hospital/map?position='+position)
.then(response => response.json())
.then(data => {
for (let i = 0; i < data.length; i++ ) {
// 지도에 마커를 생성하고 표시한다.
let marker = new kakao.maps.Marker({
position: new kakao.maps.LatLng(data[i].latitude, data[i].longitude), // 마커의 좌표
title: data.address_name,
map: map // 마커를 표시할 지도 객체
});
// 인포윈도우를 생성하고 지도에 표시합니다.
let infowindow = new kakao.maps.InfoWindow({
content : "<div class='d-flex flex-column m-3'><h6 class='text'>"
+data[i].place_name+"</h6>" +
"<label class='text'>주소 : "
+data[i].address_name+"</label>" +
"<div className='d-flex'>" +
"<a class='btn btn-sm btn-primary' href='"+data[i].place_url+"'>"+
"자세히</a>" +
"<a class='btn btn-sm btn-secondary ms-1' href=tel:'"+data[i].phone+"'>"+
"전화</a>"+
"</div></div>",
removable: true
});
// 마커에 이벤트를 등록하는 함수 만들고 즉시 호출하여 클로저를 만듭니다
// 클로저를 만들어 주지 않으면 마지막 마커에만 이벤트가 등록됩니다
(function(marker, infowindow) {
// 마커에 mouseover 이벤트를 등록하고 마우스 오버 시 인포윈도우를 표시합니다
kakao.maps.event.addListener(marker, 'click', function() {
infowindow.open(map, marker);
});
// 마커에 mouseout 이벤트를 등록하고 마우스 아웃 시 인포윈도우를 닫습니다
/*
kakao.maps.event.addListener(marker, 'mouseout', function() {
infowindow.close();
});
*/
})(marker, infowindow);
// 생성된 마커를 마커 저장하는 변수에 넣음
markers.push(marker);
}
clusterer.addMarkers(markers);
});
}
// 다중 마커 생성
document.addEventListener('DOMContentLoaded', () =>{
getData();
});
kakao.maps.event.addListener(map, 'zoom_changed', function() {
deleteClusterMarkers();
deleteMarkers();
getData();
});
kakao.maps.event.addListener(map, 'dragend', function () {
deleteClusterMarkers();
deleteMarkers();
getData();
});
kakao.maps.event.addListener( clusterer, 'clustered', function( clusterer ) {
clusterer.forEach(cluster => {
let tempMarkers = cluster.getMarkers();
temp = tempMarkers[0].address_name;
console.log(temp);
//TODO: 클러스터의 마커 정보를 확인 및 적용할 text를 구해서 각 클러스터의 커스텀 오버레이 content에 text를 재설정해주세요.
cluster.getClusterMarker().getContent().innerText = temp;
});
});
// Deletes all markers in the array by removing references to them.
function deleteMarkers() {
for(let idx = 0; idx < markers.length; idx++){
markers[idx].setMap(null);
}
markers = [];
}
function deleteClusterMarkers() {
for(let sidx = 0; sidx < markers.length; sidx++){
clusterer.removeMarker(markers[sidx]);
}
clusterer = new kakao.maps.MarkerClusterer({
map: map, // 마커들을 클러스터로 관리하고 표시할 지도 객체
averageCenter: true, // 클러스터에 포함된 마커들의 평균 위치를 클러스터 마커 위치로 설정
minLevel: 9 // 클러스터 할 최소 지도 레벨
});
}
</script>
</div>
</html>
위도와 경도 데이터로 가까운 거리를 계산하는 하버사인 공식을 활용
https://en.wikipedia.org/wiki/Haversine_formula
hospitalMapper.xml
<select id="getAll" resultMap="hospitalMap" parameterType="hashmap">
SELECT
*,
(
6371 * acos (
cos ( radians(latitude ) )
* cos ( radians(#{centerLat} ) )
* cos ( radians(longitude) - radians(#{centerLng} ) )
+ sin ( radians(latitude))
* sin ( radians(#{centerLat} ) )
)
)
AS distance
FROM hospital
where
latitude BETWEEN #{swLat} and #{neLat}
and
longitude BETWEEN #{swLng} and #{neLng}
ORDER BY distance
</select>
위 영상과 같이 동작하는데 이벤트마다 DB에서 marker와 클러스터를 다시 뿌려주니 동작이 영 느리다.
기본적으로 모든 병원 정보를 가져와서 움직임이 있을때마다 좌표 기준으로 최대 5개 아이템만 하단부에 리스트로 뿌려주도록 동작시키는게 최적화의 방법인듯 싶다.
'프로젝트 정리 > 애완동물 종합 솔루션(CatDogForest)' 카테고리의 다른 글
Kakao Map API with 애완동물병원 #9 (0) | 2023.03.24 |
---|---|
Kakao Map API with 애완동물병원 #8 (0) | 2023.03.22 |
Kakao Map API with 애완동물병원 #6 (0) | 2023.03.17 |
Kakao Map API with 애완동물병원 #5 (0) | 2023.03.16 |
Kakao Map API with 애완동물병원#4 (0) | 2023.03.15 |