logo

𝝅번째 알파카의 개발 낙서장

OpenLayers를 여행하는 개발자를 위한 안내서 - 18. WFS에 팝업 붙이기

프로젝트
⏰ 2022-05-25 14:00:06

D O W N

https://user-images.githubusercontent.com/50317129/156607880-c5abad92-1991-4c01-b85f-7153bf89cb64.png
OpenLayers를 여행하는 개발자를 위한 안내서
이 게시글은 OpenLayers를 여행하는 개발자를 위한 안내서 시리즈의 26개 중 18번 째 게시글입니다.
https://user-images.githubusercontent.com/50317129/260317030-e4b8575b-f09e-47f4-ab70-168a817268c6.png

Table of Contents

https://user-images.githubusercontent.com/50317129/260317030-e4b8575b-f09e-47f4-ab70-168a817268c6.png

개요

지도에 표시된 마커 혹은 객체를 클릭하면, 팝업을 통해 해당 객체의 자세한 정보를 보여준다. 이 장에서는 WFS 지도에 팝업을 출력하여 마커의 세부 정보를 표현해본다.




Overlay

🔗 17장에서 다룬 WFS 지도를 그대로 사용한다. Select 객체가 포함되어 있으므로, 사용자로 하여금 시각적으로 상호작용이 가능하다는 걸 보여줄 수 있을 것이다.

OpenLayers는 Overlay 객체를 통해 지도 위에 원하는 HTML 태그를 띄울 수 있다. OpenLayers가 동작을 담당하긴 하지만, canvas 위에서 렌더링되는 객체는 아니고, 실제 DOM을 출력시켜준다.

Overlay는 기본적으로 지도 상에서 클릭한 위치를 따라간다. 즉, 지도의 특정 위치에서 Overlay를 띄우고 지도를 움직이면, 위치가 고정되지 않고 마커를 따라간다. 이런 동작을 실제 DOM에서 관리하려면 매우 귀찮은 핸들링이 필요할 것이다. 이러한 특징 덕분에 팝업과 매우 잘 어울리는 객체다.



1. Overlay 생성하기

Overlay 객체를 직접 생성해보자.

ParameterTypeDefaultDescription
idnumber | string | undefined오버레이 아이디
🔗 ol/Map-Map#getOverlayById 메서드 호출 시 사용됨
elementHTMLElement | undefined오버레이 대상 Element
offsetArray<number>[ 0, 0 ]오버레이 출력의 오프셋(px)
[ x, y ]이며, x는 좌우, y는 상하를 의미한다.
각각 값이 커질 수록 우측, 아래로 이동한다.
position🔗 ol/events/condition-Condition | undefined오버레이의 출력 위치
positioning🔗 ol/OverlayPositioningtop-left오버레이 배치 기준
bottom-left, top-right 등의 값을 가진다.
stopEventbooleantrue이벤트 전파 여부
true일 경우 클래스가 ol-overlay container-stopevent인 DOM에 배치
false일 경우 className 속성에 지정된 값을 클래스로 가진 DOM에 배치
insertFirstbooleantrue오버레이를 엘리먼트에 먼저 삽입할 지, 추가할 지 선택
autoPan🔗 ol/Overlay-PanIntoViewOptions | booleanfalse오버레이 setPosition 호출 시, 오버레이가 완전히 보이도록 지도 자동 이동
autoPanAnimation🔗 ol/Overlay-PanOptionsautoPan 활성화로 인한 이동 시 애니메이션 설정.
autoPan 설정이 boolean이 아닌 객체일 경우 해당 설정은 무시됨
autoPanMarginnumber20autoPan 활성화로 인한 이동 시 오버레이와 지도 테두리 사이의 여백
autoPan 설정이 boolean이 아닌 객체일 경우 해당 설정은 무시됨
autoPanOptions🔗 ol/Overlay-PanIntoViewOptions | undefinedautoPan의 옵션
autoPanAnimation, autoPanMargin 보다 우선 시 됨
classNamestringol-overlay-container ol-selectableCSS 클래스 값

생성 방법은 아래와 같다.

HTML

1
<div id="map-popup"></div>

팝업 시 사용할 DOM을 입력한다. DOM을 특정하기 위해 id 혹은 class 속성을 적절히 활용하자. HTML 코드 상 태그의 위치는 크게 상관없다.

TYPESCRIPT

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Overlay } from 'ol';

const popup = document.getElementById('map-popup') as HTMLElement | null;

const overlay = new Overlay({
	id: 'popup',
	element: popup || undefined,
	positioning: 'center-center',
	autoPan: {
		animation: {
			duration: 250
		}
	}
});

