본문 바로가기

Function

deck.gl로 만든 웹 지도의 hillshade 표현

 

 

최근 작업에서 만든 지도가 위 그림에서 보이는 정도로 확대되는 경우가 있었다.

시군구 단위의 수치를 보여주면 그만이지만, 그래도 약간은 허전해보인다. 상세한 지도를 겹쳐 넣으면 단순하게 전달해야 할 지표들이 잘 보이지 않게 되고, 그래도 그냥 두자니 좀 허전해서 지형과 도로 정도의 희미한 디테일을 덧붙여보기로 했다.

 

 

그래서 위의 그림 정도로 지형을 새겨넣었다. 이제 허전함이 좀 사라졌다.

이 글에서는 간단한 몇 가지 절차를 거쳐 만들 수 있는 이 지도의 표현 방법을 살펴보겠다.

 

 

절차는 크게 보면 다음과 같다.

1. Qgis에서 음영기복도(hillshade) 역상으로 만들기

2. Qgis에서 래스터 타일맵 생성

3. Deck.gl에서 여러 레이어 중 적절한 순서에 타일맵을 reverse subtract 방식으로 넣기

끝.

 

여기서 설명하지 않지만, 기본적으로 알고 있어야 할 내용들은 다음과 같다.

1. qgis의 기본적인 사용방법

2. dem과 래스터 타일맵에 대한 기본적 이해

3. 웹에서 deck.gl로 지도를 표현하는 방법

이 부분들은 웹 검색을 하면 많은 설명과 튜토리얼들을 충분히 찾을 수 있으므로 여기에서는 관련 설명을 생략한다.

 

 

준비

 

우선 Qgis에서 지도 보기 좌표계를 EPSG:3857로 설정한다.

Qgis 화면 우측 하단의 좌표계 부분을 직접 클릭해서도 바꿀 수 있다.

많은 글로벌 스케일 타일맵은 이 도법을 기본으로 한다.

 

 

QGIS에서 dem 으로 음영기복도 만들기

 

 

Qgis에서 탐색기를 보면 xyz tiles 메뉴가 있고, 여기에 지도를 추가할 수 있다.

 

설정은 위와 같이 둔다. 이름은 아무래도 상관없지만, Mapzen Terrain 정도로 해 두면 된다.

맵젠이 문을 닫은 뒤 아마존에서 일부 지도들 무상으로 서비스하고 있다. 공개 기한 약속은 없으나 일단 된다.

맵 설명은 다음을 참고하면 된다.

 

 

Mapzen Terrain Tiles are 1.0 and ready to go · Mapzen

Matt Amos OpenStreetMap developer-contributor and chilli enthusiast. Writes vector tile and other data-mastication tools at Mapzen.

www.mapzen.com

 

다시 지도 추가로 돌아오자.

URL이 가장 중요하다. 다음과 같이 둔다.

https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png  

 

최저/최고 수준은 지도가 제공하는 만큼을 넘을 수 없다. 0~15로 두면 된다.

Interpretation도 중요한데 Terrarium 지형 RGB로 둔다.

DEM 데이터의 디코딩 방식을 지정한다고 보면 된다.

 

 

이제 새로 설정한 타일맵을 더블클릭하거나 드래그&드롭해서 작업 레이어에 올려둔다.

 

 

기본 설정은 단일 밴드 그레이 방식의 표현이다. 최대 줌 아웃하면 위와 같이 보인다.

 

이제 속성에서 다음과 같이 바꾼다.

 

렌더링 유형, 밝기, 대비, 리샘플링 등 위와 같이 둔다.

방위각이나 밝기, 대비등은 조금씩 조정해도 상관 없다.

리샘플링 옵션에서 확대 값은 최근접이웃이 기본인데, 이렇게 하면 확대할 때 사각형 타일 사이에 빈 공간이 좀 더 보이게 된다. 이것을 2x2 쌍선형으로 바꿔준다. 쌍선형은 두 방향의 Linear Interpolation을 뜻한다.

 

