Skip to content
Snippets Groups Projects
Commit 993d6f94 authored by Marc Feger's avatar Marc Feger
Browse files

Add visualisation with first effort

parent 1619ca14
Branches
No related tags found
No related merge requests found
......@@ -808,6 +808,12 @@
"to-fast-properties": "^2.0.0"
}
},
"@types/d3-selection": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-1.0.10.tgz",
"integrity": "sha1-3PsN3837GtJq6kNRMjdx4a6pboQ=",
"dev": true
},
"@types/events": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz",
......@@ -2433,6 +2439,76 @@
"d3-path": "1"
}
},
"d3-svg-legend": {
"version": "2.25.6",
"resolved": "https://registry.npmjs.org/d3-svg-legend/-/d3-svg-legend-2.25.6.tgz",
"integrity": "sha1-jY3BvWk8N47ki2+CPook5o8uGtI=",
"dev": true,
"requires": {
"@types/d3-selection": "1.0.10",
"d3-array": "1.0.1",
"d3-dispatch": "1.0.1",
"d3-format": "1.0.2",
"d3-scale": "1.0.3",
"d3-selection": "1.0.2",
"d3-transition": "1.0.3"
},
"dependencies": {
"d3-array": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.0.1.tgz",
"integrity": "sha1-N1wCh0/NlsFu2fG89bSnvlPzWOc=",
"dev": true
},
"d3-dispatch": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.1.tgz",
"integrity": "sha1-S9ZaQ87P9DGN653yRVKqi/KBqEA=",
"dev": true
},
"d3-format": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.0.2.tgz",
"integrity": "sha1-E4YYMgtLvrQ7XA/zBRkHn7vXN14=",
"dev": true
},
"d3-scale": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-1.0.3.tgz",
"integrity": "sha1-T56PDMLqDzkl/wSsJ63AkEX6TJA=",
"dev": true,
"requires": {
"d3-array": "1",
"d3-collection": "1",
"d3-color": "1",
"d3-format": "1",
"d3-interpolate": "1",
"d3-time": "1",
"d3-time-format": "2"
}
},
"d3-selection": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.0.2.tgz",
"integrity": "sha1-rmYq/UcCrJxdoDmyEHoXZPockHA=",
"dev": true
},
"d3-transition": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.0.3.tgz",
"integrity": "sha1-kdyYa92zCXNjkyCoXbcs5KsaJ7s=",
"dev": true,
"requires": {
"d3-color": "1",
"d3-dispatch": "1",
"d3-ease": "1",
"d3-interpolate": "1",
"d3-selection": "1",
"d3-timer": "1"
}
}
}
},
"d3-time": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.0.11.tgz",
......
......@@ -5,7 +5,7 @@
"main": "app.js",
"scripts": {
"build": "webpack",
"start:dev": "webpack-dev-server"
"start:dev": "webpack-dev-server --host 0.0.0.0"
},
"author": "",
"license": "ISC",
......@@ -15,6 +15,7 @@
"babel-loader": "^8.0.6",
"css-loader": "^2.1.1",
"d3": "^5.9.2",
"d3-svg-legend": "^2.25.6",
"file-loader": "^3.0.1",
"html-loader": "^0.5.5",
"html-webpack-plugin": "^3.2.0",
......
import {Visualizer} from './js/visualizer/visualizer'
new Visualizer().plotGraph('match (a:Issue{id:4})<-[r*..]-(b) return *');
\ No newline at end of file
import {Network} from './js/visualizer/visualization'
new Network().showNetwork('match (a:Issue{id:2})<-[r*..]-(b) return *');
\ No newline at end of file
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
background: antiquewhite;
font-family: "Inconsolata";
}
circle {
fill: coral;
stroke: grey;
.link {
stroke: #ffffff;
stroke-width: 2px;
pointer-events: all;
}
.node circle {
pointer-events: all;
stroke: #ffffff;
stroke-width: 1px;
fill: grey;
}
text {
fill: slategrey;
font-size: 20px;
div.tooltip {
position: absolute;
background-color: #ffd899;
max-width;
200px;
height: auto;
padding: 2px;
border-style: solid;
border-radius: 4px;
border-width: 1px;
box-shadow: 3px 3px 10px rgba(0, 0, 0, .5);
pointer-events: none;
}
line {
stroke: slategrey;
svg {
background: black;
}
</style>
<svg width="960" height="600">
</svg>
\ No newline at end of file
<body>
<svg width="1400" height="900"></svg>
</body>
\ No newline at end of file
import {Neo4JAdapter} from '../neo4j/neo'
import {v1 as neo4j} from 'neo4j-driver'
export class GraphCreator {
async fill(graph, withQuery) {
......@@ -9,15 +10,36 @@ export class GraphCreator{
record.forEach(function (element) {
if (element instanceof neo4j.types.Node) {
var id = element.identity.toInt();
var newNode = {"id": id};
var newNode = null;
if (element.labels.includes("Issue")){
newNode = {
"id": id,
"labels": element.labels,
"title": element.properties.title
};
}else if (element.labels.includes("Statement")){
newNode = {
"id": id,
"labels": element.labels,
"textversion": element.properties.textversion
};
}else if (element.labels.includes("Argument")){
newNode = {
"id": id,
"labels": element.labels
};
}
graph.addNode(newNode);
} else if (element instanceof neo4j.types.Relationship) {
var newEdge = {"source": element.start.toInt(), "target": element.end.toInt()};
graph.addEdge(newEdge);
} else if (element instanceof Array) {
element.forEach(function (relation) {
var newEdge = {"source": relation.start.toInt(), "target": relation.end.toInt()};
if (relation instanceof neo4j.types.Relationship) {
var newEdge = {
"source": relation.start.toInt(),
"target": relation.end.toInt(),
"type": relation.type
};
graph.addEdge(newEdge);
}
})
}
});
......
import * as d3 from 'd3'
import {legendColor} from 'd3-svg-legend'
import {Neo4JAdapter} from '../neo4j/neo'
import {Graph} from '../graph/graph'
import {GraphCreator} from '../graph/graphCreator'
import {v1 as neo4j} from 'neo4j-driver'
export class Network {
async showNetwork(query) {
const graph = await new GraphCreator().fill(new Graph(), query);
var color = d3.scaleOrdinal(d3.schemeSet3);
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("opacity", 0);
const svg = d3.select('svg'),
width = +svg.attr('width'),
height = +svg.attr('height');
const simulation = d3.forceSimulation()
.nodes(graph.nodes)
.force('link', d3.forceLink().id(d => d.id).distance(20))
.force('charge', d3.forceManyBody())
.force('center', d3.forceCenter(width / 2, height / 2))
.on('tick', ticked);
simulation.force('link')
.links(graph.edges);
const R = 6;
let link = svg.selectAll('line')
.data(graph.edges)
.enter().append('line');
link
.attr('class', 'link')
.on('mouseover.tooltip', function (d) {
tooltip.transition()
.duration(300)
.style("opacity", 0.8);
tooltip.html("Source:" + d.source.id +
"<p/>Target:" + d.target.id +
"<p/>Type:" + d.type)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY + 10) + "px");
})
.on("mouseout.tooltip", function () {
tooltip.transition()
.duration(100)
.style("opacity", 0);
})
.on('mouseout.fade', fade(1))
.on("mousemove", function () {
tooltip.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY + 10) + "px");
});
;
let node = svg.selectAll('.node')
.data(graph.nodes)
.enter().append('g')
.attr('class', 'node')
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
;
node.append('circle')
.attr('r', R)
.attr("fill", function (d) {
return color(d.labels);
})
.on('mouseover.tooltip', function (d) {
tooltip.transition()
.duration(300)
.style("opacity", .8);
let info = "";
if (d.labels.includes("Issue")) {
info = "id:" + d.id
+ "<p/>Labels:" + d.labels
+ "<p/>Title:" + d.title;
}else if (d.labels.includes("Statement")){
info = "id:" + d.id
+ "<p/>Labels:" + d.labels
+ "<p/>Textversion:" + d.textversion;
}
else if (d.labels.includes("Argument")){
info = "id:" + d.id
+ "<p/>Labels:" + d.labels;
}
tooltip.html(info)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY + 10) + "px");
})
.on('mouseover.fade', fade(0.1))
.on("mouseout.tooltip", function () {
tooltip.transition()
.duration(100)
.style("opacity", 0);
})
.on('mouseout.fade', fade(1))
.on("mousemove", function () {
tooltip.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY + 10) + "px");
})
.on('dblclick', releasenode)
node.append('text')
.attr('x', 0)
.attr('dy', '.35em')
.text(d => d.name);
function ticked() {
link
.attr('x1', d => d.source.x)
.attr('y1', d => d.source.y)
.attr('x2', d => d.target.x)
.attr('y2', d => d.target.y);
node
.attr('transform', d => `translate(${d.x},${d.y})`);
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
function releasenode(d) {
d.fx = null;
d.fy = null;
}
const linkedByIndex = {};
graph.edges.forEach(d => {
linkedByIndex[`${d.source.index},${d.target.index}`] = 1;
});
function isConnected(a, b) {
return linkedByIndex[`${a.index},${b.index}`] || linkedByIndex[`${b.index},${a.index}`] || a.index === b.index;
}
function fade(opacity) {
return d => {
node.style('stroke-opacity', function (o) {
const thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.style('stroke-opacity', o => (o.source === d || o.target === d ? 1 : opacity));
};
}
var sequentialScale = d3.scaleOrdinal(d3.schemeSet3)
.domain([0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
svg.append("g")
.attr("class", "legendSequential")
.attr("transform", "translate(" + (width - 100) + "," + (height - 300) + ")");
var legendSequential = legendColor()
.shapeWidth(30)
.cells(11)
.orient("vertical")
.title("Group number by color:")
.titleWidth(100)
.scale(sequentialScale);
svg.select(".legendSequential")
.call(legendSequential);
}
}
\ No newline at end of file
......@@ -52,6 +52,20 @@ export class Visualizer {
d.fy = d.y;
}
function click() {
d3.select(this).classed("fixed", this.fixed = true).transition()
.duration(1000)
.attr("r", 15)
.style("fill", "lightsteelblue");
}
function dbclick() {
d3.select(this).classed("fixed", this.fixed = false).transition()
.duration(1000)
.attr("r", 5)
.style("fill", "#ccc");
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
......@@ -76,6 +90,8 @@ export class Visualizer {
.data(graph.nodes)
.enter()
.append("circle")
.on("click", click)
.on("dblick", dbclick)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment