안녕하세요 캡틴개구리입니다.

이번 D3 라이브러리를 이용하기위해 구글링을 하던 중 제가 느낀점은 한국에서 제공하는 데이터를 이용한 예제가 찾을 수 없었다는 점입니다.... 대부분 미국이었습니다. 

그래서 이번에 국가공간정보포털에서 제공하는 데이터와 VWORLD 지도를 이용하여 한국 예제를 만들었습니다. 

먼저 Openlayers3와 Leaflet에서 D3 TopoJson 이용하기 결과화면부터 보겠습니다.

서비스바로가기


Openlayers3, Leaflet, D3, D3 TopoJson 라이브러리를 받을 수 있는 곳은 아래와 같습니다. 

필수 라이브러리

OpenLayers3 :  https://openlayers.org/ 

Leaflet : http://leafletjs.com/

D3 : https://d3js.org/d3.v3.min.js

TopoJson : https://d3js.org/topojson.v1.min.js


참고사항 : D3 라이브러리 상위버전이 존재합니다만... 상위버전 라이브러리를 이용할 경우... openlayers3에서 TopoJson을 제대로 가져오지 못하였습니다......머리속에 물음표만 나오지만 저는Openlayers3 example에서 사용한 버전의 라이브러리를 이용하였습니다. (위에 표시된 라이브러리 이용하시면 정상적으로 지도에 데이터를 표출 하실 수 있습니다.)


선택 라이브러리

VWORLD(배경지도) :  http://map.vworld.kr



D3.js는 Data-Driven Documents의 약어로 데이터를 렌더링할 수 있는(그릴 수 있는, 다룰수 있는) 웹 기반 문서 라이브러리로 간단하게 설명하면 데이터 시각화 라이브러리입니다. 또한 D3에서는 SVG, GeoJSON, TopoJSON 등의 포맷을 이용하실 수 있습니다.


오늘은 TOPOJSON을 이용하여 Openlayers3(이하 Ol3)와 Leaflet 지도위에 데이터를 올려보도록 하겠습니다. 



1. TOPOJSON 데이터 생성


D3라이브러리를 이용하기 위해서는 TOPOJSON 형태의 데이터가 필요합니다.


