OpenLayers를 여행하는 개발자를 위한 안내서 - 16. WMS GetImage를 사용하여 지도에 이미지 표시하기

⏰ 2022-05-16 (월) 02:13:10

screener
시리즈 모아보기
OpenLayers를 여행하는 개발자를 위한 안내서

16 / 26

Table of Contents

  • 1. WMS










WMS

이 장에선 WFS를 통해 지도에 이미지를 표시하는 방법에 대해 다룬다.

이전 장의 WFS는 공간정보 데이터를 GeoJSON으로 받아 직접 객체로 표시하지만, WMS는 객체를 GeoServer에서 이미지로 렌더링한 이미지를 받아 표시한다.

즉, GeoServer로 부터 직접 Tile Map을 받아 표현한다고 생각하면 된다.




WMS를 활용하여 지도에 표시하기

WMS를 표시하기 위해, 총 4개 객체가 필요하다. WMS는 이미지를 받아 표시하므로, 이 이미지를 담을 ImageWMS, 이를 활용하여 지도에 렌더링하는 ImageLayer. 나머지 ViewMap 객체가 필요하다

이 4가지 요소를 구현하는 방법을 차례로 설명하여, 최종적으로 WMS를 활용한 지도를 만든다.



1. GetImage URL 구성하기

GeoServer를 통해 데이터를 구축했던 데이터를 통해 WMS 이미지를 호출한다.

WMS 중에서도, 속성정보를 제공하는 GetImage를 사용한다. GetImage의 요청방법은 아래와 같다.

TXT

1
GET https://example.com/geoserver/wms?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&FORMAT=image%2Fpng&TRANSPARENT=true&layers=test:building&exceptions=application%2Fjson&WIDTH=256&HEIGHT=256&CRS=EPSG%3A3857&STYLES=&BBOX=14168061.814827133%2C4367306.048101831%2C14168367.562940273%2C4367611.796214972
ParameterExampleRequireDescription
serviceWMS (고정)Y서비스명
version1.3.0 (고정), 1.1.1, 1.1.0, 1.0.0Y버전
requestGetMap (고정)Y요청명
layersrepo_name:layer_nameY레이어명 (다수는 쉼표로 구분)
stylesstyle1적용할 스타일명 (비울 경우 GeoServer에서 설정한 기본 스타일 적용, 다수는 쉼표로 구분)
srs(or crs)EPSG:4326기준 좌표계 (비울 경우 레이어의 기본 좌표계로 인식)
bboxxmin,ymin,xmax,ymaxx_{min},y_{min},x_{max},y_{max}Y이미지 영역 좌표
width256Y이미지 넓이
height256Y이미지 높이
formatimage/pngY요청명
transparentfalse (기본)배경 투명 여부
bgcolorFFFFFF (기본)RRGGBB 형태의 배경 색상
exceptionsapplication/vnd.ogc.se_xml (기본)예외 응답 형식
time2022-03-14T22:30.27.520+09:00시계열 데이터를 위한 시간 (yyyy-MM-ddThh:mm:ss.SSSZ)
sld🔗 https://example.com/sld.xmlXML 파일 경로
sld_bodySLD XML

하지만 WMS의 URL, WFS에 비해 요구하는 파라미터의 갯수가 많아 다소 복잡하다. WFS에선, VectorSource에 직접 URL을 입력했지만, WMS의 소스 객체인 ImageWMS의 경우 몇 가지 필요한 값을 입력하면 알아서 URL을 만들어 호출해준다.

그냥 WMS는 이런 방식으로 직접 호출한다고만 알고 넘어가자.



2. ImageWMS 생성하기

OpenLayers의 ImageWMS 객체는 주어진 값을 통해 WMS URL을 생성해 호출한다.

TYPESCRIPT

1
2
3
4
5
6
7
8
9
10
import { ImageWMS } from 'ol/source';

const source = new ImageWMS({
	url: 'https://example.com/geoserver/wms',
	params: {
		layers: 'test:building',
		exceptions: 'application/json'
	},
	serverType: 'geoserver'
});

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

