지도 api 클러스터 와 마커, 그리고 커스텀 인포윈도우 겹침 문제

<div id="map" style="width:100%;height:350px;"></div>
<script>
var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
	mapOption = { 
		center: new daum.maps.LatLng(37.566826, 126.978656), // 지도의 중심좌표
		level: 3 // 지도의 확대 레벨
	};

// 지도를 표시할 div와  지도 옵션으로  지도를 생성합니다
var map = new daum.maps.Map(mapContainer, mapOption); 

// 커스텀 오버레이 엘리먼트를 만들고, 컨텐츠를 추가합니다
var content = document.createElement('div');
content.className = 'overlay';
content.innerHTML = '드래그 해주세요 :D';


// 커스텀 오버레이를 생성합니다 
var customoverlay = new daum.maps.CustomOverlay({
	map: map,
	content: content,
	position: new daum.maps.LatLng(37.566826, 126.978656)
});

 // 마커 클러스터러를 생성합니다 
var clusterer = new daum.maps.MarkerClusterer({
	map: map, // 마커들을 클러스터로 관리하고 표시할 지도 객체 
	averageCenter: true, // 클러스터에 포함된 마커들의 평균 위치를 클러스터 마커 위치로 설정 
	minLevel: 10 // 클러스터 할 최소 지도 레벨 
});

// 데이터를 가져오기 위해 jQuery를 사용합니다
// 데이터를 가져와 마커를 생성하고 클러스터러 객체에 넘겨줍니다
$.get("/download/web/data/chicken.json", function(data) {
	// 데이터에서 좌표 값을 가지고 마커를 표시합니다
	// 마커 클러스터러로 관리할 마커 객체는 생성할 때 지도 객체를 설정하지 않습니다
	var markers = $(data.positions).map(function(i, position) {
		return new daum.maps.Marker({
			position : new daum.maps.LatLng(position.lat, position.lng)
		});
	});

	// 클러스터러에 마커들을 추가합니다
	clusterer.addMarkers(markers);
});
	
// 커스텀 오버레이를 드래그 하기 위해 필요한  
// 드래그 시작좌표, 커스텀 오버레이의 위치좌표를 넣을 변수를 선업합니다
var startX, startY, startOverlayPoint;

// 커스텀 오버레이에 mousedown이벤트를 등록합니다 
addEventHandle(content, 'mousedown', onMouseDown);

// mouseup 이벤트가 일어났을때 mousemove 이벤트를 제거하기 위해
// document에 mouseup 이벤트를 등록합니다 
addEventHandle(document, 'mouseup', onMouseUp);

// 커스텀 오버레이에 mousedown 했을 때 호출되는 핸들러 입니다 
function onMouseDown(e) {
	// 커스텀 오버레이를 드래그 할 때, 내부 텍스트가 영역 선택되는 현상을 막아줍니다.
	if (e.preventDefault) {
		e.preventDefault();
	} else {
		e.returnValue = false;
	}

	var proj = map.getProjection(), // 지도 객체로 부터 화면픽셀좌표, 지도좌표간 변환을 위한 MapProjection 객체를 얻어옵니다 
		overlayPos = customoverlay.getPosition(); // 커스텀 오버레이의 현재 위치를 가져옵니다

	// 커스텀오버레이에서 마우스 관련 이벤트가 발생해도 지도가 움직이지 않도록 합니다
	daum.maps.event.preventMap();

	// mousedown된 좌표를 설정합니다 
	startX = e.clientX; 
	startY = e.clientY;

	// mousedown됐을 때의 커스텀 오버레이의 좌표를
	// 지도 컨테이너내 픽셀 좌표로 변환합니다 
	startOverlayPoint = proj.containerPointFromCoords(overlayPos);

	// document에 mousemove 이벤트를 등록합니다 
	addEventHandle(document, 'mousemove', onMouseMove);       
}

