-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1166 lines (890 loc) · 44.3 KB
/
index.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
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<link href="./static/tailwind.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/font/bootstrap-icons.min.css">
<link href="https://unpkg.com/[email protected]/dist/aos.css" rel="stylesheet">
<script src="./static/iso-3-to-iso-2.js"></script>
<script src="https://unpkg.com/@dotlottie/player-component@latest/dist/dotlottie-player.mjs" type="module"></script>
<title>Leading AI</title>
<!-- Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Manrope:wght@800&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Sans:wght@400;600&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Anton&display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Rubik+Mono+One&display=swap" rel="stylesheet">
<!-- Load d3.js -->
<script src="https://d3js.org/d3.v6.js"></script>
<script src="https://code.jquery.com/jquery-3.7.1.js" integrity="sha256-eKhayi8LEQwp4NKxN+CfCh+3qOVUtJn3QNZ0TciWLP4=" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/ScrollMagic.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/ScrollMagic/2.0.7/plugins/debug.addIndicators.js"></script>
<script src="https://unpkg.com/[email protected]/dist/aos.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/particlesjs/2.2.3/particles.min.js" integrity="sha512-jq8sZI0I9Og0nnZ+CfJRnUzNSDKxr/5Bvha5bn7AHzTnRyxUfpUArMzfH++mwE/hb2efOo1gCAgI+1RMzf8F7g==" crossorigin="anonymous" referrerpolicy="no-referrer"></script>
</script>
</head>
<style>
.full-screen {
height: 100dvh;
width: 100vw;
}
.x4-screen {
height: 1000dvh;
width: 100vw;
}
.gradient-1 {
background-size: 100% 100%;
background-position: 0px 0px,0px 0px,0px 0px,0px 0px;
background-image: radial-gradient(75% 75% at 50% 50%, #93A7E30F 0%, #0C003BA8 100%),radial-gradient(75% 75% at 75% 75%, #FF00003B 0%, #073AFF03 100%),radial-gradient(65% 65% at 25% 50%, #7E00FF42 0%, #19000040 73%),linear-gradient(152deg, #000000FF 1%, #5B00E2FF 100%);
}
.gradient-2 {
background-size: 100% 100%;
background-position: 0px 0px,0px 0px;
background-image: radial-gradient(75% 75% at 75% 25%, #000535A6 0%, #073AFF00 100%),linear-gradient(0deg, #F0F0E0FF 3%, #151515FF 70%);
}
.gradient-3 {
background-size: 100% 100%;
background-position: 50% -1px,50% -1px,50% -1px,0px 0px,0px 0px;
background-image: conic-gradient(from 288deg at 25% 50%, #6E6E6E0A 0%, #00FFFF05 50%, #8D8D8D0F 100%),conic-gradient(from 190deg at 25% 50%, #6E6E6E0A 0%, #00FFFF05 50%, #9730300F 100%),conic-gradient(from 190deg at 25% 50%, #6E6E6E0A 0%, #00FFFF05 50%, #4530970F 100%),radial-gradient(75% 75% at 77% 50%, #18182799 1%, #002B9E0A 100%),repeating-linear-gradient(158deg, #191919FF 0%, #1C1C1CFF 100%);
}
.manrope-title {
font-family: "Manrope", sans-serif;
font-optical-sizing: auto;
font-weight: 800;
font-style: normal;
}
.ibm-plex-sans-regular {
font-family: "IBM Plex Sans", sans-serif;
font-weight: 400;
font-style: normal;
}
.ibm-plex-sans-semibold {
font-family: "IBM Plex Sans", sans-serif;
font-weight: 600;
font-style: normal;
}
.anton-regular {
font-family: "Anton", sans-serif;
font-weight: 400;
font-style: normal;
}
.indicator1 {
background-color: #e87c6b;
border-color: #83443a;
}
.indicator2 {
background-color: #ffcc75;
border-color: #997943;
}
.indicator3 {
background-color: #75f075;
border-color: #4c9f4c;
}
.indicator4 {
background-color: #59eac8;
border-color: #4da08d;
}
.indicator5 {
background-color: #79ccfc;
border-color: #518daf;
}
.indicator6 {
background-color: #e6a0ff;
border-color: #7e548d;
}
.indicator7 {
background-color: hsl(345, 100%, 77%);
border-color: hsl(345, 32%, 53%);
}
@-webkit-keyframes glow {
to {
-webkit-text-shadow: 0 0 10px #e5d77ea4;
-moz-text-shadow: 0 0 10px #e5d77ea4;
text-shadow: 0 0 10px #e5d77ea4;
}
}
.glower {
-webkit-text-shadow: 0 0 10px #c3c0d0;
-moz-text-shadow: 0 0 10px #c3c0d0;
text-shadow: 0 0 10px #c3c0d0;
animation: glow 1.0s infinite alternate;
-webkit-animation: glow 1.0s infinite alternate;
-webkit-transition: border 1.0s linear, box-shadow 1.0s linear;
-moz-transition: border 1.0s linear, box-shadow 1.0s linear;
transition: border 1.0s linear, box-shadow 1.0s linear;
}
body, html{
touch-action: pan-y;
overflow-x: hidden;
scrollbar-width: none;
}
</style>
<body>
<div id="pinnedContent0" class="grid grid-cols-1 place-items-center absolute pt-16" style="z-index: 1000; width: 100vw; height: 100dvh;">
<div class="place-self-center">
<div class="flex justify-center">
<h1 class="text-white text-4xl md:text-8xl manrope-title p-3 text-center"> Leading AI</h1>
</div>
<div>
<h2 class="text-white p-3 ms-2">A data visualization project</h2>
</div>
</div>
<div class="flex w-full justify-center mt-8 manrope-title place-self-center">
<div class="flexjustify-center p-2 rounded-md" style="background-color: rgba(49, 49, 49, 0.62);">
<div class="flex text-center flex-col" style="opacity: 1;">
<h1 class="text-white break-wrap" style="max-width: 50vw;">
Written by M.Ranzetti, A. Silvestre de Sacy, Q. Esteban
</h1>
</div>
</div>
</div>
</div>
<div id="scene-0" class="gradient-1 full-screen z-10"></div>
<canvas id="particles" class="full-screen absolute top-0 z-"></canvas>
<div class="full-screen absolute p-6 grid grid-cols-1 z-4 gap-6 lg:p-10" style="top:100dvh;">
</div>
<div id="scene-1" class="x4-screen gradient-2 z-3">
</div>
<div id="introText" class="text-white absolute w-full px-5 py-8 md:text-4xl ibm-plex-sans-semibold grid grid-cols-1" style="top:105dvh; width: 100vw;">
<div id="firstIntroText" class="text-center text-2xl container place-self-center">This project is based on <span class="cursor-pointer underline" onclick="window.open('https://www.kaggle.com/datasets/katerynameleshenko/ai-index')">Kaggle data</span> by Kateryna Meleshenko, originally provided by
<span class="cursor-pointer underline" onclick="window.open('https://www.tortoisemedia.com/intelligence/global-ai/')">Tortoise Media</span> </div>
<h2 class="text-justify py-8 container place-self-center">
In past years, we have witnessed an unprecedented boom in the A.I sector. Some may think it is a buzzword, others see it as the way forward for society.
Regardless, the impact of A.I on our society is undeniable. This project aims to visualize the leading nations in the A.I sector compare to each other.
Although strong leaders emerge, we can turn our eyes to the future and imagine who the next important players will be.
</h2>
<h2 class="my-8 container place-self-center">
To calculate a score for each nation, 7 different indicators are used. These indicators are:
<ul class="list-inside mt-8 text-2xl">
<li class="my-8 leading-loose" data-aos="zoom-out-left"><span class="rounded-lg p-1 border-4 indicator1">Talent</span> availability of skilled practitioners for the provision of artificial intelligence solutions</li>
<li class="my-8 leading-loose" data-aos="zoom-out-left"><span class="rounded-lg p-1 border-4 indicator2">Infrastructure</span> reliability and scale of access infrastructure, from electricity and internet, to super computing capabilities</li>
<li class="my-8 leading-loose" data-aos="zoom-out-left"><span class="rounded-lg p-1 border-4 indicator3">Operating Environment</span> regulatory context, and public opinion surrounding artificial intelligence</li>
<li class="my-8 leading-loose" data-aos="zoom-out-left"><span class="rounded-lg p-1 border-4 indicator4">Research</span> extent of specialist research and researchers; investigating the amount of publications and citations in credible academic journals</li>
<li class="my-8 leading-loose" data-aos="zoom-out-left"><span class="rounded-lg p-1 border-4 indicator5">Development</span> capacities for development of fundamental platforms and algorithms upon which innovative artificial intelligence projects rely</li>
<li class="my-8 leading-loose" data-aos="zoom-out-left"><span class="rounded-lg p-1 border-4 indicator6">Government Strategy</span> depth of commitment from national government to artificial intelligence; investigating spending commitments and national strategies</li>
<li class="my-8 leading-loose" data-aos="zoom-out-left"><span class="rounded-lg p-1 border-4 indicator7">Commercial</span> level of startup activity, investment and business initiatives based on artificial intelligence</li>
</ul>
</h2>
<div id="butWhy" class="flex w-full justify-center flex-col my-32">
<h1 class="text-8xl manrope-title glower text-center">But why AI?</h1>
</div>
</div>
<div id="headlines" class="absolute grid grid-cols-1 md:grid-cols-2 2xl:grid-cols-3 w-full gap-12 py-10 px-4 z-4" style="top: 200dvh;">
<div id="headline1" class="flex w-full justify-center mt-4" data-aos="zoom-out-right">
<img src="/static/headlines/1.png" class="w-25 rounded-lg cursor-pointer" alt="" onclick="window.open('https://www.forbes.com/sites/siladityaray/2024/05/08/biden-will-announce-microsofts-33-billion-ai-datacenter-in-wisconsin-today-on-same-site-as-trumps-failed-foxconn-factory/', '_blank');">
</div>
<div class="text-2xl text-white text-center font-bold my-4 place-self-center">The potential of A.I is vast. It can be used to solve some of the most pressing issues of our time, from climate change to healthcare. Investments have skyrocketed in the past few years, all big tech companies have announced some form of AI project.</div>
<div></div>
<div></div>
<div class="text-2xl text-white text-center font-bold my-4 place-self-center">However, with the unprecedented (and still not fully known) abilities granted by AI, profound ethical questions are being raised by a multitude of voices.</div>
<div id="headline2" class="flex w-full justify-center mt-4" data-aos="zoom-out-left">
<img src="/static/headlines/2.png" class="w-25 rounded-lg cursor-pointer" alt="" onclick="window.open('https://www.aljazeera.com/news/2024/3/1/could-ai-exacerbate-inequalities-in-our-world-today', '_blank');">
</div>
<div id="headline3" class="flex w-full justify-center mt-4" data-aos="zoom-out-right">
<img src="/static/headlines/3.png" class="w-25 rounded-lg cursor-pointer" alt="" onclick="window.open('https://www.aljazeera.com/news/2024/3/1/could-ai-exacerbate-inequalities-in-our-world-today', '_blank');">
</div>
<div class="text-2xl text-white text-center font-bold my-4 place-self-center"> It is clear that nations that are able to harness the power of A.I will have a significant strategic advantage in the future. Are we to expect an AI monopoly from a lucky few nations? Is the divide already set?
</div>
<div></div>
<div></div>
<div class="text-2xl text-white text-center font-bold my-4 place-self-center">AI is already here (and here to stay). We can already see geographic inequalities appearing.</div>
<div id="headline4" class="flex w-full justify-center mt-4" data-aos="zoom-out-left">
<img src="/static/headlines/4.png" class="w-25 rounded-lg cursor-pointer" alt="" onclick="window.open('https://www.aljazeera.com/news/2024/3/1/could-ai-exacerbate-inequalities-in-our-world-today', '_blank');">
</div>
<div id="headline5" class="flex w-full justify-center mt-4" data-aos="zoom-out-right">
<img src="/static/headlines/5.png" class="w-25 rounded-lg cursor-pointer" alt="" onclick="window.open('https://www.aljazeera.com/news/2024/3/1/could-ai-exacerbate-inequalities-in-our-world-today', '_blank');">
</div>
<div class="text-2xl text-white text-center font-bold my-4 place-self-center">How can we ensure that A.I is used for the betterment of society? How can we ensure that the benefits of A.I are shared equally among all nations?
<h1 class=" mt-4 text-2xl manrope-title text-black text-center">In this project, we provide a tool to visualize disparities in the AI sector to help decision makers and interested parties make more informed decisions in the near future</h1>
</div>
</div>
<div id="scene-2" class="grid grid-cols-1 justify-items-center">
<div id="graphics" style="width: 100vw; height: 100dvh;">
<div class="w-full h-full grid grid-cols-1 place-content-center" style="background-color: #eeead5; width: 100vw;">
<h1 class="text-center h-full border-10 m-4 rounded" style="
font-family: 'Manrope', sans-serif;
font-weight: 800;
font-style: normal;
font-size: 4vw;
color: #000535A6;
">Scroll to start Data visualization</h1>
<dotlottie-player class="m-4 place-self-center" src="https://lottie.host/400d3cf6-2e30-408d-9c4b-aa02a804d16c/rNHh19YNm8.json" background="transparent" speed="1" style="width: 40vw; max-width: 800px;" loop autoplay></dotlottie-player>
</div>
</div>
</div>
<div id="scene-3" class="full-screen grid grid-cols-1">
<div class="text-center text-2xl md:text-6xl ibm-plex-sans-semibold w-full pt-16" style="color: #cdcdcd; width: 100vw;">
Interactive Map
</div>
<dotlottie-player class="place-self-center my-4" src="https://lottie.host/a3fec1ce-a871-465c-8188-afa219da01b5/2sLsGG5yPp.json" background="transparent" speed="1" style="width: 15%; max-width: 400px; min-width: 150px;" loop autoplay></dotlottie-player>
<h2 class="place-self-center text-center text-sm lg:text-2xl" style="
font-family: 'Manrope', sans-serif;
font-weight: 800;
font-style: normal;
">To visualize the dataset further, we have built an interactive map with which you can explore the A.I. index.</h2>
<div class="button bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded-full place-self-center my-4 cursor-pointer" onclick="window.open('/map')">Open in new tab <i class="bi bi-arrow-right"></i></div>
<div class="button bg-orange-500 hover:bg-orange-700 text-white font-bold py-2 px-4 rounded-full place-self-center my-4 cursor-pointer" onclick="document.getElementById('map').scrollIntoView({behavior:'smooth', block:'start'})">Explore the map <i class="bi bi-arrow-down"></i></div>
<div id="credits" class="flex flex-col place-self-center text-center ibm-plex-sans-regular text-sm md:text-xl lg:text-2xl">
<h2 class="font-bold text-sm md:text-xl lg:text-2xl my-2">Attributions & Credits</h2>
<a href="https://www.flaticon.com/free-icons/technology" title="technology icons">Favicon: Technology icons created by Freepik - Flaticon</a>
<a href="https://www.kaggle.com/datasets/zgrcemta/world-gdpgdp-gdp-per-capita-and-annual-growths" title="technology icons">GDP Dataset by OZGUR CEM TAS from Kaggle.com</a>
<div>This project is based on <span class="cursor-pointer underline" onclick="window.open('https://www.kaggle.com/datasets/katerynameleshenko/ai-index')">Kaggle data</span> by Kateryna Meleshenko, originally provided by
<span class="cursor-pointer underline" onclick="window.open('https://www.tortoisemedia.com/intelligence/global-ai/')">Tortoise Media</span> </div>
</div>
</div>
<div class="full-screen" id="map">
<iframe src="/map" class="w-4/5 h-4/5" style="border: 0; width: 100vw; height: 100dvh;"></iframe>
</div>
<script>
// init ScrollMagic Controller
let controller;
let wHeight = window.innerHeight;
let wWidth = window.innerWidth;
// Disable AOS on mobile devices
if(window.innerWidth < 768) {
// remove all data-aos attributes
let elements = document.querySelectorAll("[data-aos]");
elements.forEach(function(element) {
element.removeAttribute("data-aos");
});
} else {
AOS.init();
}
let currentGraphScene = -1 ;
// list of graphs to be displayed
const graphScenes = [top15Chart, gdpBubbleChart]
function initPage() {
// if the controller is not null, destroy scenes
if (controller) {
controller.destroy(true);
}
console.log("calculating page scenes...");
// create a new controller
controller = new ScrollMagic.Controller();
// Create first scene
new ScrollMagic.Scene({
triggerElement: "#scene-0",
duration: wHeight ,
triggerHook: 0,
reverse: true
})
.setPin("#particles")
.addTo(controller);
// get the y coordinate difference between the top of pinnedContent0 and IntroText, then add it to the height of pinnedContent0
let pinnedContent0Offset = $("#pinnedContent0").offset().top + ($("#pinnedContent0").height() * 1.3);
let introTextOffset = $("#introText").offset().top;
let durationTitle = introTextOffset - pinnedContent0Offset;
// Pin each headline
new ScrollMagic.Scene({
triggerElement: "#scene-1",
duration: wHeight * 1.1,
triggerHook: 0,
reverse: true
})
.setPin("#scene-1")
.addTo(controller);
// pin butWhy for the whole height of headlines
let headlinesHeight = $("#headlines").height();
new ScrollMagic.Scene({
triggerElement: "#butWhy",
duration: headlinesHeight / 2,
triggerHook: 0.3,
reverse: true
})
.setPin("#butWhy")
.addTo(controller);
// Set the top value of headlines to be after the introText section
let introTextHeight = $("#introText").height();
let introTextTop = $("#introText").offset().top;
let headlinesTop = introTextTop + introTextHeight;
$("#headlines").css("top", headlinesTop);
// set scene1 to be the same height as introText + headlines
let scene1Duration = introTextHeight + headlinesHeight - 0.7 * wHeight;
$("#scene-1").css("height", scene1Duration);
// Check to see if we are in landscape or portrait mode
let isPortrait = window.matchMedia("(orientation: portrait)").matches;
console.log("Portrait:" + isPortrait);
// add scene 2
new ScrollMagic.Scene({
triggerElement: "#graphics",
duration: wHeight * 0.7,
triggerHook: 0,
reverse: true
})
.on("progress", function (e) {
// split the progress into 2 sections
let progress = e.progress;
let section = Math.floor(progress * 2);
if (section != currentGraphScene) {
currentGraphScene = section;
let sceneFunc = graphScenes[currentGraphScene];
if (sceneFunc) {
sceneFunc();
}
console.log("Progress: " + section);
}
})
.setPin("#graphics")
.addTo(controller);
// set the top value of the interactive map to be after the ranking section
let rankingTop = $("#scene-2").offset().top + $("#scene-2").height();
//$("#scene-3").css("top", rankingTop);
}
// First init
let lastWidth = $(window).width();
let lastHeight = $(window).height();
initPage();
// Reinit on resize
$(window).resize(function() {
if ($(window).width() != lastWidth || $(window).height() != lastHeight) {
//lastWidth, lastHeight = initPage();
wHeight = window.innerHeight;
wWidth = window.innerWidth;
graphWidth = wWidth;
graphHeight = wHeight;
// if the absolute difference between the last width and the new width is greater than 200, reinit
if (Math.abs(lastWidth - $(window).width()) > 200 || Math.abs(lastHeight -$(window).height()) > 200) {
lastWidth, lastHeight = initPage();
} else {
console.log("No resize needed");
console.log("Width: " + $(window).width() + " Height: " + $(window).height() + " LastWidth: " + lastWidth + " LastHeight: " + lastHeight);
return;
}
// re-trigger the current graph scene if needed
if (currentGraphScene != null && currentGraphScene >= 0) {
let sceneFunc = graphScenes[currentGraphScene];
if (sceneFunc) {
sceneFunc();
}
}
} else {
return;
}
});
// load the json data asynchronously from /static/data-classic.json
let jsonData = null;
let gdpData = null;
let top15;
async function loadData() {
$.getJSON("/static/data-classic.json", function(data) {
jsonData = data;
console.log(jsonData);
console.log("Data loaded");
orderData();
$.getJSON("/static/gdp.json", function(data) {
gdpData = data;
});
});
}
// order the data by the total score and get the top 20 countries
function orderData() {
top15 = [];
items = Object.keys(jsonData).map(function(key) {
return [key, jsonData[key]];
});
items.sort(function(first, second) {
return second[1].TotalScore - first[1].TotalScore;
});
for (let i = 0; i < 15; i++) {
top15.push(items[i]);
}
console.log(top15);
}
loadData();
// Create a D3 js chart showing the top 20 countries
// Target: #rankingCanvas
let graphCanvas = document.getElementById("graphics");
let graphWidth = wWidth;
let graphHeight = wHeight;
let graphMargin = {top: 20, right: 50, bottom: 20, left: 50};
function top15Chart() {
// remove any children in the canvas
d3.select(graphCanvas).selectAll("*").remove();
// add a colored background to the canvas
d3.select(graphCanvas)
.style("background-color", "#eeead5");
const titleMargin = 0.1 * graphHeight;
const x = d3.scaleLinear()
.domain([0, 100])
.range([graphMargin.left, graphWidth - graphMargin.right]);
const y = d3.scaleBand()
.domain(d3.range(15))
.range([graphMargin.top, graphHeight - graphMargin.bottom - titleMargin])
.padding(0.1);
const svg = d3.select(graphCanvas)
.append("svg")
.attr("width", graphWidth)
.attr("height", graphHeight);
svg.append("g")
.selectAll()
.data(top15)
.join("rect")
.attr("x", x(0))
.attr("y", (d, i) => y(i) + titleMargin)
.attr("height", y.bandwidth())
// make rounded corners
.attr("rx", 10)
// animate the rectangle so that it grows from the left
.transition()
.duration(800)
.attr("width", d => x(d[1].TotalScore) - x(0))
svg.append("g")
.selectAll()
.data(top15)
// add a text node for each country
.join("text")
.attr("x", x(2))
.attr("y", (d, i) => y(i) + y.bandwidth() / 2 + titleMargin)
.attr("dy", "0.35em")
.text(d => {
return d[0] ;
})
.attr("fill", "white")
.attr("font-size", "3.5vh")
.attr("font-family", "Rubik Mono One")
.attr("font-weight", "400")
// animate opacity so that it fades in
.attr("opacity", 0)
.transition()
.duration(800)
.attr("opacity", 1);
// Add total score to the right of the bar
svg.append("g")
.selectAll()
.data(top15)
.join("text")
.attr("x", d => x(0))
.attr("y", (d, i) => y(i) + y.bandwidth() / 2 + titleMargin)
.attr("dy", "0.35em")
.text("0")
.attr("fill", "black")
.attr("font-size", "3.5vh")
.attr("font-family", "Rubik Mono One")
.attr("font-weight", "400")
// transition the text so that it increases until reaching the correct value
.transition()
.duration(800)
.tween("text", function(d) {
let i = d3.interpolate(0, d[1].TotalScore);
return function(t) {
// format to 2 decimal places
let rounded = i(t).toFixed(2);
if (rounded == 100) {
rounded = 100; // remove decimal places if 100
}
d3.select(this).text(rounded);
};
})
// transition the position of the text so that it moves to the right
// Use a tween so that if the target x exceeds 80% of the max we cap it and set it to white
.tween("x", function(d) {
let i = d3.interpolate(10, x(d[1].TotalScore));
return function(t) {
let newX = i(t);
if (newX > 0.7 * graphWidth) {
d3.select(this).attr("x", Math.min(Math.max( newX + 10, x(0) + 10), graphWidth - x(2))).attr("fill", "white");
} else {
d3.select(this).attr("x", Math.max( newX + 10, x(0) + 10));
}
};
})
// add a tween that sets the anchoring of the text to the end if it exceeds 80% of the width
.tween("text-anchor", function(d) {
let i = d3.interpolate(10, x(d[1].TotalScore));
return function(t) {
let newX = i(t);
if (newX > 0.7 * graphWidth) {
d3.select(this).attr("text-anchor", "end");
} else {
d3.select(this).attr("text-anchor", "start");
}
};
})
;
const title = "Top 15 Countries in A.I";
// Add a title to the chart
svg.append("g")
.append("text")
.attr("x", graphWidth / 2)
.attr("y", titleMargin / 2)
.attr("dy", "0.35em")
.text(title)
.attr("fill", "black")
.attr("font-size", "4vw")
.attr("font-family", "Rubik Mono One")
.attr("font-weight", "400")
.attr("text-anchor", "middle")
}
function gdpBubbleChart() {
/**
* Create a bubble chart: x = Total Score, y = GDP per capita, size = -, color = Region
* Target: #graphics
*/
// remove any children in the canvas
d3.select(graphCanvas).selectAll("*").remove();
let currentSelectedView = 'default';
// set the margin for the chart
const titleMargin = 0.1 * graphHeight;
// prepare data in the form [ISO 3 code, {Score, GDP, GDPPC, Region}]
const series = Object.keys(jsonData).map(function(key) {
// try to load corresponding data from gdpData
let entry;
let score = jsonData[key].TotalScore;
let gdp = 0;
let gdppc = 0;
let region = jsonData[key].Region;
let polScore = jsonData[key].GovernmentStrategy;
try {
entry = gdpData[key]
gdp = entry.GDP;
gdppc = entry.GDPPerCapita;
} catch (e) {
console.log("Error loading data for " + key);
}
console.log(key, score, gdp, gdppc, region);
return [key, {TotalScore: score, GDP: gdp, GDPPerCapita: gdppc, Region: region, PoliticalScore: polScore}];
});
console.log(series);
// set the x scale to be linear
const x = d3.scaleLinear()
.domain([0, d3.max(series, d => d[1].TotalScore) * 1.1])
.range([graphMargin.left, graphWidth - graphMargin.right]);
// set the y scale to be linear, but invert the domain
const y = d3.scaleLinear()
.domain([-100, d3.max(series, d => d[1].GDPPerCapita) * 1.1])
.range([graphHeight- titleMargin - graphMargin.bottom, graphMargin.top + titleMargin]);
// log the domain of the y scale
console.log(y.domain());
// set the size scale to be linear
const size = d3.scaleLinear()
.domain([0, 100])
.range([0, 70]);
const gdpSizing = d3.scaleSqrt()
.domain([0, d3.max(series, d => d[1].GDP)])
.range([0, 100]);
const polSizing = d3.scalePow()
.domain([0, 100])
.range([10, 50])
.exponent(4);
// set the color scale to be ordinal
const color = d3.scaleOrdinal()
.domain(["Americas", "Africa", "Asia-Pacific", "Europe", "Oceania", "Middle East"])
.range(d3.schemeCategory10);
const politicalColouring = d3.scaleOrdinal()
.domain(["Electoral democracy", "Liberal democracy", "Electoral autocracy", "Closed autocracy"])
.range(["#2FF988", "#00D9D6", "#FF5733", "#C70039"]);
// create the svg element
const svg = d3.select(graphCanvas)
.append("svg")
.attr("width", graphWidth)
.attr("height", graphHeight);
// add the axis to the chart with labels and ticks
const xAxis = g => g
.attr("transform", `translate(0,${graphHeight - graphMargin.bottom - titleMargin})`)
.call(d3.axisBottom(x).ticks(graphWidth / 80).tickSizeOuter(0))
.call(g => g.append("text")
.attr("x", graphWidth - graphMargin.right)
.attr("y", -4)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.attr("text-anchor", "end")
.text("Index Score"));
const yAxis = g => g
.attr("transform", `translate(${graphMargin.left}, 0)`)
.call(d3.axisLeft(y).ticks(graphHeight / 80).tickSizeOuter(0))
.call(g => g.append("text")
.attr("x",10)
.attr("y", titleMargin + 30)
.attr("fill", "currentColor")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("GDP per Capita"));
// add the x axis to the chart
svg.append("g")
.call(xAxis);
// add the y axis to the chart
svg.append("g")
.call(yAxis);
// add a scale on the bottom center for the coloring showing the different colors using d3 color legend
function updateColorLegend(clr) {
// remove the legend if it exists
svg.selectAll(".legend").remove();
console.log("Updating color legend: " + clr.domain());
const legend = svg.append("g")
// set class to legend
.attr("class", "legend")
.attr("transform", `translate(0, ${graphHeight - graphMargin.bottom})`)
.attr("font-family", "Rubik Mono One")
.attr("font-size", 10)
.attr("text-anchor", "middle")
.selectAll("g")
.data(clr.domain())
.join("g")
.attr("transform", (d, i) => `translate(${i * (wWidth / clr.domain().length)}, -20)`);
legend.append("rect")
.attr("x", 0)
.attr("width", (wWidth / clr.domain().length))
.attr("height", 40)
.attr("fill", clr)
.attr("fill-opacity", 0.6);
legend.append("text")
.attr("x", (wWidth / clr.domain().length / 2))
.attr("y", 20)
.attr("dy", "0.35em")
.text(d => d)
.attr("fill", "black")
.attr("font-size", "1vw");
console.log("final domain: " + clr.domain());
}
updateColorLegend(color);
// add the bubbles to the chart
svg.append("g")
.selectAll("circle")
.data(series)
.join("circle")
// set the class of the bubble to "bubble"
.attr("class", "bubble")
.attr("fill", d => color(d[1].Region))
.attr("fill-opacity", 0.6)
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("cx", d => x(0))
.attr("cy", d => y(0))
.attr("r", 5)
// first transition the position only
.transition()
.duration(1500)
.attr("cx", d => x(d[1].TotalScore))
.attr("cy", d => y(d[1].GDPPerCapita))
// then transition the size
.transition()
.duration(800)
.attr("r", d => 15);
// add short labels for each bubble
svg.append("g")
.selectAll("text")
.data(series)
.join("text")
.attr("x", d => x(d[1].TotalScore))
.attr("y", d => y(d[1].GDPPerCapita))
.attr("dy", "0.3em")
.text(d => d[0])
.attr("fill", "black")
.attr("font-size", "0.5em")
.attr("font-family", "Rubik Mono One")
.attr("font-weight", "200")
.attr("text-anchor", "middle")
.attr("opacity", 0)
.transition()
.duration(800)
.attr("opacity", 1);
// add an interactive popup for each bubble on hover
svg.append("g")
.selectAll("circle")
.data(series)
.join("circle")
.attr("fill", "none")
.attr("pointer-events", "all")
// set the class to "tooltip-detector"
.attr("class", "tooltip-detector")
.attr("stroke", "none")
.attr("cx", d => x(d[1].TotalScore))
.attr("cy", d => y(d[1].GDPPerCapita))
.attr("r", d => {
if (currentSelectedView == 'default') {
return 10;
} else {
return gdpSizing(d[1].GDP);
}
})
.on("mouseover", function(e, d) {
d3.select(this)
.attr("stroke", "black")
.attr("stroke-width", 2)
.attr("r", d => {
if (currentSelectedView == 'default') {
return 15;
} else if (currentSelectedView == 'gdp') {
return gdpSizing(d[1].GDP) + 5;
} else if (currentSelectedView == 'political') {
return polSizing(d[1].PoliticalScore) + 5;
}
});
// add a tooltip to the chart
svg.append("g")
.attr("id", "tooltip")
// bound the position so that it doesn't go off the screen
.attr("x", Math.min(x(d[1].TotalScore) + 30, graphWidth - 200))
.attr("y", Math.min(y(d[1].GDPPerCapita) + 40, graphHeight - graphMargin.bottom - 150))
.append("text")
// attach the position of the parent
.attr("x", Math.min(x(d[1].TotalScore) + 30, graphWidth - 200))
.attr("y", Math.min(y(d[1].GDPPerCapita) + 40, graphHeight - graphMargin.bottom - 150))
.text(jsonData[d[0]].Country)
// give a max width for the text and make sure it creates new lines
.attr("fill", "black")
.attr("font-size", "1em")
.attr("font-family", "Rubik Mono One")
.attr("font-weight", "400")
.call(wrap, 200)
// return to the parent
.select(function() {
return this.parentNode;
})
.append("text")
.attr("x", Math.min(x(d[1].TotalScore) + 30, graphWidth - 200))
.attr("y", Math.min(y(d[1].GDPPerCapita) + 40 + 40, graphHeight - graphMargin.bottom - 150 + 40))
.text(() => {
if (currentSelectedView == 'default') {
return "Region: " + jsonData[d[0]].Region;
} else if (currentSelectedView == 'gdp') {
let formatedGDP = d3.format(",.0f")(d[1].GDP / 1000000);
return "GDP: " + formatedGDP + " M$";
} else if (currentSelectedView == 'political') {
return "GS score: " + jsonData[d[0]].GovernmentStrategy;
}
})
.attr("fill", "black")
.attr("font-size", "1em")
.attr("font-family", "Rubik Mono One")
.attr("font-weight", "400")
.call(wrap, 200)
// return to the parent and add a white background
.select(function() {
return this.parentNode;
})
.insert("rect", "text")
.attr("x", Math.min(x(d[1].TotalScore) + 30, graphWidth - 200) - 5)
.attr("y", Math.min(y(d[1].GDPPerCapita) + 40, graphHeight - graphMargin.bottom - 150) - 20)
.attr("width", 200)
.attr("height", 90)
.attr("fill", "white")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("rx", 5)
.attr("ry", 5);