본문 바로가기

Function

Deck.gl 의 기초

 

deck.gl 은 WebGL 기반으로 만들어진 라이브러리다. deck.gl 라이브러리를 사용하면 경위도 좌표가 있는 공간정보 표현을 쉽게 할 수 있다. GPU 성능을 활용할 수 있기 때문에 수십만개의 점이나 선들을 웹에서 화면 끊김 없이 돌려볼 수 있다.

 

물론 공간정보 표현만 가능한 것은 아니다. d3.js를 이용해 위치를 정한 force 다이어그램의 좌표들을 넘겨주면 복잡한 네트워크도 빠르게 표현할 수 있다. 차트 표현에 최적화된 라이브러리를 별도로 제공해주지는 않지만, 점선면 도형과 축선을 직접 그린다고 생각하면 복잡한 산포도와 선형 그래프 등도 빠르게 그릴 수 있다.

 

deck.gl에서 만들 수 있는 시각화의 유형이 궁금하다면 아래 페이지에서 '레이어'들을 하나씩 클릭해보면 된다. Deck.gl에서는 point, line, path(line의 연속), polygon 등의 데이터 유형들을 다른 layer  객체에 입력해서 시각화하게 되는데, 그래서 이렇게 다른 유형의 지도들도 레이어라고 부른다. 

예를 들어 두 점 사이를 이어주면서 호를 그리는 레이어를 ArcLayer라고 부른다. 다르게 말하자면, 두 점씩 쌍으로 입력된 데이터를 호의 형식으로 그리고자 할 때 ArcLayer를 import 해서 사용하면 된다.

 

deck.gl | Examples

WebGL-powered visualization framework for large-scale datasets

deck.gl

 

gpu를 이용한 webgl 관련 라이브러리에 대해서 고민할 때, three.js 와 비교할 수도 있을 것 같다.

"어떤 것을 배워야 하지?" 라는 고민이 들 때, GIS에 익숙하고 웹지도를 다루어본 경험이 있으며 주로 만들게 될 시각화가 지도를 중심으로 움직인다면 주저없이 deck.gl을 선택하면 된다. geojson 파일이 있다면, 정말 간단한 코드 몇 줄로 지도 위에 올려볼 수 있기 때문이다. (물론 three.js가 더 범용적인 라이브러리인건 사실이다.)

 

deck.gl은 html에서 js 스크립트 로딩 방식으로도 사용할 수 있지만, webpack을 이용한 빌드 환경을 구성하고 import해서 사용하는 것을 권장한다. 초기 로딩 용량 문제도 있고, deck.gl 페이지의 문서들이 빌드 환경을 기준으로 설명되어 있기 때문이다.

 

 

따라해보면서 기본 지도 만들기

 

시작은 다른 npm 환경과 똑같다.

vscode를 사용한다면, 폴더 하나 만들어놓고 terminal에서 npm install deck.gl 하면 된다.

 

아래처럼 환경을 미리 셋팅해놓고 npm install 해도 된다.

deck.gl을 포함시켰으며, loaders.gl 의 csv와 json 은 자주 사용하므로 미리 포함시켰다. deck.gl의 자매품 라이브러리 같은거다.

 

//package.json

{
  "dependencies": {
    "@loaders.gl/csv": "^3.2.4",
    "@loaders.gl/json": "^3.2.4",
    "deck.gl": "^8.8.23"
  },
  "name": "basemap1",
  "version": "1.0.0",
  "main": "index.js",
  "devDependencies": {
    "html-webpack-plugin": "^5.5.0",
    "webpack": "^5.73.0",
    "webpack-cli": "^4.10.0"
  },
  "scripts": {
    "dev": "webpack --mode development",
    "start": "webpack-dev-server --mode development --open"
  },
  "keywords": [],
  "author": "",
  "license": "",
  "description": ""
}

 

같은 루트 폴더에 webck.config.js는 아래와 같이 일반적으로 구성했다.

//webpack.config.js

const HTMLWebpackPlugin = require('html-webpack-plugin')
const path = require('path')

module.exports = {
  plugins: [
    new HTMLWebpackPlugin({
      template: path.resolve(__dirname, './src', 'index.html'),
    }),    
  ],
  output: { path: path.resolve(__dirname, './dist'), filename: 'main.js' },
  entry: { index: path.resolve(__dirname, './src', 'index.js') },
}

src 폴더 아래 index.html과 index.js를 두고 작업하게 된다.

빌드한 파일들은 dist 폴더 안에 생성된다.

 

이제 vscode의 터미널에서 npm install 으로 입력하면 package.json 에 설정된 패키지들을 설치하게 된다.

 

 

이제 본격적으로 지도를 띄워보자.

src 폴더 밑에 index.html 을 만들자.

//index.html