// 커스텀 오버레이에 mousedown 한 상태에서 
// mousemove 하면 호출되는 핸들러 입니다 
function onMouseMove(e) {
	// 커스텀 오버레이를 드래그 할 때, 내부 텍스트가 영역 선택되는 현상을 막아줍니다.
	if (e.preventDefault) {
		e.preventDefault();
	} else {
		e.returnValue = false;
	}

	var proj = map.getProjection(),// 지도 객체로 부터 화면픽셀좌표, 지도좌표간 변환을 위한 MapProjection 객체를 얻어옵니다 
		deltaX = startX - e.clientX, // mousedown한 픽셀좌표에서 mousemove한 좌표를 빼서 실제로 마우스가 이동된 픽셀좌표를 구합니다 
		deltaY = startY - e.clientY,
		// mousedown됐을 때의 커스텀 오버레이의 좌표에 실제로 마우스가 이동된 픽셀좌표를 반영합니다 
		newPoint = new daum.maps.Point(startOverlayPoint.x - deltaX, startOverlayPoint.y - deltaY), 
		// 계산된 픽셀 좌표를 지도 컨테이너에 해당하는 지도 좌표로 변경합니다 
		newPos = proj.coordsFromContainerPoint(newPoint);

	// 커스텀 오버레이의 좌표를 설정합니다 
	customoverlay.setPosition(newPos);
}

// mouseup 했을 때 호출되는 핸들러 입니다 
function onMouseUp(e) {
	// 등록된 mousemove 이벤트 핸들러를 제거합니다 
	removeEventHandle(document, 'mousemove', onMouseMove);
}

// target node에 이벤트 핸들러를 등록하는 함수힙니다  
function addEventHandle(target, type, callback) {
	if (target.addEventListener) {
		target.addEventListener(type, callback);
	} else {
		target.attachEvent('on' + type, callback);
	}
}

// target node에 등록된 이벤트 핸들러를 제거하는 함수힙니다 
function removeEventHandle(target, type, callback) {
	if (target.removeEventListener) {
		target.removeEventListener(type, callback);
	} else {
		target.detachEvent('on' + type, callback);
	}
}
</script>

다음지도 클러스터 마커

위 소스구동시 커스텀 윈도우가 가장 위가 되어야할듯한데 실제로는 마커와 클러스터가 최상단으로 옵니다.

샘플소스에서 "마커 클러스터사용하기"와 “커스텀오버레이를 드래그 하기” 를 사용하여 위 소스를 테스트 했습니다.

첨부해 주신 코드에서 생성 순서로만 보아도 아래 깔리는 게 맞는 것 같은데요.

음… 음… 실례지만 왜 커스텀오버레이가 상단에 와야 할 것이라 생각하시나요?

순서랑 상관없지않나요??

왜 커스텀 오버레이가 하단에 위치하는게 맞다고 생각하시는지요??

마커가 가리키는곳이 기본적으로 맵에 위치해야한다고 생각됩니다.

커스텀 레이아웃 정보가 있는 창은 맵을 가리고 정보를 띄우기때문에 최상단으로 와야하는 거 같은데 잘못된 생각일까요?

<div id="map" style="width:100%;height:350px;"></div>
<script>
var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
	mapOption = { 
		center: new daum.maps.LatLng(37.566826, 126.978656), // 지도의 중심좌표
		level: 3 // 지도의 확대 레벨
	};

// 지도를 표시할 div와  지도 옵션으로  지도를 생성합니다
var map = new daum.maps.Map(mapContainer, mapOption); 
  
 // 마커 클러스터러를 생성합니다 
var clusterer = new daum.maps.MarkerClusterer({
	map: map, // 마커들을 클러스터로 관리하고 표시할 지도 객체 
	averageCenter: true, // 클러스터에 포함된 마커들의 평균 위치를 클러스터 마커 위치로 설정 
	minLevel: 10 // 클러스터 할 최소 지도 레벨 
});

