forked from security_vision/semantic_graph
Fancier markers and legend
This commit is contained in:
parent
7751fc05a8
commit
4dec76a325
2 changed files with 84 additions and 29 deletions
|
@ -6,7 +6,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
:root {
|
:root {
|
||||||
--color1: #f94144;
|
--color1: #9741f9;
|
||||||
--color2: #f3722c;
|
--color2: #f3722c;
|
||||||
--color3: #f8961e;
|
--color3: #f8961e;
|
||||||
/* --color4: #f9844a; */
|
/* --color4: #f9844a; */
|
||||||
|
@ -40,8 +40,17 @@ svg.dragging {
|
||||||
cursor: grabbing;
|
cursor: grabbing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#arrowHead {
|
||||||
|
fill: #9df32c;
|
||||||
|
}
|
||||||
|
|
||||||
|
#arrowHeadSelected {
|
||||||
|
fill: var(--hover-color);;
|
||||||
|
}
|
||||||
|
|
||||||
svg .links line, svg .links path {
|
svg .links line, svg .links path {
|
||||||
stroke: #f3722c;
|
stroke: #f3722c;
|
||||||
|
stroke: #9df32c;
|
||||||
stroke-width: 6;
|
stroke-width: 6;
|
||||||
fill: none;
|
fill: none;
|
||||||
transition: stroke-width 1s;
|
transition: stroke-width 1s;
|
||||||
|
@ -51,6 +60,7 @@ svg .links line, svg .links path {
|
||||||
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;
|
||||||
|
marker-end: url(#arrowHeadSelected);
|
||||||
}
|
}
|
||||||
|
|
||||||
svg.zoomed .links line, svg.zoomed .links path {
|
svg.zoomed .links line, svg.zoomed .links path {
|
||||||
|
@ -70,11 +80,12 @@ svg .subtitle {
|
||||||
font-size: 100;
|
font-size: 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
svg #countries .country{
|
svg #countries .country {
|
||||||
fill:rgba(200,200,200,0.7);
|
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 {
|
svg #header #titlePath, svg #header #subtitlePath {
|
||||||
|
@ -117,7 +128,8 @@ svg #header text#subtitle {
|
||||||
text-anchor: start;
|
text-anchor: start;
|
||||||
dominant-baseline: hanging;
|
dominant-baseline: hanging;
|
||||||
/*achieves a 'text-anchor: top'*/
|
/*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;
|
transition: font-size .4s, opacity 1s;
|
||||||
fill: white;
|
fill: white;
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
|
@ -137,13 +149,14 @@ svg.zoomed.zoomed2 .node text.nodeTitle {
|
||||||
font-size: 3pt;
|
font-size: 3pt;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node circle {
|
.node circle, .node path {
|
||||||
fill: white;
|
fill: lightgray;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Whenever a connected link is hovered */
|
/* 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: var(--hover-color);
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
}
|
}
|
||||||
|
@ -152,7 +165,8 @@ svg.zoomed.zoomed2 .node text.nodeTitle {
|
||||||
transition: opacity 0s;
|
transition: opacity 0s;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node:hover circle {
|
.node:hover circle, .node:hover path {
|
||||||
|
fill: var(--hover-color) !important;
|
||||||
stroke: var(--hover-color);
|
stroke: var(--hover-color);
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
}
|
}
|
||||||
|
@ -162,12 +176,13 @@ svg.zoomed.zoomed2 .node text.nodeTitle {
|
||||||
fill: var(--hover-color);
|
fill: var(--hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.selected circle {
|
.node.selected circle, .node.selected path {
|
||||||
|
fill: var(--selected-color) !important;
|
||||||
stroke: var(--selected-color);
|
stroke: var(--selected-color);
|
||||||
stroke-width: 5px;
|
stroke-width: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.City circle {
|
.node.City circle, .node.City path {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -178,24 +193,24 @@ svg.zoomed.zoomed2 .node text.nodeTitle {
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Person circle {
|
.node.Person circle, .node.Person path {
|
||||||
fill: lightgreen
|
fill: lightgreen
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Technology circle {
|
.node.Technology circle, .node.Technology path {
|
||||||
fill: lightcoral;
|
fill: plum;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Deployment circle {
|
.node.Deployments circle, .node.Deployments path {
|
||||||
fill: lightblue;
|
fill: lightblue;
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Institution circle {
|
.node.Institution circle, .node.Institution path {
|
||||||
fill: lightgoldenrodyellow
|
fill: lightcoral
|
||||||
}
|
}
|
||||||
|
|
||||||
.node.Dataset circle {
|
.node.Dataset circle, .node.Dataset path {
|
||||||
fill: plum
|
fill: lightgoldenrodyellow
|
||||||
}
|
}
|
||||||
|
|
||||||
.labels .label text {
|
.labels .label text {
|
||||||
|
@ -284,29 +299,37 @@ p.subtitle {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filters h3{
|
#filters h3 {
|
||||||
text-align: center;;
|
text-align: center;
|
||||||
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filters label {
|
#filters label {
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
display: block;
|
display: block;
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#filters label svg {
|
||||||
|
display: inline;
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
#filters span:hover {
|
#filters span:hover {
|
||||||
color: var(--hover-color);
|
color: var(--hover-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
#filters input {
|
#filters input {
|
||||||
/* display: none; */
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filters input+span {
|
#filters input+span {
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
padding-left: 10px;
|
padding-left: 10px;
|
||||||
|
|
||||||
/* background: var(--color9); */
|
/* background: var(--color9); */
|
||||||
text-decoration:line-through;
|
text-decoration: line-through;
|
||||||
}
|
}
|
||||||
|
|
||||||
#filters input:checked+span {
|
#filters input:checked+span {
|
||||||
|
|
40
www/graph.js
40
www/graph.js
|
@ -4,7 +4,7 @@ const CONFIG = {
|
||||||
'subtitle': "Connections in the European Union & beyond",
|
'subtitle': "Connections in the European Union & beyond",
|
||||||
// 'nodeSize': 8,
|
// 'nodeSize': 8,
|
||||||
'nodeRadius': 5,
|
'nodeRadius': 5,
|
||||||
'nodeRepositionPadding': 3,
|
'nodeRepositionPadding': 8,
|
||||||
'baseUrl': 'https://www.securityvision.io/wiki/index.php/',
|
'baseUrl': 'https://www.securityvision.io/wiki/index.php/',
|
||||||
'dataUrl': 'result.json',
|
'dataUrl': 'result.json',
|
||||||
'preSimulate': false, // run simulation before starting, so we don't start with lines jumping around
|
'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;
|
// 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/
|
// Slugify a string, by https://lucidar.me/en/web-dev/how-to-slugify-a-string-in-javascript/
|
||||||
function slugify(str) {
|
function slugify(str) {
|
||||||
str = str.replace(/^\s+|\s+$/g, '');
|
str = str.replace(/^\s+|\s+$/g, '');
|
||||||
|
@ -226,7 +247,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" ><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"></path></marker>
|
||||||
<!--Sketching:-->
|
<!--Sketching:-->
|
||||||
<defs>
|
<defs>
|
||||||
<filter id="tint">
|
<filter id="tint">
|
||||||
|
@ -302,7 +323,7 @@ class NodeMap {
|
||||||
this.container.attr("transform", transform);
|
this.container.attr("transform", transform);
|
||||||
const oldZoom = this.svg.classed('zoomed');
|
const oldZoom = this.svg.classed('zoomed');
|
||||||
const newZoom = transform.k > 2.0;
|
const newZoom = transform.k > 2.0;
|
||||||
if(zoomTimeout) {
|
if (zoomTimeout) {
|
||||||
clearTimeout(zoomTimeout)
|
clearTimeout(zoomTimeout)
|
||||||
}
|
}
|
||||||
zoomTimeout = setTimeout(() => {
|
zoomTimeout = setTimeout(() => {
|
||||||
|
@ -522,7 +543,11 @@ class NodeMap {
|
||||||
let group = enter.append("g").attr("class", getClasses);
|
let group = enter.append("g").attr("class", getClasses);
|
||||||
// group.call(drag(simulation));
|
// group.call(drag(simulation));
|
||||||
group.on("click", (evt, n) => selectNode(evt, n, node));
|
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);
|
var nodeTitle = group.append('text').attr("class", "nodeTitle").attr("y", "3").attr('x', 5);
|
||||||
nodeTitle
|
nodeTitle
|
||||||
.each(function (node, i, nodes) {
|
.each(function (node, i, nodes) {
|
||||||
|
@ -1288,9 +1313,16 @@ class Store {
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
CONFIG.filters.forEach(f => {
|
CONFIG.filters.forEach(f => {
|
||||||
|
|
||||||
let labelEl = document.createElement('label')
|
let labelEl = document.createElement('label')
|
||||||
let inputEl = document.createElement('input')
|
let inputEl = document.createElement('input')
|
||||||
let textEl = document.createElement('span');
|
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";
|
inputEl.type = "checkbox";
|
||||||
textEl.innerText = f;
|
textEl.innerText = f;
|
||||||
labelEl.appendChild(inputEl);
|
labelEl.appendChild(inputEl);
|
||||||
|
|
Loading…
Reference in a new issue