From 4dec76a32539849cf2f2b9bc91621a844722c62c Mon Sep 17 00:00:00 2001 From: Ruben van de Ven Date: Wed, 28 Apr 2021 11:18:02 +0200 Subject: [PATCH] Fancier markers and legend --- www/graph.css | 73 +++++++++++++++++++++++++++++++++------------------ www/graph.js | 40 +++++++++++++++++++++++++--- 2 files changed, 84 insertions(+), 29 deletions(-) diff --git a/www/graph.css b/www/graph.css index 3c110ad..2d7ef76 100644 --- a/www/graph.css +++ b/www/graph.css @@ -6,7 +6,7 @@ } :root { - --color1: #f94144; + --color1: #9741f9; --color2: #f3722c; --color3: #f8961e; /* --color4: #f9844a; */ @@ -40,8 +40,17 @@ svg.dragging { cursor: grabbing; } +#arrowHead { + fill: #9df32c; +} + +#arrowHeadSelected { + fill: var(--hover-color);; +} + svg .links line, svg .links path { stroke: #f3722c; + stroke: #9df32c; stroke-width: 6; fill: none; transition: stroke-width 1s; @@ -51,6 +60,7 @@ svg .links line, svg .links path { svg .links line.hover, svg .links path.hover { stroke: var(--hover-color); stroke-width: 12; + marker-end: url(#arrowHeadSelected); } svg.zoomed .links line, svg.zoomed .links path { @@ -70,11 +80,12 @@ svg .subtitle { font-size: 100; } -svg #countries .country{ - fill:rgba(200,200,200,0.7); +svg #countries .country { + fill: rgba(200, 200, 200, 0.7); } -svg #countries .country.eu_country{ - fill:black; + +svg #countries .country.eu_country { + fill: black; } svg #header #titlePath, svg #header #subtitlePath { @@ -117,7 +128,8 @@ svg #header text#subtitle { text-anchor: start; dominant-baseline: hanging; /*achieves a 'text-anchor: top'*/ - /* font-size: 16pt; */ /*Set this in JS*/ + /* font-size: 16pt; */ + /*Set this in JS*/ transition: font-size .4s, opacity 1s; fill: white; opacity: 1; @@ -137,13 +149,14 @@ svg.zoomed.zoomed2 .node text.nodeTitle { font-size: 3pt; } -.node circle { - fill: white; +.node circle, .node path { + fill: lightgray; } /* Whenever a connected link is hovered */ -.node.linkHover circle { +.node.linkHover circle, .node.linkHover path, label:hover .node path { + fill: var(--hover-color) !important; stroke: var(--hover-color); stroke-width: 5px; } @@ -152,7 +165,8 @@ svg.zoomed.zoomed2 .node text.nodeTitle { transition: opacity 0s; } -.node:hover circle { +.node:hover circle, .node:hover path { + fill: var(--hover-color) !important; stroke: var(--hover-color); stroke-width: 5px; } @@ -162,12 +176,13 @@ svg.zoomed.zoomed2 .node text.nodeTitle { fill: var(--hover-color); } -.node.selected circle { +.node.selected circle, .node.selected path { + fill: var(--selected-color) !important; stroke: var(--selected-color); stroke-width: 5px; } -.node.City circle { +.node.City circle, .node.City path { display: none; } @@ -178,24 +193,24 @@ svg.zoomed.zoomed2 .node text.nodeTitle { font-size: 20px; } -.node.Person circle { +.node.Person circle, .node.Person path { fill: lightgreen } -.node.Technology circle { - fill: lightcoral; +.node.Technology circle, .node.Technology path { + fill: plum; } -.node.Deployment circle { +.node.Deployments circle, .node.Deployments path { fill: lightblue; } -.node.Institution circle { - fill: lightgoldenrodyellow +.node.Institution circle, .node.Institution path { + fill: lightcoral } -.node.Dataset circle { - fill: plum +.node.Dataset circle, .node.Dataset path { + fill: lightgoldenrodyellow } .labels .label text { @@ -284,29 +299,37 @@ p.subtitle { display: none; } -#filters h3{ - text-align: center;; +#filters h3 { + text-align: center; + ; } + #filters label { cursor: pointer; display: block; padding: 10px; } +#filters label svg { + display: inline; + width: 30px; + height: 30px; + vertical-align: middle; +} + #filters span:hover { color: var(--hover-color); } #filters input { - /* display: none; */ + display: none; } #filters input+span { display: inline-block; padding-left: 10px; - /* background: var(--color9); */ - text-decoration:line-through; + text-decoration: line-through; } #filters input:checked+span { diff --git a/www/graph.js b/www/graph.js index 62743d1..7254319 100644 --- a/www/graph.js +++ b/www/graph.js @@ -4,7 +4,7 @@ const CONFIG = { 'subtitle': "Connections in the European Union & beyond", // 'nodeSize': 8, 'nodeRadius': 5, - 'nodeRepositionPadding': 3, + 'nodeRepositionPadding': 8, 'baseUrl': 'https://www.securityvision.io/wiki/index.php/', 'dataUrl': 'result.json', 'preSimulate': false, // run simulation before starting, so we don't start with lines jumping around @@ -58,6 +58,27 @@ const CONFIG = { // let height = window.innerHeight; +function getSymbolForCategories(classes) { + if(!Array.isArray(classes)) { + classes = [classes]; + } + if (classes.includes('Institution')) { + return d3.symbol() + .type(d3.symbolTriangle) + .size(CONFIG.nodeRadius * 16); + } + + return d3.symbol() + .type(d3.symbolCircle) + .size(CONFIG.nodeRadius * 16); +} + +// returns a symbol function +function getSymbolForNode(n) { + const classes = getCategories(n); + return getSymbolForCategories(classes); +} + // 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, ''); @@ -226,7 +247,7 @@ class NodeMap { render() { this.svg = this.root.append('svg') - this.svg.append('defs').html(` + this.svg.append('defs').html(` @@ -302,7 +323,7 @@ class NodeMap { this.container.attr("transform", transform); const oldZoom = this.svg.classed('zoomed'); const newZoom = transform.k > 2.0; - if(zoomTimeout) { + if (zoomTimeout) { clearTimeout(zoomTimeout) } zoomTimeout = setTimeout(() => { @@ -522,7 +543,11 @@ class NodeMap { let group = enter.append("g").attr("class", getClasses); // group.call(drag(simulation)); group.on("click", (evt, n) => selectNode(evt, n, node)); - group.append('circle').attr("r", 5 /*this.nodeSize*/); + // group.append('circle').attr("r", 5 /*this.nodeSize*/); + group.append('path') + .attr('d', (n) => { + return getSymbolForNode(n)(n); + }) var nodeTitle = group.append('text').attr("class", "nodeTitle").attr("y", "3").attr('x', 5); nodeTitle .each(function (node, i, nodes) { @@ -1288,9 +1313,16 @@ class Store { render() { CONFIG.filters.forEach(f => { + let labelEl = document.createElement('label') let inputEl = document.createElement('input') let textEl = document.createElement('span'); + let svg = d3.select(labelEl).append('svg') + .attr("viewBox", [-12,-12,24,24]); + svg.append('g') + .attr("class", "node "+ f) + .append('path') + .attr('d', getSymbolForCategories(f)()); inputEl.type = "checkbox"; textEl.innerText = f; labelEl.appendChild(inputEl);