<html>
  <head>
    
    <style type="text/css">
      body {margin: 0; padding: 0;}
      #container {
        width: 100vw;
         height: 100vh;
         position:absolute; 
      }
    </style>
  </head>

  <body>
    <div id="container"></div>    
  </body>

</html>

지도 객체를 넣을 div를 하나 만들었고, 화면을 채우도록 스타일을 간단히 정해줬다.

 

이제 src 폴더 밑에 index.js를 만들고 본격적인 코드를 넣어주자.

//index.js
  
import {Deck, _GlobeView as GlobeView}  from '@deck.gl/core';
import {ScatterplotLayer,SolidPolygonLayer, GeoJsonLayer, ArcLayer} from '@deck.gl/layers';
import {CSVLoader} from '@loaders.gl/csv';

const deckgl = new Deck({
  parent: document.getElementById('container'),
  //views: new GlobeView(),
  initialViewState: {
    latitude: 36.686033, 
    longitude: 127.938015,
    zoom: 10,
    bearing: 0,
    pitch: 0
  },
  controller: true,
 
});

처음에 지도를 띄우려면 기본적으로 Deck과 GeojsonLayer 정도를 import 하면 된다. 여기서는 다른 작업도 해보려고 이것저것 넣었다.

 

기본적으로 Deck 클래스를 설정하는건 간단하다.

new Deck을 호출해서 몇가지 객체변수를 설정해주면 된다.

우선, 앞서 html 에서 만들었던 div의 id를 넣어서 지도가 들어갈 자리를 지정해준다. 여기서는 container로 이름붙였다.

 

이제 화면의  초기 중심점과 zoom, bearing, pitch 등 기본 카메라 설정을 해준다.

controller를 true로 해줘야 지도를 조정할 수 있다.

 

여기까지가 기본 셋팅이다.

 

이제 무언가를 그려보자.

// source: Natural Earth http://www.naturalearthdata.com/ via geojson.xyz
const WORLD =
'https://d2ad6b4ur7yvpq.cloudfront.net/naturalearth-3.3.0/ne_50m_admin_0_scale_rank.geojson';

const update = () => {

  const layers =  [
    // new SolidPolygonLayer({
    //   id: 'background',
    //   data: [
    //     [[-180, 90], [180, 90], [180, -90], [-180, -90]]
    //   ],
    //   opacity: 0.5,
    //   getPolygon: d => d,
    //   stroked: false,
    //   filled: true,
    //   getFillColor: [5, 10, 40]
    // }),
    new GeoJsonLayer({
      id: 'base-world',
      data: WORLD,
      // Styles
      stroked: true,
      filled: true,
      lineWidthMinPixels: 2,
      getLineColor: [5, 10, 40],
      getFillColor: [15, 40, 80]
    }),
  ];
  deckgl.setProps({layers});
};

update();

우선 WORLD 변수에 세계지도  geojson이 있는 링크 문자열을 넣어주었다. 참고로 natualearthdata 에는 세계지도 관련 데이터들이 많다. geojson.xyz에서는 저렇게 개별 geojson 들을 손쉽게 불러올 수 있는 링크들을 제공한다.

 

화살표 함수를 넣은 update 부분부터 본격 그리기가 시작된다. 물론 순식간에 끝난다.

그 안에 layers 변수에 그리고자 하는 레이어들을 하나씩 넣어주자. 그리고 마지막으로 update()를 호출해주면 끝이다.

어때요, 참 쉽죠?

 

세계지도 파일은 폴리곤 형식의 geojson 파일이다. geojson 은 GeoJsonLayer에 넣어서 그려줄 수 있다. 여기서부터 레이어 이름이나 객체 변수 이름들은 반드시 정해진 것에 넣어야하므로 임의로 바꾸면 안된다.

 

우선 'id' 값은 반드시 다른 레이어들과 다르게 해준다. 레이어에 대한 일종의 key 역할을 한다.

data에는 WORLD 변수의 주소를 넣어준다. 해당 링크에서 geojson 파일을 읽어와서 파싱한 후 GPU 버퍼에 전달할 형식으로 변환하는 기능이 모두 내장되어 있다.

그 다음은 객체변수 이름들을 읽으면 이해할 수 있다.

stroke : 테두리를 그려주고,

filled : 내부도 채워준다.

lineWidthMinPixel : 지도를 아무리 축소해도 테두리를 최소 2픽셀로는 그려준다.

getLineColor : 선 색상은 [5,10,40]으로 둔다. 각각 0~255 사이의 값이다.

getFillColor : 면 색상은 [15, 40, 80]으로 둔다.

 

layers 배열 변수의 각 레이어들 설정이 끝나고 나면 변수를 마무리한다.

그리고 deckgl.setProps({layers}); 를 호출해서 이 변수 값을 deckgl에 전달한다.