태그를 할당하고 적절히 설정하면 Overlay 객체를 생성할 수 있다.

Overlay에 대한 전체 정보는 🔗 공식 문서에서 확인할 수 있다.



2. Map에 적용하기

생성한 OverlayMap에 적용시켜본다.

TYPESCRIPT

1
2
3
4
5
6
7
import { Map } from 'ol';

const map = new Map({
	// ...
	overlays: [ overlay ]
	// ...
});

위와 같이 적용이 가능하다. 배열의 형태로 Overlay를 등록할 수 있다.

TYPESCRIPT

1
2
3
4
5
6
7
8
9
10
11
// 오버레이 추가
map.addOverlay();

// 아이디별 오버레이 반환
map.getOverlayById();

// 오버레이 리스트 반환
map.getOverlays();

// 오버레이 제거
map.removeOverlay();

관련 메서드는 위와 같다. 생성된 Map 객체에서 호출 가능하다.



3. Overlay 이벤트 적용하기

오버레이를 등록했으니, 적절한 이벤트 핸들링을 통해 오버레이를 띄우고, 원하는 데이터를 보여줄 수 있을 것이다.

TSX

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
map.on('singleclick', (e) =>
{
	// 해당 픽셀에 객체가 있을 경우
	if (map.hasFeatureAtPixel(e.pixel))
	{
		map.forEachFeatureAtPixel(e.pixel, feature =>
		{
			// 해당 객체의 아이디가 buld_sejong으로 시작할 경우
			if (feature.getId()?.toString().startsWith('buld_sejong'))
			{
				const geom = feature.getGeometry();

				// 공간정보가 유효할 경우
				if (geom)
				{
					const [ minX, minY, maxX, maxY ] = geom.getExtent();

					setPopupState((
						<ul>
							<li>{feature.getId() || ''}</li>
							<li>{feature.get('buld_nm') || <span>이름 없음</span>}</li>
							<li>{feature.get('bul_man_no')}</li>
						</ul>
					));

					overlay.setPosition([ (maxX + minX) / 2, (maxY + minY) / 2 ]);
				}
			}
		});
	}

	// 없을 경우
	else
	{
		overlay.setPosition(undefined);
	}
});

위와 같이 클릭 이벤트를 적절히 활용한다.

  1. 클릭 시, 해당 픽셀에 Feature가 있는지 hasFeatureAtPixel 메서드로 확인한다.
    1. 없다면 overlay.setPosition(undefined)으로 오버레이를 숨긴다.
  2. forEachFeatureAtPixel 메서드로 해당 픽셀에 위치한 모든 Feature를 불러온다.
  3. 그 중 우리에게 필요한 Feature의 데이터를 확인한다.
    1. 본문에서는 Feature의 아이디가 buld_sejong로 시작되는 것들이 대상임.
  4. 원하는 데이터를 DOM에 표시한다.
    1. 본문에서는 상태 기반의 데이터 관리를 사용한다.
  5. feature.getGeometry()로 지오메트리 정보를 호출하여 오버레이의 위치를 계산한다.
    1. 본문에서는 Feature 영역의 센터값을 사용
  6. overlay.setPosition([ x, y ])의 형태로 원하는 위치에 오버레이 출력

위와 같은 방식으로 로직이 진행된다. 물론 어디까지나 사용의 한 예시이므로, 이벤트에 원하는 동작을 기술하여 다양한 동작을 수행할 수 있다.


3-1. Feature에 커서 표시하기

번외로, Feature에 마우스 포인터를 호버링할 경우, 마우스 커서 모양을 pointer로 지정하여 사용자로 하여금 상호작용이 가능하다는 것을 UI로 표현해줄 수 있다.

TYPESCRIPT

1
map.on('pointermove', (e) => map.getViewport().style.cursor = map.hasFeatureAtPixel(e.pixel) ? 'pointer' : '');

pointermove 이벤트를 통해, 현재 픽셀에 Feature가 하나라도 있을 경우 커서 CSS를 pointer로 변경한다.




예제 확인하기

🔗 OpenLayers6 Sandbox - WFS Popup에서 이를 구현한 예제를 확인할 수 있다.

🏷️ Related Tag

# GIS
# GeoServer
# OpenLayers
# WFS

😍 읽어주셔서 감사합니다!
도움이 되셨다면, 💝공감이나 🗨️댓글을 달아주시는 건 어떤가요?
블로그 운영에 큰 힘이 됩니다!
https://blog.itcode.dev/projects/2022/05/25/gis-guide-for-programmer-18