설정의 세부 내용은 아래와 같다.

NameTypeDefaultDescription
attributions🔗 ol/source/Source-AttributionLike | undefined기여 문구 (지도 우측 하단)
crossOriginnull | string | undefined이미지의 CORS 속성 (🔗 참조)
hidpibooleantrue원격 서버에 WMS를 요청할 때, Map 객체의 pixelRatio 값을 사용
serverType🔗 ol/source/WMSServerType | string | undefinedWMS 서버의 타입 (mapserver, geoserver, qgis 등)
hidpitrue일 때만 필요
imageLoadFunction🔗 ol/Image-LoadFunction | undefinedtrueWMS URL의 이미지를 로드 메서드
WMS 호출 메서드를 오버라이딩할 때 사용함
imageSmoothingbooleantrueDeprecated된 속성으로, interpolate를 사용하길 권고함
interpolatebooleantrue리샘플링 시 보간 값 사용 여부
paramsobjectWMS 요청 파라미터. 반드시 하나 이상의 LAYERS를 입력해야 함
STYLES는 기본적으로 ''로 빈 값을 가짐
VERSION1.3.0을 기본값으로 가짐
WIDTH, HEIGHT, BOX, CRS(SRS)는 동적으로 설정됨
projection🔗 ol/proj-ProjectionLike | undefined프로젝션 객체. MapView 객체에 선언된 projection을 기본값으로 함
rationumber1.5이미지 요청 시 사용할 뷰포트의 크기. 1은 맵 뷰포트와 동일하며, 2는 맵 뷰포트가 가진 폭, 높이의 두배를 의미함
반드시 1 이상의 값을 가져야함
resolutionsArray<number> | undefined해상도. 특정 해상도를 지정할 경우, 지정된 해상도에서만 호출이 발생함
urlstringWMS URL

url, params.layers는 반드시 입력해야 정상적인 WMS 호출을 수행할 수 있다. 위 두 값만 입력하면, WMS에 필요한 나머지 파라미터는 ImageWMS에서 알아서 계산해 넣어준다.



3. TileLayer 생성하기

OpenLayers의 TileLayer 객체는 ImageWMS로 호출한 이미지를 통해 지도를 렌더링한다. 이 지도는 배경지도와 같은 이미지로 이루어져있다.

TYPESCRIPT

1
2
3
4
5
6
7
8
import ImageLayer from 'ol/layer/Image';

const layer = new TileLayer({
	source: source,
	minZoom: 15,
	properties: { name: 'wms' },
	zIndex: 5
});
NameTypeDefaultDescription
classNamestringol-layer클래스명
opacitynumber1투명도 (0 ~ 1)
visiblebooleantrue표시 여부
extent🔗 ol/extent-Extent | undefined레이어의 렌더링 범위. 해당 범위를 넘어가면 데이터를 표시하지 않음
zIndexnumber | undefined우선 순위 (높을수록 위에 표시)
minResolutionnumber | undefined최소 표시 해상도
maxResolutionnumber | undefined최대 표시 해상도
minZoomnumber | undefined최소 표시 줌 레벨
maxZoomnumber | undefined최대 표시 줌 레벨
map🔗 ol/PluggableMap-PluggableMap | undefined지정한 Map 객체에서 해당 레이어를 오버레이로 사용
source(🔗 ol/source/Image-ImageSource | 🔗 ol/source/VectorTile-VectorTile) | undefined레이어의 소스
propertiesobject | undefined임의 속성. get(), set()으로 조작 가능

ImageLayer에 대한 전체 정보는 🔗 ol/layer/Image-ImageLayer에서 확인할 수 있다.



4. View 만들기

지도의 뷰잉 정보를 선언할 View 객체를 생성한다.

TYPESCRIPT

1
2
3
4
5
6
7
8
import View from 'ol/View';
import proj4 from 'proj4';

const view = new View({
	projection: 'EPSG:3857',
	center: proj4('EPSG:4326', 'EPSG:3857', [ 127.28923267492068, 36.48024986578043 ]),
	zoom: 17
});
NameTypeDefaultDescription
center🔗 ol/coordinate-Coordinate | undefined지도의 중심
constrainRotationboolean | numbertrue회전 구속 여부. 숫자일 경우 회전 가능 갯수를 의미 (0일 경우, 90, 180, 270, 360)
enableRotationbooleantrue회전 가능 여부
extent🔗 ol/extent-Extent | undefined지도의 뷰잉 범위. 지정된 범위 밖을 벗어날 수 없음
constrainOnlyCenterbooleanfalsetrue일 경우 extent 제한이 View 중심에만 적용되며, 전체 extent에 적용되지 않음
smoothExtentConstraintbooleantrueView가 extent 범위를 약간 벗어날 수 있는지 여부
maxResolutionnumber | undefined최대 뷰잉 해상도. 지정 해상도 이상 확대 불가능.
minResolutionnumber | undefined최소 뷰잉 해상도. 지정 해상도 이상 축소 불가능.
maxZoomnumber28최대 뷰잉 줌 레벨. 지정 줌 레벨 이상 확대 불가능.
minZoomnumber0최소 뷰잉 줌 레벨. 지정 줌 레벨 이상 축소 불가능.
multiWorldbooleanfalse다중 월드 사용 여부
constrainResolutionbooleanfalse줌 레벨 정수만 허용 여부
smoothResolutionConstraintbooleantrue느슨한 확대/축소 규칙 사용 여부
showFullExtentbooleanfalse전체 구성된 extent 표시 여부
projection🔗 ol/proj-ProjectionLikeEPSG:3857좌표계
resolutionnumber | undefined초기 해상도
resolutionsArray<number> | undefined사용 가능한 해상도 목록 (내림차순) max/minResolution, max/minZoom, zoomFactor 옵션이 무시됨
rotationnumber0기본 회전값
zoomnumber | undefined기본 줌 레벨
zoomFactornumber2줌 배율
paddingArray<number>[ 0, 0, 0, 0 ]패딩


5. Style 정의하기

WMS 역시 이미지에 그려지는 요소를 스타일링할 수 있다.

WFS는 스타일 객체를 코드 내부에서 기술할 수 있었지만, WMS는 기본적으로 GeoServer와 같은 백엔드 영역에서 이루어지므로, 해당 서버에서 직접 스타일을 기술해준다.

보통 XML 형태로 기술하며, 이를 SLD라 부른다.


GeoServer의 경우, [스타일] 메뉴에서 관리할 수 있다. 별다른 조작을 하지 않았다면, 즉시 사용 가능한 몇 가지 SLD가 기본으로 탑재된다.

레이어 추가 시 [발행] 단계에서 스타일 설정을 할 수 있는데, 이 때 설정하는 것이 WMS에 사용할 SLD다.

여러개를 등록할 수도 있으며, 이 경우 STYLES에 명시된 이름으로 원하는 스타일을 호출할 수 있다. STYLES의 기본값은 ''로 비어있으며, 이 경우 지정된 기본 스타일을 사용하여 이미지를 렌더링한다.

XML

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
<?xml version="1.0" encoding="UTF-8"?>
<StyledLayerDescriptor version="1.0.0" 
 xsi:schemaLocation="http://www.opengis.net/sld StyledLayerDescriptor.xsd" 
 xmlns="http://www.opengis.net/sld" 
 xmlns:ogc="http://www.opengis.net/ogc" 
 xmlns:xlink="http://www.w3.org/1999/xlink" 
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <!-- a Named Layer is the basic building block of an SLD document -->
  <NamedLayer>
    <Name>default_polygon</Name>
    <UserStyle>
    <!-- Styles can have names, titles and abstracts -->
      <Title>Default Polygon</Title>
      <Abstract>A sample style that draws a polygon</Abstract>
      <!-- FeatureTypeStyles describe how to render different features -->
      <!-- A FeatureTypeStyle for rendering polygons -->
      <FeatureTypeStyle>
        <Rule>
          <Name>rule1</Name>
          <Title>Gray Polygon with Black Outline</Title>
          <Abstract>A polygon with a gray fill and a 1 pixel black outline</Abstract>
          <PolygonSymbolizer>
            <Fill>
              <CssParameter name="fill">#ED143D</CssParameter>
              <CssParameter name="opacity">0.6</CssParameter>
            </Fill>
            <Stroke>
              <CssParameter name="stroke">#ED143d</CssParameter>
              <CssParameter name="stroke-width">2</CssParameter>
            </Stroke>
          </PolygonSymbolizer>
          <TextSymbolizer>
            	  <Geometry>
	    <ogc:Function name="centroid">
	      <ogc:PropertyName>SHAPE</ogc:PropertyName>
	    </ogc:Function>
	  </Geometry>
            <Label>
              <ogc:PropertyName>buld_nm</ogc:PropertyName>
            </Label>
            <Font>
              <CssParameter name="font-family">sans-serif</CssParameter>
              <CssParameter name="font-size">16</CssParameter>
            </Font>
            <LabelPlacement>
              <PointPlacement>
                <AnchorPoint>
                  <AnchorPointX>0.5</AnchorPointX>
                  <AnchorPointY>0.5</AnchorPointY>
                </AnchorPoint>
                <Displacement>
                  <DisplacementX>0</DisplacementX>
                  <DisplacementY>0</DisplacementY>
                </Displacement>
              </PointPlacement>
            </LabelPlacement>
            <Halo>
              <Radius>
                <ogc:Literal>2</ogc:Literal>
              </Radius>
              <Fill>
                <CssParameter name="fill">#000000</CssParameter>
              </Fill>
            </Halo>
            <Fill>
              <CssParameter name="fill">#FFFFFF</CssParameter>
            </Fill>
          </TextSymbolizer>
        </Rule>
      </FeatureTypeStyle>
    </UserStyle>
  </NamedLayer>
</StyledLayerDescriptor>

위 SLD는 실제로 프로젝트의 WMS 요청에서 사용하는 SLD다. WFS에서 스타일 기술에 대한 문단을 읽어봤다면 이해가 더욱 쉬울 것이다. XML의 특성 상 복잡해보이지만, 잘 뜯어보면 별거 없다.

마찬가지로 PointPolygon 등, 데이터 형식에 따라 기술되는 형태가 조금씩 다르다.


굳이 GeoServer에 SLD를 기술하지 않아도 원하는 디자인을 사용할 수 있는 방법이 있는데, WMS의 요청 파라미터 중 sld_body를 활용하면 가능하다.

sld_body에 SLD를 직접 입력하면, 해당 SLD를 우선으로 적용한다. 파라미터의 내용은 SLD 그대로를 입력한다.



6. Map 만들기

모든 정보를 종합하여 지도를 만드는 Map 객체를 생성한다.

TYPESCRIPT

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
import Map from 'ol/Map';
import { ImageWMS } from 'ol/source';
import ImageLayer from 'ol/layer/Image';
import View from 'ol/View';
import proj4 from 'proj4';

// WMS 소스 객체
const source = new ImageWMS({
	url: 'https://example.com/geoserver/wms',
	params: {
		layers: 'test:building',
		exceptions: 'application/json'
	},
	serverType: 'geoserver'
});

// WMS 레이어 객체
const layer = new ImageLayer({
	source: source,
	minZoom: 15,
	properties: { name: 'wms' },
	zIndex: 5
});

// 뷰 객체
const view = new View({
	projection: 'EPSG:3857',
	center: proj4('EPSG:4326', 'EPSG:3857', [ 127.28923267492068, 36.48024986578043 ]),
	zoom: 17
});

