├── README.md
├── part_1
├── css
│ └── style.css
├── index.html
└── js
│ ├── chartStyles.js
│ └── script.js
└── part_2
├── css
└── style.css
├── index.html
└── js
├── chartStyles.js
└── script.js
/README.md:
--------------------------------------------------------------------------------
1 | # Webinar visualización de datos web con ChartJS
2 |
3 | Materiales del webinar sobre **visualización de datos con ChartJS** impartido desde Ironhack Madrid | 45 min - abr. 2020
4 |
5 | Explora algunas de las posibilidades de [Chartjs](https://www.chartjs.org/) conectándolo a una [API de montañas rusas](https://github.com/german-alvarez-dev/api-coasters).
6 |
7 | ## Grabaciones
8 | - Vídeo completo del webinar (parte 1) [en este enlace](https://youtu.be/1R1tNZMnMmU?t=815).
9 | - Vídeo completo del webinar (parte 2) [en este enlace](https://youtu.be/Hpa3Pdb7p5U)
10 |
11 | ## Instalación
12 | No requiere instalación.
13 |
14 | ## Ejecución
15 | Ejecutar archivo `index.html` en el navegador.
16 |
--------------------------------------------------------------------------------
/part_1/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #181f38;
3 | font-family: 'Poppins', monospace;
4 | color: white;
5 | font-size: .7em;
6 | font-weight: 200;
7 | letter-spacing: 1px;
8 | }
9 |
10 | html {
11 | padding: 20px;
12 | font-weight: 300;
13 | }
14 |
15 | main {
16 | padding: 60px;
17 | }
18 |
19 | section {
20 | margin-bottom: 40px;
21 | }
22 |
23 | h1 {
24 | font-size: 3em;
25 | font-weight: 100;
26 | }
27 |
28 | h2, h3 {
29 | font-size: 1.4em;
30 | margin-bottom: 29px;
31 | border-bottom: 1px solid rgba(255,255,255,.2);
32 | padding-bottom: 15px;
33 | font-weight: 200;
34 | }
35 |
36 | hr {
37 | border-color: white;
38 | margin-bottom: 50px;
39 | }
40 |
41 | figure {
42 | background-color: rgba(255,255,255,.05);
43 | padding: 10px 10px 30px;
44 | }
45 |
46 | figure p {
47 | margin-bottom: 7px;
48 | font-size: .8em;
49 | }
50 |
51 | canvas {
52 | width: 100%;
53 | }
54 |
55 |
56 | /* grid */
57 |
58 | [class*='cols'] {
59 | display: flex;
60 | flex-direction: row;
61 | flex-wrap: nowrap;
62 | }
63 |
64 | .cols-3 > * {
65 | width: 33.33%;
66 | margin: 10px
67 | }
68 |
69 | .cols-2 > * {
70 | width: 50%;
71 | margin: 10px
72 | }
73 |
74 | .cols-1 > * {
75 | width: 100%;
76 | margin: 10px
77 | }
78 |
79 | #chart6 {
80 | max-height: 300px;
81 | }
82 |
83 | section {
84 | opacity: 0;
85 | transition: opacity .3s;
86 | }
87 |
88 | .running section {
89 | opacity: 1
90 | }
91 |
92 | .loading {
93 | position: absolute;
94 | border: none;
95 | }
96 |
97 | .running .loading {
98 | display: none;
99 | }
--------------------------------------------------------------------------------
/part_1/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Visualización de datos con ChartJS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Montañas rusas: los datos
17 | Visualización de estadísticas globales
18 |
19 |
20 |
21 | Esperando respuesta de la API...
22 |
23 |
24 |
25 | Gráfico radial
26 | Comparativa global
27 |
28 |
29 |
30 |
31 | Gráfico de área polar
32 | Montañas rusas por país
33 |
34 |
35 |
36 |
37 |
38 |
39 | Gráfico lineal
40 | Construcciones por año
41 |
42 |
43 |
44 |
45 |
46 |
47 | Gráfico radial
48 | Altura
49 |
50 |
51 |
52 |
53 | Gráfico de donut
54 | Modelos
55 |
56 |
57 |
58 |
59 | Gráfico de barras
60 | Fuerza G
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/part_1/js/chartStyles.js:
--------------------------------------------------------------------------------
1 | const styles = {
2 | color: {
3 | solids: ['rgba(116, 72, 194, 1)', 'rgba(33, 192, 215, 1)', 'rgba(217, 158, 43, 1)', 'rgba(205, 58, 129, 1)', 'rgba(156, 153, 204, 1)', 'rgba(225, 78, 202, 1)'],
4 | alphas: ['rgba(116, 72, 194, .2)', 'rgba(33, 192, 215, .2)', 'rgba(217, 158, 43, .2)', 'rgba(205, 58, 129, .2)', 'rgba(156, 153, 204, .2)', 'rgba(225, 78, 202, .2)']
5 | }
6 | }
--------------------------------------------------------------------------------
/part_1/js/script.js:
--------------------------------------------------------------------------------
1 | // API Ajax call
2 | fetch('https://coasters-api.herokuapp.com/')
3 | .then(response => response.json())
4 | .then(data => printCharts(data))
5 |
6 |
7 | function printCharts(coasters) {
8 |
9 | // Remove loading message, show chart panels
10 | document.body.classList.add('running')
11 |
12 | // Call each chart function passing the coasters and DOM Canvas tag ID to be rendered
13 | compareRadialChart(coasters, 'chart2')
14 | modelDoughnutChart(coasters, 'chart4')
15 | heightRadarChart(coasters, 'chart3')
16 |
17 | }
18 |
19 |
20 | function compareRadialChart(coasters, id) {
21 |
22 | // Every ChartJS chart needs data with labels and datasets
23 | const data = {
24 | labels: ['EEUU', 'UK', 'España', 'Japón', 'China'],
25 | datasets: [ // datasets is an Array of Objects, each Object contains one set of info/styles to be shown. In many charts, multiple sets of info can be rendered if multiple Objets are passed to the datasets Array
26 | {
27 | data: [
28 | coasters.filter(eachCoaster => eachCoaster.country === 'United States').length,
29 | coasters.filter(eachCoaster => eachCoaster.country === 'United Kingdom').length,
30 | coasters.filter(eachCoaster => eachCoaster.country === 'Spain').length,
31 | coasters.filter(eachCoaster => eachCoaster.country === 'Japan').length,
32 | coasters.filter(eachCoaster => eachCoaster.country === 'China').length
33 | ],
34 | borderWidth: 1,
35 | borderColor: styles.color.solids.map(eachColor => eachColor),
36 | backgroundColor: styles.color.alphas.map(eachColor => eachColor)
37 | }
38 | ]
39 | }
40 |
41 | // Every ChartJs chart can have multiple layout options
42 | const options = {
43 | scale: {
44 | gridLines: {
45 | color: '#444'
46 | },
47 | ticks: {
48 | display: false
49 | }
50 | },
51 | legend: {
52 | position: 'right',
53 | labels: {
54 | fontColor: '#fff'
55 | }
56 | }
57 | }
58 |
59 | // Every ChartJS chart receives two arguments: the Canvas id to place the chart, and an object with: chart type, data to show, layout options object (optional)
60 | new Chart(id, { type: 'polarArea', data, options })
61 | }
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 | function modelDoughnutChart(coasters, id) {
71 |
72 | const data = {
73 | labels: ['Propulsada', 'Hiper montaña', 'Giga montaña', 'Inversión', 'Sentado'],
74 | datasets: [
75 | {
76 | data: [
77 | coasters.filter(eachCoaster => eachCoaster.model === 'Accelerator Coaster').length,
78 | coasters.filter(eachCoaster => eachCoaster.model === 'Hyper Coaster').length,
79 | coasters.filter(eachCoaster => eachCoaster.model === 'Giga Coaster').length,
80 | coasters.filter(eachCoaster => eachCoaster.model === 'Multi Inversion Coaster').length,
81 | coasters.filter(eachCoaster => eachCoaster.model === 'Sitting Coaster').length
82 | ],
83 | borderColor: styles.color.solids.map(eachColor => eachColor),
84 | backgroundColor: styles.color.alphas.map(eachColor => eachColor),
85 | borderWidth: 1
86 | }
87 | ]
88 | }
89 |
90 | const options = {
91 | legend: {
92 | position: 'right',
93 | labels: {
94 | fontColor: '#fff'
95 | }
96 | }
97 | }
98 |
99 | new Chart(id, { type: 'doughnut', data, options })
100 | }
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 | function heightRadarChart(coasters, id) {
109 |
110 | const selectedCoasters = coasters.filter(eachCoaster => eachCoaster.height > 80)
111 |
112 | const data = {
113 | labels: selectedCoasters.map(eachCoaster => eachCoaster.name),
114 | datasets: [
115 | {
116 | label: 'Altura',
117 | data: selectedCoasters.map(eachCoaster => eachCoaster.height),
118 | borderColor: styles.color.solids[0],
119 | borderWidth: 1
120 | }
121 | ]
122 | }
123 |
124 | const options = {
125 | scale: {
126 | gridLines: {
127 | color: '#444'
128 | },
129 | pointLabels: {
130 | fontColor: '#fff'
131 | },
132 | ticks: {
133 | display: false
134 | }
135 | },
136 | legend: {
137 | display: false
138 | }
139 | }
140 |
141 | new Chart(id, { type: 'radar', data, options })
142 | }
--------------------------------------------------------------------------------
/part_2/css/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #181f38;
3 | font-family: 'Poppins', monospace;
4 | color: white;
5 | font-size: .7em;
6 | font-weight: 200;
7 | letter-spacing: 1px;
8 | }
9 |
10 | html {
11 | padding: 20px;
12 | font-weight: 300;
13 | }
14 |
15 | main {
16 | padding: 60px;
17 | }
18 |
19 | section {
20 | margin-bottom: 40px;
21 | }
22 |
23 | h1 {
24 | font-size: 3em;
25 | font-weight: 100;
26 | }
27 |
28 | h2, h3 {
29 | font-size: 1.4em;
30 | margin-bottom: 29px;
31 | border-bottom: 1px solid rgba(255,255,255,.2);
32 | padding-bottom: 15px;
33 | font-weight: 200;
34 | }
35 |
36 | hr {
37 | border-color: white;
38 | margin-bottom: 50px;
39 | }
40 |
41 | figure {
42 | background-color: rgba(255,255,255,.05);
43 | padding: 10px 10px 30px;
44 | }
45 |
46 | figure p {
47 | margin-bottom: 7px;
48 | font-size: .8em;
49 | }
50 |
51 | canvas {
52 | width: 100%;
53 | }
54 |
55 |
56 | /* grid */
57 |
58 | [class*='cols'] {
59 | display: flex;
60 | flex-direction: row;
61 | flex-wrap: nowrap;
62 | }
63 |
64 | .cols-3 > * {
65 | width: 33.33%;
66 | margin: 10px
67 | }
68 |
69 | .cols-2 > * {
70 | width: 50%;
71 | margin: 10px
72 | }
73 |
74 | .cols-1 > * {
75 | width: 100%;
76 | margin: 10px
77 | }
78 |
79 | #chart6 {
80 | max-height: 300px;
81 | }
82 |
83 | section {
84 | opacity: 0;
85 | transition: opacity .3s;
86 | }
87 |
88 | .running section {
89 | opacity: 1
90 | }
91 |
92 | .loading {
93 | position: absolute;
94 | border: none;
95 | }
96 |
97 | .running .loading {
98 | display: none;
99 | }
--------------------------------------------------------------------------------
/part_2/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Visualización de datos con ChartJS
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Montañas rusas: los datos
17 | Visualización de estadísticas globales
18 |
19 |
20 |
21 | Esperando respuesta de la API...
22 |
23 |
24 |
25 | Gráfico radial
26 | Comparativa global
27 |
28 |
29 |
30 |
31 | Gráfico de área polar
32 | Montañas rusas por país
33 |
34 |
35 |
36 |
37 |
38 |
39 | Gráfico lineal
40 | Construcciones por año
41 |
42 |
43 |
44 |
45 |
46 |
47 | Gráfico radial
48 | Altura
49 |
50 |
51 |
52 |
53 | Gráfico de donut
54 | Modelos
55 |
56 |
57 |
58 |
59 | Gráfico de barras
60 | Fuerza G
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/part_2/js/chartStyles.js:
--------------------------------------------------------------------------------
1 | const styles = {
2 | color: {
3 | solids: ['rgba(116, 72, 194, 1)', 'rgba(33, 192, 215, 1)', 'rgba(217, 158, 43, 1)', 'rgba(205, 58, 129, 1)', 'rgba(156, 153, 204, 1)', 'rgba(225, 78, 202, 1)'],
4 | alphas: ['rgba(116, 72, 194, .2)', 'rgba(33, 192, 215, .2)', 'rgba(217, 158, 43, .2)', 'rgba(205, 58, 129, .2)', 'rgba(156, 153, 204, .2)', 'rgba(225, 78, 202, .2)']
5 | }
6 | }
--------------------------------------------------------------------------------
/part_2/js/script.js:
--------------------------------------------------------------------------------
1 | // Defaults
2 | Chart.defaults.global.defaultFontColor = '#fff'
3 | Chart.defaults.global.elements.line.borderWidth = 1
4 | Chart.defaults.global.elements.rectangle.borderWidth = 1
5 | Chart.defaults.scale.gridLines.color = '#444'
6 | Chart.defaults.scale.ticks.display = false
7 |
8 |
9 |
10 | fetch('https://coasters-api.herokuapp.com/')
11 | .then(response => response.json())
12 | .then(data => printCharts(data))
13 |
14 |
15 | function printCharts(coasters) {
16 |
17 | document.body.classList.add('running')
18 |
19 | compareRadialChart(coasters, 'chart2')
20 | modelDoughnutChart(coasters, 'chart4')
21 | heightRadarChart(coasters, 'chart3')
22 | GForceBarsChart(coasters, 'chart5')
23 | countriesRadarChart(coasters, 'chart1')
24 | yearsBarChart(coasters, 'chart6')
25 |
26 | }
27 |
28 |
29 | function compareRadialChart(coasters, id) {
30 |
31 | const data = {
32 | labels: ['EEUU', 'UK', 'España', 'Japón', 'China'],
33 | datasets: [
34 | {
35 | data: [
36 | coasters.filter(eachCoaster => eachCoaster.country === 'United States').length,
37 | coasters.filter(eachCoaster => eachCoaster.country === 'United Kingdom').length,
38 | coasters.filter(eachCoaster => eachCoaster.country === 'Spain').length,
39 | coasters.filter(eachCoaster => eachCoaster.country === 'Japan').length,
40 | coasters.filter(eachCoaster => eachCoaster.country === 'China').length
41 | ],
42 | borderWidth: 1,
43 | borderColor: styles.color.solids.map(eachColor => eachColor),
44 | backgroundColor: styles.color.alphas.map(eachColor => eachColor)
45 | }
46 | ]
47 | }
48 |
49 | const options = {
50 | legend: {
51 | position: 'right'
52 | }
53 | }
54 |
55 | new Chart(id, { type: 'polarArea', data, options })
56 | }
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 | function modelDoughnutChart(coasters, id) {
66 |
67 | const data = {
68 | labels: ['Propulsada', 'Hiper montaña', 'Giga montaña', 'Inversión', 'Sentado'],
69 | datasets: [
70 | {
71 | data: [
72 | coasters.filter(eachCoaster => eachCoaster.model === 'Accelerator Coaster').length,
73 | coasters.filter(eachCoaster => eachCoaster.model === 'Hyper Coaster').length,
74 | coasters.filter(eachCoaster => eachCoaster.model === 'Giga Coaster').length,
75 | coasters.filter(eachCoaster => eachCoaster.model === 'Multi Inversion Coaster').length,
76 | coasters.filter(eachCoaster => eachCoaster.model === 'Sitting Coaster').length
77 | ],
78 | borderColor: styles.color.solids.map(eachColor => eachColor),
79 | backgroundColor: styles.color.alphas.map(eachColor => eachColor),
80 | borderWidth: 1
81 | }
82 | ]
83 | }
84 |
85 | const options = {
86 | legend: {
87 | position: 'right'
88 | }
89 | }
90 |
91 | new Chart(id, { type: 'doughnut', data, options })
92 | }
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 | function heightRadarChart(coasters, id) {
101 |
102 | const selectedCoasters = coasters.filter(eachCoaster => eachCoaster.height > 80)
103 |
104 | const data = {
105 | labels: selectedCoasters.map(eachCoaster => eachCoaster.name),
106 | datasets: [
107 | {
108 | label: 'Altura',
109 | data: selectedCoasters.map(eachCoaster => eachCoaster.height),
110 | borderColor: styles.color.solids[0],
111 | borderWidth: 1
112 | }
113 | ]
114 | }
115 |
116 | const options = {
117 | legend: {
118 | display: false
119 | }
120 | }
121 |
122 | new Chart(id, { type: 'radar', data, options })
123 | }
124 |
125 |
126 |
127 | function GForceBarsChart(coasters, id) {
128 |
129 | const selectedCoasters = coasters.filter(eachCoaster => eachCoaster.gForce)
130 |
131 | const data = {
132 | labels: selectedCoasters.map(eachCoaster => eachCoaster.name),
133 | datasets: [{
134 | data: selectedCoasters.map(eachCoaster => eachCoaster.gForce),
135 | backgroundColor: styles.color.alphas,
136 | borderColor: styles.color.solids
137 | }]
138 | }
139 |
140 | const options = {
141 | legend: {
142 | display: false
143 | },
144 | scales: {
145 | yAxes: [{
146 | gridLines: {
147 | display: false
148 | },
149 | ticks: {
150 | display: true
151 | }
152 | }]
153 | }
154 | }
155 |
156 | new Chart(id, { type: 'bar', data, options })
157 |
158 | }
159 |
160 |
161 |
162 | function countriesRadarChart(coasters, id) {
163 |
164 | const selectedCoasters = coasters.filter(eachCoaster => eachCoaster.gForce)
165 |
166 | const data = {
167 | labels: selectedCoasters.map(eachCoaster => eachCoaster.name),
168 | datasets: [
169 | {
170 | label: 'Altura',
171 | data: selectedCoasters.map(eachCoaster => eachCoaster.height),
172 | borderColor: styles.color.solids[0],
173 | backgroundColor: styles.color.alphas[0]
174 | },
175 | {
176 | label: 'Longitud',
177 | data: selectedCoasters.map(eachCoaster => eachCoaster.length),
178 | borderColor: styles.color.solids[1],
179 | backgroundColor: styles.color.alphas[1],
180 | hidden: true
181 | },
182 | {
183 | label: 'Inversiones',
184 | data: selectedCoasters.map(eachCoaster => eachCoaster.inversions),
185 | borderColor: styles.color.solids[2],
186 | backgroundColor: styles.color.alphas[2]
187 | },
188 | {
189 | label: 'Velocidad',
190 | data: selectedCoasters.map(eachCoaster => eachCoaster.speed),
191 | borderColor: styles.color.solids[3],
192 | backgroundColor: styles.color.alphas[3]
193 | },
194 | {
195 | label: 'Fuerza G',
196 | data: selectedCoasters.map(eachCoaster => eachCoaster.gForce),
197 | borderColor: styles.color.solids[4],
198 | backgroundColor: styles.color.alphas[4]
199 | }
200 | ]
201 | }
202 |
203 | const options = {
204 | legend: {
205 | position: 'left'
206 | }
207 | }
208 |
209 | new Chart(id, { type: 'radar', data, options })
210 | }
211 |
212 |
213 | function yearsBarChart(coasters, id) {
214 |
215 |
216 | const data = {
217 | labels: ['1995-1997', '1998-2000', '2001-2003', '2004-2006', '2007-2009', '2013-2015', '2016-2018', '2019-2021'],
218 | datasets: [
219 | {
220 | label: 'Montañas creadas',
221 | borderColor: styles.color.solids[5],
222 | data: [
223 | coasters.filter(eachCoaster => eachCoaster.year >= 1995 && eachCoaster.year <= 1997).length,
224 | coasters.filter(eachCoaster => eachCoaster.year >= 1998 && eachCoaster.year <= 2000).length,
225 | coasters.filter(eachCoaster => eachCoaster.year >= 2001 && eachCoaster.year <= 2003).length,
226 | coasters.filter(eachCoaster => eachCoaster.year >= 2004 && eachCoaster.year <= 2006).length,
227 | coasters.filter(eachCoaster => eachCoaster.year >= 2007 && eachCoaster.year <= 2009).length,
228 | coasters.filter(eachCoaster => eachCoaster.year >= 2010 && eachCoaster.year <= 2012).length,
229 | coasters.filter(eachCoaster => eachCoaster.year >= 2013 && eachCoaster.year <= 2015).length,
230 | coasters.filter(eachCoaster => eachCoaster.year >= 2016 && eachCoaster.year <= 2018).length,
231 | coasters.filter(eachCoaster => eachCoaster.year >= 2019 && eachCoaster.year <= 2021).length
232 | ]
233 | },
234 | {
235 | type: 'bar',
236 | label: 'Aceleración',
237 | borderColor: styles.color.solids[3],
238 | backgroundColor: styles.color.solids[3],
239 | data: [
240 | coasters.filter(eachCoaster => eachCoaster.year >= 1995 && eachCoaster.year <= 1997 && eachCoaster.model === 'Hyper Coaster').length,
241 | coasters.filter(eachCoaster => eachCoaster.year >= 1998 && eachCoaster.year <= 2000 && eachCoaster.model === 'Hyper Coaster').length,
242 | coasters.filter(eachCoaster => eachCoaster.year >= 2001 && eachCoaster.year <= 2003 && eachCoaster.model === 'Hyper Coaster').length,
243 | coasters.filter(eachCoaster => eachCoaster.year >= 2004 && eachCoaster.year <= 2006 && eachCoaster.model === 'Hyper Coaster').length,
244 | coasters.filter(eachCoaster => eachCoaster.year >= 2007 && eachCoaster.year <= 2009 && eachCoaster.model === 'Hyper Coaster').length,
245 | coasters.filter(eachCoaster => eachCoaster.year >= 2010 && eachCoaster.year <= 2012 && eachCoaster.model === 'Hyper Coaster').length,
246 | coasters.filter(eachCoaster => eachCoaster.year >= 2013 && eachCoaster.year <= 2015 && eachCoaster.model === 'Hyper Coaster').length,
247 | coasters.filter(eachCoaster => eachCoaster.year >= 2016 && eachCoaster.year <= 2018 && eachCoaster.model === 'Hyper Coaster').length,
248 | coasters.filter(eachCoaster => eachCoaster.year >= 2019 && eachCoaster.year <= 2021 && eachCoaster.model === 'Hyper Coaster').length
249 | ]
250 | }
251 | ]
252 | }
253 |
254 | const options = {
255 | maintainAspectRatio: false,
256 | scaleFontColor: '#fff',
257 | scales: {
258 | yAxes: [{
259 | ticks: {
260 | display: true
261 | }
262 | }],
263 | xAxes: [{
264 | barPercentage: 0.4,
265 | ticks: {
266 | display: true
267 | }
268 | }]
269 | }
270 | }
271 |
272 | new Chart(id, { type: 'line', data, options })
273 |
274 | }
275 |
--------------------------------------------------------------------------------