// 데이터를 가져오기 위해 jQuery를 사용합니다
// 데이터를 가져와 마커를 생성하고 클러스터러 객체에 넘겨줍니다
$.get("/download/web/data/chicken.json", function(data) {
	// 데이터에서 좌표 값을 가지고 마커를 표시합니다
	// 마커 클러스터러로 관리할 마커 객체는 생성할 때 지도 객체를 설정하지 않습니다
	var markers = $(data.positions).map(function(i, position) {
		return new daum.maps.Marker({
			position : new daum.maps.LatLng(position.lat, position.lng)
		});
	});

	// 클러스터러에 마커들을 추가합니다
	clusterer.addMarkers(markers);
});
// 커스텀 오버레이 엘리먼트를 만들고, 컨텐츠를 추가합니다
var content = document.createElement('div');
content.className = 'overlay';
content.innerHTML = '드래그 해주세요 :D';


// 커스텀 오버레이를 생성합니다 
var customoverlay = new daum.maps.CustomOverlay({
	map: map,
	content: content,
	position: new daum.maps.LatLng(37.566826, 126.978656)
});

// 커스텀 오버레이를 드래그 하기 위해 필요한  
// 드래그 시작좌표, 커스텀 오버레이의 위치좌표를 넣을 변수를 선업합니다
var startX, startY, startOverlayPoint;

// 커스텀 오버레이에 mousedown이벤트를 등록합니다 
addEventHandle(content, 'mousedown', onMouseDown);

// mouseup 이벤트가 일어났을때 mousemove 이벤트를 제거하기 위해
// document에 mouseup 이벤트를 등록합니다 
addEventHandle(document, 'mouseup', onMouseUp);

// 커스텀 오버레이에 mousedown 했을 때 호출되는 핸들러 입니다 
function onMouseDown(e) {
	// 커스텀 오버레이를 드래그 할 때, 내부 텍스트가 영역 선택되는 현상을 막아줍니다.
	if (e.preventDefault) {
		e.preventDefault();
	} else {
		e.returnValue = false;
	}

	var proj = map.getProjection(), // 지도 객체로 부터 화면픽셀좌표, 지도좌표간 변환을 위한 MapProjection 객체를 얻어옵니다 
		overlayPos = customoverlay.getPosition(); // 커스텀 오버레이의 현재 위치를 가져옵니다

	// 커스텀오버레이에서 마우스 관련 이벤트가 발생해도 지도가 움직이지 않도록 합니다
	daum.maps.event.preventMap();

	// mousedown된 좌표를 설정합니다 
	startX = e.clientX; 
	startY = e.clientY;

	// mousedown됐을 때의 커스텀 오버레이의 좌표를
	// 지도 컨테이너내 픽셀 좌표로 변환합니다 
	startOverlayPoint = proj.containerPointFromCoords(overlayPos);

	// document에 mousemove 이벤트를 등록합니다 
	addEventHandle(document, 'mousemove', onMouseMove);       
}

// 커스텀 오버레이에 mousedown 한 상태에서 
// mousemove 하면 호출되는 핸들러 입니다 
function onMouseMove(e) {
	// 커스텀 오버레이를 드래그 할 때, 내부 텍스트가 영역 선택되는 현상을 막아줍니다.
	if (e.preventDefault) {
		e.preventDefault();
	} else {
		e.returnValue = false;
	}

	var proj = map.getProjection(),// 지도 객체로 부터 화면픽셀좌표, 지도좌표간 변환을 위한 MapProjection 객체를 얻어옵니다 
		deltaX = startX - e.clientX, // mousedown한 픽셀좌표에서 mousemove한 좌표를 빼서 실제로 마우스가 이동된 픽셀좌표를 구합니다 
		deltaY = startY - e.clientY,
		// mousedown됐을 때의 커스텀 오버레이의 좌표에 실제로 마우스가 이동된 픽셀좌표를 반영합니다 
		newPoint = new daum.maps.Point(startOverlayPoint.x - deltaX, startOverlayPoint.y - deltaY), 
		// 계산된 픽셀 좌표를 지도 컨테이너에 해당하는 지도 좌표로 변경합니다 
		newPos = proj.coordsFromContainerPoint(newPoint);

	// 커스텀 오버레이의 좌표를 설정합니다 
	customoverlay.setPosition(newPos);
}