먼저 국가공간정보포털 오픈마켓(http://data.nsdi.go.kr/dataset)에서 연속지적도_서울 데이터를 다운로드 후, 여의도 부분만을 GeoJSON으로 변환하였습니다. 


데이터  : 연속지적도_전국 > LSMD_CONT_LDREG_서울.zip


좌표계 : EPSG : 5174 

 * 참고사항 : 좌표계를 정확하게 하기위해 저는 QGIS에서 제공하는 5174를 사용하지 않고 사용자정의 5174를 생성하여 사용하였습니다.(https://www.osgeo.kr/17) QGIS에서 제공하는 좌표계와 OSGeo 지부에서 제공하는 좌표계의 임계값이 미세하게 차이가 있어서 사용자정의로 좌표계를 생성하였습니다.


GeoJSON 추출 좌표계 : WGS84 EPSG 4326


간단한 QGIS 사용법은 국가공간정보포털 > 자료실에 매뉴얼이 존재합니다.




QGIS를 이용하여 저장한 GeoJSON을 위에서 언급한 TopoJSON으로 변경합니다. 구글에서 GeoJSON to TopoJSON을 검색하셔서 이용하셔도  되고 위에 언급해드린 링크를 이용하여 바로 접속하셔도 됩니다. 개인적으로 변경하는 방법이 있으시다면 그것도 좋습니다.


TopoJSON으로 데이터를 변경하셨다면 기본작업은 끝이났습니다.


2. 라이브러리 추가


이제 OL3와 Leaflet에서 D3를 이용하기 위해 라이브러리를 추가해보도록 하겠습니다. 추가하는 라이브러리는 아래와 같습니다. 


1
2
3
4
5
6
<link type="text/css" rel="stylesheet" href="<c:url value='/css/openlayers/ol.css'/>" />
<link type="text/css" rel="stylesheet" href="<c:url value='/css/leaflet/leaflet.css'/>" />
<script src="<c:url value='/js/openlayers/ol.js'/>"></script>
<script src="<c:url value='/js/leaflet/leaflet.js'/>"></script>
<script src="<c:url value='/js/D3/d3.v3.min.js'/>"></script>
<script src="<c:url value='/js/D3/topojson.v1.min.js'/>"></script>
cs


3. Openlayers3


OL3에서 D3를 이용하기위해서 제가 참고한 자료는 Openlayers3 example에 있는 d3 Integration입니다. 먼저 <script></script>부분을 먼저 보겠습니다.


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
74
75
76
<script>
    //지도띄우기
    var olMap8 = new ol.Map({
        layers: [
            new ol.layer.Tile({
                source: new ol.source.XYZ({
                    url: 'http://xdworld.vworld.kr:8080/2d/Base/201802/{z}/{x}/{y}.png'
                })
            })
        ],
       
        target: 'olMap8',
       
        view: new ol.View({
             center: [14129600.824512500.74],
            maxZoom: 19,
            zoom: 14
        })
    });
    var topoUrl8 = "/js/data/topojson/progworks_yeouido_4326.json";
    
    d3.json(topoUrl8, function(error, topology) {
        var features = topojson.feature(topology, topology.objects.collection);
  
        console.log("Ol3_topojson", topology)
        
        var canvasFunction = function(extent, resolution, pixelRatio,size, projection) {
            var canvasWidth = size[0];
            var canvasHeight = size[1];
 
            var canvas = d3.select(document.createElement('canvas'));
            canvas.attr('width', canvasWidth).attr('height', canvasHeight);
 
            var context = canvas.node().getContext('2d');
 
            var d3Projection = d3.geo.mercator().scale(1).translate([00]);
            var d3Path = d3.geo.path().projection(d3Projection);
 
            var pixelBounds = d3Path.bounds(features);
            var pixelBoundsWidth = pixelBounds[1][0- pixelBounds[0][0];
            var pixelBoundsHeight = pixelBounds[1][1- pixelBounds[0][1];
 
            var geoBounds = d3.geo.bounds(features);
            var geoBoundsLeftBottom = ol.proj.transform(geoBounds[0], 'EPSG:4326', projection);
            var geoBoundsRightTop = ol.proj.transform(geoBounds[1], 'EPSG:4326', projection);
            var geoBoundsWidth = geoBoundsRightTop[0- geoBoundsLeftBottom[0];
            if (geoBoundsWidth < 0) {
                geoBoundsWidth += ol.extent.getWidth(projection.getExtent());
            }
            var geoBoundsHeight = geoBoundsRightTop[1- geoBoundsLeftBottom[1];
 
            var widthResolution = geoBoundsWidth / pixelBoundsWidth;
            var heightResolution = geoBoundsHeight / pixelBoundsHeight;
            var r = Math.max(widthResolution, heightResolution);
            var scale = r / (resolution / pixelRatio);
 
            var center = ol.proj.transform(ol.extent.getCenter(extent),projection, 'EPSG:4326');
            d3Projection.scale(scale).center(center).translate([canvasWidth / 2, canvasHeight / 2]);
            d3Path = d3Path.projection(d3Projection).context(context);
            d3Path(features);
            context.stroke();
 
            return canvas[0][0];
        };
 
        var layer = new ol.layer.Image({
            source: new ol.source.ImageCanvas({
                canvasFunction: canvasFunction,
                projection: 'EPSG:3857'
            })
        });
        
        olMap8.addLayer(layer);
        
    });
</script>
cs


3행 ~ 19행 : OL3 지도부분입니다.

4행 : 저는 WVORLD 배경지도 사용

14행 ~17행 : 지도 중심, 최대확대 및 지도 로딩 레벨 설정

20행 : TopoJSON 데이터의 경로

22행 : d3.json( TopoJSON경로, function(error, TopoJSON의 Type)


위 그림이 제가 사용한 TopoJSON 데이터입니다. "type" : "Topology" 부분을 TopoJSON Type부분에 입력하시면됩니다. 

몇몇 다른 예제에서는 설명이 없이 값이 다 다르게 들어가 있어서 무엇을 입력해야되나 고민하다가 제가 생각한 결론은 TopoJSON의 Type 이름을 넣자 였습니다... 


27행 ~ 64행 : OL3에서 D3의 TopoJSON을 사용할 수 있도록 변경 및 canvasFunction 변수에 값 지정

66행 ~ 73행 : D3 라이브러리를 통해 변경한 데이터를 OL3 라이브러리를 이용하여 OL3 map에 add

OL3에서 D3 라이브러리를 이용하실때 가장 신경쓰셔야할 부분이 22행 부분이라 생각됩니다. 나머지는 example에 있는부분이라 별도의 변경이 필요없습니다만 22행 부분은 사용자에 맡게 변경이 필요한 부분이기 때문입니다.


다음은 <body></body>입니다.

1
2
3
<body>
    <div class="olMap" id="olMap8"></div>
</body>
cs


저의 경우, 여러개의 OL3와 Leaflet 작업을 하나의 HTML에서 진행하기 때문에 클래스를 넣었지만 <div></div>안에 id만 넣으셔도 무방합니다.


4. Leaflet

Leaflet 홈페이지에 가시면 geoJSON을 바로 이용할 수 있는 방법도 있습니다. 참고해보세요 Leaflet은 OL3와 비교하여 소스가 간단합니다. 먼저 <script></script>부분을 살펴보겠습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
<script>
//leaflet 지도 띄우기
    var letMap8 = L.map('letMap8').setView([37.52470308242787126.9234],14)
    
    //Vworld Tile 변경
    L.tileLayer('http://xdworld.vworld.kr:8080/2d/Base/201802/{z}/{x}/{y}.png').addTo(letMap8);
    
    //geoJson
    layer8 = L.geoJson(null, { style: { color: '#333', weight: 1 }})
    //geoJson map add
    letMap8.addLayer(layer8)
    
    //TopoJSON 
    var topoUrl8 = "/js/data/topojson/progworks_yeouido_4326.json";
 
    //d3활용 topoJson 파일 불러오기 및 geoJson 전달
    d3.json(topoUrl8, function(error, topology) {
        var topoYeouido8 = topojson.feature(topology, topology.objects.collection)
        layer8.addData(topoYeouido8);
        console.log("Leaflet_topojson", topology)
    })
</script>
cs


Leaflet에서는 TopoJSON 데이터를 적용하기위해서는 결론적으로 GeoJSON으로 변경하여 add시킵니다. 다시말해 굳이 TopoJSON으로 변경할 필요가 없다는겁니다.... 전 OL3를 다 끝내고 알았습니다.......

3행 : Leaflet 지도 올리기

6행 : VWORLD로 지도 변경

9행 : GeoJSON 선언 및 스타일 지정

11행 : Leaflet 지도에 GeoJSON 데이터 add

14행 : TopoJSON 데이터 경로

17행 ~ 21행 : D3 라이브러리를 이용하여 TopoJSON 데이터를 읽어오고 9행으로 값 전달

17행 : OL3 부분에서 언급한것과 동일합니다. d3.json( TopoJSON경로, function(error, TopoJSON의 Type)


다음은 <body></body>부분입니다.

1
2
3
<body>
    <div class="letMap" id="letMap8"></div>
</body>
cs


5. OL3와 Leaflet에서 D3 TopoJSON 올리기 결과화면

서비스바로가기



+ Recent posts