색상을 반전으로 둔 이유는 deck.gl에서 합성시  reverse subtract를 하기 위해서다. 그냥 음영기복도 타일맵을 만들고 싶으면 색상 반전을 체크하지 않으면 된다.

혹시라도 등고 색상이 있는 음영기복도 타일맵을 만들어 deck.gl에 넣고 싶을 경우, 이 단계에서 지도만 다르게 만들면 된다.

색상 음영기복도 만드는 방법은 다음의 영상에 잘 나와있다.

 

 

 

위와 같은 설정을 하면 아래 그림처럼 보인다.

 

음..... 모니터 특성에 따라서 완전 검은색으로 보일 수도 있겠다.

 

 

레이어 마스크 만들기

 

이번 작업의 경우 한반도 남쪽 우리나라 영토만을 대상으로 해서 나머지 부분은 생략하기로 했다.

간단하게 마스크를 만들어준다.

 

 

 

타일맵 생성 범위 보다 큰 임의의 큰 사각형을 생성한 후, 위의 지도를 subtract 연산으로 빼 준다.

(제주도와 백령도, 울릉도 독도는 미리 좀 옮겼다)

 

그리고 줌 아웃 해 보면 아래와 같은 형태를 볼 수 있다.

 

현재 지도 전체 배경색이 푸른색이고, 지금 만든 레이어가 검은색이다. 사각형에서 우리나라 영토가 구멍 뚫린 격이라고 보면 된다.

위에서 만든 역상 지도와 맞춰주려고 테두리 없는 검은색으로 칠했다.

 

 

 

음영기복도위에 마스크를 올려놓고 레이어를 모두 켜면 위의 그림처럼 보인다. 

북한쪽이 사라졌다. 제주도도 보이지 않는데, 레이어 마스크에서는 제주도를 옮겼지만 음영기복도는 그에 맞춰 수정할 수 없었으므로 제주도 구멍 뚫린 곳에는 현재 평평한 바다가 있기 때문이다.

실제로는 더 어둡게 보일 것이다.

위 지도는 도로 레이어가 겹쳐진 상태다

 


확대해보면 아래 그림과 같다.

 

도로와 역상의 음영기복도가 만들어졌다.

 

 

xyz 타일맵 만들기

 

이제 타일맵을 만든다.

공간처리 툴박스에서 [XYZ 타일생성(디렉터리)] 메뉴를 선택한다.

 

옵션은 아래와 같이 두었다.

 

범위는 적당히 드래그 하거나 원하는대로 정확한 범위를 설정한다.

지도의 좌표계가 EPSG 3857인지 다시한번 확인한다.

 

최소와 최대 확대 범위는 원하는대로 설정한다.

이번 프로젝트에서는 6~12로 제한했으므로 여기서도 동일하게 둔다.

다른 옵션은 특별히 조정하지 않고 아래 산출물 디렉터리에 적절한 폴더를 지정해준다.

 

최대 확대 범위가 11~12 수준이라면 그리 오래 걸리지 않아 타일맵이 모두 생성된다.

 

전체 용량은 약 126MB 정도다. 개별 파일의 크기가 작으므로 github 에 올려서 무리없이 page로 서비스할 수 있다.

 

12레벨의 일부 타일을 보면 아래와 같다.

타일맵이 잘 생성되었다.  (모니터에 따라서는 완전 검게 보일 수도 있다)

 

 

 

 

deck.gl에서 타일맵 얹기

 

이제 준비는 모두 끝났다.

우선 위에서 만들어진 폴더를 자신이 만드는 웹페이지의 폴더로 적절하게 이동/복사한다.

여기서는 shadowtile_hillshade 폴더를 만들어 넣어놓았다.

deck.gl에서 만들고 있던 지도가 있다고 가정할 때, 이 타일맵은 다음과 같이 설정하면 된다.

 

// 아래 GL. 부분은 여기서 import
import { GL } from "@luma.gl/constants";