// mouseup 했을 때 호출되는 핸들러 입니다 
function onMouseUp(e) {
	// 등록된 mousemove 이벤트 핸들러를 제거합니다 
	removeEventHandle(document, 'mousemove', onMouseMove);
}

// target node에 이벤트 핸들러를 등록하는 함수힙니다  
function addEventHandle(target, type, callback) {
	if (target.addEventListener) {
		target.addEventListener(type, callback);
	} else {
		target.attachEvent('on' + type, callback);
	}
}

// target node에 등록된 이벤트 핸들러를 제거하는 함수힙니다 
function removeEventHandle(target, type, callback) {
	if (target.removeEventListener) {
		target.removeEventListener(type, callback);
	} else {
		target.detachEvent('on' + type, callback);
	}
}
	
  
	
</script>

위 코드에서 별 다른 예외처리를 하신게 없다면…
생성 순서는 클러스터 마커와 그 클러스터 객체들이 무조건 나중에 그려집니다.

각 컴포넌트 생성 코드의 위치가 무엇이 먼저 나오든 상관 없이
문맥상 $.get()이라는 비동기 로직 이후에
콜백에서 클러스터가 만들어지기 때문입니다.

좀 더 상세하게 말씀드리면, 데이터를 받아와 클러스터가 생성되는 시점이 addMarkers() 함수를 호출하는 곳인데 그게 콜백 안에 있어서, 콜백 밖에 있는 CustomOverlay 생성 코드는 어디에 위치해 있든 먼저 호출되며 먼저 DOM Tree에 추가됩니다.

동일 depth상에 absolute로 설정된 애들이지만
각 컴포넌트들은 z-index가 설정되지 않았기 때문에
DOM 순서상 먼저 추가된 CustomOverlay가 아래에 깔리는 것이고요.

단순히 CustomOverlay를 위에 띄우고 싶으시다면 2가지 방법이 있습니다.

  1. CustomOverlay 생성 옵션에 zIndex: XX (0보다 큰 값)을 넣어서 다른 것들 위로 올리시거나,
  2. CustomOverlay 생성 옵션에 map: map, 줄을 빼시고
    콜백에서 clusterer.addMarkers(markers); 이 후에 customoverlay.setMap(map); 을 넣어주세요.

제가 되물은 이유는 질문자분께서 명확하게 무얼 하고 싶으신건지,
아니면 버그를 제보하신건지 알 수 없어서 였습니다.
양해 부탁드립니다.

아… 용도에 따른 레이어 순서를 말씀하시는거군요.

이는 모든 사용자의 니즈를 맞출 수 없기 때문에
의미상으로 그렇게 레이어 순서를 정할 수 없어요 ㅠ

순서를 컨트롤할 수 있는 기능만 넣어두고 나머지는 모두 브라우저의 렌더링 순서에 의존하도록 하는 것이 최선일 듯 하네요. 이건 뭐 정답이 없는 문제 같네요 ^^;

답변 감사합니다.

버그로 보여서 제보했습니다.

말씀대로 1,2번 하면 아래와 같이 해봤습니다.

맵의 확대 축소 시 버그로 보입니다.

<div id="map" style="width:100%;height:350px;"></div>
<script>
var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
	mapOption = { 
		center: new daum.maps.LatLng(37.566826, 126.978656), // 지도의 중심좌표
		level: 3 // 지도의 확대 레벨
	};

// 지도를 표시할 div와  지도 옵션으로  지도를 생성합니다
var map = new daum.maps.Map(mapContainer, mapOption); 
  // 커스텀 오버레이 엘리먼트를 만들고, 컨텐츠를 추가합니다
var content = document.createElement('div');
content.className = 'overlay';
content.innerHTML = '드래그 해주세요 :D';


// 커스텀 오버레이를 생성합니다 
var customoverlay = new daum.maps.CustomOverlay({
	map: map,
	content: content,
	position: new daum.maps.LatLng(37.566826, 126.978656)
});

 // 마커 클러스터러를 생성합니다 
