일주일동안 한 가지 주제로 목표를 세우고, 공부하고, 프로젝트를 진행해보면서 결과를 내기로 했었죠...? 이번 일주일 동안은 차트에 대해 공부했습니다. 저번 글을 통해 Chart.js에서는 차트가 canvas를 사용해 만들어진다는 것을 알 수 있었습니다.
이번 시간에는 그 이후에 Canvas API를 공부한 내용과 Geo 차트에 대해 알아보고, Geo 포맷에 대해 알아보고, 실제 GeoJSON 포맷을 커스텀해보면서 내가 사는 지역에 대한 차트까지 만든 내용을 정리해보려고 합니다.
참고 : https://developer.mozilla.org/ko/docs/Web/API/Canvas_API
Canvas API는 자바스크립트와 HTML canvas 엘리먼트를 통해 그래픽을 그리기위한 수단을 제공합니다. 무엇보다 애니메이션, 게임 그래픽, 데이터 시각화, 사진 조작 및 실시간 비디오 처리를 위해 사용됩니다.
Canvas API는 주로 2D 그래픽에 중점을 두고 있습니다. WebGL API 또한 canvas 엘리먼트를 사용하며, 하드웨어 가속 2D 및 3D 그래픽을 그립니다.
기본 html 파일을 만듭니다. 안에는 canvas 엘리먼트가 있습니다.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Document</title>
</head>
<body>
<canvas id="canvas"></canvas>
<script src="main.js"></script>
</body>
</html>
HTMLCanvasElement.getContext() 메서드를 통해 엘리먼트의 컨텍스트(렌더링될 그리기의 대상)를 얻습니다. 실제 그리기는 CanvasRenderingContext2D 인터페이스를 사용해 수행됩니다.
const canvas = document.getElementById("canvas");
const ctx = canvas.getContext("2d");
ctx.fillStyle = "green";
ctx.fillRect(10, 10, 150, 100);
fillStyle 프로퍼티는 사각형을 초록색으로 만듭니다. fillRect() 메서드는 좌측 상단 코너를 (10, 10) 위치에 놓으며, 너비를 150, 높이를 100으로 지정합니다. 결과는 아래와 같습니다.
참고 : https://developer.mozilla.org/en-US/docs/Web/API/CanvasRenderingContext2D
디버깅을 해보면 ctx가 CanvasRenderingContext2D 인 것을 알 수 있습니다. 이 안에는 도형, 텍스트, 이미지 및 기타 객체를 그리는 데 사용되는 프로퍼티와 메서드가 존재합니다. 인터페이스의 속성 및 방법은 위 페이지의 참조 섹션에 설명되어 있습니다.
캔버스 API는 매우 강력하지만 항상 사용이 간단하지는 않습니다. 아래에 나열된 라이브러리는 캔버스 기반 프로젝트를 더 빠르고 쉽게 만들 수 있습니다. 몇 개는 저도 들어보긴 한 라이브러리네요.
참고 : https://developer.mozilla.org/en-US/docs/Web/API/Canvas_API/Tutorial
이 자습서에서는 canvas 요소를 사용하여 2D 그래픽을 그리는 방법을 기본적으로 설명합니다. 제공된 예제는 canvas 로 무엇을 할 수 있는지에 대한 몇 가지 명확한 아이디어를 제공하고 자신만의 콘텐츠를 만드는 데 사용할 수 있는 코드 조각을 제공합니다.
✍️ 생각보다 볼만 하니 Canvas를 처음 다루시는 분이라면 꼭 한번 보시길 바랍니다. 여기서는 전부 다루는 대신 꼭 필요한 부분만 설명하고 넘어가도록 하겠습니다.
width 및 height 속성을 지정하지 않으면 캔버스의 처음 너비는 300 픽셀이고 높이는 150 픽셀입니다. 요소는 CSS에 의해 임의로 크기를 정할 수 있지만 렌더링하는 동안 이미지는 레이아웃 크기에 맞게 크기가 조정됩니다. CSS 크기 지정이 초기 캔버스의 비율을 고려하지 않으면 왜곡되어 나타납니다. 따라서 CSS 대신 <canvas>
속성에서 width 및 height 속성을 명시적으로 지정하는 것을 추천합니다.
<canvas id="tutorial" width="150" height="150"></canvas>
canvas 요소는 인터넷 익스플로러 9 이하의 버전이나 텍스트기반 브라우저 등과 같은, 캔버스를 지원하지 않는 오래된 브라우저들을 위한 대체 컨텐츠를 정의하기 쉽습니다. 여러분은 그러한 브라우저들을 위한 대체 컨텐츠를 제공해야 합니다. 예를 들어, 캔버스 내용에 대한 텍스트 설명을 제공하거나 동적으로 렌더링 된 내용의 정적 이미지를 제공 할 수 있습니다.
<canvas id="stockGraph" width="150" height="150">
current stock price: $3.15 +0.15
</canvas>
<canvas id="clock" width="150" height="150">
<img src="images/clock.png" width="150" height="150" alt="" />
</canvas>
canvas 엘리먼트는 고정 크기의 드로잉 영역을 생성하고 하나 이상의 렌더링 컨텍스(rendering contexts)를 노출하여, 출력할 컨텐츠를 생성하고 다루게 됩니다.
var canvas = document.getElementById("tutorial");
var ctx = canvas.getContext("2d");
// checking for support (필요시 사용)
if (canvas.getContext) {
const ctx = canvas.getContext("2d");
// drawing code here
} else {
// canvas-unsupported code here
}
간단하게 두 개의 직사각형을 그린 간단한 예제를 보도록하겠습니다. 그 중 하나는 투명도(alpha transparency)를 가집니다.
ctx.fillStyle = "rgb(200, 0, 0)";
ctx.fillRect(10, 10, 50, 50);
ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
ctx.fillRect(30, 30, 50, 50);
우리가 그림을 그리기 시작하기 전에, 우리는 캔버스 격자 또는 좌표 공간에 대해 이야기해야 합니다. 일반적으로 그리드의 1단위는 캔버스의 1픽셀에 해당합니다. 이 그리드의 원점은 좌표 (0,0)의 왼쪽 상단 모서리에 위치합니다. 모든 요소는 이 원점에 상대적으로 배치됩니다.
따라서 파란색 사각형의 왼쪽 상단 모서리의 위치는 왼쪽에서 x 픽셀이 되고 위쪽에서 y 픽셀이 좌표 (x,y)가 됩니다.
SVG와는 다르게, canvas는 직사각형과 경로(선으로 연결된 점들의 목록)라는 두 개의 원시 형태만을 지원합니다. 다른 모든 도형은 하나 이상의 경로를 결합하여 작성해야 합니다.
먼저 직사각형을 살펴봅시다. 캔버스에 직사각형을 그리는 세 가지 함수가 있습니다:
각각의 세 함수는 모두 같은 변수를 가집니다. x와 y는 캔버스의 좌측상단에서 사각형의 (원점으로부터 상대적인) 위치를 뜻하며, width 와 height는 사각형의 크기를 뜻하게 됩니다.
function draw() {
const canvas = document.getElementById("canvas");
if (canvas.getContext) {
const ctx = canvas.getContext("2d");
ctx.fillRect(25, 25, 100, 100);
ctx.clearRect(45, 45, 60, 60);
ctx.strokeRect(50, 50, 50, 50);
}
}
fillRect() 함수는 가로 세로 100 픽셀 사이즈의 검정 사각형을 그립니다. 이후 clearRect() 함수가 60x60 픽셀의 사각형 크기로 도형 중앙을 지우게 되고, strokeRect()은 이 빈 사각형 공간 안에 50x50 픽셀 사이즈의 윤곽선만 있는 사각형을 만들게 됩니다.
참고로, rect() 함수도 있습니다. 근데 이 메서드는 직접 렌더링하지 않습니다. fill()이나 stroke()를 별도로 사용해야 됩니다. 그니까 rect + fill = fillRect인 셈인듯 하네요.
경로(path)는 곡선 또는 곡선이 아닌 선의 세그먼트로 연결된 점의 목록으로, 폭과 색상이 서로 다를 수 있습니다. 경로를 사용하여 모양을 만들려면 몇 가지 추가 단계를 수행합니다:
다음은 위의 단계들을 실행하기 위해 사용되는 함수입니다:
경로를 만들기 위한 첫번째 단계는 beginPath() 메소드를 사용하는 것 입니다. 내부적으로, 경로는 도형을 이루는 하위경로(선, 아치 등)들의 집합으로 이루어져있습니다. 이 메소드가 호출될 때 마다, 하위 경로의 모음은 초기화됩니다.
두번째 단계는 실제로 경로가 그려지는 위치를 설정하는 메소드를 호출하는 것 입니다.
세번째 단계는 선택사항으로 closePath() 메소드를 호출하는 것 입니다. 이 메소드는 현재 점 위치와 시작점 위치를 직선으로 이어서 도형을 닫습니다. 참고로, fill() 메소드 호출 시, 열린 도형은 자동으로 닫히게 되므로 closePath()메소드를 호출하지 않아도 됩니다. 이것은 stroke() 메소드에는 적용되지 않습니다.
예를 들어, 하나의 두 삼각형 (윤곽선 삼각형, 색칠된 삼각형)을 그리기 위한 코드는 다음과 같습니다.
function draw() {
var canvas = document.getElementById("canvas");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
// Filled triangle
ctx.beginPath();
ctx.moveTo(25, 25);
ctx.lineTo(105, 25);
ctx.lineTo(25, 105);
ctx.fill();
// Stroked triangle
ctx.beginPath();
ctx.moveTo(125, 125);
ctx.lineTo(125, 45);
ctx.lineTo(45, 125);
ctx.closePath();
ctx.stroke();
}
}
beginPath를 먼저하고 moveTo로 시작좌표를 움직인다음, lineTo로 선을 긋고 있습니다. 그리고 마지막으로 fill을 하면 시작 좌표와 자동으로 닫히게 됩니다. 두번째 삼각형을 그릴때는 다시 beginPath로 초기화를 해주고 똑같이 반복하면 됩니다. 대신 여기서는 stroke를 사용하므로 closePath를 해줘야 합니다. (감이 잡히죠?)
직선이 있으면 곡선도 있어야겠죠? 호나 원을 그리기위해서는 arc() 혹은 arcTo() 메소드를 사용합니다.
arc 함수에서 각도는 각이 아닌 라디안 값을 사용합니다. 각도를 라디안으로 바꾸려면 다음의 자바스크립트(JavaScript) 코드를 사용하실 수 있습니다: radians = (Math.PI/180)*degrees.
function arcDraw() {
var canvas = document.getElementById("canvas3");
if (canvas.getContext) {
var ctx = canvas.getContext("2d");
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 3; j++) {
ctx.beginPath();
var x = 25 + j * 50; // x coordinate
var y = 25 + i * 50; // y coordinate
var radius = 20; // Arc radius
var startAngle = 0; // Starting point on circle
var endAngle = Math.PI + (Math.PI * j) / 2; // End point on circle
var anticlockwise = i % 2 == 0 ? false : true; // clockwise or anticlockwise
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
if (i > 1) {
ctx.fill();
} else {
ctx.stroke();
}
}
}
}
}
보면 1~2행은 stroke, 3~4행은 fill로 되어있습니다. 1, 3행은 anticlockwise false이므로 반시계 방향이고요. 2, 4행은 시계 방향입니다. 그리고 endAngle은 180도, 270도, 360도인 거 같습니다. 파이가 180도니까요. 따라서 결과는 다음과 같습니다.
좀 더 복잡한 곡선 - 베지어(Bezier) 곡선과 이차(Quadratic )곡선은 생략하겠습니다. 이를 이용하면 그럴듯한 하트나 말풍선 모형 등을 만들 수 있습니다.
곡선이 있는 rectangle은 자주 사용될 거 같습니다. 이런 것들을 유틸리티 함수로 놓고 사용하면 편하겠죠? 사용해야 할 코드의 양과 복잡함을 줄여주는데 도움을 줍니다.
보니까 line그리고 arcTo로 곡선그리고, 다시 라인 그리고, 곡선 그리고를 반복하는군요.
// A utility function to draw a rectangle with rounded corners.
function roundedRect(x, y, width, height, radius) {
var canvas = document.getElementById("canvas4");
var ctx = canvas.getContext("2d");
ctx.beginPath();
ctx.moveTo(x, y + radius);
ctx.lineTo(x, y + height - radius);
ctx.arcTo(x, y + height, x + radius, y + height, radius);
ctx.lineTo(x + width - radius, y + height);
ctx.arcTo(x + width, y + height, x + width, y + height - radius, radius);
ctx.lineTo(x + width, y + radius);
ctx.arcTo(x + width, y, x + width - radius, y, radius);
ctx.lineTo(x + radius, y);
ctx.arcTo(x, y, x, y + radius, radius);
ctx.stroke();
}
roundedRect(12, 12, 150, 150, 15);
roundedRect(19, 19, 150, 150, 9);
roundedRect(53, 53, 49, 33, 10);
1장과 2장이 가장 핵심이라고 볼 수 있고 나머지는 필요시에 보면 될 듯합니다. 간단하게 정리만 하겠습니다.
사실 이번 프로젝트 목표는 나만의 차트(커스텀 차트) 만들기입니다. 이를 위해 Chart.js도 분석하고 Canvas API도 공부했죠. 하지만 이미 기존 차트는 잘 되어있어서 굳이... 라는 생각이 들었습니다.
참고 : https://github.com/nhn/tui.chart
참고 : https://spicy-lace-142.notion.site/Han-Jung-c43f4bcd2b3f4b3d85b93aee41c5e098
그 와중에 TOAST UI 에서는 기존 SVG를 Canvas로 마이그레이션 하는 작업을 했더군요. 은근 NHN이 기술 트렌드를 잘 따라가는거 같습니다. 나중에 여기 코드도 분석해보면 좋을거 같네요. 아무튼!! 그래서 어떤 차트를 만들지 고민하다가 geo 라는게 눈에 띄었습니다.
참고 : https://github.com/chartjs/awesome#charts
참고 : https://github.com/sgratzl/chartjs-chart-geo
Chart.js에서는 별도로 geo 차트가 있었습니다. 하지만 예시는 전세계랑 미국 지도 뿐이였죠. 그게 조금 아쉽더군요. 우리나라 지도는 어떻게 적용해야 할까요? 그래서 찾아봤습니다. (아. 참고로 여기서는 SVG를 사용하지는 않지만 SVG 로 만드는 것도 가능합니다 - 참고)
참고 : https://ko.wikipedia.org/wiki/GeoJSON
이를 위해서는 먼저 GeoJSON, TopoJSON 포맷 부터 알아야 했습니다. 이게 보니까 조금 전문적인 영역이더군요. 😂
GeoJSON는 위치정보를 갖는 점을 기반으로 체계적으로 지형을 표현하기 위해 설계된 개방형 공개 표준 형식입니다. 이것은 JSON인 자바스크립트 Object Notation을 사용하는 파일 포맷입니다. 지리좌표계의 점을 기반으로 Geocoding된 지형지물(주소 및 위치), 라인스트링 또는 폴리라인, 다각형 및 이러한 유형의 여러 부분으로 구성된 모음을 특징으로 합니다. 산악 등반이나 마운틴 바이크를 위한 루트 및 길안내 자료등으로 사용할 수 있습니다. (오호 솔깃...!)
GeoJSON 형식은 공식 표준 조직이 아니라 국제 인터넷 표준화 기구 산하 워킹그룹에 의해 작성되고 유지된다는 점에서 다른 GIS 표준과 다르지만 XML을 기반으로 한 GPX와 함께 사실상 표준처럼 사용됩니다. GeoJSON의 주목할만한 계열은 TopoJSON 입니다.
다음은 말레이시아, 싱가포르등이 위치해 있는 자바 해의 말레이 제도에서의 가상의 GeoJson 표현입니다. http://geojson.io/ 에서 확인 가능합니다.
{
"type": "FeatureCollection",
"features": [
{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [102.0, 0.5]
},
"properties": {
"prop0": "value0"
}
},
{
"type": "Feature",
"geometry": {
"type": "LineString",
"coordinates": [
[102.0, 0.0], [103.0, 1.0], [104.0, 0.0], [105.0, 1.0]
]
},
"properties": {
"prop0": "value0",
"prop1": 0.0
}
},
{
"type": "Feature",
"geometry": {
"type": "Polygon",
"coordinates": [
[
[100.0, 0.0], [101.0, 0.0], [101.0, 1.0],
[100.0, 1.0], [100.0, 0.0]
]
]
},
"properties": {
"prop0": "value0",
"prop1": { "this": "that" }
}
}
]
}
기본 포맷은 type, geometry, properties 3가지로 구성됩니다. 그리고 geometry에는 7개 타입이 있습니다. coordinates는 [경도, 위도]를 나타내는 점입니다. 추가 설명은 다음을 참고하시길 바랍니다.
참고 : https://happy-chipmunk.tistory.com/108
참고2 : https://wallacearchivemain.gatsbyjs.io/blog/geojson/
아무튼 이를 이용하면 네이버 지도 위에 이런 재미난 것도 할 수 있습니다. (네이버 API 참고)
참고 : https://ko.wikipedia.org/wiki/GeoJSON#TopoJSON_스키마
참고 : https://github.com/topojson/topojson/blob/master/README.md
참고 : https://life-of-panda.tistory.com/11
GeoJSON의 확장 포맷이 바로 TopoJSON 입니다. TopoJSON에서 기하학적 구조는 분리되는 대신 arc라는 공유되는 선 세그먼트에서 함께 연결합니다. 예를 들어, 캘리포니아와 네바다의 공유 경계선은 두 주 모두에 대해 중복되지 않고 단 한 번만 표시되는 방식입니다. 따라서, TopoJSON은 중복성을 제거하여 관련 기하학적 구조를 동일한 파일에 효율적으로 저장할 수 있도록 합니다.
단순하지만 유효하고 완전한 topojson 파일의 'Polygon', ' LineString ', 'Point' ,'arc' 및 'properties'는 다음과 같이 정의할 수 있습니다.
{
"type":"Topology",
"transform":{
"scale": [1,1],
"translate": [0,0]
},
"objects":{
"two-squares":{
"type": "GeometryCollection",
"geometries":[
{"type": "Polygon", "arcs":[[0,1]],"properties": {"name": "Left_Polygon" }},
{"type": "Polygon", "arcs":[[2,-1]],"properties": {"name": "Right_Polygon" }}
]
},
"one-line": {
"type":"GeometryCollection",
"geometries":[
{"type": "LineString", "arcs": [3],"properties":{"name":"Under_LineString"}}
]
},
"two-places":{
"type":"GeometryCollection",
"geometries":[
{"type":"Point","coordinates":[0,0],"properties":{"name":"Origine_Point"}},
{"type":"Point","coordinates":[0,-1],"properties":{"name":"Under_Point"}}
]
}
},
"arcs": [
[[1,2],[0,-2]],
[[1,0],[-1,0],[0,2],[1,0]],
[[1,2],[1,0],[0,-2],[-1,0]],
[[0,-1],[2,0]]
]
}
뭔가 느낌상 arcs는 공유되는 자원인거 같고, geometries 안에 arcs에서 가져다가 사용하는 느낌... 인 듯 합니다.
우리나라 차트를 보여주고 싶다면 우리나라 GeoJSON 포맷이 있어야 될 것입니다. 혹은 더 나아가서 제가 사는 시흥시만 보여주고 싶다면 또 그에 따른 GeoJSON 포맷이 있어야 합니다. 이걸 어떻게 구할까요? 이미 깃허브에 있습니다. ㅎㅎ
참고 : https://github.com/vuski/admdongkor
참고 2: https://business.juso.go.kr/addrlink/adresInfoProvd/guidance/provdAdresInfo.do (여기서 '구역의 도형'을 신청하여 받으면 됩니다.)
저는 신청하는게 귀찮아서 깃허브에서 ver20230701 를 다운받아 사용하기로 했습니다. 33.2 MB 라는 꽤 큰 용량이더군요. 다운로드 했으니 봐야겠죠? 근데 어디서 봐야될까요...ㅠㅠ
찾아보니 kepler 서비스 라는 곳에서 간단하게 무료로 볼 수 있었습니다. 오오!! 모든 지역정보까지 다 나눠놓은것을 볼 수 있습니다.
그리고 데이터를 살펴보니 여러가지 정보가 있었습니다. 그 중에서 adm_cd, adm_cd2를 중점적으로 볼 필요가 있습니다.
저는 오늘 동이 2개라는 사실을 처음 알게되었습니다. ㅋㅋㅋㅋ 간단하게 의미를 살펴보면 다음과 같습니다.
예를 들어, 서울특별시 중구 명동은 법정동으로보면 무교동, 다동, 태평로1가, 을지로1가, 을지로2가, 남대문로1가, 삼각동, 수하동, 장교동,… 등 16개로 잘게 쪼개져있는데, 도심 공동화로 인구는 점점 줄어들어 행정수요가 줄었기 때문에 이들을 모두 포함하는 '명동'이라는 하나의 행정동으로 운영하고 명동 주민센터 하나만 설치되어있다. - 나무위키 참고
근데... 자료를 찾다보니 법정동코드목록조회는 쉽게 찾을 수 있었습니다. 하지만 행정동코드는 자료 찾기가 좀 힘들더군요. 결국 최신 자료를 못찾았습니다. 그래서 그냥 저는 법정동을 사용하기로 했습니다. 다른 곳에서도 법정동을 사용하는거 같고요.
✍️ 참고 : http://www.gisdeveloper.co.kr/?p=2332 (여기 시도, 시군구, 읍면동 기준으로 만든 자료가 있습니다.)
전체 GeoJSON 포맷을 구했지만 아직 제가 사는 시흥시에 대한 GeoJSON 포맷은 구하지 못했습니다. 이를 구하기 위해서는 필터링을 해야 됩니다. 하지만 이상하게 kepler 서비스에서 adm_cd2 타입을 time으로 인식하더군요. adm_cd는 int 타입이면서... 어째서 이런일이.. 결국 방법을 찾을 수 없었습니다.
참고 : https://medium.com/@masoolsa7/행정구역-경계-topojson-파일-만들기-392800d0bc0b
참고 2 : https://blog.naver.com/flower756/222393681058
대신 QGIS 라는 오픈 소스 프로그램에 대해 알게되었습니다. 근데 문제는 좀 무겁다는 것과 (설치하면 3GB...), 다운로드 시간이 좀 걸립니다. 그리고 Mac에서는 이 프로그램을 악성 프로그램인지 판단할 수 없으므로 안 열립니다. 설정을 통해 열 수 있긴 한데 살짝 꺼림직한 측면도 있습니다.
아무튼 별수 없으니 QGIS를 설치하고 열어봤습니다. 잘 되더군요.
여기서 표현식 필터링을 통해 시흥시에 해당하는 동 영역만 불러오도록 해줍니다.
시흥시 선택이 잘되었습니다. 이제 선택된 영역을 내보내기를 해줍니다.
여기서 주의할 점은 파일 이름만 작성하고 내보내려고 하면 “Failed to create GeoJSON datasource:” 에러를 볼 수 있습니다. 이는 파일 쓰기 권한이 없어서 그런건데, 파일 저장하려는 위치를 봤더니 루트였습니다. 위치를 수정해줬더니 성공적으로 저장되었습니다.
그리고 나서 https://mapshaper.org 사이트에 접속해서 만든 파일을 올리고 import 해줍니다. 이를 topojson로 export 해줍니다.
Chart.js Geo 를 이용해 위에서 만든 시흥시 TopoJSON를 차트로 보여주는 예시입니다. 그냥 하면 재미없으니 시흥시 법정동별 인구수 자료를 찾아서 TopoJSON properties 속성에 추가해주었습니다.
{
"arcs": [[42, -41, 43, -34, 44, 45]],
"type": "Polygon",
"properties": {
"adm_nm": "경기도 시흥시 군자동",
"adm_cd": "3115068",
"adm_cd2": "4139058100",
"sgg": "41390",
"sido": "41",
"sidonm": "경기도",
"temp": "시흥시 군자동",
"sggnm": "시흥시",
"adm_cd8": "31150680",
"population": 9267
}
},
import Chart from "chart.js/auto";
import {
topojson,
ChoroplethController,
ProjectionScale,
ColorScale,
GeoFeature,
} from "chartjs-chart-geo";
export default async function () {
Chart.register(ChoroplethController, ProjectionScale, ColorScale, GeoFeature);
const ctx = document.getElementById("myChart")! as HTMLCanvasElement;
fetch("siheung.json")
.then((r) => r.json())
.then((data) => {
const nation = topojson.feature(data, data.objects.siheung).features[0];
const states = topojson.feature(data, data.objects.siheung).features;
const chart = new Chart(ctx, {
type: "choropleth",
data: {
labels: states.map((d) => d.properties.adm_nm),
datasets: [
{
label: "States",
outline: nation,
data: states.map((d) => ({
feature: d,
value: d.properties.population,
})),
},
],
},
options: {
showOutline: false,
showGraticule: false,
plugins: {
legend: {
display: false,
},
},
scales: {
projection: {
axis: "x",
projection: "mercator", // 메르카토르 투영법 설정 : 구형인 지구를 평면으로 표현하는 방법
projectionScale: 1, // 스케일 배율 조절하는게 아닌가?
projectionOffset: [50, -150], // 원점 조절
padding: 210, // 차트의 크기 조절?
},
},
},
});
});
}
드디어 완성입니다. 월곶동 인구수는 생각보다 얼마 안되었군요. 만명은 넘을 줄 알았더니... 확실히 시흥시는 여기 따로 저기 따로 인거 같습니다. 정왕동 중심 생활권, 신천-대야-은행 중심 생활권, 그 외 나머지 지역들... ㅋㅋㅋ
GeoJSON, TopoJSON 이라는 포맷을 배우고 이를 조작해서 제가 사는 지역에 대한 차트를 시각화 해봤다는게 너무 재미있었습니다. 새로운 사실에 대해 알게 되는 즐거움? 그리고 이를 통해 많은 다양한 것들을 해볼 수 있을 거 같다는 기대감도 들었습니다.