///////

const hillShade = new TileLayer({
    id: "background-map-hillshade",
    data: "./shadowtile_hillshade/{z}/{x}/{y}.png",
    minZoom: 6,
    maxZoom: 12,
    tileSize: 256,
    opacity: 1.0,
    renderSubLayers: (props: any) => {
      const {
        bbox: { west, south, east, north },
      } = props.tile;
      return new BitmapLayer(props, {
        data: undefined,
        image: props.data,
        bounds: [west, south, east, north],        
      });
    },

    visible: true,
    parameters: {    
 
      depthTest: false,
      blendFunc: [GL.SRC_ALPHA, GL.ONE, GL.ONE_MINUS_DST_ALPHA, GL.ONE],
      blendEquation: GL.FUNC_REVERSE_SUBTRACT,
    },
  });

 

 

다른 내용들은 타일레이어의 일반적인 사용 방식과 같다.

특별한 부분은 아래 부분이다.

 

    parameters: { 
      depthTest: false,
      blendFunc: [GL.SRC_ALPHA, GL.ONE, GL.ONE_MINUS_DST_ALPHA, GL.ONE],
      blendEquation: GL.FUNC_REVERSE_SUBTRACT,
    },

 

 

GL.SRC_ALPHA ... 등의 부분은 OpenGL에서 fragment shader를 거쳐가는 여러 요소들을 혼합하는 blend 규칙과 동일하다.

아래 링크 정도에 자세한 설명이 있으니 참고하며 된다.

 

LearnOpenGL - Blending

Blending Advanced-OpenGL/Blending Blending in OpenGL is commonly known as the technique to implement transparency within objects. Transparency is all about objects (or parts of them) not having a solid color, but having a combination of colors from the obj

learnopengl.com

 

blendEquation의 reverse_subtract는 색상이 겹쳐질 때 점점 어두워지도록 하고 싶을 때 사용하는데, 주의할 점은, 표현하고 싶은 색상의 역상을 넣어야 한다는 부분이다. 예를 들어 저렇게 설정을 해두고 붉은색이 점점 겹쳐져서 검붉은색이 되도록 만들고 싶다면, RGB값을 [250,50,25] 으로 넣으면 안되고, [5,205, 230] 로, 즉 255에서 뺀 값을 넣어야 한다.

 

 

그래서 이 레이어를 넣기 전후의 지도를 다시 한번 차례로 비교해보면 다음과 같다.

 

 

QGIS에서 음영기복도를 만들 때 색상 밝기나 대비를 조절하면 좀 더 뚜렷하게 보이게 만들 수도 있을 것 같다.

여기에서는 그냥 은은하게 드러나도록 하고 싶어 저 정도로 두었다.

 

나가며

 

여기까지 읽다보면 의문이 들 것 같다.

'아니 왜 저렇게 어렵게 하지? 그냥 바탕에 정상적인 음영기복도를 깔고 시작하면 안되나...?'

 

만약 그 방법으로 성공한다면 댓글에 알려주기 바란다.

그게 잘 안된 것이 저렇게 만든 이유다.

 

위의 작업에서 레이어를 겹친 순서르 가장 밑에서 부터 말하자면 다음과 같다.

- 회색 바탕

- 보라색 그라데이션

- 음영기복도 reverse subtract

- 경계선

- 텍스트

 

개념적으로 말해보자면 

일단 바탕색을 다 칠한 뒤 음양각을 새기는 방법이라고 보면 된다.

 

만약 역상 아닌 정상적 음영기복도를 맨 아래에 깔게 되면 그 위에 색상 그라데이션 등을 올려야 하는데,

이런저런 블렌딩 방법으로 해봐도 원래의 그라데이션 색상들이 다소 변질되는 결과들을 가져왔다.

 

reverse_subtract 방법의 단점은, 그 블렌딩 방식의 특성상 지도가 검은색일 경우 더 이상의 음영 표현이 불가하다는 점이다.