// 맵 객체
const map = new Map({
	layers: [ vworldBaseLayer, vworldHybridLayer, wfsLayer ],
	target: 'map',
	view: view
});
NameTypeDefaultDescription
controls🔗 ol/Collection-Collection<🔗 ol/control/Control-Control> | Array<🔗 ol/control/Control-Control> | undefined🔗 ol/control/defaults지도 컨트롤 객체
pixelRationumberwindow.devicePixelRatio기기 픽셀 비율
interactions🔗 ol/Collection-Collection<🔗 ol/interaction/Interaction-Interaction> | Array<🔗 ol/interaction/Interaction-Interaction> | undefined
keyboardEventTargetHTMLElement | Document | string | undefined키보드 이벤트 대상 요소
layersArray<🔗 ol/layer/Base-BaseLayer> | 🔗 ol/Collection-Collection<🔗 ol/layer/Base-BaseLayer> | 🔗 ol/layer/Group-LayerGroup | undefined레이어 목록. 배열 뒤에 있을 수록 우선순위가 높아짐
maxTilesLoadingnumber16동시 로드 가능한 최대 타일 수
moveTolerancenumber1지도 이동 이벤트로 인식하기 위해 마우스가 움직여야할 최소 픽셀
overlays🔗 ol/Collection-Collection<🔗 ol/Overlay-Overlay> | Array<🔗 ol/Overlay-Overlay> | undefined지도 오버레이 객체
targetHTMLElement | string | undefined지도를 표시할 DOM 혹은 DOM 아이디
view🔗 ol/View-View | Promise<🔗 ol/View-View> | undefined지도 뷰 객체

Map 객체에 지금까지 선언한 객체들을 할당한다. target에 지정된 DOM에 선언된 지도가 표시된다.

target: map은 아이디가 map인 DOM에 지도를 표시한다는 뜻이다. 꼭 아이디가 아니더라도 HTMLElement를 할당할 수도 있다.

WMS로 호출한 이미지가 지도에 표시되는 것을 확인할 수 있다.



6-1. WMS 호출 방식

OpenLayers에서 제공하는 WMS 호출 방식에는 두 가지가 존재한다. 현재 영역의 전체 이미지를 불러오는 Image 방식과, 여러 격자로 쪼개서 불러오는 Tile 방식이 있다.

이 장에서는 현재 영역의 전체 이미지를 호출하는 Image 방식을 기술했다.

만약, 타일 형태로 WMS를 호출하고 싶다면, TileWMSTileLayer를 사용하면 된다. 사용법은 동일하다. 이 장에서 설명한 코드를 기준으로 ImageWMSTileWMS로 바꾸기만 해도 이상이 없는 수준. TileLayer도 마찬가지.


두 방식의 차이를 도식화하면 위 그림과 같다. 배경지도 또한 TileWMS 방식을 사용한다.

  • Image 방식

    • WMS 호출이 한 번만 가므로, 요청 수를 줄일 수 있다.
    • 응답 하나의 용량이 상대적으로 크며, 속도가 느리다.
  • Tile 방식

    • Image 방식 대비 WMS 호출 요청이 훨씬 많아진다.
    • 작은 이미지를 여러개 호출하므로, 상대적으로 속도가 빠르다.

차이를 확인하고, 자신의 서비스에 더 적합한 방식을 채택하면 된다.

배경 지도 또한 일종의 TileWMS와 같다.




예제 확인하기

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


GeoServer를 통해 공간정보 데이터를 호출하여, OpenLayers가 지도에 렌더링하는 걸 확인할 수 있다.


🏷️ 태그
# GIS
# GeoServer
# OpenLayers
# OGC
# WMS

읽어주셔서 고마워요!

도움이 되셨다면, 공감이나 댓글을 달아주시는 건 어떤가요?

블로그 운영에 큰 힘이 됩니다.

https://hits.seeyoufarm.com/api/count/incr/badge.svg?count_bg=%23484848&icon=react.svg&icon_color=dodgerblue&title=view&title_bg=%23242424&url=https%3A%2F%2Fblog.itcode.dev%2Fprojects%2F2022%2F05%2F16%2Fgis-guide-for-programmer-16