var clusterer = new daum.maps.MarkerClusterer({
	map: map, // 마커들을 클러스터로 관리하고 표시할 지도 객체 
	averageCenter: true, // 클러스터에 포함된 마커들의 평균 위치를 클러스터 마커 위치로 설정 
	minLevel: 10 // 클러스터 할 최소 지도 레벨 
});

// 데이터를 가져오기 위해 jQuery를 사용합니다
// 데이터를 가져와 마커를 생성하고 클러스터러 객체에 넘겨줍니다
$.get("/download/web/data/chicken.json", function(data) {
	// 데이터에서 좌표 값을 가지고 마커를 표시합니다
	// 마커 클러스터러로 관리할 마커 객체는 생성할 때 지도 객체를 설정하지 않습니다
	var markers = $(data.positions).map(function(i, position) {
		return new daum.maps.Marker({
			position : new daum.maps.LatLng(position.lat, position.lng)
		});
	});

	// 클러스터러에 마커들을 추가합니다
	clusterer.addMarkers(markers);
	//커스텀 레이아웃 추가
	customoverlay.setMap(map); 
});
  
	
</script>	

아래는 css 기본으로 제공하는 css에 zindex 추가했습니다.
.overlay {
position:absolute;
left: -50px;
top:0;
width:100px;
height: 100px;
background: #fff;
border:1px solid #ccc;
border-radius: 5px;
padding:5px;
font-size:12px;
text-align: center;
white-space: pre;
word-wrap: break-word;
z-index:99999999;
}

현재 기본 마커로 생성하면 버그 없습니다.

<div id="map" style="width:100%;height:350px;"></div>
<script>
var mapContainer = document.getElementById('map'), // 지도를 표시할 div 
	mapOption = { 
		center: new daum.maps.LatLng(37.566826, 126.978656), // 지도의 중심좌표
		level: 3 // 지도의 확대 레벨
	};

// 지도를 표시할 div와  지도 옵션으로  지도를 생성합니다
var map = new daum.maps.Map(mapContainer, mapOption); 
  // 커스텀 오버레이 엘리먼트를 만들고, 컨텐츠를 추가합니다
var content = document.createElement('div');
content.className = 'overlay';
content.innerHTML = '드래그 해주세요 :D';


// 커스텀 오버레이를 생성합니다 
var customoverlay = new daum.maps.CustomOverlay({
  
	content: content,
	position: new daum.maps.LatLng(37.566826, 126.978656)
});



// 데이터를 가져오기 위해 jQuery를 사용합니다
// 데이터를 가져와 마커를 생성하고 클러스터러 객체에 넘겨줍니다
$.get("/download/web/data/chicken.json", function(data) {
	// 데이터에서 좌표 값을 가지고 마커를 표시합니다
	// 마커 클러스터러로 관리할 마커 객체는 생성할 때 지도 객체를 설정하지 않습니다
	var markers = $(data.positions).map(function(i, position) {
		return new daum.maps.Marker({
			map: map, // 마커들을 클러스터로 관리하고 표시할 지도 객체 
			position : new daum.maps.LatLng(position.lat, position.lng)
		});
	});

	// 클러스터러에 마커들을 추가합니다
	//clusterer.addMarkers(markers);
	//커스텀 레이아웃 추가
	customoverlay.setMap(map); 
});
  
	
</script>

보기엔 클러스터를 추가하면 마커가 최상위로 와서 문제시 되는듯합니다.

http://apis.map.daum.net/web/documentation/#CustomOverlay

zIndex는 CustomOverlay의 생성 옵션으로 넣어야 합니다.
CustomOverlay가 기본적으로 하나의 wrapper element를 사용하고 있고 그 안에 사용자 content를 넣는 구조이기 때문에 wrapper element에 직접 속성을 부여해야만 해요.

사용자 element에 z-index를 부여하는 것은 이미 하위 depth이므로 다른 상위 depth 객체들과 비교 대상이 되지 않습니다.

말씀하신대로 하니까 원하는대로 동작합니다.

감사합니다.

1개의 좋아요