이제까지의 과정을 update 변수 안에 함수로 구성했으므로, 당연히 update()를 호출해줘야 이 내용이 실행된다.

 

여기까지 한 후 vscode의 터미널에서 상황에 따라 npm run dev 혹은 npm run build 하면 빌드해서 결과를 볼 수 있다.

다른 웹 개발과 마찬가지로 vscode의 Go Live 라이브러리등을 설치하면 크롬에 localhost 주소를 입력해서 빌드한 결과를 볼 수 있다.

 

굳이 update 함수로 구성한 이유는, 대부분의 경우에 조작을 통해 지도의 내용을 바꾸는 경우가 많기 때문이다. 버튼을 통해 특정한 레이어를 켜고 끄거나, 필터링해서 표시하는 내용을 바꾸는 등, zoom/pan을 제외한 레이어 개체 자체를 변화시키려면 반드시 다시한번 layers 변수에 내용들을 고스란히 입력시킨 후 다시한번 deckgl.setProps를 호출해줘야 한다. 그런 상황이 매우 일반적이기 때문에 그 과정들을 update 변수에 담아 함수처럼 호출해주는 방식으로 초기 셋팅을 해 놓았다.

 

 

기본 지도를 mapbox로 두고 mapbox에 컨트롤러를 넘겨 주기

 

 deck.gl에서는 직접 지도를 구성할 수도 있지만, mapbox 와 같은 몇몇 특정한 지도 서비스의 API 를 이용하면서 컨트롤러를 넘겨주는 방식도 있다. 해당 방식의 기본 셋팅은 아래와 같다.

(당연히 mapbox에 회원가입을 하고 토큰을 받아오는 작업이 우선되어야 한다.)

 

일단 mapbox를 이용하므로 html에 다음의 스타일시트를 넣어준다.

  <link href="https://api.mapbox.com/mapbox-gl-js/v2.9.1/mapbox-gl.css" rel="stylesheet">

 

이제 index.js 부분이다.

import {ScatterplotLayer, TextLayer, BitmapLayer, SolidPolygonLayer, GeoJsonLayer} from '@deck.gl/layers';
import {CSVLoader} from '@loaders.gl/csv';
import {load} from '@loaders.gl/core';
import {MapboxOverlay} from '@deck.gl/mapbox';

import mapboxgl from 'mapbox-gl';
import MapboxLanguage from '@mapbox/mapbox-gl-language';
mapboxgl.accessToken = '자신의 토큰을 넣어준다.';

const map = createMap('container');
initMap(map);
const deckOverlay =  new MapboxOverlay({
  layers: [] //일단 비워두고 아래에서 업데이트 한다.
}); 
map.addControl(deckOverlay);

function createMap(containerID) {
  return  new mapboxgl.Map({
      container: containerID, // container ID
      style: 'mapbox://styles/mapbox/streets-v11', // style URL    
      center: [ 127.6, 35.7], // starting position [lng, lat]
      zoom: 6, // starting zoom 
      //projection: 'globe' // display the map as a 3D globe
  });    
}

function initMap(map) {
  map.addControl(new MapboxLanguage({
      defaultLanguage: 'ko'    
    } 
  ));
}

const update = () => {

  const layers =  [

   //.. 넣고 싶은 레이어를 넣는다...
  ];


  deckOverlay.setProps({
    layers : layers
  });  

};

update();

위의 코드와 비교해보면 어떤 부분들이 달라졌는지 알 수 있다.

우선 deckgl의 MapboxOverlay 를 추가로 import해야 한다. 당연히 mapbox 패키지도 설치하고 import해야 한다.

초기 지도 설정부분을 잘 읽어보면 mapbox가 엮이면서 어떻게 달라졌는지 알 수 있다.

아래부분에서도 layers 변수에 레이어들을 넣어준 후, deckOverlay.setProps() 를 호출해서 layers 에 설정값들을 넣어줘야 한다.

 

이제 빌드하고 실행시켜보면 mapbox 벡터 타일맵이 배경으로 깔리는 지도들을 볼 수 있다.

 

 

 

아래 깃허브에 기본 예제 세 가지를 넣어놓았다.

 

GitHub - vuski/jsBoda

Contribute to vuski/jsBoda development by creating an account on GitHub.

github.com

 

 

아래 깃허브 페이지에서 작동하는 웹페이지를 볼 수 있다.

tutorial01은 간단한 geojson 배경지도에 몇가지 레이어를 올려놓았다.

tutorial02는 mapbox 지도에 검은 ScatterpoltLayer를 올려놓았다.

tutorial03은 mapbox 지도에 데이터를 올리고 레인지 슬라이더에 따라 필터링되는 예시를 올려놓았다. 개체에 마우스오버시 d3.js의 canvas를 사용하여 tooptip이 등장하도록 했다.

 

https://vuski.github.io/jsBoda/

 

vuski.github.io