forked from security_vision/semantic_graph
Title placement
This commit is contained in:
parent
9a79cea9d7
commit
2dda26ff77
3 changed files with 268 additions and 90 deletions
227
www/graph.css
227
www/graph.css
|
@ -1,5 +1,11 @@
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Lexend Mega Regular';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: normal;
|
||||||
|
src: local('Lexend Mega Regular'), url('LexendMega-Regular.woff') format('woff');
|
||||||
|
}
|
||||||
|
|
||||||
:root{
|
:root {
|
||||||
--color1: #f94144;
|
--color1: #f94144;
|
||||||
--color2: #f3722c;
|
--color2: #f3722c;
|
||||||
--color3: #f8961e;
|
--color3: #f8961e;
|
||||||
|
@ -10,7 +16,6 @@
|
||||||
--color8: #4d908e;
|
--color8: #4d908e;
|
||||||
--color9: #577590;
|
--color9: #577590;
|
||||||
--color10: #277da1;
|
--color10: #277da1;
|
||||||
|
|
||||||
--hover-color: var(--color1);
|
--hover-color: var(--color1);
|
||||||
/* --hover-color: var(darkblue); */
|
/* --hover-color: var(darkblue); */
|
||||||
--selected-color: var(--color1);
|
--selected-color: var(--color1);
|
||||||
|
@ -21,7 +26,7 @@ body {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
/* overflow: hidden; */
|
/* overflow: hidden; */
|
||||||
/* background: linear-gradient(to top, #040308, #AD4A28, #DD723C, #fc7001, #dcb697, #9ba5ae, #3e5879, #020b1a); */
|
/* background: linear-gradient(to top, #040308, #AD4A28, #DD723C, #fc7001, #dcb697, #9ba5ae, #3e5879, #020b1a); */
|
||||||
background:linear-gradient(to top, #414141, #99a6b8);
|
background: linear-gradient(to top, #414141, #99a6b8);
|
||||||
font-family: sans-serif;
|
font-family: sans-serif;
|
||||||
min-height: 100vh;
|
min-height: 100vh;
|
||||||
}
|
}
|
||||||
|
@ -35,115 +40,166 @@ svg.dragging {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg .links line,svg .links path{
|
svg .links line, svg .links path {
|
||||||
stroke: #f3722c;
|
stroke: #f3722c;
|
||||||
stroke-width: 6;
|
stroke-width: 6;
|
||||||
fill:none;
|
fill: none;
|
||||||
transition: stroke-width 1s;
|
transition: stroke-width 1s;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg .links line.hover, svg .links path.hover{
|
svg .links line.hover, svg .links path.hover {
|
||||||
stroke:var(--hover-color);
|
stroke: var(--hover-color);
|
||||||
stroke-width: 12;
|
stroke-width: 12;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.zoomed .links line, svg.zoomed .links path{
|
svg.zoomed .links line, svg.zoomed .links path {
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
}
|
}
|
||||||
svg.zoomed .links line, svg.zoomed .links path.hover{
|
|
||||||
|
svg.zoomed .links line, svg.zoomed .links path.hover {
|
||||||
stroke-width: 2;
|
stroke-width: 2;
|
||||||
stroke-width: 4;
|
stroke-width: 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
.links text{
|
svg .title {
|
||||||
display:none;
|
font-size: 200;
|
||||||
font-size:5pt;
|
}
|
||||||
|
|
||||||
|
svg .subtitle {
|
||||||
|
font-size: 100;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg #countries .country{
|
||||||
|
fill:rgba(200,200,200,0.7);
|
||||||
|
}
|
||||||
|
svg #countries .country.eu_country{
|
||||||
|
fill:black;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg #header #titlePath, svg #header #subtitlePath {
|
||||||
|
stroke: none;
|
||||||
|
fill: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
svg #header text {
|
||||||
|
font-size: 180px;
|
||||||
|
font-family: "Lexend Mega Regular";
|
||||||
|
/*Comfortaa*/
|
||||||
|
opacity: .8;
|
||||||
|
fill: black;
|
||||||
|
text-shadow: rgba(0, 0, 0, .5) 5px 5px 10px;
|
||||||
|
/* text-transform: uppercase; */
|
||||||
|
}
|
||||||
|
|
||||||
|
svg #header text:nth-of-type(2) {
|
||||||
|
dominant-baseline: hanging;
|
||||||
|
transform: translate(10px, 25px);
|
||||||
|
}
|
||||||
|
|
||||||
|
svg #header text#subtitle {
|
||||||
|
font-size: 70px;
|
||||||
|
fill: var(--color9);
|
||||||
|
}
|
||||||
|
|
||||||
|
.links text {
|
||||||
|
display: none;
|
||||||
|
font-size: 5pt;
|
||||||
text-anchor: middle;
|
text-anchor: middle;
|
||||||
fill: whitesmoke;
|
fill: whitesmoke;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node{
|
.node {
|
||||||
|
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node text.nodeTitle{
|
.node text.nodeTitle {
|
||||||
text-anchor: start;
|
text-anchor: start;
|
||||||
dominant-baseline: hanging; /*achieves a 'text-anchor: top'*/
|
dominant-baseline: hanging;
|
||||||
font-size:16pt;
|
/*achieves a 'text-anchor: top'*/
|
||||||
|
font-size: 16pt;
|
||||||
transition: font-size .4s, opacity 1s;
|
transition: font-size .4s, opacity 1s;
|
||||||
fill: white;
|
fill: white;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
pointer-events: none; /*prevent mouse glitches*/
|
pointer-events: none;
|
||||||
|
/*prevent mouse glitches*/
|
||||||
}
|
}
|
||||||
.node:not(:hover):not(.linkHover) text.nodeTitle.overlapping{
|
|
||||||
|
.node:not(:hover):not(.linkHover) text.nodeTitle.overlapping {
|
||||||
opacity: 0;
|
opacity: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
svg.zoomed .node text.nodeTitle {
|
||||||
svg.zoomed .node text.nodeTitle{
|
font-size: 6pt;
|
||||||
font-size:6pt;
|
|
||||||
}
|
|
||||||
svg.zoomed.zoomed2 .node text.nodeTitle{
|
|
||||||
font-size:3pt;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.node circle{
|
svg.zoomed.zoomed2 .node text.nodeTitle {
|
||||||
|
font-size: 3pt;
|
||||||
|
}
|
||||||
|
|
||||||
|
.node circle {
|
||||||
fill: white;
|
fill: white;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Whenever a connected link is hovered */
|
/* Whenever a connected link is hovered */
|
||||||
.node.linkHover circle{
|
|
||||||
|
.node.linkHover circle {
|
||||||
stroke: var(--hover-color);
|
stroke: var(--hover-color);
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
}
|
}
|
||||||
.node.linkHover text.nodeTitle.overlapping{
|
|
||||||
|
.node.linkHover text.nodeTitle.overlapping {
|
||||||
transition: opacity 0s;
|
transition: opacity 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node:hover circle{
|
.node:hover circle {
|
||||||
stroke: var(--hover-color);
|
stroke: var(--hover-color);
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
}
|
}
|
||||||
.node:hover text{
|
|
||||||
|
.node:hover text {
|
||||||
transition: none;
|
transition: none;
|
||||||
fill: var(--hover-color);
|
fill: var(--hover-color);
|
||||||
}
|
}
|
||||||
.node.selected circle{
|
|
||||||
|
.node.selected circle {
|
||||||
stroke: var(--selected-color);
|
stroke: var(--selected-color);
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.City circle{
|
.node.City circle {
|
||||||
display:none;
|
display: none;
|
||||||
}
|
}
|
||||||
.node.City{
|
|
||||||
fill:white;
|
.node.City {
|
||||||
|
fill: white;
|
||||||
stroke: black;
|
stroke: black;
|
||||||
stroke-width: .5px;
|
stroke-width: .5px;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Person circle {
|
.node.Person circle {
|
||||||
fill: lightgreen
|
fill: lightgreen
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Technology circle {
|
.node.Technology circle {
|
||||||
fill: lightcoral;
|
fill: lightcoral;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Deployment circle {
|
.node.Deployment circle {
|
||||||
fill: lightblue;
|
fill: lightblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Institution circle {
|
.node.Institution circle {
|
||||||
fill: lightgoldenrodyellow
|
fill: lightgoldenrodyellow
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Dataset circle {
|
.node.Dataset circle {
|
||||||
fill: plum
|
fill: plum
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.labels .label text {
|
||||||
|
fill: yellow;
|
||||||
.labels .label text{
|
|
||||||
fill:yellow;
|
|
||||||
opacity: 1 !important;
|
opacity: 1 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -163,69 +219,114 @@ svg.zoomed.zoomed2 .node text.nodeTitle{
|
||||||
fill: var(--color7)
|
fill: var(--color7)
|
||||||
} */
|
} */
|
||||||
|
|
||||||
|
#nodeInfo {
|
||||||
#nodeInfo{
|
|
||||||
position: fixed;
|
position: fixed;
|
||||||
display:block;
|
display: block;
|
||||||
right:20px;
|
right: 20px;
|
||||||
bottom:20px;
|
bottom: 20px;
|
||||||
background:white;
|
background: white;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: solid 1px #ccc;
|
border: solid 1px #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nodeInfo.hidden{
|
#nodeInfo.hidden {
|
||||||
display:none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nodeInfo h2{
|
#nodeInfo h2 {
|
||||||
margin: 0;
|
margin: 0;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#nodeInfo iframe{
|
#nodeInfo iframe {
|
||||||
width: 50vw;
|
width: 50vw;
|
||||||
height: calc(100vh - 40px - 20px - 30px);
|
height: calc(100vh - 40px - 20px - 30px);
|
||||||
}
|
}
|
||||||
|
|
||||||
#closeInfo{
|
#closeInfo {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 10px;
|
right: 10px;
|
||||||
top: 10px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#closeInfo:hover{
|
#closeInfo:hover {
|
||||||
color: var(--hover-color);
|
color: var(--hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
a, a:link{
|
a, a:link {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
a:hover{
|
|
||||||
|
a:hover {
|
||||||
text-decoration: underline;
|
text-decoration: underline;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filters,#menu{
|
header {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
bottom: 0;
|
||||||
top: 0;
|
right: 0;
|
||||||
background: white;
|
background: white;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#map .borders{
|
h1 {
|
||||||
|
/* color:var(--color9); */
|
||||||
|
margin: 0;
|
||||||
|
text-transform: uppercase;
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p.subtitle {
|
||||||
|
margin: 0;
|
||||||
|
color: var(--color10);
|
||||||
|
/* text-transform: uppercase;; */
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters h3{
|
||||||
|
text-align: center;;
|
||||||
|
}
|
||||||
|
#filters label {
|
||||||
|
cursor: pointer;
|
||||||
|
display: block;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters span:hover {
|
||||||
|
color: var(--hover-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters input {
|
||||||
|
/* display: none; */
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters input+span {
|
||||||
|
display: inline-block;
|
||||||
|
padding-left: 10px;
|
||||||
|
|
||||||
|
/* background: var(--color9); */
|
||||||
|
text-decoration:line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
#filters input:checked+span {
|
||||||
|
/* color: var(--selected-color); */
|
||||||
|
text-decoration: none;
|
||||||
|
/* box-shadow: inset 0 0 5px black; */
|
||||||
|
}
|
||||||
|
|
||||||
|
#map .borders {
|
||||||
stroke-width: 6px;
|
stroke-width: 6px;
|
||||||
stroke: white;
|
stroke: white;
|
||||||
fill:none;
|
fill: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#alluvial{
|
#alluvial {
|
||||||
/* position:fixed; */
|
/* position:fixed; */
|
||||||
top:0;
|
top: 0;
|
||||||
left:0;
|
left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#alluvial .flow_label text{
|
#alluvial .flow_label text {
|
||||||
font-size: 30;
|
font-size: 30;
|
||||||
}
|
}
|
94
www/graph.js
94
www/graph.js
|
@ -1,5 +1,7 @@
|
||||||
|
|
||||||
const CONFIG = {
|
const CONFIG = {
|
||||||
|
'title': "Biometric Mass Surveillance",
|
||||||
|
'subtitle': "Connections in the European Union & beyond",
|
||||||
// 'nodeSize': 8,
|
// 'nodeSize': 8,
|
||||||
'nodeRadius': 5,
|
'nodeRadius': 5,
|
||||||
'nodeRepositionPadding': 3,
|
'nodeRepositionPadding': 3,
|
||||||
|
@ -56,6 +58,29 @@ const CONFIG = {
|
||||||
// let height = window.innerHeight;
|
// let height = window.innerHeight;
|
||||||
|
|
||||||
|
|
||||||
|
// Slugify a string, by https://lucidar.me/en/web-dev/how-to-slugify-a-string-in-javascript/
|
||||||
|
function slugify(str) {
|
||||||
|
str = str.replace(/^\s+|\s+$/g, '');
|
||||||
|
|
||||||
|
// Make the string lowercase
|
||||||
|
str = str.toLowerCase();
|
||||||
|
|
||||||
|
// Remove accents, swap ñ for n, etc
|
||||||
|
var from = "ÁÄÂÀÃÅČÇĆĎÉĚËÈÊẼĔȆÍÌÎÏŇÑÓÖÒÔÕØŘŔŠŤÚŮÜÙÛÝŸŽáäâàãåčçćďéěëèêẽĕȇíìîïňñóöòôõøðřŕšťúůüùûýÿžþÞĐđßÆa·/_,:;";
|
||||||
|
var to = "AAAAAACCCDEEEEEEEEIIIINNOOOOOORRSTUUUUUYYZaaaaaacccdeeeeeeeeiiiinnooooooorrstuuuuuyyzbBDdBAa------";
|
||||||
|
for (var i = 0, l = from.length; i < l; i++) {
|
||||||
|
str = str.replace(new RegExp(from.charAt(i), 'g'), to.charAt(i));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove invalid chars
|
||||||
|
str = str.replace(/[^a-z0-9 -]/g, '')
|
||||||
|
// Collapse whitespace and replace by -
|
||||||
|
.replace(/\s+/g, '-')
|
||||||
|
// Collapse dashes
|
||||||
|
.replace(/-+/g, '-');
|
||||||
|
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -202,6 +227,7 @@ class NodeMap {
|
||||||
render() {
|
render() {
|
||||||
this.svg = this.root.append('svg')
|
this.svg = this.root.append('svg')
|
||||||
this.svg.append('defs').html('<marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHead" fill="#f3722c"><path d="M0,-3L8,0L0,3"></path></marker><marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHeadSelected"><path d="M0,-3L8,0L0,3" fill="white"></path></marker>');
|
this.svg.append('defs').html('<marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHead" fill="#f3722c"><path d="M0,-3L8,0L0,3"></path></marker><marker markerHeight="4" markerWidth="4" refY="0" refX="6" viewBox="0 -3 8 6" preserveAspectRatio="none" orient="auto" id="arrowHeadSelected"><path d="M0,-3L8,0L0,3" fill="white"></path></marker>');
|
||||||
|
|
||||||
this.resize();
|
this.resize();
|
||||||
|
|
||||||
this.projection = d3.geoHill()
|
this.projection = d3.geoHill()
|
||||||
|
@ -236,14 +262,14 @@ class NodeMap {
|
||||||
.data(this.countries)
|
.data(this.countries)
|
||||||
.enter()
|
.enter()
|
||||||
.append("path")
|
.append("path")
|
||||||
.attr("class", "countries")
|
.attr("class", (n) => {
|
||||||
.attr("d", this.proj)
|
|
||||||
.attr("fill", (n) => {
|
|
||||||
if (CONFIG.countries.indexOf(n.properties.name) !== -1) {
|
if (CONFIG.countries.indexOf(n.properties.name) !== -1) {
|
||||||
return '';
|
return 'country eu_country';
|
||||||
}
|
}
|
||||||
return "rgba(200,200,200,.7)";
|
return "country";
|
||||||
});
|
})
|
||||||
|
.attr("d", this.proj)
|
||||||
|
// .attr("fill", );
|
||||||
|
|
||||||
this.g_borders
|
this.g_borders
|
||||||
.append("path")
|
.append("path")
|
||||||
|
@ -270,6 +296,49 @@ class NodeMap {
|
||||||
.call(zoom)
|
.call(zoom)
|
||||||
.call(zoom.transform, d3.zoomIdentity.scale(.5, .5));
|
.call(zoom.transform, d3.zoomIdentity.scale(.5, .5));
|
||||||
|
|
||||||
|
this.title = this.container.append('g').attr('id', 'header');
|
||||||
|
|
||||||
|
const titleFeature = {
|
||||||
|
"type": "LineString",
|
||||||
|
"coordinates": []
|
||||||
|
};
|
||||||
|
const subtitleFeature = {
|
||||||
|
"type": "LineString",
|
||||||
|
"coordinates": []
|
||||||
|
};
|
||||||
|
for (let index = 26; index < 70; index++) {
|
||||||
|
// projection apparently tries to find the shortest path between two points
|
||||||
|
// which is NOT following a lat/lon line on the globe
|
||||||
|
titleFeature.coordinates.push([index, 52]);
|
||||||
|
subtitleFeature.coordinates.push([index, 49]);
|
||||||
|
}
|
||||||
|
this.title.append("path")
|
||||||
|
.attr("id", "titlePath")
|
||||||
|
.attr("d", this.proj(titleFeature))
|
||||||
|
;
|
||||||
|
this.title.append("path")
|
||||||
|
.attr("id", "subtitlePath")
|
||||||
|
.attr("d", this.proj(subtitleFeature))
|
||||||
|
;
|
||||||
|
this.title.append("text")
|
||||||
|
.html('<textPath xlink:href="#titlePath">Biometric</textPath>')
|
||||||
|
this.title.append("text")
|
||||||
|
.html('<textPath xlink:href="#titlePath">Mass Surveillance</textPath>')
|
||||||
|
this.title.append("text")
|
||||||
|
.attr("id", "subtitle")
|
||||||
|
.html('<textPath xlink:href="#subtitlePath">' + CONFIG.subtitle + '</textPath>')
|
||||||
|
|
||||||
|
// this.title.append('text')
|
||||||
|
// .attr('class', 'title')
|
||||||
|
// .attr('x', 1000)
|
||||||
|
// .attr('y', 1000)
|
||||||
|
// .text(CONFIG.title);
|
||||||
|
// this.title.append('text')
|
||||||
|
// .attr('class', 'subtitle')
|
||||||
|
// .attr('x', 1000)
|
||||||
|
// .attr('y', 1200)
|
||||||
|
// .text(CONFIG.subtitle);
|
||||||
|
|
||||||
this.link = this.container.append("g")
|
this.link = this.container.append("g")
|
||||||
.attr('class', 'links')
|
.attr('class', 'links')
|
||||||
.selectAll(".link");
|
.selectAll(".link");
|
||||||
|
@ -376,7 +445,7 @@ class NodeMap {
|
||||||
let alpha = 0; // angle
|
let alpha = 0; // angle
|
||||||
let step = 1;
|
let step = 1;
|
||||||
const d_alpha = Math.PI / 4;
|
const d_alpha = Math.PI / 4;
|
||||||
const r = this.getNodeRadius(n) + CONFIG.nodeRepositionPadding/2;
|
const r = this.getNodeRadius(n) + CONFIG.nodeRepositionPadding / 2;
|
||||||
let foundNodes;
|
let foundNodes;
|
||||||
let i = 0;
|
let i = 0;
|
||||||
// find a new pos until it's not overlapping anymore...
|
// find a new pos until it's not overlapping anymore...
|
||||||
|
@ -387,10 +456,10 @@ class NodeMap {
|
||||||
this.store.quadtree.add(n);
|
this.store.quadtree.add(n);
|
||||||
alpha += d_alpha;
|
alpha += d_alpha;
|
||||||
// on to the next round:
|
// on to the next round:
|
||||||
if(alpha > Math.PI * 2) {
|
if (alpha > Math.PI * 2) {
|
||||||
step++;
|
step++;
|
||||||
alpha -= Math.PI *2;
|
alpha -= Math.PI * 2;
|
||||||
alpha += d_alpha-2; // little offset
|
alpha += d_alpha - 2; // little offset
|
||||||
}
|
}
|
||||||
i++;
|
i++;
|
||||||
|
|
||||||
|
@ -408,7 +477,7 @@ class NodeMap {
|
||||||
// we moved something, update tree
|
// we moved something, update tree
|
||||||
|
|
||||||
console.debug('resolved for', n.fulltext, i);
|
console.debug('resolved for', n.fulltext, i);
|
||||||
moved ++;
|
moved++;
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
@ -516,7 +585,8 @@ class NodeMap {
|
||||||
.data(this.graph.links)
|
.data(this.graph.links)
|
||||||
.join(
|
.join(
|
||||||
enter => {
|
enter => {
|
||||||
let group = enter.append("g").attr("class", "link");
|
let group = enter.append("g")
|
||||||
|
.attr("class", (l) => "link " + slugify(l.name));
|
||||||
group.append("path")
|
group.append("path")
|
||||||
.attr("marker-end", "url(#arrowHead)")
|
.attr("marker-end", "url(#arrowHead)")
|
||||||
.attr('id', (d, i) => 'linkid_' + i)
|
.attr('id', (d, i) => 'linkid_' + i)
|
||||||
|
|
|
@ -8,22 +8,29 @@
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<div id='map'></div>
|
<div id='map'></div>
|
||||||
<div id='alluvial'></div>
|
<!-- <div id='alluvial'></div> -->
|
||||||
|
|
||||||
<div id="filters">
|
<header>
|
||||||
|
<h1>Biometric Mass Surveillance</h1>
|
||||||
|
<p class='subtitle'>Connections in the European Union & beyond</p>
|
||||||
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<script src="https://d3js.org/d3.v6.js"></script>
|
<aside id="filters">
|
||||||
|
<h3>Legend</h3>
|
||||||
|
</aside>
|
||||||
|
|
||||||
<script src="https://d3js.org/d3-geo-projection.v3.min.js"></script>
|
</header>
|
||||||
<script src="https://d3js.org/topojson.v3.min.js"></script>
|
|
||||||
<script src="//unpkg.com/d3-geo-zoom"></script>
|
|
||||||
|
|
||||||
<script src="https://unpkg.com/d3-sankey@0"></script>
|
<script src="https://d3js.org/d3.v6.js"></script>
|
||||||
<!-- <script src="//unpkg.com/d3fc@14.0.1"></script> -->
|
|
||||||
<script src="graph.js"></script>
|
<script src="https://d3js.org/d3-geo-projection.v3.min.js"></script>
|
||||||
|
<script src="https://d3js.org/topojson.v3.min.js"></script>
|
||||||
|
<script src="//unpkg.com/d3-geo-zoom"></script>
|
||||||
|
|
||||||
|
<script src="https://unpkg.com/d3-sankey@0"></script>
|
||||||
|
<!-- <script src="//unpkg.com/d3fc@14.0.1"></script> -->
|
||||||
|
<script src="graph.js"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
Loading…
Reference in a new issue