데이터 시각화로 유명한 오픈소스로 D3.js가 있습니다. v5 버전과 topojson을 이용하여 이번 포스팅에서는 한국 지도 시각화를 구현해보고자 합니다. 앞서, 우리나라 지도에 대한 경계선 데이터가 필요합니다. ( 데이터는 오른편 링크에 공개되어 있으니 이를 참조하시기 바랍니다. 경계선 JSON 데이터 ) 그리고 테스트를 위해 아래와 같이 점에 대한 데이터를 구성합니다.
1
2
3
4
5
6
7
8
9
10
11
12
|
[
{
"tag": "spot1",
"lat": 35.86,
"lon": 129.18
},
{
"tag": "spot2",
"lat": 36.13,
"lon": 129.25
}
]
|
cs |
가장 먼저 장고를 이용해 기본적인 서버를 구현합니다.
$ python manage.py startapp d3_korea_map
위 명령어를 수행하여 서버 어플리케이션을 생성한 뒤, 기본적인 세팅을 합니다.
전체 프로젝트의 path에 map을 추가합니다.
[/urls.py]
1
2
3
4
5
6
7
|
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('map/', include('d3_korea_map.urls'))
]
|
cs |
서버 어플리케이션 내에 map.py과 urls.py를 생성하여 GET 메소드일 경우 웹 문서를 반환할 수 있도록 작성합니다.
[/d3_korea_map/map.py]
1
2
3
4
5
6
7
8
9
10
11
12
|
from django.http import HttpResponse
from django.template import loader
def show_korea_map(request):
def get():
template = loader.get_template('map/main.html')
return HttpResponse(template.render())
if request.method == 'GET':
return get()
else:
return HttpResponse(status= 405)
|
cs |
[/d3_korea_map/urls.py]
1
2
3
4
5
6
7
|
from django.contrib import admin
from django.urls import path, include
from d3_korea_map.map import show_korea_map
urlpatterns = [
path('', show_korea_map, name='KoreaMap'),
]
|
cs |
또한 HTML 파일에서 외부 Javascript, CSS 파일을 읽기 위해 static 관련 설정을 해줍니다.
프로젝트의 settings.py 하단에 아래와 같은 내용을 추가해줍니다.
[/settings.py]
1
2
3
|
STATIC_ROOT = ''
STATIC_URL = '/static/'
STATICFILES_DIRS = (('map', os.path.join(BASE_DIR, 'static')),)
|
cs |
그리고 서버 프로젝트 하위 폴더로 static 폴더를 생성합니다. 앞으로 이 폴더를 정적 파일 관련 폴더로 사용할 것입니다. 간단한 서버 설정은 끝났습니다. 이제 template 폴더에서 html 및 d3 관련 javascript 코드를 작성해보겠습니다.
우선 templates 폴더에 map 폴더를 생성해봅니다. 이 후, main.html를 생성하여 아래와 같이 코드를 작성해봅니다. d3를 이용한 자바스크립트를 이용하여 body 내의 <div id="map"/>에 지도가 구성됩니다. 그리고 테스트용 gps 좌표는 main.html에서 전달하는 방식으로 작성해보겠습니다.
[templates/map/main.html]
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
|
{% load staticfiles %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>korea</title>
<script src="http://d3js.org/d3.v5.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script src="{% static 'map/script/map.js' %}"></script>
<script>
var KOREA_JSON_DATA_URL = "{% static 'map/province.json' %}",
TEST_SPOT_JSON_DATA_URL = "{% static 'map/testSpots.json' %}"
function initialize(){
d3.json(TEST_SPOT_JSON_DATA_URL).then(function(_data){
d3_korea_map('#map', _data);
})
}
</script>
</head>
<body onload="initialize()">
<div id="map"></div>
</body>
</html>
|
cs |
이후 지도를 만들기 위해 static에 먼저 위의 한국지도 관련 json 파일과, 테스트로 사용할 spot 관련 json 파일을 작성합니다. 이후 script와 stylesheet 폴더를 생성하고, script 폴더 내에 map.js를 생성합니다.
map.js에서는 먼저 지도를 생성(create) 한 후, 테스트용 점을 찍는(spotting_on_map) 순서로 진행됩니다. 그리고 서울특별시, 대구광역시 등 특별한 도에 한하여 줌 레벨을 조금 변경하였습니다. 그리고 점을 클릭할 시, 점에 대한 태그정보만 알림이 뜨도록 간단한 이벤트 핸들러를 작성해보았습니다.
[static/script/map.js]
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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
function d3_korea_map(mapContainerId, spots){
var WIDTH, HEIGHT,
CENTERED,
MAP_CONTAINER_ID = mapContainerId,
KOREA_PROVINCE_OBJECT = 'skorea_provinces_2018_geo';
var SPECIAL_CITIES = ['서울특별시', '인천광역시', '대전광역시', '대구광역시', '부산광역시', '울산광역시', '광주광역시', '세종특별자치시', '제주특별자치도'];
var projection, path, svg,
geoJson, features, bounds, center,
map, places;
function create(callback){
HEIGHT = window.innerHeight;
WIDTH = window.innerWidth;
console.log('Map scale', {'height': HEIGHT, 'width': WIDTH});
projection = d3.geoMercator().translate([WIDTH / 2, HEIGHT / 2]);
path = d3.geoPath().projection(projection);
svg = d3.select(MAP_CONTAINER_ID).append("svg")
.attr("width", WIDTH)
.attr("height", HEIGHT);
map = svg.append("g").attr("id", "map"),
places = svg.append("g").attr("id", "places");
d3.json(KOREA_JSON_DATA_URL).then(function(data){
geoJson = topojson.feature(data, data.objects[KOREA_PROVINCE_OBJECT]);
features = geoJson.features;
bounds = d3.geoBounds(geoJson);
center = d3.geoCentroid(geoJson);
var distance = d3.geoDistance(bounds[0], bounds[1]);
var scale = HEIGHT / distance / Math.sqrt(2) * 1.2;
projection.scale(scale).center(center);
console.log("center", center);
console.log("scale", scale);
map.selectAll("path")
.data(features)
.enter().append("path")
.attr("class", function(d) {console.log(d);
return "municipality c " + d.properties.code;})
.attr("d", path)
.on("click", province_clicked_event);
callback();
});
}
function spotting_on_map(){
var circles = map.selectAll("circle")
.data(spots).enter()
.append("circle")
.attr("class", "spot")
.attr("cx", function(d){ return projection([d.lon, d.lat])[0];})
.attr("cy", function(d){ return projection([d.lon, d.lat])[1];})
.attr("r", "2px")
.attr("fill", "red")
.on('click', spot_clicked_event)
.transition()
.ease(d3.easeElastic);
}
function spot_clicked_event(d){
alert(d['tag']);
}
function province_clicked_event(d){
var x, y, zoomLevel;
if (d && CENTERED != d){
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
if (d.properties.name == '제주특별자치도' || d.properties.name == '인천광역시')
zoomLevel = 10;
else if (SPECIAL_CITIES.indexOf(d.properties.name) != -1)
zoomLevel = 15;
else
zoomLevel = 3;
CENTERED = d;
console.log('centered', CENTERED);
} else {
x = WIDTH / 2;
y = HEIGHT / 2;
zoomLevel = 1;
CENTERED = null;
}
map.selectAll("path")
.classed("active", CENTERED && function(d) {return d === CENTERED;});
map.transition()
.duration(750)
.attr("transform", "translate(" + WIDTH / 2 + "," + HEIGHT / 2 + ")scale(" + zoomLevel + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / zoomLevel + "px");
}
create(function(){
spotting_on_map();
})
}
|
cs |
서버를 실행하여 확인해봅시다 !
... 지도가 생성되긴 하였습니다 ;; 그리고 점을 누르면 간단한 알림도 뜨긴 합니다.
그러면 지도를 조금 꾸며보도록 합시다. static 폴더 내 stylesheet 폴더에서 map.css 파일을 생성합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
|
path{
fill: gainsboro;
stroke: white;
stroke-width: 1.5px;
}
path:hover{
fill: orange;
}
#map .active{
fill: #2fa4e7;
}
|
cs |
위와 같이 아주 간단하게 색상 변경을 해봅니다. (색상은 제 취향대로 하였습니다.) 그리고 main.html 헤더 중간에 링크를 추가합니다.
1
|
<link rel="stylesheet" href="{% static 'map/stylesheet/map.css' %}">
|
cs |
이후 새로고침을 하면 !
위와 같이 조금 더 이뻐진(?) 우리 나라 지도가 등장합니다. 마우스를 올려 둘 경우 위와같이 이벤트가 발생하여 해당 도가 표시 됩니다.
클릭한 경우 파란색으로 색이 바뀌며, 확대됩니다.
우연히 D3.js 라는 오픈소스를 발견하여 우리나라 지도에 대한 시각화를 구현해 보았습니다. (굉장히 재미있었습니다.) 이 오픈 소스에는 지도 말고도 차트 및 다양한 요소가 많습니다. 각종 프로젝트에서 데이터를 시각화 할때 사용하면 편할 것이라 생각됩니다.
이상으로 이번 포스팅을 마치겠습니다.
'Development > Etc' 카테고리의 다른 글
Linux 로그 관리 logrotate (0) | 2020.11.18 |
---|---|
Apache Kafka를 이용한 단일 노드 / 메시지 전송 (0) | 2019.03.26 |
Python Flask[플라스크] 시작하기 - 1 (0) | 2019.01.20 |
Python Django[장고] 시작하기 - 2 (0) | 2019.01.18 |
Python Django[장고] 시작하기 - 1 (Pycharm, Anaconda를 이용하여) (0) | 2019.01.18 |