From 252271037368d543c0f15effd60e97a617d79526 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Mon, 30 Dec 2019 22:28:30 +0100 Subject: [PATCH 01/74] Created 'Graph-Visualization' in 'own-modules' Created 'Graph-Visualization' in 'own-modules' and d3.js installed --- frontend/app/layout/layout-routing.module.ts | 28 ++++++++++++ frontend/package-lock.json | 43 +++++++++++++++---- frontend/package.json | 1 + .../components/sidebar/sidebar.component.html | 4 ++ .../graph-visualization.component.html | 3 ++ .../graph-visualization.component.ts | 13 ++++++ .../graph-visualization.module.ts | 11 +++++ .../graph-visualization.routing.ts | 22 ++++++++++ 8 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 frontend/app/layout/layout-routing.module.ts create mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.component.html create mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.component.ts create mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.module.ts create mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts diff --git a/frontend/app/layout/layout-routing.module.ts b/frontend/app/layout/layout-routing.module.ts new file mode 100644 index 00000000..e7acd6b0 --- /dev/null +++ b/frontend/app/layout/layout-routing.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { LayoutComponent } from './layout.component'; + +const routes: Routes = [ + { + path: '', + component: LayoutComponent, + children: [ + { path: '', redirectTo: 'dashboard', pathMatch: 'prefix' }, + { path: 'module-management', loadChildren: '../own-modules/module-management/module-management.module#ModuleManagementModule'}, + { path: 'order-management', loadChildren: '../own-modules/order-management/order-management.module#OrderManagementModule'}, + { path: 'kpi-dashboard', loadChildren: '../own-modules/kpi-dashboard/kpi-dashboard.module#KpiDashboardModule'}, + { path: 'reconfiguration-overview', loadChildren: '../own-modules/reconfiguration-overview/reconfiguration-overview.module#ReconfigurationOverviewModule'}, + { path: 'ops-configuration', loadChildren: '../own-modules/ops-configuration/ops-configuration.module#OpsConfigurationModule'}, + { path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardModule' }, + { path: 'charts', loadChildren: './charts/charts.module#ChartsModule' }, + {path:'graph-visualization', loadChildren: '../own-modules/graph-visualization/graph-visualization.module#GraphVisualizationModule'}, + { path: 'blank-page', loadChildren: './blank-page/blank-page.module#BlankPageModule' } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class LayoutRoutingModule {} diff --git a/frontend/package-lock.json b/frontend/package-lock.json index b884c35c..54d1f4a3 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19568,6 +19568,7 @@ "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.13.16.tgz", "integrity": "sha512-1eMtTrXtrwscjcAeO4BVK+vvkxaLJSPFz1w1KLawz6HLNi9bPFGBNwwDyVfiu1Tv/vRRFYfoGaKhmAQPGPn5Wg==", "dev": true, + "optional": true, "requires": { "@babel/traverse": "^7.13.15", "@babel/types": "^7.13.16" @@ -23892,10 +23893,12 @@ } } }, - "engine.io-parser": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-2.2.0.tgz", - "integrity": "sha512-6I3qD9iUxotsC5HEMuuGsKA0cXerGz+4uGcXQEkfBidgKf0amsjrrtwcbwK/nzpZBxclXlV7gGl9dgWvu4LF6w==", + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "optional": true, "requires": { "after": "0.8.2", "arraybuffer.slice": "~0.0.7", @@ -23909,6 +23912,7 @@ "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.7.0.tgz", "integrity": "sha512-6njwt/NsZFUKhM6j9U8hzVyD4E4r0x7NQzhTCbcWOJ0IQjNSAoalWmb0AE51Wn+fwan5qVESWi7t2ToBxs9vrw==", "dev": true, + "optional": true, "requires": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -25836,6 +25840,7 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, + "optional": true, "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -29195,6 +29200,7 @@ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.1.tgz", "integrity": "sha512-Xn2+z++vWObbEPhiiKO1a78JiyhqipyrXHBb3AHpv0ks7Cdg+GxQQJ24ODNMTanldf7197gSP3axppO9yaG0lA==", "dev": true, + "optional": true, "requires": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -30514,6 +30520,7 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, + "optional": true, "requires": { "load-json-file": "^2.0.0", "normalize-package-data": "^2.3.2", @@ -30525,6 +30532,7 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, + "optional": true, "requires": { "pify": "^2.0.0" } @@ -30533,7 +30541,8 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true + "dev": true, + "optional": true } } }, @@ -30542,6 +30551,7 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, + "optional": true, "requires": { "find-up": "^2.0.0", "read-pkg": "^2.0.0" @@ -30906,6 +30916,11 @@ "queue-microtask": "^1.2.2" } }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha1-P4Yt+pGrdmsUiF700BEkv9oHT7Q=" + }, "rxjs": { "version": "6.5.5", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-6.5.5.tgz", @@ -33446,10 +33461,20 @@ "integrity": "sha1-2e8H3Od7mQK4o6j6SzHD4/fm6Ho=", "dev": true }, - "wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", + "worker-farm": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", + "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", + "dev": true, + "optional": true, + "requires": { + "errno": "~0.1.7" + } + }, + "worker-plugin": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/worker-plugin/-/worker-plugin-4.0.2.tgz", + "integrity": "sha512-V+1zSZMOOKk+uBzKyNIODLQLsx59zSIOaI75J1EMS0iR1qy+KQR3y/pQ3T0vIhvPfDFapGRMsoMvQNEL3okqSA==", "dev": true, "requires": { "string-width": "^1.0.2 || 2" diff --git a/frontend/package.json b/frontend/package.json index 34763977..35f3a39e 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,6 +31,7 @@ "camunda-bpmn-moddle": "^5.1.2", "chart.js": "2.7.3", "core-js": "2.6.5", + "d3": "^5.15.0", "flag-icon-css": "3.2.1", "formidable": "^1.2.1", "json-groupby": "^1.1.0", diff --git a/frontend/src/layout/components/sidebar/sidebar.component.html b/frontend/src/layout/components/sidebar/sidebar.component.html index 9911365a..a37215e1 100644 --- a/frontend/src/layout/components/sidebar/sidebar.component.html +++ b/frontend/src/layout/components/sidebar/sidebar.component.html @@ -44,6 +44,10 @@ + +   + Graph-Visualization +   KPI-Dashboard diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html new file mode 100644 index 00000000..70b08142 --- /dev/null +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html @@ -0,0 +1,3 @@ +

+Graph-Visualization +

diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts new file mode 100644 index 00000000..1b195074 --- /dev/null +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'graph-visualization', + templateUrl: './graph-visualization.component.html' +}) +export class GraphVisualizationComponent { + + constructor() { } + + + +} diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts new file mode 100644 index 00000000..afb5fff0 --- /dev/null +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { GraphVisualizationComponent } from './graph-visualization.component'; +import {GraphVisualizationRouter} from './graph-visualization.routing'; + +@NgModule({ + imports: [ + GraphVisualizationRouter + ], + declarations: [GraphVisualizationComponent] +}) +export class GraphVisualizationModule { } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts new file mode 100644 index 00000000..ded3fe38 --- /dev/null +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts @@ -0,0 +1,22 @@ +import { Routes, RouterModule } from '@angular/router'; +import { NgModule } from '@angular/core'; +import {GraphVisualizationComponent} from './graph-visualization.component' + +const routes: Routes = [ + { path: '', + component: GraphVisualizationComponent, + data: { + title: 'Graph-Visualization' + }, + children: [] + + + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) + +export class GraphVisualizationRouter {} From 6d6d21d376c0da3e865e615294ffb5be28728f90 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Mon, 27 Jan 2020 12:24:21 +0100 Subject: [PATCH 02/74] Added visual graph by d3.js --- .../graph-visualization.component.scss | 0 .../graph-visualization.component.html | 5 + .../graph-visualization.component.ts | 191 +++++++++++++++++- 3 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 frontend/app/own-modules/graph-visualization/graph-visualization.component.scss diff --git a/frontend/app/own-modules/graph-visualization/graph-visualization.component.scss b/frontend/app/own-modules/graph-visualization/graph-visualization.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html index 70b08142..fcff4a3d 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html @@ -1,3 +1,8 @@

Graph-Visualization

+
+
+ +
+
\ No newline at end of file diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 1b195074..cf171b0d 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -1,13 +1,196 @@ -import { Component } from '@angular/core'; +import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'; +import * as d3 from "d3" +import { enterView } from '@angular/core/src/render3/state'; +import { pathToFileURL } from 'url'; @Component({ selector: 'graph-visualization', - templateUrl: './graph-visualization.component.html' + encapsulation: ViewEncapsulation.None, + templateUrl: './graph-visualization.component.html', + styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent { +export class GraphVisualizationComponent implements AfterContentInit { + + ngAfterContentInit(): void { + const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen + const width = 1400 - margin.left - margin.right; + const height = 900 - margin.top - margin.bottom; + const rad=20; // radius + + +var svg = d3.select("#graph") // Größe u. Grenzen SVG +.append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) +.append("g") + .attr("transform", + "translate(" + margin.left + "," + margin.top + ")"); + + + +var colors = d3.scaleOrdinal(d3.schemeCategory10); //Farben + + + +//d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten + +const data = { + "nodes": [ + { + "id": 1, + "name": "ModulA", + "group": 1 + }, + { + "id": 2, + "name": "ModulB", + "group": 2 + }, + { + "id": 3, + "name": "ModulC", + "group": 3 + + }, { + "id": 4, + "name": "cap11", + "group":1 + }, + { + "id": 5, + "name": "cap12", + "group":1 + }, + { + "id": 6, + "name": "cap21", + "group":2 + } + ], + "links": [ + + { + "source": 1, + "target": 4, + "type": "is_a" + } , + { + "source": 1, + "target": 5, + "type": "has" + } , + { + "source": 2, + "target": 6, + "type":"has" + } + ] +} - constructor() { } + var link = svg // Links initialisieren + .selectAll(".links") + .data(data.links) + .enter() + .append("line") + .style("stroke", "#aaa") + ; + link.append("title") + .text(function(d){return d.type}); + var node = svg // Nodes initialisieren + .selectAll("circle") + .data(data.nodes) + .enter() + .append("circle") + .attr("r", rad) + .style( "fill", "white") + .style("stroke-width", 8) + .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit + /*.append ("text") // name als text neben node + .attr ("dx", 12) + .attr ("dy", ".35em") + .text(function(d){return d.name})*/ + .call(d3.drag() + .on("start", dragstarted) + .on("drag", dragged)); +; + + const text =svg + .selectAll("text") + .data(data.nodes) + .enter() + .append("text") + + .text(function(d){return d.name}); + + const linkText =svg + .selectAll("links") + .data(data.links) + .enter() + .append("text") + .text(function (d){return d.type}); + + + + var simulation = d3.forceSimulation(data.nodes) //Simulation + .force("link", d3.forceLink() + .id(function(d) { return d.id; }) + .distance(100) + .links(data.links) + ) + .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen + .force("center", d3.forceCenter(width / 2, height / 2)) // Nodes mittig von svg + .on("tick", ticked); // tick wird in jedem Schritt durchgeführt + + + function ticked() { + console.log(data); + text + .attr("dx", function(d) { + return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) + .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }) + + linkText + .attr("dx", function(d){return 0.5*((d.source.x)+(d.target.x))}) + .attr("dy", function(d){return 0.5*((d.source.y)+(d.target.y)) }) + + + + link // Anfang und Ende der Links / Bestimmung der Position + .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)); }) // Postionen nur innerhalb des Fensters erlaubt + .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)); }) + .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)); }) + .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)); }); + + node // Bestimmung der Postion einzelner Nodes + .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)); }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }); + } + + function ended() { + console.log("ended"); + } + + function dragstarted(d) { + if (!d3.event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + console.log(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; + } + + } + constructor() { } } From d3fd8cb591183b0687f1bba7ab6425d1216a2810 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 31 Jan 2020 12:17:01 +0100 Subject: [PATCH 03/74] Added arrows Added arrows for relation directions --- .../graph-visualization.component.ts | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index cf171b0d..efe39ef0 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -15,10 +15,12 @@ export class GraphVisualizationComponent implements AfterContentInit { const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen const width = 1400 - margin.left - margin.right; const height = 900 - margin.top - margin.bottom; - const rad=20; // radius + const rad=20 // Radius eines Knotens + const nodeborder =8; // Umrandungsdicke eines Knotens -var svg = d3.select("#graph") // Größe u. Grenzen SVG + +const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) @@ -28,8 +30,21 @@ var svg = d3.select("#graph") // Größe u. Grenzen SVG -var colors = d3.scaleOrdinal(d3.schemeCategory10); //Farben - +const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + +svg.append('defs').append('marker') + .attr("id",'arrowhead') + .attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5) + .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('refY',0) + .attr('orient','auto') + .attr('markerWidth',13) + .attr('markerHeight',13) + .attr('xoverflow','visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke','none'); //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten @@ -89,23 +104,25 @@ const data = { - var link = svg // Links initialisieren + const link = svg // Links initialisieren .selectAll(".links") .data(data.links) .enter() .append("line") .style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)') ; link.append("title") .text(function(d){return d.type}); - var node = svg // Nodes initialisieren + const node = svg // Nodes initialisieren .selectAll("circle") .data(data.nodes) .enter() .append("circle") .attr("r", rad) .style( "fill", "white") - .style("stroke-width", 8) + .style("stroke-width", nodeborder) .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit /*.append ("text") // name als text neben node .attr ("dx", 12) @@ -133,10 +150,10 @@ const data = { - var simulation = d3.forceSimulation(data.nodes) //Simulation + const simulation = d3.forceSimulation(data.nodes) //Simulation .force("link", d3.forceLink() .id(function(d) { return d.id; }) - .distance(100) + .distance(150) .links(data.links) ) .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen @@ -145,7 +162,6 @@ const data = { function ticked() { - console.log(data); text .attr("dx", function(d) { return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) From 6c27373f46cd7e231af2be72920e7ab687947048 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 31 Jan 2020 14:56:00 +0100 Subject: [PATCH 04/74] Fixed bug Type of relation fixed (on path between two linked nodes) --- .../graph-visualization.component.ts | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index efe39ef0..d7894ca6 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -34,7 +34,7 @@ const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung v svg.append('defs').append('marker') .attr("id",'arrowhead') - .attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5) + .attr('viewBox','-0 -5 10 10') //Koordinatensystem .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten .attr('refY',0) .attr('orient','auto') @@ -146,6 +146,7 @@ const data = { .data(data.links) .enter() .append("text") + .style("fill","#999" ) .text(function (d){return d.type}); @@ -164,24 +165,46 @@ const data = { function ticked() { text .attr("dx", function(d) { - return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) - .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }) + return Math.max(0+rad, Math.min(d.x+23, width-rad))}) + .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }) linkText - .attr("dx", function(d){return 0.5*((d.source.x)+(d.target.x))}) - .attr("dy", function(d){return 0.5*((d.source.y)+(d.target.y)) }) + .attr("dx", function(d){ + if ((d.target.x>width) || (d.source.x>width)) { + if (d.target.x>d.source.x) { + return Math.min(width,width-0.5*(width-d.source.x)); + } else { + return Math.min(width,width-0.5*(width-d.target.x)); + } ; + } else { + return Math.min(0.5*((Math.max(0,(d.target.x)))-(Math.max(0,(d.source.x))))+(Math.max(0,d.source.x))); + } + }) + //Relationstext bleibt am Link + .attr("dy", function(d){ + if ((d.target.y>height) || (d.source.y>height)) { + if (d.target.y>d.source.y) { + return Math.min(height,height-0.5*(height-d.source.y)); + } + else { + return Math.min(height,height-0.5*(height-d.target.y)); + } + } + else { + return (0.5*((Math.max(0,(d.target.y)))-(Math.max(0,(d.source.y))))+(Math.max(0,d.source.y))); + }}) link // Anfang und Ende der Links / Bestimmung der Position - .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)); }) // Postionen nur innerhalb des Fensters erlaubt - .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)); }) - .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)); }) - .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)); }); + .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)) }) // Postionen nur innerhalb des Fensters erlaubt + .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)) }) + .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)) }) + .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)) }); node // Bestimmung der Postion einzelner Nodes - .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)); }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße - .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }); + .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)) }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }); } function ended() { From 06e333111ba79d16fc641e50b5e056d26bfcbdc9 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 18 Feb 2020 16:58:55 +0100 Subject: [PATCH 05/74] Added Link to Graph Visualization in Module-Management --- .../graph-visualization/graph-visualization.component.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html index fcff4a3d..3163f4b5 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html @@ -1,8 +1,9 @@

Graph-Visualization

-
+ +
-
\ No newline at end of file +
From c2015ed2b7c11ba94057165b7b47264c962c0c7a Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 28 Feb 2020 19:15:38 +0100 Subject: [PATCH 06/74] Resize svg when window resized --- .../graph-visualization.component.html | 7 +- .../graph-visualization.component.ts | 413 ++++++++++-------- 2 files changed, 238 insertions(+), 182 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html index 3163f4b5..a454716a 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html @@ -1,9 +1,8 @@

Graph-Visualization

- -
-
+
+
-
+
\ No newline at end of file diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index d7894ca6..b57ca058 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -1,7 +1,8 @@ -import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'; +import { Component, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; +import { window } from 'rxjs/operators'; @Component({ selector: 'graph-visualization', @@ -11,225 +12,281 @@ import { pathToFileURL } from 'url'; }) export class GraphVisualizationComponent implements AfterContentInit { - ngAfterContentInit(): void { - const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen - const width = 1400 - margin.left - margin.right; - const height = 900 - margin.top - margin.bottom; - const rad=20 // Radius eines Knotens - const nodeborder =8; // Umrandungsdicke eines Knotens + @ViewChild('g') svgContainer: ElementRef; + ngAfterContentInit(): void { + console.log('element ref width:'); + console.log(this.svgContainer.nativeElement.offsetWidth); + const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen + const width = 100; //parent.innerWidth - margin.left - margin.right; + //var width= window.innerWidth; + const height = 100; + const rad = 20 // Radius eines Knotens + const nodeborder = 8; // Umrandungsdicke eines Knotens + const testweite = parent.innerWidth; + console.log("Die ermittelte Weite ist:" + testweite); -const svg = d3.select("#graph") // Größe u. Grenzen SVG -.append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) -.append("g") - .attr("transform", + const svg = d3.select("#graph") // Größe u. Grenzen SVG + .append("svg") + .attr("width", width + '%') + .attr("height", height+'%') + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - -const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes - -svg.append('defs').append('marker') - .attr("id",'arrowhead') - .attr('viewBox','-0 -5 10 10') //Koordinatensystem - .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten - .attr('refY',0) - .attr('orient','auto') - .attr('markerWidth',13) - .attr('markerHeight',13) - .attr('xoverflow','visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke','none'); - -//d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - -const data = { - "nodes": [ - { - "id": 1, - "name": "ModulA", - "group": 1 - }, - { - "id": 2, - "name": "ModulB", - "group": 2 - }, - { - "id": 3, - "name": "ModulC", - "group": 3 - - }, { - "id": 4, - "name": "cap11", - "group":1 - }, - { - "id": 5, - "name": "cap12", - "group":1 - }, - { - "id": 6, - "name": "cap21", - "group":2 + + const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + + svg.append('defs').append('marker') + .attr("id", 'arrowhead') + .attr('viewBox', '-0 -5 10 10') //Koordinatensystem + .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('refY', 0) + .attr('orient', 'auto') + .attr('markerWidth', 13) + .attr('markerHeight', 13) + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke', 'none'); + + + //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten + + const data = { + "nodes": [ + { + "id": 1, + "name": "ModulA", + "group": 1 + }, + { + "id": 2, + "name": "ModulB", + "group": 2 + }, + { + "id": 3, + "name": "ModulC", + "group": 3 + + }, { + "id": 4, + "name": "cap11", + "group": 1 + }, + { + "id": 5, + "name": "cap12", + "group": 1 + }, + { + "id": 6, + "name": "cap21", + "group": 2 + } + ], + "links": [ + + { + "source": 1, + "target": 4, + "type": "is_a" + }, + { + "source": 1, + "target": 5, + "type": "has" + }, + { + "source": 2, + "target": 6, + "type": "has" + } + ] } - ], - "links": [ - - { - "source": 1, - "target": 4, - "type": "is_a" - } , - { - "source": 1, - "target": 5, - "type": "has" - } , - { - "source": 2, - "target": 6, - "type":"has" - } - ] -} - const link = svg // Links initialisieren - .selectAll(".links") - .data(data.links) - .enter() - .append("line") + const link = svg // Links initialisieren + .selectAll(".links") + .data(data.links) + .enter() + .append("line") .style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') - ; + ; link.append("title") - .text(function(d){return d.type}); - const node = svg // Nodes initialisieren - .selectAll("circle") - .data(data.nodes) - .enter() - .append("circle") + .text(function (d) { return d.type }); + const node = svg // Nodes initialisieren + .selectAll("circle") + .data(data.nodes) + .enter() + .append("circle") .attr("r", rad) - .style( "fill", "white") - .style("stroke-width", nodeborder) - .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit - /*.append ("text") // name als text neben node - .attr ("dx", 12) - .attr ("dy", ".35em") - .text(function(d){return d.name})*/ + .on('mouseover', function(){ + d3.select(this).transition() + .duration('2') + .attr('r', rad+5) + }) + .on('mouseout', function(){ + d3.select(this).transition() + .attr('r', rad) + }) + .style("fill", "white") + .style("stroke-width", nodeborder) + .style("stroke", function (d) { return colors(d.group); }) // Node-Farben nach Gruppenzugehörigkeit + /*.append ("text") // name als text neben node + .attr ("dx", 12) + .attr ("dy", ".35em") + .text(function(d){return d.name})*/ .call(d3.drag() - .on("start", dragstarted) - .on("drag", dragged)); -; - - const text =svg + .on("start", dragstarted) + .on("drag", dragged)); + ; + + const text = svg .selectAll("text") .data(data.nodes) .enter() .append("text") - - .text(function(d){return d.name}); - const linkText =svg + .text(function (d) { return d.name }); + + const linkText = svg .selectAll("links") .data(data.links) .enter() .append("text") - .style("fill","#999" ) - .text(function (d){return d.type}); + .style("fill", "#999") + .text(function (d) { return d.type }); + - - - const simulation = d3.forceSimulation(data.nodes) //Simulation - .force("link", d3.forceLink() - .id(function(d) { return d.id; }) - .distance(150) - .links(data.links) - ) - .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen - .force("center", d3.forceCenter(width / 2, height / 2)) // Nodes mittig von svg - .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - - function ticked() { - text - .attr("dx", function(d) { - return Math.max(0+rad, Math.min(d.x+23, width-rad))}) - .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }) - linkText - .attr("dx", function(d){ - if ((d.target.x>width) || (d.source.x>width)) { - if (d.target.x>d.source.x) { - return Math.min(width,width-0.5*(width-d.source.x)); - } else { - return Math.min(width,width-0.5*(width-d.target.x)); - } ; - } else { - return Math.min(0.5*((Math.max(0,(d.target.x)))-(Math.max(0,(d.source.x))))+(Math.max(0,d.source.x))); - } - }) - //Relationstext bleibt am Link - .attr("dy", function(d){ - if ((d.target.y>height) || (d.source.y>height)) { - if (d.target.y>d.source.y) { - return Math.min(height,height-0.5*(height-d.source.y)); - } - else { - return Math.min(height,height-0.5*(height-d.target.y)); - } - } - else { - return (0.5*((Math.max(0,(d.target.y)))-(Math.max(0,(d.source.y))))+(Math.max(0,d.source.y))); - }}) - - link // Anfang und Ende der Links / Bestimmung der Position - .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)) }) // Postionen nur innerhalb des Fensters erlaubt - .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)) }) - .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)) }) - .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)) }); + const ticked = () => { + const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; + const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - node // Bestimmung der Postion einzelner Nodes - .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)) }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße - .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }); - } + // data.nodes.indexOf[0].fx= centerWidth; + //data.nodes.indexOf[0].fy= centerHeight; + text + .attr("dx", (d)=>{ + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) + .attr("dy", (d) => { + const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) - function ended() { - console.log("ended"); - } + linkText + .attr("dx", (d)=>{ + d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); + d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(width- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceX-relativeTargetX)/2)+ relativeTargetX))) +'%'; + }) + .attr("dy", (d)=>{ + d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; + }) - function dragstarted(d) { - if (!d3.event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } + link // Anfang und Ende der Links / Bestimmung der Position nachfolgend Closure-Funtktionen um Zugriff mit this. außerhalb der Funktion + .attr("x1", (d)=> { + d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // Umwandlung des Radius in relative Zahl (in % !) + const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX+'%';}) + .attr("y1", (d)=> { + d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY+'%';}) + .attr("x2", (d)=> { + d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX+'%';}) + .attr("y2", (d)=> { + d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY+'%';}) + + node // Bestimmung der Postion einzelner Nodes + .attr("cx", (d) => { + d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX+'%'; + }) // Zentrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", (d) => { + d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) + const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY+'%';}) + } + + const simulation = d3.forceSimulation(data.nodes) //Simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(300) + .links(data.links) + ) + .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes + + + + + .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - function dragged(d) { - console.log(d) + function ended() { + console.log("ended"); + } + function mouseover(){ + d3.select(this).select("circle").transition() + .duration(750) + .attr("r", rad+10) + } + + function mouseout(){ + d3.select(this).select("circle").transition() + .duration(750) + .attr("r", rad) + } + function dragstarted(d) { + if (!d3.event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + + console.log(d3.event) 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; } - - } + + } constructor() { } } From 7d715f375e47311553fd7f33ccba657c69346aaf Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:15:06 +0100 Subject: [PATCH 07/74] Added function to get data for ontology-visualization of each module --- .../manufacturing-service-executor.service.ts | 44 ++++++ .../graph-visualization.component.ts | 130 ++++++++++++++++-- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 + 4 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts diff --git a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts new file mode 100644 index 00000000..338e7fa0 --- /dev/null +++ b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from "@angular/core"; +import { HttpClient, HttpRequest, HttpHeaders, HttpParams } from "@angular/common/http"; +import { Observable } from "rxjs"; +//import { ServiceExecutionDescription } from "./self-description"; +import { map } from 'rxjs/operators'; +import { ServiceExecutionDescription } from "app/shared/models/self-description"; + + +@Injectable() + +export class ManufacturingServiceExecutor { + + constructor(private http: HttpClient) {} + + executeService(executionDescription:ServiceExecutionDescription) { + console.log(`service called`); + console.log(executionDescription); + + // // construct the request + // let headers = new HttpHeaders(); + // executionDescription.parameters.forEach(parameter => { + // if (parameter.getShortType() == "HeaderParameter") { + // headers.set(parameter.name, parameter.value); + // } + // }); + + // let queryParams = new HttpParams(); + // executionDescription.parameters.forEach(parameter => { + // if (parameter.getShortType() == "QueryParameter") { + // queryParams.set(parameter.name, parameter.value); + // } + // }); + + // let request = new HttpRequest(executionDescription.methodType, executionDescription.fullPath, { + // "headers": headers, + // "params": queryParams + // }) + + // Send + this.http.post(`api/service-executions`, executionDescription).subscribe(res => {console.log(JSON.stringify(res))}); + } + + +} \ No newline at end of file diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index b57ca058..2c46fd12 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -3,6 +3,8 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; +import { ModuleService } from 'app/shared/services/module.service'; + @Component({ selector: 'graph-visualization', @@ -12,11 +14,16 @@ import { window } from 'rxjs/operators'; }) export class GraphVisualizationComponent implements AfterContentInit { + constructor( + private moduleService: ModuleService + ) { + }; + @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - console.log('element ref width:'); - console.log(this.svgContainer.nativeElement.offsetWidth); + //console.log('element ref width:'); + // console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -25,7 +32,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - console.log("Die ermittelte Weite ist:" + testweite); + //console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -54,9 +61,12 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - const data = { + +// ##### DATA ###### + + + /*const data = { "nodes": [ { "id": 1, @@ -108,8 +118,107 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } +*/ +var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + var group=0; + var moduleId=0; + var capabilityId=100; + var inputId=1000; + var outputId=10000; + var skillId=100000; + var typeId=1000000; + var data= {nodes:[], links:[]}; + //################################################ + receivedData.forEach(modul => { + moduleId++; + group++; + data.nodes.push({ + "id" : moduleId, + "name": modul.name, + "group": 1 + }); + modul.capabilities.forEach(capability=>{ + capabilityId++; + data.nodes.push({ + "id" : capabilityId, + "name": capability.name, + "group": 2 + }) + data.links.push({ + "source": moduleId, + "target": capabilityId, + "type": "has_capability" + }) + capability.hasInput.forEach(input=>{ + inputId++; + typeId++; + data.nodes.push({ + "id" : inputId, + "name": input.name, + "group": 3 + }) + data.links.push({ + "source": capabilityId, + "target": inputId, + "type": "has_input" + }) + data.nodes.push({ + "id": typeId, + "name": input.stateType, + "group": 6 + }) + data.links.push({ + "source": inputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.hasOutput.forEach(output=>{ + outputId++; + typeId++; + data.nodes.push({ + "id" : outputId, + "name": output.name, + "group": 4 + }) + data.links.push({ + "source": capabilityId, + "target": outputId, + "type": "has_output" + }) + + data.nodes.push({ + "id": typeId, + "name": output.stateType, + "group": 6 + }) + data.links.push({ + "source": outputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.executableViaSkill.forEach(skill=>{ + skillId++; + data.nodes.push({ + "id" : skillId, + "name": skill.name, + "group": 5 + }) + data.links.push({ + "source": capabilityId, + "target": skillId, + "type": "executable_via_Skill" + }) + }) + }) + + }); +// ################################################## + + console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -246,7 +355,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -255,7 +364,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - console.log("ended"); + //console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -276,7 +385,7 @@ export class GraphVisualizationComponent implements AfterContentInit { function dragged(d) { - console.log(d3.event) + // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -287,6 +396,9 @@ export class GraphVisualizationComponent implements AfterContentInit { } } - constructor() { } + + + + } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts index afb5fff0..df9c2713 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -1,11 +1,15 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; +import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter + GraphVisualizationRouter, ], - declarations: [GraphVisualizationComponent] + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index ae60048e..5790705b 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,3 +1,4 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; + From 5a2ef6810b6b704d6f56da9760a207cae03c3b04 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:17:05 +0100 Subject: [PATCH 08/74] Revert "Added function to get data for ontology-visualization of each module" This reverts commit 8b9e53e21219f952dc96071abaab17af6acf8a40. --- .../manufacturing-service-executor.service.ts | 4 +- .../graph-visualization.component.ts | 130 ++---------------- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 - 4 files changed, 12 insertions(+), 131 deletions(-) diff --git a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts index 338e7fa0..4b1a70c8 100644 --- a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts +++ b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts @@ -1,10 +1,8 @@ import { Injectable } from "@angular/core"; import { HttpClient, HttpRequest, HttpHeaders, HttpParams } from "@angular/common/http"; import { Observable } from "rxjs"; -//import { ServiceExecutionDescription } from "./self-description"; +import { ServiceExecutionDescription } from "./self-description"; import { map } from 'rxjs/operators'; -import { ServiceExecutionDescription } from "app/shared/models/self-description"; - @Injectable() diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 2c46fd12..b57ca058 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -3,8 +3,6 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; -import { ModuleService } from 'app/shared/services/module.service'; - @Component({ selector: 'graph-visualization', @@ -14,16 +12,11 @@ import { ModuleService } from 'app/shared/services/module.service'; }) export class GraphVisualizationComponent implements AfterContentInit { - constructor( - private moduleService: ModuleService - ) { - }; - @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - //console.log('element ref width:'); - // console.log(this.svgContainer.nativeElement.offsetWidth); + console.log('element ref width:'); + console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -32,7 +25,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - //console.log("Die ermittelte Weite ist:" + testweite); + console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -61,12 +54,9 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); + //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - -// ##### DATA ###### - - - /*const data = { + const data = { "nodes": [ { "id": 1, @@ -118,107 +108,8 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } -*/ -var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - var group=0; - var moduleId=0; - var capabilityId=100; - var inputId=1000; - var outputId=10000; - var skillId=100000; - var typeId=1000000; - var data= {nodes:[], links:[]}; - //################################################ - receivedData.forEach(modul => { - moduleId++; - group++; - data.nodes.push({ - "id" : moduleId, - "name": modul.name, - "group": 1 - }); - modul.capabilities.forEach(capability=>{ - capabilityId++; - data.nodes.push({ - "id" : capabilityId, - "name": capability.name, - "group": 2 - }) - data.links.push({ - "source": moduleId, - "target": capabilityId, - "type": "has_capability" - }) - capability.hasInput.forEach(input=>{ - inputId++; - typeId++; - data.nodes.push({ - "id" : inputId, - "name": input.name, - "group": 3 - }) - data.links.push({ - "source": capabilityId, - "target": inputId, - "type": "has_input" - }) - data.nodes.push({ - "id": typeId, - "name": input.stateType, - "group": 6 - }) - data.links.push({ - "source": inputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.hasOutput.forEach(output=>{ - outputId++; - typeId++; - data.nodes.push({ - "id" : outputId, - "name": output.name, - "group": 4 - }) - data.links.push({ - "source": capabilityId, - "target": outputId, - "type": "has_output" - }) - - data.nodes.push({ - "id": typeId, - "name": output.stateType, - "group": 6 - }) - data.links.push({ - "source": outputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.executableViaSkill.forEach(skill=>{ - skillId++; - data.nodes.push({ - "id" : skillId, - "name": skill.name, - "group": 5 - }) - data.links.push({ - "source": capabilityId, - "target": skillId, - "type": "executable_via_Skill" - }) - }) - }) - - }); -// ################################################## - - console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -355,7 +246,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -364,7 +255,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - //console.log("ended"); + console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -385,7 +276,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); function dragged(d) { - // console.log(d3.event) + console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -396,9 +287,6 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); } } - - - - + constructor() { } } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts index df9c2713..afb5fff0 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -1,15 +1,11 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter, + GraphVisualizationRouter ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - ] + declarations: [GraphVisualizationComponent] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index 5790705b..ae60048e 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,4 +1,3 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; - From 620625f76c7bbdf5743cbc0cf7631cf96d6ef67a Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:17:55 +0100 Subject: [PATCH 09/74] Revert "Revert "Added function to get data for ontology-visualization of each module"" This reverts commit e3e86ce7d5f9a64ca29b6727bcc3077f1b4ba03a. --- .../manufacturing-service-executor.service.ts | 4 +- .../graph-visualization.component.ts | 130 ++++++++++++++++-- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 + 4 files changed, 131 insertions(+), 12 deletions(-) diff --git a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts index 4b1a70c8..338e7fa0 100644 --- a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts +++ b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts @@ -1,8 +1,10 @@ import { Injectable } from "@angular/core"; import { HttpClient, HttpRequest, HttpHeaders, HttpParams } from "@angular/common/http"; import { Observable } from "rxjs"; -import { ServiceExecutionDescription } from "./self-description"; +//import { ServiceExecutionDescription } from "./self-description"; import { map } from 'rxjs/operators'; +import { ServiceExecutionDescription } from "app/shared/models/self-description"; + @Injectable() diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index b57ca058..2c46fd12 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -3,6 +3,8 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; +import { ModuleService } from 'app/shared/services/module.service'; + @Component({ selector: 'graph-visualization', @@ -12,11 +14,16 @@ import { window } from 'rxjs/operators'; }) export class GraphVisualizationComponent implements AfterContentInit { + constructor( + private moduleService: ModuleService + ) { + }; + @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - console.log('element ref width:'); - console.log(this.svgContainer.nativeElement.offsetWidth); + //console.log('element ref width:'); + // console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -25,7 +32,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - console.log("Die ermittelte Weite ist:" + testweite); + //console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -54,9 +61,12 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - const data = { + +// ##### DATA ###### + + + /*const data = { "nodes": [ { "id": 1, @@ -108,8 +118,107 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } +*/ +var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + var group=0; + var moduleId=0; + var capabilityId=100; + var inputId=1000; + var outputId=10000; + var skillId=100000; + var typeId=1000000; + var data= {nodes:[], links:[]}; + //################################################ + receivedData.forEach(modul => { + moduleId++; + group++; + data.nodes.push({ + "id" : moduleId, + "name": modul.name, + "group": 1 + }); + modul.capabilities.forEach(capability=>{ + capabilityId++; + data.nodes.push({ + "id" : capabilityId, + "name": capability.name, + "group": 2 + }) + data.links.push({ + "source": moduleId, + "target": capabilityId, + "type": "has_capability" + }) + capability.hasInput.forEach(input=>{ + inputId++; + typeId++; + data.nodes.push({ + "id" : inputId, + "name": input.name, + "group": 3 + }) + data.links.push({ + "source": capabilityId, + "target": inputId, + "type": "has_input" + }) + data.nodes.push({ + "id": typeId, + "name": input.stateType, + "group": 6 + }) + data.links.push({ + "source": inputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.hasOutput.forEach(output=>{ + outputId++; + typeId++; + data.nodes.push({ + "id" : outputId, + "name": output.name, + "group": 4 + }) + data.links.push({ + "source": capabilityId, + "target": outputId, + "type": "has_output" + }) + + data.nodes.push({ + "id": typeId, + "name": output.stateType, + "group": 6 + }) + data.links.push({ + "source": outputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.executableViaSkill.forEach(skill=>{ + skillId++; + data.nodes.push({ + "id" : skillId, + "name": skill.name, + "group": 5 + }) + data.links.push({ + "source": capabilityId, + "target": skillId, + "type": "executable_via_Skill" + }) + }) + }) + + }); +// ################################################## + + console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -246,7 +355,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -255,7 +364,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - console.log("ended"); + //console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -276,7 +385,7 @@ export class GraphVisualizationComponent implements AfterContentInit { function dragged(d) { - console.log(d3.event) + // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -287,6 +396,9 @@ export class GraphVisualizationComponent implements AfterContentInit { } } - constructor() { } + + + + } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts index afb5fff0..df9c2713 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -1,11 +1,15 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; +import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter + GraphVisualizationRouter, ], - declarations: [GraphVisualizationComponent] + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index ae60048e..5790705b 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,3 +1,4 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; + From f06b29b0ededbd0fdd43f07d30db4d5e641fde61 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Sun, 29 Mar 2020 14:18:25 +0200 Subject: [PATCH 10/74] Modified node-ID assignment. Modified comments --- .../graph-visualization.component.ts | 345 +++++++----------- frontend/typings.d.ts | 5 - 2 files changed, 140 insertions(+), 210 deletions(-) delete mode 100644 frontend/typings.d.ts diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 2c46fd12..87a1ef6a 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -22,19 +22,14 @@ export class GraphVisualizationComponent implements AfterContentInit { @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - //console.log('element ref width:'); - // console.log(this.svgContainer.nativeElement.offsetWidth); - - const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen - const width = 100; //parent.innerWidth - margin.left - margin.right; + const margin = { top: 10, right: 30, bottom: 30, left: 40 }; + const width = 100; //parent.innerWidth - margin.left - margin.right; //var width= window.innerWidth; const height = 100; - const rad = 20 // Radius eines Knotens - const nodeborder = 8; // Umrandungsdicke eines Knotens + const rad = 20 // node radius + const nodeborder = 8; // node border thickness const testweite = parent.innerWidth; - //console.log("Die ermittelte Weite ist:" + testweite); - - const svg = d3.select("#graph") // Größe u. Grenzen SVG + const svg = d3.select("#graph") // size definitions for the svg .append("svg") .attr("width", width + '%') .attr("height", height+'%') @@ -44,12 +39,12 @@ export class GraphVisualizationComponent implements AfterContentInit { - const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + const colors = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours svg.append('defs').append('marker') .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //Koordinatensystem - .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('viewBox', '-0 -5 10 10') //coordinate system + .attr('refX', rad + nodeborder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -63,193 +58,141 @@ export class GraphVisualizationComponent implements AfterContentInit { -// ##### DATA ###### - - - /*const data = { - "nodes": [ - { - "id": 1, - "name": "ModulA", - "group": 1 - }, - { - "id": 2, - "name": "ModulB", - "group": 2 - }, - { - "id": 3, - "name": "ModulC", - "group": 3 - - }, { - "id": 4, - "name": "cap11", - "group": 1 - }, - { - "id": 5, - "name": "cap12", - "group": 1 - }, - { - "id": 6, - "name": "cap21", - "group": 2 - } - ], - "links": [ - - { - "source": 1, - "target": 4, - "type": "is_a" - }, - { - "source": 1, - "target": 5, - "type": "has" - }, - { - "source": 2, - "target": 6, - "type": "has" - } - ] - } -*/ +/* Get data*/ +//########################################################## var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - var group=0; - var moduleId=0; - var capabilityId=100; - var inputId=1000; - var outputId=10000; - var skillId=100000; - var typeId=1000000; - var data= {nodes:[], links:[]}; - //################################################ - receivedData.forEach(modul => { - moduleId++; - group++; - data.nodes.push({ - "id" : moduleId, - "name": modul.name, - "group": 1 - }); - modul.capabilities.forEach(capability=>{ - capabilityId++; - data.nodes.push({ - "id" : capabilityId, - "name": capability.name, - "group": 2 - }) - data.links.push({ - "source": moduleId, - "target": capabilityId, - "type": "has_capability" - }) - capability.hasInput.forEach(input=>{ - inputId++; - typeId++; - data.nodes.push({ - "id" : inputId, - "name": input.name, - "group": 3 - }) - data.links.push({ - "source": capabilityId, - "target": inputId, - "type": "has_input" - }) - data.nodes.push({ - "id": typeId, - "name": input.stateType, - "group": 6 - }) - data.links.push({ - "source": inputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.hasOutput.forEach(output=>{ - outputId++; - typeId++; - data.nodes.push({ - "id" : outputId, - "name": output.name, - "group": 4 - }) - data.links.push({ - "source": capabilityId, - "target": outputId, - "type": "has_output" - }) - - data.nodes.push({ - "id": typeId, - "name": output.stateType, - "group": 6 - }) - data.links.push({ - "source": outputId, - "target": typeId, - "type": "is_state_type" - }) - - }) - capability.executableViaSkill.forEach(skill=>{ - skillId++; - data.nodes.push({ - "id" : skillId, - "name": skill.name, - "group": 5 - }) - data.links.push({ - "source": capabilityId, - "target": skillId, - "type": "executable_via_Skill" - }) - }) - - }) +var data= {nodes:[], links:[]}; +let idMap= new Map(); // Map for saving Node-IDs +var id=0; // ID start value +receivedData.forEach(receivedModule => { //loop over all modules +id++; +idMap.set(receivedModule, id); // assigns an ID for each module-node +data.nodes.push({ // adds a node for each module +"id" : idMap.get(receivedModule), +"name": receivedModule.name, +"group": 1 // assings node-groups (here: node colour) +}); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.name, + "group": 2 + }) + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }) + + capability.hasInput.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.name, + "group": 3 + }) + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }) + }) + + capability.hasOutput.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.name, + "group": 4 + }) + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }) + }) + + capability.executableViaSkill.forEach(skill=>{ + id++; + idMap.set(skill, id) // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.name, + "group": 5 + }) + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }) + }) + + }) - }); +}); // ################################################## console.log(data); - const link = svg // Links initialisieren + const link = svg // link definitions .selectAll(".links") .data(data.links) .enter() .append("line") .style("stroke", "#aaa") .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') + .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; link.append("title") - .text(function (d) { return d.type }); - const node = svg // Nodes initialisieren + .text(function (d) { return d.type }); // get link title from data + const node = svg // node definitions .selectAll("circle") .data(data.nodes) .enter() .append("circle") .attr("r", rad) - .on('mouseover', function(){ + .on('mouseover', function(){ // function on mousover: increase radius d3.select(this).transition() .duration('2') - .attr('r', rad+5) + .attr('r', rad+6) }) - .on('mouseout', function(){ + .on('mouseout', function(){ // function on mousout: decrease radius to orgin d3.select(this).transition() .attr('r', rad) }) + .on('dblclick', doubleclick)// function on doubleclick: + .style("fill", "white") .style("stroke-width", nodeborder) - .style("stroke", function (d) { return colors(d.group); }) // Node-Farben nach Gruppenzugehörigkeit - /*.append ("text") // name als text neben node + .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group + /*.append ("text") .attr ("dx", 12) .attr ("dy", ".35em") .text(function(d){return d.name})*/ @@ -258,21 +201,21 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .on("drag", dragged)); ; - const text = svg + const text = svg // node text definitions .selectAll("text") .data(data.nodes) .enter() .append("text") - .text(function (d) { return d.name }); + .text(function (d) { return d.name }); // get text from data - const linkText = svg + const linkText = svg // link text definitions .selectAll("links") .data(data.links) .enter() .append("text") .style("fill", "#999") - .text(function (d) { return d.type }); + .text(function (d) { return d.type }); // get link text from data @@ -280,13 +223,11 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const ticked = () => { - const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; + const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; // center definitions of the svg const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - // data.nodes.indexOf[0].fx= centerWidth; - //data.nodes.indexOf[0].fy= centerHeight; text - .attr("dx", (d)=>{ + .attr("dx", (d)=>{ // restrictions for node text positions in the svg const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) @@ -296,7 +237,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) linkText - .attr("dx", (d)=>{ + .attr("dx", (d)=>{ // restrictions for link text positions in the svg d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; @@ -313,10 +254,10 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; }) - link // Anfang und Ende der Links / Bestimmung der Position nachfolgend Closure-Funtktionen um Zugriff mit this. außerhalb der Funktion - .attr("x1", (d)=> { + link + .attr("x1", (d)=> { // restrictions for link positions in the svg d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // Umwandlung des Radius in relative Zahl (in % !) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // converting radius from absolute to relative const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; return relativeSourceX+'%';}) .attr("y1", (d)=> { @@ -335,13 +276,13 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; return relativeTargetY+'%';}) - node // Bestimmung der Postion einzelner Nodes - .attr("cx", (d) => { + node + .attr("cx", (d) => { // restrictions for node positions in the svg d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; return relativeDragX+'%'; - }) // Zentrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + }) .attr("cy", (d) => { d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; @@ -349,39 +290,33 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return relativeDragY+'%';}) } - const simulation = d3.forceSimulation(data.nodes) //Simulation + const simulation = d3.forceSimulation(data.nodes) // simulation .force("link", d3.forceLink() .id(function (d) { return d.id; }) .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes - .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - - function ended() { - //console.log("ended"); - } - function mouseover(){ - d3.select(this).select("circle").transition() - .duration(750) - .attr("r", rad+10) - } + .on("tick", ticked); // tick on every step of the simulation - function mouseout(){ - d3.select(this).select("circle").transition() - .duration(750) - .attr("r", rad) - } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } + function doubleclick(){ + id++; + data.nodes.push({ + "id": id, + "name": "neighbor", + "group": 7 + }) + } function dragged(d) { diff --git a/frontend/typings.d.ts b/frontend/typings.d.ts deleted file mode 100644 index ef5c7bd6..00000000 --- a/frontend/typings.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* SystemJS module definition */ -declare var module: NodeModule; -interface NodeModule { - id: string; -} From 668d94f9a36f9f282e3c2c239b48a29836eb635d Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Thu, 2 Apr 2020 20:51:41 +0200 Subject: [PATCH 11/74] Added new service for node creation --- .../node-creator.service.spec.ts | 16 ++ .../node-creator.service.ts | 122 +++++++++++++++ .../graph-visualization.component.ts | 139 ++++-------------- .../graph-visualization.module.ts | 3 + 4 files changed, 168 insertions(+), 112 deletions(-) create mode 100644 frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts create mode 100644 frontend/app/own-modules/graph-visualization/node-creator.service.ts diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts new file mode 100644 index 00000000..c3ee0483 --- /dev/null +++ b/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts @@ -0,0 +1,16 @@ +/* tslint:disable:no-unused-variable */ + +import { TestBed, async, inject } from '@angular/core/testing'; +import { NodeCreatorService } from './node-creator.service'; + +describe('Service: NodeCreator', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [NodeCreatorService] + }); + }); + + it('should ...', inject([NodeCreatorService], (service: NodeCreatorService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.ts new file mode 100644 index 00000000..fbad86eb --- /dev/null +++ b/frontend/app/own-modules/graph-visualization/node-creator.service.ts @@ -0,0 +1,122 @@ +import { Injectable } from '@angular/core'; +import { ModuleService } from 'app/shared/services/module.service'; + +@Injectable({ + providedIn: 'root' +}) +export class NodeCreatorService { + apiRoot: string = "/api"; +constructor( + private moduleService: ModuleService +) {} +getAllNodes() { + + var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + const data= {nodes:[], links:[]}; + let idMap= new Map(); // Map for saving Node-IDs + var id=0; // ID start value + receivedData.forEach(receivedModule => { //loop over all modules + id++; + idMap.set(receivedModule, id); // assigns an ID for each module-node + data.nodes.push({ // adds a node for each module + "id" : idMap.get(receivedModule), + "name": receivedModule.name, + "group": 1 // assings node-groups (here: node colour) + }); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.name, + "group": 2 + }) + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }) + + capability.hasInput.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.name, + "group": 3 + }) + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }) + }) + + capability.hasOutput.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.name, + "group": 4 + }) + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }) + }) + + capability.executableViaSkill.forEach(skill=>{ + id++; + idMap.set(skill, id) // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.name, + "group": 5 + }) + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }) + }) + + }) + + }); + +return data; +} + + +} + + + + + + diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 87a1ef6a..2b712bc0 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -4,6 +4,8 @@ import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; import { ModuleService } from 'app/shared/services/module.service'; +import { ResolveStart } from '@angular/router'; +import { NodeCreatorService } from './node-creator.service'; @Component({ @@ -15,7 +17,8 @@ import { ModuleService } from 'app/shared/services/module.service'; export class GraphVisualizationComponent implements AfterContentInit { constructor( - private moduleService: ModuleService + //private moduleService: ModuleService, + private nodeCreatorService: NodeCreatorService ) { }; @@ -56,108 +59,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - - -/* Get data*/ -//########################################################## -var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); -var data= {nodes:[], links:[]}; -let idMap= new Map(); // Map for saving Node-IDs -var id=0; // ID start value -receivedData.forEach(receivedModule => { //loop over all modules -id++; -idMap.set(receivedModule, id); // assigns an ID for each module-node -data.nodes.push({ // adds a node for each module -"id" : idMap.get(receivedModule), -"name": receivedModule.name, -"group": 1 // assings node-groups (here: node colour) -}); - - receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module - id++; - idMap.set(capability, id); // assigns an ID for each capability-node - data.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.name, - "group": 2 - }) - data.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description - }) - - capability.hasInput.forEach(input=>{ - id++; - idMap.set(input, id); // assigns an ID for each input-node - data.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.name, - "group": 3 - }) - data.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }) - }) - - capability.hasOutput.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - data.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.name, - "group": 4 - }) - data.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }) - }) - - capability.executableViaSkill.forEach(skill=>{ - id++; - idMap.set(skill, id) // adds a node for each skill of current capability - data.nodes.push({ - "id" : idMap.get(skill), - "name": skill.name, - "group": 5 - }) - data.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" - }) - }) - - }) - -}); -// ################################################## +var data= this.nodeCreatorService.getAllNodes(); // load data created by node-creator.service console.log(data); @@ -187,8 +89,17 @@ data.nodes.push({ // adds a node for each module d3.select(this).transition() .attr('r', rad) }) - .on('dblclick', doubleclick)// function on doubleclick: - + .on('dblclick', function(d){ const node= { "id":30, + "name": "neighbor", + "group": 7} + data.nodes.push(node) + data.links.push({ + "source": d.id, + "target":30 + }) + + console.log(d);// function on doubleclick: + }) .style("fill", "white") .style("stroke-width", nodeborder) .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group @@ -309,15 +220,19 @@ data.nodes.push({ // adds a node for each module d.fx = d.x; d.fy = d.y; } - function doubleclick(){ - id++; - data.nodes.push({ - "id": id, - "name": "neighbor", - "group": 7 + /*function doubleclick(d){ + + const node= {x: 500, y:50, "id":1000, + "name": "neighbor", + "group": 7} + data.nodes.push(node) + data.links.push({ + "source": d.id, + "target":1000 }) + console.log(d); } - +*/ function dragged(d) { // console.log(d3.event) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts index df9c2713..711024bc 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; import { ModuleService } from 'app/shared/services/module.service'; +import { NodeCreatorService } from './node-creator.service'; @NgModule({ imports: [ @@ -10,6 +11,8 @@ import { ModuleService } from 'app/shared/services/module.service'; declarations: [GraphVisualizationComponent], providers: [ ModuleService, + NodeCreatorService + ] }) export class GraphVisualizationModule { } From d375ec3afa6f18b87a3d187c7b186355d36a0bc5 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 1 May 2020 15:41:52 +0200 Subject: [PATCH 12/74] Visualization of chosen module-graph possible. --- .../node-creator.service.ts | 17 +++++++-- .../components/sidebar/sidebar.component.html | 1 + .../graph-visualization.component.ts | 35 +++++++++++++++---- .../graph-visualization.routing.ts | 12 +++---- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.ts index fbad86eb..89c940ec 100644 --- a/frontend/app/own-modules/graph-visualization/node-creator.service.ts +++ b/frontend/app/own-modules/graph-visualization/node-creator.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { ModuleService } from 'app/shared/services/module.service'; +import { containsElement } from '@angular/animations/browser/src/render/shared'; @Injectable({ providedIn: 'root' @@ -9,13 +10,14 @@ export class NodeCreatorService { constructor( private moduleService: ModuleService ) {} -getAllNodes() { +getAllNodes(moduleName:String) { var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const data= {nodes:[], links:[]}; let idMap= new Map(); // Map for saving Node-IDs var id=0; // ID start value receivedData.forEach(receivedModule => { //loop over all modules + if(receivedModule.name==moduleName|| moduleName== undefined){ // creates nodes for chosen modules id++; idMap.set(receivedModule, id); // assigns an ID for each module-node data.nodes.push({ // adds a node for each module @@ -106,9 +108,18 @@ getAllNodes() { }) }) - - }); + } + }); + if(id==0){ //if module does not exist, show error-node + id++; + data.nodes.push({ + "id" : id, + "name": "<<>>", + "group": 1 + }) + } + return data; } diff --git a/frontend/src/layout/components/sidebar/sidebar.component.html b/frontend/src/layout/components/sidebar/sidebar.component.html index a37215e1..b6d73e7d 100644 --- a/frontend/src/layout/components/sidebar/sidebar.component.html +++ b/frontend/src/layout/components/sidebar/sidebar.component.html @@ -45,6 +45,7 @@
+   Graph-Visualization diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 2b712bc0..5a918e16 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -1,11 +1,15 @@ -import { Component, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component,OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; import { ModuleService } from 'app/shared/services/module.service'; -import { ResolveStart } from '@angular/router'; +import { ResolveStart, ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; +import { getModuleFactory__POST_R3__ } from '@angular/core/src/linker/ng_module_factory_loader'; +import { defineDirective } from '@angular/core/src/render3'; + + @Component({ @@ -14,15 +18,30 @@ import { NodeCreatorService } from './node-creator.service'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit { +export class GraphVisualizationComponent implements AfterContentInit, OnInit {name:String; constructor( - //private moduleService: ModuleService, + private route: ActivatedRoute, private nodeCreatorService: NodeCreatorService ) { }; @ViewChild('g') svgContainer: ElementRef; + moduleName: string; + ngOnInit(): void + { + + + // console.log("Der Name ist :"+this.moduleName); + +//console.log(this.route.queryParams); + + //.subscribe(params=>params.name) + + //this.name= params.name; + + } + ngAfterContentInit(): void { const margin = { top: 10, right: 30, bottom: 30, left: 40 }; @@ -58,10 +77,11 @@ export class GraphVisualizationComponent implements AfterContentInit { .attr('fill', '#999') .style('stroke', 'none'); +this.route.params.subscribe(p=>{ + this.moduleName=p['moduleName']; +}) +var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service -var data= this.nodeCreatorService.getAllNodes(); // load data created by node-creator.service - - console.log(data); const link = svg // link definitions .selectAll(".links") @@ -246,6 +266,7 @@ var data= this.nodeCreatorService.getAllNodes(); // load data created by node-cr } } + diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts index ded3fe38..ba430b6c 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts @@ -1,15 +1,13 @@ import { Routes, RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import {GraphVisualizationComponent} from './graph-visualization.component' +import {GraphVisualizationComponent} from './graph-visualization.component'; const routes: Routes = [ - { path: '', + { path: 'modules/:moduleName', component: GraphVisualizationComponent, - data: { - title: 'Graph-Visualization' - }, - children: [] - + + }, + {path:'modules', component: GraphVisualizationComponent } ]; From 82da9f1216084fc73969be22992b218779066b1d Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 1 May 2020 15:50:51 +0200 Subject: [PATCH 13/74] Modified some stylings --- .../graph-visualization.component.ts | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 5a918e16..b705e793 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -99,28 +99,16 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .data(data.nodes) .enter() .append("circle") - .attr("r", rad) - .on('mouseover', function(){ // function on mousover: increase radius - d3.select(this).transition() - .duration('2') - .attr('r', rad+6) + .attr("r",function(d){ + if(d.group==1){return rad+8} + else{return rad} }) - .on('mouseout', function(){ // function on mousout: decrease radius to orgin - d3.select(this).transition() - .attr('r', rad) - }) - .on('dblclick', function(d){ const node= { "id":30, - "name": "neighbor", - "group": 7} - data.nodes.push(node) - data.links.push({ - "source": d.id, - "target":30 + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .style("fill", function(d){ + if(d.group==1){return "black"} + else{return "whitesmoke"} }) - - console.log(d);// function on doubleclick: - }) - .style("fill", "white") .style("stroke-width", nodeborder) .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group /*.append ("text") @@ -137,7 +125,10 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .data(data.nodes) .enter() .append("text") - + .attr("font-weight", function(d){ + if(d.group==1){return "bold"} + else{return "normal"} + }) .text(function (d) { return d.name }); // get text from data const linkText = svg // link text definitions @@ -146,6 +137,7 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .enter() .append("text") .style("fill", "#999") + .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data @@ -265,6 +257,25 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre //d.fy = null; } + function mouseover(d){ // function on mousover: increase radius + if(d.group==1){ + d3.select(this).transition() + .duration('2') + .attr('r', rad+12)} + else{ + d3.select(this).transition() + .duration('2') + .attr('r', rad+6)} + } + function mouseout(d){ // function on mousout: decrease radius to orgin + if(d.group==1){ + d3.select(this).transition() + .attr('r', rad+8) + } + else{ d3.select(this).transition() + .attr('r', rad)} + } + } From 2458ff3d8eba14594340da6aa1e9c9415e0258d7 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Wed, 27 May 2020 21:17:44 +0200 Subject: [PATCH 14/74] Adding nodes on doubleclick possible --- .../graph-visualization.component.ts | 569 +++++++++++------- 1 file changed, 363 insertions(+), 206 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index b705e793..0cd91854 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -1,13 +1,11 @@ -import { Component,OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" -import { enterView } from '@angular/core/src/render3/state'; -import { pathToFileURL } from 'url'; -import { window } from 'rxjs/operators'; -import { ModuleService } from 'app/shared/services/module.service'; -import { ResolveStart, ActivatedRoute } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; -import { getModuleFactory__POST_R3__ } from '@angular/core/src/linker/ng_module_factory_loader'; -import { defineDirective } from '@angular/core/src/render3'; + + + + @@ -18,55 +16,76 @@ import { defineDirective } from '@angular/core/src/render3'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit, OnInit {name:String; +export class GraphVisualizationComponent implements AfterContentInit, OnInit { + name: String; + svg: any; + link: any; + /** The displayed node name*/ + text: any; + /** The displayed link description */ + linkText: any; + /** The displayed node */ + node: any; + /** Loaded data: Connected modules with their capabilities and skills*/ + data: any; + /**Returns different colors automatically. The node-border color is the same for all node-group members*/ + color: any; +/**Simulation of the force directed graph */ + simulation: any; + + margin = { top: 10, right: 30, bottom: 30, left: 40 }; + width = 100; + height = 100; +/**Defines the nodeborder thickness*/ + nodeborder = 8; + /**Defines the standard radius of a node*/ + rad = 20; + + index=0; constructor( private route: ActivatedRoute, private nodeCreatorService: NodeCreatorService - ) { + ) { }; @ViewChild('g') svgContainer: ElementRef; moduleName: string; - ngOnInit(): void - { - + ngOnInit(): void { - // console.log("Der Name ist :"+this.moduleName); -//console.log(this.route.queryParams); + // console.log("Der Name ist :"+this.moduleName); + + //console.log(this.route.queryParams); //.subscribe(params=>params.name) - + //this.name= params.name; } ngAfterContentInit(): void { - const margin = { top: 10, right: 30, bottom: 30, left: 40 }; - const width = 100; //parent.innerWidth - margin.left - margin.right; - //var width= window.innerWidth; - const height = 100; - const rad = 20 // node radius - const nodeborder = 8; // node border thickness - const testweite = parent.innerWidth; - const svg = d3.select("#graph") // size definitions for the svg + + console.log(this.svgContainer); + + + this.svg = d3.select("#graph") // size definitions for the svg .append("svg") - .attr("width", width + '%') - .attr("height", height+'%') + .attr("width", this.width + '%') + .attr("height", this.height + '%') .append("g") .attr("transform", - "translate(" + margin.left + "," + margin.top + ")"); + "translate(" + this.margin.left + "," + this.margin.top + ")"); - const colors = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours + this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - svg.append('defs').append('marker') + this.svg.append('defs').append('marker') .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', rad + nodeborder) // arrow position and dimensions + .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -77,210 +96,348 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit {na .attr('fill', '#999') .style('stroke', 'none'); -this.route.params.subscribe(p=>{ - this.moduleName=p['moduleName']; -}) -var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }) + this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + + + //############################################################################################################## + + this.simulation = d3.forceSimulation(this.data.nodes); + // simulation.force('link').links(data.links); // simulation + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links) + ) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - const link = svg // link definitions - .selectAll(".links") - .data(data.links) - .enter() - .append("line") - .style("stroke", "#aaa") + this.refresh(); + } + + /** + * Draws the graph, executed first on AfterContentInit and on every doubleclick on a node + */ + refresh() { + console.log("refresh starting..") + console.log(this.data.nodes) + + + /* this.simulation.force("link").links(this.data.links); + this.simulation.force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links) + + + )*/ +//this.link.exit().remove(); +//this.node.exit().remove(); + + + this.link = this.svg // link definitions + .selectAll(".link") + .data(this.data.links, function (d){return d.target.id}) + /*.join(enter => enter.append("line") + .append("line").style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)'), // arrow on end of the link + exit => exit.remove())*/ + + var linkEnter=this.link.enter() + .append("line").style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; - link.append("title") - .text(function (d) { return d.type }); // get link title from data - const node = svg // node definitions - .selectAll("circle") - .data(data.nodes) - .enter() - .append("circle") - .attr("r",function(d){ - if(d.group==1){return rad+8} - else{return rad} + linkEnter.append("title") + .text(function (d){return d.type}) + this.link = this.link.merge(linkEnter); + this.link.exit().remove(); + + this.node = this.svg.selectAll(".node") + .data(this.data.nodes, function (d){return d.id}) + .join(enter => enter.append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8 } + else { return this.rad } + }) + .on('mouseover', this.mouseover) + .on('mouseout', this.mouseout) + .style("fill", function (d) { + if (d.group == 1) { return "black" } + else { return "whitesmoke" } + }) + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }) + .on('dblclick', this.nodeDoubleClick) + .call(d3.drag() + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)), + update => update.style("fill", "red"), + exit => exit.remove()) + + + /*var nodeEnter= this.node.enter().append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8 } + else { return this.rad } }) - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .style("fill", function(d){ - if(d.group==1){return "black"} - else{return "whitesmoke"} + /*nodeEnter.append("title") + .text(function(d){return d.id}) + nodeEnter.append("text") + .text(function (d){return d.name})*/ + /* nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseout', this.mouseout); + nodeEnter.style("fill", function (d) { + if (d.group == 1) { return "black" } + else { return "whitesmoke" } }) - .style("stroke-width", nodeborder) - .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group /*.append ("text") .attr ("dx", 12) .attr ("dy", ".35em") .text(function(d){return d.name})*/ - .call(d3.drag() - .on("start", dragstarted) - .on("drag", dragged)); - ; - - const text = svg // node text definitions - .selectAll("text") - .data(data.nodes) - .enter() - .append("text") - .attr("font-weight", function(d){ - if(d.group==1){return "bold"} - else{return "normal"} - }) - .text(function (d) { return d.name }); // get text from data +/* + nodeEnter.on('dblclick', this.nodeDoubleClick); + nodeEnter.call(d3.drag() + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)); + this.node=this.node.merge(nodeEnter); + this.node.exit().remove(); + + console.log(this.node) + */ + + this.text = this.svg.selectAll("text").data(this.data.nodes) + /*.join(enter=> enter.append("text") + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold" } else { return "normal" }}) + .text(function (d) { console.log(d.name);return d.name }), + exit=> exit.remove() )*/ - const linkText = svg // link text definitions - .selectAll("links") - .data(data.links) - .enter() - .append("text") + + var textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name}) + + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold" } + else { return "normal" } + }) + .text(function (d) { console.log(d.name); + return d.name }); // get text from data + this.text=this.text.merge(textEnter); +//this.text.exit().remove(); + + this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) + .join(enter=> enter.append("text") + .style("fill", "#999") + .attr("font-style", "italic") + .text(function (d) { return d.type }), + exit=> exit.remove()) + + + + + /* var linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data + this.linkText=this.linkText.merge(linkTextEnter); +//this.linkText.exit().remove(); +*/ + + this.simulation.nodes(this.data.nodes); + this.simulation.force("link").links(this.data.links); + //########################################################################################################### + } +/** + * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element + * @param d The position of every graph element (i.e. node, link...) + */ + ticked = () => { + const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg + const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; + + + this.text + .attr("dx", (d) => { // restrictions for node text positions in the svg + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%' + }) + .attr("dy", (d) => { + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%' + }) + this.linkText + .attr("dx", (d) => { // restrictions for link text positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; + }) + .attr("dy", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; + }) - + this.link + .attr("x1", (d) => { // restrictions for link positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX + '%'; + }) + .attr("y1", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY + '%'; + }) + .attr("x2", (d) => { + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX + '%'; + }) + .attr("y2", (d) => { + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY + '%'; + }) + this.node + .attr("cx", (d) => { + d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX + '%'; + }) + .attr("cy", (d) => { + d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY + '%'; + }) + } - const ticked = () => { - const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; // center definitions of the svg - const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - - text - .attr("dx", (d)=>{ // restrictions for node text positions in the svg - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) - .attr("dy", (d) => { - const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) - - linkText - .attr("dx", (d)=>{ // restrictions for link text positions in the svg - d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(width- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceX-relativeTargetX)/2)+ relativeTargetX))) +'%'; - }) - .attr("dy", (d)=>{ - d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; - }) - - link - .attr("x1", (d)=> { // restrictions for link positions in the svg - d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // converting radius from absolute to relative - const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX+'%';}) - .attr("y1", (d)=> { - d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY+'%';}) - .attr("x2", (d)=> { - d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX+'%';}) - .attr("y2", (d)=> { - d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY+'%';}) - - node - .attr("cx", (d) => { // restrictions for node positions in the svg - d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX+'%'; - }) - .attr("cy", (d) => { - d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) - const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY+'%';}) - } - const simulation = d3.forceSimulation(data.nodes) // simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(300) - .links(data.links) - ) - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes - + dragstarted = (d) => { + if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } - - - .on("tick", ticked); // tick on every step of the simulation - function dragstarted(d) { - if (!d3.event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - /*function doubleclick(d){ - - const node= {x: 500, y:50, "id":1000, - "name": "neighbor", - "group": 7} - data.nodes.push(node) - data.links.push({ - "source": d.id, - "target":1000 - }) - console.log(d); - } -*/ - function dragged(d) { - // console.log(d3.event) - 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; - } + /** + * Drag function, executed on every drag movement + * @param d The dragged node + */ + dragged(d) { + // console.log(d3.event) + d.fx = d3.event.x; + d.fy = d3.event.y; + } - function mouseover(d){ // function on mousover: increase radius - if(d.group==1){ - d3.select(this).transition() + dragended(d) { + //if (!d3.event.active) this.simulation.alphaTarget(0); + //d.fx = null; + // d.fy = null; + } +/** + * Mouseover function, executed when mouse over node. The radius of the node will increase. + * @param d node under the cursor + */ + mouseover(d) { + if (d.group == 1) { + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 12) + } + else { + d3.select(this).transition() .duration('2') - .attr('r', rad+12)} - else{ + .attr('r', 20 + 6) + } + } +/** + * Mousout function, executed when mouse cursor leaves the node + * @param d node wich is left by the cursor after mouseover + */ + mouseout(d) { // function on mousout: decrease this.radius to orgin + if (d.group == 1) { d3.select(this).transition() - .duration('2') - .attr('r', rad+6)} + .attr('r', 20 + 8) } - function mouseout(d){ // function on mousout: decrease radius to orgin - if(d.group==1){ - d3.select(this).transition() - .attr('r', rad+8) - } - else{ d3.select(this).transition() - .attr('r', rad)} + else { + d3.select(this).transition() + .attr('r', 20) } - + } +/** + * Doubleclick function, executed on every doubleclick on a node + *@param d The clicked node + */ + + nodeDoubleClick=(d)=> { +//this.simulation.stop(); + this.index++; + + const newNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(newNode); + this.data.links.push({ "source": d.id, "target": newNode, "type": "testclick" }) + /* + this.link.exit().remove(); + this.linkText.exit().remove(); + this.text.exit().remove(); */ + /*this.simulation.force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + + // ) + // .force("collision", d3.forceCollide(40)) + // .on("tick", this.ticked) // tick on every ste + -400)) // node magnetism /attraction + //.force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) + //simulation.restart(); //node oben links + fehlermeldungen + // this.simulation.nodes(this.data.nodes) + // this.simulation.force('link').links(this.data.links) + //simulation.force('nodes').nodes(data.nodes) + //node.data(data.nodes); + //link.data(data.links); + //node.enter().append("circle"); + //this.node.exit().remove(); + //link.exit().remove(); + //linkText.exit().remove(); + //text.exit().remove(); + // this.node = this.node.data(this.data.nodes); + //link= link.data(data.links) + //node.selectAll('circle').remove(); + //this.simulation.restart(); */ + this.refresh(); + // this.simulation.restart(); + //console.log(this.data.nodes) } - - - - - -} +} \ No newline at end of file From 84327c9b3dedfab05a0b0a069e9e81f74368a0b6 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 2 Jun 2020 18:50:48 +0200 Subject: [PATCH 15/74] Modified some code --- .../node-creator.service.ts | 1 + .../graph-visualization.component.ts | 271 +++++------------- 2 files changed, 79 insertions(+), 193 deletions(-) diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.ts index 89c940ec..822850e7 100644 --- a/frontend/app/own-modules/graph-visualization/node-creator.service.ts +++ b/frontend/app/own-modules/graph-visualization/node-creator.service.ts @@ -10,6 +10,7 @@ export class NodeCreatorService { constructor( private moduleService: ModuleService ) {} +/** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ getAllNodes(moduleName:String) { var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 0cd91854..7a368116 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -4,12 +4,6 @@ import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; - - - - - - @Component({ selector: 'graph-visualization', encapsulation: ViewEncapsulation.None, @@ -33,6 +27,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { /**Simulation of the force directed graph */ simulation: any; + margin = { top: 10, right: 30, bottom: 30, left: 40 }; width = 100; height = 100; @@ -51,24 +46,42 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { @ViewChild('g') svgContainer: ElementRef; moduleName: string; - ngOnInit(): void { - + ngOnInit(): void {} - // console.log("Der Name ist :"+this.moduleName); - //console.log(this.route.queryParams); - - //.subscribe(params=>params.name) + ngAfterContentInit(): void { + + + + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }) + this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + + this.setSvg(); + this.setSimulation(); + } - //this.name= params.name; +/**Defines settings of the simulation */ +setSimulation(){ + this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - } + this.refreshSimulation(); +} - ngAfterContentInit(): void { - console.log(this.svgContainer); - +/**Defines settings of the SVG and the color-scheme of the nodes */ + setSvg(){ this.svg = d3.select("#graph") // size definitions for the svg .append("svg") @@ -82,7 +95,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - this.svg.append('defs').append('marker') + this.svg.append('defs').append('marker') // marker/ arrow settings .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions @@ -94,139 +107,49 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .append('svg:path') .attr('d', 'M 0,-5 L 10 ,0 L 0,5') .attr('fill', '#999') - .style('stroke', 'none'); - - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }) - this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service - + .style('stroke', 'none');} - //############################################################################################################## - this.simulation = d3.forceSimulation(this.data.nodes); - // simulation.force('link').links(data.links); // simulation - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links) - - ) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - - this.refresh(); - } /** - * Draws the graph, executed first on AfterContentInit and on every doubleclick on a node + * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ - refresh() { - console.log("refresh starting..") - console.log(this.data.nodes) - - - /* this.simulation.force("link").links(this.data.links); - this.simulation.force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links) - - - )*/ -//this.link.exit().remove(); -//this.node.exit().remove(); - - - this.link = this.svg // link definitions - .selectAll(".link") - .data(this.data.links, function (d){return d.target.id}) - /*.join(enter => enter.append("line") - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)'), // arrow on end of the link - exit => exit.remove())*/ - - var linkEnter=this.link.enter() + refreshSimulation() { + this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id}) + var linkEnter=this.link.enter() //enter-selection .append("line").style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; linkEnter.append("title") .text(function (d){return d.type}) - this.link = this.link.merge(linkEnter); - this.link.exit().remove(); + this.link = this.link.merge(linkEnter); // merge old elements with entered elements + this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - this.node = this.svg.selectAll(".node") - .data(this.data.nodes, function (d){return d.id}) - .join(enter => enter.append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8 } - else { return this.rad } - }) - .on('mouseover', this.mouseover) - .on('mouseout', this.mouseout) - .style("fill", function (d) { - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - }) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }) - .on('dblclick', this.nodeDoubleClick) - .call(d3.drag() - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)), - update => update.style("fill", "red"), - exit => exit.remove()) - - - /*var nodeEnter= this.node.enter().append("circle") + this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id}) + + var nodeEnter= this.node.enter().append("circle") .attr("r", (d) => { if (d.group == 1) { return this.rad + 8 } else { return this.rad } }) - /*nodeEnter.append("title") - .text(function(d){return d.id}) - nodeEnter.append("text") - .text(function (d){return d.name})*/ - /* nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseover', this.mouseover); nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", function (d) { - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - }) + nodeEnter.style("fill", this.setNodeStyle) .style("stroke-width", this.nodeborder) .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group - /*.append ("text") - .attr ("dx", 12) - .attr ("dy", ".35em") - .text(function(d){return d.name})*/ -/* nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() + nodeEnter.call(d3.drag() // reactions when dragging a node .on("start", this.dragstarted) .on("drag", this.dragged) .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); - this.node.exit().remove(); + this.node=this.node.merge(nodeEnter); // merge old elements with entered elements + this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + - console.log(this.node) - */ - this.text = this.svg.selectAll("text").data(this.data.nodes) - /*.join(enter=> enter.append("text") - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold" } else { return "normal" }}) - .text(function (d) { console.log(d.name);return d.name }), - exit=> exit.remove() )*/ - - - var textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name}) + var textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name}) .attr("font-weight", function (d) { if (d.group == 1) { return "bold" } @@ -235,30 +158,20 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .text(function (d) { console.log(d.name); return d.name }); // get text from data this.text=this.text.merge(textEnter); -//this.text.exit().remove(); + this.text.exit().remove(); this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) - .join(enter=> enter.append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type }), - exit=> exit.remove()) - - - - - /* var linkTextEnter=this.linkText.enter().append("text") + var linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data this.linkText=this.linkText.merge(linkTextEnter); -//this.linkText.exit().remove(); -*/ + this.linkText.exit().remove(); + - this.simulation.nodes(this.data.nodes); + this.simulation.nodes(this.data.nodes); // simualtion uses current data this.simulation.force("link").links(this.data.links); - - //########################################################################################################### + } /** * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element @@ -267,8 +180,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { ticked = () => { const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - + this.text .attr("dx", (d) => { // restrictions for node text positions in the svg const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; @@ -340,36 +252,33 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { }) } - +/** + * Function executed on a drag start + */ dragstarted = (d) => { if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } - - /** * Drag function, executed on every drag movement * @param d The dragged node */ dragged(d) { - // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } dragended(d) { - //if (!d3.event.active) this.simulation.alphaTarget(0); - //d.fx = null; - // d.fy = null; + } /** * Mouseover function, executed when mouse over node. The radius of the node will increase. * @param d node under the cursor */ mouseover(d) { - if (d.group == 1) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node d3.select(this).transition() .duration('2') .attr('r', 20 + 12) @@ -381,11 +290,11 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { } } /** - * Mousout function, executed when mouse cursor leaves the node + * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. * @param d node wich is left by the cursor after mouseover */ - mouseout(d) { // function on mousout: decrease this.radius to orgin - if (d.group == 1) { + mouseout(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node d3.select(this).transition() .attr('r', 20 + 8) } @@ -398,46 +307,22 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { * Doubleclick function, executed on every doubleclick on a node *@param d The clicked node */ - nodeDoubleClick=(d)=> { -//this.simulation.stop(); this.index++; - - const newNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(newNode); - this.data.links.push({ "source": d.id, "target": newNode, "type": "testclick" }) - /* - this.link.exit().remove(); - this.linkText.exit().remove(); - this.text.exit().remove(); */ - /*this.simulation.force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links)) - - // ) - // .force("collision", d3.forceCollide(40)) - // .on("tick", this.ticked) // tick on every ste - -400)) // node magnetism /attraction - //.force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) - //simulation.restart(); //node oben links + fehlermeldungen - // this.simulation.nodes(this.data.nodes) - // this.simulation.force('link').links(this.data.links) - //simulation.force('nodes').nodes(data.nodes) - //node.data(data.nodes); - //link.data(data.links); - //node.enter().append("circle"); - //this.node.exit().remove(); - //link.exit().remove(); - //linkText.exit().remove(); - //text.exit().remove(); - // this.node = this.node.data(this.data.nodes); - //link= link.data(data.links) - //node.selectAll('circle').remove(); - //this.simulation.restart(); */ - this.refresh(); - // this.simulation.restart(); - //console.log(this.data.nodes) + const testNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(testNode); + this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }) + this.refreshSimulation(); + } + + /** Returns the color of the inner circle of a node */ + setNodeStyle(d){ + if (d.group == 1) { return "black" } + else { return "whitesmoke" } + } +/** Returns the node border color for each node-group */ + setNodeBorderStyle=(d)=>{ + return this.color(d.group); } } \ No newline at end of file From 93d9c3689950e2cf026bbf9f4058b1ec1f7d27ff Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 14 May 2021 15:43:20 +0200 Subject: [PATCH 16/74] First step to fixing the graph visualization --- .../capabilities/capability-mappings.ts | 28 +- .../routes/capabilities/capability.service.ts | 3 + .../production-modules/module.service.ts | 1 - frontend/app/layout/layout-routing.module.ts | 28 -- .../node-creator.service.spec.ts | 16 - .../node-creator.service.ts | 134 ----- .../manufacturing-service-executor.service.ts | 44 -- frontend/package-lock.json | 474 ++++++++++++++++-- .../components/sidebar/sidebar.component.html | 136 ++--- frontend/src/layout/layout-routing.module.ts | 1 + .../graph-visualization/D3GraphData.ts | 29 ++ .../graph-visualization.component.html | 0 .../graph-visualization.component.scss | 0 .../graph-visualization.component.ts | 336 +++++++++++++ .../graph-visualization.module.ts | 20 +- .../graph-visualization.routing.ts | 16 +- .../node-creator.service.spec.ts | 16 + .../node-creator.service.ts | 89 ++++ .../graph-visualization.component.ts | 328 ------------ shared/models/capability/Capability.ts | 8 +- 20 files changed, 1025 insertions(+), 682 deletions(-) delete mode 100644 frontend/app/layout/layout-routing.module.ts delete mode 100644 frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts delete mode 100644 frontend/app/own-modules/graph-visualization/node-creator.service.ts delete mode 100644 frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts create mode 100644 frontend/src/modules/graph-visualization/D3GraphData.ts rename frontend/src/{own-modules => modules}/graph-visualization/graph-visualization.component.html (100%) rename frontend/{app/own-modules => src/modules}/graph-visualization/graph-visualization.component.scss (100%) create mode 100644 frontend/src/modules/graph-visualization/graph-visualization.component.ts rename frontend/src/{own-modules => modules}/graph-visualization/graph-visualization.module.ts (55%) rename frontend/src/{own-modules => modules}/graph-visualization/graph-visualization.routing.ts (53%) create mode 100644 frontend/src/modules/graph-visualization/node-creator.service.spec.ts create mode 100644 frontend/src/modules/graph-visualization/node-creator.service.ts delete mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.component.ts diff --git a/backend/src/routes/capabilities/capability-mappings.ts b/backend/src/routes/capabilities/capability-mappings.ts index f7bf788a..48c9cddc 100644 --- a/backend/src/routes/capabilities/capability-mappings.ts +++ b/backend/src/routes/capabilities/capability-mappings.ts @@ -10,20 +10,30 @@ const capabilityMapping: MappingDefinition[] = [ rootName: 'inputs', propertyToGroup: 'input', name: 'inputs', - childMappings: [ - { - rootName: 'outputs', - propertyToGroup: 'output', - name: 'outputs', - } - ] }, + { + rootName: 'outputs', + propertyToGroup: 'output', + name: 'outputs', + } ] }, - - ]; +// { +// rootName: 'skillParameters', +// propertyToGroup: 'parameterIri', +// name: 'parameterIri', +// toCollect: ['parameterIri', 'parameterName', 'parameterType', 'parameterRequired', 'parameterDefault'], +// childMappings: [ +// { +// rootName: 'parameterOptionValues', +// propertyToGroup: 'paramOptionValue', +// name: 'value', +// } +// ] +// }, + export { capabilityMapping, }; diff --git a/backend/src/routes/capabilities/capability.service.ts b/backend/src/routes/capabilities/capability.service.ts index 17a2f970..849821c3 100644 --- a/backend/src/routes/capabilities/capability.service.ts +++ b/backend/src/routes/capabilities/capability.service.ts @@ -147,6 +147,9 @@ export class CapabilityService { } }`); const capabilities = converter.convertToDefinition(queryResult.results.bindings, capabilityMapping).getFirstRootElement() as CapabilityDto[]; + console.log("backend caps"); + console.log(capabilities); + return capabilities; } catch (error) { console.error(`Error while returning capabilities of skill ${skillIri}, ${error}`); diff --git a/backend/src/routes/production-modules/module.service.ts b/backend/src/routes/production-modules/module.service.ts index 9ef74d03..14724a79 100644 --- a/backend/src/routes/production-modules/module.service.ts +++ b/backend/src/routes/production-modules/module.service.ts @@ -49,7 +49,6 @@ export class ModuleService { const moduleSkillDtos = await this.skillService.getSkillsOfModule(moduleDto.iri); moduleDto.skillDtos = moduleSkillDtos; } - return productionModuleDtos; } diff --git a/frontend/app/layout/layout-routing.module.ts b/frontend/app/layout/layout-routing.module.ts deleted file mode 100644 index e7acd6b0..00000000 --- a/frontend/app/layout/layout-routing.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; -import { LayoutComponent } from './layout.component'; - -const routes: Routes = [ - { - path: '', - component: LayoutComponent, - children: [ - { path: '', redirectTo: 'dashboard', pathMatch: 'prefix' }, - { path: 'module-management', loadChildren: '../own-modules/module-management/module-management.module#ModuleManagementModule'}, - { path: 'order-management', loadChildren: '../own-modules/order-management/order-management.module#OrderManagementModule'}, - { path: 'kpi-dashboard', loadChildren: '../own-modules/kpi-dashboard/kpi-dashboard.module#KpiDashboardModule'}, - { path: 'reconfiguration-overview', loadChildren: '../own-modules/reconfiguration-overview/reconfiguration-overview.module#ReconfigurationOverviewModule'}, - { path: 'ops-configuration', loadChildren: '../own-modules/ops-configuration/ops-configuration.module#OpsConfigurationModule'}, - { path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardModule' }, - { path: 'charts', loadChildren: './charts/charts.module#ChartsModule' }, - {path:'graph-visualization', loadChildren: '../own-modules/graph-visualization/graph-visualization.module#GraphVisualizationModule'}, - { path: 'blank-page', loadChildren: './blank-page/blank-page.module#BlankPageModule' } - ] - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class LayoutRoutingModule {} diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts deleted file mode 100644 index c3ee0483..00000000 --- a/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* tslint:disable:no-unused-variable */ - -import { TestBed, async, inject } from '@angular/core/testing'; -import { NodeCreatorService } from './node-creator.service'; - -describe('Service: NodeCreator', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [NodeCreatorService] - }); - }); - - it('should ...', inject([NodeCreatorService], (service: NodeCreatorService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.ts deleted file mode 100644 index 822850e7..00000000 --- a/frontend/app/own-modules/graph-visualization/node-creator.service.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ModuleService } from 'app/shared/services/module.service'; -import { containsElement } from '@angular/animations/browser/src/render/shared'; - -@Injectable({ - providedIn: 'root' -}) -export class NodeCreatorService { - apiRoot: string = "/api"; -constructor( - private moduleService: ModuleService -) {} -/** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ -getAllNodes(moduleName:String) { - - var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - const data= {nodes:[], links:[]}; - let idMap= new Map(); // Map for saving Node-IDs - var id=0; // ID start value - receivedData.forEach(receivedModule => { //loop over all modules - if(receivedModule.name==moduleName|| moduleName== undefined){ // creates nodes for chosen modules - id++; - idMap.set(receivedModule, id); // assigns an ID for each module-node - data.nodes.push({ // adds a node for each module - "id" : idMap.get(receivedModule), - "name": receivedModule.name, - "group": 1 // assings node-groups (here: node colour) - }); - - receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module - id++; - idMap.set(capability, id); // assigns an ID for each capability-node - data.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.name, - "group": 2 - }) - data.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description - }) - - capability.hasInput.forEach(input=>{ - id++; - idMap.set(input, id); // assigns an ID for each input-node - data.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.name, - "group": 3 - }) - data.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }) - }) - - capability.hasOutput.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - data.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.name, - "group": 4 - }) - data.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }) - }) - - capability.executableViaSkill.forEach(skill=>{ - id++; - idMap.set(skill, id) // adds a node for each skill of current capability - data.nodes.push({ - "id" : idMap.get(skill), - "name": skill.name, - "group": 5 - }) - data.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" - }) - }) - - }) - } - - }); - if(id==0){ //if module does not exist, show error-node - id++; - data.nodes.push({ - "id" : id, - "name": "<<>>", - "group": 1 - }) - } - -return data; -} - - -} - - - - - - diff --git a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts deleted file mode 100644 index 338e7fa0..00000000 --- a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Injectable } from "@angular/core"; -import { HttpClient, HttpRequest, HttpHeaders, HttpParams } from "@angular/common/http"; -import { Observable } from "rxjs"; -//import { ServiceExecutionDescription } from "./self-description"; -import { map } from 'rxjs/operators'; -import { ServiceExecutionDescription } from "app/shared/models/self-description"; - - -@Injectable() - -export class ManufacturingServiceExecutor { - - constructor(private http: HttpClient) {} - - executeService(executionDescription:ServiceExecutionDescription) { - console.log(`service called`); - console.log(executionDescription); - - // // construct the request - // let headers = new HttpHeaders(); - // executionDescription.parameters.forEach(parameter => { - // if (parameter.getShortType() == "HeaderParameter") { - // headers.set(parameter.name, parameter.value); - // } - // }); - - // let queryParams = new HttpParams(); - // executionDescription.parameters.forEach(parameter => { - // if (parameter.getShortType() == "QueryParameter") { - // queryParams.set(parameter.name, parameter.value); - // } - // }); - - // let request = new HttpRequest(executionDescription.methodType, executionDescription.fullPath, { - // "headers": headers, - // "params": queryParams - // }) - - // Send - this.http.post(`api/service-executions`, executionDescription).subscribe(res => {console.log(JSON.stringify(res))}); - } - - -} \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 54d1f4a3..59baeec9 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -19295,10 +19295,96 @@ } } }, - "@angular/platform-browser-dynamic": { - "version": "12.0.2", - "resolved": "https://registry.npmjs.org/@angular/platform-browser-dynamic/-/platform-browser-dynamic-12.0.2.tgz", - "integrity": "sha512-FVJBIxn85EX3a6kSdqwXAC8i7jxeo5iwHBKUEFYukbAXrHuMmwFz7Nvqkqqo0xIJXymFz8Q04VbTThkgp2uerg==", + "@types/color-name": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@types/color-name/-/color-name-1.1.1.tgz", + "integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==" + }, + "@types/eslint-visitor-keys": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz", + "integrity": "sha512-OCutwjDZ4aFS6PB1UZ988C4YgwlBHJd6wCeQqaLdmadZ/7e+w79+hbMUFC1QXDNCmdyoRfAFdm0RypzwR+Qpag==", + "dev": true + }, + "@types/events": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/events/-/events-3.0.0.tgz", + "integrity": "sha512-EaObqwIvayI5a8dCzhFrjKzVwKLxjoG9T6Ppd5CEo07LRKfQ8Yokw54r5+Wq7FaBQ+yXRvQAYPrHwya1/UFt9g==", + "dev": true + }, + "@types/glob": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", + "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "dev": true, + "requires": { + "@types/events": "*", + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "@types/jasmine": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/@types/jasmine/-/jasmine-2.8.9.tgz", + "integrity": "sha512-8dPZwjosElZOGGYw1nwTvOEMof4gjwAWNFS93nBI091BoEfd5drnHOLRMiRF/LOPuMTn5LgEdv0bTUO8QFVuHQ==", + "dev": true + }, + "@types/jasminewd2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@types/jasminewd2/-/jasminewd2-2.0.5.tgz", + "integrity": "sha512-1awkm/O4pQCR9hI2F80HmIOda/L+ogkSL8Arj1k00eue5VLY5ooewhSOyF/cUJE0S+/34uD5EYY3zmd6fu2OCA==", + "dev": true, + "requires": { + "@types/jasmine": "*" + } + }, + "@types/json-schema": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.5.tgz", + "integrity": "sha512-7+2BITlgjgDhH0vvwZU/HZJVyk+2XUlvxXe8dFMedNX/aMkaOq++rMAFXc0tM7ij15QaWlbdQASBR9dihi+bDQ==", + "dev": true + }, + "@types/json5": { + "version": "0.0.29", + "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", + "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", + "dev": true + }, + "@types/minimatch": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", + "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", + "dev": true + }, + "@types/node": { + "version": "10.12.2", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.2.tgz", + "integrity": "sha512-53ElVDSnZeFUUFIYzI8WLQ25IhWzb6vbddNp8UHlXQyU0ET2RhV5zg0NfubzU7iNMh5bBXb0htCzfvrSVNgzaQ==", + "dev": true + }, + "@types/q": { + "version": "0.0.32", + "resolved": "https://registry.npmjs.org/@types/q/-/q-0.0.32.tgz", + "integrity": "sha1-vShOV8hPEyXacCur/IKlMoGQwMU=", + "dev": true + }, + "@types/selenium-webdriver": { + "version": "3.0.17", + "resolved": "https://registry.npmjs.org/@types/selenium-webdriver/-/selenium-webdriver-3.0.17.tgz", + "integrity": "sha512-tGomyEuzSC1H28y2zlW6XPCaDaXFaD6soTdb4GNdmte2qfHtrKqhy0ZFs4r/1hpazCfEZqeTSRLvSasmEx89uw==", + "dev": true + }, + "@types/source-list-map": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/@types/source-list-map/-/source-list-map-0.1.2.tgz", + "integrity": "sha512-K5K+yml8LTo9bWJI/rECfIPrGgxdpeNbj+d53lwN4QjW1MCwlkhUms+gtdzigTeUyBr09+u8BwOIY3MXvHdcsA==", + "dev": true + }, + "@types/webpack-sources": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/@types/webpack-sources/-/webpack-sources-0.1.7.tgz", + "integrity": "sha512-XyaHrJILjK1VHVC4aVlKsdNN5KBTwufMb43cQs+flGxtPAf/1Qwl8+Q0tp5BwEGaI8D6XT1L+9bSWXckgkjTLw==", + "dev": true, "requires": { "tslib": "^2.1.0" }, @@ -19685,10 +19771,19 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz", "integrity": "sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==" }, - "@babel/helper-validator-option": { - "version": "7.12.17", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.12.17.tgz", - "integrity": "sha512-TopkMDmLzq8ngChwRlyjR6raKD6gMSae4JdYDB8bByKreQgG0RBTuKe9LRxW3wFtUnjxOPRKBDwEH6Mg5KeDfw==", + "angular-resize-event": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/angular-resize-event/-/angular-resize-event-1.2.2.tgz", + "integrity": "sha512-fILJkLvPViNt8HVQ8nZzpaZWKFcTGFuNhjTSVzukthA8bJuX5irMgSWzO3Y7z5JSRPOEvgROU9YfDimMoZPa0Q==", + "requires": { + "css-element-queries": "1.2.3", + "tslib": "^1.9.0" + } + }, + "ansi-colors": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-3.2.4.tgz", + "integrity": "sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==", "dev": true }, "@babel/helper-wrap-function": { @@ -21112,10 +21207,47 @@ "@xtuc/ieee754": "^1.2.0" } }, - "@webassemblyjs/leb128": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@webassemblyjs/leb128/-/leb128-1.11.0.tgz", - "integrity": "sha512-aqbsHa1mSQAbeeNcl38un6qVY++hh8OpCOzxhixSYgbRfNWcxJNJQwe2rezK9XEcssJbbWIkblaJRwGMS9zp+g==", + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "commondir": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", + "integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", + "dev": true + }, + "compare-versions": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-3.6.0.tgz", + "integrity": "sha512-W6Af2Iw1z4CB7q4uU4hv646dW9GQuBM+YpC0UvUCWSD8w90SJjp+ujJuXaEMtAXBtSqGfMPuFOVn4/+FlaqfBA==", + "dev": true + }, + "component-bind": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/component-bind/-/component-bind-1.0.0.tgz", + "integrity": "sha1-AMYIq33Nk4l8AAllGx06jh5zu9E=" + }, + "component-emitter": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.2.1.tgz", + "integrity": "sha1-E3kY1teCg/ffemt8WmPhQOaUJeY=" + }, + "component-event": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/component-event/-/component-event-0.1.4.tgz", + "integrity": "sha1-PeePwoeCOBeH4kvyp8U2vwFCybQ=" + }, + "component-inherit": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/component-inherit/-/component-inherit-0.0.3.tgz", + "integrity": "sha1-ZF/ErfWLcrZJ1crmUTVhnbJv8UM=" + }, + "compressible": { + "version": "2.0.18", + "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", + "integrity": "sha512-AF3r7P5dWxL8MxyITRMlORQNaOA2IkAFaTr4k7BUumjPtRpGDTZpl0Pb1XCO6JeDCBdp126Cgs9sMxqSjgYyRg==", "dev": true, "requires": { "@xtuc/long": "4.2.2" @@ -21664,10 +21796,15 @@ "ast-types-flow": "0.0.7" } }, - "babel-loader": { - "version": "8.2.2", - "resolved": "https://registry.npmjs.org/babel-loader/-/babel-loader-8.2.2.tgz", - "integrity": "sha512-JvTd0/D889PQBtUXJ2PXaKU/pjZDMtHA9V2ecm+eNRmmBCMR09a+fmpGTNwnJtFmFl5Ei7Vy47LjBb+L0wQ99g==", + "css-element-queries": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/css-element-queries/-/css-element-queries-1.2.3.tgz", + "integrity": "sha512-QK9uovYmKTsV2GXWQiMOByVNrLn2qz6m3P7vWpOR4IdD6I3iXoDw5qtgJEN3Xq7gIbdHVKvzHjdAtcl+4Arc4Q==" + }, + "css-loader": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-3.5.1.tgz", + "integrity": "sha512-0G4CbcZzQ9D1Q6ndOfjFuMDo8uLYMu5vc9Abs5ztyHcKvmil6GJrMiNjzzi3tQvUF+mVRuDg7bE6Oc0Prolgig==", "dev": true, "requires": { "find-cache-dir": "^3.3.1", @@ -21984,11 +22121,275 @@ "integrity": "sha1-aN/1++YMUes3cl6p4+0xDcwed24=", "dev": true }, - "bootstrap": { - "version": "5.0.2", - "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.0.2.tgz", - "integrity": "sha512-1Ge963tyEQWJJ+8qtXFU6wgmAVj9gweEjibUdbmcCEYsn38tVwRk8107rk2vzt6cfQcRr3SlZ8aQBqaD8aqf+Q==", - "requires": {} + "d3": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", + "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==", + "requires": { + "d3-array": "1", + "d3-axis": "1", + "d3-brush": "1", + "d3-chord": "1", + "d3-collection": "1", + "d3-color": "1", + "d3-contour": "1", + "d3-dispatch": "1", + "d3-drag": "1", + "d3-dsv": "1", + "d3-ease": "1", + "d3-fetch": "1", + "d3-force": "1", + "d3-format": "1", + "d3-geo": "1", + "d3-hierarchy": "1", + "d3-interpolate": "1", + "d3-path": "1", + "d3-polygon": "1", + "d3-quadtree": "1", + "d3-random": "1", + "d3-scale": "2", + "d3-scale-chromatic": "1", + "d3-selection": "1", + "d3-shape": "1", + "d3-time": "1", + "d3-time-format": "2", + "d3-timer": "1", + "d3-transition": "1", + "d3-voronoi": "1", + "d3-zoom": "1" + } + }, + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "d3-axis": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", + "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + }, + "d3-brush": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", + "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "d3-chord": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", + "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "requires": { + "d3-array": "1", + "d3-path": "1" + } + }, + "d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "d3-contour": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", + "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "requires": { + "d3-array": "^1.1.1" + } + }, + "d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "d3-drag": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "requires": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "d3-dsv": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", + "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", + "requires": { + "commander": "2", + "iconv-lite": "0.4", + "rw": "1" + } + }, + "d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + }, + "d3-fetch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", + "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", + "requires": { + "d3-dsv": "1" + } + }, + "d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "requires": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "d3-geo": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", + "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "requires": { + "d3-array": "1" + } + }, + "d3-hierarchy": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + }, + "d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "d3-polygon": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", + "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==" + }, + "d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + }, + "d3-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", + "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + }, + "d3-scale": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", + "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "requires": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "d3-scale-chromatic": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", + "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", + "requires": { + "d3-color": "1", + "d3-interpolate": "1" + } + }, + "d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + }, + "d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + }, + "d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "requires": { + "d3-time": "1" + } + }, + "d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "d3-transition": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "requires": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, + "d3-zoom": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", + "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "damerau-levenshtein": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.6.tgz", + "integrity": "sha512-JVrozIeElnj3QzfUIt8tB8YMluBJom4Vw9qTPpjGYQ9fYlB3D/rb6OordUxf3xeFB35LKWs0xqcO5U6ySvBtug==", + "dev": true }, "bpmn-js": { "version": "8.7.1", @@ -24262,10 +24663,24 @@ } } }, - "eslint-scope": { - "version": "5.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", - "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", + "find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=", + "dev": true, + "requires": { + "locate-path": "^2.0.0" + } + }, + "flag-icon-css": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/flag-icon-css/-/flag-icon-css-3.2.1.tgz", + "integrity": "sha512-0t7zPm2crM2cBIm3epZQ+EmiHuzgFNTTSMUMkWlrztDDGL+y31D+eY8zaB9zYCzJGAsn4KEMAKY+jCU1mt9jwg==" + }, + "flat-cache": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz", + "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==", "dev": true, "requires": { "esrecurse": "^4.3.0", @@ -25840,7 +26255,6 @@ "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", "dev": true, - "optional": true, "requires": { "is-accessor-descriptor": "^0.1.6", "is-data-descriptor": "^0.1.4", @@ -29200,7 +29614,6 @@ "resolved": "https://registry.npmjs.org/postcss-import/-/postcss-import-14.0.1.tgz", "integrity": "sha512-Xn2+z++vWObbEPhiiKO1a78JiyhqipyrXHBb3AHpv0ks7Cdg+GxQQJ24ODNMTanldf7197gSP3axppO9yaG0lA==", "dev": true, - "optional": true, "requires": { "postcss-value-parser": "^4.0.0", "read-cache": "^1.0.0", @@ -30520,7 +30933,6 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", "integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "dev": true, - "optional": true, "requires": { "load-json-file": "^2.0.0", "normalize-package-data": "^2.3.2", @@ -30532,7 +30944,6 @@ "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", "integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "dev": true, - "optional": true, "requires": { "pify": "^2.0.0" } @@ -30541,8 +30952,7 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", - "dev": true, - "optional": true + "dev": true } } }, @@ -30551,7 +30961,6 @@ "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", "integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "dev": true, - "optional": true, "requires": { "find-up": "^2.0.0", "read-pkg": "^2.0.0" @@ -33466,7 +33875,6 @@ "resolved": "https://registry.npmjs.org/worker-farm/-/worker-farm-1.7.0.tgz", "integrity": "sha512-rvw3QTZc8lAxyVrqcSGVm5yP/IJ2UcB3U0graE3LCFoZ0Yn2x4EoVSqJKdB/T5M+FLcRPjz4TDacRf3OCfNUzw==", "dev": true, - "optional": true, "requires": { "errno": "~0.1.7" } diff --git a/frontend/src/layout/components/sidebar/sidebar.component.html b/frontend/src/layout/components/sidebar/sidebar.component.html index b6d73e7d..7188435f 100644 --- a/frontend/src/layout/components/sidebar/sidebar.component.html +++ b/frontend/src/layout/components/sidebar/sidebar.component.html @@ -1,78 +1,80 @@ diff --git a/frontend/src/layout/layout-routing.module.ts b/frontend/src/layout/layout-routing.module.ts index c9029b18..4816a669 100644 --- a/frontend/src/layout/layout-routing.module.ts +++ b/frontend/src/layout/layout-routing.module.ts @@ -13,6 +13,7 @@ const routes: Routes = [ { path: 'skills', loadChildren: () => import('../modules/skills/skill-container.module').then(m => m.SkillContainerModule)}, { path: 'capabilities', loadChildren: () => import('../modules/capabilities/capability.module').then(m => m.CapabilityModule)}, { path: 'skill-processes', loadChildren: () => import('../modules/skill-processes/skill-processes.module').then(m => m.SkillProcessesModule)}, + { path:'graph-visualization', loadChildren: () => import('../modules/graph-visualization/graph-visualization.module').then(m =>m.GraphVisualizationModule)}, { path: 'kpi-dashboard', loadChildren: () => import('../modules/kpi-dashboard/kpi-dashboard.module').then(m => m.KpiDashboardModule)}, { path: 'reconfiguration-overview', loadChildren: () => import('../modules/reconfiguration-overview/reconfiguration-overview.module').then(m => m.ReconfigurationOverviewModule)}, { path: 'ops-configuration', loadChildren: () => import('../modules/ops-configuration/ops-configuration.module').then(m => m.OpsConfigurationModule)}, diff --git a/frontend/src/modules/graph-visualization/D3GraphData.ts b/frontend/src/modules/graph-visualization/D3GraphData.ts new file mode 100644 index 00000000..e1bfcd3e --- /dev/null +++ b/frontend/src/modules/graph-visualization/D3GraphData.ts @@ -0,0 +1,29 @@ + +export class D3GraphData { + // nodes: D3Node [] = []; + // links: D3Link[] = []; + constructor(private nodes: D3Node[] = [], private links: D3Link[] = []) { } + + addNode(node: D3Node): void { + this.nodes.push(node); + } + + addLink(link: D3Link): void { + this.links.push(link); + } +} + + +export class D3Node { + constructor( + private id: string, + private name: string, + private group: number) { } +} + +export class D3Link { + constructor( + private source: string, + private target: string, + private type: string) { } +} diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html similarity index 100% rename from frontend/src/own-modules/graph-visualization/graph-visualization.component.html rename to frontend/src/modules/graph-visualization/graph-visualization.component.html diff --git a/frontend/app/own-modules/graph-visualization/graph-visualization.component.scss b/frontend/src/modules/graph-visualization/graph-visualization.component.scss similarity index 100% rename from frontend/app/own-modules/graph-visualization/graph-visualization.component.scss rename to frontend/src/modules/graph-visualization/graph-visualization.component.scss diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts new file mode 100644 index 00000000..d93ed4e7 --- /dev/null +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -0,0 +1,336 @@ +import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import * as d3 from "d3"; +import { ActivatedRoute } from '@angular/router'; +import { NodeCreatorService } from './node-creator.service'; + + + @Component({ + selector: 'graph-visualization', + encapsulation: ViewEncapsulation.None, + templateUrl: './graph-visualization.component.html', + styleUrls: ['./graph-visualization.component.scss'] + }) +export class GraphVisualizationComponent implements AfterContentInit, OnInit { + name: string; + svg: any; + link: any; + /** The displayed node name*/ + text: any; + /** The displayed link description */ + linkText: any; + /** The displayed node */ + node: any; + /** Loaded data: Connected modules with their capabilities and skills*/ + data: any; + /**Returns different colors automatically. The node-border color is the same for all node-group members*/ + color: any; + /**Simulation of the force directed graph */ + simulation: any; + + + margin = { top: 10, right: 30, bottom: 30, left: 40 }; + width = 100; + height = 100; + /**Defines the nodeborder thickness*/ + nodeborder = 8; + /**Defines the standard radius of a node*/ + rad = 20; + + index=0; + + constructor( + private route: ActivatedRoute, + private nodeCreatorService: NodeCreatorService + ) { + } + + @ViewChild('g') svgContainer: ElementRef; + moduleName: string; + ngOnInit(): void {} + + + ngAfterContentInit(): void { + console.log("ng after content"); + + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }); + console.log("moduleName"); + console.log(this.moduleName); + + this.nodeCreatorService.getAllNodes(this.moduleName).subscribe(data => { + this.data = data; // load data created by node-creator.service + console.log("data"); + console.log(this.data); + + + this.setSvg(); + this.setSimulation(); + }); + + } + + /**Defines settings of the simulation */ + setSimulation(){ + this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)); //Zentrierung der Nodes + + this.refreshSimulation(); + } + + + + /**Defines settings of the SVG and the color-scheme of the nodes */ + setSvg(){ + + this.svg = d3.select("#graph") // size definitions for the svg + .append("svg") + .attr("width", this.width + '%') + .attr("height", this.height + '%') + .append("g") + .attr("transform", + "translate(" + this.margin.left + "," + this.margin.top + ")"); + + + + this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours + + this.svg.append('defs').append('marker') // marker/ arrow settings + .attr("id", 'arrowhead') + .attr('viewBox', '-0 -5 10 10') //coordinate system + .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions + .attr('refY', 0) + .attr('orient', 'auto') + .attr('markerWidth', 13) + .attr('markerHeight', 13) + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke', 'none');} + + + + /** + * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node + */ + refreshSimulation() { + this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id;}); + const linkEnter=this.link.enter() //enter-selection + .append("line").style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link + ; + linkEnter.append("title") + .text(function (d){return d.type;}); + this.link = this.link.merge(linkEnter); // merge old elements with entered elements + this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + + this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id;}); + + const nodeEnter= this.node.enter().append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8; } + else { return this.rad; } + }); + nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseout', this.mouseout); + nodeEnter.style("fill", this.setNodeStyle) + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }); // set different node colours for each node-group + nodeEnter.on('dblclick', this.nodeDoubleClick); + nodeEnter.call(d3.drag() // reactions when dragging a node + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)); + this.node=this.node.merge(nodeEnter); // merge old elements with entered elements + this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + + + this.text = this.svg.selectAll("text").data(this.data.nodes); + const textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name;}) + + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold"; } + else { return "normal"; } + }) + .text(function (d) { console.log(d.name); + return d.name; }); // get text from data + this.text=this.text.merge(textEnter); + this.text.exit().remove(); + + this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type;}); + const linkTextEnter=this.linkText.enter().append("text") + .style("fill", "#999") + .attr("font-style", "italic") + .text(function (d) { return d.type; }); // get link text from data + this.linkText=this.linkText.merge(linkTextEnter); + this.linkText.exit().remove(); + + + this.simulation.nodes(this.data.nodes); // simualtion uses current data + this.simulation.force("link").links(this.data.links); + + } + /** + * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element + * @param d The position of every graph element (i.e. node, link...) + */ + ticked = () => { + const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg + const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; + + this.text + .attr("dx", (d) => { // restrictions for node text positions in the svg + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%'; + }) + .attr("dy", (d) => { + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%'; + }); + + this.linkText + .attr("dx", (d) => { // restrictions for link text positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; + }) + .attr("dy", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; + }); + + this.link + .attr("x1", (d) => { // restrictions for link positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX + '%'; + }) + .attr("y1", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY + '%'; + }) + .attr("x2", (d) => { + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX + '%'; + }) + .attr("y2", (d) => { + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY + '%'; + }); + + this.node + .attr("cx", (d) => { + d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX + '%'; + }) + .attr("cy", (d) => { + d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY + '%'; + }); + } + + /** + * Function executed on a drag start + */ + dragstarted = (d) => { + if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + /** + * Drag function, executed on every drag movement + * @param d The dragged node + */ + dragged(d) { + d.fx = d3.event.x; + d.fy = d3.event.y; + } + + dragended(d) { + + } + /** + * Mouseover function, executed when mouse over node. The radius of the node will increase. + * @param d node under the cursor + */ + mouseover(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 12); + } + else { + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 6); + } + } + /** + * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. + * @param d node wich is left by the cursor after mouseover + */ + mouseout(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node + d3.select(this).transition() + .attr('r', 20 + 8); + } + else { + d3.select(this).transition() + .attr('r', 20); + } + } + /** + * Doubleclick function, executed on every doubleclick on a node + *@param d The clicked node + */ + nodeDoubleClick=(d)=> { + this.index++; + const testNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(testNode); + this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }); + this.refreshSimulation(); + } + + /** Returns the color of the inner circle of a node */ + setNodeStyle(d){ + if (d.group == 1) { return "black"; } + else { return "whitesmoke"; } + } + /** Returns the node border color for each node-group */ + setNodeBorderStyle=(d)=>{ + return this.color(d.group); + } + +} diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts similarity index 55% rename from frontend/src/own-modules/graph-visualization/graph-visualization.module.ts rename to frontend/src/modules/graph-visualization/graph-visualization.module.ts index 711024bc..17f69cd0 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,18 +1,18 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from 'app/shared/services/module.service'; +import { ModuleService } from '../../shared/services/module.service'; import { NodeCreatorService } from './node-creator.service'; @NgModule({ - imports: [ - GraphVisualizationRouter, - ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - NodeCreatorService - - ] + imports: [ + GraphVisualizationRouter, + ], + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + NodeCreatorService + + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts similarity index 53% rename from frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts rename to frontend/src/modules/graph-visualization/graph-visualization.routing.ts index ba430b6c..c9d65f24 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts @@ -3,18 +3,18 @@ import { NgModule } from '@angular/core'; import {GraphVisualizationComponent} from './graph-visualization.component'; const routes: Routes = [ - { path: 'modules/:moduleName', - component: GraphVisualizationComponent, - - }, - {path:'modules', component: GraphVisualizationComponent + { path: 'modules/:moduleName', + component: GraphVisualizationComponent, - } + }, + {path:'modules', component: GraphVisualizationComponent + + } ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] }) export class GraphVisualizationRouter {} diff --git a/frontend/src/modules/graph-visualization/node-creator.service.spec.ts b/frontend/src/modules/graph-visualization/node-creator.service.spec.ts new file mode 100644 index 00000000..2ba009ab --- /dev/null +++ b/frontend/src/modules/graph-visualization/node-creator.service.spec.ts @@ -0,0 +1,16 @@ +/* tslint:disable:no-unused-variable */ + +import { TestBed, async, inject } from '@angular/core/testing'; +import { NodeCreatorService } from './node-creator.service'; + +describe('Service: NodeCreator', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [NodeCreatorService] + }); + }); + + it('should ...', inject([NodeCreatorService], (service: NodeCreatorService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts new file mode 100644 index 00000000..9a6ec11f --- /dev/null +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -0,0 +1,89 @@ +import { Injectable } from '@angular/core'; +import { ProductionModule } from '@shared/models/production-module/ProductionModule'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ModuleService } from '../../shared/services/module.service'; +import { D3GraphData, D3Link, D3Node } from './D3GraphData'; +// import { containsElement } from '@angular/animations/browser/browser'; + + +@Injectable({ + providedIn: 'root' +}) +export class NodeCreatorService { + apiRoot = "/api"; + modules: ProductionModule[] + + constructor(private moduleService: ModuleService) { } + + /** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ + getAllNodes(moduleIri: string): Observable { + + return this.moduleService.getAllModules().pipe(map(modules => { + // If a module name is passed, filter the module list for that specific one + if(moduleIri != undefined) { + modules = modules.filter(module => module.iri == moduleIri); + } + return this.formatData(modules); + })); + } + + formatData(modules: ProductionModule[]): D3GraphData { + + const graphData = new D3GraphData(); + modules.forEach(module => { //loop over all modules + + graphData.addNode(new D3Node(module.iri, module.getLocalName(), 1)); // adds a node for each module + + module.skills.forEach(skill => { + graphData.addNode(new D3Node(skill.iri, skill.getLocalName(), 100)); + graphData.addLink(new D3Link(module.iri, skill.iri, "hasSkill")); + + // Add state machine + graphData.addNode(new D3Node(skill.stateMachine.iri, skill.stateMachine.getLocalName(), 120)); + graphData.addLink(new D3Link(skill.iri, skill.stateMachine.iri, "hasStateMachine")); + + // Add skill parameters + skill.skillParameters.forEach(parameter => { + graphData.addNode(new D3Node(parameter.iri, parameter.getLocalName(), 140)); + graphData.addLink(new D3Link(skill.iri, parameter.iri, "hasSkillParameter")); + }); + + // Add skill outputs + skill.skillOutputs.forEach(output => { + graphData.addNode(new D3Node(output.iri,output.getLocalName(), 160)); + graphData.addLink(new D3Link(skill.iri, output.iri, "hasSkillOutput")); + }); + }); + + // Add module's capabilities + module.capabilities.forEach(capability => { //loop over all capabilities of current module + graphData.addNode(new D3Node(capability.iri, capability.getLocalName(), 200)); + graphData.addLink(new D3Link(module.iri, capability.iri, "hasCapability")); // adds a link between capability and module + + capability.inputs.forEach(input => { + graphData.addNode(new D3Node(input.iri, input.getLocalName(), 240)); + graphData.addLink(new D3Link(capability.iri, input.iri, "hasInput")); + + }); + + capability.outputs.forEach(output => { + graphData.addNode(new D3Node(output.iri, output.getLocalName(), 270)); + graphData.addLink(new D3Link(capability.iri, output.iri, "hasOutput")); + }); + + }); + + }); + return graphData; + } + + +} + + + + + + + diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts deleted file mode 100644 index 7a368116..00000000 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ /dev/null @@ -1,328 +0,0 @@ -import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; -import * as d3 from "d3" -import { ActivatedRoute } from '@angular/router'; -import { NodeCreatorService } from './node-creator.service'; - - -@Component({ - selector: 'graph-visualization', - encapsulation: ViewEncapsulation.None, - templateUrl: './graph-visualization.component.html', - styleUrls: ['./graph-visualization.component.scss'] -}) -export class GraphVisualizationComponent implements AfterContentInit, OnInit { - name: String; - svg: any; - link: any; - /** The displayed node name*/ - text: any; - /** The displayed link description */ - linkText: any; - /** The displayed node */ - node: any; - /** Loaded data: Connected modules with their capabilities and skills*/ - data: any; - /**Returns different colors automatically. The node-border color is the same for all node-group members*/ - color: any; -/**Simulation of the force directed graph */ - simulation: any; - - - margin = { top: 10, right: 30, bottom: 30, left: 40 }; - width = 100; - height = 100; -/**Defines the nodeborder thickness*/ - nodeborder = 8; - /**Defines the standard radius of a node*/ - rad = 20; - - index=0; - - constructor( - private route: ActivatedRoute, - private nodeCreatorService: NodeCreatorService - ) { - }; - - @ViewChild('g') svgContainer: ElementRef; - moduleName: string; - ngOnInit(): void {} - - - ngAfterContentInit(): void { - - - - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }) - this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service - - this.setSvg(); - this.setSimulation(); - } - -/**Defines settings of the simulation */ -setSimulation(){ - this.simulation = d3.forceSimulation(this.data.nodes); - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links)) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - - this.refreshSimulation(); -} - - - -/**Defines settings of the SVG and the color-scheme of the nodes */ - setSvg(){ - - this.svg = d3.select("#graph") // size definitions for the svg - .append("svg") - .attr("width", this.width + '%') - .attr("height", this.height + '%') - .append("g") - .attr("transform", - "translate(" + this.margin.left + "," + this.margin.top + ")"); - - - - this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - - this.svg.append('defs').append('marker') // marker/ arrow settings - .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions - .attr('refY', 0) - .attr('orient', 'auto') - .attr('markerWidth', 13) - .attr('markerHeight', 13) - .attr('xoverflow', 'visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke', 'none');} - - - - /** - * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node - */ - refreshSimulation() { - this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id}) - var linkEnter=this.link.enter() //enter-selection - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link - ; - linkEnter.append("title") - .text(function (d){return d.type}) - this.link = this.link.merge(linkEnter); // merge old elements with entered elements - this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id}) - - var nodeEnter= this.node.enter().append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8 } - else { return this.rad } - }) - nodeEnter.on('mouseover', this.mouseover); - nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", this.setNodeStyle) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group - nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() // reactions when dragging a node - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); // merge old elements with entered elements - this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - - this.text = this.svg.selectAll("text").data(this.data.nodes) - var textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name}) - - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold" } - else { return "normal" } - }) - .text(function (d) { console.log(d.name); - return d.name }); // get text from data - this.text=this.text.merge(textEnter); - this.text.exit().remove(); - - this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) - var linkTextEnter=this.linkText.enter().append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type }); // get link text from data - this.linkText=this.linkText.merge(linkTextEnter); - this.linkText.exit().remove(); - - - this.simulation.nodes(this.data.nodes); // simualtion uses current data - this.simulation.force("link").links(this.data.links); - - } -/** - * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element - * @param d The position of every graph element (i.e. node, link...) - */ - ticked = () => { - const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg - const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - this.text - .attr("dx", (d) => { // restrictions for node text positions in the svg - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%' - }) - .attr("dy", (d) => { - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%' - }) - - this.linkText - .attr("dx", (d) => { // restrictions for link text positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; - }) - .attr("dy", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; - }) - - this.link - .attr("x1", (d) => { // restrictions for link positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX + '%'; - }) - .attr("y1", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY + '%'; - }) - .attr("x2", (d) => { - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX + '%'; - }) - .attr("y2", (d) => { - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY + '%'; - }) - - this.node - .attr("cx", (d) => { - d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX + '%'; - }) - .attr("cy", (d) => { - d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY + '%'; - }) - } - -/** - * Function executed on a drag start - */ - dragstarted = (d) => { - if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - - /** - * Drag function, executed on every drag movement - * @param d The dragged node - */ - dragged(d) { - d.fx = d3.event.x; - d.fy = d3.event.y; - } - - dragended(d) { - - } -/** - * Mouseover function, executed when mouse over node. The radius of the node will increase. - * @param d node under the cursor - */ - mouseover(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 12) - } - else { - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 6) - } - } -/** - * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. - * @param d node wich is left by the cursor after mouseover - */ - mouseout(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .attr('r', 20 + 8) - } - else { - d3.select(this).transition() - .attr('r', 20) - } - } -/** - * Doubleclick function, executed on every doubleclick on a node - *@param d The clicked node - */ - nodeDoubleClick=(d)=> { - this.index++; - const testNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(testNode); - this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }) - this.refreshSimulation(); - } - - /** Returns the color of the inner circle of a node */ - setNodeStyle(d){ - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - } -/** Returns the node border color for each node-group */ - setNodeBorderStyle=(d)=>{ - return this.color(d.group); - } - -} \ No newline at end of file diff --git a/shared/models/capability/Capability.ts b/shared/models/capability/Capability.ts index e17b78dd..c82ae40c 100644 --- a/shared/models/capability/Capability.ts +++ b/shared/models/capability/Capability.ts @@ -2,8 +2,8 @@ import { RdfElement, RdfElementDto } from "../RdfElement"; import { FpbElement } from "../fpb/FpbElement"; export class Capability extends RdfElement { - public inputs?: Array; - public outputs?: Array; + public inputs? = new Array(); + public outputs? = new Array(); constructor(dto: CapabilityDto) { super(dto.iri); @@ -15,6 +15,6 @@ export class Capability extends RdfElement { export class CapabilityDto extends RdfElementDto{ public capabilityType: RdfElementDto; - public inputs?: Array; - public outputs?: Array; + public inputs? = new Array(); + public outputs? = new Array(); } From 4c73b8fc31c2f8bbc43a2b6a5ce349afad8412b3 Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Mon, 17 May 2021 11:32:52 +0200 Subject: [PATCH 17/74] Added capability and skill graph visus --- .../components/sidebar/sidebar.component.html | 2 +- .../graph-visualization/D3GraphData.ts | 81 ++++++++-- .../graph-visualization.component.ts | 60 +++++--- .../graph-visualization.routing.ts | 2 +- .../node-creator.service.ts | 140 +++++++++++++----- .../module-overview.component.html | 3 + .../production-module.component.html | 6 +- .../production-module.routing.ts | 2 +- 8 files changed, 219 insertions(+), 77 deletions(-) diff --git a/frontend/src/layout/components/sidebar/sidebar.component.html b/frontend/src/layout/components/sidebar/sidebar.component.html index 7188435f..1dee3d2c 100644 --- a/frontend/src/layout/components/sidebar/sidebar.component.html +++ b/frontend/src/layout/components/sidebar/sidebar.component.html @@ -46,7 +46,7 @@ -   Graph-Visualization diff --git a/frontend/src/modules/graph-visualization/D3GraphData.ts b/frontend/src/modules/graph-visualization/D3GraphData.ts index e1bfcd3e..9fdef379 100644 --- a/frontend/src/modules/graph-visualization/D3GraphData.ts +++ b/frontend/src/modules/graph-visualization/D3GraphData.ts @@ -1,7 +1,46 @@ + +export enum NodeType { + None = "None", + D3ModuleNode ="D3ModuleNode", + D3SkillNode = "D3SkillNode", + D3CapabilityNode = "D3CapabilityNode" +} + +export class D3Node { + constructor( + public id: string, + public name: string, + public group: number, + public type: NodeType = NodeType.None) { } +} + +export class D3ModuleNode extends D3Node { + constructor(id: string, name: string) { + super(id, name, 1, NodeType.D3ModuleNode); + } +} +export class D3SkillNode extends D3Node{ + constructor(id: string, name: string) { + super(id, name, 100, NodeType.D3SkillNode); + } +} + +export class D3CapabilityNode extends D3Node{ + constructor(id: string, name: string) { + super(id, name, 200, NodeType.D3CapabilityNode); + } +} + +export class D3Link { + constructor( + public source: string, + public target: string, + public type: string) { } +} + + export class D3GraphData { - // nodes: D3Node [] = []; - // links: D3Link[] = []; constructor(private nodes: D3Node[] = [], private links: D3Link[] = []) { } addNode(node: D3Node): void { @@ -11,19 +50,33 @@ export class D3GraphData { addLink(link: D3Link): void { this.links.push(link); } -} + getNodes(): D3Node[] {return this.nodes;} + getNodesById(id: string): D3Node[] { + return this.nodes.filter(node => node.id == id); + } -export class D3Node { - constructor( - private id: string, - private name: string, - private group: number) { } -} + getNodesByNodeTyp(type: string): D3Node[] { + return this.nodes.filter(node => node.type === type); + } -export class D3Link { - constructor( - private source: string, - private target: string, - private type: string) { } + getLinks(): D3Link[] {return this.links;} + + // Adds another D3GraphData object to the current one + appendAndConnectData(data: D3GraphData, idToConnectTo: string, nodeType: NodeType, connectionType: string): void { + const newNodes = data.getNodes(); + const newLinks = data.getLinks(); + // Add the new data + newNodes.forEach(newNode => this.addNode(newNode)); + newLinks.forEach(newLink => this.addLink(newLink)); + // Connect with existing nodes + const oldNodesToConnect = this.getNodesById(idToConnectTo); + const newNodesToConnect = this.getNodesByNodeTyp(nodeType); + + oldNodesToConnect.forEach(oldNode => { + newNodesToConnect.forEach(newNode => { + this.addLink(new D3Link(oldNode.id, newNode.id, connectionType)); + }); + }); + } } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index d93ed4e7..daa9810e 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,7 +1,9 @@ -import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef, OnDestroy } from '@angular/core'; import * as d3 from "d3"; import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; +import { Observable, Subscription } from 'rxjs'; +import { D3GraphData } from './D3GraphData'; @Component({ @@ -10,7 +12,8 @@ import { NodeCreatorService } from './node-creator.service'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit, OnInit { +export class GraphVisualizationComponent implements AfterContentInit, OnDestroy { + $graphDataSubscription: Subscription; name: string; svg: any; link: any; @@ -45,27 +48,50 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { } @ViewChild('g') svgContainer: ElementRef; - moduleName: string; - ngOnInit(): void {} + ngOnDestroy(): void { + // Kill data subscription and stop simulation -> Otherwise killing performance + this.$graphDataSubscription.unsubscribe(); + this.simulation.stop(); + } ngAfterContentInit(): void { - console.log("ng after content"); - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }); - console.log("moduleName"); - console.log(this.moduleName); - - this.nodeCreatorService.getAllNodes(this.moduleName).subscribe(data => { - this.data = data; // load data created by node-creator.service - console.log("data"); - console.log(this.data); + console.log(p); + + const elementType = p['elementType']; + const elementIri = p['elementIri']; + console.log("elementType"); + console.log(elementType); + console.log("elementIri"); + console.log(elementIri); + + + switch (elementType) { + case "modules": + this.$graphDataSubscription = this.nodeCreatorService.getAllModuleNodes(elementIri).subscribe(data => { + this.data = data; // load data created by node-creator.service + this.setSvg(); + this.setSimulation(); + }); + break; + case "skills": + this.$graphDataSubscription = this.nodeCreatorService.getAllSkillNodes(elementIri).subscribe(data => { + this.data = data; // load data created by node-creator.service + this.setSvg(); + this.setSimulation(); + }); + break; + case "capabilities": + this.$graphDataSubscription = this.nodeCreatorService.getAllCapabilityNodes(elementIri).subscribe(data => { + this.data = data; // load data created by node-creator.service + this.setSvg(); + this.setSimulation(); + }); + break; + } - this.setSvg(); - this.setSimulation(); }); } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts index c9d65f24..86962e1e 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import {GraphVisualizationComponent} from './graph-visualization.component'; const routes: Routes = [ - { path: 'modules/:moduleName', + { path: ':elementType/:elementIri', component: GraphVisualizationComponent, }, diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts index 9a6ec11f..b000a9aa 100644 --- a/frontend/src/modules/graph-visualization/node-creator.service.ts +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -1,10 +1,13 @@ import { Injectable } from '@angular/core'; +import { Capability } from '@shared/models/capability/Capability'; import { ProductionModule } from '@shared/models/production-module/ProductionModule'; +import { Skill } from '@shared/models/skill/Skill'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { CapabilityService } from 'src/shared/services/capability.service'; +import { SkillService } from 'src/shared/services/skill.service'; import { ModuleService } from '../../shared/services/module.service'; -import { D3GraphData, D3Link, D3Node } from './D3GraphData'; -// import { containsElement } from '@angular/animations/browser/browser'; +import { D3CapabilityNode, D3GraphData, D3Link, D3ModuleNode, D3Node, D3SkillNode, NodeType } from './D3GraphData'; @Injectable({ @@ -14,71 +17,128 @@ export class NodeCreatorService { apiRoot = "/api"; modules: ProductionModule[] - constructor(private moduleService: ModuleService) { } + constructor( + private moduleService: ModuleService, + private skillService: SkillService, + private capabilityService: CapabilityService) { } - /** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ - getAllNodes(moduleIri: string): Observable { + // /** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ + // getAllNodes(moduleIri: string): Observable { + + // return this.moduleService.getAllModules().pipe(map(modules => { + // // If a module name is passed, filter the module list for that specific one + // if(moduleIri != undefined) { + // modules = modules.filter(module => module.iri == moduleIri); + // } + // return this.getDataFromModules(modules); + // })); + // } + + getAllModuleNodes(moduleIri: string): Observable { return this.moduleService.getAllModules().pipe(map(modules => { - // If a module name is passed, filter the module list for that specific one - if(moduleIri != undefined) { + // If a module IRI is passed, filter the module list for that specific one + if(moduleIri != undefined && moduleIri != "") { + console.log("filtering"); + console.log("moduleIri: '" + moduleIri + "'"); + + modules = modules.filter(module => module.iri == moduleIri); } - return this.formatData(modules); + + console.log("modules"); + console.log(modules); + + + + const graphData = new D3GraphData(); + modules.forEach(module => { //loop over all modules + + graphData.addNode(new D3ModuleNode(module.iri, module.getLocalName())); + // TODO: Add module components + + const skillData = this.getDataFromSkills(module.skills); + graphData.appendAndConnectData(skillData, module.iri, NodeType.D3SkillNode, "hasSkill"); + + const capabilityData = this.getDataFromCapabilities(module.capabilities); + graphData.appendAndConnectData(capabilityData, module.iri, NodeType.D3CapabilityNode, "hasCapability"); + }); + return graphData; + })); } - formatData(modules: ProductionModule[]): D3GraphData { + getAllSkillNodes(skillIri: string): Observable { + return this.skillService.getAllSkills().pipe(map(skills => { + // If a skill IRI is passed, filter the skill list for that specific one + if(skillIri != undefined && skillIri != "") { + skills = skills.filter(skill => skill.iri == skillIri); + } - const graphData = new D3GraphData(); - modules.forEach(module => { //loop over all modules + return this.getDataFromSkills(skills); + })); + } + + getAllCapabilityNodes(capabilityIri: string): Observable { + return this.capabilityService.getAllCapabilities().pipe(map(capabilities => { + // If a capability IRI is passed, filter the capability list for that specific one + if(capabilityIri != undefined && capabilityIri != "") { + capabilities = capabilities.filter(capability => capability.iri == capabilityIri); + } + + return this.getDataFromCapabilities(capabilities); + })); + } - graphData.addNode(new D3Node(module.iri, module.getLocalName(), 1)); // adds a node for each module - module.skills.forEach(skill => { - graphData.addNode(new D3Node(skill.iri, skill.getLocalName(), 100)); - graphData.addLink(new D3Link(module.iri, skill.iri, "hasSkill")); + private getDataFromSkills(skills: Skill[]): D3GraphData { + const graphData = new D3GraphData(); + skills.forEach(skill => { - // Add state machine - graphData.addNode(new D3Node(skill.stateMachine.iri, skill.stateMachine.getLocalName(), 120)); - graphData.addLink(new D3Link(skill.iri, skill.stateMachine.iri, "hasStateMachine")); + graphData.addNode(new D3SkillNode(skill.iri, skill.getLocalName())); - // Add skill parameters - skill.skillParameters.forEach(parameter => { - graphData.addNode(new D3Node(parameter.iri, parameter.getLocalName(), 140)); - graphData.addLink(new D3Link(skill.iri, parameter.iri, "hasSkillParameter")); - }); + // Add state machine + graphData.addNode(new D3Node(skill.stateMachine.iri, skill.stateMachine.getLocalName(), 120)); + graphData.addLink(new D3Link(skill.iri, skill.stateMachine.iri, "hasStateMachine")); - // Add skill outputs - skill.skillOutputs.forEach(output => { - graphData.addNode(new D3Node(output.iri,output.getLocalName(), 160)); - graphData.addLink(new D3Link(skill.iri, output.iri, "hasSkillOutput")); - }); + // Add skill parameters + skill.skillParameters.forEach(parameter => { + graphData.addNode(new D3Node(parameter.iri, parameter.getLocalName(), 140)); + graphData.addLink(new D3Link(skill.iri, parameter.iri, "hasSkillParameter")); }); - // Add module's capabilities - module.capabilities.forEach(capability => { //loop over all capabilities of current module - graphData.addNode(new D3Node(capability.iri, capability.getLocalName(), 200)); - graphData.addLink(new D3Link(module.iri, capability.iri, "hasCapability")); // adds a link between capability and module + // Add skill outputs + skill.skillOutputs.forEach(output => { + graphData.addNode(new D3Node(output.iri,output.getLocalName(), 160)); + graphData.addLink(new D3Link(skill.iri, output.iri, "hasSkillOutput")); + }); + }); + return graphData; + } - capability.inputs.forEach(input => { - graphData.addNode(new D3Node(input.iri, input.getLocalName(), 240)); - graphData.addLink(new D3Link(capability.iri, input.iri, "hasInput")); + private getDataFromCapabilities(capabilities: Capability[]): D3GraphData { + const graphData = new D3GraphData(); - }); + capabilities.forEach(capability => { //loop over all capabilities of current module + graphData.addNode(new D3CapabilityNode(capability.iri, capability.getLocalName())); - capability.outputs.forEach(output => { - graphData.addNode(new D3Node(output.iri, output.getLocalName(), 270)); - graphData.addLink(new D3Link(capability.iri, output.iri, "hasOutput")); - }); + capability.inputs.forEach(input => { + graphData.addNode(new D3Node(input.iri, input.getLocalName(), 240)); + graphData.addLink(new D3Link(capability.iri, input.iri, "hasInput")); }); + capability.outputs.forEach(output => { + graphData.addNode(new D3Node(output.iri, output.getLocalName(), 270)); + graphData.addLink(new D3Link(capability.iri, output.iri, "hasOutput")); + }); }); return graphData; } + + } diff --git a/frontend/src/modules/production-modules/module-overview/module-overview.component.html b/frontend/src/modules/production-modules/module-overview/module-overview.component.html index d8e5dddb..c1de1d60 100644 --- a/frontend/src/modules/production-modules/module-overview/module-overview.component.html +++ b/frontend/src/modules/production-modules/module-overview/module-overview.component.html @@ -3,6 +3,9 @@
diff --git a/frontend/src/modules/production-modules/production-module.component.html b/frontend/src/modules/production-modules/production-module.component.html index 09c0fc29..7e169aa6 100644 --- a/frontend/src/modules/production-modules/production-module.component.html +++ b/frontend/src/modules/production-modules/production-module.component.html @@ -2,13 +2,13 @@

Production Modules

diff --git a/frontend/src/modules/production-modules/production-module.routing.ts b/frontend/src/modules/production-modules/production-module.routing.ts index 3e443b73..a68013f5 100644 --- a/frontend/src/modules/production-modules/production-module.routing.ts +++ b/frontend/src/modules/production-modules/production-module.routing.ts @@ -17,7 +17,7 @@ const routes: Routes = [ children: [ {path: '', redirectTo: 'overview', pathMatch: 'full'}, {path: 'overview', component: ModuleOverviewComponent}, - {path: 'graph-visualization', component: ModuleGraphVisuComponent}, + // {path: 'graph-visualization', component: ModuleGraphVisuComponent}, // Note that graph visu is passed to the graph visu module {path: 'register', component: ModuleRegistrationComponent}, ] } From 5517f35d09ff63b2c4dd3528ef8078392040ee6e Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Wed, 19 May 2021 09:08:03 +0200 Subject: [PATCH 18/74] Added types to graph visu component --- .../graph-visualization/D3GraphData.ts | 3 +- .../graph-visualization.component.ts | 28 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/frontend/src/modules/graph-visualization/D3GraphData.ts b/frontend/src/modules/graph-visualization/D3GraphData.ts index 9fdef379..cfbd9284 100644 --- a/frontend/src/modules/graph-visualization/D3GraphData.ts +++ b/frontend/src/modules/graph-visualization/D3GraphData.ts @@ -52,6 +52,8 @@ export class D3GraphData { } getNodes(): D3Node[] {return this.nodes;} + getLinks(): D3Link[] {return this.links;} + getNodesById(id: string): D3Node[] { return this.nodes.filter(node => node.id == id); } @@ -60,7 +62,6 @@ export class D3GraphData { return this.nodes.filter(node => node.type === type); } - getLinks(): D3Link[] {return this.links;} // Adds another D3GraphData object to the current one appendAndConnectData(data: D3GraphData, idToConnectTo: string, nodeType: NodeType, connectionType: string): void { diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index daa9810e..bb3dc26e 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -3,7 +3,7 @@ import * as d3 from "d3"; import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; import { Observable, Subscription } from 'rxjs'; -import { D3GraphData } from './D3GraphData'; +import { D3GraphData, D3Link, D3Node } from './D3GraphData'; @Component({ @@ -24,7 +24,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy /** The displayed node */ node: any; /** Loaded data: Connected modules with their capabilities and skills*/ - data: any; + data: D3GraphData; /**Returns different colors automatically. The node-border color is the same for all node-group members*/ color: any; /**Simulation of the force directed graph */ @@ -98,12 +98,12 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy /**Defines settings of the simulation */ setSimulation(){ - this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation = d3.forceSimulation(this.data.getNodes()); this.simulation .force("link", d3.forceLink() .id(function (d) { return d.id; }) .distance(80) - .links(this.data.links)) + .links(this.data.getLinks())) .force("collision", d3.forceCollide(40)) .on("tick", this.ticked) // tick on every step of the simulation .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction @@ -149,7 +149,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ refreshSimulation() { - this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id;}); + this.link = this.svg.selectAll(".link").data(this.data.getLinks(), function (d){return d.target.id;}); const linkEnter=this.link.enter() //enter-selection .append("line").style("stroke", "#aaa") .attr("class", "links") @@ -160,7 +160,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy this.link = this.link.merge(linkEnter); // merge old elements with entered elements this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id;}); + this.node = this.svg.selectAll(".node").data(this.data.getNodes(), function (d){return d.id;}); const nodeEnter= this.node.enter().append("circle") .attr("r", (d) => { @@ -181,9 +181,9 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - this.text = this.svg.selectAll("text").data(this.data.nodes); + this.text = this.svg.selectAll("text").data(this.data.getNodes()); const textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name;}) + .data(this.data.getNodes(), function (d){return d.name;}) .attr("font-weight", function (d) { if (d.group == 1) { return "bold"; } @@ -194,7 +194,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy this.text=this.text.merge(textEnter); this.text.exit().remove(); - this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type;}); + this.linkText = this.svg.selectAll("links").data(this.data.getLinks(), function (d){ return d.type;}); const linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") @@ -203,8 +203,8 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy this.linkText.exit().remove(); - this.simulation.nodes(this.data.nodes); // simualtion uses current data - this.simulation.force("link").links(this.data.links); + this.simulation.nodes(this.data.getNodes()); // simualtion uses current data + this.simulation.force("link").links(this.data.getLinks()); } /** @@ -343,9 +343,9 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy */ nodeDoubleClick=(d)=> { this.index++; - const testNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(testNode); - this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }); + const testNode = new D3Node(this.index.toString(), "neighbor", 8); + this.data.addNode(testNode); + this.data.addLink(new D3Link(d.id, testNode.id, "testclick")); this.refreshSimulation(); } From 54b244aa8f927f2b24d2782b2cf9baddeceb3026 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Mon, 30 Dec 2019 22:28:30 +0100 Subject: [PATCH 19/74] Created 'Graph-Visualization' in 'own-modules' Created 'Graph-Visualization' in 'own-modules' and d3.js installed --- .../graph-visualization.component.html | 5 - .../graph-visualization.component.ts | 363 +----------------- .../graph-visualization.module.ts | 15 +- .../graph-visualization.routing.ts | 18 +- 4 files changed, 21 insertions(+), 380 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index a454716a..70b08142 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,8 +1,3 @@

Graph-Visualization

-
-
- -
-
\ No newline at end of file diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index bb3dc26e..1b195074 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,362 +1,13 @@ -import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef, OnDestroy } from '@angular/core'; -import * as d3 from "d3"; -import { ActivatedRoute } from '@angular/router'; -import { NodeCreatorService } from './node-creator.service'; -import { Observable, Subscription } from 'rxjs'; -import { D3GraphData, D3Link, D3Node } from './D3GraphData'; +import { Component } from '@angular/core'; +@Component({ + selector: 'graph-visualization', + templateUrl: './graph-visualization.component.html' +}) +export class GraphVisualizationComponent { - @Component({ - selector: 'graph-visualization', - encapsulation: ViewEncapsulation.None, - templateUrl: './graph-visualization.component.html', - styleUrls: ['./graph-visualization.component.scss'] - }) -export class GraphVisualizationComponent implements AfterContentInit, OnDestroy { - $graphDataSubscription: Subscription; - name: string; - svg: any; - link: any; - /** The displayed node name*/ - text: any; - /** The displayed link description */ - linkText: any; - /** The displayed node */ - node: any; - /** Loaded data: Connected modules with their capabilities and skills*/ - data: D3GraphData; - /**Returns different colors automatically. The node-border color is the same for all node-group members*/ - color: any; - /**Simulation of the force directed graph */ - simulation: any; + constructor() { } - margin = { top: 10, right: 30, bottom: 30, left: 40 }; - width = 100; - height = 100; - /**Defines the nodeborder thickness*/ - nodeborder = 8; - /**Defines the standard radius of a node*/ - rad = 20; - - index=0; - - constructor( - private route: ActivatedRoute, - private nodeCreatorService: NodeCreatorService - ) { - } - - @ViewChild('g') svgContainer: ElementRef; - - ngOnDestroy(): void { - // Kill data subscription and stop simulation -> Otherwise killing performance - this.$graphDataSubscription.unsubscribe(); - this.simulation.stop(); - } - - ngAfterContentInit(): void { - this.route.params.subscribe(p => { - console.log(p); - - const elementType = p['elementType']; - const elementIri = p['elementIri']; - console.log("elementType"); - console.log(elementType); - console.log("elementIri"); - console.log(elementIri); - - - switch (elementType) { - case "modules": - this.$graphDataSubscription = this.nodeCreatorService.getAllModuleNodes(elementIri).subscribe(data => { - this.data = data; // load data created by node-creator.service - this.setSvg(); - this.setSimulation(); - }); - break; - case "skills": - this.$graphDataSubscription = this.nodeCreatorService.getAllSkillNodes(elementIri).subscribe(data => { - this.data = data; // load data created by node-creator.service - this.setSvg(); - this.setSimulation(); - }); - break; - case "capabilities": - this.$graphDataSubscription = this.nodeCreatorService.getAllCapabilityNodes(elementIri).subscribe(data => { - this.data = data; // load data created by node-creator.service - this.setSvg(); - this.setSimulation(); - }); - break; - } - - - }); - - } - - /**Defines settings of the simulation */ - setSimulation(){ - this.simulation = d3.forceSimulation(this.data.getNodes()); - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.getLinks())) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)); //Zentrierung der Nodes - - this.refreshSimulation(); - } - - - - /**Defines settings of the SVG and the color-scheme of the nodes */ - setSvg(){ - - this.svg = d3.select("#graph") // size definitions for the svg - .append("svg") - .attr("width", this.width + '%') - .attr("height", this.height + '%') - .append("g") - .attr("transform", - "translate(" + this.margin.left + "," + this.margin.top + ")"); - - - - this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - - this.svg.append('defs').append('marker') // marker/ arrow settings - .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions - .attr('refY', 0) - .attr('orient', 'auto') - .attr('markerWidth', 13) - .attr('markerHeight', 13) - .attr('xoverflow', 'visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke', 'none');} - - - - /** - * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node - */ - refreshSimulation() { - this.link = this.svg.selectAll(".link").data(this.data.getLinks(), function (d){return d.target.id;}); - const linkEnter=this.link.enter() //enter-selection - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link - ; - linkEnter.append("title") - .text(function (d){return d.type;}); - this.link = this.link.merge(linkEnter); // merge old elements with entered elements - this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - this.node = this.svg.selectAll(".node").data(this.data.getNodes(), function (d){return d.id;}); - - const nodeEnter= this.node.enter().append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8; } - else { return this.rad; } - }); - nodeEnter.on('mouseover', this.mouseover); - nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", this.setNodeStyle) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }); // set different node colours for each node-group - nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() // reactions when dragging a node - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); // merge old elements with entered elements - this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - - this.text = this.svg.selectAll("text").data(this.data.getNodes()); - const textEnter=this.text.enter().append("text") - .data(this.data.getNodes(), function (d){return d.name;}) - - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold"; } - else { return "normal"; } - }) - .text(function (d) { console.log(d.name); - return d.name; }); // get text from data - this.text=this.text.merge(textEnter); - this.text.exit().remove(); - - this.linkText = this.svg.selectAll("links").data(this.data.getLinks(), function (d){ return d.type;}); - const linkTextEnter=this.linkText.enter().append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type; }); // get link text from data - this.linkText=this.linkText.merge(linkTextEnter); - this.linkText.exit().remove(); - - - this.simulation.nodes(this.data.getNodes()); // simualtion uses current data - this.simulation.force("link").links(this.data.getLinks()); - - } - /** - * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element - * @param d The position of every graph element (i.e. node, link...) - */ - ticked = () => { - const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg - const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - this.text - .attr("dx", (d) => { // restrictions for node text positions in the svg - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%'; - }) - .attr("dy", (d) => { - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%'; - }); - - this.linkText - .attr("dx", (d) => { // restrictions for link text positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; - }) - .attr("dy", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; - }); - - this.link - .attr("x1", (d) => { // restrictions for link positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX + '%'; - }) - .attr("y1", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY + '%'; - }) - .attr("x2", (d) => { - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX + '%'; - }) - .attr("y2", (d) => { - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY + '%'; - }); - - this.node - .attr("cx", (d) => { - d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX + '%'; - }) - .attr("cy", (d) => { - d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY + '%'; - }); - } - - /** - * Function executed on a drag start - */ - dragstarted = (d) => { - if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - - /** - * Drag function, executed on every drag movement - * @param d The dragged node - */ - dragged(d) { - d.fx = d3.event.x; - d.fy = d3.event.y; - } - - dragended(d) { - - } - /** - * Mouseover function, executed when mouse over node. The radius of the node will increase. - * @param d node under the cursor - */ - mouseover(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 12); - } - else { - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 6); - } - } - /** - * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. - * @param d node wich is left by the cursor after mouseover - */ - mouseout(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .attr('r', 20 + 8); - } - else { - d3.select(this).transition() - .attr('r', 20); - } - } - /** - * Doubleclick function, executed on every doubleclick on a node - *@param d The clicked node - */ - nodeDoubleClick=(d)=> { - this.index++; - const testNode = new D3Node(this.index.toString(), "neighbor", 8); - this.data.addNode(testNode); - this.data.addLink(new D3Link(d.id, testNode.id, "testclick")); - this.refreshSimulation(); - } - - /** Returns the color of the inner circle of a node */ - setNodeStyle(d){ - if (d.group == 1) { return "black"; } - else { return "whitesmoke"; } - } - /** Returns the node border color for each node-group */ - setNodeBorderStyle=(d)=>{ - return this.color(d.group); - } } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index 17f69cd0..afb5fff0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,18 +1,11 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from '../../shared/services/module.service'; -import { NodeCreatorService } from './node-creator.service'; @NgModule({ - imports: [ - GraphVisualizationRouter, - ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - NodeCreatorService - - ] + imports: [ + GraphVisualizationRouter + ], + declarations: [GraphVisualizationComponent] }) export class GraphVisualizationModule { } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts index 86962e1e..ded3fe38 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts @@ -1,20 +1,22 @@ import { Routes, RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import {GraphVisualizationComponent} from './graph-visualization.component'; +import {GraphVisualizationComponent} from './graph-visualization.component' const routes: Routes = [ - { path: ':elementType/:elementIri', - component: GraphVisualizationComponent, + { path: '', + component: GraphVisualizationComponent, + data: { + title: 'Graph-Visualization' + }, + children: [] - }, - {path:'modules', component: GraphVisualizationComponent - } + } ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] }) export class GraphVisualizationRouter {} From ccafb4be91a0edcf27002ef7f3bbfb36a9d7f762 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Mon, 27 Jan 2020 12:24:21 +0100 Subject: [PATCH 20/74] Added visual graph by d3.js --- .../graph-visualization.component.html | 5 + .../graph-visualization.component.ts | 191 +++++++++++++++++- 2 files changed, 192 insertions(+), 4 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index 70b08142..fcff4a3d 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,3 +1,8 @@

Graph-Visualization

+
+
+ +
+
\ No newline at end of file diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 1b195074..cf171b0d 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,13 +1,196 @@ -import { Component } from '@angular/core'; +import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'; +import * as d3 from "d3" +import { enterView } from '@angular/core/src/render3/state'; +import { pathToFileURL } from 'url'; @Component({ selector: 'graph-visualization', - templateUrl: './graph-visualization.component.html' + encapsulation: ViewEncapsulation.None, + templateUrl: './graph-visualization.component.html', + styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent { +export class GraphVisualizationComponent implements AfterContentInit { + + ngAfterContentInit(): void { + const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen + const width = 1400 - margin.left - margin.right; + const height = 900 - margin.top - margin.bottom; + const rad=20; // radius + + +var svg = d3.select("#graph") // Größe u. Grenzen SVG +.append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) +.append("g") + .attr("transform", + "translate(" + margin.left + "," + margin.top + ")"); + + + +var colors = d3.scaleOrdinal(d3.schemeCategory10); //Farben + + + +//d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten + +const data = { + "nodes": [ + { + "id": 1, + "name": "ModulA", + "group": 1 + }, + { + "id": 2, + "name": "ModulB", + "group": 2 + }, + { + "id": 3, + "name": "ModulC", + "group": 3 + + }, { + "id": 4, + "name": "cap11", + "group":1 + }, + { + "id": 5, + "name": "cap12", + "group":1 + }, + { + "id": 6, + "name": "cap21", + "group":2 + } + ], + "links": [ + + { + "source": 1, + "target": 4, + "type": "is_a" + } , + { + "source": 1, + "target": 5, + "type": "has" + } , + { + "source": 2, + "target": 6, + "type":"has" + } + ] +} - constructor() { } + var link = svg // Links initialisieren + .selectAll(".links") + .data(data.links) + .enter() + .append("line") + .style("stroke", "#aaa") + ; + link.append("title") + .text(function(d){return d.type}); + var node = svg // Nodes initialisieren + .selectAll("circle") + .data(data.nodes) + .enter() + .append("circle") + .attr("r", rad) + .style( "fill", "white") + .style("stroke-width", 8) + .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit + /*.append ("text") // name als text neben node + .attr ("dx", 12) + .attr ("dy", ".35em") + .text(function(d){return d.name})*/ + .call(d3.drag() + .on("start", dragstarted) + .on("drag", dragged)); +; + + const text =svg + .selectAll("text") + .data(data.nodes) + .enter() + .append("text") + + .text(function(d){return d.name}); + + const linkText =svg + .selectAll("links") + .data(data.links) + .enter() + .append("text") + .text(function (d){return d.type}); + + + + var simulation = d3.forceSimulation(data.nodes) //Simulation + .force("link", d3.forceLink() + .id(function(d) { return d.id; }) + .distance(100) + .links(data.links) + ) + .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen + .force("center", d3.forceCenter(width / 2, height / 2)) // Nodes mittig von svg + .on("tick", ticked); // tick wird in jedem Schritt durchgeführt + + + function ticked() { + console.log(data); + text + .attr("dx", function(d) { + return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) + .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }) + + linkText + .attr("dx", function(d){return 0.5*((d.source.x)+(d.target.x))}) + .attr("dy", function(d){return 0.5*((d.source.y)+(d.target.y)) }) + + + + link // Anfang und Ende der Links / Bestimmung der Position + .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)); }) // Postionen nur innerhalb des Fensters erlaubt + .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)); }) + .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)); }) + .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)); }); + + node // Bestimmung der Postion einzelner Nodes + .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)); }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }); + } + + function ended() { + console.log("ended"); + } + + function dragstarted(d) { + if (!d3.event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + console.log(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; + } + + } + constructor() { } } From 0b2cde9beed00385dd60f5ab45a5e7356db61373 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 31 Jan 2020 12:17:01 +0100 Subject: [PATCH 21/74] Added arrows Added arrows for relation directions --- .../graph-visualization.component.ts | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index cf171b0d..efe39ef0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -15,10 +15,12 @@ export class GraphVisualizationComponent implements AfterContentInit { const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen const width = 1400 - margin.left - margin.right; const height = 900 - margin.top - margin.bottom; - const rad=20; // radius + const rad=20 // Radius eines Knotens + const nodeborder =8; // Umrandungsdicke eines Knotens -var svg = d3.select("#graph") // Größe u. Grenzen SVG + +const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) @@ -28,8 +30,21 @@ var svg = d3.select("#graph") // Größe u. Grenzen SVG -var colors = d3.scaleOrdinal(d3.schemeCategory10); //Farben - +const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + +svg.append('defs').append('marker') + .attr("id",'arrowhead') + .attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5) + .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('refY',0) + .attr('orient','auto') + .attr('markerWidth',13) + .attr('markerHeight',13) + .attr('xoverflow','visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke','none'); //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten @@ -89,23 +104,25 @@ const data = { - var link = svg // Links initialisieren + const link = svg // Links initialisieren .selectAll(".links") .data(data.links) .enter() .append("line") .style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)') ; link.append("title") .text(function(d){return d.type}); - var node = svg // Nodes initialisieren + const node = svg // Nodes initialisieren .selectAll("circle") .data(data.nodes) .enter() .append("circle") .attr("r", rad) .style( "fill", "white") - .style("stroke-width", 8) + .style("stroke-width", nodeborder) .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit /*.append ("text") // name als text neben node .attr ("dx", 12) @@ -133,10 +150,10 @@ const data = { - var simulation = d3.forceSimulation(data.nodes) //Simulation + const simulation = d3.forceSimulation(data.nodes) //Simulation .force("link", d3.forceLink() .id(function(d) { return d.id; }) - .distance(100) + .distance(150) .links(data.links) ) .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen @@ -145,7 +162,6 @@ const data = { function ticked() { - console.log(data); text .attr("dx", function(d) { return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) From 411376c064f08d47202b54dc0f30c5f43e80e9d4 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 31 Jan 2020 14:56:00 +0100 Subject: [PATCH 22/74] Fixed bug Type of relation fixed (on path between two linked nodes) --- .../graph-visualization.component.ts | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index efe39ef0..d7894ca6 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -34,7 +34,7 @@ const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung v svg.append('defs').append('marker') .attr("id",'arrowhead') - .attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5) + .attr('viewBox','-0 -5 10 10') //Koordinatensystem .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten .attr('refY',0) .attr('orient','auto') @@ -146,6 +146,7 @@ const data = { .data(data.links) .enter() .append("text") + .style("fill","#999" ) .text(function (d){return d.type}); @@ -164,24 +165,46 @@ const data = { function ticked() { text .attr("dx", function(d) { - return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) - .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }) + return Math.max(0+rad, Math.min(d.x+23, width-rad))}) + .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }) linkText - .attr("dx", function(d){return 0.5*((d.source.x)+(d.target.x))}) - .attr("dy", function(d){return 0.5*((d.source.y)+(d.target.y)) }) + .attr("dx", function(d){ + if ((d.target.x>width) || (d.source.x>width)) { + if (d.target.x>d.source.x) { + return Math.min(width,width-0.5*(width-d.source.x)); + } else { + return Math.min(width,width-0.5*(width-d.target.x)); + } ; + } else { + return Math.min(0.5*((Math.max(0,(d.target.x)))-(Math.max(0,(d.source.x))))+(Math.max(0,d.source.x))); + } + }) + //Relationstext bleibt am Link + .attr("dy", function(d){ + if ((d.target.y>height) || (d.source.y>height)) { + if (d.target.y>d.source.y) { + return Math.min(height,height-0.5*(height-d.source.y)); + } + else { + return Math.min(height,height-0.5*(height-d.target.y)); + } + } + else { + return (0.5*((Math.max(0,(d.target.y)))-(Math.max(0,(d.source.y))))+(Math.max(0,d.source.y))); + }}) link // Anfang und Ende der Links / Bestimmung der Position - .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)); }) // Postionen nur innerhalb des Fensters erlaubt - .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)); }) - .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)); }) - .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)); }); + .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)) }) // Postionen nur innerhalb des Fensters erlaubt + .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)) }) + .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)) }) + .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)) }); node // Bestimmung der Postion einzelner Nodes - .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)); }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße - .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }); + .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)) }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }); } function ended() { From 637dbcb939c57607ea53fb51354eed0ff282b2ae Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 18 Feb 2020 16:58:55 +0100 Subject: [PATCH 23/74] Added Link to Graph Visualization in Module-Management --- .../graph-visualization/graph-visualization.component.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index fcff4a3d..3163f4b5 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,8 +1,9 @@

Graph-Visualization

-
+ +
-
\ No newline at end of file +
From 382bf4c1a33fb3e1542222f2c5b835fcafe12b79 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 28 Feb 2020 19:15:38 +0100 Subject: [PATCH 24/74] Resize svg when window resized --- .../graph-visualization.component.html | 7 +- .../graph-visualization.component.ts | 413 ++++++++++-------- 2 files changed, 238 insertions(+), 182 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index 3163f4b5..a454716a 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,9 +1,8 @@

Graph-Visualization

- -
-
+
+
-
+
\ No newline at end of file diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index d7894ca6..b57ca058 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,7 +1,8 @@ -import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'; +import { Component, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; +import { window } from 'rxjs/operators'; @Component({ selector: 'graph-visualization', @@ -11,225 +12,281 @@ import { pathToFileURL } from 'url'; }) export class GraphVisualizationComponent implements AfterContentInit { - ngAfterContentInit(): void { - const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen - const width = 1400 - margin.left - margin.right; - const height = 900 - margin.top - margin.bottom; - const rad=20 // Radius eines Knotens - const nodeborder =8; // Umrandungsdicke eines Knotens + @ViewChild('g') svgContainer: ElementRef; + ngAfterContentInit(): void { + console.log('element ref width:'); + console.log(this.svgContainer.nativeElement.offsetWidth); + const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen + const width = 100; //parent.innerWidth - margin.left - margin.right; + //var width= window.innerWidth; + const height = 100; + const rad = 20 // Radius eines Knotens + const nodeborder = 8; // Umrandungsdicke eines Knotens + const testweite = parent.innerWidth; + console.log("Die ermittelte Weite ist:" + testweite); -const svg = d3.select("#graph") // Größe u. Grenzen SVG -.append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) -.append("g") - .attr("transform", + const svg = d3.select("#graph") // Größe u. Grenzen SVG + .append("svg") + .attr("width", width + '%') + .attr("height", height+'%') + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - -const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes - -svg.append('defs').append('marker') - .attr("id",'arrowhead') - .attr('viewBox','-0 -5 10 10') //Koordinatensystem - .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten - .attr('refY',0) - .attr('orient','auto') - .attr('markerWidth',13) - .attr('markerHeight',13) - .attr('xoverflow','visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke','none'); - -//d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - -const data = { - "nodes": [ - { - "id": 1, - "name": "ModulA", - "group": 1 - }, - { - "id": 2, - "name": "ModulB", - "group": 2 - }, - { - "id": 3, - "name": "ModulC", - "group": 3 - - }, { - "id": 4, - "name": "cap11", - "group":1 - }, - { - "id": 5, - "name": "cap12", - "group":1 - }, - { - "id": 6, - "name": "cap21", - "group":2 + + const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + + svg.append('defs').append('marker') + .attr("id", 'arrowhead') + .attr('viewBox', '-0 -5 10 10') //Koordinatensystem + .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('refY', 0) + .attr('orient', 'auto') + .attr('markerWidth', 13) + .attr('markerHeight', 13) + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke', 'none'); + + + //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten + + const data = { + "nodes": [ + { + "id": 1, + "name": "ModulA", + "group": 1 + }, + { + "id": 2, + "name": "ModulB", + "group": 2 + }, + { + "id": 3, + "name": "ModulC", + "group": 3 + + }, { + "id": 4, + "name": "cap11", + "group": 1 + }, + { + "id": 5, + "name": "cap12", + "group": 1 + }, + { + "id": 6, + "name": "cap21", + "group": 2 + } + ], + "links": [ + + { + "source": 1, + "target": 4, + "type": "is_a" + }, + { + "source": 1, + "target": 5, + "type": "has" + }, + { + "source": 2, + "target": 6, + "type": "has" + } + ] } - ], - "links": [ - - { - "source": 1, - "target": 4, - "type": "is_a" - } , - { - "source": 1, - "target": 5, - "type": "has" - } , - { - "source": 2, - "target": 6, - "type":"has" - } - ] -} - const link = svg // Links initialisieren - .selectAll(".links") - .data(data.links) - .enter() - .append("line") + const link = svg // Links initialisieren + .selectAll(".links") + .data(data.links) + .enter() + .append("line") .style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') - ; + ; link.append("title") - .text(function(d){return d.type}); - const node = svg // Nodes initialisieren - .selectAll("circle") - .data(data.nodes) - .enter() - .append("circle") + .text(function (d) { return d.type }); + const node = svg // Nodes initialisieren + .selectAll("circle") + .data(data.nodes) + .enter() + .append("circle") .attr("r", rad) - .style( "fill", "white") - .style("stroke-width", nodeborder) - .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit - /*.append ("text") // name als text neben node - .attr ("dx", 12) - .attr ("dy", ".35em") - .text(function(d){return d.name})*/ + .on('mouseover', function(){ + d3.select(this).transition() + .duration('2') + .attr('r', rad+5) + }) + .on('mouseout', function(){ + d3.select(this).transition() + .attr('r', rad) + }) + .style("fill", "white") + .style("stroke-width", nodeborder) + .style("stroke", function (d) { return colors(d.group); }) // Node-Farben nach Gruppenzugehörigkeit + /*.append ("text") // name als text neben node + .attr ("dx", 12) + .attr ("dy", ".35em") + .text(function(d){return d.name})*/ .call(d3.drag() - .on("start", dragstarted) - .on("drag", dragged)); -; - - const text =svg + .on("start", dragstarted) + .on("drag", dragged)); + ; + + const text = svg .selectAll("text") .data(data.nodes) .enter() .append("text") - - .text(function(d){return d.name}); - const linkText =svg + .text(function (d) { return d.name }); + + const linkText = svg .selectAll("links") .data(data.links) .enter() .append("text") - .style("fill","#999" ) - .text(function (d){return d.type}); + .style("fill", "#999") + .text(function (d) { return d.type }); + - - - const simulation = d3.forceSimulation(data.nodes) //Simulation - .force("link", d3.forceLink() - .id(function(d) { return d.id; }) - .distance(150) - .links(data.links) - ) - .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen - .force("center", d3.forceCenter(width / 2, height / 2)) // Nodes mittig von svg - .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - - function ticked() { - text - .attr("dx", function(d) { - return Math.max(0+rad, Math.min(d.x+23, width-rad))}) - .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }) - linkText - .attr("dx", function(d){ - if ((d.target.x>width) || (d.source.x>width)) { - if (d.target.x>d.source.x) { - return Math.min(width,width-0.5*(width-d.source.x)); - } else { - return Math.min(width,width-0.5*(width-d.target.x)); - } ; - } else { - return Math.min(0.5*((Math.max(0,(d.target.x)))-(Math.max(0,(d.source.x))))+(Math.max(0,d.source.x))); - } - }) - //Relationstext bleibt am Link - .attr("dy", function(d){ - if ((d.target.y>height) || (d.source.y>height)) { - if (d.target.y>d.source.y) { - return Math.min(height,height-0.5*(height-d.source.y)); - } - else { - return Math.min(height,height-0.5*(height-d.target.y)); - } - } - else { - return (0.5*((Math.max(0,(d.target.y)))-(Math.max(0,(d.source.y))))+(Math.max(0,d.source.y))); - }}) - - link // Anfang und Ende der Links / Bestimmung der Position - .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)) }) // Postionen nur innerhalb des Fensters erlaubt - .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)) }) - .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)) }) - .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)) }); + const ticked = () => { + const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; + const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - node // Bestimmung der Postion einzelner Nodes - .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)) }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße - .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }); - } + // data.nodes.indexOf[0].fx= centerWidth; + //data.nodes.indexOf[0].fy= centerHeight; + text + .attr("dx", (d)=>{ + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) + .attr("dy", (d) => { + const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) - function ended() { - console.log("ended"); - } + linkText + .attr("dx", (d)=>{ + d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); + d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(width- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceX-relativeTargetX)/2)+ relativeTargetX))) +'%'; + }) + .attr("dy", (d)=>{ + d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; + }) - function dragstarted(d) { - if (!d3.event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } + link // Anfang und Ende der Links / Bestimmung der Position nachfolgend Closure-Funtktionen um Zugriff mit this. außerhalb der Funktion + .attr("x1", (d)=> { + d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // Umwandlung des Radius in relative Zahl (in % !) + const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX+'%';}) + .attr("y1", (d)=> { + d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY+'%';}) + .attr("x2", (d)=> { + d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX+'%';}) + .attr("y2", (d)=> { + d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY+'%';}) + + node // Bestimmung der Postion einzelner Nodes + .attr("cx", (d) => { + d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX+'%'; + }) // Zentrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", (d) => { + d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) + const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY+'%';}) + } + + const simulation = d3.forceSimulation(data.nodes) //Simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(300) + .links(data.links) + ) + .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes + + + + + .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - function dragged(d) { - console.log(d) + function ended() { + console.log("ended"); + } + function mouseover(){ + d3.select(this).select("circle").transition() + .duration(750) + .attr("r", rad+10) + } + + function mouseout(){ + d3.select(this).select("circle").transition() + .duration(750) + .attr("r", rad) + } + function dragstarted(d) { + if (!d3.event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + + console.log(d3.event) 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; } - - } + + } constructor() { } } From d3716e280690cba275a711780f22c1ecb98827d1 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:15:06 +0100 Subject: [PATCH 25/74] Added function to get data for ontology-visualization of each module --- .../graph-visualization.component.ts | 130 ++++++++++++++++-- .../graph-visualization.module.ts | 8 +- 2 files changed, 127 insertions(+), 11 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index b57ca058..2c46fd12 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -3,6 +3,8 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; +import { ModuleService } from 'app/shared/services/module.service'; + @Component({ selector: 'graph-visualization', @@ -12,11 +14,16 @@ import { window } from 'rxjs/operators'; }) export class GraphVisualizationComponent implements AfterContentInit { + constructor( + private moduleService: ModuleService + ) { + }; + @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - console.log('element ref width:'); - console.log(this.svgContainer.nativeElement.offsetWidth); + //console.log('element ref width:'); + // console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -25,7 +32,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - console.log("Die ermittelte Weite ist:" + testweite); + //console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -54,9 +61,12 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - const data = { + +// ##### DATA ###### + + + /*const data = { "nodes": [ { "id": 1, @@ -108,8 +118,107 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } +*/ +var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + var group=0; + var moduleId=0; + var capabilityId=100; + var inputId=1000; + var outputId=10000; + var skillId=100000; + var typeId=1000000; + var data= {nodes:[], links:[]}; + //################################################ + receivedData.forEach(modul => { + moduleId++; + group++; + data.nodes.push({ + "id" : moduleId, + "name": modul.name, + "group": 1 + }); + modul.capabilities.forEach(capability=>{ + capabilityId++; + data.nodes.push({ + "id" : capabilityId, + "name": capability.name, + "group": 2 + }) + data.links.push({ + "source": moduleId, + "target": capabilityId, + "type": "has_capability" + }) + capability.hasInput.forEach(input=>{ + inputId++; + typeId++; + data.nodes.push({ + "id" : inputId, + "name": input.name, + "group": 3 + }) + data.links.push({ + "source": capabilityId, + "target": inputId, + "type": "has_input" + }) + data.nodes.push({ + "id": typeId, + "name": input.stateType, + "group": 6 + }) + data.links.push({ + "source": inputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.hasOutput.forEach(output=>{ + outputId++; + typeId++; + data.nodes.push({ + "id" : outputId, + "name": output.name, + "group": 4 + }) + data.links.push({ + "source": capabilityId, + "target": outputId, + "type": "has_output" + }) + + data.nodes.push({ + "id": typeId, + "name": output.stateType, + "group": 6 + }) + data.links.push({ + "source": outputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.executableViaSkill.forEach(skill=>{ + skillId++; + data.nodes.push({ + "id" : skillId, + "name": skill.name, + "group": 5 + }) + data.links.push({ + "source": capabilityId, + "target": skillId, + "type": "executable_via_Skill" + }) + }) + }) + + }); +// ################################################## + + console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -246,7 +355,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -255,7 +364,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - console.log("ended"); + //console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -276,7 +385,7 @@ export class GraphVisualizationComponent implements AfterContentInit { function dragged(d) { - console.log(d3.event) + // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -287,6 +396,9 @@ export class GraphVisualizationComponent implements AfterContentInit { } } - constructor() { } + + + + } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index afb5fff0..df9c2713 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,11 +1,15 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; +import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter + GraphVisualizationRouter, ], - declarations: [GraphVisualizationComponent] + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + ] }) export class GraphVisualizationModule { } From d65778e4d304adcb544d32d755dd567195b16033 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:17:05 +0100 Subject: [PATCH 26/74] Revert "Added function to get data for ontology-visualization of each module" This reverts commit 8b9e53e21219f952dc96071abaab17af6acf8a40. --- .../graph-visualization.component.ts | 130 ++---------------- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 - 3 files changed, 11 insertions(+), 128 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 2c46fd12..b57ca058 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -3,8 +3,6 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; -import { ModuleService } from 'app/shared/services/module.service'; - @Component({ selector: 'graph-visualization', @@ -14,16 +12,11 @@ import { ModuleService } from 'app/shared/services/module.service'; }) export class GraphVisualizationComponent implements AfterContentInit { - constructor( - private moduleService: ModuleService - ) { - }; - @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - //console.log('element ref width:'); - // console.log(this.svgContainer.nativeElement.offsetWidth); + console.log('element ref width:'); + console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -32,7 +25,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - //console.log("Die ermittelte Weite ist:" + testweite); + console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -61,12 +54,9 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); + //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - -// ##### DATA ###### - - - /*const data = { + const data = { "nodes": [ { "id": 1, @@ -118,107 +108,8 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } -*/ -var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - var group=0; - var moduleId=0; - var capabilityId=100; - var inputId=1000; - var outputId=10000; - var skillId=100000; - var typeId=1000000; - var data= {nodes:[], links:[]}; - //################################################ - receivedData.forEach(modul => { - moduleId++; - group++; - data.nodes.push({ - "id" : moduleId, - "name": modul.name, - "group": 1 - }); - modul.capabilities.forEach(capability=>{ - capabilityId++; - data.nodes.push({ - "id" : capabilityId, - "name": capability.name, - "group": 2 - }) - data.links.push({ - "source": moduleId, - "target": capabilityId, - "type": "has_capability" - }) - capability.hasInput.forEach(input=>{ - inputId++; - typeId++; - data.nodes.push({ - "id" : inputId, - "name": input.name, - "group": 3 - }) - data.links.push({ - "source": capabilityId, - "target": inputId, - "type": "has_input" - }) - data.nodes.push({ - "id": typeId, - "name": input.stateType, - "group": 6 - }) - data.links.push({ - "source": inputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.hasOutput.forEach(output=>{ - outputId++; - typeId++; - data.nodes.push({ - "id" : outputId, - "name": output.name, - "group": 4 - }) - data.links.push({ - "source": capabilityId, - "target": outputId, - "type": "has_output" - }) - - data.nodes.push({ - "id": typeId, - "name": output.stateType, - "group": 6 - }) - data.links.push({ - "source": outputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.executableViaSkill.forEach(skill=>{ - skillId++; - data.nodes.push({ - "id" : skillId, - "name": skill.name, - "group": 5 - }) - data.links.push({ - "source": capabilityId, - "target": skillId, - "type": "executable_via_Skill" - }) - }) - }) - - }); -// ################################################## - - console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -355,7 +246,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -364,7 +255,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - //console.log("ended"); + console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -385,7 +276,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); function dragged(d) { - // console.log(d3.event) + console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -396,9 +287,6 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); } } - - - - + constructor() { } } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index df9c2713..afb5fff0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,15 +1,11 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter, + GraphVisualizationRouter ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - ] + declarations: [GraphVisualizationComponent] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index 5790705b..ae60048e 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,4 +1,3 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; - From 6a972f51f7b32cf427e0a8d980e3669bec658002 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:17:55 +0100 Subject: [PATCH 27/74] Revert "Revert "Added function to get data for ontology-visualization of each module"" This reverts commit e3e86ce7d5f9a64ca29b6727bcc3077f1b4ba03a. --- .../graph-visualization.component.ts | 130 ++++++++++++++++-- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 + 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index b57ca058..2c46fd12 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -3,6 +3,8 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; +import { ModuleService } from 'app/shared/services/module.service'; + @Component({ selector: 'graph-visualization', @@ -12,11 +14,16 @@ import { window } from 'rxjs/operators'; }) export class GraphVisualizationComponent implements AfterContentInit { + constructor( + private moduleService: ModuleService + ) { + }; + @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - console.log('element ref width:'); - console.log(this.svgContainer.nativeElement.offsetWidth); + //console.log('element ref width:'); + // console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -25,7 +32,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - console.log("Die ermittelte Weite ist:" + testweite); + //console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -54,9 +61,12 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - const data = { + +// ##### DATA ###### + + + /*const data = { "nodes": [ { "id": 1, @@ -108,8 +118,107 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } +*/ +var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + var group=0; + var moduleId=0; + var capabilityId=100; + var inputId=1000; + var outputId=10000; + var skillId=100000; + var typeId=1000000; + var data= {nodes:[], links:[]}; + //################################################ + receivedData.forEach(modul => { + moduleId++; + group++; + data.nodes.push({ + "id" : moduleId, + "name": modul.name, + "group": 1 + }); + modul.capabilities.forEach(capability=>{ + capabilityId++; + data.nodes.push({ + "id" : capabilityId, + "name": capability.name, + "group": 2 + }) + data.links.push({ + "source": moduleId, + "target": capabilityId, + "type": "has_capability" + }) + capability.hasInput.forEach(input=>{ + inputId++; + typeId++; + data.nodes.push({ + "id" : inputId, + "name": input.name, + "group": 3 + }) + data.links.push({ + "source": capabilityId, + "target": inputId, + "type": "has_input" + }) + data.nodes.push({ + "id": typeId, + "name": input.stateType, + "group": 6 + }) + data.links.push({ + "source": inputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.hasOutput.forEach(output=>{ + outputId++; + typeId++; + data.nodes.push({ + "id" : outputId, + "name": output.name, + "group": 4 + }) + data.links.push({ + "source": capabilityId, + "target": outputId, + "type": "has_output" + }) + + data.nodes.push({ + "id": typeId, + "name": output.stateType, + "group": 6 + }) + data.links.push({ + "source": outputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.executableViaSkill.forEach(skill=>{ + skillId++; + data.nodes.push({ + "id" : skillId, + "name": skill.name, + "group": 5 + }) + data.links.push({ + "source": capabilityId, + "target": skillId, + "type": "executable_via_Skill" + }) + }) + }) + + }); +// ################################################## + + console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -246,7 +355,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -255,7 +364,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - console.log("ended"); + //console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -276,7 +385,7 @@ export class GraphVisualizationComponent implements AfterContentInit { function dragged(d) { - console.log(d3.event) + // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -287,6 +396,9 @@ export class GraphVisualizationComponent implements AfterContentInit { } } - constructor() { } + + + + } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index afb5fff0..df9c2713 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,11 +1,15 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; +import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter + GraphVisualizationRouter, ], - declarations: [GraphVisualizationComponent] + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index ae60048e..5790705b 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,3 +1,4 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; + From 50250d29295c5806545ddffedbd9643ee9101f06 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Sun, 29 Mar 2020 14:18:25 +0200 Subject: [PATCH 28/74] Modified node-ID assignment. Modified comments --- .../graph-visualization.component.ts | 345 +++++++----------- 1 file changed, 140 insertions(+), 205 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 2c46fd12..87a1ef6a 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -22,19 +22,14 @@ export class GraphVisualizationComponent implements AfterContentInit { @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - //console.log('element ref width:'); - // console.log(this.svgContainer.nativeElement.offsetWidth); - - const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen - const width = 100; //parent.innerWidth - margin.left - margin.right; + const margin = { top: 10, right: 30, bottom: 30, left: 40 }; + const width = 100; //parent.innerWidth - margin.left - margin.right; //var width= window.innerWidth; const height = 100; - const rad = 20 // Radius eines Knotens - const nodeborder = 8; // Umrandungsdicke eines Knotens + const rad = 20 // node radius + const nodeborder = 8; // node border thickness const testweite = parent.innerWidth; - //console.log("Die ermittelte Weite ist:" + testweite); - - const svg = d3.select("#graph") // Größe u. Grenzen SVG + const svg = d3.select("#graph") // size definitions for the svg .append("svg") .attr("width", width + '%') .attr("height", height+'%') @@ -44,12 +39,12 @@ export class GraphVisualizationComponent implements AfterContentInit { - const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + const colors = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours svg.append('defs').append('marker') .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //Koordinatensystem - .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('viewBox', '-0 -5 10 10') //coordinate system + .attr('refX', rad + nodeborder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -63,193 +58,141 @@ export class GraphVisualizationComponent implements AfterContentInit { -// ##### DATA ###### - - - /*const data = { - "nodes": [ - { - "id": 1, - "name": "ModulA", - "group": 1 - }, - { - "id": 2, - "name": "ModulB", - "group": 2 - }, - { - "id": 3, - "name": "ModulC", - "group": 3 - - }, { - "id": 4, - "name": "cap11", - "group": 1 - }, - { - "id": 5, - "name": "cap12", - "group": 1 - }, - { - "id": 6, - "name": "cap21", - "group": 2 - } - ], - "links": [ - - { - "source": 1, - "target": 4, - "type": "is_a" - }, - { - "source": 1, - "target": 5, - "type": "has" - }, - { - "source": 2, - "target": 6, - "type": "has" - } - ] - } -*/ +/* Get data*/ +//########################################################## var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - var group=0; - var moduleId=0; - var capabilityId=100; - var inputId=1000; - var outputId=10000; - var skillId=100000; - var typeId=1000000; - var data= {nodes:[], links:[]}; - //################################################ - receivedData.forEach(modul => { - moduleId++; - group++; - data.nodes.push({ - "id" : moduleId, - "name": modul.name, - "group": 1 - }); - modul.capabilities.forEach(capability=>{ - capabilityId++; - data.nodes.push({ - "id" : capabilityId, - "name": capability.name, - "group": 2 - }) - data.links.push({ - "source": moduleId, - "target": capabilityId, - "type": "has_capability" - }) - capability.hasInput.forEach(input=>{ - inputId++; - typeId++; - data.nodes.push({ - "id" : inputId, - "name": input.name, - "group": 3 - }) - data.links.push({ - "source": capabilityId, - "target": inputId, - "type": "has_input" - }) - data.nodes.push({ - "id": typeId, - "name": input.stateType, - "group": 6 - }) - data.links.push({ - "source": inputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.hasOutput.forEach(output=>{ - outputId++; - typeId++; - data.nodes.push({ - "id" : outputId, - "name": output.name, - "group": 4 - }) - data.links.push({ - "source": capabilityId, - "target": outputId, - "type": "has_output" - }) - - data.nodes.push({ - "id": typeId, - "name": output.stateType, - "group": 6 - }) - data.links.push({ - "source": outputId, - "target": typeId, - "type": "is_state_type" - }) - - }) - capability.executableViaSkill.forEach(skill=>{ - skillId++; - data.nodes.push({ - "id" : skillId, - "name": skill.name, - "group": 5 - }) - data.links.push({ - "source": capabilityId, - "target": skillId, - "type": "executable_via_Skill" - }) - }) - - }) +var data= {nodes:[], links:[]}; +let idMap= new Map(); // Map for saving Node-IDs +var id=0; // ID start value +receivedData.forEach(receivedModule => { //loop over all modules +id++; +idMap.set(receivedModule, id); // assigns an ID for each module-node +data.nodes.push({ // adds a node for each module +"id" : idMap.get(receivedModule), +"name": receivedModule.name, +"group": 1 // assings node-groups (here: node colour) +}); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.name, + "group": 2 + }) + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }) + + capability.hasInput.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.name, + "group": 3 + }) + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }) + }) + + capability.hasOutput.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.name, + "group": 4 + }) + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }) + }) + + capability.executableViaSkill.forEach(skill=>{ + id++; + idMap.set(skill, id) // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.name, + "group": 5 + }) + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }) + }) + + }) - }); +}); // ################################################## console.log(data); - const link = svg // Links initialisieren + const link = svg // link definitions .selectAll(".links") .data(data.links) .enter() .append("line") .style("stroke", "#aaa") .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') + .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; link.append("title") - .text(function (d) { return d.type }); - const node = svg // Nodes initialisieren + .text(function (d) { return d.type }); // get link title from data + const node = svg // node definitions .selectAll("circle") .data(data.nodes) .enter() .append("circle") .attr("r", rad) - .on('mouseover', function(){ + .on('mouseover', function(){ // function on mousover: increase radius d3.select(this).transition() .duration('2') - .attr('r', rad+5) + .attr('r', rad+6) }) - .on('mouseout', function(){ + .on('mouseout', function(){ // function on mousout: decrease radius to orgin d3.select(this).transition() .attr('r', rad) }) + .on('dblclick', doubleclick)// function on doubleclick: + .style("fill", "white") .style("stroke-width", nodeborder) - .style("stroke", function (d) { return colors(d.group); }) // Node-Farben nach Gruppenzugehörigkeit - /*.append ("text") // name als text neben node + .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group + /*.append ("text") .attr ("dx", 12) .attr ("dy", ".35em") .text(function(d){return d.name})*/ @@ -258,21 +201,21 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .on("drag", dragged)); ; - const text = svg + const text = svg // node text definitions .selectAll("text") .data(data.nodes) .enter() .append("text") - .text(function (d) { return d.name }); + .text(function (d) { return d.name }); // get text from data - const linkText = svg + const linkText = svg // link text definitions .selectAll("links") .data(data.links) .enter() .append("text") .style("fill", "#999") - .text(function (d) { return d.type }); + .text(function (d) { return d.type }); // get link text from data @@ -280,13 +223,11 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const ticked = () => { - const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; + const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; // center definitions of the svg const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - // data.nodes.indexOf[0].fx= centerWidth; - //data.nodes.indexOf[0].fy= centerHeight; text - .attr("dx", (d)=>{ + .attr("dx", (d)=>{ // restrictions for node text positions in the svg const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) @@ -296,7 +237,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) linkText - .attr("dx", (d)=>{ + .attr("dx", (d)=>{ // restrictions for link text positions in the svg d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; @@ -313,10 +254,10 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; }) - link // Anfang und Ende der Links / Bestimmung der Position nachfolgend Closure-Funtktionen um Zugriff mit this. außerhalb der Funktion - .attr("x1", (d)=> { + link + .attr("x1", (d)=> { // restrictions for link positions in the svg d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // Umwandlung des Radius in relative Zahl (in % !) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // converting radius from absolute to relative const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; return relativeSourceX+'%';}) .attr("y1", (d)=> { @@ -335,13 +276,13 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; return relativeTargetY+'%';}) - node // Bestimmung der Postion einzelner Nodes - .attr("cx", (d) => { + node + .attr("cx", (d) => { // restrictions for node positions in the svg d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; return relativeDragX+'%'; - }) // Zentrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + }) .attr("cy", (d) => { d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; @@ -349,39 +290,33 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return relativeDragY+'%';}) } - const simulation = d3.forceSimulation(data.nodes) //Simulation + const simulation = d3.forceSimulation(data.nodes) // simulation .force("link", d3.forceLink() .id(function (d) { return d.id; }) .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes - .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - - function ended() { - //console.log("ended"); - } - function mouseover(){ - d3.select(this).select("circle").transition() - .duration(750) - .attr("r", rad+10) - } + .on("tick", ticked); // tick on every step of the simulation - function mouseout(){ - d3.select(this).select("circle").transition() - .duration(750) - .attr("r", rad) - } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } + function doubleclick(){ + id++; + data.nodes.push({ + "id": id, + "name": "neighbor", + "group": 7 + }) + } function dragged(d) { From 4004e02916782cab695dd79313974712b24e51ca Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Thu, 2 Apr 2020 20:51:41 +0200 Subject: [PATCH 29/74] Added new service for node creation --- .../graph-visualization.component.ts | 139 ++-------- .../graph-visualization.module.ts | 3 + .../node-creator.service.ts | 241 ++++++++---------- 3 files changed, 137 insertions(+), 246 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 87a1ef6a..2b712bc0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -4,6 +4,8 @@ import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; import { ModuleService } from 'app/shared/services/module.service'; +import { ResolveStart } from '@angular/router'; +import { NodeCreatorService } from './node-creator.service'; @Component({ @@ -15,7 +17,8 @@ import { ModuleService } from 'app/shared/services/module.service'; export class GraphVisualizationComponent implements AfterContentInit { constructor( - private moduleService: ModuleService + //private moduleService: ModuleService, + private nodeCreatorService: NodeCreatorService ) { }; @@ -56,108 +59,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - - -/* Get data*/ -//########################################################## -var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); -var data= {nodes:[], links:[]}; -let idMap= new Map(); // Map for saving Node-IDs -var id=0; // ID start value -receivedData.forEach(receivedModule => { //loop over all modules -id++; -idMap.set(receivedModule, id); // assigns an ID for each module-node -data.nodes.push({ // adds a node for each module -"id" : idMap.get(receivedModule), -"name": receivedModule.name, -"group": 1 // assings node-groups (here: node colour) -}); - - receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module - id++; - idMap.set(capability, id); // assigns an ID for each capability-node - data.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.name, - "group": 2 - }) - data.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description - }) - - capability.hasInput.forEach(input=>{ - id++; - idMap.set(input, id); // assigns an ID for each input-node - data.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.name, - "group": 3 - }) - data.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }) - }) - - capability.hasOutput.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - data.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.name, - "group": 4 - }) - data.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }) - }) - - capability.executableViaSkill.forEach(skill=>{ - id++; - idMap.set(skill, id) // adds a node for each skill of current capability - data.nodes.push({ - "id" : idMap.get(skill), - "name": skill.name, - "group": 5 - }) - data.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" - }) - }) - - }) - -}); -// ################################################## +var data= this.nodeCreatorService.getAllNodes(); // load data created by node-creator.service console.log(data); @@ -187,8 +89,17 @@ data.nodes.push({ // adds a node for each module d3.select(this).transition() .attr('r', rad) }) - .on('dblclick', doubleclick)// function on doubleclick: - + .on('dblclick', function(d){ const node= { "id":30, + "name": "neighbor", + "group": 7} + data.nodes.push(node) + data.links.push({ + "source": d.id, + "target":30 + }) + + console.log(d);// function on doubleclick: + }) .style("fill", "white") .style("stroke-width", nodeborder) .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group @@ -309,15 +220,19 @@ data.nodes.push({ // adds a node for each module d.fx = d.x; d.fy = d.y; } - function doubleclick(){ - id++; - data.nodes.push({ - "id": id, - "name": "neighbor", - "group": 7 + /*function doubleclick(d){ + + const node= {x: 500, y:50, "id":1000, + "name": "neighbor", + "group": 7} + data.nodes.push(node) + data.links.push({ + "source": d.id, + "target":1000 }) + console.log(d); } - +*/ function dragged(d) { // console.log(d3.event) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index df9c2713..711024bc 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; import { ModuleService } from 'app/shared/services/module.service'; +import { NodeCreatorService } from './node-creator.service'; @NgModule({ imports: [ @@ -10,6 +11,8 @@ import { ModuleService } from 'app/shared/services/module.service'; declarations: [GraphVisualizationComponent], providers: [ ModuleService, + NodeCreatorService + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts index b000a9aa..50422f64 100644 --- a/frontend/src/modules/graph-visualization/node-creator.service.ts +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -1,142 +1,116 @@ import { Injectable } from '@angular/core'; -import { Capability } from '@shared/models/capability/Capability'; -import { ProductionModule } from '@shared/models/production-module/ProductionModule'; -import { Skill } from '@shared/models/skill/Skill'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { CapabilityService } from 'src/shared/services/capability.service'; -import { SkillService } from 'src/shared/services/skill.service'; -import { ModuleService } from '../../shared/services/module.service'; -import { D3CapabilityNode, D3GraphData, D3Link, D3ModuleNode, D3Node, D3SkillNode, NodeType } from './D3GraphData'; - +import { ModuleService } from 'app/shared/services/module.service'; @Injectable({ providedIn: 'root' }) export class NodeCreatorService { - apiRoot = "/api"; - modules: ProductionModule[] - - constructor( - private moduleService: ModuleService, - private skillService: SkillService, - private capabilityService: CapabilityService) { } - - // /** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ - // getAllNodes(moduleIri: string): Observable { - - // return this.moduleService.getAllModules().pipe(map(modules => { - // // If a module name is passed, filter the module list for that specific one - // if(moduleIri != undefined) { - // modules = modules.filter(module => module.iri == moduleIri); - // } - // return this.getDataFromModules(modules); - // })); - // } - - getAllModuleNodes(moduleIri: string): Observable { - - return this.moduleService.getAllModules().pipe(map(modules => { - // If a module IRI is passed, filter the module list for that specific one - if(moduleIri != undefined && moduleIri != "") { - console.log("filtering"); - console.log("moduleIri: '" + moduleIri + "'"); - - - modules = modules.filter(module => module.iri == moduleIri); - } - - console.log("modules"); - console.log(modules); - - - - const graphData = new D3GraphData(); - modules.forEach(module => { //loop over all modules - - graphData.addNode(new D3ModuleNode(module.iri, module.getLocalName())); - // TODO: Add module components - - const skillData = this.getDataFromSkills(module.skills); - graphData.appendAndConnectData(skillData, module.iri, NodeType.D3SkillNode, "hasSkill"); - - const capabilityData = this.getDataFromCapabilities(module.capabilities); - graphData.appendAndConnectData(capabilityData, module.iri, NodeType.D3CapabilityNode, "hasCapability"); - }); - return graphData; - - })); - } - - getAllSkillNodes(skillIri: string): Observable { - return this.skillService.getAllSkills().pipe(map(skills => { - // If a skill IRI is passed, filter the skill list for that specific one - if(skillIri != undefined && skillIri != "") { - skills = skills.filter(skill => skill.iri == skillIri); - } - - return this.getDataFromSkills(skills); - })); - } - - getAllCapabilityNodes(capabilityIri: string): Observable { - return this.capabilityService.getAllCapabilities().pipe(map(capabilities => { - // If a capability IRI is passed, filter the capability list for that specific one - if(capabilityIri != undefined && capabilityIri != "") { - capabilities = capabilities.filter(capability => capability.iri == capabilityIri); - } - - return this.getDataFromCapabilities(capabilities); - })); - } - - - private getDataFromSkills(skills: Skill[]): D3GraphData { - const graphData = new D3GraphData(); - skills.forEach(skill => { - - graphData.addNode(new D3SkillNode(skill.iri, skill.getLocalName())); - - // Add state machine - graphData.addNode(new D3Node(skill.stateMachine.iri, skill.stateMachine.getLocalName(), 120)); - graphData.addLink(new D3Link(skill.iri, skill.stateMachine.iri, "hasStateMachine")); - - // Add skill parameters - skill.skillParameters.forEach(parameter => { - graphData.addNode(new D3Node(parameter.iri, parameter.getLocalName(), 140)); - graphData.addLink(new D3Link(skill.iri, parameter.iri, "hasSkillParameter")); - }); - - // Add skill outputs - skill.skillOutputs.forEach(output => { - graphData.addNode(new D3Node(output.iri,output.getLocalName(), 160)); - graphData.addLink(new D3Link(skill.iri, output.iri, "hasSkillOutput")); - }); - }); - return graphData; - } - - private getDataFromCapabilities(capabilities: Capability[]): D3GraphData { - const graphData = new D3GraphData(); - - capabilities.forEach(capability => { //loop over all capabilities of current module - graphData.addNode(new D3CapabilityNode(capability.iri, capability.getLocalName())); - - capability.inputs.forEach(input => { - graphData.addNode(new D3Node(input.iri, input.getLocalName(), 240)); - graphData.addLink(new D3Link(capability.iri, input.iri, "hasInput")); - - }); - - capability.outputs.forEach(output => { - graphData.addNode(new D3Node(output.iri, output.getLocalName(), 270)); - graphData.addLink(new D3Link(capability.iri, output.iri, "hasOutput")); - }); - }); - return graphData; - } - - + apiRoot = "/api"; + constructor( + private moduleService: ModuleService + ) {} + getAllNodes() { + + const receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + const data= {nodes:[], links:[]}; + const idMap= new Map(); // Map for saving Node-IDs + let id=0; // ID start value + receivedData.forEach(receivedModule => { //loop over all modules + id++; + idMap.set(receivedModule, id); // assigns an ID for each module-node + data.nodes.push({ // adds a node for each module + "id" : idMap.get(receivedModule), + "name": receivedModule.name, + "group": 1 // assings node-groups (here: node colour) + }); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.name, + "group": 2 + }); + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }); + + capability.hasInput.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.name, + "group": 3 + }); + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }); + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.stateType, + "group": 6 + }); + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }); + }); + + capability.hasOutput.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.name, + "group": 4 + }); + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }); + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.stateType, + "group": 6 + }); + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }); + }); + + capability.executableViaSkill.forEach(skill=>{ + id++; + idMap.set(skill, id); // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.name, + "group": 5 + }); + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }); + }); + + }); + + }); + + return data; + } } @@ -146,4 +120,3 @@ export class NodeCreatorService { - From 1ded15dc56cbd54b3c9c4bde15865118527a8d7d Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 1 May 2020 15:41:52 +0200 Subject: [PATCH 30/74] Visualization of chosen module-graph possible. --- .../graph-visualization.component.ts | 35 +++++++++++++++---- .../graph-visualization.routing.ts | 12 +++---- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 2b712bc0..5a918e16 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,11 +1,15 @@ -import { Component, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component,OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; import { ModuleService } from 'app/shared/services/module.service'; -import { ResolveStart } from '@angular/router'; +import { ResolveStart, ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; +import { getModuleFactory__POST_R3__ } from '@angular/core/src/linker/ng_module_factory_loader'; +import { defineDirective } from '@angular/core/src/render3'; + + @Component({ @@ -14,15 +18,30 @@ import { NodeCreatorService } from './node-creator.service'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit { +export class GraphVisualizationComponent implements AfterContentInit, OnInit {name:String; constructor( - //private moduleService: ModuleService, + private route: ActivatedRoute, private nodeCreatorService: NodeCreatorService ) { }; @ViewChild('g') svgContainer: ElementRef; + moduleName: string; + ngOnInit(): void + { + + + // console.log("Der Name ist :"+this.moduleName); + +//console.log(this.route.queryParams); + + //.subscribe(params=>params.name) + + //this.name= params.name; + + } + ngAfterContentInit(): void { const margin = { top: 10, right: 30, bottom: 30, left: 40 }; @@ -58,10 +77,11 @@ export class GraphVisualizationComponent implements AfterContentInit { .attr('fill', '#999') .style('stroke', 'none'); +this.route.params.subscribe(p=>{ + this.moduleName=p['moduleName']; +}) +var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service -var data= this.nodeCreatorService.getAllNodes(); // load data created by node-creator.service - - console.log(data); const link = svg // link definitions .selectAll(".links") @@ -246,6 +266,7 @@ var data= this.nodeCreatorService.getAllNodes(); // load data created by node-cr } } + diff --git a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts index ded3fe38..ba430b6c 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts @@ -1,15 +1,13 @@ import { Routes, RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import {GraphVisualizationComponent} from './graph-visualization.component' +import {GraphVisualizationComponent} from './graph-visualization.component'; const routes: Routes = [ - { path: '', + { path: 'modules/:moduleName', component: GraphVisualizationComponent, - data: { - title: 'Graph-Visualization' - }, - children: [] - + + }, + {path:'modules', component: GraphVisualizationComponent } ]; From 2904f5be2b1f100784a6bd9e23b4b1309042e20c Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 1 May 2020 15:50:51 +0200 Subject: [PATCH 31/74] Modified some stylings --- .../graph-visualization.component.ts | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 5a918e16..b705e793 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -99,28 +99,16 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .data(data.nodes) .enter() .append("circle") - .attr("r", rad) - .on('mouseover', function(){ // function on mousover: increase radius - d3.select(this).transition() - .duration('2') - .attr('r', rad+6) + .attr("r",function(d){ + if(d.group==1){return rad+8} + else{return rad} }) - .on('mouseout', function(){ // function on mousout: decrease radius to orgin - d3.select(this).transition() - .attr('r', rad) - }) - .on('dblclick', function(d){ const node= { "id":30, - "name": "neighbor", - "group": 7} - data.nodes.push(node) - data.links.push({ - "source": d.id, - "target":30 + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .style("fill", function(d){ + if(d.group==1){return "black"} + else{return "whitesmoke"} }) - - console.log(d);// function on doubleclick: - }) - .style("fill", "white") .style("stroke-width", nodeborder) .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group /*.append ("text") @@ -137,7 +125,10 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .data(data.nodes) .enter() .append("text") - + .attr("font-weight", function(d){ + if(d.group==1){return "bold"} + else{return "normal"} + }) .text(function (d) { return d.name }); // get text from data const linkText = svg // link text definitions @@ -146,6 +137,7 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .enter() .append("text") .style("fill", "#999") + .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data @@ -265,6 +257,25 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre //d.fy = null; } + function mouseover(d){ // function on mousover: increase radius + if(d.group==1){ + d3.select(this).transition() + .duration('2') + .attr('r', rad+12)} + else{ + d3.select(this).transition() + .duration('2') + .attr('r', rad+6)} + } + function mouseout(d){ // function on mousout: decrease radius to orgin + if(d.group==1){ + d3.select(this).transition() + .attr('r', rad+8) + } + else{ d3.select(this).transition() + .attr('r', rad)} + } + } From 9715b93ceea8fe3a37e585e16f60efa9cdcac3f9 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Wed, 27 May 2020 21:17:44 +0200 Subject: [PATCH 32/74] Adding nodes on doubleclick possible --- .../graph-visualization.component.ts | 569 +++++++++++------- 1 file changed, 363 insertions(+), 206 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index b705e793..0cd91854 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,13 +1,11 @@ -import { Component,OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" -import { enterView } from '@angular/core/src/render3/state'; -import { pathToFileURL } from 'url'; -import { window } from 'rxjs/operators'; -import { ModuleService } from 'app/shared/services/module.service'; -import { ResolveStart, ActivatedRoute } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; -import { getModuleFactory__POST_R3__ } from '@angular/core/src/linker/ng_module_factory_loader'; -import { defineDirective } from '@angular/core/src/render3'; + + + + @@ -18,55 +16,76 @@ import { defineDirective } from '@angular/core/src/render3'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit, OnInit {name:String; +export class GraphVisualizationComponent implements AfterContentInit, OnInit { + name: String; + svg: any; + link: any; + /** The displayed node name*/ + text: any; + /** The displayed link description */ + linkText: any; + /** The displayed node */ + node: any; + /** Loaded data: Connected modules with their capabilities and skills*/ + data: any; + /**Returns different colors automatically. The node-border color is the same for all node-group members*/ + color: any; +/**Simulation of the force directed graph */ + simulation: any; + + margin = { top: 10, right: 30, bottom: 30, left: 40 }; + width = 100; + height = 100; +/**Defines the nodeborder thickness*/ + nodeborder = 8; + /**Defines the standard radius of a node*/ + rad = 20; + + index=0; constructor( private route: ActivatedRoute, private nodeCreatorService: NodeCreatorService - ) { + ) { }; @ViewChild('g') svgContainer: ElementRef; moduleName: string; - ngOnInit(): void - { - + ngOnInit(): void { - // console.log("Der Name ist :"+this.moduleName); -//console.log(this.route.queryParams); + // console.log("Der Name ist :"+this.moduleName); + + //console.log(this.route.queryParams); //.subscribe(params=>params.name) - + //this.name= params.name; } ngAfterContentInit(): void { - const margin = { top: 10, right: 30, bottom: 30, left: 40 }; - const width = 100; //parent.innerWidth - margin.left - margin.right; - //var width= window.innerWidth; - const height = 100; - const rad = 20 // node radius - const nodeborder = 8; // node border thickness - const testweite = parent.innerWidth; - const svg = d3.select("#graph") // size definitions for the svg + + console.log(this.svgContainer); + + + this.svg = d3.select("#graph") // size definitions for the svg .append("svg") - .attr("width", width + '%') - .attr("height", height+'%') + .attr("width", this.width + '%') + .attr("height", this.height + '%') .append("g") .attr("transform", - "translate(" + margin.left + "," + margin.top + ")"); + "translate(" + this.margin.left + "," + this.margin.top + ")"); - const colors = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours + this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - svg.append('defs').append('marker') + this.svg.append('defs').append('marker') .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', rad + nodeborder) // arrow position and dimensions + .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -77,210 +96,348 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit {na .attr('fill', '#999') .style('stroke', 'none'); -this.route.params.subscribe(p=>{ - this.moduleName=p['moduleName']; -}) -var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }) + this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + + + //############################################################################################################## + + this.simulation = d3.forceSimulation(this.data.nodes); + // simulation.force('link').links(data.links); // simulation + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links) + ) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - const link = svg // link definitions - .selectAll(".links") - .data(data.links) - .enter() - .append("line") - .style("stroke", "#aaa") + this.refresh(); + } + + /** + * Draws the graph, executed first on AfterContentInit and on every doubleclick on a node + */ + refresh() { + console.log("refresh starting..") + console.log(this.data.nodes) + + + /* this.simulation.force("link").links(this.data.links); + this.simulation.force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links) + + + )*/ +//this.link.exit().remove(); +//this.node.exit().remove(); + + + this.link = this.svg // link definitions + .selectAll(".link") + .data(this.data.links, function (d){return d.target.id}) + /*.join(enter => enter.append("line") + .append("line").style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)'), // arrow on end of the link + exit => exit.remove())*/ + + var linkEnter=this.link.enter() + .append("line").style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; - link.append("title") - .text(function (d) { return d.type }); // get link title from data - const node = svg // node definitions - .selectAll("circle") - .data(data.nodes) - .enter() - .append("circle") - .attr("r",function(d){ - if(d.group==1){return rad+8} - else{return rad} + linkEnter.append("title") + .text(function (d){return d.type}) + this.link = this.link.merge(linkEnter); + this.link.exit().remove(); + + this.node = this.svg.selectAll(".node") + .data(this.data.nodes, function (d){return d.id}) + .join(enter => enter.append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8 } + else { return this.rad } + }) + .on('mouseover', this.mouseover) + .on('mouseout', this.mouseout) + .style("fill", function (d) { + if (d.group == 1) { return "black" } + else { return "whitesmoke" } + }) + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }) + .on('dblclick', this.nodeDoubleClick) + .call(d3.drag() + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)), + update => update.style("fill", "red"), + exit => exit.remove()) + + + /*var nodeEnter= this.node.enter().append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8 } + else { return this.rad } }) - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .style("fill", function(d){ - if(d.group==1){return "black"} - else{return "whitesmoke"} + /*nodeEnter.append("title") + .text(function(d){return d.id}) + nodeEnter.append("text") + .text(function (d){return d.name})*/ + /* nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseout', this.mouseout); + nodeEnter.style("fill", function (d) { + if (d.group == 1) { return "black" } + else { return "whitesmoke" } }) - .style("stroke-width", nodeborder) - .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group /*.append ("text") .attr ("dx", 12) .attr ("dy", ".35em") .text(function(d){return d.name})*/ - .call(d3.drag() - .on("start", dragstarted) - .on("drag", dragged)); - ; - - const text = svg // node text definitions - .selectAll("text") - .data(data.nodes) - .enter() - .append("text") - .attr("font-weight", function(d){ - if(d.group==1){return "bold"} - else{return "normal"} - }) - .text(function (d) { return d.name }); // get text from data +/* + nodeEnter.on('dblclick', this.nodeDoubleClick); + nodeEnter.call(d3.drag() + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)); + this.node=this.node.merge(nodeEnter); + this.node.exit().remove(); + + console.log(this.node) + */ + + this.text = this.svg.selectAll("text").data(this.data.nodes) + /*.join(enter=> enter.append("text") + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold" } else { return "normal" }}) + .text(function (d) { console.log(d.name);return d.name }), + exit=> exit.remove() )*/ - const linkText = svg // link text definitions - .selectAll("links") - .data(data.links) - .enter() - .append("text") + + var textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name}) + + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold" } + else { return "normal" } + }) + .text(function (d) { console.log(d.name); + return d.name }); // get text from data + this.text=this.text.merge(textEnter); +//this.text.exit().remove(); + + this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) + .join(enter=> enter.append("text") + .style("fill", "#999") + .attr("font-style", "italic") + .text(function (d) { return d.type }), + exit=> exit.remove()) + + + + + /* var linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data + this.linkText=this.linkText.merge(linkTextEnter); +//this.linkText.exit().remove(); +*/ + + this.simulation.nodes(this.data.nodes); + this.simulation.force("link").links(this.data.links); + //########################################################################################################### + } +/** + * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element + * @param d The position of every graph element (i.e. node, link...) + */ + ticked = () => { + const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg + const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; + + + this.text + .attr("dx", (d) => { // restrictions for node text positions in the svg + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%' + }) + .attr("dy", (d) => { + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%' + }) + this.linkText + .attr("dx", (d) => { // restrictions for link text positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; + }) + .attr("dy", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; + }) - + this.link + .attr("x1", (d) => { // restrictions for link positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX + '%'; + }) + .attr("y1", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY + '%'; + }) + .attr("x2", (d) => { + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX + '%'; + }) + .attr("y2", (d) => { + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY + '%'; + }) + this.node + .attr("cx", (d) => { + d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX + '%'; + }) + .attr("cy", (d) => { + d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY + '%'; + }) + } - const ticked = () => { - const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; // center definitions of the svg - const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - - text - .attr("dx", (d)=>{ // restrictions for node text positions in the svg - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) - .attr("dy", (d) => { - const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) - - linkText - .attr("dx", (d)=>{ // restrictions for link text positions in the svg - d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(width- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceX-relativeTargetX)/2)+ relativeTargetX))) +'%'; - }) - .attr("dy", (d)=>{ - d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; - }) - - link - .attr("x1", (d)=> { // restrictions for link positions in the svg - d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // converting radius from absolute to relative - const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX+'%';}) - .attr("y1", (d)=> { - d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY+'%';}) - .attr("x2", (d)=> { - d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX+'%';}) - .attr("y2", (d)=> { - d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY+'%';}) - - node - .attr("cx", (d) => { // restrictions for node positions in the svg - d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX+'%'; - }) - .attr("cy", (d) => { - d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) - const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY+'%';}) - } - const simulation = d3.forceSimulation(data.nodes) // simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(300) - .links(data.links) - ) - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes - + dragstarted = (d) => { + if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } - - - .on("tick", ticked); // tick on every step of the simulation - function dragstarted(d) { - if (!d3.event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - /*function doubleclick(d){ - - const node= {x: 500, y:50, "id":1000, - "name": "neighbor", - "group": 7} - data.nodes.push(node) - data.links.push({ - "source": d.id, - "target":1000 - }) - console.log(d); - } -*/ - function dragged(d) { - // console.log(d3.event) - 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; - } + /** + * Drag function, executed on every drag movement + * @param d The dragged node + */ + dragged(d) { + // console.log(d3.event) + d.fx = d3.event.x; + d.fy = d3.event.y; + } - function mouseover(d){ // function on mousover: increase radius - if(d.group==1){ - d3.select(this).transition() + dragended(d) { + //if (!d3.event.active) this.simulation.alphaTarget(0); + //d.fx = null; + // d.fy = null; + } +/** + * Mouseover function, executed when mouse over node. The radius of the node will increase. + * @param d node under the cursor + */ + mouseover(d) { + if (d.group == 1) { + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 12) + } + else { + d3.select(this).transition() .duration('2') - .attr('r', rad+12)} - else{ + .attr('r', 20 + 6) + } + } +/** + * Mousout function, executed when mouse cursor leaves the node + * @param d node wich is left by the cursor after mouseover + */ + mouseout(d) { // function on mousout: decrease this.radius to orgin + if (d.group == 1) { d3.select(this).transition() - .duration('2') - .attr('r', rad+6)} + .attr('r', 20 + 8) } - function mouseout(d){ // function on mousout: decrease radius to orgin - if(d.group==1){ - d3.select(this).transition() - .attr('r', rad+8) - } - else{ d3.select(this).transition() - .attr('r', rad)} + else { + d3.select(this).transition() + .attr('r', 20) } - + } +/** + * Doubleclick function, executed on every doubleclick on a node + *@param d The clicked node + */ + + nodeDoubleClick=(d)=> { +//this.simulation.stop(); + this.index++; + + const newNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(newNode); + this.data.links.push({ "source": d.id, "target": newNode, "type": "testclick" }) + /* + this.link.exit().remove(); + this.linkText.exit().remove(); + this.text.exit().remove(); */ + /*this.simulation.force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + + // ) + // .force("collision", d3.forceCollide(40)) + // .on("tick", this.ticked) // tick on every ste + -400)) // node magnetism /attraction + //.force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) + //simulation.restart(); //node oben links + fehlermeldungen + // this.simulation.nodes(this.data.nodes) + // this.simulation.force('link').links(this.data.links) + //simulation.force('nodes').nodes(data.nodes) + //node.data(data.nodes); + //link.data(data.links); + //node.enter().append("circle"); + //this.node.exit().remove(); + //link.exit().remove(); + //linkText.exit().remove(); + //text.exit().remove(); + // this.node = this.node.data(this.data.nodes); + //link= link.data(data.links) + //node.selectAll('circle').remove(); + //this.simulation.restart(); */ + this.refresh(); + // this.simulation.restart(); + //console.log(this.data.nodes) } - - - - - -} +} \ No newline at end of file From e0981898d79f22a2603c5c6a3ee6f9cef0c8505c Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 2 Jun 2020 18:50:48 +0200 Subject: [PATCH 33/74] Modified some code --- .../graph-visualization.component.ts | 271 +++++------------- 1 file changed, 78 insertions(+), 193 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 0cd91854..7a368116 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -4,12 +4,6 @@ import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; - - - - - - @Component({ selector: 'graph-visualization', encapsulation: ViewEncapsulation.None, @@ -33,6 +27,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { /**Simulation of the force directed graph */ simulation: any; + margin = { top: 10, right: 30, bottom: 30, left: 40 }; width = 100; height = 100; @@ -51,24 +46,42 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { @ViewChild('g') svgContainer: ElementRef; moduleName: string; - ngOnInit(): void { - + ngOnInit(): void {} - // console.log("Der Name ist :"+this.moduleName); - //console.log(this.route.queryParams); - - //.subscribe(params=>params.name) + ngAfterContentInit(): void { + + + + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }) + this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + + this.setSvg(); + this.setSimulation(); + } - //this.name= params.name; +/**Defines settings of the simulation */ +setSimulation(){ + this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - } + this.refreshSimulation(); +} - ngAfterContentInit(): void { - console.log(this.svgContainer); - +/**Defines settings of the SVG and the color-scheme of the nodes */ + setSvg(){ this.svg = d3.select("#graph") // size definitions for the svg .append("svg") @@ -82,7 +95,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - this.svg.append('defs').append('marker') + this.svg.append('defs').append('marker') // marker/ arrow settings .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions @@ -94,139 +107,49 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .append('svg:path') .attr('d', 'M 0,-5 L 10 ,0 L 0,5') .attr('fill', '#999') - .style('stroke', 'none'); - - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }) - this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service - + .style('stroke', 'none');} - //############################################################################################################## - this.simulation = d3.forceSimulation(this.data.nodes); - // simulation.force('link').links(data.links); // simulation - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links) - - ) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - - this.refresh(); - } /** - * Draws the graph, executed first on AfterContentInit and on every doubleclick on a node + * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ - refresh() { - console.log("refresh starting..") - console.log(this.data.nodes) - - - /* this.simulation.force("link").links(this.data.links); - this.simulation.force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links) - - - )*/ -//this.link.exit().remove(); -//this.node.exit().remove(); - - - this.link = this.svg // link definitions - .selectAll(".link") - .data(this.data.links, function (d){return d.target.id}) - /*.join(enter => enter.append("line") - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)'), // arrow on end of the link - exit => exit.remove())*/ - - var linkEnter=this.link.enter() + refreshSimulation() { + this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id}) + var linkEnter=this.link.enter() //enter-selection .append("line").style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; linkEnter.append("title") .text(function (d){return d.type}) - this.link = this.link.merge(linkEnter); - this.link.exit().remove(); + this.link = this.link.merge(linkEnter); // merge old elements with entered elements + this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - this.node = this.svg.selectAll(".node") - .data(this.data.nodes, function (d){return d.id}) - .join(enter => enter.append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8 } - else { return this.rad } - }) - .on('mouseover', this.mouseover) - .on('mouseout', this.mouseout) - .style("fill", function (d) { - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - }) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }) - .on('dblclick', this.nodeDoubleClick) - .call(d3.drag() - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)), - update => update.style("fill", "red"), - exit => exit.remove()) - - - /*var nodeEnter= this.node.enter().append("circle") + this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id}) + + var nodeEnter= this.node.enter().append("circle") .attr("r", (d) => { if (d.group == 1) { return this.rad + 8 } else { return this.rad } }) - /*nodeEnter.append("title") - .text(function(d){return d.id}) - nodeEnter.append("text") - .text(function (d){return d.name})*/ - /* nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseover', this.mouseover); nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", function (d) { - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - }) + nodeEnter.style("fill", this.setNodeStyle) .style("stroke-width", this.nodeborder) .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group - /*.append ("text") - .attr ("dx", 12) - .attr ("dy", ".35em") - .text(function(d){return d.name})*/ -/* nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() + nodeEnter.call(d3.drag() // reactions when dragging a node .on("start", this.dragstarted) .on("drag", this.dragged) .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); - this.node.exit().remove(); + this.node=this.node.merge(nodeEnter); // merge old elements with entered elements + this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + - console.log(this.node) - */ - this.text = this.svg.selectAll("text").data(this.data.nodes) - /*.join(enter=> enter.append("text") - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold" } else { return "normal" }}) - .text(function (d) { console.log(d.name);return d.name }), - exit=> exit.remove() )*/ - - - var textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name}) + var textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name}) .attr("font-weight", function (d) { if (d.group == 1) { return "bold" } @@ -235,30 +158,20 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .text(function (d) { console.log(d.name); return d.name }); // get text from data this.text=this.text.merge(textEnter); -//this.text.exit().remove(); + this.text.exit().remove(); this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) - .join(enter=> enter.append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type }), - exit=> exit.remove()) - - - - - /* var linkTextEnter=this.linkText.enter().append("text") + var linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data this.linkText=this.linkText.merge(linkTextEnter); -//this.linkText.exit().remove(); -*/ + this.linkText.exit().remove(); + - this.simulation.nodes(this.data.nodes); + this.simulation.nodes(this.data.nodes); // simualtion uses current data this.simulation.force("link").links(this.data.links); - - //########################################################################################################### + } /** * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element @@ -267,8 +180,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { ticked = () => { const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - + this.text .attr("dx", (d) => { // restrictions for node text positions in the svg const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; @@ -340,36 +252,33 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { }) } - +/** + * Function executed on a drag start + */ dragstarted = (d) => { if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } - - /** * Drag function, executed on every drag movement * @param d The dragged node */ dragged(d) { - // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } dragended(d) { - //if (!d3.event.active) this.simulation.alphaTarget(0); - //d.fx = null; - // d.fy = null; + } /** * Mouseover function, executed when mouse over node. The radius of the node will increase. * @param d node under the cursor */ mouseover(d) { - if (d.group == 1) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node d3.select(this).transition() .duration('2') .attr('r', 20 + 12) @@ -381,11 +290,11 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { } } /** - * Mousout function, executed when mouse cursor leaves the node + * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. * @param d node wich is left by the cursor after mouseover */ - mouseout(d) { // function on mousout: decrease this.radius to orgin - if (d.group == 1) { + mouseout(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node d3.select(this).transition() .attr('r', 20 + 8) } @@ -398,46 +307,22 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { * Doubleclick function, executed on every doubleclick on a node *@param d The clicked node */ - nodeDoubleClick=(d)=> { -//this.simulation.stop(); this.index++; - - const newNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(newNode); - this.data.links.push({ "source": d.id, "target": newNode, "type": "testclick" }) - /* - this.link.exit().remove(); - this.linkText.exit().remove(); - this.text.exit().remove(); */ - /*this.simulation.force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links)) - - // ) - // .force("collision", d3.forceCollide(40)) - // .on("tick", this.ticked) // tick on every ste - -400)) // node magnetism /attraction - //.force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) - //simulation.restart(); //node oben links + fehlermeldungen - // this.simulation.nodes(this.data.nodes) - // this.simulation.force('link').links(this.data.links) - //simulation.force('nodes').nodes(data.nodes) - //node.data(data.nodes); - //link.data(data.links); - //node.enter().append("circle"); - //this.node.exit().remove(); - //link.exit().remove(); - //linkText.exit().remove(); - //text.exit().remove(); - // this.node = this.node.data(this.data.nodes); - //link= link.data(data.links) - //node.selectAll('circle').remove(); - //this.simulation.restart(); */ - this.refresh(); - // this.simulation.restart(); - //console.log(this.data.nodes) + const testNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(testNode); + this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }) + this.refreshSimulation(); + } + + /** Returns the color of the inner circle of a node */ + setNodeStyle(d){ + if (d.group == 1) { return "black" } + else { return "whitesmoke" } + } +/** Returns the node border color for each node-group */ + setNodeBorderStyle=(d)=>{ + return this.color(d.group); } } \ No newline at end of file From fb298245ac0a55c4ac316284fd558a0ffcf8185b Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 5 Nov 2021 16:39:31 +0100 Subject: [PATCH 34/74] Added gitignore to ignore uploaded-files directory --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1c4b2bb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# ---> VisualStudioCode +/uploaded-files/ From 00e4ad9d7f4885cfce80acceee21faeb134f65eb Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 19 Aug 2022 20:17:16 +0200 Subject: [PATCH 35/74] Minor formatting fixes --- .../mtp-mapping-settings.component.html | 42 +++++++++---------- .../mtp-mapping-settings.component.ts | 1 - 2 files changed, 21 insertions(+), 22 deletions(-) diff --git a/frontend/src/modules/skillmex-configuration/subcomponents/mtp-mapping-settings.component.html b/frontend/src/modules/skillmex-configuration/subcomponents/mtp-mapping-settings.component.html index afd8c203..bae589fd 100644 --- a/frontend/src/modules/skillmex-configuration/subcomponents/mtp-mapping-settings.component.html +++ b/frontend/src/modules/skillmex-configuration/subcomponents/mtp-mapping-settings.component.html @@ -1,26 +1,26 @@

Mtp-Mapping-Settings

-
-
-
-
-
- URL: -
- +
+
+
+
+
+ URL: +
+ +
+
+
+
+
+ +
+
+

Server not reachable

+
-
-
-
-
- -
-
-

Server not reachable

-
-
diff --git a/frontend/src/modules/skillmex-configuration/subcomponents/mtp-mapping-settings.component.ts b/frontend/src/modules/skillmex-configuration/subcomponents/mtp-mapping-settings.component.ts index 318558be..b01b2686 100644 --- a/frontend/src/modules/skillmex-configuration/subcomponents/mtp-mapping-settings.component.ts +++ b/frontend/src/modules/skillmex-configuration/subcomponents/mtp-mapping-settings.component.ts @@ -7,7 +7,6 @@ import {MappingServiceConfig} from '@shared/models/mappings/MappingServiceConfig selector: 'mtp-mapping-settings', templateUrl: './mtp-mapping-settings.component.html', }) - export class MtpMappingSettingsComponent implements OnInit{ url = "asd"; From 6d7b1f90defa131ba2461de08a31cd2559ab787c Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 19 Aug 2022 20:17:28 +0200 Subject: [PATCH 36/74] Added reflect-metadata back in --- backend/package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/backend/package.json b/backend/package.json index cfeced6d..c2534282 100644 --- a/backend/package.json +++ b/backend/package.json @@ -25,6 +25,7 @@ "formidable": "^1.2.1", "node-opcua": "^2.62.5", "request": "^2.88.0", + "reflect-metadata": "0.1.13", "rimraf": "3.0.2", "rxjs": "^7.4.0", "sparql-result-converter": "^3.0.1", From 54fce1cb2f7ac753786cb03be1e22dc33dbd0e61 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Mon, 30 Dec 2019 22:28:30 +0100 Subject: [PATCH 37/74] Created 'Graph-Visualization' in 'own-modules' Created 'Graph-Visualization' in 'own-modules' and d3.js installed --- frontend/app/layout/layout-routing.module.ts | 28 +++++++++++++++++++ frontend/package.json | 1 + .../components/sidebar/sidebar.component.html | 4 +++ .../graph-visualization.component.html | 3 ++ .../graph-visualization.component.ts | 13 +++++++++ .../graph-visualization.module.ts | 11 ++++++++ .../graph-visualization.routing.ts | 22 +++++++++++++++ 7 files changed, 82 insertions(+) create mode 100644 frontend/app/layout/layout-routing.module.ts create mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.component.html create mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.component.ts create mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.module.ts create mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts diff --git a/frontend/app/layout/layout-routing.module.ts b/frontend/app/layout/layout-routing.module.ts new file mode 100644 index 00000000..e7acd6b0 --- /dev/null +++ b/frontend/app/layout/layout-routing.module.ts @@ -0,0 +1,28 @@ +import { NgModule } from '@angular/core'; +import { Routes, RouterModule } from '@angular/router'; +import { LayoutComponent } from './layout.component'; + +const routes: Routes = [ + { + path: '', + component: LayoutComponent, + children: [ + { path: '', redirectTo: 'dashboard', pathMatch: 'prefix' }, + { path: 'module-management', loadChildren: '../own-modules/module-management/module-management.module#ModuleManagementModule'}, + { path: 'order-management', loadChildren: '../own-modules/order-management/order-management.module#OrderManagementModule'}, + { path: 'kpi-dashboard', loadChildren: '../own-modules/kpi-dashboard/kpi-dashboard.module#KpiDashboardModule'}, + { path: 'reconfiguration-overview', loadChildren: '../own-modules/reconfiguration-overview/reconfiguration-overview.module#ReconfigurationOverviewModule'}, + { path: 'ops-configuration', loadChildren: '../own-modules/ops-configuration/ops-configuration.module#OpsConfigurationModule'}, + { path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardModule' }, + { path: 'charts', loadChildren: './charts/charts.module#ChartsModule' }, + {path:'graph-visualization', loadChildren: '../own-modules/graph-visualization/graph-visualization.module#GraphVisualizationModule'}, + { path: 'blank-page', loadChildren: './blank-page/blank-page.module#BlankPageModule' } + ] + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) +export class LayoutRoutingModule {} diff --git a/frontend/package.json b/frontend/package.json index 5a39f6d0..79df4c5a 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,6 +31,7 @@ "chartjs-plugin-annotation": "^1.4.0", "chartjs-plugin-datalabels": "^2.0.0", "core-js": "2.6.5", + "d3": "^5.15.0", "formidable": "^2.0.1", "ng2-charts": "3.0.11", "rxjs": "^7.4.0", diff --git a/frontend/src/layout/components/sidebar/sidebar.component.html b/frontend/src/layout/components/sidebar/sidebar.component.html index 5dea069b..fab4a892 100644 --- a/frontend/src/layout/components/sidebar/sidebar.component.html +++ b/frontend/src/layout/components/sidebar/sidebar.component.html @@ -46,6 +46,10 @@
+ +   + Graph-Visualization +   SkillMeX Configuration diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html new file mode 100644 index 00000000..70b08142 --- /dev/null +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html @@ -0,0 +1,3 @@ +

+Graph-Visualization +

diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts new file mode 100644 index 00000000..1b195074 --- /dev/null +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -0,0 +1,13 @@ +import { Component } from '@angular/core'; + +@Component({ + selector: 'graph-visualization', + templateUrl: './graph-visualization.component.html' +}) +export class GraphVisualizationComponent { + + constructor() { } + + + +} diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts new file mode 100644 index 00000000..afb5fff0 --- /dev/null +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -0,0 +1,11 @@ +import { NgModule } from '@angular/core'; +import { GraphVisualizationComponent } from './graph-visualization.component'; +import {GraphVisualizationRouter} from './graph-visualization.routing'; + +@NgModule({ + imports: [ + GraphVisualizationRouter + ], + declarations: [GraphVisualizationComponent] +}) +export class GraphVisualizationModule { } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts new file mode 100644 index 00000000..ded3fe38 --- /dev/null +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts @@ -0,0 +1,22 @@ +import { Routes, RouterModule } from '@angular/router'; +import { NgModule } from '@angular/core'; +import {GraphVisualizationComponent} from './graph-visualization.component' + +const routes: Routes = [ + { path: '', + component: GraphVisualizationComponent, + data: { + title: 'Graph-Visualization' + }, + children: [] + + + } +]; + +@NgModule({ + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] +}) + +export class GraphVisualizationRouter {} From 0c3a57faa9bcf2e7a7cfd03e5f6fec15af5b2a32 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Mon, 27 Jan 2020 12:24:21 +0100 Subject: [PATCH 38/74] Added visual graph by d3.js --- .../graph-visualization.component.scss | 0 .../graph-visualization.component.html | 5 + .../graph-visualization.component.ts | 191 +++++++++++++++++- 3 files changed, 192 insertions(+), 4 deletions(-) create mode 100644 frontend/app/own-modules/graph-visualization/graph-visualization.component.scss diff --git a/frontend/app/own-modules/graph-visualization/graph-visualization.component.scss b/frontend/app/own-modules/graph-visualization/graph-visualization.component.scss new file mode 100644 index 00000000..e69de29b diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html index 70b08142..fcff4a3d 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html @@ -1,3 +1,8 @@

Graph-Visualization

+
+
+ +
+
\ No newline at end of file diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 1b195074..cf171b0d 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -1,13 +1,196 @@ -import { Component } from '@angular/core'; +import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'; +import * as d3 from "d3" +import { enterView } from '@angular/core/src/render3/state'; +import { pathToFileURL } from 'url'; @Component({ selector: 'graph-visualization', - templateUrl: './graph-visualization.component.html' + encapsulation: ViewEncapsulation.None, + templateUrl: './graph-visualization.component.html', + styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent { +export class GraphVisualizationComponent implements AfterContentInit { + + ngAfterContentInit(): void { + const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen + const width = 1400 - margin.left - margin.right; + const height = 900 - margin.top - margin.bottom; + const rad=20; // radius + + +var svg = d3.select("#graph") // Größe u. Grenzen SVG +.append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) +.append("g") + .attr("transform", + "translate(" + margin.left + "," + margin.top + ")"); + + + +var colors = d3.scaleOrdinal(d3.schemeCategory10); //Farben + + + +//d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten + +const data = { + "nodes": [ + { + "id": 1, + "name": "ModulA", + "group": 1 + }, + { + "id": 2, + "name": "ModulB", + "group": 2 + }, + { + "id": 3, + "name": "ModulC", + "group": 3 + + }, { + "id": 4, + "name": "cap11", + "group":1 + }, + { + "id": 5, + "name": "cap12", + "group":1 + }, + { + "id": 6, + "name": "cap21", + "group":2 + } + ], + "links": [ + + { + "source": 1, + "target": 4, + "type": "is_a" + } , + { + "source": 1, + "target": 5, + "type": "has" + } , + { + "source": 2, + "target": 6, + "type":"has" + } + ] +} - constructor() { } + var link = svg // Links initialisieren + .selectAll(".links") + .data(data.links) + .enter() + .append("line") + .style("stroke", "#aaa") + ; + link.append("title") + .text(function(d){return d.type}); + var node = svg // Nodes initialisieren + .selectAll("circle") + .data(data.nodes) + .enter() + .append("circle") + .attr("r", rad) + .style( "fill", "white") + .style("stroke-width", 8) + .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit + /*.append ("text") // name als text neben node + .attr ("dx", 12) + .attr ("dy", ".35em") + .text(function(d){return d.name})*/ + .call(d3.drag() + .on("start", dragstarted) + .on("drag", dragged)); +; + + const text =svg + .selectAll("text") + .data(data.nodes) + .enter() + .append("text") + + .text(function(d){return d.name}); + + const linkText =svg + .selectAll("links") + .data(data.links) + .enter() + .append("text") + .text(function (d){return d.type}); + + + + var simulation = d3.forceSimulation(data.nodes) //Simulation + .force("link", d3.forceLink() + .id(function(d) { return d.id; }) + .distance(100) + .links(data.links) + ) + .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen + .force("center", d3.forceCenter(width / 2, height / 2)) // Nodes mittig von svg + .on("tick", ticked); // tick wird in jedem Schritt durchgeführt + + + function ticked() { + console.log(data); + text + .attr("dx", function(d) { + return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) + .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }) + + linkText + .attr("dx", function(d){return 0.5*((d.source.x)+(d.target.x))}) + .attr("dy", function(d){return 0.5*((d.source.y)+(d.target.y)) }) + + + + link // Anfang und Ende der Links / Bestimmung der Position + .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)); }) // Postionen nur innerhalb des Fensters erlaubt + .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)); }) + .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)); }) + .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)); }); + + node // Bestimmung der Postion einzelner Nodes + .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)); }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }); + } + + function ended() { + console.log("ended"); + } + + function dragstarted(d) { + if (!d3.event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + console.log(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; + } + + } + constructor() { } } From 709ce70b60e85bc14cb9a4f212f5a1594feef9df Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 31 Jan 2020 12:17:01 +0100 Subject: [PATCH 39/74] Added arrows Added arrows for relation directions --- .../graph-visualization.component.ts | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index cf171b0d..efe39ef0 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -15,10 +15,12 @@ export class GraphVisualizationComponent implements AfterContentInit { const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen const width = 1400 - margin.left - margin.right; const height = 900 - margin.top - margin.bottom; - const rad=20; // radius + const rad=20 // Radius eines Knotens + const nodeborder =8; // Umrandungsdicke eines Knotens -var svg = d3.select("#graph") // Größe u. Grenzen SVG + +const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) @@ -28,8 +30,21 @@ var svg = d3.select("#graph") // Größe u. Grenzen SVG -var colors = d3.scaleOrdinal(d3.schemeCategory10); //Farben - +const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + +svg.append('defs').append('marker') + .attr("id",'arrowhead') + .attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5) + .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('refY',0) + .attr('orient','auto') + .attr('markerWidth',13) + .attr('markerHeight',13) + .attr('xoverflow','visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke','none'); //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten @@ -89,23 +104,25 @@ const data = { - var link = svg // Links initialisieren + const link = svg // Links initialisieren .selectAll(".links") .data(data.links) .enter() .append("line") .style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)') ; link.append("title") .text(function(d){return d.type}); - var node = svg // Nodes initialisieren + const node = svg // Nodes initialisieren .selectAll("circle") .data(data.nodes) .enter() .append("circle") .attr("r", rad) .style( "fill", "white") - .style("stroke-width", 8) + .style("stroke-width", nodeborder) .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit /*.append ("text") // name als text neben node .attr ("dx", 12) @@ -133,10 +150,10 @@ const data = { - var simulation = d3.forceSimulation(data.nodes) //Simulation + const simulation = d3.forceSimulation(data.nodes) //Simulation .force("link", d3.forceLink() .id(function(d) { return d.id; }) - .distance(100) + .distance(150) .links(data.links) ) .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen @@ -145,7 +162,6 @@ const data = { function ticked() { - console.log(data); text .attr("dx", function(d) { return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) From 8dfde52c87f40c5d3fa5575dc2f065e8f7b750f7 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 31 Jan 2020 14:56:00 +0100 Subject: [PATCH 40/74] Fixed bug Type of relation fixed (on path between two linked nodes) --- .../graph-visualization.component.ts | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index efe39ef0..d7894ca6 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -34,7 +34,7 @@ const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung v svg.append('defs').append('marker') .attr("id",'arrowhead') - .attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5) + .attr('viewBox','-0 -5 10 10') //Koordinatensystem .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten .attr('refY',0) .attr('orient','auto') @@ -146,6 +146,7 @@ const data = { .data(data.links) .enter() .append("text") + .style("fill","#999" ) .text(function (d){return d.type}); @@ -164,24 +165,46 @@ const data = { function ticked() { text .attr("dx", function(d) { - return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) - .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }) + return Math.max(0+rad, Math.min(d.x+23, width-rad))}) + .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }) linkText - .attr("dx", function(d){return 0.5*((d.source.x)+(d.target.x))}) - .attr("dy", function(d){return 0.5*((d.source.y)+(d.target.y)) }) + .attr("dx", function(d){ + if ((d.target.x>width) || (d.source.x>width)) { + if (d.target.x>d.source.x) { + return Math.min(width,width-0.5*(width-d.source.x)); + } else { + return Math.min(width,width-0.5*(width-d.target.x)); + } ; + } else { + return Math.min(0.5*((Math.max(0,(d.target.x)))-(Math.max(0,(d.source.x))))+(Math.max(0,d.source.x))); + } + }) + //Relationstext bleibt am Link + .attr("dy", function(d){ + if ((d.target.y>height) || (d.source.y>height)) { + if (d.target.y>d.source.y) { + return Math.min(height,height-0.5*(height-d.source.y)); + } + else { + return Math.min(height,height-0.5*(height-d.target.y)); + } + } + else { + return (0.5*((Math.max(0,(d.target.y)))-(Math.max(0,(d.source.y))))+(Math.max(0,d.source.y))); + }}) link // Anfang und Ende der Links / Bestimmung der Position - .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)); }) // Postionen nur innerhalb des Fensters erlaubt - .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)); }) - .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)); }) - .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)); }); + .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)) }) // Postionen nur innerhalb des Fensters erlaubt + .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)) }) + .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)) }) + .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)) }); node // Bestimmung der Postion einzelner Nodes - .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)); }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße - .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }); + .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)) }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }); } function ended() { From 8e1e2338a9c4ac4a0552402e61ca5562c78385ec Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 18 Feb 2020 16:58:55 +0100 Subject: [PATCH 41/74] Added Link to Graph Visualization in Module-Management --- .../graph-visualization/graph-visualization.component.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html index fcff4a3d..3163f4b5 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html @@ -1,8 +1,9 @@

Graph-Visualization

-
+ +
-
\ No newline at end of file +
From 005504485eb0465287453d9ad3024de3838ac730 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 28 Feb 2020 19:15:38 +0100 Subject: [PATCH 42/74] Resize svg when window resized --- .../graph-visualization.component.html | 7 +- .../graph-visualization.component.ts | 413 ++++++++++-------- 2 files changed, 238 insertions(+), 182 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html index 3163f4b5..a454716a 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.html @@ -1,9 +1,8 @@

Graph-Visualization

- -
-
+
+
-
+
\ No newline at end of file diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index d7894ca6..b57ca058 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -1,7 +1,8 @@ -import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'; +import { Component, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; +import { window } from 'rxjs/operators'; @Component({ selector: 'graph-visualization', @@ -11,225 +12,281 @@ import { pathToFileURL } from 'url'; }) export class GraphVisualizationComponent implements AfterContentInit { - ngAfterContentInit(): void { - const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen - const width = 1400 - margin.left - margin.right; - const height = 900 - margin.top - margin.bottom; - const rad=20 // Radius eines Knotens - const nodeborder =8; // Umrandungsdicke eines Knotens + @ViewChild('g') svgContainer: ElementRef; + ngAfterContentInit(): void { + console.log('element ref width:'); + console.log(this.svgContainer.nativeElement.offsetWidth); + const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen + const width = 100; //parent.innerWidth - margin.left - margin.right; + //var width= window.innerWidth; + const height = 100; + const rad = 20 // Radius eines Knotens + const nodeborder = 8; // Umrandungsdicke eines Knotens + const testweite = parent.innerWidth; + console.log("Die ermittelte Weite ist:" + testweite); -const svg = d3.select("#graph") // Größe u. Grenzen SVG -.append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) -.append("g") - .attr("transform", + const svg = d3.select("#graph") // Größe u. Grenzen SVG + .append("svg") + .attr("width", width + '%') + .attr("height", height+'%') + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - -const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes - -svg.append('defs').append('marker') - .attr("id",'arrowhead') - .attr('viewBox','-0 -5 10 10') //Koordinatensystem - .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten - .attr('refY',0) - .attr('orient','auto') - .attr('markerWidth',13) - .attr('markerHeight',13) - .attr('xoverflow','visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke','none'); - -//d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - -const data = { - "nodes": [ - { - "id": 1, - "name": "ModulA", - "group": 1 - }, - { - "id": 2, - "name": "ModulB", - "group": 2 - }, - { - "id": 3, - "name": "ModulC", - "group": 3 - - }, { - "id": 4, - "name": "cap11", - "group":1 - }, - { - "id": 5, - "name": "cap12", - "group":1 - }, - { - "id": 6, - "name": "cap21", - "group":2 + + const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + + svg.append('defs').append('marker') + .attr("id", 'arrowhead') + .attr('viewBox', '-0 -5 10 10') //Koordinatensystem + .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('refY', 0) + .attr('orient', 'auto') + .attr('markerWidth', 13) + .attr('markerHeight', 13) + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke', 'none'); + + + //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten + + const data = { + "nodes": [ + { + "id": 1, + "name": "ModulA", + "group": 1 + }, + { + "id": 2, + "name": "ModulB", + "group": 2 + }, + { + "id": 3, + "name": "ModulC", + "group": 3 + + }, { + "id": 4, + "name": "cap11", + "group": 1 + }, + { + "id": 5, + "name": "cap12", + "group": 1 + }, + { + "id": 6, + "name": "cap21", + "group": 2 + } + ], + "links": [ + + { + "source": 1, + "target": 4, + "type": "is_a" + }, + { + "source": 1, + "target": 5, + "type": "has" + }, + { + "source": 2, + "target": 6, + "type": "has" + } + ] } - ], - "links": [ - - { - "source": 1, - "target": 4, - "type": "is_a" - } , - { - "source": 1, - "target": 5, - "type": "has" - } , - { - "source": 2, - "target": 6, - "type":"has" - } - ] -} - const link = svg // Links initialisieren - .selectAll(".links") - .data(data.links) - .enter() - .append("line") + const link = svg // Links initialisieren + .selectAll(".links") + .data(data.links) + .enter() + .append("line") .style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') - ; + ; link.append("title") - .text(function(d){return d.type}); - const node = svg // Nodes initialisieren - .selectAll("circle") - .data(data.nodes) - .enter() - .append("circle") + .text(function (d) { return d.type }); + const node = svg // Nodes initialisieren + .selectAll("circle") + .data(data.nodes) + .enter() + .append("circle") .attr("r", rad) - .style( "fill", "white") - .style("stroke-width", nodeborder) - .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit - /*.append ("text") // name als text neben node - .attr ("dx", 12) - .attr ("dy", ".35em") - .text(function(d){return d.name})*/ + .on('mouseover', function(){ + d3.select(this).transition() + .duration('2') + .attr('r', rad+5) + }) + .on('mouseout', function(){ + d3.select(this).transition() + .attr('r', rad) + }) + .style("fill", "white") + .style("stroke-width", nodeborder) + .style("stroke", function (d) { return colors(d.group); }) // Node-Farben nach Gruppenzugehörigkeit + /*.append ("text") // name als text neben node + .attr ("dx", 12) + .attr ("dy", ".35em") + .text(function(d){return d.name})*/ .call(d3.drag() - .on("start", dragstarted) - .on("drag", dragged)); -; - - const text =svg + .on("start", dragstarted) + .on("drag", dragged)); + ; + + const text = svg .selectAll("text") .data(data.nodes) .enter() .append("text") - - .text(function(d){return d.name}); - const linkText =svg + .text(function (d) { return d.name }); + + const linkText = svg .selectAll("links") .data(data.links) .enter() .append("text") - .style("fill","#999" ) - .text(function (d){return d.type}); + .style("fill", "#999") + .text(function (d) { return d.type }); + - - - const simulation = d3.forceSimulation(data.nodes) //Simulation - .force("link", d3.forceLink() - .id(function(d) { return d.id; }) - .distance(150) - .links(data.links) - ) - .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen - .force("center", d3.forceCenter(width / 2, height / 2)) // Nodes mittig von svg - .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - - function ticked() { - text - .attr("dx", function(d) { - return Math.max(0+rad, Math.min(d.x+23, width-rad))}) - .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }) - linkText - .attr("dx", function(d){ - if ((d.target.x>width) || (d.source.x>width)) { - if (d.target.x>d.source.x) { - return Math.min(width,width-0.5*(width-d.source.x)); - } else { - return Math.min(width,width-0.5*(width-d.target.x)); - } ; - } else { - return Math.min(0.5*((Math.max(0,(d.target.x)))-(Math.max(0,(d.source.x))))+(Math.max(0,d.source.x))); - } - }) - //Relationstext bleibt am Link - .attr("dy", function(d){ - if ((d.target.y>height) || (d.source.y>height)) { - if (d.target.y>d.source.y) { - return Math.min(height,height-0.5*(height-d.source.y)); - } - else { - return Math.min(height,height-0.5*(height-d.target.y)); - } - } - else { - return (0.5*((Math.max(0,(d.target.y)))-(Math.max(0,(d.source.y))))+(Math.max(0,d.source.y))); - }}) - - link // Anfang und Ende der Links / Bestimmung der Position - .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)) }) // Postionen nur innerhalb des Fensters erlaubt - .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)) }) - .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)) }) - .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)) }); + const ticked = () => { + const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; + const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - node // Bestimmung der Postion einzelner Nodes - .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)) }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße - .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }); - } + // data.nodes.indexOf[0].fx= centerWidth; + //data.nodes.indexOf[0].fy= centerHeight; + text + .attr("dx", (d)=>{ + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) + .attr("dy", (d) => { + const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) - function ended() { - console.log("ended"); - } + linkText + .attr("dx", (d)=>{ + d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); + d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(width- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceX-relativeTargetX)/2)+ relativeTargetX))) +'%'; + }) + .attr("dy", (d)=>{ + d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; + }) - function dragstarted(d) { - if (!d3.event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } + link // Anfang und Ende der Links / Bestimmung der Position nachfolgend Closure-Funtktionen um Zugriff mit this. außerhalb der Funktion + .attr("x1", (d)=> { + d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // Umwandlung des Radius in relative Zahl (in % !) + const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX+'%';}) + .attr("y1", (d)=> { + d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY+'%';}) + .attr("x2", (d)=> { + d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX+'%';}) + .attr("y2", (d)=> { + d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY+'%';}) + + node // Bestimmung der Postion einzelner Nodes + .attr("cx", (d) => { + d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX+'%'; + }) // Zentrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", (d) => { + d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) + const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY+'%';}) + } + + const simulation = d3.forceSimulation(data.nodes) //Simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(300) + .links(data.links) + ) + .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes + + + + + .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - function dragged(d) { - console.log(d) + function ended() { + console.log("ended"); + } + function mouseover(){ + d3.select(this).select("circle").transition() + .duration(750) + .attr("r", rad+10) + } + + function mouseout(){ + d3.select(this).select("circle").transition() + .duration(750) + .attr("r", rad) + } + function dragstarted(d) { + if (!d3.event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + + console.log(d3.event) 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; } - - } + + } constructor() { } } From 83f8bb4adc4a37600a62e939bcc8d61a0b9b50e4 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:15:06 +0100 Subject: [PATCH 43/74] Added function to get data for ontology-visualization of each module --- .../manufacturing-service-executor.service.ts | 44 ++++++ .../graph-visualization.component.ts | 130 ++++++++++++++++-- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 + 4 files changed, 172 insertions(+), 11 deletions(-) create mode 100644 frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts diff --git a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts new file mode 100644 index 00000000..338e7fa0 --- /dev/null +++ b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts @@ -0,0 +1,44 @@ +import { Injectable } from "@angular/core"; +import { HttpClient, HttpRequest, HttpHeaders, HttpParams } from "@angular/common/http"; +import { Observable } from "rxjs"; +//import { ServiceExecutionDescription } from "./self-description"; +import { map } from 'rxjs/operators'; +import { ServiceExecutionDescription } from "app/shared/models/self-description"; + + +@Injectable() + +export class ManufacturingServiceExecutor { + + constructor(private http: HttpClient) {} + + executeService(executionDescription:ServiceExecutionDescription) { + console.log(`service called`); + console.log(executionDescription); + + // // construct the request + // let headers = new HttpHeaders(); + // executionDescription.parameters.forEach(parameter => { + // if (parameter.getShortType() == "HeaderParameter") { + // headers.set(parameter.name, parameter.value); + // } + // }); + + // let queryParams = new HttpParams(); + // executionDescription.parameters.forEach(parameter => { + // if (parameter.getShortType() == "QueryParameter") { + // queryParams.set(parameter.name, parameter.value); + // } + // }); + + // let request = new HttpRequest(executionDescription.methodType, executionDescription.fullPath, { + // "headers": headers, + // "params": queryParams + // }) + + // Send + this.http.post(`api/service-executions`, executionDescription).subscribe(res => {console.log(JSON.stringify(res))}); + } + + +} \ No newline at end of file diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index b57ca058..2c46fd12 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -3,6 +3,8 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; +import { ModuleService } from 'app/shared/services/module.service'; + @Component({ selector: 'graph-visualization', @@ -12,11 +14,16 @@ import { window } from 'rxjs/operators'; }) export class GraphVisualizationComponent implements AfterContentInit { + constructor( + private moduleService: ModuleService + ) { + }; + @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - console.log('element ref width:'); - console.log(this.svgContainer.nativeElement.offsetWidth); + //console.log('element ref width:'); + // console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -25,7 +32,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - console.log("Die ermittelte Weite ist:" + testweite); + //console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -54,9 +61,12 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - const data = { + +// ##### DATA ###### + + + /*const data = { "nodes": [ { "id": 1, @@ -108,8 +118,107 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } +*/ +var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + var group=0; + var moduleId=0; + var capabilityId=100; + var inputId=1000; + var outputId=10000; + var skillId=100000; + var typeId=1000000; + var data= {nodes:[], links:[]}; + //################################################ + receivedData.forEach(modul => { + moduleId++; + group++; + data.nodes.push({ + "id" : moduleId, + "name": modul.name, + "group": 1 + }); + modul.capabilities.forEach(capability=>{ + capabilityId++; + data.nodes.push({ + "id" : capabilityId, + "name": capability.name, + "group": 2 + }) + data.links.push({ + "source": moduleId, + "target": capabilityId, + "type": "has_capability" + }) + capability.hasInput.forEach(input=>{ + inputId++; + typeId++; + data.nodes.push({ + "id" : inputId, + "name": input.name, + "group": 3 + }) + data.links.push({ + "source": capabilityId, + "target": inputId, + "type": "has_input" + }) + data.nodes.push({ + "id": typeId, + "name": input.stateType, + "group": 6 + }) + data.links.push({ + "source": inputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.hasOutput.forEach(output=>{ + outputId++; + typeId++; + data.nodes.push({ + "id" : outputId, + "name": output.name, + "group": 4 + }) + data.links.push({ + "source": capabilityId, + "target": outputId, + "type": "has_output" + }) + + data.nodes.push({ + "id": typeId, + "name": output.stateType, + "group": 6 + }) + data.links.push({ + "source": outputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.executableViaSkill.forEach(skill=>{ + skillId++; + data.nodes.push({ + "id" : skillId, + "name": skill.name, + "group": 5 + }) + data.links.push({ + "source": capabilityId, + "target": skillId, + "type": "executable_via_Skill" + }) + }) + }) + + }); +// ################################################## + + console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -246,7 +355,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -255,7 +364,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - console.log("ended"); + //console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -276,7 +385,7 @@ export class GraphVisualizationComponent implements AfterContentInit { function dragged(d) { - console.log(d3.event) + // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -287,6 +396,9 @@ export class GraphVisualizationComponent implements AfterContentInit { } } - constructor() { } + + + + } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts index afb5fff0..df9c2713 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -1,11 +1,15 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; +import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter + GraphVisualizationRouter, ], - declarations: [GraphVisualizationComponent] + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index ae60048e..5790705b 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,3 +1,4 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; + From d63a739d36c1090fe631655d4425dbdc1a867f13 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:17:05 +0100 Subject: [PATCH 44/74] Revert "Added function to get data for ontology-visualization of each module" This reverts commit 8b9e53e21219f952dc96071abaab17af6acf8a40. --- .../manufacturing-service-executor.service.ts | 4 +- .../graph-visualization.component.ts | 130 ++---------------- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 - 4 files changed, 12 insertions(+), 131 deletions(-) diff --git a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts index 338e7fa0..4b1a70c8 100644 --- a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts +++ b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts @@ -1,10 +1,8 @@ import { Injectable } from "@angular/core"; import { HttpClient, HttpRequest, HttpHeaders, HttpParams } from "@angular/common/http"; import { Observable } from "rxjs"; -//import { ServiceExecutionDescription } from "./self-description"; +import { ServiceExecutionDescription } from "./self-description"; import { map } from 'rxjs/operators'; -import { ServiceExecutionDescription } from "app/shared/models/self-description"; - @Injectable() diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 2c46fd12..b57ca058 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -3,8 +3,6 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; -import { ModuleService } from 'app/shared/services/module.service'; - @Component({ selector: 'graph-visualization', @@ -14,16 +12,11 @@ import { ModuleService } from 'app/shared/services/module.service'; }) export class GraphVisualizationComponent implements AfterContentInit { - constructor( - private moduleService: ModuleService - ) { - }; - @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - //console.log('element ref width:'); - // console.log(this.svgContainer.nativeElement.offsetWidth); + console.log('element ref width:'); + console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -32,7 +25,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - //console.log("Die ermittelte Weite ist:" + testweite); + console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -61,12 +54,9 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); + //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - -// ##### DATA ###### - - - /*const data = { + const data = { "nodes": [ { "id": 1, @@ -118,107 +108,8 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } -*/ -var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - var group=0; - var moduleId=0; - var capabilityId=100; - var inputId=1000; - var outputId=10000; - var skillId=100000; - var typeId=1000000; - var data= {nodes:[], links:[]}; - //################################################ - receivedData.forEach(modul => { - moduleId++; - group++; - data.nodes.push({ - "id" : moduleId, - "name": modul.name, - "group": 1 - }); - modul.capabilities.forEach(capability=>{ - capabilityId++; - data.nodes.push({ - "id" : capabilityId, - "name": capability.name, - "group": 2 - }) - data.links.push({ - "source": moduleId, - "target": capabilityId, - "type": "has_capability" - }) - capability.hasInput.forEach(input=>{ - inputId++; - typeId++; - data.nodes.push({ - "id" : inputId, - "name": input.name, - "group": 3 - }) - data.links.push({ - "source": capabilityId, - "target": inputId, - "type": "has_input" - }) - data.nodes.push({ - "id": typeId, - "name": input.stateType, - "group": 6 - }) - data.links.push({ - "source": inputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.hasOutput.forEach(output=>{ - outputId++; - typeId++; - data.nodes.push({ - "id" : outputId, - "name": output.name, - "group": 4 - }) - data.links.push({ - "source": capabilityId, - "target": outputId, - "type": "has_output" - }) - - data.nodes.push({ - "id": typeId, - "name": output.stateType, - "group": 6 - }) - data.links.push({ - "source": outputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.executableViaSkill.forEach(skill=>{ - skillId++; - data.nodes.push({ - "id" : skillId, - "name": skill.name, - "group": 5 - }) - data.links.push({ - "source": capabilityId, - "target": skillId, - "type": "executable_via_Skill" - }) - }) - }) - - }); -// ################################################## - - console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -355,7 +246,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -364,7 +255,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - //console.log("ended"); + console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -385,7 +276,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); function dragged(d) { - // console.log(d3.event) + console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -396,9 +287,6 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); } } - - - - + constructor() { } } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts index df9c2713..afb5fff0 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -1,15 +1,11 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter, + GraphVisualizationRouter ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - ] + declarations: [GraphVisualizationComponent] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index 5790705b..ae60048e 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,4 +1,3 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; - From e8ff41b28df8534567e69b1431a144798e51d21c Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:17:55 +0100 Subject: [PATCH 45/74] Revert "Revert "Added function to get data for ontology-visualization of each module"" This reverts commit e3e86ce7d5f9a64ca29b6727bcc3077f1b4ba03a. --- .../manufacturing-service-executor.service.ts | 4 +- .../graph-visualization.component.ts | 130 ++++++++++++++++-- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 + 4 files changed, 131 insertions(+), 12 deletions(-) diff --git a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts index 4b1a70c8..338e7fa0 100644 --- a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts +++ b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts @@ -1,8 +1,10 @@ import { Injectable } from "@angular/core"; import { HttpClient, HttpRequest, HttpHeaders, HttpParams } from "@angular/common/http"; import { Observable } from "rxjs"; -import { ServiceExecutionDescription } from "./self-description"; +//import { ServiceExecutionDescription } from "./self-description"; import { map } from 'rxjs/operators'; +import { ServiceExecutionDescription } from "app/shared/models/self-description"; + @Injectable() diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index b57ca058..2c46fd12 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -3,6 +3,8 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; +import { ModuleService } from 'app/shared/services/module.service'; + @Component({ selector: 'graph-visualization', @@ -12,11 +14,16 @@ import { window } from 'rxjs/operators'; }) export class GraphVisualizationComponent implements AfterContentInit { + constructor( + private moduleService: ModuleService + ) { + }; + @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - console.log('element ref width:'); - console.log(this.svgContainer.nativeElement.offsetWidth); + //console.log('element ref width:'); + // console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -25,7 +32,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - console.log("Die ermittelte Weite ist:" + testweite); + //console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -54,9 +61,12 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - const data = { + +// ##### DATA ###### + + + /*const data = { "nodes": [ { "id": 1, @@ -108,8 +118,107 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } +*/ +var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + var group=0; + var moduleId=0; + var capabilityId=100; + var inputId=1000; + var outputId=10000; + var skillId=100000; + var typeId=1000000; + var data= {nodes:[], links:[]}; + //################################################ + receivedData.forEach(modul => { + moduleId++; + group++; + data.nodes.push({ + "id" : moduleId, + "name": modul.name, + "group": 1 + }); + modul.capabilities.forEach(capability=>{ + capabilityId++; + data.nodes.push({ + "id" : capabilityId, + "name": capability.name, + "group": 2 + }) + data.links.push({ + "source": moduleId, + "target": capabilityId, + "type": "has_capability" + }) + capability.hasInput.forEach(input=>{ + inputId++; + typeId++; + data.nodes.push({ + "id" : inputId, + "name": input.name, + "group": 3 + }) + data.links.push({ + "source": capabilityId, + "target": inputId, + "type": "has_input" + }) + data.nodes.push({ + "id": typeId, + "name": input.stateType, + "group": 6 + }) + data.links.push({ + "source": inputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.hasOutput.forEach(output=>{ + outputId++; + typeId++; + data.nodes.push({ + "id" : outputId, + "name": output.name, + "group": 4 + }) + data.links.push({ + "source": capabilityId, + "target": outputId, + "type": "has_output" + }) + + data.nodes.push({ + "id": typeId, + "name": output.stateType, + "group": 6 + }) + data.links.push({ + "source": outputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.executableViaSkill.forEach(skill=>{ + skillId++; + data.nodes.push({ + "id" : skillId, + "name": skill.name, + "group": 5 + }) + data.links.push({ + "source": capabilityId, + "target": skillId, + "type": "executable_via_Skill" + }) + }) + }) + + }); +// ################################################## + + console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -246,7 +355,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -255,7 +364,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - console.log("ended"); + //console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -276,7 +385,7 @@ export class GraphVisualizationComponent implements AfterContentInit { function dragged(d) { - console.log(d3.event) + // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -287,6 +396,9 @@ export class GraphVisualizationComponent implements AfterContentInit { } } - constructor() { } + + + + } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts index afb5fff0..df9c2713 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -1,11 +1,15 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; +import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter + GraphVisualizationRouter, ], - declarations: [GraphVisualizationComponent] + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index ae60048e..5790705b 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,3 +1,4 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; + From a3a64336da02920c137c33a8511f1b98abf24fa6 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Sun, 29 Mar 2020 14:18:25 +0200 Subject: [PATCH 46/74] Modified node-ID assignment. Modified comments --- .../graph-visualization.component.ts | 345 +++++++----------- frontend/typings.d.ts | 5 - 2 files changed, 140 insertions(+), 210 deletions(-) delete mode 100644 frontend/typings.d.ts diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 2c46fd12..87a1ef6a 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -22,19 +22,14 @@ export class GraphVisualizationComponent implements AfterContentInit { @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - //console.log('element ref width:'); - // console.log(this.svgContainer.nativeElement.offsetWidth); - - const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen - const width = 100; //parent.innerWidth - margin.left - margin.right; + const margin = { top: 10, right: 30, bottom: 30, left: 40 }; + const width = 100; //parent.innerWidth - margin.left - margin.right; //var width= window.innerWidth; const height = 100; - const rad = 20 // Radius eines Knotens - const nodeborder = 8; // Umrandungsdicke eines Knotens + const rad = 20 // node radius + const nodeborder = 8; // node border thickness const testweite = parent.innerWidth; - //console.log("Die ermittelte Weite ist:" + testweite); - - const svg = d3.select("#graph") // Größe u. Grenzen SVG + const svg = d3.select("#graph") // size definitions for the svg .append("svg") .attr("width", width + '%') .attr("height", height+'%') @@ -44,12 +39,12 @@ export class GraphVisualizationComponent implements AfterContentInit { - const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + const colors = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours svg.append('defs').append('marker') .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //Koordinatensystem - .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('viewBox', '-0 -5 10 10') //coordinate system + .attr('refX', rad + nodeborder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -63,193 +58,141 @@ export class GraphVisualizationComponent implements AfterContentInit { -// ##### DATA ###### - - - /*const data = { - "nodes": [ - { - "id": 1, - "name": "ModulA", - "group": 1 - }, - { - "id": 2, - "name": "ModulB", - "group": 2 - }, - { - "id": 3, - "name": "ModulC", - "group": 3 - - }, { - "id": 4, - "name": "cap11", - "group": 1 - }, - { - "id": 5, - "name": "cap12", - "group": 1 - }, - { - "id": 6, - "name": "cap21", - "group": 2 - } - ], - "links": [ - - { - "source": 1, - "target": 4, - "type": "is_a" - }, - { - "source": 1, - "target": 5, - "type": "has" - }, - { - "source": 2, - "target": 6, - "type": "has" - } - ] - } -*/ +/* Get data*/ +//########################################################## var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - var group=0; - var moduleId=0; - var capabilityId=100; - var inputId=1000; - var outputId=10000; - var skillId=100000; - var typeId=1000000; - var data= {nodes:[], links:[]}; - //################################################ - receivedData.forEach(modul => { - moduleId++; - group++; - data.nodes.push({ - "id" : moduleId, - "name": modul.name, - "group": 1 - }); - modul.capabilities.forEach(capability=>{ - capabilityId++; - data.nodes.push({ - "id" : capabilityId, - "name": capability.name, - "group": 2 - }) - data.links.push({ - "source": moduleId, - "target": capabilityId, - "type": "has_capability" - }) - capability.hasInput.forEach(input=>{ - inputId++; - typeId++; - data.nodes.push({ - "id" : inputId, - "name": input.name, - "group": 3 - }) - data.links.push({ - "source": capabilityId, - "target": inputId, - "type": "has_input" - }) - data.nodes.push({ - "id": typeId, - "name": input.stateType, - "group": 6 - }) - data.links.push({ - "source": inputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.hasOutput.forEach(output=>{ - outputId++; - typeId++; - data.nodes.push({ - "id" : outputId, - "name": output.name, - "group": 4 - }) - data.links.push({ - "source": capabilityId, - "target": outputId, - "type": "has_output" - }) - - data.nodes.push({ - "id": typeId, - "name": output.stateType, - "group": 6 - }) - data.links.push({ - "source": outputId, - "target": typeId, - "type": "is_state_type" - }) - - }) - capability.executableViaSkill.forEach(skill=>{ - skillId++; - data.nodes.push({ - "id" : skillId, - "name": skill.name, - "group": 5 - }) - data.links.push({ - "source": capabilityId, - "target": skillId, - "type": "executable_via_Skill" - }) - }) - - }) +var data= {nodes:[], links:[]}; +let idMap= new Map(); // Map for saving Node-IDs +var id=0; // ID start value +receivedData.forEach(receivedModule => { //loop over all modules +id++; +idMap.set(receivedModule, id); // assigns an ID for each module-node +data.nodes.push({ // adds a node for each module +"id" : idMap.get(receivedModule), +"name": receivedModule.name, +"group": 1 // assings node-groups (here: node colour) +}); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.name, + "group": 2 + }) + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }) + + capability.hasInput.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.name, + "group": 3 + }) + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }) + }) + + capability.hasOutput.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.name, + "group": 4 + }) + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }) + }) + + capability.executableViaSkill.forEach(skill=>{ + id++; + idMap.set(skill, id) // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.name, + "group": 5 + }) + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }) + }) + + }) - }); +}); // ################################################## console.log(data); - const link = svg // Links initialisieren + const link = svg // link definitions .selectAll(".links") .data(data.links) .enter() .append("line") .style("stroke", "#aaa") .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') + .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; link.append("title") - .text(function (d) { return d.type }); - const node = svg // Nodes initialisieren + .text(function (d) { return d.type }); // get link title from data + const node = svg // node definitions .selectAll("circle") .data(data.nodes) .enter() .append("circle") .attr("r", rad) - .on('mouseover', function(){ + .on('mouseover', function(){ // function on mousover: increase radius d3.select(this).transition() .duration('2') - .attr('r', rad+5) + .attr('r', rad+6) }) - .on('mouseout', function(){ + .on('mouseout', function(){ // function on mousout: decrease radius to orgin d3.select(this).transition() .attr('r', rad) }) + .on('dblclick', doubleclick)// function on doubleclick: + .style("fill", "white") .style("stroke-width", nodeborder) - .style("stroke", function (d) { return colors(d.group); }) // Node-Farben nach Gruppenzugehörigkeit - /*.append ("text") // name als text neben node + .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group + /*.append ("text") .attr ("dx", 12) .attr ("dy", ".35em") .text(function(d){return d.name})*/ @@ -258,21 +201,21 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .on("drag", dragged)); ; - const text = svg + const text = svg // node text definitions .selectAll("text") .data(data.nodes) .enter() .append("text") - .text(function (d) { return d.name }); + .text(function (d) { return d.name }); // get text from data - const linkText = svg + const linkText = svg // link text definitions .selectAll("links") .data(data.links) .enter() .append("text") .style("fill", "#999") - .text(function (d) { return d.type }); + .text(function (d) { return d.type }); // get link text from data @@ -280,13 +223,11 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const ticked = () => { - const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; + const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; // center definitions of the svg const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - // data.nodes.indexOf[0].fx= centerWidth; - //data.nodes.indexOf[0].fy= centerHeight; text - .attr("dx", (d)=>{ + .attr("dx", (d)=>{ // restrictions for node text positions in the svg const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) @@ -296,7 +237,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) linkText - .attr("dx", (d)=>{ + .attr("dx", (d)=>{ // restrictions for link text positions in the svg d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; @@ -313,10 +254,10 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; }) - link // Anfang und Ende der Links / Bestimmung der Position nachfolgend Closure-Funtktionen um Zugriff mit this. außerhalb der Funktion - .attr("x1", (d)=> { + link + .attr("x1", (d)=> { // restrictions for link positions in the svg d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // Umwandlung des Radius in relative Zahl (in % !) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // converting radius from absolute to relative const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; return relativeSourceX+'%';}) .attr("y1", (d)=> { @@ -335,13 +276,13 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; return relativeTargetY+'%';}) - node // Bestimmung der Postion einzelner Nodes - .attr("cx", (d) => { + node + .attr("cx", (d) => { // restrictions for node positions in the svg d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; return relativeDragX+'%'; - }) // Zentrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + }) .attr("cy", (d) => { d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; @@ -349,39 +290,33 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return relativeDragY+'%';}) } - const simulation = d3.forceSimulation(data.nodes) //Simulation + const simulation = d3.forceSimulation(data.nodes) // simulation .force("link", d3.forceLink() .id(function (d) { return d.id; }) .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes - .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - - function ended() { - //console.log("ended"); - } - function mouseover(){ - d3.select(this).select("circle").transition() - .duration(750) - .attr("r", rad+10) - } + .on("tick", ticked); // tick on every step of the simulation - function mouseout(){ - d3.select(this).select("circle").transition() - .duration(750) - .attr("r", rad) - } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } + function doubleclick(){ + id++; + data.nodes.push({ + "id": id, + "name": "neighbor", + "group": 7 + }) + } function dragged(d) { diff --git a/frontend/typings.d.ts b/frontend/typings.d.ts deleted file mode 100644 index ef5c7bd6..00000000 --- a/frontend/typings.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -/* SystemJS module definition */ -declare var module: NodeModule; -interface NodeModule { - id: string; -} From ae93758b31abec29aae6945a7382339145c0ca88 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Thu, 2 Apr 2020 20:51:41 +0200 Subject: [PATCH 47/74] Added new service for node creation --- .../node-creator.service.spec.ts | 16 ++ .../node-creator.service.ts | 122 +++++++++++++++ .../graph-visualization.component.ts | 139 ++++-------------- .../graph-visualization.module.ts | 3 + 4 files changed, 168 insertions(+), 112 deletions(-) create mode 100644 frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts create mode 100644 frontend/app/own-modules/graph-visualization/node-creator.service.ts diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts new file mode 100644 index 00000000..c3ee0483 --- /dev/null +++ b/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts @@ -0,0 +1,16 @@ +/* tslint:disable:no-unused-variable */ + +import { TestBed, async, inject } from '@angular/core/testing'; +import { NodeCreatorService } from './node-creator.service'; + +describe('Service: NodeCreator', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [NodeCreatorService] + }); + }); + + it('should ...', inject([NodeCreatorService], (service: NodeCreatorService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.ts new file mode 100644 index 00000000..fbad86eb --- /dev/null +++ b/frontend/app/own-modules/graph-visualization/node-creator.service.ts @@ -0,0 +1,122 @@ +import { Injectable } from '@angular/core'; +import { ModuleService } from 'app/shared/services/module.service'; + +@Injectable({ + providedIn: 'root' +}) +export class NodeCreatorService { + apiRoot: string = "/api"; +constructor( + private moduleService: ModuleService +) {} +getAllNodes() { + + var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + const data= {nodes:[], links:[]}; + let idMap= new Map(); // Map for saving Node-IDs + var id=0; // ID start value + receivedData.forEach(receivedModule => { //loop over all modules + id++; + idMap.set(receivedModule, id); // assigns an ID for each module-node + data.nodes.push({ // adds a node for each module + "id" : idMap.get(receivedModule), + "name": receivedModule.name, + "group": 1 // assings node-groups (here: node colour) + }); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.name, + "group": 2 + }) + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }) + + capability.hasInput.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.name, + "group": 3 + }) + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }) + }) + + capability.hasOutput.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.name, + "group": 4 + }) + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }) + }) + + capability.executableViaSkill.forEach(skill=>{ + id++; + idMap.set(skill, id) // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.name, + "group": 5 + }) + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }) + }) + + }) + + }); + +return data; +} + + +} + + + + + + diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 87a1ef6a..2b712bc0 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -4,6 +4,8 @@ import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; import { ModuleService } from 'app/shared/services/module.service'; +import { ResolveStart } from '@angular/router'; +import { NodeCreatorService } from './node-creator.service'; @Component({ @@ -15,7 +17,8 @@ import { ModuleService } from 'app/shared/services/module.service'; export class GraphVisualizationComponent implements AfterContentInit { constructor( - private moduleService: ModuleService + //private moduleService: ModuleService, + private nodeCreatorService: NodeCreatorService ) { }; @@ -56,108 +59,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - - -/* Get data*/ -//########################################################## -var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); -var data= {nodes:[], links:[]}; -let idMap= new Map(); // Map for saving Node-IDs -var id=0; // ID start value -receivedData.forEach(receivedModule => { //loop over all modules -id++; -idMap.set(receivedModule, id); // assigns an ID for each module-node -data.nodes.push({ // adds a node for each module -"id" : idMap.get(receivedModule), -"name": receivedModule.name, -"group": 1 // assings node-groups (here: node colour) -}); - - receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module - id++; - idMap.set(capability, id); // assigns an ID for each capability-node - data.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.name, - "group": 2 - }) - data.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description - }) - - capability.hasInput.forEach(input=>{ - id++; - idMap.set(input, id); // assigns an ID for each input-node - data.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.name, - "group": 3 - }) - data.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }) - }) - - capability.hasOutput.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - data.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.name, - "group": 4 - }) - data.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }) - }) - - capability.executableViaSkill.forEach(skill=>{ - id++; - idMap.set(skill, id) // adds a node for each skill of current capability - data.nodes.push({ - "id" : idMap.get(skill), - "name": skill.name, - "group": 5 - }) - data.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" - }) - }) - - }) - -}); -// ################################################## +var data= this.nodeCreatorService.getAllNodes(); // load data created by node-creator.service console.log(data); @@ -187,8 +89,17 @@ data.nodes.push({ // adds a node for each module d3.select(this).transition() .attr('r', rad) }) - .on('dblclick', doubleclick)// function on doubleclick: - + .on('dblclick', function(d){ const node= { "id":30, + "name": "neighbor", + "group": 7} + data.nodes.push(node) + data.links.push({ + "source": d.id, + "target":30 + }) + + console.log(d);// function on doubleclick: + }) .style("fill", "white") .style("stroke-width", nodeborder) .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group @@ -309,15 +220,19 @@ data.nodes.push({ // adds a node for each module d.fx = d.x; d.fy = d.y; } - function doubleclick(){ - id++; - data.nodes.push({ - "id": id, - "name": "neighbor", - "group": 7 + /*function doubleclick(d){ + + const node= {x: 500, y:50, "id":1000, + "name": "neighbor", + "group": 7} + data.nodes.push(node) + data.links.push({ + "source": d.id, + "target":1000 }) + console.log(d); } - +*/ function dragged(d) { // console.log(d3.event) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts index df9c2713..711024bc 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; import { ModuleService } from 'app/shared/services/module.service'; +import { NodeCreatorService } from './node-creator.service'; @NgModule({ imports: [ @@ -10,6 +11,8 @@ import { ModuleService } from 'app/shared/services/module.service'; declarations: [GraphVisualizationComponent], providers: [ ModuleService, + NodeCreatorService + ] }) export class GraphVisualizationModule { } From 60fad8a0af6168ceb730dfb12c0cf07136c55aac Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 1 May 2020 15:41:52 +0200 Subject: [PATCH 48/74] Visualization of chosen module-graph possible. --- .../node-creator.service.ts | 17 +++++++-- .../components/sidebar/sidebar.component.html | 1 + .../graph-visualization.component.ts | 35 +++++++++++++++---- .../graph-visualization.routing.ts | 12 +++---- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.ts index fbad86eb..89c940ec 100644 --- a/frontend/app/own-modules/graph-visualization/node-creator.service.ts +++ b/frontend/app/own-modules/graph-visualization/node-creator.service.ts @@ -1,5 +1,6 @@ import { Injectable } from '@angular/core'; import { ModuleService } from 'app/shared/services/module.service'; +import { containsElement } from '@angular/animations/browser/src/render/shared'; @Injectable({ providedIn: 'root' @@ -9,13 +10,14 @@ export class NodeCreatorService { constructor( private moduleService: ModuleService ) {} -getAllNodes() { +getAllNodes(moduleName:String) { var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const data= {nodes:[], links:[]}; let idMap= new Map(); // Map for saving Node-IDs var id=0; // ID start value receivedData.forEach(receivedModule => { //loop over all modules + if(receivedModule.name==moduleName|| moduleName== undefined){ // creates nodes for chosen modules id++; idMap.set(receivedModule, id); // assigns an ID for each module-node data.nodes.push({ // adds a node for each module @@ -106,9 +108,18 @@ getAllNodes() { }) }) - - }); + } + }); + if(id==0){ //if module does not exist, show error-node + id++; + data.nodes.push({ + "id" : id, + "name": "<<>>", + "group": 1 + }) + } + return data; } diff --git a/frontend/src/layout/components/sidebar/sidebar.component.html b/frontend/src/layout/components/sidebar/sidebar.component.html index fab4a892..e5833dae 100644 --- a/frontend/src/layout/components/sidebar/sidebar.component.html +++ b/frontend/src/layout/components/sidebar/sidebar.component.html @@ -47,6 +47,7 @@
+   Graph-Visualization diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 2b712bc0..5a918e16 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -1,11 +1,15 @@ -import { Component, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component,OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; import { ModuleService } from 'app/shared/services/module.service'; -import { ResolveStart } from '@angular/router'; +import { ResolveStart, ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; +import { getModuleFactory__POST_R3__ } from '@angular/core/src/linker/ng_module_factory_loader'; +import { defineDirective } from '@angular/core/src/render3'; + + @Component({ @@ -14,15 +18,30 @@ import { NodeCreatorService } from './node-creator.service'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit { +export class GraphVisualizationComponent implements AfterContentInit, OnInit {name:String; constructor( - //private moduleService: ModuleService, + private route: ActivatedRoute, private nodeCreatorService: NodeCreatorService ) { }; @ViewChild('g') svgContainer: ElementRef; + moduleName: string; + ngOnInit(): void + { + + + // console.log("Der Name ist :"+this.moduleName); + +//console.log(this.route.queryParams); + + //.subscribe(params=>params.name) + + //this.name= params.name; + + } + ngAfterContentInit(): void { const margin = { top: 10, right: 30, bottom: 30, left: 40 }; @@ -58,10 +77,11 @@ export class GraphVisualizationComponent implements AfterContentInit { .attr('fill', '#999') .style('stroke', 'none'); +this.route.params.subscribe(p=>{ + this.moduleName=p['moduleName']; +}) +var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service -var data= this.nodeCreatorService.getAllNodes(); // load data created by node-creator.service - - console.log(data); const link = svg // link definitions .selectAll(".links") @@ -246,6 +266,7 @@ var data= this.nodeCreatorService.getAllNodes(); // load data created by node-cr } } + diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts index ded3fe38..ba430b6c 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts @@ -1,15 +1,13 @@ import { Routes, RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import {GraphVisualizationComponent} from './graph-visualization.component' +import {GraphVisualizationComponent} from './graph-visualization.component'; const routes: Routes = [ - { path: '', + { path: 'modules/:moduleName', component: GraphVisualizationComponent, - data: { - title: 'Graph-Visualization' - }, - children: [] - + + }, + {path:'modules', component: GraphVisualizationComponent } ]; From cfb1e747f4891b68f3533f993baf13c08fddfbbd Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 1 May 2020 15:50:51 +0200 Subject: [PATCH 49/74] Modified some stylings --- .../graph-visualization.component.ts | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 5a918e16..b705e793 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -99,28 +99,16 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .data(data.nodes) .enter() .append("circle") - .attr("r", rad) - .on('mouseover', function(){ // function on mousover: increase radius - d3.select(this).transition() - .duration('2') - .attr('r', rad+6) + .attr("r",function(d){ + if(d.group==1){return rad+8} + else{return rad} }) - .on('mouseout', function(){ // function on mousout: decrease radius to orgin - d3.select(this).transition() - .attr('r', rad) - }) - .on('dblclick', function(d){ const node= { "id":30, - "name": "neighbor", - "group": 7} - data.nodes.push(node) - data.links.push({ - "source": d.id, - "target":30 + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .style("fill", function(d){ + if(d.group==1){return "black"} + else{return "whitesmoke"} }) - - console.log(d);// function on doubleclick: - }) - .style("fill", "white") .style("stroke-width", nodeborder) .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group /*.append ("text") @@ -137,7 +125,10 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .data(data.nodes) .enter() .append("text") - + .attr("font-weight", function(d){ + if(d.group==1){return "bold"} + else{return "normal"} + }) .text(function (d) { return d.name }); // get text from data const linkText = svg // link text definitions @@ -146,6 +137,7 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .enter() .append("text") .style("fill", "#999") + .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data @@ -265,6 +257,25 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre //d.fy = null; } + function mouseover(d){ // function on mousover: increase radius + if(d.group==1){ + d3.select(this).transition() + .duration('2') + .attr('r', rad+12)} + else{ + d3.select(this).transition() + .duration('2') + .attr('r', rad+6)} + } + function mouseout(d){ // function on mousout: decrease radius to orgin + if(d.group==1){ + d3.select(this).transition() + .attr('r', rad+8) + } + else{ d3.select(this).transition() + .attr('r', rad)} + } + } From 35ceebf7185442942bfa9cac21e7287b510a6ece Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Wed, 27 May 2020 21:17:44 +0200 Subject: [PATCH 50/74] Adding nodes on doubleclick possible --- .../graph-visualization.component.ts | 569 +++++++++++------- 1 file changed, 363 insertions(+), 206 deletions(-) diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index b705e793..0cd91854 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -1,13 +1,11 @@ -import { Component,OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" -import { enterView } from '@angular/core/src/render3/state'; -import { pathToFileURL } from 'url'; -import { window } from 'rxjs/operators'; -import { ModuleService } from 'app/shared/services/module.service'; -import { ResolveStart, ActivatedRoute } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; -import { getModuleFactory__POST_R3__ } from '@angular/core/src/linker/ng_module_factory_loader'; -import { defineDirective } from '@angular/core/src/render3'; + + + + @@ -18,55 +16,76 @@ import { defineDirective } from '@angular/core/src/render3'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit, OnInit {name:String; +export class GraphVisualizationComponent implements AfterContentInit, OnInit { + name: String; + svg: any; + link: any; + /** The displayed node name*/ + text: any; + /** The displayed link description */ + linkText: any; + /** The displayed node */ + node: any; + /** Loaded data: Connected modules with their capabilities and skills*/ + data: any; + /**Returns different colors automatically. The node-border color is the same for all node-group members*/ + color: any; +/**Simulation of the force directed graph */ + simulation: any; + + margin = { top: 10, right: 30, bottom: 30, left: 40 }; + width = 100; + height = 100; +/**Defines the nodeborder thickness*/ + nodeborder = 8; + /**Defines the standard radius of a node*/ + rad = 20; + + index=0; constructor( private route: ActivatedRoute, private nodeCreatorService: NodeCreatorService - ) { + ) { }; @ViewChild('g') svgContainer: ElementRef; moduleName: string; - ngOnInit(): void - { - + ngOnInit(): void { - // console.log("Der Name ist :"+this.moduleName); -//console.log(this.route.queryParams); + // console.log("Der Name ist :"+this.moduleName); + + //console.log(this.route.queryParams); //.subscribe(params=>params.name) - + //this.name= params.name; } ngAfterContentInit(): void { - const margin = { top: 10, right: 30, bottom: 30, left: 40 }; - const width = 100; //parent.innerWidth - margin.left - margin.right; - //var width= window.innerWidth; - const height = 100; - const rad = 20 // node radius - const nodeborder = 8; // node border thickness - const testweite = parent.innerWidth; - const svg = d3.select("#graph") // size definitions for the svg + + console.log(this.svgContainer); + + + this.svg = d3.select("#graph") // size definitions for the svg .append("svg") - .attr("width", width + '%') - .attr("height", height+'%') + .attr("width", this.width + '%') + .attr("height", this.height + '%') .append("g") .attr("transform", - "translate(" + margin.left + "," + margin.top + ")"); + "translate(" + this.margin.left + "," + this.margin.top + ")"); - const colors = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours + this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - svg.append('defs').append('marker') + this.svg.append('defs').append('marker') .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', rad + nodeborder) // arrow position and dimensions + .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -77,210 +96,348 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit {na .attr('fill', '#999') .style('stroke', 'none'); -this.route.params.subscribe(p=>{ - this.moduleName=p['moduleName']; -}) -var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }) + this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + + + //############################################################################################################## + + this.simulation = d3.forceSimulation(this.data.nodes); + // simulation.force('link').links(data.links); // simulation + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links) + ) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - const link = svg // link definitions - .selectAll(".links") - .data(data.links) - .enter() - .append("line") - .style("stroke", "#aaa") + this.refresh(); + } + + /** + * Draws the graph, executed first on AfterContentInit and on every doubleclick on a node + */ + refresh() { + console.log("refresh starting..") + console.log(this.data.nodes) + + + /* this.simulation.force("link").links(this.data.links); + this.simulation.force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links) + + + )*/ +//this.link.exit().remove(); +//this.node.exit().remove(); + + + this.link = this.svg // link definitions + .selectAll(".link") + .data(this.data.links, function (d){return d.target.id}) + /*.join(enter => enter.append("line") + .append("line").style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)'), // arrow on end of the link + exit => exit.remove())*/ + + var linkEnter=this.link.enter() + .append("line").style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; - link.append("title") - .text(function (d) { return d.type }); // get link title from data - const node = svg // node definitions - .selectAll("circle") - .data(data.nodes) - .enter() - .append("circle") - .attr("r",function(d){ - if(d.group==1){return rad+8} - else{return rad} + linkEnter.append("title") + .text(function (d){return d.type}) + this.link = this.link.merge(linkEnter); + this.link.exit().remove(); + + this.node = this.svg.selectAll(".node") + .data(this.data.nodes, function (d){return d.id}) + .join(enter => enter.append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8 } + else { return this.rad } + }) + .on('mouseover', this.mouseover) + .on('mouseout', this.mouseout) + .style("fill", function (d) { + if (d.group == 1) { return "black" } + else { return "whitesmoke" } + }) + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }) + .on('dblclick', this.nodeDoubleClick) + .call(d3.drag() + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)), + update => update.style("fill", "red"), + exit => exit.remove()) + + + /*var nodeEnter= this.node.enter().append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8 } + else { return this.rad } }) - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .style("fill", function(d){ - if(d.group==1){return "black"} - else{return "whitesmoke"} + /*nodeEnter.append("title") + .text(function(d){return d.id}) + nodeEnter.append("text") + .text(function (d){return d.name})*/ + /* nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseout', this.mouseout); + nodeEnter.style("fill", function (d) { + if (d.group == 1) { return "black" } + else { return "whitesmoke" } }) - .style("stroke-width", nodeborder) - .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group /*.append ("text") .attr ("dx", 12) .attr ("dy", ".35em") .text(function(d){return d.name})*/ - .call(d3.drag() - .on("start", dragstarted) - .on("drag", dragged)); - ; - - const text = svg // node text definitions - .selectAll("text") - .data(data.nodes) - .enter() - .append("text") - .attr("font-weight", function(d){ - if(d.group==1){return "bold"} - else{return "normal"} - }) - .text(function (d) { return d.name }); // get text from data +/* + nodeEnter.on('dblclick', this.nodeDoubleClick); + nodeEnter.call(d3.drag() + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)); + this.node=this.node.merge(nodeEnter); + this.node.exit().remove(); + + console.log(this.node) + */ + + this.text = this.svg.selectAll("text").data(this.data.nodes) + /*.join(enter=> enter.append("text") + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold" } else { return "normal" }}) + .text(function (d) { console.log(d.name);return d.name }), + exit=> exit.remove() )*/ - const linkText = svg // link text definitions - .selectAll("links") - .data(data.links) - .enter() - .append("text") + + var textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name}) + + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold" } + else { return "normal" } + }) + .text(function (d) { console.log(d.name); + return d.name }); // get text from data + this.text=this.text.merge(textEnter); +//this.text.exit().remove(); + + this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) + .join(enter=> enter.append("text") + .style("fill", "#999") + .attr("font-style", "italic") + .text(function (d) { return d.type }), + exit=> exit.remove()) + + + + + /* var linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data + this.linkText=this.linkText.merge(linkTextEnter); +//this.linkText.exit().remove(); +*/ + + this.simulation.nodes(this.data.nodes); + this.simulation.force("link").links(this.data.links); + //########################################################################################################### + } +/** + * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element + * @param d The position of every graph element (i.e. node, link...) + */ + ticked = () => { + const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg + const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; + + + this.text + .attr("dx", (d) => { // restrictions for node text positions in the svg + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%' + }) + .attr("dy", (d) => { + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%' + }) + this.linkText + .attr("dx", (d) => { // restrictions for link text positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; + }) + .attr("dy", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; + }) - + this.link + .attr("x1", (d) => { // restrictions for link positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX + '%'; + }) + .attr("y1", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY + '%'; + }) + .attr("x2", (d) => { + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX + '%'; + }) + .attr("y2", (d) => { + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY + '%'; + }) + this.node + .attr("cx", (d) => { + d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX + '%'; + }) + .attr("cy", (d) => { + d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY + '%'; + }) + } - const ticked = () => { - const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; // center definitions of the svg - const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - - text - .attr("dx", (d)=>{ // restrictions for node text positions in the svg - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) - .attr("dy", (d) => { - const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) - - linkText - .attr("dx", (d)=>{ // restrictions for link text positions in the svg - d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(width- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceX-relativeTargetX)/2)+ relativeTargetX))) +'%'; - }) - .attr("dy", (d)=>{ - d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; - }) - - link - .attr("x1", (d)=> { // restrictions for link positions in the svg - d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // converting radius from absolute to relative - const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX+'%';}) - .attr("y1", (d)=> { - d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY+'%';}) - .attr("x2", (d)=> { - d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX+'%';}) - .attr("y2", (d)=> { - d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY+'%';}) - - node - .attr("cx", (d) => { // restrictions for node positions in the svg - d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX+'%'; - }) - .attr("cy", (d) => { - d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) - const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY+'%';}) - } - const simulation = d3.forceSimulation(data.nodes) // simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(300) - .links(data.links) - ) - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes - + dragstarted = (d) => { + if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } - - - .on("tick", ticked); // tick on every step of the simulation - function dragstarted(d) { - if (!d3.event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - /*function doubleclick(d){ - - const node= {x: 500, y:50, "id":1000, - "name": "neighbor", - "group": 7} - data.nodes.push(node) - data.links.push({ - "source": d.id, - "target":1000 - }) - console.log(d); - } -*/ - function dragged(d) { - // console.log(d3.event) - 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; - } + /** + * Drag function, executed on every drag movement + * @param d The dragged node + */ + dragged(d) { + // console.log(d3.event) + d.fx = d3.event.x; + d.fy = d3.event.y; + } - function mouseover(d){ // function on mousover: increase radius - if(d.group==1){ - d3.select(this).transition() + dragended(d) { + //if (!d3.event.active) this.simulation.alphaTarget(0); + //d.fx = null; + // d.fy = null; + } +/** + * Mouseover function, executed when mouse over node. The radius of the node will increase. + * @param d node under the cursor + */ + mouseover(d) { + if (d.group == 1) { + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 12) + } + else { + d3.select(this).transition() .duration('2') - .attr('r', rad+12)} - else{ + .attr('r', 20 + 6) + } + } +/** + * Mousout function, executed when mouse cursor leaves the node + * @param d node wich is left by the cursor after mouseover + */ + mouseout(d) { // function on mousout: decrease this.radius to orgin + if (d.group == 1) { d3.select(this).transition() - .duration('2') - .attr('r', rad+6)} + .attr('r', 20 + 8) } - function mouseout(d){ // function on mousout: decrease radius to orgin - if(d.group==1){ - d3.select(this).transition() - .attr('r', rad+8) - } - else{ d3.select(this).transition() - .attr('r', rad)} + else { + d3.select(this).transition() + .attr('r', 20) } - + } +/** + * Doubleclick function, executed on every doubleclick on a node + *@param d The clicked node + */ + + nodeDoubleClick=(d)=> { +//this.simulation.stop(); + this.index++; + + const newNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(newNode); + this.data.links.push({ "source": d.id, "target": newNode, "type": "testclick" }) + /* + this.link.exit().remove(); + this.linkText.exit().remove(); + this.text.exit().remove(); */ + /*this.simulation.force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + + // ) + // .force("collision", d3.forceCollide(40)) + // .on("tick", this.ticked) // tick on every ste + -400)) // node magnetism /attraction + //.force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) + //simulation.restart(); //node oben links + fehlermeldungen + // this.simulation.nodes(this.data.nodes) + // this.simulation.force('link').links(this.data.links) + //simulation.force('nodes').nodes(data.nodes) + //node.data(data.nodes); + //link.data(data.links); + //node.enter().append("circle"); + //this.node.exit().remove(); + //link.exit().remove(); + //linkText.exit().remove(); + //text.exit().remove(); + // this.node = this.node.data(this.data.nodes); + //link= link.data(data.links) + //node.selectAll('circle').remove(); + //this.simulation.restart(); */ + this.refresh(); + // this.simulation.restart(); + //console.log(this.data.nodes) } - - - - - -} +} \ No newline at end of file From d036e9af4d5d1386b8518e0fb56e5e4ad1afe7c5 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 2 Jun 2020 18:50:48 +0200 Subject: [PATCH 51/74] Modified some code --- .../node-creator.service.ts | 1 + .../graph-visualization.component.ts | 271 +++++------------- 2 files changed, 79 insertions(+), 193 deletions(-) diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.ts index 89c940ec..822850e7 100644 --- a/frontend/app/own-modules/graph-visualization/node-creator.service.ts +++ b/frontend/app/own-modules/graph-visualization/node-creator.service.ts @@ -10,6 +10,7 @@ export class NodeCreatorService { constructor( private moduleService: ModuleService ) {} +/** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ getAllNodes(moduleName:String) { var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts index 0cd91854..7a368116 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts @@ -4,12 +4,6 @@ import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; - - - - - - @Component({ selector: 'graph-visualization', encapsulation: ViewEncapsulation.None, @@ -33,6 +27,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { /**Simulation of the force directed graph */ simulation: any; + margin = { top: 10, right: 30, bottom: 30, left: 40 }; width = 100; height = 100; @@ -51,24 +46,42 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { @ViewChild('g') svgContainer: ElementRef; moduleName: string; - ngOnInit(): void { - + ngOnInit(): void {} - // console.log("Der Name ist :"+this.moduleName); - //console.log(this.route.queryParams); - - //.subscribe(params=>params.name) + ngAfterContentInit(): void { + + + + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }) + this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + + this.setSvg(); + this.setSimulation(); + } - //this.name= params.name; +/**Defines settings of the simulation */ +setSimulation(){ + this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - } + this.refreshSimulation(); +} - ngAfterContentInit(): void { - console.log(this.svgContainer); - +/**Defines settings of the SVG and the color-scheme of the nodes */ + setSvg(){ this.svg = d3.select("#graph") // size definitions for the svg .append("svg") @@ -82,7 +95,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - this.svg.append('defs').append('marker') + this.svg.append('defs').append('marker') // marker/ arrow settings .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions @@ -94,139 +107,49 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .append('svg:path') .attr('d', 'M 0,-5 L 10 ,0 L 0,5') .attr('fill', '#999') - .style('stroke', 'none'); - - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }) - this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service - + .style('stroke', 'none');} - //############################################################################################################## - this.simulation = d3.forceSimulation(this.data.nodes); - // simulation.force('link').links(data.links); // simulation - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links) - - ) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - - this.refresh(); - } /** - * Draws the graph, executed first on AfterContentInit and on every doubleclick on a node + * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ - refresh() { - console.log("refresh starting..") - console.log(this.data.nodes) - - - /* this.simulation.force("link").links(this.data.links); - this.simulation.force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links) - - - )*/ -//this.link.exit().remove(); -//this.node.exit().remove(); - - - this.link = this.svg // link definitions - .selectAll(".link") - .data(this.data.links, function (d){return d.target.id}) - /*.join(enter => enter.append("line") - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)'), // arrow on end of the link - exit => exit.remove())*/ - - var linkEnter=this.link.enter() + refreshSimulation() { + this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id}) + var linkEnter=this.link.enter() //enter-selection .append("line").style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; linkEnter.append("title") .text(function (d){return d.type}) - this.link = this.link.merge(linkEnter); - this.link.exit().remove(); + this.link = this.link.merge(linkEnter); // merge old elements with entered elements + this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - this.node = this.svg.selectAll(".node") - .data(this.data.nodes, function (d){return d.id}) - .join(enter => enter.append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8 } - else { return this.rad } - }) - .on('mouseover', this.mouseover) - .on('mouseout', this.mouseout) - .style("fill", function (d) { - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - }) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }) - .on('dblclick', this.nodeDoubleClick) - .call(d3.drag() - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)), - update => update.style("fill", "red"), - exit => exit.remove()) - - - /*var nodeEnter= this.node.enter().append("circle") + this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id}) + + var nodeEnter= this.node.enter().append("circle") .attr("r", (d) => { if (d.group == 1) { return this.rad + 8 } else { return this.rad } }) - /*nodeEnter.append("title") - .text(function(d){return d.id}) - nodeEnter.append("text") - .text(function (d){return d.name})*/ - /* nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseover', this.mouseover); nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", function (d) { - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - }) + nodeEnter.style("fill", this.setNodeStyle) .style("stroke-width", this.nodeborder) .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group - /*.append ("text") - .attr ("dx", 12) - .attr ("dy", ".35em") - .text(function(d){return d.name})*/ -/* nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() + nodeEnter.call(d3.drag() // reactions when dragging a node .on("start", this.dragstarted) .on("drag", this.dragged) .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); - this.node.exit().remove(); + this.node=this.node.merge(nodeEnter); // merge old elements with entered elements + this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + - console.log(this.node) - */ - this.text = this.svg.selectAll("text").data(this.data.nodes) - /*.join(enter=> enter.append("text") - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold" } else { return "normal" }}) - .text(function (d) { console.log(d.name);return d.name }), - exit=> exit.remove() )*/ - - - var textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name}) + var textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name}) .attr("font-weight", function (d) { if (d.group == 1) { return "bold" } @@ -235,30 +158,20 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .text(function (d) { console.log(d.name); return d.name }); // get text from data this.text=this.text.merge(textEnter); -//this.text.exit().remove(); + this.text.exit().remove(); this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) - .join(enter=> enter.append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type }), - exit=> exit.remove()) - - - - - /* var linkTextEnter=this.linkText.enter().append("text") + var linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data this.linkText=this.linkText.merge(linkTextEnter); -//this.linkText.exit().remove(); -*/ + this.linkText.exit().remove(); + - this.simulation.nodes(this.data.nodes); + this.simulation.nodes(this.data.nodes); // simualtion uses current data this.simulation.force("link").links(this.data.links); - - //########################################################################################################### + } /** * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element @@ -267,8 +180,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { ticked = () => { const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - + this.text .attr("dx", (d) => { // restrictions for node text positions in the svg const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; @@ -340,36 +252,33 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { }) } - +/** + * Function executed on a drag start + */ dragstarted = (d) => { if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } - - /** * Drag function, executed on every drag movement * @param d The dragged node */ dragged(d) { - // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } dragended(d) { - //if (!d3.event.active) this.simulation.alphaTarget(0); - //d.fx = null; - // d.fy = null; + } /** * Mouseover function, executed when mouse over node. The radius of the node will increase. * @param d node under the cursor */ mouseover(d) { - if (d.group == 1) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node d3.select(this).transition() .duration('2') .attr('r', 20 + 12) @@ -381,11 +290,11 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { } } /** - * Mousout function, executed when mouse cursor leaves the node + * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. * @param d node wich is left by the cursor after mouseover */ - mouseout(d) { // function on mousout: decrease this.radius to orgin - if (d.group == 1) { + mouseout(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node d3.select(this).transition() .attr('r', 20 + 8) } @@ -398,46 +307,22 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { * Doubleclick function, executed on every doubleclick on a node *@param d The clicked node */ - nodeDoubleClick=(d)=> { -//this.simulation.stop(); this.index++; - - const newNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(newNode); - this.data.links.push({ "source": d.id, "target": newNode, "type": "testclick" }) - /* - this.link.exit().remove(); - this.linkText.exit().remove(); - this.text.exit().remove(); */ - /*this.simulation.force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links)) - - // ) - // .force("collision", d3.forceCollide(40)) - // .on("tick", this.ticked) // tick on every ste - -400)) // node magnetism /attraction - //.force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) - //simulation.restart(); //node oben links + fehlermeldungen - // this.simulation.nodes(this.data.nodes) - // this.simulation.force('link').links(this.data.links) - //simulation.force('nodes').nodes(data.nodes) - //node.data(data.nodes); - //link.data(data.links); - //node.enter().append("circle"); - //this.node.exit().remove(); - //link.exit().remove(); - //linkText.exit().remove(); - //text.exit().remove(); - // this.node = this.node.data(this.data.nodes); - //link= link.data(data.links) - //node.selectAll('circle').remove(); - //this.simulation.restart(); */ - this.refresh(); - // this.simulation.restart(); - //console.log(this.data.nodes) + const testNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(testNode); + this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }) + this.refreshSimulation(); + } + + /** Returns the color of the inner circle of a node */ + setNodeStyle(d){ + if (d.group == 1) { return "black" } + else { return "whitesmoke" } + } +/** Returns the node border color for each node-group */ + setNodeBorderStyle=(d)=>{ + return this.color(d.group); } } \ No newline at end of file From 97d575242db37bf47b6af6b0b95a9817f07fa051 Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 14 May 2021 15:43:20 +0200 Subject: [PATCH 52/74] First step to fixing the graph visualization --- .../routes/capabilities/capability.service.ts | 3 + .../production-modules/module.service.ts | 1 - frontend/app/layout/layout-routing.module.ts | 28 -- .../node-creator.service.spec.ts | 16 - .../node-creator.service.ts | 134 ------- .../manufacturing-service-executor.service.ts | 44 --- frontend/src/layout/layout-routing.module.ts | 1 + .../graph-visualization/D3GraphData.ts | 29 ++ .../graph-visualization.component.html | 0 .../graph-visualization.component.scss | 0 .../graph-visualization.component.ts | 336 ++++++++++++++++++ .../graph-visualization.module.ts | 20 +- .../graph-visualization.routing.ts | 16 +- .../node-creator.service.spec.ts | 16 + .../node-creator.service.ts | 89 +++++ .../graph-visualization.component.ts | 328 ----------------- 16 files changed, 492 insertions(+), 569 deletions(-) delete mode 100644 frontend/app/layout/layout-routing.module.ts delete mode 100644 frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts delete mode 100644 frontend/app/own-modules/graph-visualization/node-creator.service.ts delete mode 100644 frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts create mode 100644 frontend/src/modules/graph-visualization/D3GraphData.ts rename frontend/src/{own-modules => modules}/graph-visualization/graph-visualization.component.html (100%) rename frontend/{app/own-modules => src/modules}/graph-visualization/graph-visualization.component.scss (100%) create mode 100644 frontend/src/modules/graph-visualization/graph-visualization.component.ts rename frontend/src/{own-modules => modules}/graph-visualization/graph-visualization.module.ts (55%) rename frontend/src/{own-modules => modules}/graph-visualization/graph-visualization.routing.ts (53%) create mode 100644 frontend/src/modules/graph-visualization/node-creator.service.spec.ts create mode 100644 frontend/src/modules/graph-visualization/node-creator.service.ts delete mode 100644 frontend/src/own-modules/graph-visualization/graph-visualization.component.ts diff --git a/backend/src/routes/capabilities/capability.service.ts b/backend/src/routes/capabilities/capability.service.ts index d10de521..7bee6fb8 100644 --- a/backend/src/routes/capabilities/capability.service.ts +++ b/backend/src/routes/capabilities/capability.service.ts @@ -199,6 +199,9 @@ export class CapabilityService { } }`); const capabilities = converter.convertToDefinition(queryResult.results.bindings, capabilityMapping).getFirstRootElement() as CapabilityDto[]; + console.log("backend caps"); + console.log(capabilities); + return capabilities; } catch (error) { console.error(`Error while returning capabilities of skill ${skillIri}, ${error}`); diff --git a/backend/src/routes/production-modules/module.service.ts b/backend/src/routes/production-modules/module.service.ts index 2935dc10..d4aae3a3 100644 --- a/backend/src/routes/production-modules/module.service.ts +++ b/backend/src/routes/production-modules/module.service.ts @@ -50,7 +50,6 @@ export class ModuleService { const moduleSkillDtos = await this.skillService.getSkillsOfModule(moduleDto.iri); moduleDto.skillDtos = moduleSkillDtos; } - return productionModuleDtos; } diff --git a/frontend/app/layout/layout-routing.module.ts b/frontend/app/layout/layout-routing.module.ts deleted file mode 100644 index e7acd6b0..00000000 --- a/frontend/app/layout/layout-routing.module.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { NgModule } from '@angular/core'; -import { Routes, RouterModule } from '@angular/router'; -import { LayoutComponent } from './layout.component'; - -const routes: Routes = [ - { - path: '', - component: LayoutComponent, - children: [ - { path: '', redirectTo: 'dashboard', pathMatch: 'prefix' }, - { path: 'module-management', loadChildren: '../own-modules/module-management/module-management.module#ModuleManagementModule'}, - { path: 'order-management', loadChildren: '../own-modules/order-management/order-management.module#OrderManagementModule'}, - { path: 'kpi-dashboard', loadChildren: '../own-modules/kpi-dashboard/kpi-dashboard.module#KpiDashboardModule'}, - { path: 'reconfiguration-overview', loadChildren: '../own-modules/reconfiguration-overview/reconfiguration-overview.module#ReconfigurationOverviewModule'}, - { path: 'ops-configuration', loadChildren: '../own-modules/ops-configuration/ops-configuration.module#OpsConfigurationModule'}, - { path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardModule' }, - { path: 'charts', loadChildren: './charts/charts.module#ChartsModule' }, - {path:'graph-visualization', loadChildren: '../own-modules/graph-visualization/graph-visualization.module#GraphVisualizationModule'}, - { path: 'blank-page', loadChildren: './blank-page/blank-page.module#BlankPageModule' } - ] - } -]; - -@NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] -}) -export class LayoutRoutingModule {} diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts deleted file mode 100644 index c3ee0483..00000000 --- a/frontend/app/own-modules/graph-visualization/node-creator.service.spec.ts +++ /dev/null @@ -1,16 +0,0 @@ -/* tslint:disable:no-unused-variable */ - -import { TestBed, async, inject } from '@angular/core/testing'; -import { NodeCreatorService } from './node-creator.service'; - -describe('Service: NodeCreator', () => { - beforeEach(() => { - TestBed.configureTestingModule({ - providers: [NodeCreatorService] - }); - }); - - it('should ...', inject([NodeCreatorService], (service: NodeCreatorService) => { - expect(service).toBeTruthy(); - })); -}); diff --git a/frontend/app/own-modules/graph-visualization/node-creator.service.ts b/frontend/app/own-modules/graph-visualization/node-creator.service.ts deleted file mode 100644 index 822850e7..00000000 --- a/frontend/app/own-modules/graph-visualization/node-creator.service.ts +++ /dev/null @@ -1,134 +0,0 @@ -import { Injectable } from '@angular/core'; -import { ModuleService } from 'app/shared/services/module.service'; -import { containsElement } from '@angular/animations/browser/src/render/shared'; - -@Injectable({ - providedIn: 'root' -}) -export class NodeCreatorService { - apiRoot: string = "/api"; -constructor( - private moduleService: ModuleService -) {} -/** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ -getAllNodes(moduleName:String) { - - var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - const data= {nodes:[], links:[]}; - let idMap= new Map(); // Map for saving Node-IDs - var id=0; // ID start value - receivedData.forEach(receivedModule => { //loop over all modules - if(receivedModule.name==moduleName|| moduleName== undefined){ // creates nodes for chosen modules - id++; - idMap.set(receivedModule, id); // assigns an ID for each module-node - data.nodes.push({ // adds a node for each module - "id" : idMap.get(receivedModule), - "name": receivedModule.name, - "group": 1 // assings node-groups (here: node colour) - }); - - receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module - id++; - idMap.set(capability, id); // assigns an ID for each capability-node - data.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.name, - "group": 2 - }) - data.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description - }) - - capability.hasInput.forEach(input=>{ - id++; - idMap.set(input, id); // assigns an ID for each input-node - data.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.name, - "group": 3 - }) - data.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }) - }) - - capability.hasOutput.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - data.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.name, - "group": 4 - }) - data.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }) - }) - - capability.executableViaSkill.forEach(skill=>{ - id++; - idMap.set(skill, id) // adds a node for each skill of current capability - data.nodes.push({ - "id" : idMap.get(skill), - "name": skill.name, - "group": 5 - }) - data.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" - }) - }) - - }) - } - - }); - if(id==0){ //if module does not exist, show error-node - id++; - data.nodes.push({ - "id" : id, - "name": "<<>>", - "group": 1 - }) - } - -return data; -} - - -} - - - - - - diff --git a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts b/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts deleted file mode 100644 index 338e7fa0..00000000 --- a/frontend/app/own-modules/module-management/manufacturing-service-executor.service.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { Injectable } from "@angular/core"; -import { HttpClient, HttpRequest, HttpHeaders, HttpParams } from "@angular/common/http"; -import { Observable } from "rxjs"; -//import { ServiceExecutionDescription } from "./self-description"; -import { map } from 'rxjs/operators'; -import { ServiceExecutionDescription } from "app/shared/models/self-description"; - - -@Injectable() - -export class ManufacturingServiceExecutor { - - constructor(private http: HttpClient) {} - - executeService(executionDescription:ServiceExecutionDescription) { - console.log(`service called`); - console.log(executionDescription); - - // // construct the request - // let headers = new HttpHeaders(); - // executionDescription.parameters.forEach(parameter => { - // if (parameter.getShortType() == "HeaderParameter") { - // headers.set(parameter.name, parameter.value); - // } - // }); - - // let queryParams = new HttpParams(); - // executionDescription.parameters.forEach(parameter => { - // if (parameter.getShortType() == "QueryParameter") { - // queryParams.set(parameter.name, parameter.value); - // } - // }); - - // let request = new HttpRequest(executionDescription.methodType, executionDescription.fullPath, { - // "headers": headers, - // "params": queryParams - // }) - - // Send - this.http.post(`api/service-executions`, executionDescription).subscribe(res => {console.log(JSON.stringify(res))}); - } - - -} \ No newline at end of file diff --git a/frontend/src/layout/layout-routing.module.ts b/frontend/src/layout/layout-routing.module.ts index 6e8853c0..3efcbe27 100644 --- a/frontend/src/layout/layout-routing.module.ts +++ b/frontend/src/layout/layout-routing.module.ts @@ -14,6 +14,7 @@ const routes: Routes = [ { path: 'capabilities', loadChildren: () => import('../modules/capabilities/capability.module').then(m => m.CapabilityModule)}, { path: 'production-processes', loadChildren: () => import('../modules/production-processes/production-processes.module').then(m => m.ProductionProcessesModule)}, { path: 'skillmex-configuration', loadChildren: () => import('../modules/skillmex-configuration/skillmex-configuration.module').then(m => m.SkillMexConfigurationModule)}, + { path: 'graph-visualization', loadChildren: () => import('../modules/graph-visualization/graph-visualization.module').then(m =>m.GraphVisualizationModule)}, { path: 'dashboard', loadChildren: () => import('./dashboard/dashboard.module').then(m => m.DashboardModule)}, { path: 'charts', loadChildren: () => import('./charts/charts.module').then(m => m.ChartsModule)}, { path: 'blank-page', loadChildren: () => import('./blank-page/blank-page.module').then(m => m.BlankPageModule)}, diff --git a/frontend/src/modules/graph-visualization/D3GraphData.ts b/frontend/src/modules/graph-visualization/D3GraphData.ts new file mode 100644 index 00000000..e1bfcd3e --- /dev/null +++ b/frontend/src/modules/graph-visualization/D3GraphData.ts @@ -0,0 +1,29 @@ + +export class D3GraphData { + // nodes: D3Node [] = []; + // links: D3Link[] = []; + constructor(private nodes: D3Node[] = [], private links: D3Link[] = []) { } + + addNode(node: D3Node): void { + this.nodes.push(node); + } + + addLink(link: D3Link): void { + this.links.push(link); + } +} + + +export class D3Node { + constructor( + private id: string, + private name: string, + private group: number) { } +} + +export class D3Link { + constructor( + private source: string, + private target: string, + private type: string) { } +} diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html similarity index 100% rename from frontend/src/own-modules/graph-visualization/graph-visualization.component.html rename to frontend/src/modules/graph-visualization/graph-visualization.component.html diff --git a/frontend/app/own-modules/graph-visualization/graph-visualization.component.scss b/frontend/src/modules/graph-visualization/graph-visualization.component.scss similarity index 100% rename from frontend/app/own-modules/graph-visualization/graph-visualization.component.scss rename to frontend/src/modules/graph-visualization/graph-visualization.component.scss diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts new file mode 100644 index 00000000..d93ed4e7 --- /dev/null +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -0,0 +1,336 @@ +import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import * as d3 from "d3"; +import { ActivatedRoute } from '@angular/router'; +import { NodeCreatorService } from './node-creator.service'; + + + @Component({ + selector: 'graph-visualization', + encapsulation: ViewEncapsulation.None, + templateUrl: './graph-visualization.component.html', + styleUrls: ['./graph-visualization.component.scss'] + }) +export class GraphVisualizationComponent implements AfterContentInit, OnInit { + name: string; + svg: any; + link: any; + /** The displayed node name*/ + text: any; + /** The displayed link description */ + linkText: any; + /** The displayed node */ + node: any; + /** Loaded data: Connected modules with their capabilities and skills*/ + data: any; + /**Returns different colors automatically. The node-border color is the same for all node-group members*/ + color: any; + /**Simulation of the force directed graph */ + simulation: any; + + + margin = { top: 10, right: 30, bottom: 30, left: 40 }; + width = 100; + height = 100; + /**Defines the nodeborder thickness*/ + nodeborder = 8; + /**Defines the standard radius of a node*/ + rad = 20; + + index=0; + + constructor( + private route: ActivatedRoute, + private nodeCreatorService: NodeCreatorService + ) { + } + + @ViewChild('g') svgContainer: ElementRef; + moduleName: string; + ngOnInit(): void {} + + + ngAfterContentInit(): void { + console.log("ng after content"); + + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }); + console.log("moduleName"); + console.log(this.moduleName); + + this.nodeCreatorService.getAllNodes(this.moduleName).subscribe(data => { + this.data = data; // load data created by node-creator.service + console.log("data"); + console.log(this.data); + + + this.setSvg(); + this.setSimulation(); + }); + + } + + /**Defines settings of the simulation */ + setSimulation(){ + this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)); //Zentrierung der Nodes + + this.refreshSimulation(); + } + + + + /**Defines settings of the SVG and the color-scheme of the nodes */ + setSvg(){ + + this.svg = d3.select("#graph") // size definitions for the svg + .append("svg") + .attr("width", this.width + '%') + .attr("height", this.height + '%') + .append("g") + .attr("transform", + "translate(" + this.margin.left + "," + this.margin.top + ")"); + + + + this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours + + this.svg.append('defs').append('marker') // marker/ arrow settings + .attr("id", 'arrowhead') + .attr('viewBox', '-0 -5 10 10') //coordinate system + .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions + .attr('refY', 0) + .attr('orient', 'auto') + .attr('markerWidth', 13) + .attr('markerHeight', 13) + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke', 'none');} + + + + /** + * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node + */ + refreshSimulation() { + this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id;}); + const linkEnter=this.link.enter() //enter-selection + .append("line").style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link + ; + linkEnter.append("title") + .text(function (d){return d.type;}); + this.link = this.link.merge(linkEnter); // merge old elements with entered elements + this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + + this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id;}); + + const nodeEnter= this.node.enter().append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8; } + else { return this.rad; } + }); + nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseout', this.mouseout); + nodeEnter.style("fill", this.setNodeStyle) + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }); // set different node colours for each node-group + nodeEnter.on('dblclick', this.nodeDoubleClick); + nodeEnter.call(d3.drag() // reactions when dragging a node + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)); + this.node=this.node.merge(nodeEnter); // merge old elements with entered elements + this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + + + this.text = this.svg.selectAll("text").data(this.data.nodes); + const textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name;}) + + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold"; } + else { return "normal"; } + }) + .text(function (d) { console.log(d.name); + return d.name; }); // get text from data + this.text=this.text.merge(textEnter); + this.text.exit().remove(); + + this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type;}); + const linkTextEnter=this.linkText.enter().append("text") + .style("fill", "#999") + .attr("font-style", "italic") + .text(function (d) { return d.type; }); // get link text from data + this.linkText=this.linkText.merge(linkTextEnter); + this.linkText.exit().remove(); + + + this.simulation.nodes(this.data.nodes); // simualtion uses current data + this.simulation.force("link").links(this.data.links); + + } + /** + * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element + * @param d The position of every graph element (i.e. node, link...) + */ + ticked = () => { + const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg + const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; + + this.text + .attr("dx", (d) => { // restrictions for node text positions in the svg + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%'; + }) + .attr("dy", (d) => { + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%'; + }); + + this.linkText + .attr("dx", (d) => { // restrictions for link text positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; + }) + .attr("dy", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; + }); + + this.link + .attr("x1", (d) => { // restrictions for link positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX + '%'; + }) + .attr("y1", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY + '%'; + }) + .attr("x2", (d) => { + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX + '%'; + }) + .attr("y2", (d) => { + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY + '%'; + }); + + this.node + .attr("cx", (d) => { + d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX + '%'; + }) + .attr("cy", (d) => { + d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY + '%'; + }); + } + + /** + * Function executed on a drag start + */ + dragstarted = (d) => { + if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + /** + * Drag function, executed on every drag movement + * @param d The dragged node + */ + dragged(d) { + d.fx = d3.event.x; + d.fy = d3.event.y; + } + + dragended(d) { + + } + /** + * Mouseover function, executed when mouse over node. The radius of the node will increase. + * @param d node under the cursor + */ + mouseover(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 12); + } + else { + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 6); + } + } + /** + * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. + * @param d node wich is left by the cursor after mouseover + */ + mouseout(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node + d3.select(this).transition() + .attr('r', 20 + 8); + } + else { + d3.select(this).transition() + .attr('r', 20); + } + } + /** + * Doubleclick function, executed on every doubleclick on a node + *@param d The clicked node + */ + nodeDoubleClick=(d)=> { + this.index++; + const testNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(testNode); + this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }); + this.refreshSimulation(); + } + + /** Returns the color of the inner circle of a node */ + setNodeStyle(d){ + if (d.group == 1) { return "black"; } + else { return "whitesmoke"; } + } + /** Returns the node border color for each node-group */ + setNodeBorderStyle=(d)=>{ + return this.color(d.group); + } + +} diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts similarity index 55% rename from frontend/src/own-modules/graph-visualization/graph-visualization.module.ts rename to frontend/src/modules/graph-visualization/graph-visualization.module.ts index 711024bc..17f69cd0 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,18 +1,18 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from 'app/shared/services/module.service'; +import { ModuleService } from '../../shared/services/module.service'; import { NodeCreatorService } from './node-creator.service'; @NgModule({ - imports: [ - GraphVisualizationRouter, - ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - NodeCreatorService - - ] + imports: [ + GraphVisualizationRouter, + ], + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + NodeCreatorService + + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts similarity index 53% rename from frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts rename to frontend/src/modules/graph-visualization/graph-visualization.routing.ts index ba430b6c..c9d65f24 100644 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts @@ -3,18 +3,18 @@ import { NgModule } from '@angular/core'; import {GraphVisualizationComponent} from './graph-visualization.component'; const routes: Routes = [ - { path: 'modules/:moduleName', - component: GraphVisualizationComponent, - - }, - {path:'modules', component: GraphVisualizationComponent + { path: 'modules/:moduleName', + component: GraphVisualizationComponent, - } + }, + {path:'modules', component: GraphVisualizationComponent + + } ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] }) export class GraphVisualizationRouter {} diff --git a/frontend/src/modules/graph-visualization/node-creator.service.spec.ts b/frontend/src/modules/graph-visualization/node-creator.service.spec.ts new file mode 100644 index 00000000..2ba009ab --- /dev/null +++ b/frontend/src/modules/graph-visualization/node-creator.service.spec.ts @@ -0,0 +1,16 @@ +/* tslint:disable:no-unused-variable */ + +import { TestBed, async, inject } from '@angular/core/testing'; +import { NodeCreatorService } from './node-creator.service'; + +describe('Service: NodeCreator', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + providers: [NodeCreatorService] + }); + }); + + it('should ...', inject([NodeCreatorService], (service: NodeCreatorService) => { + expect(service).toBeTruthy(); + })); +}); diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts new file mode 100644 index 00000000..9a6ec11f --- /dev/null +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -0,0 +1,89 @@ +import { Injectable } from '@angular/core'; +import { ProductionModule } from '@shared/models/production-module/ProductionModule'; +import { Observable } from 'rxjs'; +import { map } from 'rxjs/operators'; +import { ModuleService } from '../../shared/services/module.service'; +import { D3GraphData, D3Link, D3Node } from './D3GraphData'; +// import { containsElement } from '@angular/animations/browser/browser'; + + +@Injectable({ + providedIn: 'root' +}) +export class NodeCreatorService { + apiRoot = "/api"; + modules: ProductionModule[] + + constructor(private moduleService: ModuleService) { } + + /** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ + getAllNodes(moduleIri: string): Observable { + + return this.moduleService.getAllModules().pipe(map(modules => { + // If a module name is passed, filter the module list for that specific one + if(moduleIri != undefined) { + modules = modules.filter(module => module.iri == moduleIri); + } + return this.formatData(modules); + })); + } + + formatData(modules: ProductionModule[]): D3GraphData { + + const graphData = new D3GraphData(); + modules.forEach(module => { //loop over all modules + + graphData.addNode(new D3Node(module.iri, module.getLocalName(), 1)); // adds a node for each module + + module.skills.forEach(skill => { + graphData.addNode(new D3Node(skill.iri, skill.getLocalName(), 100)); + graphData.addLink(new D3Link(module.iri, skill.iri, "hasSkill")); + + // Add state machine + graphData.addNode(new D3Node(skill.stateMachine.iri, skill.stateMachine.getLocalName(), 120)); + graphData.addLink(new D3Link(skill.iri, skill.stateMachine.iri, "hasStateMachine")); + + // Add skill parameters + skill.skillParameters.forEach(parameter => { + graphData.addNode(new D3Node(parameter.iri, parameter.getLocalName(), 140)); + graphData.addLink(new D3Link(skill.iri, parameter.iri, "hasSkillParameter")); + }); + + // Add skill outputs + skill.skillOutputs.forEach(output => { + graphData.addNode(new D3Node(output.iri,output.getLocalName(), 160)); + graphData.addLink(new D3Link(skill.iri, output.iri, "hasSkillOutput")); + }); + }); + + // Add module's capabilities + module.capabilities.forEach(capability => { //loop over all capabilities of current module + graphData.addNode(new D3Node(capability.iri, capability.getLocalName(), 200)); + graphData.addLink(new D3Link(module.iri, capability.iri, "hasCapability")); // adds a link between capability and module + + capability.inputs.forEach(input => { + graphData.addNode(new D3Node(input.iri, input.getLocalName(), 240)); + graphData.addLink(new D3Link(capability.iri, input.iri, "hasInput")); + + }); + + capability.outputs.forEach(output => { + graphData.addNode(new D3Node(output.iri, output.getLocalName(), 270)); + graphData.addLink(new D3Link(capability.iri, output.iri, "hasOutput")); + }); + + }); + + }); + return graphData; + } + + +} + + + + + + + diff --git a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts b/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts deleted file mode 100644 index 7a368116..00000000 --- a/frontend/src/own-modules/graph-visualization/graph-visualization.component.ts +++ /dev/null @@ -1,328 +0,0 @@ -import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; -import * as d3 from "d3" -import { ActivatedRoute } from '@angular/router'; -import { NodeCreatorService } from './node-creator.service'; - - -@Component({ - selector: 'graph-visualization', - encapsulation: ViewEncapsulation.None, - templateUrl: './graph-visualization.component.html', - styleUrls: ['./graph-visualization.component.scss'] -}) -export class GraphVisualizationComponent implements AfterContentInit, OnInit { - name: String; - svg: any; - link: any; - /** The displayed node name*/ - text: any; - /** The displayed link description */ - linkText: any; - /** The displayed node */ - node: any; - /** Loaded data: Connected modules with their capabilities and skills*/ - data: any; - /**Returns different colors automatically. The node-border color is the same for all node-group members*/ - color: any; -/**Simulation of the force directed graph */ - simulation: any; - - - margin = { top: 10, right: 30, bottom: 30, left: 40 }; - width = 100; - height = 100; -/**Defines the nodeborder thickness*/ - nodeborder = 8; - /**Defines the standard radius of a node*/ - rad = 20; - - index=0; - - constructor( - private route: ActivatedRoute, - private nodeCreatorService: NodeCreatorService - ) { - }; - - @ViewChild('g') svgContainer: ElementRef; - moduleName: string; - ngOnInit(): void {} - - - ngAfterContentInit(): void { - - - - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }) - this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service - - this.setSvg(); - this.setSimulation(); - } - -/**Defines settings of the simulation */ -setSimulation(){ - this.simulation = d3.forceSimulation(this.data.nodes); - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links)) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - - this.refreshSimulation(); -} - - - -/**Defines settings of the SVG and the color-scheme of the nodes */ - setSvg(){ - - this.svg = d3.select("#graph") // size definitions for the svg - .append("svg") - .attr("width", this.width + '%') - .attr("height", this.height + '%') - .append("g") - .attr("transform", - "translate(" + this.margin.left + "," + this.margin.top + ")"); - - - - this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - - this.svg.append('defs').append('marker') // marker/ arrow settings - .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions - .attr('refY', 0) - .attr('orient', 'auto') - .attr('markerWidth', 13) - .attr('markerHeight', 13) - .attr('xoverflow', 'visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke', 'none');} - - - - /** - * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node - */ - refreshSimulation() { - this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id}) - var linkEnter=this.link.enter() //enter-selection - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link - ; - linkEnter.append("title") - .text(function (d){return d.type}) - this.link = this.link.merge(linkEnter); // merge old elements with entered elements - this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id}) - - var nodeEnter= this.node.enter().append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8 } - else { return this.rad } - }) - nodeEnter.on('mouseover', this.mouseover); - nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", this.setNodeStyle) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group - nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() // reactions when dragging a node - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); // merge old elements with entered elements - this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - - this.text = this.svg.selectAll("text").data(this.data.nodes) - var textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name}) - - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold" } - else { return "normal" } - }) - .text(function (d) { console.log(d.name); - return d.name }); // get text from data - this.text=this.text.merge(textEnter); - this.text.exit().remove(); - - this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) - var linkTextEnter=this.linkText.enter().append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type }); // get link text from data - this.linkText=this.linkText.merge(linkTextEnter); - this.linkText.exit().remove(); - - - this.simulation.nodes(this.data.nodes); // simualtion uses current data - this.simulation.force("link").links(this.data.links); - - } -/** - * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element - * @param d The position of every graph element (i.e. node, link...) - */ - ticked = () => { - const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg - const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - this.text - .attr("dx", (d) => { // restrictions for node text positions in the svg - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%' - }) - .attr("dy", (d) => { - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%' - }) - - this.linkText - .attr("dx", (d) => { // restrictions for link text positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; - }) - .attr("dy", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; - }) - - this.link - .attr("x1", (d) => { // restrictions for link positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX + '%'; - }) - .attr("y1", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY + '%'; - }) - .attr("x2", (d) => { - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX + '%'; - }) - .attr("y2", (d) => { - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY + '%'; - }) - - this.node - .attr("cx", (d) => { - d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX + '%'; - }) - .attr("cy", (d) => { - d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY + '%'; - }) - } - -/** - * Function executed on a drag start - */ - dragstarted = (d) => { - if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - - /** - * Drag function, executed on every drag movement - * @param d The dragged node - */ - dragged(d) { - d.fx = d3.event.x; - d.fy = d3.event.y; - } - - dragended(d) { - - } -/** - * Mouseover function, executed when mouse over node. The radius of the node will increase. - * @param d node under the cursor - */ - mouseover(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 12) - } - else { - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 6) - } - } -/** - * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. - * @param d node wich is left by the cursor after mouseover - */ - mouseout(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .attr('r', 20 + 8) - } - else { - d3.select(this).transition() - .attr('r', 20) - } - } -/** - * Doubleclick function, executed on every doubleclick on a node - *@param d The clicked node - */ - nodeDoubleClick=(d)=> { - this.index++; - const testNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(testNode); - this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }) - this.refreshSimulation(); - } - - /** Returns the color of the inner circle of a node */ - setNodeStyle(d){ - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - } -/** Returns the node border color for each node-group */ - setNodeBorderStyle=(d)=>{ - return this.color(d.group); - } - -} \ No newline at end of file From e21d7288420e0c6bf57b34118d1d6b8b82aadf8e Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Mon, 17 May 2021 11:32:52 +0200 Subject: [PATCH 53/74] Added capability and skill graph visus --- .../components/sidebar/sidebar.component.html | 10 +- .../graph-visualization/D3GraphData.ts | 81 ++++++++-- .../graph-visualization.component.ts | 60 +++++--- .../graph-visualization.routing.ts | 2 +- .../node-creator.service.ts | 140 +++++++++++++----- .../module-overview.component.html | 3 + .../production-module.component.html | 6 +- .../production-module.routing.ts | 2 +- 8 files changed, 223 insertions(+), 81 deletions(-) diff --git a/frontend/src/layout/components/sidebar/sidebar.component.html b/frontend/src/layout/components/sidebar/sidebar.component.html index e5833dae..57a49972 100644 --- a/frontend/src/layout/components/sidebar/sidebar.component.html +++ b/frontend/src/layout/components/sidebar/sidebar.component.html @@ -46,11 +46,11 @@
- - -   - Graph-Visualization - + +   + Graph-Visualization +   SkillMeX Configuration diff --git a/frontend/src/modules/graph-visualization/D3GraphData.ts b/frontend/src/modules/graph-visualization/D3GraphData.ts index e1bfcd3e..9fdef379 100644 --- a/frontend/src/modules/graph-visualization/D3GraphData.ts +++ b/frontend/src/modules/graph-visualization/D3GraphData.ts @@ -1,7 +1,46 @@ + +export enum NodeType { + None = "None", + D3ModuleNode ="D3ModuleNode", + D3SkillNode = "D3SkillNode", + D3CapabilityNode = "D3CapabilityNode" +} + +export class D3Node { + constructor( + public id: string, + public name: string, + public group: number, + public type: NodeType = NodeType.None) { } +} + +export class D3ModuleNode extends D3Node { + constructor(id: string, name: string) { + super(id, name, 1, NodeType.D3ModuleNode); + } +} +export class D3SkillNode extends D3Node{ + constructor(id: string, name: string) { + super(id, name, 100, NodeType.D3SkillNode); + } +} + +export class D3CapabilityNode extends D3Node{ + constructor(id: string, name: string) { + super(id, name, 200, NodeType.D3CapabilityNode); + } +} + +export class D3Link { + constructor( + public source: string, + public target: string, + public type: string) { } +} + + export class D3GraphData { - // nodes: D3Node [] = []; - // links: D3Link[] = []; constructor(private nodes: D3Node[] = [], private links: D3Link[] = []) { } addNode(node: D3Node): void { @@ -11,19 +50,33 @@ export class D3GraphData { addLink(link: D3Link): void { this.links.push(link); } -} + getNodes(): D3Node[] {return this.nodes;} + getNodesById(id: string): D3Node[] { + return this.nodes.filter(node => node.id == id); + } -export class D3Node { - constructor( - private id: string, - private name: string, - private group: number) { } -} + getNodesByNodeTyp(type: string): D3Node[] { + return this.nodes.filter(node => node.type === type); + } -export class D3Link { - constructor( - private source: string, - private target: string, - private type: string) { } + getLinks(): D3Link[] {return this.links;} + + // Adds another D3GraphData object to the current one + appendAndConnectData(data: D3GraphData, idToConnectTo: string, nodeType: NodeType, connectionType: string): void { + const newNodes = data.getNodes(); + const newLinks = data.getLinks(); + // Add the new data + newNodes.forEach(newNode => this.addNode(newNode)); + newLinks.forEach(newLink => this.addLink(newLink)); + // Connect with existing nodes + const oldNodesToConnect = this.getNodesById(idToConnectTo); + const newNodesToConnect = this.getNodesByNodeTyp(nodeType); + + oldNodesToConnect.forEach(oldNode => { + newNodesToConnect.forEach(newNode => { + this.addLink(new D3Link(oldNode.id, newNode.id, connectionType)); + }); + }); + } } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index d93ed4e7..daa9810e 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,7 +1,9 @@ -import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef, OnDestroy } from '@angular/core'; import * as d3 from "d3"; import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; +import { Observable, Subscription } from 'rxjs'; +import { D3GraphData } from './D3GraphData'; @Component({ @@ -10,7 +12,8 @@ import { NodeCreatorService } from './node-creator.service'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit, OnInit { +export class GraphVisualizationComponent implements AfterContentInit, OnDestroy { + $graphDataSubscription: Subscription; name: string; svg: any; link: any; @@ -45,27 +48,50 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { } @ViewChild('g') svgContainer: ElementRef; - moduleName: string; - ngOnInit(): void {} + ngOnDestroy(): void { + // Kill data subscription and stop simulation -> Otherwise killing performance + this.$graphDataSubscription.unsubscribe(); + this.simulation.stop(); + } ngAfterContentInit(): void { - console.log("ng after content"); - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }); - console.log("moduleName"); - console.log(this.moduleName); - - this.nodeCreatorService.getAllNodes(this.moduleName).subscribe(data => { - this.data = data; // load data created by node-creator.service - console.log("data"); - console.log(this.data); + console.log(p); + + const elementType = p['elementType']; + const elementIri = p['elementIri']; + console.log("elementType"); + console.log(elementType); + console.log("elementIri"); + console.log(elementIri); + + + switch (elementType) { + case "modules": + this.$graphDataSubscription = this.nodeCreatorService.getAllModuleNodes(elementIri).subscribe(data => { + this.data = data; // load data created by node-creator.service + this.setSvg(); + this.setSimulation(); + }); + break; + case "skills": + this.$graphDataSubscription = this.nodeCreatorService.getAllSkillNodes(elementIri).subscribe(data => { + this.data = data; // load data created by node-creator.service + this.setSvg(); + this.setSimulation(); + }); + break; + case "capabilities": + this.$graphDataSubscription = this.nodeCreatorService.getAllCapabilityNodes(elementIri).subscribe(data => { + this.data = data; // load data created by node-creator.service + this.setSvg(); + this.setSimulation(); + }); + break; + } - this.setSvg(); - this.setSimulation(); }); } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts index c9d65f24..86962e1e 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts @@ -3,7 +3,7 @@ import { NgModule } from '@angular/core'; import {GraphVisualizationComponent} from './graph-visualization.component'; const routes: Routes = [ - { path: 'modules/:moduleName', + { path: ':elementType/:elementIri', component: GraphVisualizationComponent, }, diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts index 9a6ec11f..b000a9aa 100644 --- a/frontend/src/modules/graph-visualization/node-creator.service.ts +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -1,10 +1,13 @@ import { Injectable } from '@angular/core'; +import { Capability } from '@shared/models/capability/Capability'; import { ProductionModule } from '@shared/models/production-module/ProductionModule'; +import { Skill } from '@shared/models/skill/Skill'; import { Observable } from 'rxjs'; import { map } from 'rxjs/operators'; +import { CapabilityService } from 'src/shared/services/capability.service'; +import { SkillService } from 'src/shared/services/skill.service'; import { ModuleService } from '../../shared/services/module.service'; -import { D3GraphData, D3Link, D3Node } from './D3GraphData'; -// import { containsElement } from '@angular/animations/browser/browser'; +import { D3CapabilityNode, D3GraphData, D3Link, D3ModuleNode, D3Node, D3SkillNode, NodeType } from './D3GraphData'; @Injectable({ @@ -14,71 +17,128 @@ export class NodeCreatorService { apiRoot = "/api"; modules: ProductionModule[] - constructor(private moduleService: ModuleService) { } + constructor( + private moduleService: ModuleService, + private skillService: SkillService, + private capabilityService: CapabilityService) { } - /** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ - getAllNodes(moduleIri: string): Observable { + // /** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ + // getAllNodes(moduleIri: string): Observable { + + // return this.moduleService.getAllModules().pipe(map(modules => { + // // If a module name is passed, filter the module list for that specific one + // if(moduleIri != undefined) { + // modules = modules.filter(module => module.iri == moduleIri); + // } + // return this.getDataFromModules(modules); + // })); + // } + + getAllModuleNodes(moduleIri: string): Observable { return this.moduleService.getAllModules().pipe(map(modules => { - // If a module name is passed, filter the module list for that specific one - if(moduleIri != undefined) { + // If a module IRI is passed, filter the module list for that specific one + if(moduleIri != undefined && moduleIri != "") { + console.log("filtering"); + console.log("moduleIri: '" + moduleIri + "'"); + + modules = modules.filter(module => module.iri == moduleIri); } - return this.formatData(modules); + + console.log("modules"); + console.log(modules); + + + + const graphData = new D3GraphData(); + modules.forEach(module => { //loop over all modules + + graphData.addNode(new D3ModuleNode(module.iri, module.getLocalName())); + // TODO: Add module components + + const skillData = this.getDataFromSkills(module.skills); + graphData.appendAndConnectData(skillData, module.iri, NodeType.D3SkillNode, "hasSkill"); + + const capabilityData = this.getDataFromCapabilities(module.capabilities); + graphData.appendAndConnectData(capabilityData, module.iri, NodeType.D3CapabilityNode, "hasCapability"); + }); + return graphData; + })); } - formatData(modules: ProductionModule[]): D3GraphData { + getAllSkillNodes(skillIri: string): Observable { + return this.skillService.getAllSkills().pipe(map(skills => { + // If a skill IRI is passed, filter the skill list for that specific one + if(skillIri != undefined && skillIri != "") { + skills = skills.filter(skill => skill.iri == skillIri); + } - const graphData = new D3GraphData(); - modules.forEach(module => { //loop over all modules + return this.getDataFromSkills(skills); + })); + } + + getAllCapabilityNodes(capabilityIri: string): Observable { + return this.capabilityService.getAllCapabilities().pipe(map(capabilities => { + // If a capability IRI is passed, filter the capability list for that specific one + if(capabilityIri != undefined && capabilityIri != "") { + capabilities = capabilities.filter(capability => capability.iri == capabilityIri); + } + + return this.getDataFromCapabilities(capabilities); + })); + } - graphData.addNode(new D3Node(module.iri, module.getLocalName(), 1)); // adds a node for each module - module.skills.forEach(skill => { - graphData.addNode(new D3Node(skill.iri, skill.getLocalName(), 100)); - graphData.addLink(new D3Link(module.iri, skill.iri, "hasSkill")); + private getDataFromSkills(skills: Skill[]): D3GraphData { + const graphData = new D3GraphData(); + skills.forEach(skill => { - // Add state machine - graphData.addNode(new D3Node(skill.stateMachine.iri, skill.stateMachine.getLocalName(), 120)); - graphData.addLink(new D3Link(skill.iri, skill.stateMachine.iri, "hasStateMachine")); + graphData.addNode(new D3SkillNode(skill.iri, skill.getLocalName())); - // Add skill parameters - skill.skillParameters.forEach(parameter => { - graphData.addNode(new D3Node(parameter.iri, parameter.getLocalName(), 140)); - graphData.addLink(new D3Link(skill.iri, parameter.iri, "hasSkillParameter")); - }); + // Add state machine + graphData.addNode(new D3Node(skill.stateMachine.iri, skill.stateMachine.getLocalName(), 120)); + graphData.addLink(new D3Link(skill.iri, skill.stateMachine.iri, "hasStateMachine")); - // Add skill outputs - skill.skillOutputs.forEach(output => { - graphData.addNode(new D3Node(output.iri,output.getLocalName(), 160)); - graphData.addLink(new D3Link(skill.iri, output.iri, "hasSkillOutput")); - }); + // Add skill parameters + skill.skillParameters.forEach(parameter => { + graphData.addNode(new D3Node(parameter.iri, parameter.getLocalName(), 140)); + graphData.addLink(new D3Link(skill.iri, parameter.iri, "hasSkillParameter")); }); - // Add module's capabilities - module.capabilities.forEach(capability => { //loop over all capabilities of current module - graphData.addNode(new D3Node(capability.iri, capability.getLocalName(), 200)); - graphData.addLink(new D3Link(module.iri, capability.iri, "hasCapability")); // adds a link between capability and module + // Add skill outputs + skill.skillOutputs.forEach(output => { + graphData.addNode(new D3Node(output.iri,output.getLocalName(), 160)); + graphData.addLink(new D3Link(skill.iri, output.iri, "hasSkillOutput")); + }); + }); + return graphData; + } - capability.inputs.forEach(input => { - graphData.addNode(new D3Node(input.iri, input.getLocalName(), 240)); - graphData.addLink(new D3Link(capability.iri, input.iri, "hasInput")); + private getDataFromCapabilities(capabilities: Capability[]): D3GraphData { + const graphData = new D3GraphData(); - }); + capabilities.forEach(capability => { //loop over all capabilities of current module + graphData.addNode(new D3CapabilityNode(capability.iri, capability.getLocalName())); - capability.outputs.forEach(output => { - graphData.addNode(new D3Node(output.iri, output.getLocalName(), 270)); - graphData.addLink(new D3Link(capability.iri, output.iri, "hasOutput")); - }); + capability.inputs.forEach(input => { + graphData.addNode(new D3Node(input.iri, input.getLocalName(), 240)); + graphData.addLink(new D3Link(capability.iri, input.iri, "hasInput")); }); + capability.outputs.forEach(output => { + graphData.addNode(new D3Node(output.iri, output.getLocalName(), 270)); + graphData.addLink(new D3Link(capability.iri, output.iri, "hasOutput")); + }); }); return graphData; } + + } diff --git a/frontend/src/modules/production-modules/module-overview/module-overview.component.html b/frontend/src/modules/production-modules/module-overview/module-overview.component.html index ac9a8893..0b2c4250 100644 --- a/frontend/src/modules/production-modules/module-overview/module-overview.component.html +++ b/frontend/src/modules/production-modules/module-overview/module-overview.component.html @@ -3,6 +3,9 @@
diff --git a/frontend/src/modules/production-modules/production-module.component.html b/frontend/src/modules/production-modules/production-module.component.html index 09c0fc29..7e169aa6 100644 --- a/frontend/src/modules/production-modules/production-module.component.html +++ b/frontend/src/modules/production-modules/production-module.component.html @@ -2,13 +2,13 @@

Production Modules

diff --git a/frontend/src/modules/production-modules/production-module.routing.ts b/frontend/src/modules/production-modules/production-module.routing.ts index 3e443b73..a68013f5 100644 --- a/frontend/src/modules/production-modules/production-module.routing.ts +++ b/frontend/src/modules/production-modules/production-module.routing.ts @@ -17,7 +17,7 @@ const routes: Routes = [ children: [ {path: '', redirectTo: 'overview', pathMatch: 'full'}, {path: 'overview', component: ModuleOverviewComponent}, - {path: 'graph-visualization', component: ModuleGraphVisuComponent}, + // {path: 'graph-visualization', component: ModuleGraphVisuComponent}, // Note that graph visu is passed to the graph visu module {path: 'register', component: ModuleRegistrationComponent}, ] } From 902ce4a6798841e5fa3c5090a28a4d072134a42a Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Wed, 19 May 2021 09:08:03 +0200 Subject: [PATCH 54/74] Added types to graph visu component --- .../graph-visualization/D3GraphData.ts | 3 +- .../graph-visualization.component.ts | 28 +++++++++---------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/frontend/src/modules/graph-visualization/D3GraphData.ts b/frontend/src/modules/graph-visualization/D3GraphData.ts index 9fdef379..cfbd9284 100644 --- a/frontend/src/modules/graph-visualization/D3GraphData.ts +++ b/frontend/src/modules/graph-visualization/D3GraphData.ts @@ -52,6 +52,8 @@ export class D3GraphData { } getNodes(): D3Node[] {return this.nodes;} + getLinks(): D3Link[] {return this.links;} + getNodesById(id: string): D3Node[] { return this.nodes.filter(node => node.id == id); } @@ -60,7 +62,6 @@ export class D3GraphData { return this.nodes.filter(node => node.type === type); } - getLinks(): D3Link[] {return this.links;} // Adds another D3GraphData object to the current one appendAndConnectData(data: D3GraphData, idToConnectTo: string, nodeType: NodeType, connectionType: string): void { diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index daa9810e..bb3dc26e 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -3,7 +3,7 @@ import * as d3 from "d3"; import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; import { Observable, Subscription } from 'rxjs'; -import { D3GraphData } from './D3GraphData'; +import { D3GraphData, D3Link, D3Node } from './D3GraphData'; @Component({ @@ -24,7 +24,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy /** The displayed node */ node: any; /** Loaded data: Connected modules with their capabilities and skills*/ - data: any; + data: D3GraphData; /**Returns different colors automatically. The node-border color is the same for all node-group members*/ color: any; /**Simulation of the force directed graph */ @@ -98,12 +98,12 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy /**Defines settings of the simulation */ setSimulation(){ - this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation = d3.forceSimulation(this.data.getNodes()); this.simulation .force("link", d3.forceLink() .id(function (d) { return d.id; }) .distance(80) - .links(this.data.links)) + .links(this.data.getLinks())) .force("collision", d3.forceCollide(40)) .on("tick", this.ticked) // tick on every step of the simulation .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction @@ -149,7 +149,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ refreshSimulation() { - this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id;}); + this.link = this.svg.selectAll(".link").data(this.data.getLinks(), function (d){return d.target.id;}); const linkEnter=this.link.enter() //enter-selection .append("line").style("stroke", "#aaa") .attr("class", "links") @@ -160,7 +160,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy this.link = this.link.merge(linkEnter); // merge old elements with entered elements this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id;}); + this.node = this.svg.selectAll(".node").data(this.data.getNodes(), function (d){return d.id;}); const nodeEnter= this.node.enter().append("circle") .attr("r", (d) => { @@ -181,9 +181,9 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - this.text = this.svg.selectAll("text").data(this.data.nodes); + this.text = this.svg.selectAll("text").data(this.data.getNodes()); const textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name;}) + .data(this.data.getNodes(), function (d){return d.name;}) .attr("font-weight", function (d) { if (d.group == 1) { return "bold"; } @@ -194,7 +194,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy this.text=this.text.merge(textEnter); this.text.exit().remove(); - this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type;}); + this.linkText = this.svg.selectAll("links").data(this.data.getLinks(), function (d){ return d.type;}); const linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") @@ -203,8 +203,8 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy this.linkText.exit().remove(); - this.simulation.nodes(this.data.nodes); // simualtion uses current data - this.simulation.force("link").links(this.data.links); + this.simulation.nodes(this.data.getNodes()); // simualtion uses current data + this.simulation.force("link").links(this.data.getLinks()); } /** @@ -343,9 +343,9 @@ export class GraphVisualizationComponent implements AfterContentInit, OnDestroy */ nodeDoubleClick=(d)=> { this.index++; - const testNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(testNode); - this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }); + const testNode = new D3Node(this.index.toString(), "neighbor", 8); + this.data.addNode(testNode); + this.data.addLink(new D3Link(d.id, testNode.id, "testclick")); this.refreshSimulation(); } From a67d8b58fdbab89a9407791d6baa8df11dca68a7 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Mon, 30 Dec 2019 22:28:30 +0100 Subject: [PATCH 55/74] Created 'Graph-Visualization' in 'own-modules' Created 'Graph-Visualization' in 'own-modules' and d3.js installed --- .../graph-visualization.component.html | 5 - .../graph-visualization.component.ts | 363 +----------------- .../graph-visualization.module.ts | 15 +- .../graph-visualization.routing.ts | 18 +- 4 files changed, 21 insertions(+), 380 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index a454716a..70b08142 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,8 +1,3 @@

Graph-Visualization

-
-
- -
-
\ No newline at end of file diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index bb3dc26e..1b195074 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,362 +1,13 @@ -import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef, OnDestroy } from '@angular/core'; -import * as d3 from "d3"; -import { ActivatedRoute } from '@angular/router'; -import { NodeCreatorService } from './node-creator.service'; -import { Observable, Subscription } from 'rxjs'; -import { D3GraphData, D3Link, D3Node } from './D3GraphData'; +import { Component } from '@angular/core'; +@Component({ + selector: 'graph-visualization', + templateUrl: './graph-visualization.component.html' +}) +export class GraphVisualizationComponent { - @Component({ - selector: 'graph-visualization', - encapsulation: ViewEncapsulation.None, - templateUrl: './graph-visualization.component.html', - styleUrls: ['./graph-visualization.component.scss'] - }) -export class GraphVisualizationComponent implements AfterContentInit, OnDestroy { - $graphDataSubscription: Subscription; - name: string; - svg: any; - link: any; - /** The displayed node name*/ - text: any; - /** The displayed link description */ - linkText: any; - /** The displayed node */ - node: any; - /** Loaded data: Connected modules with their capabilities and skills*/ - data: D3GraphData; - /**Returns different colors automatically. The node-border color is the same for all node-group members*/ - color: any; - /**Simulation of the force directed graph */ - simulation: any; + constructor() { } - margin = { top: 10, right: 30, bottom: 30, left: 40 }; - width = 100; - height = 100; - /**Defines the nodeborder thickness*/ - nodeborder = 8; - /**Defines the standard radius of a node*/ - rad = 20; - - index=0; - - constructor( - private route: ActivatedRoute, - private nodeCreatorService: NodeCreatorService - ) { - } - - @ViewChild('g') svgContainer: ElementRef; - - ngOnDestroy(): void { - // Kill data subscription and stop simulation -> Otherwise killing performance - this.$graphDataSubscription.unsubscribe(); - this.simulation.stop(); - } - - ngAfterContentInit(): void { - this.route.params.subscribe(p => { - console.log(p); - - const elementType = p['elementType']; - const elementIri = p['elementIri']; - console.log("elementType"); - console.log(elementType); - console.log("elementIri"); - console.log(elementIri); - - - switch (elementType) { - case "modules": - this.$graphDataSubscription = this.nodeCreatorService.getAllModuleNodes(elementIri).subscribe(data => { - this.data = data; // load data created by node-creator.service - this.setSvg(); - this.setSimulation(); - }); - break; - case "skills": - this.$graphDataSubscription = this.nodeCreatorService.getAllSkillNodes(elementIri).subscribe(data => { - this.data = data; // load data created by node-creator.service - this.setSvg(); - this.setSimulation(); - }); - break; - case "capabilities": - this.$graphDataSubscription = this.nodeCreatorService.getAllCapabilityNodes(elementIri).subscribe(data => { - this.data = data; // load data created by node-creator.service - this.setSvg(); - this.setSimulation(); - }); - break; - } - - - }); - - } - - /**Defines settings of the simulation */ - setSimulation(){ - this.simulation = d3.forceSimulation(this.data.getNodes()); - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.getLinks())) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)); //Zentrierung der Nodes - - this.refreshSimulation(); - } - - - - /**Defines settings of the SVG and the color-scheme of the nodes */ - setSvg(){ - - this.svg = d3.select("#graph") // size definitions for the svg - .append("svg") - .attr("width", this.width + '%') - .attr("height", this.height + '%') - .append("g") - .attr("transform", - "translate(" + this.margin.left + "," + this.margin.top + ")"); - - - - this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - - this.svg.append('defs').append('marker') // marker/ arrow settings - .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions - .attr('refY', 0) - .attr('orient', 'auto') - .attr('markerWidth', 13) - .attr('markerHeight', 13) - .attr('xoverflow', 'visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke', 'none');} - - - - /** - * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node - */ - refreshSimulation() { - this.link = this.svg.selectAll(".link").data(this.data.getLinks(), function (d){return d.target.id;}); - const linkEnter=this.link.enter() //enter-selection - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link - ; - linkEnter.append("title") - .text(function (d){return d.type;}); - this.link = this.link.merge(linkEnter); // merge old elements with entered elements - this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - this.node = this.svg.selectAll(".node").data(this.data.getNodes(), function (d){return d.id;}); - - const nodeEnter= this.node.enter().append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8; } - else { return this.rad; } - }); - nodeEnter.on('mouseover', this.mouseover); - nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", this.setNodeStyle) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }); // set different node colours for each node-group - nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() // reactions when dragging a node - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); // merge old elements with entered elements - this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - - this.text = this.svg.selectAll("text").data(this.data.getNodes()); - const textEnter=this.text.enter().append("text") - .data(this.data.getNodes(), function (d){return d.name;}) - - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold"; } - else { return "normal"; } - }) - .text(function (d) { console.log(d.name); - return d.name; }); // get text from data - this.text=this.text.merge(textEnter); - this.text.exit().remove(); - - this.linkText = this.svg.selectAll("links").data(this.data.getLinks(), function (d){ return d.type;}); - const linkTextEnter=this.linkText.enter().append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type; }); // get link text from data - this.linkText=this.linkText.merge(linkTextEnter); - this.linkText.exit().remove(); - - - this.simulation.nodes(this.data.getNodes()); // simualtion uses current data - this.simulation.force("link").links(this.data.getLinks()); - - } - /** - * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element - * @param d The position of every graph element (i.e. node, link...) - */ - ticked = () => { - const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg - const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - this.text - .attr("dx", (d) => { // restrictions for node text positions in the svg - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%'; - }) - .attr("dy", (d) => { - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%'; - }); - - this.linkText - .attr("dx", (d) => { // restrictions for link text positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; - }) - .attr("dy", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; - }); - - this.link - .attr("x1", (d) => { // restrictions for link positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX + '%'; - }) - .attr("y1", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY + '%'; - }) - .attr("x2", (d) => { - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX + '%'; - }) - .attr("y2", (d) => { - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY + '%'; - }); - - this.node - .attr("cx", (d) => { - d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX + '%'; - }) - .attr("cy", (d) => { - d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY + '%'; - }); - } - - /** - * Function executed on a drag start - */ - dragstarted = (d) => { - if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - - /** - * Drag function, executed on every drag movement - * @param d The dragged node - */ - dragged(d) { - d.fx = d3.event.x; - d.fy = d3.event.y; - } - - dragended(d) { - - } - /** - * Mouseover function, executed when mouse over node. The radius of the node will increase. - * @param d node under the cursor - */ - mouseover(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 12); - } - else { - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 6); - } - } - /** - * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. - * @param d node wich is left by the cursor after mouseover - */ - mouseout(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .attr('r', 20 + 8); - } - else { - d3.select(this).transition() - .attr('r', 20); - } - } - /** - * Doubleclick function, executed on every doubleclick on a node - *@param d The clicked node - */ - nodeDoubleClick=(d)=> { - this.index++; - const testNode = new D3Node(this.index.toString(), "neighbor", 8); - this.data.addNode(testNode); - this.data.addLink(new D3Link(d.id, testNode.id, "testclick")); - this.refreshSimulation(); - } - - /** Returns the color of the inner circle of a node */ - setNodeStyle(d){ - if (d.group == 1) { return "black"; } - else { return "whitesmoke"; } - } - /** Returns the node border color for each node-group */ - setNodeBorderStyle=(d)=>{ - return this.color(d.group); - } } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index 17f69cd0..afb5fff0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,18 +1,11 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from '../../shared/services/module.service'; -import { NodeCreatorService } from './node-creator.service'; @NgModule({ - imports: [ - GraphVisualizationRouter, - ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - NodeCreatorService - - ] + imports: [ + GraphVisualizationRouter + ], + declarations: [GraphVisualizationComponent] }) export class GraphVisualizationModule { } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts index 86962e1e..ded3fe38 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts @@ -1,20 +1,22 @@ import { Routes, RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import {GraphVisualizationComponent} from './graph-visualization.component'; +import {GraphVisualizationComponent} from './graph-visualization.component' const routes: Routes = [ - { path: ':elementType/:elementIri', - component: GraphVisualizationComponent, + { path: '', + component: GraphVisualizationComponent, + data: { + title: 'Graph-Visualization' + }, + children: [] - }, - {path:'modules', component: GraphVisualizationComponent - } + } ]; @NgModule({ - imports: [RouterModule.forChild(routes)], - exports: [RouterModule] + imports: [RouterModule.forChild(routes)], + exports: [RouterModule] }) export class GraphVisualizationRouter {} From 261708c1da247903cfc3c656631926e6f36a6bb2 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Mon, 27 Jan 2020 12:24:21 +0100 Subject: [PATCH 56/74] Added visual graph by d3.js --- .../graph-visualization.component.html | 5 + .../graph-visualization.component.ts | 191 +++++++++++++++++- 2 files changed, 192 insertions(+), 4 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index 70b08142..fcff4a3d 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,3 +1,8 @@

Graph-Visualization

+
+
+ +
+
\ No newline at end of file diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 1b195074..cf171b0d 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,13 +1,196 @@ -import { Component } from '@angular/core'; +import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'; +import * as d3 from "d3" +import { enterView } from '@angular/core/src/render3/state'; +import { pathToFileURL } from 'url'; @Component({ selector: 'graph-visualization', - templateUrl: './graph-visualization.component.html' + encapsulation: ViewEncapsulation.None, + templateUrl: './graph-visualization.component.html', + styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent { +export class GraphVisualizationComponent implements AfterContentInit { + + ngAfterContentInit(): void { + const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen + const width = 1400 - margin.left - margin.right; + const height = 900 - margin.top - margin.bottom; + const rad=20; // radius + + +var svg = d3.select("#graph") // Größe u. Grenzen SVG +.append("svg") + .attr("width", width + margin.left + margin.right) + .attr("height", height + margin.top + margin.bottom) +.append("g") + .attr("transform", + "translate(" + margin.left + "," + margin.top + ")"); + + + +var colors = d3.scaleOrdinal(d3.schemeCategory10); //Farben + + + +//d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten + +const data = { + "nodes": [ + { + "id": 1, + "name": "ModulA", + "group": 1 + }, + { + "id": 2, + "name": "ModulB", + "group": 2 + }, + { + "id": 3, + "name": "ModulC", + "group": 3 + + }, { + "id": 4, + "name": "cap11", + "group":1 + }, + { + "id": 5, + "name": "cap12", + "group":1 + }, + { + "id": 6, + "name": "cap21", + "group":2 + } + ], + "links": [ + + { + "source": 1, + "target": 4, + "type": "is_a" + } , + { + "source": 1, + "target": 5, + "type": "has" + } , + { + "source": 2, + "target": 6, + "type":"has" + } + ] +} - constructor() { } + var link = svg // Links initialisieren + .selectAll(".links") + .data(data.links) + .enter() + .append("line") + .style("stroke", "#aaa") + ; + link.append("title") + .text(function(d){return d.type}); + var node = svg // Nodes initialisieren + .selectAll("circle") + .data(data.nodes) + .enter() + .append("circle") + .attr("r", rad) + .style( "fill", "white") + .style("stroke-width", 8) + .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit + /*.append ("text") // name als text neben node + .attr ("dx", 12) + .attr ("dy", ".35em") + .text(function(d){return d.name})*/ + .call(d3.drag() + .on("start", dragstarted) + .on("drag", dragged)); +; + + const text =svg + .selectAll("text") + .data(data.nodes) + .enter() + .append("text") + + .text(function(d){return d.name}); + + const linkText =svg + .selectAll("links") + .data(data.links) + .enter() + .append("text") + .text(function (d){return d.type}); + + + + var simulation = d3.forceSimulation(data.nodes) //Simulation + .force("link", d3.forceLink() + .id(function(d) { return d.id; }) + .distance(100) + .links(data.links) + ) + .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen + .force("center", d3.forceCenter(width / 2, height / 2)) // Nodes mittig von svg + .on("tick", ticked); // tick wird in jedem Schritt durchgeführt + + + function ticked() { + console.log(data); + text + .attr("dx", function(d) { + return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) + .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }) + + linkText + .attr("dx", function(d){return 0.5*((d.source.x)+(d.target.x))}) + .attr("dy", function(d){return 0.5*((d.source.y)+(d.target.y)) }) + + + + link // Anfang und Ende der Links / Bestimmung der Position + .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)); }) // Postionen nur innerhalb des Fensters erlaubt + .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)); }) + .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)); }) + .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)); }); + + node // Bestimmung der Postion einzelner Nodes + .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)); }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }); + } + + function ended() { + console.log("ended"); + } + + function dragstarted(d) { + if (!d3.event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + console.log(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; + } + + } + constructor() { } } From 6ca0f9e8826ef935f1a4d8ddcc82d9807761b748 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 31 Jan 2020 12:17:01 +0100 Subject: [PATCH 57/74] Added arrows Added arrows for relation directions --- .../graph-visualization.component.ts | 36 +++++++++++++------ 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index cf171b0d..efe39ef0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -15,10 +15,12 @@ export class GraphVisualizationComponent implements AfterContentInit { const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen const width = 1400 - margin.left - margin.right; const height = 900 - margin.top - margin.bottom; - const rad=20; // radius + const rad=20 // Radius eines Knotens + const nodeborder =8; // Umrandungsdicke eines Knotens -var svg = d3.select("#graph") // Größe u. Grenzen SVG + +const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) @@ -28,8 +30,21 @@ var svg = d3.select("#graph") // Größe u. Grenzen SVG -var colors = d3.scaleOrdinal(d3.schemeCategory10); //Farben - +const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + +svg.append('defs').append('marker') + .attr("id",'arrowhead') + .attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5) + .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('refY',0) + .attr('orient','auto') + .attr('markerWidth',13) + .attr('markerHeight',13) + .attr('xoverflow','visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke','none'); //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten @@ -89,23 +104,25 @@ const data = { - var link = svg // Links initialisieren + const link = svg // Links initialisieren .selectAll(".links") .data(data.links) .enter() .append("line") .style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)') ; link.append("title") .text(function(d){return d.type}); - var node = svg // Nodes initialisieren + const node = svg // Nodes initialisieren .selectAll("circle") .data(data.nodes) .enter() .append("circle") .attr("r", rad) .style( "fill", "white") - .style("stroke-width", 8) + .style("stroke-width", nodeborder) .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit /*.append ("text") // name als text neben node .attr ("dx", 12) @@ -133,10 +150,10 @@ const data = { - var simulation = d3.forceSimulation(data.nodes) //Simulation + const simulation = d3.forceSimulation(data.nodes) //Simulation .force("link", d3.forceLink() .id(function(d) { return d.id; }) - .distance(100) + .distance(150) .links(data.links) ) .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen @@ -145,7 +162,6 @@ const data = { function ticked() { - console.log(data); text .attr("dx", function(d) { return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) From f8c9b6a72fcbe7f696ff214982976727ed0c2473 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 31 Jan 2020 14:56:00 +0100 Subject: [PATCH 58/74] Fixed bug Type of relation fixed (on path between two linked nodes) --- .../graph-visualization.component.ts | 45 ++++++++++++++----- 1 file changed, 34 insertions(+), 11 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index efe39ef0..d7894ca6 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -34,7 +34,7 @@ const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung v svg.append('defs').append('marker') .attr("id",'arrowhead') - .attr('viewBox','-0 -5 10 10') //the bound of the SVG viewport for the current SVG fragment. defines a coordinate system 10 wide and 10 high starting on (0,-5) + .attr('viewBox','-0 -5 10 10') //Koordinatensystem .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten .attr('refY',0) .attr('orient','auto') @@ -146,6 +146,7 @@ const data = { .data(data.links) .enter() .append("text") + .style("fill","#999" ) .text(function (d){return d.type}); @@ -164,24 +165,46 @@ const data = { function ticked() { text .attr("dx", function(d) { - return Math.max(0+rad, Math.min(d.x+23, width-rad)); }) - .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }) + return Math.max(0+rad, Math.min(d.x+23, width-rad))}) + .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }) linkText - .attr("dx", function(d){return 0.5*((d.source.x)+(d.target.x))}) - .attr("dy", function(d){return 0.5*((d.source.y)+(d.target.y)) }) + .attr("dx", function(d){ + if ((d.target.x>width) || (d.source.x>width)) { + if (d.target.x>d.source.x) { + return Math.min(width,width-0.5*(width-d.source.x)); + } else { + return Math.min(width,width-0.5*(width-d.target.x)); + } ; + } else { + return Math.min(0.5*((Math.max(0,(d.target.x)))-(Math.max(0,(d.source.x))))+(Math.max(0,d.source.x))); + } + }) + //Relationstext bleibt am Link + .attr("dy", function(d){ + if ((d.target.y>height) || (d.source.y>height)) { + if (d.target.y>d.source.y) { + return Math.min(height,height-0.5*(height-d.source.y)); + } + else { + return Math.min(height,height-0.5*(height-d.target.y)); + } + } + else { + return (0.5*((Math.max(0,(d.target.y)))-(Math.max(0,(d.source.y))))+(Math.max(0,d.source.y))); + }}) link // Anfang und Ende der Links / Bestimmung der Position - .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)); }) // Postionen nur innerhalb des Fensters erlaubt - .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)); }) - .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)); }) - .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)); }); + .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)) }) // Postionen nur innerhalb des Fensters erlaubt + .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)) }) + .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)) }) + .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)) }); node // Bestimmung der Postion einzelner Nodes - .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)); }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße - .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)); }); + .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)) }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }); } function ended() { From 6842b0b74f7773d44acc4b486f0c49ae1893224a Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 18 Feb 2020 16:58:55 +0100 Subject: [PATCH 59/74] Added Link to Graph Visualization in Module-Management --- .../graph-visualization/graph-visualization.component.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index fcff4a3d..3163f4b5 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,8 +1,9 @@

Graph-Visualization

-
+ +
-
\ No newline at end of file +
From f09f59b006a6138dff60f32d623dbcb6230a3340 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 28 Feb 2020 19:15:38 +0100 Subject: [PATCH 60/74] Resize svg when window resized --- .../graph-visualization.component.html | 7 +- .../graph-visualization.component.ts | 413 ++++++++++-------- 2 files changed, 238 insertions(+), 182 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index 3163f4b5..a454716a 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,9 +1,8 @@

Graph-Visualization

- -
-
+
+
-
+
\ No newline at end of file diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index d7894ca6..b57ca058 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,7 +1,8 @@ -import { Component, AfterContentInit, ViewEncapsulation } from '@angular/core'; +import { Component, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; +import { window } from 'rxjs/operators'; @Component({ selector: 'graph-visualization', @@ -11,225 +12,281 @@ import { pathToFileURL } from 'url'; }) export class GraphVisualizationComponent implements AfterContentInit { - ngAfterContentInit(): void { - const margin = {top: 10, right: 30, bottom: 30, left: 40}; // Definition der Größen - const width = 1400 - margin.left - margin.right; - const height = 900 - margin.top - margin.bottom; - const rad=20 // Radius eines Knotens - const nodeborder =8; // Umrandungsdicke eines Knotens + @ViewChild('g') svgContainer: ElementRef; + ngAfterContentInit(): void { + console.log('element ref width:'); + console.log(this.svgContainer.nativeElement.offsetWidth); + const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen + const width = 100; //parent.innerWidth - margin.left - margin.right; + //var width= window.innerWidth; + const height = 100; + const rad = 20 // Radius eines Knotens + const nodeborder = 8; // Umrandungsdicke eines Knotens + const testweite = parent.innerWidth; + console.log("Die ermittelte Weite ist:" + testweite); -const svg = d3.select("#graph") // Größe u. Grenzen SVG -.append("svg") - .attr("width", width + margin.left + margin.right) - .attr("height", height + margin.top + margin.bottom) -.append("g") - .attr("transform", + const svg = d3.select("#graph") // Größe u. Grenzen SVG + .append("svg") + .attr("width", width + '%') + .attr("height", height+'%') + .append("g") + .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); - - -const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes - -svg.append('defs').append('marker') - .attr("id",'arrowhead') - .attr('viewBox','-0 -5 10 10') //Koordinatensystem - .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten - .attr('refY',0) - .attr('orient','auto') - .attr('markerWidth',13) - .attr('markerHeight',13) - .attr('xoverflow','visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke','none'); - -//d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - -const data = { - "nodes": [ - { - "id": 1, - "name": "ModulA", - "group": 1 - }, - { - "id": 2, - "name": "ModulB", - "group": 2 - }, - { - "id": 3, - "name": "ModulC", - "group": 3 - - }, { - "id": 4, - "name": "cap11", - "group":1 - }, - { - "id": 5, - "name": "cap12", - "group":1 - }, - { - "id": 6, - "name": "cap21", - "group":2 + + const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + + svg.append('defs').append('marker') + .attr("id", 'arrowhead') + .attr('viewBox', '-0 -5 10 10') //Koordinatensystem + .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('refY', 0) + .attr('orient', 'auto') + .attr('markerWidth', 13) + .attr('markerHeight', 13) + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke', 'none'); + + + //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten + + const data = { + "nodes": [ + { + "id": 1, + "name": "ModulA", + "group": 1 + }, + { + "id": 2, + "name": "ModulB", + "group": 2 + }, + { + "id": 3, + "name": "ModulC", + "group": 3 + + }, { + "id": 4, + "name": "cap11", + "group": 1 + }, + { + "id": 5, + "name": "cap12", + "group": 1 + }, + { + "id": 6, + "name": "cap21", + "group": 2 + } + ], + "links": [ + + { + "source": 1, + "target": 4, + "type": "is_a" + }, + { + "source": 1, + "target": 5, + "type": "has" + }, + { + "source": 2, + "target": 6, + "type": "has" + } + ] } - ], - "links": [ - - { - "source": 1, - "target": 4, - "type": "is_a" - } , - { - "source": 1, - "target": 5, - "type": "has" - } , - { - "source": 2, - "target": 6, - "type":"has" - } - ] -} - const link = svg // Links initialisieren - .selectAll(".links") - .data(data.links) - .enter() - .append("line") + const link = svg // Links initialisieren + .selectAll(".links") + .data(data.links) + .enter() + .append("line") .style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') - ; + ; link.append("title") - .text(function(d){return d.type}); - const node = svg // Nodes initialisieren - .selectAll("circle") - .data(data.nodes) - .enter() - .append("circle") + .text(function (d) { return d.type }); + const node = svg // Nodes initialisieren + .selectAll("circle") + .data(data.nodes) + .enter() + .append("circle") .attr("r", rad) - .style( "fill", "white") - .style("stroke-width", nodeborder) - .style("stroke",function(d){return colors(d.group);} ) // Node-Farben nach Gruppenzugehörigkeit - /*.append ("text") // name als text neben node - .attr ("dx", 12) - .attr ("dy", ".35em") - .text(function(d){return d.name})*/ + .on('mouseover', function(){ + d3.select(this).transition() + .duration('2') + .attr('r', rad+5) + }) + .on('mouseout', function(){ + d3.select(this).transition() + .attr('r', rad) + }) + .style("fill", "white") + .style("stroke-width", nodeborder) + .style("stroke", function (d) { return colors(d.group); }) // Node-Farben nach Gruppenzugehörigkeit + /*.append ("text") // name als text neben node + .attr ("dx", 12) + .attr ("dy", ".35em") + .text(function(d){return d.name})*/ .call(d3.drag() - .on("start", dragstarted) - .on("drag", dragged)); -; - - const text =svg + .on("start", dragstarted) + .on("drag", dragged)); + ; + + const text = svg .selectAll("text") .data(data.nodes) .enter() .append("text") - - .text(function(d){return d.name}); - const linkText =svg + .text(function (d) { return d.name }); + + const linkText = svg .selectAll("links") .data(data.links) .enter() .append("text") - .style("fill","#999" ) - .text(function (d){return d.type}); + .style("fill", "#999") + .text(function (d) { return d.type }); + - - - const simulation = d3.forceSimulation(data.nodes) //Simulation - .force("link", d3.forceLink() - .id(function(d) { return d.id; }) - .distance(150) - .links(data.links) - ) - .force("charge", d3.forceManyBody().strength(-200)) // Anziehungskraft bzw. Abstoßen - .force("center", d3.forceCenter(width / 2, height / 2)) // Nodes mittig von svg - .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - - function ticked() { - text - .attr("dx", function(d) { - return Math.max(0+rad, Math.min(d.x+23, width-rad))}) - .attr("dy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }) - linkText - .attr("dx", function(d){ - if ((d.target.x>width) || (d.source.x>width)) { - if (d.target.x>d.source.x) { - return Math.min(width,width-0.5*(width-d.source.x)); - } else { - return Math.min(width,width-0.5*(width-d.target.x)); - } ; - } else { - return Math.min(0.5*((Math.max(0,(d.target.x)))-(Math.max(0,(d.source.x))))+(Math.max(0,d.source.x))); - } - }) - //Relationstext bleibt am Link - .attr("dy", function(d){ - if ((d.target.y>height) || (d.source.y>height)) { - if (d.target.y>d.source.y) { - return Math.min(height,height-0.5*(height-d.source.y)); - } - else { - return Math.min(height,height-0.5*(height-d.target.y)); - } - } - else { - return (0.5*((Math.max(0,(d.target.y)))-(Math.max(0,(d.source.y))))+(Math.max(0,d.source.y))); - }}) - - link // Anfang und Ende der Links / Bestimmung der Position - .attr("x1", function(d) { return Math.max(0+rad, Math.min(d.source.x, width-rad)) }) // Postionen nur innerhalb des Fensters erlaubt - .attr("y1", function(d) { return Math.max(0+rad,Math.min(d.source.y, height-rad)) }) - .attr("x2", function(d) { return Math.max(0+rad,Math.min(d.target.x, width-rad)) }) - .attr("y2", function(d) { return Math.max(0+rad, Math.min(d.target.y, height-rad)) }); + const ticked = () => { + const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; + const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - node // Bestimmung der Postion einzelner Nodes - .attr("cx", function (d) { return Math.max(0+rad,Math.min(d.x, width-rad)) }) // Zertrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße - .attr("cy", function(d) { return Math.max(0+rad, Math.min(d.y, height-rad)) }); - } + // data.nodes.indexOf[0].fx= centerWidth; + //data.nodes.indexOf[0].fy= centerHeight; + text + .attr("dx", (d)=>{ + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) + .attr("dy", (d) => { + const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) - function ended() { - console.log("ended"); - } + linkText + .attr("dx", (d)=>{ + d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); + d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(width- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceX-relativeTargetX)/2)+ relativeTargetX))) +'%'; + }) + .attr("dy", (d)=>{ + d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; + }) - function dragstarted(d) { - if (!d3.event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } + link // Anfang und Ende der Links / Bestimmung der Position nachfolgend Closure-Funtktionen um Zugriff mit this. außerhalb der Funktion + .attr("x1", (d)=> { + d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // Umwandlung des Radius in relative Zahl (in % !) + const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX+'%';}) + .attr("y1", (d)=> { + d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY+'%';}) + .attr("x2", (d)=> { + d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX+'%';}) + .attr("y2", (d)=> { + d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY+'%';}) + + node // Bestimmung der Postion einzelner Nodes + .attr("cx", (d) => { + d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX+'%'; + }) // Zentrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + .attr("cy", (d) => { + d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) + const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY+'%';}) + } + + const simulation = d3.forceSimulation(data.nodes) //Simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(300) + .links(data.links) + ) + .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes + + + + + .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - function dragged(d) { - console.log(d) + function ended() { + console.log("ended"); + } + function mouseover(){ + d3.select(this).select("circle").transition() + .duration(750) + .attr("r", rad+10) + } + + function mouseout(){ + d3.select(this).select("circle").transition() + .duration(750) + .attr("r", rad) + } + function dragstarted(d) { + if (!d3.event.active) simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + function dragged(d) { + + console.log(d3.event) 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; } - - } + + } constructor() { } } From f11fac176416dc78abfd4d5faecbe616ad25692d Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:15:06 +0100 Subject: [PATCH 61/74] Added function to get data for ontology-visualization of each module --- .../graph-visualization.component.ts | 130 ++++++++++++++++-- .../graph-visualization.module.ts | 8 +- 2 files changed, 127 insertions(+), 11 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index b57ca058..2c46fd12 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -3,6 +3,8 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; +import { ModuleService } from 'app/shared/services/module.service'; + @Component({ selector: 'graph-visualization', @@ -12,11 +14,16 @@ import { window } from 'rxjs/operators'; }) export class GraphVisualizationComponent implements AfterContentInit { + constructor( + private moduleService: ModuleService + ) { + }; + @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - console.log('element ref width:'); - console.log(this.svgContainer.nativeElement.offsetWidth); + //console.log('element ref width:'); + // console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -25,7 +32,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - console.log("Die ermittelte Weite ist:" + testweite); + //console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -54,9 +61,12 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - const data = { + +// ##### DATA ###### + + + /*const data = { "nodes": [ { "id": 1, @@ -108,8 +118,107 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } +*/ +var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + var group=0; + var moduleId=0; + var capabilityId=100; + var inputId=1000; + var outputId=10000; + var skillId=100000; + var typeId=1000000; + var data= {nodes:[], links:[]}; + //################################################ + receivedData.forEach(modul => { + moduleId++; + group++; + data.nodes.push({ + "id" : moduleId, + "name": modul.name, + "group": 1 + }); + modul.capabilities.forEach(capability=>{ + capabilityId++; + data.nodes.push({ + "id" : capabilityId, + "name": capability.name, + "group": 2 + }) + data.links.push({ + "source": moduleId, + "target": capabilityId, + "type": "has_capability" + }) + capability.hasInput.forEach(input=>{ + inputId++; + typeId++; + data.nodes.push({ + "id" : inputId, + "name": input.name, + "group": 3 + }) + data.links.push({ + "source": capabilityId, + "target": inputId, + "type": "has_input" + }) + data.nodes.push({ + "id": typeId, + "name": input.stateType, + "group": 6 + }) + data.links.push({ + "source": inputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.hasOutput.forEach(output=>{ + outputId++; + typeId++; + data.nodes.push({ + "id" : outputId, + "name": output.name, + "group": 4 + }) + data.links.push({ + "source": capabilityId, + "target": outputId, + "type": "has_output" + }) + + data.nodes.push({ + "id": typeId, + "name": output.stateType, + "group": 6 + }) + data.links.push({ + "source": outputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.executableViaSkill.forEach(skill=>{ + skillId++; + data.nodes.push({ + "id" : skillId, + "name": skill.name, + "group": 5 + }) + data.links.push({ + "source": capabilityId, + "target": skillId, + "type": "executable_via_Skill" + }) + }) + }) + + }); +// ################################################## + + console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -246,7 +355,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -255,7 +364,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - console.log("ended"); + //console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -276,7 +385,7 @@ export class GraphVisualizationComponent implements AfterContentInit { function dragged(d) { - console.log(d3.event) + // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -287,6 +396,9 @@ export class GraphVisualizationComponent implements AfterContentInit { } } - constructor() { } + + + + } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index afb5fff0..df9c2713 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,11 +1,15 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; +import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter + GraphVisualizationRouter, ], - declarations: [GraphVisualizationComponent] + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + ] }) export class GraphVisualizationModule { } From a1646df38d571ddd34db57bb9ec6f8732ec6f35a Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:17:05 +0100 Subject: [PATCH 62/74] Revert "Added function to get data for ontology-visualization of each module" This reverts commit 8b9e53e21219f952dc96071abaab17af6acf8a40. --- .../graph-visualization.component.ts | 130 ++---------------- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 - 3 files changed, 11 insertions(+), 128 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 2c46fd12..b57ca058 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -3,8 +3,6 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; -import { ModuleService } from 'app/shared/services/module.service'; - @Component({ selector: 'graph-visualization', @@ -14,16 +12,11 @@ import { ModuleService } from 'app/shared/services/module.service'; }) export class GraphVisualizationComponent implements AfterContentInit { - constructor( - private moduleService: ModuleService - ) { - }; - @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - //console.log('element ref width:'); - // console.log(this.svgContainer.nativeElement.offsetWidth); + console.log('element ref width:'); + console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -32,7 +25,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - //console.log("Die ermittelte Weite ist:" + testweite); + console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -61,12 +54,9 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); + //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - -// ##### DATA ###### - - - /*const data = { + const data = { "nodes": [ { "id": 1, @@ -118,107 +108,8 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } -*/ -var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - var group=0; - var moduleId=0; - var capabilityId=100; - var inputId=1000; - var outputId=10000; - var skillId=100000; - var typeId=1000000; - var data= {nodes:[], links:[]}; - //################################################ - receivedData.forEach(modul => { - moduleId++; - group++; - data.nodes.push({ - "id" : moduleId, - "name": modul.name, - "group": 1 - }); - modul.capabilities.forEach(capability=>{ - capabilityId++; - data.nodes.push({ - "id" : capabilityId, - "name": capability.name, - "group": 2 - }) - data.links.push({ - "source": moduleId, - "target": capabilityId, - "type": "has_capability" - }) - capability.hasInput.forEach(input=>{ - inputId++; - typeId++; - data.nodes.push({ - "id" : inputId, - "name": input.name, - "group": 3 - }) - data.links.push({ - "source": capabilityId, - "target": inputId, - "type": "has_input" - }) - data.nodes.push({ - "id": typeId, - "name": input.stateType, - "group": 6 - }) - data.links.push({ - "source": inputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.hasOutput.forEach(output=>{ - outputId++; - typeId++; - data.nodes.push({ - "id" : outputId, - "name": output.name, - "group": 4 - }) - data.links.push({ - "source": capabilityId, - "target": outputId, - "type": "has_output" - }) - - data.nodes.push({ - "id": typeId, - "name": output.stateType, - "group": 6 - }) - data.links.push({ - "source": outputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.executableViaSkill.forEach(skill=>{ - skillId++; - data.nodes.push({ - "id" : skillId, - "name": skill.name, - "group": 5 - }) - data.links.push({ - "source": capabilityId, - "target": skillId, - "type": "executable_via_Skill" - }) - }) - }) - - }); -// ################################################## - - console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -355,7 +246,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -364,7 +255,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - //console.log("ended"); + console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -385,7 +276,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); function dragged(d) { - // console.log(d3.event) + console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -396,9 +287,6 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); } } - - - - + constructor() { } } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index df9c2713..afb5fff0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,15 +1,11 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter, + GraphVisualizationRouter ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - ] + declarations: [GraphVisualizationComponent] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index 5790705b..ae60048e 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,4 +1,3 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; - From a4a6555455fefa2d380bfedbf88534d00aa0a443 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 24 Mar 2020 21:17:55 +0100 Subject: [PATCH 63/74] Revert "Revert "Added function to get data for ontology-visualization of each module"" This reverts commit e3e86ce7d5f9a64ca29b6727bcc3077f1b4ba03a. --- .../graph-visualization.component.ts | 130 ++++++++++++++++-- .../graph-visualization.module.ts | 8 +- frontend/src/shared/index.ts | 1 + 3 files changed, 128 insertions(+), 11 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index b57ca058..2c46fd12 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -3,6 +3,8 @@ import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; +import { ModuleService } from 'app/shared/services/module.service'; + @Component({ selector: 'graph-visualization', @@ -12,11 +14,16 @@ import { window } from 'rxjs/operators'; }) export class GraphVisualizationComponent implements AfterContentInit { + constructor( + private moduleService: ModuleService + ) { + }; + @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - console.log('element ref width:'); - console.log(this.svgContainer.nativeElement.offsetWidth); + //console.log('element ref width:'); + // console.log(this.svgContainer.nativeElement.offsetWidth); const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen const width = 100; //parent.innerWidth - margin.left - margin.right; @@ -25,7 +32,7 @@ export class GraphVisualizationComponent implements AfterContentInit { const rad = 20 // Radius eines Knotens const nodeborder = 8; // Umrandungsdicke eines Knotens const testweite = parent.innerWidth; - console.log("Die ermittelte Weite ist:" + testweite); + //console.log("Die ermittelte Weite ist:" + testweite); const svg = d3.select("#graph") // Größe u. Grenzen SVG .append("svg") @@ -54,9 +61,12 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - //d3.json("\frontend\app\own-modules\graph-visualization\graph-data.json", function(input) { // Input-Daten - const data = { + +// ##### DATA ###### + + + /*const data = { "nodes": [ { "id": 1, @@ -108,8 +118,107 @@ export class GraphVisualizationComponent implements AfterContentInit { } ] } +*/ +var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + var group=0; + var moduleId=0; + var capabilityId=100; + var inputId=1000; + var outputId=10000; + var skillId=100000; + var typeId=1000000; + var data= {nodes:[], links:[]}; + //################################################ + receivedData.forEach(modul => { + moduleId++; + group++; + data.nodes.push({ + "id" : moduleId, + "name": modul.name, + "group": 1 + }); + modul.capabilities.forEach(capability=>{ + capabilityId++; + data.nodes.push({ + "id" : capabilityId, + "name": capability.name, + "group": 2 + }) + data.links.push({ + "source": moduleId, + "target": capabilityId, + "type": "has_capability" + }) + capability.hasInput.forEach(input=>{ + inputId++; + typeId++; + data.nodes.push({ + "id" : inputId, + "name": input.name, + "group": 3 + }) + data.links.push({ + "source": capabilityId, + "target": inputId, + "type": "has_input" + }) + data.nodes.push({ + "id": typeId, + "name": input.stateType, + "group": 6 + }) + data.links.push({ + "source": inputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.hasOutput.forEach(output=>{ + outputId++; + typeId++; + data.nodes.push({ + "id" : outputId, + "name": output.name, + "group": 4 + }) + data.links.push({ + "source": capabilityId, + "target": outputId, + "type": "has_output" + }) + + data.nodes.push({ + "id": typeId, + "name": output.stateType, + "group": 6 + }) + data.links.push({ + "source": outputId, + "target": typeId, + "type": "is_state_type" + }) + }) + capability.executableViaSkill.forEach(skill=>{ + skillId++; + data.nodes.push({ + "id" : skillId, + "name": skill.name, + "group": 5 + }) + data.links.push({ + "source": capabilityId, + "target": skillId, + "type": "executable_via_Skill" + }) + }) + }) + + }); +// ################################################## + + console.log(data); const link = svg // Links initialisieren .selectAll(".links") @@ -246,7 +355,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-150)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes @@ -255,7 +364,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .on("tick", ticked); // tick wird in jedem Schritt durchgeführt function ended() { - console.log("ended"); + //console.log("ended"); } function mouseover(){ d3.select(this).select("circle").transition() @@ -276,7 +385,7 @@ export class GraphVisualizationComponent implements AfterContentInit { function dragged(d) { - console.log(d3.event) + // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } @@ -287,6 +396,9 @@ export class GraphVisualizationComponent implements AfterContentInit { } } - constructor() { } + + + + } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index afb5fff0..df9c2713 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,11 +1,15 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; +import { ModuleService } from 'app/shared/services/module.service'; @NgModule({ imports: [ - GraphVisualizationRouter + GraphVisualizationRouter, ], - declarations: [GraphVisualizationComponent] + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/shared/index.ts b/frontend/src/shared/index.ts index ae60048e..5790705b 100644 --- a/frontend/src/shared/index.ts +++ b/frontend/src/shared/index.ts @@ -1,3 +1,4 @@ export * from './modules'; export * from './pipes/shared-pipes.module'; export * from './guard'; + From eaad69ff21b18147bd8f11ea5ae1c797f6ebebc3 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Sun, 29 Mar 2020 14:18:25 +0200 Subject: [PATCH 64/74] Modified node-ID assignment. Modified comments --- .../graph-visualization.component.ts | 345 +++++++----------- 1 file changed, 140 insertions(+), 205 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 2c46fd12..87a1ef6a 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -22,19 +22,14 @@ export class GraphVisualizationComponent implements AfterContentInit { @ViewChild('g') svgContainer: ElementRef; ngAfterContentInit(): void { - //console.log('element ref width:'); - // console.log(this.svgContainer.nativeElement.offsetWidth); - - const margin = { top: 10, right: 30, bottom: 30, left: 40 }; // Definition der Größen - const width = 100; //parent.innerWidth - margin.left - margin.right; + const margin = { top: 10, right: 30, bottom: 30, left: 40 }; + const width = 100; //parent.innerWidth - margin.left - margin.right; //var width= window.innerWidth; const height = 100; - const rad = 20 // Radius eines Knotens - const nodeborder = 8; // Umrandungsdicke eines Knotens + const rad = 20 // node radius + const nodeborder = 8; // node border thickness const testweite = parent.innerWidth; - //console.log("Die ermittelte Weite ist:" + testweite); - - const svg = d3.select("#graph") // Größe u. Grenzen SVG + const svg = d3.select("#graph") // size definitions for the svg .append("svg") .attr("width", width + '%') .attr("height", height+'%') @@ -44,12 +39,12 @@ export class GraphVisualizationComponent implements AfterContentInit { - const colors = d3.scaleOrdinal(d3.schemeCategory10); // Automatische Zuteilung von Farben für die Nodes + const colors = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours svg.append('defs').append('marker') .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //Koordinatensystem - .attr('refX', rad + nodeborder) // Position Pfeilspitze in Abh. v. Radius und Umrandung der Knoten + .attr('viewBox', '-0 -5 10 10') //coordinate system + .attr('refX', rad + nodeborder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -63,193 +58,141 @@ export class GraphVisualizationComponent implements AfterContentInit { -// ##### DATA ###### - - - /*const data = { - "nodes": [ - { - "id": 1, - "name": "ModulA", - "group": 1 - }, - { - "id": 2, - "name": "ModulB", - "group": 2 - }, - { - "id": 3, - "name": "ModulC", - "group": 3 - - }, { - "id": 4, - "name": "cap11", - "group": 1 - }, - { - "id": 5, - "name": "cap12", - "group": 1 - }, - { - "id": 6, - "name": "cap21", - "group": 2 - } - ], - "links": [ - - { - "source": 1, - "target": 4, - "type": "is_a" - }, - { - "source": 1, - "target": 5, - "type": "has" - }, - { - "source": 2, - "target": 6, - "type": "has" - } - ] - } -*/ +/* Get data*/ +//########################################################## var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - var group=0; - var moduleId=0; - var capabilityId=100; - var inputId=1000; - var outputId=10000; - var skillId=100000; - var typeId=1000000; - var data= {nodes:[], links:[]}; - //################################################ - receivedData.forEach(modul => { - moduleId++; - group++; - data.nodes.push({ - "id" : moduleId, - "name": modul.name, - "group": 1 - }); - modul.capabilities.forEach(capability=>{ - capabilityId++; - data.nodes.push({ - "id" : capabilityId, - "name": capability.name, - "group": 2 - }) - data.links.push({ - "source": moduleId, - "target": capabilityId, - "type": "has_capability" - }) - capability.hasInput.forEach(input=>{ - inputId++; - typeId++; - data.nodes.push({ - "id" : inputId, - "name": input.name, - "group": 3 - }) - data.links.push({ - "source": capabilityId, - "target": inputId, - "type": "has_input" - }) - data.nodes.push({ - "id": typeId, - "name": input.stateType, - "group": 6 - }) - data.links.push({ - "source": inputId, - "target": typeId, - "type": "is_state_type" - }) - }) - capability.hasOutput.forEach(output=>{ - outputId++; - typeId++; - data.nodes.push({ - "id" : outputId, - "name": output.name, - "group": 4 - }) - data.links.push({ - "source": capabilityId, - "target": outputId, - "type": "has_output" - }) - - data.nodes.push({ - "id": typeId, - "name": output.stateType, - "group": 6 - }) - data.links.push({ - "source": outputId, - "target": typeId, - "type": "is_state_type" - }) - - }) - capability.executableViaSkill.forEach(skill=>{ - skillId++; - data.nodes.push({ - "id" : skillId, - "name": skill.name, - "group": 5 - }) - data.links.push({ - "source": capabilityId, - "target": skillId, - "type": "executable_via_Skill" - }) - }) - - }) +var data= {nodes:[], links:[]}; +let idMap= new Map(); // Map for saving Node-IDs +var id=0; // ID start value +receivedData.forEach(receivedModule => { //loop over all modules +id++; +idMap.set(receivedModule, id); // assigns an ID for each module-node +data.nodes.push({ // adds a node for each module +"id" : idMap.get(receivedModule), +"name": receivedModule.name, +"group": 1 // assings node-groups (here: node colour) +}); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.name, + "group": 2 + }) + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }) + + capability.hasInput.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.name, + "group": 3 + }) + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }) + }) + + capability.hasOutput.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.name, + "group": 4 + }) + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }) + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.stateType, + "group": 6 + }) + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }) + }) + + capability.executableViaSkill.forEach(skill=>{ + id++; + idMap.set(skill, id) // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.name, + "group": 5 + }) + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }) + }) + + }) - }); +}); // ################################################## console.log(data); - const link = svg // Links initialisieren + const link = svg // link definitions .selectAll(".links") .data(data.links) .enter() .append("line") .style("stroke", "#aaa") .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') + .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; link.append("title") - .text(function (d) { return d.type }); - const node = svg // Nodes initialisieren + .text(function (d) { return d.type }); // get link title from data + const node = svg // node definitions .selectAll("circle") .data(data.nodes) .enter() .append("circle") .attr("r", rad) - .on('mouseover', function(){ + .on('mouseover', function(){ // function on mousover: increase radius d3.select(this).transition() .duration('2') - .attr('r', rad+5) + .attr('r', rad+6) }) - .on('mouseout', function(){ + .on('mouseout', function(){ // function on mousout: decrease radius to orgin d3.select(this).transition() .attr('r', rad) }) + .on('dblclick', doubleclick)// function on doubleclick: + .style("fill", "white") .style("stroke-width", nodeborder) - .style("stroke", function (d) { return colors(d.group); }) // Node-Farben nach Gruppenzugehörigkeit - /*.append ("text") // name als text neben node + .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group + /*.append ("text") .attr ("dx", 12) .attr ("dy", ".35em") .text(function(d){return d.name})*/ @@ -258,21 +201,21 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); .on("drag", dragged)); ; - const text = svg + const text = svg // node text definitions .selectAll("text") .data(data.nodes) .enter() .append("text") - .text(function (d) { return d.name }); + .text(function (d) { return d.name }); // get text from data - const linkText = svg + const linkText = svg // link text definitions .selectAll("links") .data(data.links) .enter() .append("text") .style("fill", "#999") - .text(function (d) { return d.type }); + .text(function (d) { return d.type }); // get link text from data @@ -280,13 +223,11 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const ticked = () => { - const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; + const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; // center definitions of the svg const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - // data.nodes.indexOf[0].fx= centerWidth; - //data.nodes.indexOf[0].fy= centerHeight; text - .attr("dx", (d)=>{ + .attr("dx", (d)=>{ // restrictions for node text positions in the svg const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) @@ -296,7 +237,7 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) linkText - .attr("dx", (d)=>{ + .attr("dx", (d)=>{ // restrictions for link text positions in the svg d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; @@ -313,10 +254,10 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; }) - link // Anfang und Ende der Links / Bestimmung der Position nachfolgend Closure-Funtktionen um Zugriff mit this. außerhalb der Funktion - .attr("x1", (d)=> { + link + .attr("x1", (d)=> { // restrictions for link positions in the svg d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // Umwandlung des Radius in relative Zahl (in % !) + const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // converting radius from absolute to relative const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; return relativeSourceX+'%';}) .attr("y1", (d)=> { @@ -335,13 +276,13 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; return relativeTargetY+'%';}) - node // Bestimmung der Postion einzelner Nodes - .attr("cx", (d) => { + node + .attr("cx", (d) => { // restrictions for node positions in the svg d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; return relativeDragX+'%'; - }) // Zentrum der Node-kreise, Maximxaler Wert begrenzt auf Fenstergröße + }) .attr("cy", (d) => { d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; @@ -349,39 +290,33 @@ var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); return relativeDragY+'%';}) } - const simulation = d3.forceSimulation(data.nodes) //Simulation + const simulation = d3.forceSimulation(data.nodes) // simulation .force("link", d3.forceLink() .id(function (d) { return d.id; }) .distance(300) .links(data.links) ) - .force("charge", d3.forceManyBody().strength(-550)) // Anziehungskraft bzw. Abstoßen + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes - .on("tick", ticked); // tick wird in jedem Schritt durchgeführt - - function ended() { - //console.log("ended"); - } - function mouseover(){ - d3.select(this).select("circle").transition() - .duration(750) - .attr("r", rad+10) - } + .on("tick", ticked); // tick on every step of the simulation - function mouseout(){ - d3.select(this).select("circle").transition() - .duration(750) - .attr("r", rad) - } function dragstarted(d) { if (!d3.event.active) simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } + function doubleclick(){ + id++; + data.nodes.push({ + "id": id, + "name": "neighbor", + "group": 7 + }) + } function dragged(d) { From 22ca4119096563b7f56c398e2f91d49a71b59df4 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Thu, 2 Apr 2020 20:51:41 +0200 Subject: [PATCH 65/74] Added new service for node creation --- .../graph-visualization.component.ts | 139 ++-------- .../graph-visualization.module.ts | 3 + .../node-creator.service.ts | 241 ++++++++---------- 3 files changed, 137 insertions(+), 246 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 87a1ef6a..2b712bc0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -4,6 +4,8 @@ import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; import { ModuleService } from 'app/shared/services/module.service'; +import { ResolveStart } from '@angular/router'; +import { NodeCreatorService } from './node-creator.service'; @Component({ @@ -15,7 +17,8 @@ import { ModuleService } from 'app/shared/services/module.service'; export class GraphVisualizationComponent implements AfterContentInit { constructor( - private moduleService: ModuleService + //private moduleService: ModuleService, + private nodeCreatorService: NodeCreatorService ) { }; @@ -56,108 +59,7 @@ export class GraphVisualizationComponent implements AfterContentInit { .style('stroke', 'none'); - - -/* Get data*/ -//########################################################## -var receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); -var data= {nodes:[], links:[]}; -let idMap= new Map(); // Map for saving Node-IDs -var id=0; // ID start value -receivedData.forEach(receivedModule => { //loop over all modules -id++; -idMap.set(receivedModule, id); // assigns an ID for each module-node -data.nodes.push({ // adds a node for each module -"id" : idMap.get(receivedModule), -"name": receivedModule.name, -"group": 1 // assings node-groups (here: node colour) -}); - - receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module - id++; - idMap.set(capability, id); // assigns an ID for each capability-node - data.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.name, - "group": 2 - }) - data.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description - }) - - capability.hasInput.forEach(input=>{ - id++; - idMap.set(input, id); // assigns an ID for each input-node - data.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.name, - "group": 3 - }) - data.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }) - }) - - capability.hasOutput.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - data.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.name, - "group": 4 - }) - data.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }) - id++; - data.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.stateType, - "group": 6 - }) - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }) - }) - - capability.executableViaSkill.forEach(skill=>{ - id++; - idMap.set(skill, id) // adds a node for each skill of current capability - data.nodes.push({ - "id" : idMap.get(skill), - "name": skill.name, - "group": 5 - }) - data.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" - }) - }) - - }) - -}); -// ################################################## +var data= this.nodeCreatorService.getAllNodes(); // load data created by node-creator.service console.log(data); @@ -187,8 +89,17 @@ data.nodes.push({ // adds a node for each module d3.select(this).transition() .attr('r', rad) }) - .on('dblclick', doubleclick)// function on doubleclick: - + .on('dblclick', function(d){ const node= { "id":30, + "name": "neighbor", + "group": 7} + data.nodes.push(node) + data.links.push({ + "source": d.id, + "target":30 + }) + + console.log(d);// function on doubleclick: + }) .style("fill", "white") .style("stroke-width", nodeborder) .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group @@ -309,15 +220,19 @@ data.nodes.push({ // adds a node for each module d.fx = d.x; d.fy = d.y; } - function doubleclick(){ - id++; - data.nodes.push({ - "id": id, - "name": "neighbor", - "group": 7 + /*function doubleclick(d){ + + const node= {x: 500, y:50, "id":1000, + "name": "neighbor", + "group": 7} + data.nodes.push(node) + data.links.push({ + "source": d.id, + "target":1000 }) + console.log(d); } - +*/ function dragged(d) { // console.log(d3.event) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index df9c2713..711024bc 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -2,6 +2,7 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; import { ModuleService } from 'app/shared/services/module.service'; +import { NodeCreatorService } from './node-creator.service'; @NgModule({ imports: [ @@ -10,6 +11,8 @@ import { ModuleService } from 'app/shared/services/module.service'; declarations: [GraphVisualizationComponent], providers: [ ModuleService, + NodeCreatorService + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts index b000a9aa..50422f64 100644 --- a/frontend/src/modules/graph-visualization/node-creator.service.ts +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -1,142 +1,116 @@ import { Injectable } from '@angular/core'; -import { Capability } from '@shared/models/capability/Capability'; -import { ProductionModule } from '@shared/models/production-module/ProductionModule'; -import { Skill } from '@shared/models/skill/Skill'; -import { Observable } from 'rxjs'; -import { map } from 'rxjs/operators'; -import { CapabilityService } from 'src/shared/services/capability.service'; -import { SkillService } from 'src/shared/services/skill.service'; -import { ModuleService } from '../../shared/services/module.service'; -import { D3CapabilityNode, D3GraphData, D3Link, D3ModuleNode, D3Node, D3SkillNode, NodeType } from './D3GraphData'; - +import { ModuleService } from 'app/shared/services/module.service'; @Injectable({ providedIn: 'root' }) export class NodeCreatorService { - apiRoot = "/api"; - modules: ProductionModule[] - - constructor( - private moduleService: ModuleService, - private skillService: SkillService, - private capabilityService: CapabilityService) { } - - // /** Creates node- and link-data with transmitted data of connected modules for d3.js visualization */ - // getAllNodes(moduleIri: string): Observable { - - // return this.moduleService.getAllModules().pipe(map(modules => { - // // If a module name is passed, filter the module list for that specific one - // if(moduleIri != undefined) { - // modules = modules.filter(module => module.iri == moduleIri); - // } - // return this.getDataFromModules(modules); - // })); - // } - - getAllModuleNodes(moduleIri: string): Observable { - - return this.moduleService.getAllModules().pipe(map(modules => { - // If a module IRI is passed, filter the module list for that specific one - if(moduleIri != undefined && moduleIri != "") { - console.log("filtering"); - console.log("moduleIri: '" + moduleIri + "'"); - - - modules = modules.filter(module => module.iri == moduleIri); - } - - console.log("modules"); - console.log(modules); - - - - const graphData = new D3GraphData(); - modules.forEach(module => { //loop over all modules - - graphData.addNode(new D3ModuleNode(module.iri, module.getLocalName())); - // TODO: Add module components - - const skillData = this.getDataFromSkills(module.skills); - graphData.appendAndConnectData(skillData, module.iri, NodeType.D3SkillNode, "hasSkill"); - - const capabilityData = this.getDataFromCapabilities(module.capabilities); - graphData.appendAndConnectData(capabilityData, module.iri, NodeType.D3CapabilityNode, "hasCapability"); - }); - return graphData; - - })); - } - - getAllSkillNodes(skillIri: string): Observable { - return this.skillService.getAllSkills().pipe(map(skills => { - // If a skill IRI is passed, filter the skill list for that specific one - if(skillIri != undefined && skillIri != "") { - skills = skills.filter(skill => skill.iri == skillIri); - } - - return this.getDataFromSkills(skills); - })); - } - - getAllCapabilityNodes(capabilityIri: string): Observable { - return this.capabilityService.getAllCapabilities().pipe(map(capabilities => { - // If a capability IRI is passed, filter the capability list for that specific one - if(capabilityIri != undefined && capabilityIri != "") { - capabilities = capabilities.filter(capability => capability.iri == capabilityIri); - } - - return this.getDataFromCapabilities(capabilities); - })); - } - - - private getDataFromSkills(skills: Skill[]): D3GraphData { - const graphData = new D3GraphData(); - skills.forEach(skill => { - - graphData.addNode(new D3SkillNode(skill.iri, skill.getLocalName())); - - // Add state machine - graphData.addNode(new D3Node(skill.stateMachine.iri, skill.stateMachine.getLocalName(), 120)); - graphData.addLink(new D3Link(skill.iri, skill.stateMachine.iri, "hasStateMachine")); - - // Add skill parameters - skill.skillParameters.forEach(parameter => { - graphData.addNode(new D3Node(parameter.iri, parameter.getLocalName(), 140)); - graphData.addLink(new D3Link(skill.iri, parameter.iri, "hasSkillParameter")); - }); - - // Add skill outputs - skill.skillOutputs.forEach(output => { - graphData.addNode(new D3Node(output.iri,output.getLocalName(), 160)); - graphData.addLink(new D3Link(skill.iri, output.iri, "hasSkillOutput")); - }); - }); - return graphData; - } - - private getDataFromCapabilities(capabilities: Capability[]): D3GraphData { - const graphData = new D3GraphData(); - - capabilities.forEach(capability => { //loop over all capabilities of current module - graphData.addNode(new D3CapabilityNode(capability.iri, capability.getLocalName())); - - capability.inputs.forEach(input => { - graphData.addNode(new D3Node(input.iri, input.getLocalName(), 240)); - graphData.addLink(new D3Link(capability.iri, input.iri, "hasInput")); - - }); - - capability.outputs.forEach(output => { - graphData.addNode(new D3Node(output.iri, output.getLocalName(), 270)); - graphData.addLink(new D3Link(capability.iri, output.iri, "hasOutput")); - }); - }); - return graphData; - } - - + apiRoot = "/api"; + constructor( + private moduleService: ModuleService + ) {} + getAllNodes() { + + const receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); + const data= {nodes:[], links:[]}; + const idMap= new Map(); // Map for saving Node-IDs + let id=0; // ID start value + receivedData.forEach(receivedModule => { //loop over all modules + id++; + idMap.set(receivedModule, id); // assigns an ID for each module-node + data.nodes.push({ // adds a node for each module + "id" : idMap.get(receivedModule), + "name": receivedModule.name, + "group": 1 // assings node-groups (here: node colour) + }); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.name, + "group": 2 + }); + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }); + + capability.hasInput.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.name, + "group": 3 + }); + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }); + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.stateType, + "group": 6 + }); + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }); + }); + + capability.hasOutput.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.name, + "group": 4 + }); + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }); + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.stateType, + "group": 6 + }); + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }); + }); + + capability.executableViaSkill.forEach(skill=>{ + id++; + idMap.set(skill, id); // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.name, + "group": 5 + }); + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }); + }); + + }); + + }); + + return data; + } } @@ -146,4 +120,3 @@ export class NodeCreatorService { - From a3dcfb8907d39258ff3c3fa44455199a28d03cba Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 1 May 2020 15:41:52 +0200 Subject: [PATCH 66/74] Visualization of chosen module-graph possible. --- .../graph-visualization.component.ts | 35 +++++++++++++++---- .../graph-visualization.routing.ts | 12 +++---- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 2b712bc0..5a918e16 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,11 +1,15 @@ -import { Component, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component,OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" import { enterView } from '@angular/core/src/render3/state'; import { pathToFileURL } from 'url'; import { window } from 'rxjs/operators'; import { ModuleService } from 'app/shared/services/module.service'; -import { ResolveStart } from '@angular/router'; +import { ResolveStart, ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; +import { getModuleFactory__POST_R3__ } from '@angular/core/src/linker/ng_module_factory_loader'; +import { defineDirective } from '@angular/core/src/render3'; + + @Component({ @@ -14,15 +18,30 @@ import { NodeCreatorService } from './node-creator.service'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit { +export class GraphVisualizationComponent implements AfterContentInit, OnInit {name:String; constructor( - //private moduleService: ModuleService, + private route: ActivatedRoute, private nodeCreatorService: NodeCreatorService ) { }; @ViewChild('g') svgContainer: ElementRef; + moduleName: string; + ngOnInit(): void + { + + + // console.log("Der Name ist :"+this.moduleName); + +//console.log(this.route.queryParams); + + //.subscribe(params=>params.name) + + //this.name= params.name; + + } + ngAfterContentInit(): void { const margin = { top: 10, right: 30, bottom: 30, left: 40 }; @@ -58,10 +77,11 @@ export class GraphVisualizationComponent implements AfterContentInit { .attr('fill', '#999') .style('stroke', 'none'); +this.route.params.subscribe(p=>{ + this.moduleName=p['moduleName']; +}) +var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service -var data= this.nodeCreatorService.getAllNodes(); // load data created by node-creator.service - - console.log(data); const link = svg // link definitions .selectAll(".links") @@ -246,6 +266,7 @@ var data= this.nodeCreatorService.getAllNodes(); // load data created by node-cr } } + diff --git a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts index ded3fe38..ba430b6c 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.routing.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.routing.ts @@ -1,15 +1,13 @@ import { Routes, RouterModule } from '@angular/router'; import { NgModule } from '@angular/core'; -import {GraphVisualizationComponent} from './graph-visualization.component' +import {GraphVisualizationComponent} from './graph-visualization.component'; const routes: Routes = [ - { path: '', + { path: 'modules/:moduleName', component: GraphVisualizationComponent, - data: { - title: 'Graph-Visualization' - }, - children: [] - + + }, + {path:'modules', component: GraphVisualizationComponent } ]; From 92f4ebedb59ed4baf2fa1f1abdfe98df938df34b Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Fri, 1 May 2020 15:50:51 +0200 Subject: [PATCH 67/74] Modified some stylings --- .../graph-visualization.component.ts | 53 +++++++++++-------- 1 file changed, 32 insertions(+), 21 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 5a918e16..b705e793 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -99,28 +99,16 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .data(data.nodes) .enter() .append("circle") - .attr("r", rad) - .on('mouseover', function(){ // function on mousover: increase radius - d3.select(this).transition() - .duration('2') - .attr('r', rad+6) + .attr("r",function(d){ + if(d.group==1){return rad+8} + else{return rad} }) - .on('mouseout', function(){ // function on mousout: decrease radius to orgin - d3.select(this).transition() - .attr('r', rad) - }) - .on('dblclick', function(d){ const node= { "id":30, - "name": "neighbor", - "group": 7} - data.nodes.push(node) - data.links.push({ - "source": d.id, - "target":30 + .on('mouseover', mouseover) + .on('mouseout', mouseout) + .style("fill", function(d){ + if(d.group==1){return "black"} + else{return "whitesmoke"} }) - - console.log(d);// function on doubleclick: - }) - .style("fill", "white") .style("stroke-width", nodeborder) .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group /*.append ("text") @@ -137,7 +125,10 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .data(data.nodes) .enter() .append("text") - + .attr("font-weight", function(d){ + if(d.group==1){return "bold"} + else{return "normal"} + }) .text(function (d) { return d.name }); // get text from data const linkText = svg // link text definitions @@ -146,6 +137,7 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre .enter() .append("text") .style("fill", "#999") + .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data @@ -265,6 +257,25 @@ var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data cre //d.fy = null; } + function mouseover(d){ // function on mousover: increase radius + if(d.group==1){ + d3.select(this).transition() + .duration('2') + .attr('r', rad+12)} + else{ + d3.select(this).transition() + .duration('2') + .attr('r', rad+6)} + } + function mouseout(d){ // function on mousout: decrease radius to orgin + if(d.group==1){ + d3.select(this).transition() + .attr('r', rad+8) + } + else{ d3.select(this).transition() + .attr('r', rad)} + } + } From 346b69083a6dd430bbead8583074bea887d80377 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Wed, 27 May 2020 21:17:44 +0200 Subject: [PATCH 68/74] Adding nodes on doubleclick possible --- .../graph-visualization.component.ts | 569 +++++++++++------- 1 file changed, 363 insertions(+), 206 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index b705e793..0cd91854 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,13 +1,11 @@ -import { Component,OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; +import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; import * as d3 from "d3" -import { enterView } from '@angular/core/src/render3/state'; -import { pathToFileURL } from 'url'; -import { window } from 'rxjs/operators'; -import { ModuleService } from 'app/shared/services/module.service'; -import { ResolveStart, ActivatedRoute } from '@angular/router'; +import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; -import { getModuleFactory__POST_R3__ } from '@angular/core/src/linker/ng_module_factory_loader'; -import { defineDirective } from '@angular/core/src/render3'; + + + + @@ -18,55 +16,76 @@ import { defineDirective } from '@angular/core/src/render3'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit, OnInit {name:String; +export class GraphVisualizationComponent implements AfterContentInit, OnInit { + name: String; + svg: any; + link: any; + /** The displayed node name*/ + text: any; + /** The displayed link description */ + linkText: any; + /** The displayed node */ + node: any; + /** Loaded data: Connected modules with their capabilities and skills*/ + data: any; + /**Returns different colors automatically. The node-border color is the same for all node-group members*/ + color: any; +/**Simulation of the force directed graph */ + simulation: any; + + margin = { top: 10, right: 30, bottom: 30, left: 40 }; + width = 100; + height = 100; +/**Defines the nodeborder thickness*/ + nodeborder = 8; + /**Defines the standard radius of a node*/ + rad = 20; + + index=0; constructor( private route: ActivatedRoute, private nodeCreatorService: NodeCreatorService - ) { + ) { }; @ViewChild('g') svgContainer: ElementRef; moduleName: string; - ngOnInit(): void - { - + ngOnInit(): void { - // console.log("Der Name ist :"+this.moduleName); -//console.log(this.route.queryParams); + // console.log("Der Name ist :"+this.moduleName); + + //console.log(this.route.queryParams); //.subscribe(params=>params.name) - + //this.name= params.name; } ngAfterContentInit(): void { - const margin = { top: 10, right: 30, bottom: 30, left: 40 }; - const width = 100; //parent.innerWidth - margin.left - margin.right; - //var width= window.innerWidth; - const height = 100; - const rad = 20 // node radius - const nodeborder = 8; // node border thickness - const testweite = parent.innerWidth; - const svg = d3.select("#graph") // size definitions for the svg + + console.log(this.svgContainer); + + + this.svg = d3.select("#graph") // size definitions for the svg .append("svg") - .attr("width", width + '%') - .attr("height", height+'%') + .attr("width", this.width + '%') + .attr("height", this.height + '%') .append("g") .attr("transform", - "translate(" + margin.left + "," + margin.top + ")"); + "translate(" + this.margin.left + "," + this.margin.top + ")"); - const colors = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours + this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - svg.append('defs').append('marker') + this.svg.append('defs').append('marker') .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', rad + nodeborder) // arrow position and dimensions + .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -77,210 +96,348 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit {na .attr('fill', '#999') .style('stroke', 'none'); -this.route.params.subscribe(p=>{ - this.moduleName=p['moduleName']; -}) -var data= this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }) + this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + + + //############################################################################################################## + + this.simulation = d3.forceSimulation(this.data.nodes); + // simulation.force('link').links(data.links); // simulation + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links) + ) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - const link = svg // link definitions - .selectAll(".links") - .data(data.links) - .enter() - .append("line") - .style("stroke", "#aaa") + this.refresh(); + } + + /** + * Draws the graph, executed first on AfterContentInit and on every doubleclick on a node + */ + refresh() { + console.log("refresh starting..") + console.log(this.data.nodes) + + + /* this.simulation.force("link").links(this.data.links); + this.simulation.force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links) + + + )*/ +//this.link.exit().remove(); +//this.node.exit().remove(); + + + this.link = this.svg // link definitions + .selectAll(".link") + .data(this.data.links, function (d){return d.target.id}) + /*.join(enter => enter.append("line") + .append("line").style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)'), // arrow on end of the link + exit => exit.remove())*/ + + var linkEnter=this.link.enter() + .append("line").style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; - link.append("title") - .text(function (d) { return d.type }); // get link title from data - const node = svg // node definitions - .selectAll("circle") - .data(data.nodes) - .enter() - .append("circle") - .attr("r",function(d){ - if(d.group==1){return rad+8} - else{return rad} + linkEnter.append("title") + .text(function (d){return d.type}) + this.link = this.link.merge(linkEnter); + this.link.exit().remove(); + + this.node = this.svg.selectAll(".node") + .data(this.data.nodes, function (d){return d.id}) + .join(enter => enter.append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8 } + else { return this.rad } + }) + .on('mouseover', this.mouseover) + .on('mouseout', this.mouseout) + .style("fill", function (d) { + if (d.group == 1) { return "black" } + else { return "whitesmoke" } + }) + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }) + .on('dblclick', this.nodeDoubleClick) + .call(d3.drag() + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)), + update => update.style("fill", "red"), + exit => exit.remove()) + + + /*var nodeEnter= this.node.enter().append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8 } + else { return this.rad } }) - .on('mouseover', mouseover) - .on('mouseout', mouseout) - .style("fill", function(d){ - if(d.group==1){return "black"} - else{return "whitesmoke"} + /*nodeEnter.append("title") + .text(function(d){return d.id}) + nodeEnter.append("text") + .text(function (d){return d.name})*/ + /* nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseout', this.mouseout); + nodeEnter.style("fill", function (d) { + if (d.group == 1) { return "black" } + else { return "whitesmoke" } }) - .style("stroke-width", nodeborder) - .style("stroke", function (d) { return colors(d.group); }) // set different node colours for each node-group + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group /*.append ("text") .attr ("dx", 12) .attr ("dy", ".35em") .text(function(d){return d.name})*/ - .call(d3.drag() - .on("start", dragstarted) - .on("drag", dragged)); - ; - - const text = svg // node text definitions - .selectAll("text") - .data(data.nodes) - .enter() - .append("text") - .attr("font-weight", function(d){ - if(d.group==1){return "bold"} - else{return "normal"} - }) - .text(function (d) { return d.name }); // get text from data +/* + nodeEnter.on('dblclick', this.nodeDoubleClick); + nodeEnter.call(d3.drag() + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)); + this.node=this.node.merge(nodeEnter); + this.node.exit().remove(); + + console.log(this.node) + */ + + this.text = this.svg.selectAll("text").data(this.data.nodes) + /*.join(enter=> enter.append("text") + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold" } else { return "normal" }}) + .text(function (d) { console.log(d.name);return d.name }), + exit=> exit.remove() )*/ - const linkText = svg // link text definitions - .selectAll("links") - .data(data.links) - .enter() - .append("text") + + var textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name}) + + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold" } + else { return "normal" } + }) + .text(function (d) { console.log(d.name); + return d.name }); // get text from data + this.text=this.text.merge(textEnter); +//this.text.exit().remove(); + + this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) + .join(enter=> enter.append("text") + .style("fill", "#999") + .attr("font-style", "italic") + .text(function (d) { return d.type }), + exit=> exit.remove()) + + + + + /* var linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data + this.linkText=this.linkText.merge(linkTextEnter); +//this.linkText.exit().remove(); +*/ + + this.simulation.nodes(this.data.nodes); + this.simulation.force("link").links(this.data.links); + //########################################################################################################### + } +/** + * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element + * @param d The position of every graph element (i.e. node, link...) + */ + ticked = () => { + const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg + const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; + + + this.text + .attr("dx", (d) => { // restrictions for node text positions in the svg + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%' + }) + .attr("dy", (d) => { + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%' + }) + this.linkText + .attr("dx", (d) => { // restrictions for link text positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; + }) + .attr("dy", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; + }) - + this.link + .attr("x1", (d) => { // restrictions for link positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX + '%'; + }) + .attr("y1", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY + '%'; + }) + .attr("x2", (d) => { + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX + '%'; + }) + .attr("y2", (d) => { + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY + '%'; + }) + this.node + .attr("cx", (d) => { + d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX + '%'; + }) + .attr("cy", (d) => { + d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)) + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY + '%'; + }) + } - const ticked = () => { - const centerWidth= this.svgContainer.nativeElement.offsetWidth/2; // center definitions of the svg - const centerHeight= this.svgContainer.nativeElement.offsetHeight/2; - - text - .attr("dx", (d)=>{ // restrictions for node text positions in the svg - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, width - 5*relativeRad))+'%'}) - .attr("dy", (d) => { - const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, height - 5*relativeRad))+'%' }) - - linkText - .attr("dx", (d)=>{ // restrictions for link text positions in the svg - d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(width- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceX-relativeTargetX)/2)+ relativeTargetX))) +'%'; - }) - .attr("dy", (d)=>{ - d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(height- 5*relativeRad, (Math.max(0+ relativeRad, ((relativeSourceY-relativeTargetY)/2)+ relativeTargetY))) +'%'; - }) - - link - .attr("x1", (d)=> { // restrictions for link positions in the svg - d.source.x= Math.max(0 + rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; // converting radius from absolute to relative - const relativeSourceX = 100*d.source.x/this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX+'%';}) - .attr("y1", (d)=> { - d.source.y=Math.max(0 + rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100*d.source.y/this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY+'%';}) - .attr("x2", (d)=> { - d.target.x=Math.max(0 + rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100*d.target.x/this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX+'%';}) - .attr("y2", (d)=> { - d.target.y=Math.max(0 + rad , Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)); - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100*d.target.y/this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY+'%';}) - - node - .attr("cx", (d) => { // restrictions for node positions in the svg - d.x=Math.max(0 + rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5*rad)) - const relativeRad =100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX=100* d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX+'%'; - }) - .attr("cy", (d) => { - d.y= Math.max(0 + rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5*rad)) - const relativeRad = 100* rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY=100* d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY+'%';}) - } - const simulation = d3.forceSimulation(data.nodes) // simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(300) - .links(data.links) - ) - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) //Zentrierung der Nodes - + dragstarted = (d) => { + if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } - - - .on("tick", ticked); // tick on every step of the simulation - function dragstarted(d) { - if (!d3.event.active) simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - /*function doubleclick(d){ - - const node= {x: 500, y:50, "id":1000, - "name": "neighbor", - "group": 7} - data.nodes.push(node) - data.links.push({ - "source": d.id, - "target":1000 - }) - console.log(d); - } -*/ - function dragged(d) { - // console.log(d3.event) - 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; - } + /** + * Drag function, executed on every drag movement + * @param d The dragged node + */ + dragged(d) { + // console.log(d3.event) + d.fx = d3.event.x; + d.fy = d3.event.y; + } - function mouseover(d){ // function on mousover: increase radius - if(d.group==1){ - d3.select(this).transition() + dragended(d) { + //if (!d3.event.active) this.simulation.alphaTarget(0); + //d.fx = null; + // d.fy = null; + } +/** + * Mouseover function, executed when mouse over node. The radius of the node will increase. + * @param d node under the cursor + */ + mouseover(d) { + if (d.group == 1) { + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 12) + } + else { + d3.select(this).transition() .duration('2') - .attr('r', rad+12)} - else{ + .attr('r', 20 + 6) + } + } +/** + * Mousout function, executed when mouse cursor leaves the node + * @param d node wich is left by the cursor after mouseover + */ + mouseout(d) { // function on mousout: decrease this.radius to orgin + if (d.group == 1) { d3.select(this).transition() - .duration('2') - .attr('r', rad+6)} + .attr('r', 20 + 8) } - function mouseout(d){ // function on mousout: decrease radius to orgin - if(d.group==1){ - d3.select(this).transition() - .attr('r', rad+8) - } - else{ d3.select(this).transition() - .attr('r', rad)} + else { + d3.select(this).transition() + .attr('r', 20) } - + } +/** + * Doubleclick function, executed on every doubleclick on a node + *@param d The clicked node + */ + + nodeDoubleClick=(d)=> { +//this.simulation.stop(); + this.index++; + + const newNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(newNode); + this.data.links.push({ "source": d.id, "target": newNode, "type": "testclick" }) + /* + this.link.exit().remove(); + this.linkText.exit().remove(); + this.text.exit().remove(); */ + /*this.simulation.force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + + // ) + // .force("collision", d3.forceCollide(40)) + // .on("tick", this.ticked) // tick on every ste + -400)) // node magnetism /attraction + //.force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) + //simulation.restart(); //node oben links + fehlermeldungen + // this.simulation.nodes(this.data.nodes) + // this.simulation.force('link').links(this.data.links) + //simulation.force('nodes').nodes(data.nodes) + //node.data(data.nodes); + //link.data(data.links); + //node.enter().append("circle"); + //this.node.exit().remove(); + //link.exit().remove(); + //linkText.exit().remove(); + //text.exit().remove(); + // this.node = this.node.data(this.data.nodes); + //link= link.data(data.links) + //node.selectAll('circle').remove(); + //this.simulation.restart(); */ + this.refresh(); + // this.simulation.restart(); + //console.log(this.data.nodes) } - - - - - -} +} \ No newline at end of file From fba43144236059b169bc5374e863f58aa1dc8466 Mon Sep 17 00:00:00 2001 From: Tom Jeleniewski Date: Tue, 2 Jun 2020 18:50:48 +0200 Subject: [PATCH 69/74] Modified some code --- .../graph-visualization.component.ts | 271 +++++------------- 1 file changed, 78 insertions(+), 193 deletions(-) diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 0cd91854..7a368116 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -4,12 +4,6 @@ import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; - - - - - - @Component({ selector: 'graph-visualization', encapsulation: ViewEncapsulation.None, @@ -33,6 +27,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { /**Simulation of the force directed graph */ simulation: any; + margin = { top: 10, right: 30, bottom: 30, left: 40 }; width = 100; height = 100; @@ -51,24 +46,42 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { @ViewChild('g') svgContainer: ElementRef; moduleName: string; - ngOnInit(): void { - + ngOnInit(): void {} - // console.log("Der Name ist :"+this.moduleName); - //console.log(this.route.queryParams); - - //.subscribe(params=>params.name) + ngAfterContentInit(): void { + + + + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }) + this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + + this.setSvg(); + this.setSimulation(); + } - //this.name= params.name; +/**Defines settings of the simulation */ +setSimulation(){ + this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - } + this.refreshSimulation(); +} - ngAfterContentInit(): void { - console.log(this.svgContainer); - +/**Defines settings of the SVG and the color-scheme of the nodes */ + setSvg(){ this.svg = d3.select("#graph") // size definitions for the svg .append("svg") @@ -82,7 +95,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - this.svg.append('defs').append('marker') + this.svg.append('defs').append('marker') // marker/ arrow settings .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions @@ -94,139 +107,49 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .append('svg:path') .attr('d', 'M 0,-5 L 10 ,0 L 0,5') .attr('fill', '#999') - .style('stroke', 'none'); - - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }) - this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service - + .style('stroke', 'none');} - //############################################################################################################## - this.simulation = d3.forceSimulation(this.data.nodes); - // simulation.force('link').links(data.links); // simulation - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links) - - ) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism /attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - - this.refresh(); - } /** - * Draws the graph, executed first on AfterContentInit and on every doubleclick on a node + * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ - refresh() { - console.log("refresh starting..") - console.log(this.data.nodes) - - - /* this.simulation.force("link").links(this.data.links); - this.simulation.force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links) - - - )*/ -//this.link.exit().remove(); -//this.node.exit().remove(); - - - this.link = this.svg // link definitions - .selectAll(".link") - .data(this.data.links, function (d){return d.target.id}) - /*.join(enter => enter.append("line") - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)'), // arrow on end of the link - exit => exit.remove())*/ - - var linkEnter=this.link.enter() + refreshSimulation() { + this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id}) + var linkEnter=this.link.enter() //enter-selection .append("line").style("stroke", "#aaa") .attr("class", "links") .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; linkEnter.append("title") .text(function (d){return d.type}) - this.link = this.link.merge(linkEnter); - this.link.exit().remove(); + this.link = this.link.merge(linkEnter); // merge old elements with entered elements + this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - this.node = this.svg.selectAll(".node") - .data(this.data.nodes, function (d){return d.id}) - .join(enter => enter.append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8 } - else { return this.rad } - }) - .on('mouseover', this.mouseover) - .on('mouseout', this.mouseout) - .style("fill", function (d) { - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - }) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }) - .on('dblclick', this.nodeDoubleClick) - .call(d3.drag() - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)), - update => update.style("fill", "red"), - exit => exit.remove()) - - - /*var nodeEnter= this.node.enter().append("circle") + this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id}) + + var nodeEnter= this.node.enter().append("circle") .attr("r", (d) => { if (d.group == 1) { return this.rad + 8 } else { return this.rad } }) - /*nodeEnter.append("title") - .text(function(d){return d.id}) - nodeEnter.append("text") - .text(function (d){return d.name})*/ - /* nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseover', this.mouseover); nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", function (d) { - if (d.group == 1) { return "black" } - else { return "whitesmoke" } - }) + nodeEnter.style("fill", this.setNodeStyle) .style("stroke-width", this.nodeborder) .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group - /*.append ("text") - .attr ("dx", 12) - .attr ("dy", ".35em") - .text(function(d){return d.name})*/ -/* nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() + nodeEnter.call(d3.drag() // reactions when dragging a node .on("start", this.dragstarted) .on("drag", this.dragged) .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); - this.node.exit().remove(); + this.node=this.node.merge(nodeEnter); // merge old elements with entered elements + this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + - console.log(this.node) - */ - this.text = this.svg.selectAll("text").data(this.data.nodes) - /*.join(enter=> enter.append("text") - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold" } else { return "normal" }}) - .text(function (d) { console.log(d.name);return d.name }), - exit=> exit.remove() )*/ - - - var textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name}) + var textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name}) .attr("font-weight", function (d) { if (d.group == 1) { return "bold" } @@ -235,30 +158,20 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .text(function (d) { console.log(d.name); return d.name }); // get text from data this.text=this.text.merge(textEnter); -//this.text.exit().remove(); + this.text.exit().remove(); this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) - .join(enter=> enter.append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type }), - exit=> exit.remove()) - - - - - /* var linkTextEnter=this.linkText.enter().append("text") + var linkTextEnter=this.linkText.enter().append("text") .style("fill", "#999") .attr("font-style", "italic") .text(function (d) { return d.type }); // get link text from data this.linkText=this.linkText.merge(linkTextEnter); -//this.linkText.exit().remove(); -*/ + this.linkText.exit().remove(); + - this.simulation.nodes(this.data.nodes); + this.simulation.nodes(this.data.nodes); // simualtion uses current data this.simulation.force("link").links(this.data.links); - - //########################################################################################################### + } /** * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element @@ -267,8 +180,7 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { ticked = () => { const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - + this.text .attr("dx", (d) => { // restrictions for node text positions in the svg const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; @@ -340,36 +252,33 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { }) } - +/** + * Function executed on a drag start + */ dragstarted = (d) => { if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); d.fx = d.x; d.fy = d.y; } - - /** * Drag function, executed on every drag movement * @param d The dragged node */ dragged(d) { - // console.log(d3.event) d.fx = d3.event.x; d.fy = d3.event.y; } dragended(d) { - //if (!d3.event.active) this.simulation.alphaTarget(0); - //d.fx = null; - // d.fy = null; + } /** * Mouseover function, executed when mouse over node. The radius of the node will increase. * @param d node under the cursor */ mouseover(d) { - if (d.group == 1) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node d3.select(this).transition() .duration('2') .attr('r', 20 + 12) @@ -381,11 +290,11 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { } } /** - * Mousout function, executed when mouse cursor leaves the node + * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. * @param d node wich is left by the cursor after mouseover */ - mouseout(d) { // function on mousout: decrease this.radius to orgin - if (d.group == 1) { + mouseout(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node d3.select(this).transition() .attr('r', 20 + 8) } @@ -398,46 +307,22 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { * Doubleclick function, executed on every doubleclick on a node *@param d The clicked node */ - nodeDoubleClick=(d)=> { -//this.simulation.stop(); this.index++; - - const newNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(newNode); - this.data.links.push({ "source": d.id, "target": newNode, "type": "testclick" }) - /* - this.link.exit().remove(); - this.linkText.exit().remove(); - this.text.exit().remove(); */ - /*this.simulation.force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links)) - - // ) - // .force("collision", d3.forceCollide(40)) - // .on("tick", this.ticked) // tick on every ste - -400)) // node magnetism /attraction - //.force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth/2, this.svgContainer.nativeElement.offsetHeight/2)) - //simulation.restart(); //node oben links + fehlermeldungen - // this.simulation.nodes(this.data.nodes) - // this.simulation.force('link').links(this.data.links) - //simulation.force('nodes').nodes(data.nodes) - //node.data(data.nodes); - //link.data(data.links); - //node.enter().append("circle"); - //this.node.exit().remove(); - //link.exit().remove(); - //linkText.exit().remove(); - //text.exit().remove(); - // this.node = this.node.data(this.data.nodes); - //link= link.data(data.links) - //node.selectAll('circle').remove(); - //this.simulation.restart(); */ - this.refresh(); - // this.simulation.restart(); - //console.log(this.data.nodes) + const testNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(testNode); + this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }) + this.refreshSimulation(); + } + + /** Returns the color of the inner circle of a node */ + setNodeStyle(d){ + if (d.group == 1) { return "black" } + else { return "whitesmoke" } + } +/** Returns the node border color for each node-group */ + setNodeBorderStyle=(d)=>{ + return this.color(d.group); } } \ No newline at end of file From 4886ba0d3bb8cf84ed21c5ebb9ff88b2f23261b7 Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Fri, 5 Nov 2021 16:39:31 +0100 Subject: [PATCH 70/74] Added gitignore to ignore uploaded-files directory --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .gitignore diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..1c4b2bb0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# ---> VisualStudioCode +/uploaded-files/ From 518c903902c85570340d28c58d97a616a1898a19 Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Sat, 20 Aug 2022 13:59:21 +0200 Subject: [PATCH 71/74] Brought graph visu up to date --- backend/package-lock.json | 7 +- frontend/package-lock.json | 564 +++++++++++++++++- .../graph-visualization.component.ts | 530 ++++++++-------- .../graph-visualization.module.ts | 20 +- .../node-creator.service.ts | 217 +++---- 5 files changed, 942 insertions(+), 396 deletions(-) diff --git a/backend/package-lock.json b/backend/package-lock.json index 1fe7e40c..179658b9 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -19,6 +19,7 @@ "form-data": "^4.0.0", "formidable": "^1.2.1", "node-opcua": "^2.62.5", + "reflect-metadata": "0.1.13", "request": "^2.88.0", "rimraf": "3.0.2", "rxjs": "^7.4.0", @@ -12911,8 +12912,7 @@ "node_modules/reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "peer": true + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, "node_modules/regexp.prototype.flags": { "version": "1.3.1", @@ -25132,8 +25132,7 @@ "reflect-metadata": { "version": "0.1.13", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.1.13.tgz", - "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==", - "peer": true + "integrity": "sha512-Ts1Y/anZELhSsjMcU605fU9RE4Oi3p5ORujwbIKXfWa+0Zxs510Qrmrce5/Jowq3cHSZSJqBjypxmHarc+vEWg==" }, "regexp.prototype.flags": { "version": "1.3.1", diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 7e2148ce..d578c761 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,6 +26,7 @@ "chartjs-plugin-annotation": "^1.4.0", "chartjs-plugin-datalabels": "^2.0.0", "core-js": "2.6.5", + "d3": "^5.15.0", "formidable": "^2.0.1", "ng2-charts": "3.0.11", "rxjs": "^7.4.0", @@ -5322,8 +5323,7 @@ "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "node_modules/commondir": { "version": "1.0.1", @@ -5933,6 +5933,281 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "node_modules/d3": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", + "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==", + "dependencies": { + "d3-array": "1", + "d3-axis": "1", + "d3-brush": "1", + "d3-chord": "1", + "d3-collection": "1", + "d3-color": "1", + "d3-contour": "1", + "d3-dispatch": "1", + "d3-drag": "1", + "d3-dsv": "1", + "d3-ease": "1", + "d3-fetch": "1", + "d3-force": "1", + "d3-format": "1", + "d3-geo": "1", + "d3-hierarchy": "1", + "d3-interpolate": "1", + "d3-path": "1", + "d3-polygon": "1", + "d3-quadtree": "1", + "d3-random": "1", + "d3-scale": "2", + "d3-scale-chromatic": "1", + "d3-selection": "1", + "d3-shape": "1", + "d3-time": "1", + "d3-time-format": "2", + "d3-timer": "1", + "d3-transition": "1", + "d3-voronoi": "1", + "d3-zoom": "1" + } + }, + "node_modules/d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "node_modules/d3-axis": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", + "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + }, + "node_modules/d3-brush": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", + "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", + "dependencies": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "node_modules/d3-chord": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", + "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "dependencies": { + "d3-array": "1", + "d3-path": "1" + } + }, + "node_modules/d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "node_modules/d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "node_modules/d3-contour": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", + "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "dependencies": { + "d3-array": "^1.1.1" + } + }, + "node_modules/d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "node_modules/d3-drag": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "dependencies": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "node_modules/d3-dsv": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", + "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", + "dependencies": { + "commander": "2", + "iconv-lite": "0.4", + "rw": "1" + }, + "bin": { + "csv2json": "bin/dsv2json", + "csv2tsv": "bin/dsv2dsv", + "dsv2dsv": "bin/dsv2dsv", + "dsv2json": "bin/dsv2json", + "json2csv": "bin/json2dsv", + "json2dsv": "bin/json2dsv", + "json2tsv": "bin/json2dsv", + "tsv2csv": "bin/dsv2dsv", + "tsv2json": "bin/dsv2json" + } + }, + "node_modules/d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + }, + "node_modules/d3-fetch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", + "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", + "dependencies": { + "d3-dsv": "1" + } + }, + "node_modules/d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "dependencies": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "node_modules/d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "node_modules/d3-geo": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", + "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "dependencies": { + "d3-array": "1" + } + }, + "node_modules/d3-hierarchy": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + }, + "node_modules/d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "dependencies": { + "d3-color": "1" + } + }, + "node_modules/d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "node_modules/d3-polygon": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", + "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==" + }, + "node_modules/d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + }, + "node_modules/d3-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", + "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + }, + "node_modules/d3-scale": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", + "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "dependencies": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "node_modules/d3-scale-chromatic": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", + "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", + "dependencies": { + "d3-color": "1", + "d3-interpolate": "1" + } + }, + "node_modules/d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + }, + "node_modules/d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "dependencies": { + "d3-path": "1" + } + }, + "node_modules/d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + }, + "node_modules/d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "dependencies": { + "d3-time": "1" + } + }, + "node_modules/d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "node_modules/d3-transition": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "dependencies": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "node_modules/d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, + "node_modules/d3-zoom": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", + "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "dependencies": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, "node_modules/damerau-levenshtein": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", @@ -8613,7 +8888,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -13620,6 +13894,11 @@ "queue-microtask": "^1.2.2" } }, + "node_modules/rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "node_modules/rxjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", @@ -13641,8 +13920,7 @@ "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "node_modules/sass": { "version": "1.49.9", @@ -19927,8 +20205,7 @@ "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" }, "commondir": { "version": "1.0.1", @@ -20391,6 +20668,270 @@ "integrity": "sha1-XQKkaFCt8bSjF5RqOSj8y1v9BCU=", "dev": true }, + "d3": { + "version": "5.16.0", + "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", + "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==", + "requires": { + "d3-array": "1", + "d3-axis": "1", + "d3-brush": "1", + "d3-chord": "1", + "d3-collection": "1", + "d3-color": "1", + "d3-contour": "1", + "d3-dispatch": "1", + "d3-drag": "1", + "d3-dsv": "1", + "d3-ease": "1", + "d3-fetch": "1", + "d3-force": "1", + "d3-format": "1", + "d3-geo": "1", + "d3-hierarchy": "1", + "d3-interpolate": "1", + "d3-path": "1", + "d3-polygon": "1", + "d3-quadtree": "1", + "d3-random": "1", + "d3-scale": "2", + "d3-scale-chromatic": "1", + "d3-selection": "1", + "d3-shape": "1", + "d3-time": "1", + "d3-time-format": "2", + "d3-timer": "1", + "d3-transition": "1", + "d3-voronoi": "1", + "d3-zoom": "1" + } + }, + "d3-array": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", + "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + }, + "d3-axis": { + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", + "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + }, + "d3-brush": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", + "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, + "d3-chord": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", + "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "requires": { + "d3-array": "1", + "d3-path": "1" + } + }, + "d3-collection": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", + "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" + }, + "d3-color": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", + "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + }, + "d3-contour": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", + "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "requires": { + "d3-array": "^1.1.1" + } + }, + "d3-dispatch": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", + "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + }, + "d3-drag": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", + "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "requires": { + "d3-dispatch": "1", + "d3-selection": "1" + } + }, + "d3-dsv": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", + "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", + "requires": { + "commander": "2", + "iconv-lite": "0.4", + "rw": "1" + } + }, + "d3-ease": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", + "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + }, + "d3-fetch": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", + "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", + "requires": { + "d3-dsv": "1" + } + }, + "d3-force": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", + "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "requires": { + "d3-collection": "1", + "d3-dispatch": "1", + "d3-quadtree": "1", + "d3-timer": "1" + } + }, + "d3-format": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", + "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + }, + "d3-geo": { + "version": "1.12.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", + "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "requires": { + "d3-array": "1" + } + }, + "d3-hierarchy": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", + "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + }, + "d3-interpolate": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", + "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "requires": { + "d3-color": "1" + } + }, + "d3-path": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", + "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + }, + "d3-polygon": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", + "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==" + }, + "d3-quadtree": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", + "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + }, + "d3-random": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", + "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + }, + "d3-scale": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", + "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "requires": { + "d3-array": "^1.2.0", + "d3-collection": "1", + "d3-format": "1", + "d3-interpolate": "1", + "d3-time": "1", + "d3-time-format": "2" + } + }, + "d3-scale-chromatic": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", + "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", + "requires": { + "d3-color": "1", + "d3-interpolate": "1" + } + }, + "d3-selection": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", + "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + }, + "d3-shape": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", + "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "requires": { + "d3-path": "1" + } + }, + "d3-time": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", + "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + }, + "d3-time-format": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", + "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "requires": { + "d3-time": "1" + } + }, + "d3-timer": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", + "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + }, + "d3-transition": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", + "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "requires": { + "d3-color": "1", + "d3-dispatch": "1", + "d3-ease": "1", + "d3-interpolate": "1", + "d3-selection": "^1.1.0", + "d3-timer": "1" + } + }, + "d3-voronoi": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", + "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" + }, + "d3-zoom": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", + "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "requires": { + "d3-dispatch": "1", + "d3-drag": "1", + "d3-interpolate": "1", + "d3-selection": "1", + "d3-transition": "1" + } + }, "damerau-levenshtein": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.7.tgz", @@ -22359,7 +22900,6 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", - "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -26092,6 +26632,11 @@ "queue-microtask": "^1.2.2" } }, + "rw": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/rw/-/rw-1.3.3.tgz", + "integrity": "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==" + }, "rxjs": { "version": "7.4.0", "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.4.0.tgz", @@ -26115,8 +26660,7 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" }, "sass": { "version": "1.49.9", diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 7a368116..26ff3bf1 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,264 +1,264 @@ import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; -import * as d3 from "d3" +import * as d3 from "d3"; import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; @Component({ - selector: 'graph-visualization', - encapsulation: ViewEncapsulation.None, - templateUrl: './graph-visualization.component.html', - styleUrls: ['./graph-visualization.component.scss'] + selector: 'graph-visualization', + encapsulation: ViewEncapsulation.None, + templateUrl: './graph-visualization.component.html', + styleUrls: ['./graph-visualization.component.scss'] }) export class GraphVisualizationComponent implements AfterContentInit, OnInit { - name: String; - svg: any; - link: any; - /** The displayed node name*/ - text: any; - /** The displayed link description */ - linkText: any; - /** The displayed node */ - node: any; - /** Loaded data: Connected modules with their capabilities and skills*/ - data: any; - /**Returns different colors automatically. The node-border color is the same for all node-group members*/ - color: any; -/**Simulation of the force directed graph */ - simulation: any; - - - margin = { top: 10, right: 30, bottom: 30, left: 40 }; - width = 100; - height = 100; -/**Defines the nodeborder thickness*/ - nodeborder = 8; - /**Defines the standard radius of a node*/ - rad = 20; - - index=0; - - constructor( - private route: ActivatedRoute, - private nodeCreatorService: NodeCreatorService - ) { - }; - - @ViewChild('g') svgContainer: ElementRef; - moduleName: string; - ngOnInit(): void {} - - - ngAfterContentInit(): void { - - - - this.route.params.subscribe(p => { - this.moduleName = p['moduleName']; - }) - this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service - - this.setSvg(); - this.setSimulation(); - } + name: string; + svg: any; + link: any; + /** The displayed node name*/ + text: any; + /** The displayed link description */ + linkText: any; + /** The displayed node */ + node: any; + /** Loaded data: Connected modules with their capabilities and skills*/ + data: any; + /**Returns different colors automatically. The node-border color is the same for all node-group members*/ + color: any; + /**Simulation of the force directed graph */ + simulation: any; + + + margin = { top: 10, right: 30, bottom: 30, left: 40 }; + width = 100; + height = 100; + /**Defines the nodeborder thickness*/ + nodeborder = 8; + /**Defines the standard radius of a node*/ + rad = 20; + + index=0; + + constructor( + private route: ActivatedRoute, + private nodeCreatorService: NodeCreatorService + ) {} + + @ViewChild('g') svgContainer: ElementRef; + moduleName: string; + ngOnInit(): void {} + + + ngAfterContentInit(): void { + + + + this.route.params.subscribe(p => { + this.moduleName = p['moduleName']; + }); + // this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service + this.data = this.nodeCreatorService.getAllNodes(); + + this.setSvg(); + this.setSimulation(); + } -/**Defines settings of the simulation */ -setSimulation(){ - this.simulation = d3.forceSimulation(this.data.nodes); - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links)) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)) //Zentrierung der Nodes - - this.refreshSimulation(); -} + /**Defines settings of the simulation */ + setSimulation(){ + this.simulation = d3.forceSimulation(this.data.nodes); + this.simulation + .force("link", d3.forceLink() + .id(function (d) { return d.id; }) + .distance(80) + .links(this.data.links)) + .force("collision", d3.forceCollide(40)) + .on("tick", this.ticked) // tick on every step of the simulation + .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction + .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)); //Zentrierung der Nodes + + this.refreshSimulation(); + } -/**Defines settings of the SVG and the color-scheme of the nodes */ - setSvg(){ + /**Defines settings of the SVG and the color-scheme of the nodes */ + setSvg(){ - this.svg = d3.select("#graph") // size definitions for the svg - .append("svg") - .attr("width", this.width + '%') - .attr("height", this.height + '%') - .append("g") - .attr("transform", - "translate(" + this.margin.left + "," + this.margin.top + ")"); + this.svg = d3.select("#graph") // size definitions for the svg + .append("svg") + .attr("width", this.width + '%') + .attr("height", this.height + '%') + .append("g") + .attr("transform", + "translate(" + this.margin.left + "," + this.margin.top + ")"); - this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours + this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours - this.svg.append('defs').append('marker') // marker/ arrow settings - .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions - .attr('refY', 0) - .attr('orient', 'auto') - .attr('markerWidth', 13) - .attr('markerHeight', 13) - .attr('xoverflow', 'visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') - .style('stroke', 'none');} + this.svg.append('defs').append('marker') // marker/ arrow settings + .attr("id", 'arrowhead') + .attr('viewBox', '-0 -5 10 10') //coordinate system + .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions + .attr('refY', 0) + .attr('orient', 'auto') + .attr('markerWidth', 13) + .attr('markerHeight', 13) + .attr('xoverflow', 'visible') + .append('svg:path') + .attr('d', 'M 0,-5 L 10 ,0 L 0,5') + .attr('fill', '#999') + .style('stroke', 'none');} - /** + /** * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ - refreshSimulation() { - this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id}) - var linkEnter=this.link.enter() //enter-selection - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link + refreshSimulation() { + this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id;}); + const linkEnter=this.link.enter() //enter-selection + .append("line").style("stroke", "#aaa") + .attr("class", "links") + .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link ; - linkEnter.append("title") - .text(function (d){return d.type}) - this.link = this.link.merge(linkEnter); // merge old elements with entered elements - this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id}) - - var nodeEnter= this.node.enter().append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8 } - else { return this.rad } - }) - nodeEnter.on('mouseover', this.mouseover); - nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", this.setNodeStyle) - .style("stroke-width", this.nodeborder) - .style("stroke", (d) => { return this.color(d.group); }) // set different node colours for each node-group - nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() // reactions when dragging a node - .on("start", this.dragstarted) - .on("drag", this.dragged) - .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); // merge old elements with entered elements - this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - - this.text = this.svg.selectAll("text").data(this.data.nodes) - var textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name}) - - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold" } - else { return "normal" } - }) - .text(function (d) { console.log(d.name); - return d.name }); // get text from data - this.text=this.text.merge(textEnter); - this.text.exit().remove(); - - this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type}) - var linkTextEnter=this.linkText.enter().append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type }); // get link text from data - this.linkText=this.linkText.merge(linkTextEnter); - this.linkText.exit().remove(); - - - this.simulation.nodes(this.data.nodes); // simualtion uses current data - this.simulation.force("link").links(this.data.links); - - } -/** + linkEnter.append("title") + .text(function (d){return d.type;}); + this.link = this.link.merge(linkEnter); // merge old elements with entered elements + this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + + this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id;}); + + const nodeEnter= this.node.enter().append("circle") + .attr("r", (d) => { + if (d.group == 1) { return this.rad + 8; } + else { return this.rad; } + }); + nodeEnter.on('mouseover', this.mouseover); + nodeEnter.on('mouseout', this.mouseout); + nodeEnter.style("fill", this.setNodeStyle) + .style("stroke-width", this.nodeborder) + .style("stroke", (d) => { return this.color(d.group); }); // set different node colours for each node-group + nodeEnter.on('dblclick', this.nodeDoubleClick); + nodeEnter.call(d3.drag() // reactions when dragging a node + .on("start", this.dragstarted) + .on("drag", this.dragged) + .on("end", this.dragended)); + this.node=this.node.merge(nodeEnter); // merge old elements with entered elements + this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + + + this.text = this.svg.selectAll("text").data(this.data.nodes); + const textEnter=this.text.enter().append("text") + .data(this.data.nodes, function (d){return d.name;}) + + .attr("font-weight", function (d) { + if (d.group == 1) { return "bold"; } + else { return "normal"; } + }) + .text(function (d) { console.log(d.name); + return d.name; }); // get text from data + this.text=this.text.merge(textEnter); + this.text.exit().remove(); + + this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type;}); + const linkTextEnter=this.linkText.enter().append("text") + .style("fill", "#999") + .attr("font-style", "italic") + .text(function (d) { return d.type; }); // get link text from data + this.linkText=this.linkText.merge(linkTextEnter); + this.linkText.exit().remove(); + + + this.simulation.nodes(this.data.nodes); // simualtion uses current data + this.simulation.force("link").links(this.data.links); + + } + /** * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element * @param d The position of every graph element (i.e. node, link...) */ ticked = () => { - const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg - const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - this.text - .attr("dx", (d) => { // restrictions for node text positions in the svg - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%' - }) - .attr("dy", (d) => { - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%' - }) - - this.linkText - .attr("dx", (d) => { // restrictions for link text positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; - }) - .attr("dy", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; - }) - - this.link - .attr("x1", (d) => { // restrictions for link positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX + '%'; - }) - .attr("y1", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY + '%'; - }) - .attr("x2", (d) => { - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX + '%'; - }) - .attr("y2", (d) => { - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY + '%'; - }) - - this.node - .attr("cx", (d) => { - d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX + '%'; - }) - .attr("cy", (d) => { - d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)) - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY + '%'; - }) + const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg + const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; + + this.text + .attr("dx", (d) => { // restrictions for node text positions in the svg + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%'; + }) + .attr("dy", (d) => { + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%'; + }); + + this.linkText + .attr("dx", (d) => { // restrictions for link text positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; + }) + .attr("dy", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; + return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; + }); + + this.link + .attr("x1", (d) => { // restrictions for link positions in the svg + d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative + const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; + return relativeSourceX + '%'; + }) + .attr("y1", (d) => { + d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; + return relativeSourceY + '%'; + }) + .attr("x2", (d) => { + d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; + return relativeTargetX + '%'; + }) + .attr("y2", (d) => { + d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; + return relativeTargetY + '%'; + }); + + this.node + .attr("cx", (d) => { + d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; + return relativeDragX + '%'; + }) + .attr("cy", (d) => { + d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); + const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; + const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; + return relativeDragY + '%'; + }); } -/** + /** * Function executed on a drag start */ dragstarted = (d) => { - if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; + if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; } /** @@ -266,63 +266,63 @@ setSimulation(){ * @param d The dragged node */ dragged(d) { - d.fx = d3.event.x; - d.fy = d3.event.y; + d.fx = d3.event.x; + d.fy = d3.event.y; } dragended(d) { } -/** + /** * Mouseover function, executed when mouse over node. The radius of the node will increase. * @param d node under the cursor */ - mouseover(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 12) - } - else { - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 6) - } + mouseover(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 12); + } + else { + d3.select(this).transition() + .duration('2') + .attr('r', 20 + 6); + } } -/** + /** * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. * @param d node wich is left by the cursor after mouseover */ - mouseout(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .attr('r', 20 + 8) - } - else { - d3.select(this).transition() - .attr('r', 20) - } + mouseout(d) { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node + d3.select(this).transition() + .attr('r', 20 + 8); + } + else { + d3.select(this).transition() + .attr('r', 20); + } } -/** + /** * Doubleclick function, executed on every doubleclick on a node *@param d The clicked node */ nodeDoubleClick=(d)=> { - this.index++; - const testNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(testNode); - this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }) - this.refreshSimulation(); + this.index++; + const testNode = { "name": "neighbor"+ this.index, "group": 8 }; + this.data.nodes.push(testNode); + this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }); + this.refreshSimulation(); } /** Returns the color of the inner circle of a node */ setNodeStyle(d){ - if (d.group == 1) { return "black" } - else { return "whitesmoke" } + if (d.group == 1) { return "black"; } + else { return "whitesmoke"; } } -/** Returns the node border color for each node-group */ - setNodeBorderStyle=(d)=>{ - return this.color(d.group); + /** Returns the node border color for each node-group */ + setNodeBorderStyle=(d)=>{ + return this.color(d.group); } -} \ No newline at end of file +} diff --git a/frontend/src/modules/graph-visualization/graph-visualization.module.ts b/frontend/src/modules/graph-visualization/graph-visualization.module.ts index 711024bc..17f69cd0 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.module.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.module.ts @@ -1,18 +1,18 @@ import { NgModule } from '@angular/core'; import { GraphVisualizationComponent } from './graph-visualization.component'; import {GraphVisualizationRouter} from './graph-visualization.routing'; -import { ModuleService } from 'app/shared/services/module.service'; +import { ModuleService } from '../../shared/services/module.service'; import { NodeCreatorService } from './node-creator.service'; @NgModule({ - imports: [ - GraphVisualizationRouter, - ], - declarations: [GraphVisualizationComponent], - providers: [ - ModuleService, - NodeCreatorService - - ] + imports: [ + GraphVisualizationRouter, + ], + declarations: [GraphVisualizationComponent], + providers: [ + ModuleService, + NodeCreatorService + + ] }) export class GraphVisualizationModule { } diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts index 50422f64..b3970c6a 100644 --- a/frontend/src/modules/graph-visualization/node-creator.service.ts +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -1,116 +1,119 @@ import { Injectable } from '@angular/core'; -import { ModuleService } from 'app/shared/services/module.service'; +import { lastValueFrom } from 'rxjs'; +import { ModuleService } from '../../shared/services/module.service'; @Injectable({ providedIn: 'root' }) export class NodeCreatorService { - apiRoot = "/api"; - constructor( - private moduleService: ModuleService - ) {} - getAllNodes() { - - const receivedData= this.moduleService.getAllModulesWithCapabilitiesAndSkills(); - const data= {nodes:[], links:[]}; - const idMap= new Map(); // Map for saving Node-IDs - let id=0; // ID start value - receivedData.forEach(receivedModule => { //loop over all modules - id++; - idMap.set(receivedModule, id); // assigns an ID for each module-node - data.nodes.push({ // adds a node for each module - "id" : idMap.get(receivedModule), - "name": receivedModule.name, - "group": 1 // assings node-groups (here: node colour) - }); - - receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module - id++; - idMap.set(capability, id); // assigns an ID for each capability-node - data.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.name, - "group": 2 - }); - data.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description - }); - - capability.hasInput.forEach(input=>{ - id++; - idMap.set(input, id); // assigns an ID for each input-node - data.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.name, - "group": 3 - }); - data.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" - }); - id++; - data.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.stateType, - "group": 6 - }); - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }); - }); - - capability.hasOutput.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - data.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.name, - "group": 4 - }); - data.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }); - id++; - data.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.stateType, - "group": 6 - }); - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }); - }); - - capability.executableViaSkill.forEach(skill=>{ - id++; - idMap.set(skill, id); // adds a node for each skill of current capability - data.nodes.push({ - "id" : idMap.get(skill), - "name": skill.name, - "group": 5 - }); - data.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" - }); - }); - - }); - - }); - - return data; - } + apiRoot = "/api"; + + constructor( + private moduleService: ModuleService + ) {} + + async getAllNodes() { + + const receivedData = await lastValueFrom(this.moduleService.getAllModules()); + const data= {nodes:[], links:[]}; + const idMap= new Map(); // Map for saving Node-IDs + let id=0; // ID start value + receivedData.forEach(receivedModule => { //loop over all modules + id++; + idMap.set(receivedModule, id); // assigns an ID for each module-node + data.nodes.push({ // adds a node for each module + "id" : idMap.get(receivedModule), + "name": receivedModule.getLocalName(), + "group": 1 // assings node-groups (here: node colour) + }); + + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + id++; + idMap.set(capability, id); // assigns an ID for each capability-node + data.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.getLocalName(), + "group": 2 + }); + data.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description + }); + + capability.inputs.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + data.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.getLocalName(), + "group": 3 + }); + data.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }); + id++; + data.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.type, + "group": 6 + }); + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }); + }); + + capability.outputs.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + data.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.getLocalName(), + "group": 4 + }); + data.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }); + id++; + data.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.type, + "group": 6 + }); + data.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }); + }); + + capability.skills.forEach(skill=>{ + id++; + idMap.set(skill, id); // adds a node for each skill of current capability + data.nodes.push({ + "id" : idMap.get(skill), + "name": skill.getLocalName(), + "group": 5 + }); + data.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }); + }); + + }); + + }); + + return data; + } } From 9aba47a6e96f8454be27cc39be628772544aee74 Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Mon, 22 Aug 2022 18:46:55 +0200 Subject: [PATCH 72/74] Finally fixed graph visualization --- .../routes/capabilities/capability.service.ts | 2 - frontend/package-lock.json | 1402 ++++++++++++----- frontend/package.json | 3 +- .../graph-visualization/D3GraphData.ts | 25 +- .../graph-visualization.component.html | 7 +- .../graph-visualization.component.scss | 4 + .../graph-visualization.component.ts | 503 +++--- .../node-creator.service.ts | 178 ++- 8 files changed, 1425 insertions(+), 699 deletions(-) diff --git a/backend/src/routes/capabilities/capability.service.ts b/backend/src/routes/capabilities/capability.service.ts index 7bee6fb8..12203b94 100644 --- a/backend/src/routes/capabilities/capability.service.ts +++ b/backend/src/routes/capabilities/capability.service.ts @@ -199,8 +199,6 @@ export class CapabilityService { } }`); const capabilities = converter.convertToDefinition(queryResult.results.bindings, capabilityMapping).getFirstRootElement() as CapabilityDto[]; - console.log("backend caps"); - console.log(capabilities); return capabilities; } catch (error) { diff --git a/frontend/package-lock.json b/frontend/package-lock.json index d578c761..50ae2e93 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -26,7 +26,7 @@ "chartjs-plugin-annotation": "^1.4.0", "chartjs-plugin-datalabels": "^2.0.0", "core-js": "2.6.5", - "d3": "^5.15.0", + "d3": "^7.4.0", "formidable": "^2.0.1", "ng2-charts": "3.0.11", "rxjs": "^7.4.0", @@ -38,6 +38,7 @@ "@angular/cli": "13.3.5", "@angular/compiler-cli": "13.3.8", "@angular/language-service": "13.3.8", + "@types/d3": "^7.4.0", "@types/jasmine": "~3.6.0", "@types/jasminewd2": "2.0.5", "@typescript-eslint/eslint-plugin": "^2.23.0", @@ -3381,6 +3382,259 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, + "node_modules/@types/d3": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz", + "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "node_modules/@types/d3-array": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz", + "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==", + "dev": true + }, + "node_modules/@types/d3-axis": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.1.tgz", + "integrity": "sha512-zji/iIbdd49g9WN0aIsGcwcTBUkgLsCSwB+uH+LPVDAiKWENMtI3cJEWt+7/YYwelMoZmbBfzA3qCdrZ2XFNnw==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-brush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.1.tgz", + "integrity": "sha512-B532DozsiTuQMHu2YChdZU0qsFJSio3Q6jmBYGYNp3gMDzBmuFFgPt9qKA4VYuLZMp4qc6eX7IUFUEsvHiXZAw==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-eQfcxIHrg7V++W8Qxn6QkqBNBokyhdWSAS73AbkbMzvLQmVVBviknoz2SRS/ZJdIOmhcmmdCRE/NFOm28Z1AMw==", + "dev": true + }, + "node_modules/@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==", + "dev": true + }, + "node_modules/@types/d3-contour": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.1.tgz", + "integrity": "sha512-C3zfBrhHZvrpAAK3YXqLWVAGo87A4SvJ83Q/zVJ8rFWJdKejUnDYaWZPkA8K84kb2vDA/g90LTQAz7etXcgoQQ==", + "dev": true, + "dependencies": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-delaunay": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", + "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==", + "dev": true + }, + "node_modules/@types/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-NhxMn3bAkqhjoxabVJWKryhnZXXYYVQxaBnbANu0O94+O/nX9qSjrA1P1jbAQJxJf+VC72TxDX/YJcKue5bRqw==", + "dev": true + }, + "node_modules/@types/d3-drag": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.1.tgz", + "integrity": "sha512-o1Va7bLwwk6h03+nSM8dpaGEYnoIG19P0lKqlic8Un36ymh9NSkNFX1yiXMKNMx8rJ0Kfnn2eovuFaL6Jvj0zA==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-dsv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.0.tgz", + "integrity": "sha512-o0/7RlMl9p5n6FQDptuJVMxDf/7EDEv2SYEO/CwdG2tr1hTfUVi0Iavkk2ax+VpaQ/1jVhpnj5rq1nj8vwhn2A==", + "dev": true + }, + "node_modules/@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==", + "dev": true + }, + "node_modules/@types/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-toZJNOwrOIqz7Oh6Q7l2zkaNfXkfR7mFSJvGvlD/Ciq/+SQ39d5gynHJZ/0fjt83ec3WL7+u3ssqIijQtBISsw==", + "dev": true, + "dependencies": { + "@types/d3-dsv": "*" + } + }, + "node_modules/@types/d3-force": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.3.tgz", + "integrity": "sha512-z8GteGVfkWJMKsx6hwC3SiTSLspL98VNpmvLpEFJQpZPq6xpA1I8HNBDNSpukfK0Vb0l64zGFhzunLgEAcBWSA==", + "dev": true + }, + "node_modules/@types/d3-format": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", + "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==", + "dev": true + }, + "node_modules/@types/d3-geo": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.2.tgz", + "integrity": "sha512-DbqK7MLYA8LpyHQfv6Klz0426bQEf7bRTvhMy44sNGVyZoWn//B0c+Qbeg8Osi2Obdc9BLLXYAKpyWege2/7LQ==", + "dev": true, + "dependencies": { + "@types/geojson": "*" + } + }, + "node_modules/@types/d3-hierarchy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.0.tgz", + "integrity": "sha512-g+sey7qrCa3UbsQlMZZBOHROkFqx7KZKvUpRzI/tAp/8erZWpYq7FgNKvYwebi2LaEiVs1klhUfd3WCThxmmWQ==", + "dev": true + }, + "node_modules/@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "dev": true, + "dependencies": { + "@types/d3-color": "*" + } + }, + "node_modules/@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==", + "dev": true + }, + "node_modules/@types/d3-polygon": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz", + "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==", + "dev": true + }, + "node_modules/@types/d3-quadtree": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz", + "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==", + "dev": true + }, + "node_modules/@types/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==", + "dev": true + }, + "node_modules/@types/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==", + "dev": true, + "dependencies": { + "@types/d3-time": "*" + } + }, + "node_modules/@types/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==", + "dev": true + }, + "node_modules/@types/d3-selection": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.3.tgz", + "integrity": "sha512-Mw5cf6nlW1MlefpD9zrshZ+DAWL4IQ5LnWfRheW6xwsdaWOb6IRRu2H7XPAQcyXEx1D7XQWgdoKR83ui1/HlEA==", + "dev": true + }, + "node_modules/@types/d3-shape": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==", + "dev": true, + "dependencies": { + "@types/d3-path": "*" + } + }, + "node_modules/@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==", + "dev": true + }, + "node_modules/@types/d3-time-format": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz", + "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==", + "dev": true + }, + "node_modules/@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==", + "dev": true + }, + "node_modules/@types/d3-transition": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.2.tgz", + "integrity": "sha512-jo5o/Rf+/u6uerJ/963Dc39NI16FQzqwOc54bwvksGAdVfvDrqDpVeq95bEvPtBwLCVZutAEyAtmSyEMxN7vxQ==", + "dev": true, + "dependencies": { + "@types/d3-selection": "*" + } + }, + "node_modules/@types/d3-zoom": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.1.tgz", + "integrity": "sha512-7s5L9TjfqIYQmQQEUcpMAcBOahem7TRoSO/+Gkz02GbMVuULiZzjF2BOdw291dbO2aNon4m2OdFsRGaCq2caLQ==", + "dev": true, + "dependencies": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "node_modules/@types/eslint": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", @@ -3436,6 +3690,12 @@ "@types/range-parser": "*" } }, + "node_modules/@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==", + "dev": true + }, "node_modules/@types/http-proxy": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", @@ -5323,7 +5583,8 @@ "node_modules/commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "node_modules/commondir": { "version": "1.0.1", @@ -5934,278 +6195,392 @@ "dev": true }, "node_modules/d3": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", - "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==", - "dependencies": { - "d3-array": "1", - "d3-axis": "1", - "d3-brush": "1", - "d3-chord": "1", - "d3-collection": "1", - "d3-color": "1", - "d3-contour": "1", - "d3-dispatch": "1", - "d3-drag": "1", - "d3-dsv": "1", - "d3-ease": "1", - "d3-fetch": "1", - "d3-force": "1", - "d3-format": "1", - "d3-geo": "1", - "d3-hierarchy": "1", - "d3-interpolate": "1", - "d3-path": "1", - "d3-polygon": "1", - "d3-quadtree": "1", - "d3-random": "1", - "d3-scale": "2", - "d3-scale-chromatic": "1", - "d3-selection": "1", - "d3-shape": "1", - "d3-time": "1", - "d3-time-format": "2", - "d3-timer": "1", - "d3-transition": "1", - "d3-voronoi": "1", - "d3-zoom": "1" + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", + "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==", + "dependencies": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-array": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", + "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", + "dependencies": { + "internmap": "1 - 2" + }, + "engines": { + "node": ">=12" + } }, "node_modules/d3-axis": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", - "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-brush": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", - "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", "dependencies": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-chord": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", - "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", "dependencies": { - "d3-array": "1", - "d3-path": "1" + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" } }, - "node_modules/d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" - }, "node_modules/d3-color": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", - "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-contour": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", - "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", + "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", "dependencies": { - "d3-array": "^1.1.1" + "d3-array": "^3.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "dependencies": { + "delaunator": "5" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-dispatch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", - "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-drag": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", - "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "dependencies": { - "d3-dispatch": "1", - "d3-selection": "1" + "d3-dispatch": "1 - 3", + "d3-selection": "3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-dsv": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", - "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", "dependencies": { - "commander": "2", - "iconv-lite": "0.4", + "commander": "7", + "iconv-lite": "0.6", "rw": "1" }, "bin": { - "csv2json": "bin/dsv2json", - "csv2tsv": "bin/dsv2dsv", - "dsv2dsv": "bin/dsv2dsv", - "dsv2json": "bin/dsv2json", - "json2csv": "bin/json2dsv", - "json2dsv": "bin/json2dsv", - "json2tsv": "bin/json2dsv", - "tsv2csv": "bin/dsv2dsv", - "tsv2json": "bin/dsv2json" + "csv2json": "bin/dsv2json.js", + "csv2tsv": "bin/dsv2dsv.js", + "dsv2dsv": "bin/dsv2dsv.js", + "dsv2json": "bin/dsv2json.js", + "json2csv": "bin/json2dsv.js", + "json2dsv": "bin/json2dsv.js", + "json2tsv": "bin/json2dsv.js", + "tsv2csv": "bin/dsv2dsv.js", + "tsv2json": "bin/dsv2json.js" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/d3-dsv/node_modules/commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==", + "engines": { + "node": ">= 10" + } + }, + "node_modules/d3-dsv/node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" } }, "node_modules/d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-fetch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", - "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", "dependencies": { - "d3-dsv": "1" + "d3-dsv": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-force": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", - "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", "dependencies": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-geo": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", - "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", + "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", "dependencies": { - "d3-array": "1" + "d3-array": "2.5.0 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-hierarchy": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", - "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "dependencies": { - "d3-color": "1" + "d3-color": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", + "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-polygon": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", - "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-quadtree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", - "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-random": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", - "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-scale": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", - "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "dependencies": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-scale-chromatic": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", - "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", "dependencies": { - "d3-color": "1", - "d3-interpolate": "1" + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-selection": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", - "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", "dependencies": { - "d3-path": "1" + "d3-path": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "dependencies": { + "d3-array": "2 - 3" + }, + "engines": { + "node": ">=12" + } }, "node_modules/d3-time-format": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", - "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "dependencies": { - "d3-time": "1" + "d3-time": "1 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/d3-timer": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", - "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==", + "engines": { + "node": ">=12" + } }, "node_modules/d3-transition": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", - "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", "dependencies": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" + }, + "engines": { + "node": ">=12" + }, + "peerDependencies": { + "d3-selection": "2 - 3" } }, - "node_modules/d3-voronoi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", - "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" - }, "node_modules/d3-zoom": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", - "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", "dependencies": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" + }, + "engines": { + "node": ">=12" } }, "node_modules/damerau-levenshtein": { @@ -6420,6 +6795,14 @@ "node": ">=8" } }, + "node_modules/delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "dependencies": { + "robust-predicates": "^3.0.0" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -8888,6 +9271,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3" }, @@ -9141,6 +9525,14 @@ "node": ">=8" } }, + "node_modules/internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==", + "engines": { + "node": ">=12" + } + }, "node_modules/ip": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", @@ -13862,6 +14254,11 @@ "rimraf": "bin.js" } }, + "node_modules/robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, "node_modules/run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", @@ -18680,6 +19077,259 @@ "integrity": "sha512-vt+kDhq/M2ayberEtJcIN/hxXy1Pk+59g2FV/ZQceeaTyCtCucjL2Q7FXlFjtWn4n15KCr1NE2lNNFhp0lEThw==", "dev": true }, + "@types/d3": { + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/@types/d3/-/d3-7.4.0.tgz", + "integrity": "sha512-jIfNVK0ZlxcuRDKtRS/SypEyOQ6UHaFQBKv032X45VvxSJ6Yi5G9behy9h6tNTHTDGh5Vq+KbmBjUWLgY4meCA==", + "dev": true, + "requires": { + "@types/d3-array": "*", + "@types/d3-axis": "*", + "@types/d3-brush": "*", + "@types/d3-chord": "*", + "@types/d3-color": "*", + "@types/d3-contour": "*", + "@types/d3-delaunay": "*", + "@types/d3-dispatch": "*", + "@types/d3-drag": "*", + "@types/d3-dsv": "*", + "@types/d3-ease": "*", + "@types/d3-fetch": "*", + "@types/d3-force": "*", + "@types/d3-format": "*", + "@types/d3-geo": "*", + "@types/d3-hierarchy": "*", + "@types/d3-interpolate": "*", + "@types/d3-path": "*", + "@types/d3-polygon": "*", + "@types/d3-quadtree": "*", + "@types/d3-random": "*", + "@types/d3-scale": "*", + "@types/d3-scale-chromatic": "*", + "@types/d3-selection": "*", + "@types/d3-shape": "*", + "@types/d3-time": "*", + "@types/d3-time-format": "*", + "@types/d3-timer": "*", + "@types/d3-transition": "*", + "@types/d3-zoom": "*" + } + }, + "@types/d3-array": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.0.3.tgz", + "integrity": "sha512-Reoy+pKnvsksN0lQUlcH6dOGjRZ/3WRwXR//m+/8lt1BXeI4xyaUZoqULNjyXXRuh0Mj4LNpkCvhUpQlY3X5xQ==", + "dev": true + }, + "@types/d3-axis": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-axis/-/d3-axis-3.0.1.tgz", + "integrity": "sha512-zji/iIbdd49g9WN0aIsGcwcTBUkgLsCSwB+uH+LPVDAiKWENMtI3cJEWt+7/YYwelMoZmbBfzA3qCdrZ2XFNnw==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-brush": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-brush/-/d3-brush-3.0.1.tgz", + "integrity": "sha512-B532DozsiTuQMHu2YChdZU0qsFJSio3Q6jmBYGYNp3gMDzBmuFFgPt9qKA4VYuLZMp4qc6eX7IUFUEsvHiXZAw==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-chord": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-eQfcxIHrg7V++W8Qxn6QkqBNBokyhdWSAS73AbkbMzvLQmVVBviknoz2SRS/ZJdIOmhcmmdCRE/NFOm28Z1AMw==", + "dev": true + }, + "@types/d3-color": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-HKuicPHJuvPgCD+np6Se9MQvS6OCbJmOjGvylzMJRlDwUXjKTTXs6Pwgk79O09Vj/ho3u1ofXnhFOaEWWPrlwA==", + "dev": true + }, + "@types/d3-contour": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-contour/-/d3-contour-3.0.1.tgz", + "integrity": "sha512-C3zfBrhHZvrpAAK3YXqLWVAGo87A4SvJ83Q/zVJ8rFWJdKejUnDYaWZPkA8K84kb2vDA/g90LTQAz7etXcgoQQ==", + "dev": true, + "requires": { + "@types/d3-array": "*", + "@types/geojson": "*" + } + }, + "@types/d3-delaunay": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-delaunay/-/d3-delaunay-6.0.1.tgz", + "integrity": "sha512-tLxQ2sfT0p6sxdG75c6f/ekqxjyYR0+LwPrsO1mbC9YDBzPJhs2HbJJRrn8Ez1DBoHRo2yx7YEATI+8V1nGMnQ==", + "dev": true + }, + "@types/d3-dispatch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-NhxMn3bAkqhjoxabVJWKryhnZXXYYVQxaBnbANu0O94+O/nX9qSjrA1P1jbAQJxJf+VC72TxDX/YJcKue5bRqw==", + "dev": true + }, + "@types/d3-drag": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-drag/-/d3-drag-3.0.1.tgz", + "integrity": "sha512-o1Va7bLwwk6h03+nSM8dpaGEYnoIG19P0lKqlic8Un36ymh9NSkNFX1yiXMKNMx8rJ0Kfnn2eovuFaL6Jvj0zA==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-dsv": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-dsv/-/d3-dsv-3.0.0.tgz", + "integrity": "sha512-o0/7RlMl9p5n6FQDptuJVMxDf/7EDEv2SYEO/CwdG2tr1hTfUVi0Iavkk2ax+VpaQ/1jVhpnj5rq1nj8vwhn2A==", + "dev": true + }, + "@types/d3-ease": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.0.tgz", + "integrity": "sha512-aMo4eaAOijJjA6uU+GIeW018dvy9+oH5Y2VPPzjjfxevvGQ/oRDs+tfYC9b50Q4BygRR8yE2QCLsrT0WtAVseA==", + "dev": true + }, + "@types/d3-fetch": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-toZJNOwrOIqz7Oh6Q7l2zkaNfXkfR7mFSJvGvlD/Ciq/+SQ39d5gynHJZ/0fjt83ec3WL7+u3ssqIijQtBISsw==", + "dev": true, + "requires": { + "@types/d3-dsv": "*" + } + }, + "@types/d3-force": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-force/-/d3-force-3.0.3.tgz", + "integrity": "sha512-z8GteGVfkWJMKsx6hwC3SiTSLspL98VNpmvLpEFJQpZPq6xpA1I8HNBDNSpukfK0Vb0l64zGFhzunLgEAcBWSA==", + "dev": true + }, + "@types/d3-format": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-format/-/d3-format-3.0.1.tgz", + "integrity": "sha512-5KY70ifCCzorkLuIkDe0Z9YTf9RR2CjBX1iaJG+rgM/cPP+sO+q9YdQ9WdhQcgPj1EQiJ2/0+yUkkziTG6Lubg==", + "dev": true + }, + "@types/d3-geo": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-geo/-/d3-geo-3.0.2.tgz", + "integrity": "sha512-DbqK7MLYA8LpyHQfv6Klz0426bQEf7bRTvhMy44sNGVyZoWn//B0c+Qbeg8Osi2Obdc9BLLXYAKpyWege2/7LQ==", + "dev": true, + "requires": { + "@types/geojson": "*" + } + }, + "@types/d3-hierarchy": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-hierarchy/-/d3-hierarchy-3.1.0.tgz", + "integrity": "sha512-g+sey7qrCa3UbsQlMZZBOHROkFqx7KZKvUpRzI/tAp/8erZWpYq7FgNKvYwebi2LaEiVs1klhUfd3WCThxmmWQ==", + "dev": true + }, + "@types/d3-interpolate": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-jx5leotSeac3jr0RePOH1KdR9rISG91QIE4Q2PYTu4OymLTZfA3SrnURSLzKH48HmXVUru50b8nje4E79oQSQw==", + "dev": true, + "requires": { + "@types/d3-color": "*" + } + }, + "@types/d3-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.0.0.tgz", + "integrity": "sha512-0g/A+mZXgFkQxN3HniRDbXMN79K3CdTpLsevj+PXiTcb2hVyvkZUBg37StmgCQkaD84cUJ4uaDAWq7UJOQy2Tg==", + "dev": true + }, + "@types/d3-polygon": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-polygon/-/d3-polygon-3.0.0.tgz", + "integrity": "sha512-D49z4DyzTKXM0sGKVqiTDTYr+DHg/uxsiWDAkNrwXYuiZVd9o9wXZIo+YsHkifOiyBkmSWlEngHCQme54/hnHw==", + "dev": true + }, + "@types/d3-quadtree": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-quadtree/-/d3-quadtree-3.0.2.tgz", + "integrity": "sha512-QNcK8Jguvc8lU+4OfeNx+qnVy7c0VrDJ+CCVFS9srBo2GL9Y18CnIxBdTF3v38flrGy5s1YggcoAiu6s4fLQIw==", + "dev": true + }, + "@types/d3-random": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-IIE6YTekGczpLYo/HehAy3JGF1ty7+usI97LqraNa8IiDur+L44d0VOjAvFQWJVdZOJHukUJw+ZdZBlgeUsHOQ==", + "dev": true + }, + "@types/d3-scale": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-Yk4htunhPAwN0XGlIwArRomOjdoBFXC3+kCxK2Ubg7I9shQlVSJy/pG/Ht5ASN+gdMIalpk8TJ5xV74jFsetLA==", + "dev": true, + "requires": { + "@types/d3-time": "*" + } + }, + "@types/d3-scale-chromatic": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-dsoJGEIShosKVRBZB0Vo3C8nqSDqVGujJU6tPznsBJxNJNwMF8utmS83nvCBKQYPpjCzaaHcrf66iTRpZosLPw==", + "dev": true + }, + "@types/d3-selection": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/d3-selection/-/d3-selection-3.0.3.tgz", + "integrity": "sha512-Mw5cf6nlW1MlefpD9zrshZ+DAWL4IQ5LnWfRheW6xwsdaWOb6IRRu2H7XPAQcyXEx1D7XQWgdoKR83ui1/HlEA==", + "dev": true + }, + "@types/d3-shape": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-jYIYxFFA9vrJ8Hd4Se83YI6XF+gzDL1aC5DCsldai4XYYiVNdhtpGbA/GM6iyQ8ayhSp3a148LY34hy7A4TxZA==", + "dev": true, + "requires": { + "@types/d3-path": "*" + } + }, + "@types/d3-time": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-sZLCdHvBUcNby1cB6Fd3ZBrABbjz3v1Vm90nysCQ6Vt7vd6e/h9Lt7SiJUoEX0l4Dzc7P5llKyhqSi1ycSf1Hg==", + "dev": true + }, + "@types/d3-time-format": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-time-format/-/d3-time-format-4.0.0.tgz", + "integrity": "sha512-yjfBUe6DJBsDin2BMIulhSHmr5qNR5Pxs17+oW4DoVPyVIXZ+m6bs7j1UVKP08Emv6jRmYrYqxYzO63mQxy1rw==", + "dev": true + }, + "@types/d3-timer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.0.tgz", + "integrity": "sha512-HNB/9GHqu7Fo8AQiugyJbv6ZxYz58wef0esl4Mv828w1ZKpAshw/uFWVDUcIB9KKFeFKoxS3cHY07FFgtTRZ1g==", + "dev": true + }, + "@types/d3-transition": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@types/d3-transition/-/d3-transition-3.0.2.tgz", + "integrity": "sha512-jo5o/Rf+/u6uerJ/963Dc39NI16FQzqwOc54bwvksGAdVfvDrqDpVeq95bEvPtBwLCVZutAEyAtmSyEMxN7vxQ==", + "dev": true, + "requires": { + "@types/d3-selection": "*" + } + }, + "@types/d3-zoom": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@types/d3-zoom/-/d3-zoom-3.0.1.tgz", + "integrity": "sha512-7s5L9TjfqIYQmQQEUcpMAcBOahem7TRoSO/+Gkz02GbMVuULiZzjF2BOdw291dbO2aNon4m2OdFsRGaCq2caLQ==", + "dev": true, + "requires": { + "@types/d3-interpolate": "*", + "@types/d3-selection": "*" + } + }, "@types/eslint": { "version": "8.4.2", "resolved": "https://registry.npmjs.org/@types/eslint/-/eslint-8.4.2.tgz", @@ -18735,6 +19385,12 @@ "@types/range-parser": "*" } }, + "@types/geojson": { + "version": "7946.0.10", + "resolved": "https://registry.npmjs.org/@types/geojson/-/geojson-7946.0.10.tgz", + "integrity": "sha512-Nmh0K3iWQJzniTuPRcJn5hxXkfB1T1pgB89SBig5PlJQU5yocazeu4jATJlaA0GYFKWMqDdvYemoSnF2pXgLVA==", + "dev": true + }, "@types/http-proxy": { "version": "1.17.9", "resolved": "https://registry.npmjs.org/@types/http-proxy/-/http-proxy-1.17.9.tgz", @@ -20205,7 +20861,8 @@ "commander": { "version": "2.20.3", "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true }, "commondir": { "version": "1.0.1", @@ -20669,267 +21326,281 @@ "dev": true }, "d3": { - "version": "5.16.0", - "resolved": "https://registry.npmjs.org/d3/-/d3-5.16.0.tgz", - "integrity": "sha512-4PL5hHaHwX4m7Zr1UapXW23apo6pexCgdetdJ5kTmADpG/7T9Gkxw0M0tf/pjoB63ezCCm0u5UaFYy2aMt0Mcw==", - "requires": { - "d3-array": "1", - "d3-axis": "1", - "d3-brush": "1", - "d3-chord": "1", - "d3-collection": "1", - "d3-color": "1", - "d3-contour": "1", - "d3-dispatch": "1", - "d3-drag": "1", - "d3-dsv": "1", - "d3-ease": "1", - "d3-fetch": "1", - "d3-force": "1", - "d3-format": "1", - "d3-geo": "1", - "d3-hierarchy": "1", - "d3-interpolate": "1", - "d3-path": "1", - "d3-polygon": "1", - "d3-quadtree": "1", - "d3-random": "1", - "d3-scale": "2", - "d3-scale-chromatic": "1", - "d3-selection": "1", - "d3-shape": "1", - "d3-time": "1", - "d3-time-format": "2", - "d3-timer": "1", - "d3-transition": "1", - "d3-voronoi": "1", - "d3-zoom": "1" + "version": "7.6.1", + "resolved": "https://registry.npmjs.org/d3/-/d3-7.6.1.tgz", + "integrity": "sha512-txMTdIHFbcpLx+8a0IFhZsbp+PfBBPt8yfbmukZTQFroKuFqIwqswF0qE5JXWefylaAVpSXFoKm3yP+jpNLFLw==", + "requires": { + "d3-array": "3", + "d3-axis": "3", + "d3-brush": "3", + "d3-chord": "3", + "d3-color": "3", + "d3-contour": "4", + "d3-delaunay": "6", + "d3-dispatch": "3", + "d3-drag": "3", + "d3-dsv": "3", + "d3-ease": "3", + "d3-fetch": "3", + "d3-force": "3", + "d3-format": "3", + "d3-geo": "3", + "d3-hierarchy": "3", + "d3-interpolate": "3", + "d3-path": "3", + "d3-polygon": "3", + "d3-quadtree": "3", + "d3-random": "3", + "d3-scale": "4", + "d3-scale-chromatic": "3", + "d3-selection": "3", + "d3-shape": "3", + "d3-time": "3", + "d3-time-format": "4", + "d3-timer": "3", + "d3-transition": "3", + "d3-zoom": "3" } }, "d3-array": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", - "integrity": "sha512-KHW6M86R+FUPYGb3R5XiYjXPq7VzwxZ22buHhAEVG5ztoEcZZMLov530mmccaqA1GghZArjQV46fuc8kUqhhHw==" + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.0.tgz", + "integrity": "sha512-3yXFQo0oG3QCxbF06rMPFyGRMGJNS7NvsV1+2joOjbBE+9xvWQ8+GcMJAjRCzw06zQ3/arXeJgbPYcjUCuC+3g==", + "requires": { + "internmap": "1 - 2" + } }, "d3-axis": { - "version": "1.0.12", - "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-1.0.12.tgz", - "integrity": "sha512-ejINPfPSNdGFKEOAtnBtdkpr24c4d4jsei6Lg98mxf424ivoDP2956/5HDpIAtmHo85lqT4pruy+zEgvRUBqaQ==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-axis/-/d3-axis-3.0.0.tgz", + "integrity": "sha512-IH5tgjV4jE/GhHkRV0HiVYPDtvfjHQlQfJHs0usq7M30XcSBvOotpmH1IgkcXsO/5gEQZD43B//fc7SRT5S+xw==" }, "d3-brush": { - "version": "1.1.6", - "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-1.1.6.tgz", - "integrity": "sha512-7RW+w7HfMCPyZLifTz/UnJmI5kdkXtpCbombUSs8xniAyo0vIbrDzDwUJB6eJOgl9u5DQOt2TQlYumxzD1SvYA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-brush/-/d3-brush-3.0.0.tgz", + "integrity": "sha512-ALnjWlVYkXsVIGlOsuWH1+3udkYFI48Ljihfnh8FZPF2QS9o+PzGLBslO0PjzVoHLZ2KCVgAM8NVkXPJB2aNnQ==", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "3", + "d3-transition": "3" } }, "d3-chord": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-1.0.6.tgz", - "integrity": "sha512-JXA2Dro1Fxw9rJe33Uv+Ckr5IrAa74TlfDEhE/jfLOaXegMQFQTAgAw9WnZL8+HxVBRXaRGCkrNU7pJeylRIuA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-chord/-/d3-chord-3.0.1.tgz", + "integrity": "sha512-VE5S6TNa+j8msksl7HwjxMHDM2yNK3XCkusIlpX5kwauBfXuyLAtNg9jCp/iHH61tgI4sb6R/EIMWCqEIdjT/g==", "requires": { - "d3-array": "1", - "d3-path": "1" + "d3-path": "1 - 3" } }, - "d3-collection": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-collection/-/d3-collection-1.0.7.tgz", - "integrity": "sha512-ii0/r5f4sjKNTfh84Di+DpztYwqKhEyUlKoPrzUFfeSkWxjW49xU2QzO9qrPrNkpdI0XJkfzvmTu8V2Zylln6A==" - }, "d3-color": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-1.4.1.tgz", - "integrity": "sha512-p2sTHSLCJI2QKunbGb7ocOh7DgTAn8IrLx21QRc/BSnodXM4sv6aLQlnfpvehFMLZEfBc6g9pH9SWQccFYfJ9Q==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz", + "integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==" }, "d3-contour": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-1.3.2.tgz", - "integrity": "sha512-hoPp4K/rJCu0ladiH6zmJUEz6+u3lgR+GSm/QdM2BBvDraU39Vr7YdDCicJcxP1z8i9B/2dJLgDC1NcvlF8WCg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/d3-contour/-/d3-contour-4.0.0.tgz", + "integrity": "sha512-7aQo0QHUTu/Ko3cP9YK9yUTxtoDEiDGwnBHyLxG5M4vqlBkO/uixMRele3nfsfj6UXOcuReVpVXzAboGraYIJw==", "requires": { - "d3-array": "^1.1.1" + "d3-array": "^3.2.0" + } + }, + "d3-delaunay": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/d3-delaunay/-/d3-delaunay-6.0.2.tgz", + "integrity": "sha512-IMLNldruDQScrcfT+MWnazhHbDJhcRJyOEBAJfwQnHle1RPh6WDuLvxNArUju2VSMSUuKlY5BGHRJ2cYyoFLQQ==", + "requires": { + "delaunator": "5" } }, "d3-dispatch": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-1.0.6.tgz", - "integrity": "sha512-fVjoElzjhCEy+Hbn8KygnmMS7Or0a9sI2UzGwoB7cCtvI1XpVN9GpoYlnb3xt2YV66oXYb1fLJ8GMvP4hdU1RA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dispatch/-/d3-dispatch-3.0.1.tgz", + "integrity": "sha512-rzUyPU/S7rwUflMyLc1ETDeBj0NRuHKKAcvukozwhshr6g6c5d8zh4c2gQjY2bZ0dXeGLWc1PF174P2tVvKhfg==" }, "d3-drag": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-1.2.5.tgz", - "integrity": "sha512-rD1ohlkKQwMZYkQlYVCrSFxsWPzI97+W+PaEIBNTMxRuxz9RF0Hi5nJWHGVJ3Om9d2fRTe1yOBINJyy/ahV95w==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-drag/-/d3-drag-3.0.0.tgz", + "integrity": "sha512-pWbUJLdETVA8lQNJecMxoXfH6x+mO2UQo8rSmZ+QqxcbyA3hfeprFgIT//HW2nlHChWeIIMwS2Fq+gEARkhTkg==", "requires": { - "d3-dispatch": "1", - "d3-selection": "1" + "d3-dispatch": "1 - 3", + "d3-selection": "3" } }, "d3-dsv": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-1.2.0.tgz", - "integrity": "sha512-9yVlqvZcSOMhCYzniHE7EVUws7Fa1zgw+/EAV2BxJoG3ME19V6BQFBwI855XQDsxyOuG7NibqRMTtiF/Qup46g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-dsv/-/d3-dsv-3.0.1.tgz", + "integrity": "sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==", "requires": { - "commander": "2", - "iconv-lite": "0.4", + "commander": "7", + "iconv-lite": "0.6", "rw": "1" + }, + "dependencies": { + "commander": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-7.2.0.tgz", + "integrity": "sha512-QrWXB+ZQSVPmIWIhtEO9H+gwHaMGYiF5ChvoJ+K9ZGHG/sVsa6yiesAD1GC/x46sET00Xlwo1u49RVVVzvcSkw==" + }, + "iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } } }, "d3-ease": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-1.0.7.tgz", - "integrity": "sha512-lx14ZPYkhNx0s/2HX5sLFUI3mbasHjSSpwO/KaaNACweVwxUruKyWVcb293wMv1RqTPZyZ8kSZ2NogUZNcLOFQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz", + "integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==" }, "d3-fetch": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-1.2.0.tgz", - "integrity": "sha512-yC78NBVcd2zFAyR/HnUiBS7Lf6inSCoWcSxFfw8FYL7ydiqe80SazNwoffcqOfs95XaLo7yebsmQqDKSsXUtvA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-fetch/-/d3-fetch-3.0.1.tgz", + "integrity": "sha512-kpkQIM20n3oLVBKGg6oHrUchHM3xODkTzjMoj7aWQFq5QEM+R6E4WkzT5+tojDY7yjez8KgCBRoj4aEr99Fdqw==", "requires": { - "d3-dsv": "1" + "d3-dsv": "1 - 3" } }, "d3-force": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-1.2.1.tgz", - "integrity": "sha512-HHvehyaiUlVo5CxBJ0yF/xny4xoaxFxDnBXNvNcfW9adORGZfyNF1dj6DGLKyk4Yh3brP/1h3rnDzdIAwL08zg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-force/-/d3-force-3.0.0.tgz", + "integrity": "sha512-zxV/SsA+U4yte8051P4ECydjD/S+qeYtnaIyAs9tgHCqfguma/aAQDjo85A9Z6EKhBirHRJHXIgJUlffT4wdLg==", "requires": { - "d3-collection": "1", - "d3-dispatch": "1", - "d3-quadtree": "1", - "d3-timer": "1" + "d3-dispatch": "1 - 3", + "d3-quadtree": "1 - 3", + "d3-timer": "1 - 3" } }, "d3-format": { - "version": "1.4.5", - "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-1.4.5.tgz", - "integrity": "sha512-J0piedu6Z8iB6TbIGfZgDzfXxUFN3qQRMofy2oPdXzQibYGqPB/9iMcxr/TGalU+2RsyDO+U4f33id8tbnSRMQ==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.0.tgz", + "integrity": "sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==" }, "d3-geo": { - "version": "1.12.1", - "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-1.12.1.tgz", - "integrity": "sha512-XG4d1c/UJSEX9NfU02KwBL6BYPj8YKHxgBEw5om2ZnTRSbIcego6dhHwcxuSR3clxh0EpE38os1DVPOmnYtTPg==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-geo/-/d3-geo-3.0.1.tgz", + "integrity": "sha512-Wt23xBych5tSy9IYAM1FR2rWIBFWa52B/oF/GYe5zbdHrg08FU8+BuI6X4PvTwPDdqdAdq04fuWJpELtsaEjeA==", "requires": { - "d3-array": "1" + "d3-array": "2.5.0 - 3" } }, "d3-hierarchy": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-1.1.9.tgz", - "integrity": "sha512-j8tPxlqh1srJHAtxfvOUwKNYJkQuBFdM1+JAUfq6xqH5eAqf93L7oG1NVqDa4CpFZNvnNKtCYEUC8KY9yEn9lQ==" + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/d3-hierarchy/-/d3-hierarchy-3.1.2.tgz", + "integrity": "sha512-FX/9frcub54beBdugHjDCdikxThEqjnR93Qt7PvQTOHxyiNCAlvMrHhclk3cD5VeAaq9fxmfRp+CnWw9rEMBuA==" }, "d3-interpolate": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-1.4.0.tgz", - "integrity": "sha512-V9znK0zc3jOPV4VD2zZn0sDhZU3WAE2bmlxdIwwQPPzPjvyLkd8B3JUVdS1IDUFDkWZ72c9qnv1GK2ZagTZ8EA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz", + "integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==", "requires": { - "d3-color": "1" + "d3-color": "1 - 3" } }, "d3-path": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-1.0.9.tgz", - "integrity": "sha512-VLaYcn81dtHVTjEHd8B+pbe9yHWpXKZUC87PzoFmsFrJqgFwDe/qxfp5MlfsfM1V5E/iVt0MmEbWQ7FVIXh/bg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.0.1.tgz", + "integrity": "sha512-gq6gZom9AFZby0YLduxT1qmrp4xpBA1YZr19OI717WIdKE2OM5ETq5qrHLb301IgxhLwcuxvGZVLeeWc/k1I6w==" }, "d3-polygon": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-1.0.6.tgz", - "integrity": "sha512-k+RF7WvI08PC8reEoXa/w2nSg5AUMTi+peBD9cmFc+0ixHfbs4QmxxkarVal1IkVkgxVuk9JSHhJURHiyHKAuQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-polygon/-/d3-polygon-3.0.1.tgz", + "integrity": "sha512-3vbA7vXYwfe1SYhED++fPUQlWSYTTGmFmQiany/gdbiWgU/iEyQzyymwL9SkJjFFuCS4902BSzewVGsHHmHtXg==" }, "d3-quadtree": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-1.0.7.tgz", - "integrity": "sha512-RKPAeXnkC59IDGD0Wu5mANy0Q2V28L+fNe65pOCXVdVuTJS3WPKaJlFHer32Rbh9gIo9qMuJXio8ra4+YmIymA==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-quadtree/-/d3-quadtree-3.0.1.tgz", + "integrity": "sha512-04xDrxQTDTCFwP5H6hRhsRcb9xxv2RzkcsygFzmkSIOJy3PeRJP7sNk3VRIbKXcog561P9oU0/rVH6vDROAgUw==" }, "d3-random": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-1.1.2.tgz", - "integrity": "sha512-6AK5BNpIFqP+cx/sreKzNjWbwZQCSUatxq+pPRmFIQaWuoD+NrbVWw7YWpHiXpCQ/NanKdtGDuB+VQcZDaEmYQ==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-random/-/d3-random-3.0.1.tgz", + "integrity": "sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==" }, "d3-scale": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-2.2.2.tgz", - "integrity": "sha512-LbeEvGgIb8UMcAa0EATLNX0lelKWGYDQiPdHj+gLblGVhGLyNbaCn3EvrJf0A3Y/uOOU5aD6MTh5ZFCdEwGiCw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz", + "integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==", "requires": { - "d3-array": "^1.2.0", - "d3-collection": "1", - "d3-format": "1", - "d3-interpolate": "1", - "d3-time": "1", - "d3-time-format": "2" + "d3-array": "2.10.0 - 3", + "d3-format": "1 - 3", + "d3-interpolate": "1.2.0 - 3", + "d3-time": "2.1.1 - 3", + "d3-time-format": "2 - 4" } }, "d3-scale-chromatic": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-1.5.0.tgz", - "integrity": "sha512-ACcL46DYImpRFMBcpk9HhtIyC7bTBR4fNOPxwVSl0LfulDAwyiHyPOTqcDG1+t5d4P9W7t/2NAuWu59aKko/cg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz", + "integrity": "sha512-Lx9thtxAKrO2Pq6OO2Ua474opeziKr279P/TKZsMAhYyNDD3EnCffdbgeSYN5O7m2ByQsxtuP2CSDczNUIZ22g==", "requires": { - "d3-color": "1", - "d3-interpolate": "1" + "d3-color": "1 - 3", + "d3-interpolate": "1 - 3" } }, "d3-selection": { - "version": "1.4.2", - "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-1.4.2.tgz", - "integrity": "sha512-SJ0BqYihzOjDnnlfyeHT0e30k0K1+5sR3d5fNueCNeuhZTnGw4M4o8mqJchSwgKMXCNFo+e2VTChiSJ0vYtXkg==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-selection/-/d3-selection-3.0.0.tgz", + "integrity": "sha512-fmTRWbNMmsmWq6xJV8D19U/gw/bwrHfNXxrIN+HfZgnzqTHp9jOmKMhsTUjXOJnZOdZY9Q28y4yebKzqDKlxlQ==" }, "d3-shape": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-1.3.7.tgz", - "integrity": "sha512-EUkvKjqPFUAZyOlhY5gzCxCeI0Aep04LwIRpsZ/mLFelJiUfnK56jo5JMDSE7yyP2kLSb6LtF+S5chMk7uqPqw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.1.0.tgz", + "integrity": "sha512-tGDh1Muf8kWjEDT/LswZJ8WF85yDZLvVJpYU9Nq+8+yW1Z5enxrmXOhTArlkaElU+CTn0OTVNli+/i+HP45QEQ==", "requires": { - "d3-path": "1" + "d3-path": "1 - 3" } }, "d3-time": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-1.1.0.tgz", - "integrity": "sha512-Xh0isrZ5rPYYdqhAVk8VLnMEidhz5aP7htAADH6MfzgmmicPkTo8LhkLxci61/lCB7n7UmE3bN0leRt+qvkLxA==" + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.0.0.tgz", + "integrity": "sha512-zmV3lRnlaLI08y9IMRXSDshQb5Nj77smnfpnd2LrBa/2K281Jijactokeak14QacHs/kKq0AQ121nidNYlarbQ==", + "requires": { + "d3-array": "2 - 3" + } }, "d3-time-format": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-2.3.0.tgz", - "integrity": "sha512-guv6b2H37s2Uq/GefleCDtbe0XZAuy7Wa49VGkPVPMfLL9qObgBST3lEHJBMUp8S7NdLQAGIvr2KXk8Hc98iKQ==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz", + "integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==", "requires": { - "d3-time": "1" + "d3-time": "1 - 3" } }, "d3-timer": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-1.0.10.tgz", - "integrity": "sha512-B1JDm0XDaQC+uvo4DT79H0XmBskgS3l6Ve+1SBCfxgmtIb1AVrPIoqd+nPSv+loMX8szQ0sVUhGngL7D5QPiXw==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz", + "integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==" }, "d3-transition": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-1.3.2.tgz", - "integrity": "sha512-sc0gRU4PFqZ47lPVHloMn9tlPcv8jxgOQg+0zjhfZXMQuvppjG6YuwdMBE0TuqCZjeJkLecku/l9R0JPcRhaDA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/d3-transition/-/d3-transition-3.0.1.tgz", + "integrity": "sha512-ApKvfjsSR6tg06xrL434C0WydLr7JewBB3V+/39RMHsaXTOG0zmt/OAXeng5M5LBm0ojmxJrpomQVZ1aPvBL4w==", "requires": { - "d3-color": "1", - "d3-dispatch": "1", - "d3-ease": "1", - "d3-interpolate": "1", - "d3-selection": "^1.1.0", - "d3-timer": "1" + "d3-color": "1 - 3", + "d3-dispatch": "1 - 3", + "d3-ease": "1 - 3", + "d3-interpolate": "1 - 3", + "d3-timer": "1 - 3" } }, - "d3-voronoi": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/d3-voronoi/-/d3-voronoi-1.1.4.tgz", - "integrity": "sha512-dArJ32hchFsrQ8uMiTBLq256MpnZjeuBtdHpaDlYuQyjU0CVzCJl/BVW+SkszaAeH95D/8gxqAhgx0ouAWAfRg==" - }, "d3-zoom": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-1.8.3.tgz", - "integrity": "sha512-VoLXTK4wvy1a0JpH2Il+F2CiOhVu7VRXWF5M/LroMIh3/zBAC3WAt7QoIvPibOavVo20hN6/37vwAsdBejLyKQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/d3-zoom/-/d3-zoom-3.0.0.tgz", + "integrity": "sha512-b8AmV3kfQaqWAuacbPuNbL6vahnOJflOhexLzMMNLga62+/nh0JzvJ0aO/5a5MVgUFGS7Hu1P9P03o3fJkDCyw==", "requires": { - "d3-dispatch": "1", - "d3-drag": "1", - "d3-interpolate": "1", - "d3-selection": "1", - "d3-transition": "1" + "d3-dispatch": "1 - 3", + "d3-drag": "2 - 3", + "d3-interpolate": "1 - 3", + "d3-selection": "2 - 3", + "d3-transition": "2 - 3" } }, "damerau-levenshtein": { @@ -21092,6 +21763,14 @@ } } }, + "delaunator": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/delaunator/-/delaunator-5.0.0.tgz", + "integrity": "sha512-AyLvtyJdbv/U1GkiS6gUUzclRoAY4Gs75qkMygJJhU75LW4DNuSF2RMzpxs9jw9Oz1BobHjTdkG3zdP55VxAqw==", + "requires": { + "robust-predicates": "^3.0.0" + } + }, "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", @@ -22900,6 +23579,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -23085,6 +23765,11 @@ } } }, + "internmap": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz", + "integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==" + }, "ip": { "version": "1.1.8", "resolved": "https://registry.npmjs.org/ip/-/ip-1.1.8.tgz", @@ -26617,6 +27302,11 @@ "glob": "^7.1.3" } }, + "robust-predicates": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/robust-predicates/-/robust-predicates-3.0.1.tgz", + "integrity": "sha512-ndEIpszUHiG4HtDsQLeIuMvRsDnn8c8rYStabochtUeCvfuvNptb5TUbVD68LRAILPX7p9nqQGh4xJgn3EHS/g==" + }, "run-async": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz", diff --git a/frontend/package.json b/frontend/package.json index 79df4c5a..9cfe533b 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,7 +31,7 @@ "chartjs-plugin-annotation": "^1.4.0", "chartjs-plugin-datalabels": "^2.0.0", "core-js": "2.6.5", - "d3": "^5.15.0", + "d3": "^7.4.0", "formidable": "^2.0.1", "ng2-charts": "3.0.11", "rxjs": "^7.4.0", @@ -43,6 +43,7 @@ "@angular/cli": "13.3.5", "@angular/compiler-cli": "13.3.8", "@angular/language-service": "13.3.8", + "@types/d3": "^7.4.0", "@types/jasmine": "~3.6.0", "@types/jasminewd2": "2.0.5", "@typescript-eslint/eslint-plugin": "^2.23.0", diff --git a/frontend/src/modules/graph-visualization/D3GraphData.ts b/frontend/src/modules/graph-visualization/D3GraphData.ts index cfbd9284..3fb26a1e 100644 --- a/frontend/src/modules/graph-visualization/D3GraphData.ts +++ b/frontend/src/modules/graph-visualization/D3GraphData.ts @@ -1,4 +1,5 @@ +import * as d3Force from 'd3-force'; export enum NodeType { None = "None", @@ -7,12 +8,24 @@ export enum NodeType { D3CapabilityNode = "D3CapabilityNode" } -export class D3Node { +export class D3Node implements d3Force.SimulationNodeDatum { + index?: number | undefined; + x?: number | undefined; + y?: number | undefined; + vx?: number | undefined; + vy?: number | undefined; + fx?: number | null | undefined; + fy?: number | null | undefined; + constructor( public id: string, public name: string, public group: number, - public type: NodeType = NodeType.None) { } + public type: NodeType = NodeType.None, + x?, y?) { + this.x = x; + this.y = y; + } } export class D3ModuleNode extends D3Node { @@ -32,10 +45,10 @@ export class D3CapabilityNode extends D3Node{ } } -export class D3Link { +export class D3Link implements d3Force.SimulationLinkDatum { constructor( - public source: string, - public target: string, + public source: D3Node, + public target: D3Node, public type: string) { } } @@ -76,7 +89,7 @@ export class D3GraphData { oldNodesToConnect.forEach(oldNode => { newNodesToConnect.forEach(newNode => { - this.addLink(new D3Link(oldNode.id, newNode.id, connectionType)); + this.addLink(new D3Link(oldNode, newNode, connectionType)); }); }); } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index a454716a..4c132057 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -2,7 +2,6 @@

Graph-Visualization

-
- -
-
\ No newline at end of file + + +
diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.scss b/frontend/src/modules/graph-visualization/graph-visualization.component.scss index e69de29b..29a9075f 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.scss +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.scss @@ -0,0 +1,4 @@ +.visuContainer { + height: 600px; + width: 100% +} diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index 26ff3bf1..d7f856af 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -1,7 +1,14 @@ -import { Component, OnInit, AfterContentInit, ViewEncapsulation, HostListener, ViewChild, ElementRef } from '@angular/core'; -import * as d3 from "d3"; +import { Component, ViewEncapsulation, ViewChild, ElementRef, AfterViewInit } from '@angular/core'; +import * as d3Selection from 'd3-selection'; +import * as d3Force from 'd3-force'; +import * as d3Transition from 'd3-transition'; +import * as d3Scale from 'd3-scale'; +import * as d3ScaleChromatic from'd3-scale-chromatic'; +import * as d3Drag from'd3-drag'; import { ActivatedRoute } from '@angular/router'; import { NodeCreatorService } from './node-creator.service'; +import { D3Link, D3Node, NodeType } from './D3GraphData'; +import {v4} from "uuid"; @Component({ @@ -10,33 +17,39 @@ import { NodeCreatorService } from './node-creator.service'; templateUrl: './graph-visualization.component.html', styleUrls: ['./graph-visualization.component.scss'] }) -export class GraphVisualizationComponent implements AfterContentInit, OnInit { +export class GraphVisualizationComponent implements AfterViewInit { + name: string; - svg: any; - link: any; - /** The displayed node name*/ - text: any; - /** The displayed link description */ - linkText: any; - /** The displayed node */ - node: any; + svg: any; // Reference to the svg element of the simulation + + nodesContainer: any; // Container for all nodes + nodes: d3Selection.Selection; // The nodes of the simulation + texts: any; // The node texts of the simulation + + linksContainer: any; // Container for all links + links: d3Selection.Selection; ; // The links of the simulation + linkTexts: any; // The linkTexts of the simulation + /** Loaded data: Connected modules with their capabilities and skills*/ - data: any; + data: { + links: D3Link[]; + nodes: D3Node[]; + }; + /**Returns different colors automatically. The node-border color is the same for all node-group members*/ color: any; /**Simulation of the force directed graph */ - simulation: any; + simulation: d3Force.Simulation; - margin = { top: 10, right: 30, bottom: 30, left: 40 }; + // margin = { top: 10, right: 30, bottom: 30, left: 40 }; + margin = 5; width = 100; height = 100; - /**Defines the nodeborder thickness*/ - nodeborder = 8; - /**Defines the standard radius of a node*/ - rad = 20; - index=0; + + nodeBorder = 4; // node border thickness + nodeRadius = 16; // standard radius of a node constructor( private route: ActivatedRoute, @@ -44,61 +57,38 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { ) {} @ViewChild('g') svgContainer: ElementRef; + svgHeight = 100; + svgWidth = 200; moduleName: string; - ngOnInit(): void {} - - - ngAfterContentInit(): void { - + ngAfterViewInit(): void { + this.svgWidth = this.svgContainer.nativeElement.offsetWidth; + this.svgHeight = this.svgContainer.nativeElement.offsetHeight; this.route.params.subscribe(p => { this.moduleName = p['moduleName']; }); // this.data = this.nodeCreatorService.getAllNodes(this.moduleName); // load data created by node-creator.service - this.data = this.nodeCreatorService.getAllNodes(); - - this.setSvg(); - this.setSimulation(); - } - - /**Defines settings of the simulation */ - setSimulation(){ - this.simulation = d3.forceSimulation(this.data.nodes); - this.simulation - .force("link", d3.forceLink() - .id(function (d) { return d.id; }) - .distance(80) - .links(this.data.links)) - .force("collision", d3.forceCollide(40)) - .on("tick", this.ticked) // tick on every step of the simulation - .force("charge", d3.forceManyBody().strength(-400)) // node magnetism / attraction - .force("center", d3.forceCenter(this.svgContainer.nativeElement.offsetWidth / 2, this.svgContainer.nativeElement.offsetHeight / 2)); //Zentrierung der Nodes - - this.refreshSimulation(); + this.nodeCreatorService.getAllNodes().subscribe(data => { + this.data = data; + this.setSvg(); + this.setSimulation(); + }); } - - /**Defines settings of the SVG and the color-scheme of the nodes */ - setSvg(){ - - this.svg = d3.select("#graph") // size definitions for the svg - .append("svg") - .attr("width", this.width + '%') - .attr("height", this.height + '%') - .append("g") - .attr("transform", - "translate(" + this.margin.left + "," + this.margin.top + ")"); + setSvg(): void { + this.svg = d3Selection.select("#graph"); // size definitions for the svg + // .attr("width", this.width + '%') + // .attr("height", this.height + '%'); - - this.color = d3.scaleOrdinal(d3.schemeCategory10); // chooses a scheme category for node colours + this.color = d3Scale.scaleOrdinal(d3ScaleChromatic.schemeCategory10); // chooses a scheme category for node colours this.svg.append('defs').append('marker') // marker/ arrow settings .attr("id", 'arrowhead') .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', this.rad + this.nodeborder) // arrow position and dimensions + .attr('refX', this.nodeRadius + this.nodeBorder) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') .attr('markerWidth', 13) @@ -107,222 +97,243 @@ export class GraphVisualizationComponent implements AfterContentInit, OnInit { .append('svg:path') .attr('d', 'M 0,-5 L 10 ,0 L 0,5') .attr('fill', '#999') - .style('stroke', 'none');} + .style('stroke', 'none'); + + // Define elemennt containers. Important nodes after links so that nodes are rendered on top + this.linksContainer = this.svg.append('g').attr('class', 'linksContainer'); + this.nodesContainer = this.svg.append('g').attr('class', 'nodesContainer'); + } + + /**Defines settings of the simulation */ + setSimulation(): void { + this.simulation = d3Force.forceSimulation(this.data.nodes) + .force("link", d3Force.forceLink() + .id((node: D3Node) => node.id) + .distance(150) + .links(this.data.links)) + .force("collision", d3Force.forceCollide(this.nodeRadius*1.5)) + .force("charge", d3Force.forceManyBody().strength(-300)) // node magnetism / attraction + .force("center", d3Force.forceCenter(this.svgWidth/ 2, this.svgHeight / 2)) //Zentrierung der Nodes + .on("tick", this.ticked); // tick on every step of the simulation + + this.updateSimulation(); + } /** * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ - refreshSimulation() { - this.link = this.svg.selectAll(".link").data(this.data.links, function (d){return d.target.id;}); - const linkEnter=this.link.enter() //enter-selection - .append("line").style("stroke", "#aaa") - .attr("class", "links") - .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link - ; - linkEnter.append("title") - .text(function (d){return d.type;}); - this.link = this.link.merge(linkEnter); // merge old elements with entered elements - this.link.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) - - this.node = this.svg.selectAll(".node").data(this.data.nodes, function (d){return d.id;}); - - const nodeEnter= this.node.enter().append("circle") - .attr("r", (d) => { - if (d.group == 1) { return this.rad + 8; } - else { return this.rad; } - }); + updateSimulation(): void { + this.nodes = this.nodesContainer.selectAll(".nodes").data(this.data.nodes, function (d){return d.id;}); + + const nodeEnter= this.nodes.enter().append("circle") + .attr("class", "nodes") + .attr("r", (d) => this.nodeRadius) + .attr("cx", (d) => { return d.x; }) + .attr("cy", (d) => { return d.y; }); + nodeEnter.on('mouseover', this.mouseover); nodeEnter.on('mouseout', this.mouseout); nodeEnter.style("fill", this.setNodeStyle) - .style("stroke-width", this.nodeborder) + .style("stroke-width", this.nodeBorder) .style("stroke", (d) => { return this.color(d.group); }); // set different node colours for each node-group nodeEnter.on('dblclick', this.nodeDoubleClick); - nodeEnter.call(d3.drag() // reactions when dragging a node + nodeEnter.call(d3Drag.drag() // reactions when dragging a node .on("start", this.dragstarted) .on("drag", this.dragged) .on("end", this.dragended)); - this.node=this.node.merge(nodeEnter); // merge old elements with entered elements - this.node.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + this.nodes = this.nodes.merge(nodeEnter); // merge old elements with entered elements + + this.nodes.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + + this.links = this.linksContainer.selectAll(".links").data(this.data.links); + + const linkEnter=this.links.enter() + .append("line") + .style("stroke", "#aaa") + .attr("class", "links") + .attr("x2", (d) => { return d.source.x; }) + .attr("y2", (d) => { return d.source.y; }) + .attr("x1", (d) => { return d.target.x; }) + .attr("x1", (d) => { return d.target.y; }) + .attr('marker-end', 'url(#arrowhead)') // arrow on end of the link + ; + + this.links = this.links.merge(linkEnter); // merge old elements with entered elements + this.links.exit().remove(); // remove data-elements of exit-selection(all old elements, wich are not in the new dataset) + this.linkTexts = this.linksContainer.selectAll('.linkTexts') + .data(this.data.links); - this.text = this.svg.selectAll("text").data(this.data.nodes); - const textEnter=this.text.enter().append("text") - .data(this.data.nodes, function (d){return d.name;}) + const linkTextsEnter = this.linkTexts.enter().append('text') + .attr("class", "linkTexts") + // .data(this.data.links, function (l: D3Link){return `${l.source.id}_${l.target.id}`;}) + .attr("font-weight", "normal") + .text((d) => d.type); // get text from data + this.linkTexts = this.linkTexts.merge(linkTextsEnter); + this.linkTexts.exit().remove(); + + this.texts = this.nodesContainer.selectAll(".nodeTexts").data(this.data.nodes); + const textEnter=this.texts.enter().append("text") + .attr("class", "nodeTexts") + .data(this.data.nodes, function (d){return d.id;}) .attr("font-weight", function (d) { if (d.group == 1) { return "bold"; } else { return "normal"; } }) - .text(function (d) { console.log(d.name); - return d.name; }); // get text from data - this.text=this.text.merge(textEnter); - this.text.exit().remove(); + .text((d) => d.name); // get text from data - this.linkText = this.svg.selectAll("links").data(this.data.links, function (d){ return d.type;}); - const linkTextEnter=this.linkText.enter().append("text") - .style("fill", "#999") - .attr("font-style", "italic") - .text(function (d) { return d.type; }); // get link text from data - this.linkText=this.linkText.merge(linkTextEnter); - this.linkText.exit().remove(); + this.texts=this.texts.merge(textEnter); + this.texts.exit().remove(); - this.simulation.nodes(this.data.nodes); // simualtion uses current data - this.simulation.force("link").links(this.data.links); + this.simulation.nodes(this.data.nodes); } - /** - * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element - * @param d The position of every graph element (i.e. node, link...) - */ - ticked = () => { - const centerWidth = this.svgContainer.nativeElement.offsetWidth / 2; // center definitions of the svg - const centerHeight = this.svgContainer.nativeElement.offsetHeight / 2; - - this.text - .attr("dx", (d) => { // restrictions for node text positions in the svg - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return Math.max(0 + relativeRad, Math.min(relativeDragX + 1.5, this.width - 5 * relativeRad)) + '%'; - }) - .attr("dy", (d) => { - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.max(0 + relativeRad, Math.min(relativeDragY, this.height - 5 * relativeRad)) + '%'; - }); - - this.linkText - .attr("dx", (d) => { // restrictions for link text positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - return Math.min(this.width - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceX - relativeTargetX) / 2) + relativeTargetX))) + '%'; - }) - .attr("dy", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetHeight; - return Math.min(this.height - 5 * relativeRad, (Math.max(0 + relativeRad, ((relativeSourceY - relativeTargetY) / 2) + relativeTargetY))) + '%'; - }); - - this.link - .attr("x1", (d) => { // restrictions for link positions in the svg - d.source.x = Math.max(0 + this.rad, Math.min(d.source.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; // converting this.radius from absolute to relative - const relativeSourceX = 100 * d.source.x / this.svgContainer.nativeElement.offsetWidth; - return relativeSourceX + '%'; - }) - .attr("y1", (d) => { - d.source.y = Math.max(0 + this.rad, Math.min(d.source.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeSourceY = 100 * d.source.y / this.svgContainer.nativeElement.offsetHeight; - return relativeSourceY + '%'; - }) - .attr("x2", (d) => { - d.target.x = Math.max(0 + this.rad, Math.min(d.target.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetX = 100 * d.target.x / this.svgContainer.nativeElement.offsetWidth; - return relativeTargetX + '%'; - }) - .attr("y2", (d) => { - d.target.y = Math.max(0 + this.rad, Math.min(d.target.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeTargetY = 100 * d.target.y / this.svgContainer.nativeElement.offsetHeight; - return relativeTargetY + '%'; - }); - - this.node - .attr("cx", (d) => { - d.x = Math.max(0 + this.rad, Math.min(d.x, this.svgContainer.nativeElement.offsetWidth - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragX = 100 * d.x / this.svgContainer.nativeElement.offsetWidth; - return relativeDragX + '%'; - }) - .attr("cy", (d) => { - d.y = Math.max(0 + this.rad, Math.min(d.y, this.svgContainer.nativeElement.offsetHeight - 5 * this.rad)); - const relativeRad = 100 * this.rad / this.svgContainer.nativeElement.offsetWidth; - const relativeDragY = 100 * d.y / this.svgContainer.nativeElement.offsetHeight; - return relativeDragY + '%'; - }); - } - - /** - * Function executed on a drag start - */ - dragstarted = (d) => { - if (!d3.event.active) this.simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; - } - - /** + + /** + * Function executed on every tick in the simulation. Defines restrictions for positions of node, text, link-text and link. The functions computes the position of each element + * @param d The position of every graph element (i.e. node, link...) + */ + ticked = (): void => { + this.nodes + .attr("cx", (d: D3Node) => { + d.x = Math.max(0 + this.nodeRadius, Math.min(d.x, this.svgWidth - 5 * this.nodeRadius)); + const relativeDragX = 100 * d.x / this.svgWidth; + return relativeDragX + '%'; + }) + .attr("cy", (d: D3Node) => { + d.y = Math.max(0 + this.nodeRadius, Math.min(d.y, this.svgHeight - 5 * this.nodeRadius)); + const relativeDragY = 100 * d.y / this.svgHeight; + return relativeDragY + '%'; + }); + + this.links + .attr("x1", (l: D3Link) => { // restrictions for link positions in the svg + l.source.x = Math.max(0 + this.nodeRadius, Math.min(l.source.x, this.svgWidth - 5 * this.nodeRadius)); + const relativeSourceX = 100 * l.source.x / this.svgWidth; + return relativeSourceX + '%'; + }) + .attr("y1", (l: D3Link) => { + l.source.y = Math.max(0 + this.nodeRadius, Math.min(l.source.y, this.svgHeight - 5 * this.nodeRadius)); + const relativeSourceY = 100 * l.source.y / this.svgHeight; + return relativeSourceY + '%'; + }) + .attr("x2", (l: D3Link) => { + l.target.x = Math.max(0 + this.nodeRadius, Math.min(l.target.x, this.svgWidth - 5 * this.nodeRadius)); + const relativeTargetX = 100 * l.target.x / this.svgWidth; + return relativeTargetX + '%'; + }) + .attr("y2", (l: D3Link) => { + l.target.y = Math.max(0 + this.nodeRadius, Math.min(l.target.y, this.svgHeight - 5 * this.nodeRadius)); + const relativeTargetY = 100 * l.target.y / this.svgHeight; + return relativeTargetY + '%'; + }); + + this.texts + .attr("dx", (d: D3Node) => { // restrictions for node text positions in the svg + const relativeRadius = 100 * this.nodeRadius / this.svgWidth; + const relativeDragX = 100 * d.x / this.svgWidth; + return (relativeDragX + relativeRadius) + '%'; + }) + .attr("dy", (d: D3Node) => { + const relativeRadius = 100 * this.nodeRadius / this.svgHeight; + const relativeDragY = 100 * d.y / this.svgHeight; + return (relativeDragY - 0.5*relativeRadius) + '%'; + }); + + this.linkTexts + .attr("dx", (l: D3Link) => { // restrictions for link text positions in the svg + const midX = (l.target.x + l.source.x) / 2 - 10; + const relativeMidX = 100 * midX / this.svgWidth; + return relativeMidX + "%"; + }) + .attr("dy", (l: D3Link) => { + const midY = (l.target.y + l.source.y) / 2; + const relativeMidY = 100 * midY / this.svgHeight; + return relativeMidY + "%"; + }); + + } + + /** + * Function executed on a drag start + */ + dragstarted = (d: d3Force.SimulationNodeDatum) => { + if (!d3Transition.active(d as d3Selection.BaseType)) this.simulation.alphaTarget(0.3).restart(); + d.fx = d.x; + d.fy = d.y; + } + + /** * Drag function, executed on every drag movement * @param d The dragged node */ - dragged(d) { - d.fx = d3.event.x; - d.fy = d3.event.y; - } - - dragended(d) { - - } - /** - * Mouseover function, executed when mouse over node. The radius of the node will increase. - * @param d node under the cursor - */ - mouseover(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 12); - } - else { - d3.select(this).transition() - .duration('2') - .attr('r', 20 + 6); - } - } - /** - * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. - * @param d node wich is left by the cursor after mouseover - */ - mouseout(d) { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3.select(this).transition() - .attr('r', 20 + 8); - } - else { - d3.select(this).transition() - .attr('r', 20); - } - } - /** - * Doubleclick function, executed on every doubleclick on a node - *@param d The clicked node - */ - nodeDoubleClick=(d)=> { - this.index++; - const testNode = { "name": "neighbor"+ this.index, "group": 8 }; - this.data.nodes.push(testNode); - this.data.links.push({ "source": d.id, "target": testNode, "type": "testclick" }); - this.refreshSimulation(); - } - - /** Returns the color of the inner circle of a node */ - setNodeStyle(d){ - if (d.group == 1) { return "black"; } - else { return "whitesmoke"; } - } - /** Returns the node border color for each node-group */ - setNodeBorderStyle=(d)=>{ - return this.color(d.group); - } + dragged(event: d3Drag.D3DragEvent, d: d3Force.SimulationNodeDatum): void { + d.fx = event.x; + d.fy = event.y; + } + + dragended(d) { + + } + + /** + * Mouseover function, executed when mouse over node. The radius of the node will increase. + * @param d node under the cursor + */ + mouseover(event: MouseEvent, d: D3Node) { + + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node + // d3Selection.select(d.id) + // .transition() + // .duration(2) + // .attr('r', 20 + 12); + } + else { + d3Selection.select(d.id).transition() + .duration(2) + .attr('r', 20 + 6); + } + } + /** + * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. + * @param d node wich is left by the cursor after mouseover + */ + mouseout(d: D3Node): void { + if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node + d3Selection.select(d.id).transition() + .attr('r', 20 + 8); + } + else { + d3Selection.select(d.id).transition() + .attr('r', 20); + } + } + + /** + * Doubleclick function, executed on every doubleclick on a node + *@param d The clicked node + */ + nodeDoubleClick = (e: MouseEvent, d: D3Node)=> { + const nodeId: string = v4(); + const testNode = new D3Node(nodeId , nodeId.substring(0,4), 3); + const testLink = new D3Link(d, testNode, "test"); + this.data.links.push(testLink); + this.data.nodes.push(testNode); + this.updateSimulation(); + } + + /** Returns the color of the inner circle of a node */ + setNodeStyle(d): string{ + if (d.group == 1) { + return "black"; + } + else { + return "whitesmoke"; + } + } } diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts index b3970c6a..5db2a717 100644 --- a/frontend/src/modules/graph-visualization/node-creator.service.ts +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -1,6 +1,8 @@ import { Injectable } from '@angular/core'; -import { lastValueFrom } from 'rxjs'; +import { lastValueFrom, map, Observable } from 'rxjs'; import { ModuleService } from '../../shared/services/module.service'; +import * as d3Force from 'd3-force'; +import { D3Link, D3Node } from './D3GraphData'; @Injectable({ providedIn: 'root' @@ -12,107 +14,115 @@ export class NodeCreatorService { private moduleService: ModuleService ) {} - async getAllNodes() { + getAllNodes(): Observable<{links: D3Link[]; nodes: D3Node[] }> { + console.log("getting all nodes"); - const receivedData = await lastValueFrom(this.moduleService.getAllModules()); - const data= {nodes:[], links:[]}; + // const receivedData = await (lastValueFrom(this.moduleService.getAllModules())); + const $receivedData$ = this.moduleService.getAllModules(); + console.log("received data"); + console.log($receivedData$); + + + const nodeData= {nodes:[], links:[]}; const idMap= new Map(); // Map for saving Node-IDs let id=0; // ID start value - receivedData.forEach(receivedModule => { //loop over all modules - id++; - idMap.set(receivedModule, id); // assigns an ID for each module-node - data.nodes.push({ // adds a node for each module - "id" : idMap.get(receivedModule), - "name": receivedModule.getLocalName(), - "group": 1 // assings node-groups (here: node colour) - }); - - receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module + return $receivedData$.pipe(map(data => { + data.forEach(receivedModule => { //loop over all modules id++; - idMap.set(capability, id); // assigns an ID for each capability-node - data.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.getLocalName(), - "group": 2 - }); - data.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description + idMap.set(receivedModule, id); // assigns an ID for each module-node + nodeData.nodes.push({ // adds a node for each module + "id" : idMap.get(receivedModule), + "name": receivedModule.getLocalName(), + "group": 1 // assings node-groups (here: node colour) }); - capability.inputs.forEach(input=>{ + receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module id++; - idMap.set(input, id); // assigns an ID for each input-node - data.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.getLocalName(), - "group": 3 + idMap.set(capability, id); // assigns an ID for each capability-node + nodeData.nodes.push({ // adds a node for each capability of current module + "id" : idMap.get(capability), + "name": capability.getLocalName(), + "group": 2 }); - data.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" + nodeData.links.push({ // adds a link between capability and module + "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow + "target": idMap.get(capability), + "type": "has_capability" // adds a link description }); - id++; - data.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.type, - "group": 6 - }); - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }); - }); - capability.outputs.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - data.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.getLocalName(), - "group": 4 + capability.inputs.forEach(input=>{ + id++; + idMap.set(input, id); // assigns an ID for each input-node + nodeData.nodes.push({ // adds a node for each input of current capability + "id": idMap.get(input), + "name": input.getLocalName(), + "group": 3 + }); + nodeData.links.push({ // adds a link between capability and input + "source": idMap.get(capability), + "target": idMap.get(input), + "type": "has_input" + }); + id++; + nodeData.nodes.push({ // adds a node for rdf:type of current input + "id": id, // adds an ID for the type node (not saved in idMap) + "name": input.type, + "group": 6 + }); + nodeData.links.push({ // adds a link between rdf:type and input + "source": idMap.get(input), + "target": id, //assigns an ID for each type-node (not saved in idMap) + "type": "rdf:type" + }); }); - data.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }); - id++; - data.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.type, - "group": 6 - }); - data.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }); - }); - capability.skills.forEach(skill=>{ - id++; - idMap.set(skill, id); // adds a node for each skill of current capability - data.nodes.push({ - "id" : idMap.get(skill), - "name": skill.getLocalName(), - "group": 5 + capability.outputs.forEach(output=>{ + id++; + idMap.set(output,id); // assigns an ID for each output-node + nodeData.nodes.push({ // adds a node for each output of current capability + "id" : idMap.get(output), + "name": output.getLocalName(), + "group": 4 + }); + nodeData.links.push({ // adds a link between capability and output + "source": idMap.get(capability), + "target": idMap.get(output), + "type": "has_output" + }); + id++; + nodeData.nodes.push({ // adds a node for rdf:type of current output + "id": id, + "name": output.type, + "group": 6 + }); + nodeData.links.push({ // adds a link between rdf:type and input + "source": idMap.get(output), + "target": id, + "type": "rdf:type" + }); }); - data.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" + + capability.skills.forEach(skill=>{ + id++; + idMap.set(skill, id); // adds a node for each skill of current capability + nodeData.nodes.push({ + "id" : idMap.get(skill), + "name": skill.getLocalName(), + "group": 5 + }); + nodeData.links.push({ // adds a link between current capability and current skill node + "source": idMap.get(capability), + "target": idMap.get(skill), + "type": "executable_via_Skill" + }); }); + }); }); - }); - - return data; + return nodeData; + })); } From bec070a65ee1625c1459fc3c9bd954c9beaa092f Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Tue, 23 Aug 2022 10:46:09 +0200 Subject: [PATCH 73/74] Now fixed all minor issues with graph visualization as wel --- .../graph-visualization/D3GraphData.ts | 27 +++- .../graph-visualization.component.html | 4 +- .../graph-visualization.component.scss | 8 +- .../graph-visualization.component.ts | 123 +++++++++--------- .../node-creator.service.ts | 110 ++++------------ 5 files changed, 117 insertions(+), 155 deletions(-) diff --git a/frontend/src/modules/graph-visualization/D3GraphData.ts b/frontend/src/modules/graph-visualization/D3GraphData.ts index 3fb26a1e..b339b820 100644 --- a/frontend/src/modules/graph-visualization/D3GraphData.ts +++ b/frontend/src/modules/graph-visualization/D3GraphData.ts @@ -3,9 +3,11 @@ import * as d3Force from 'd3-force'; export enum NodeType { None = "None", - D3ModuleNode ="D3ModuleNode", - D3SkillNode = "D3SkillNode", - D3CapabilityNode = "D3CapabilityNode" + Module ="D3ModuleNode", + Skill = "D3SkillNode", + Capability = "D3CapabilityNode", + Input = "D3InputNode", + Output = "D3OutputNode" } export class D3Node implements d3Force.SimulationNodeDatum { @@ -20,7 +22,6 @@ export class D3Node implements d3Force.SimulationNodeDatum { constructor( public id: string, public name: string, - public group: number, public type: NodeType = NodeType.None, x?, y?) { this.x = x; @@ -30,18 +31,30 @@ export class D3Node implements d3Force.SimulationNodeDatum { export class D3ModuleNode extends D3Node { constructor(id: string, name: string) { - super(id, name, 1, NodeType.D3ModuleNode); + super(id, name, NodeType.Module); } } export class D3SkillNode extends D3Node{ constructor(id: string, name: string) { - super(id, name, 100, NodeType.D3SkillNode); + super(id, name, NodeType.Skill); } } export class D3CapabilityNode extends D3Node{ constructor(id: string, name: string) { - super(id, name, 200, NodeType.D3CapabilityNode); + super(id, name, NodeType.Capability); + } +} + +export class D3InputNode extends D3Node{ + constructor(id: string, name: string) { + super(id, name, NodeType.Input); + } +} + +export class D3OutputNode extends D3Node{ + constructor(id: string, name: string) { + super(id, name, NodeType.Output); } } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.html b/frontend/src/modules/graph-visualization/graph-visualization.component.html index 4c132057..ff8bcf04 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.html +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.html @@ -1,7 +1,7 @@

Graph-Visualization

-
- +
+
diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.scss b/frontend/src/modules/graph-visualization/graph-visualization.component.scss index 29a9075f..38ecf2c6 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.scss +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.scss @@ -1,4 +1,8 @@ -.visuContainer { +.svgContainer { height: 600px; - width: 100% + border: 1px solid #bbb; + border-radius: 5px; + width: 100%; + overflow: hidden; + display: block; } diff --git a/frontend/src/modules/graph-visualization/graph-visualization.component.ts b/frontend/src/modules/graph-visualization/graph-visualization.component.ts index d7f856af..175d3cdb 100644 --- a/frontend/src/modules/graph-visualization/graph-visualization.component.ts +++ b/frontend/src/modules/graph-visualization/graph-visualization.component.ts @@ -20,7 +20,7 @@ import {v4} from "uuid"; export class GraphVisualizationComponent implements AfterViewInit { name: string; - svg: any; // Reference to the svg element of the simulation + svg: d3Selection.Selection; // Reference to the svg element of the simulation nodesContainer: any; // Container for all nodes nodes: d3Selection.Selection; // The nodes of the simulation @@ -36,8 +36,9 @@ export class GraphVisualizationComponent implements AfterViewInit { nodes: D3Node[]; }; - /**Returns different colors automatically. The node-border color is the same for all node-group members*/ - color: any; + color: any; // Contains scale of colors + + typeColors = new Map(); // Used to store colors per type /**Simulation of the force directed graph */ simulation: d3Force.Simulation; @@ -48,7 +49,6 @@ export class GraphVisualizationComponent implements AfterViewInit { height = 100; - nodeBorder = 4; // node border thickness nodeRadius = 16; // standard radius of a node constructor( @@ -64,7 +64,8 @@ export class GraphVisualizationComponent implements AfterViewInit { ngAfterViewInit(): void { this.svgWidth = this.svgContainer.nativeElement.offsetWidth; this.svgHeight = this.svgContainer.nativeElement.offsetHeight; - + console.log(this.svgWidth); + console.log(this.svgHeight); this.route.params.subscribe(p => { this.moduleName = p['moduleName']; }); @@ -78,25 +79,27 @@ export class GraphVisualizationComponent implements AfterViewInit { /**Defines settings of the SVG and the color-scheme of the nodes */ setSvg(): void { - - this.svg = d3Selection.select("#graph"); // size definitions for the svg - // .attr("width", this.width + '%') - // .attr("height", this.height + '%'); + this.svg = d3Selection.select("#graph") + .attr("width",this.svgWidth + 65) + .attr("height", this.svgHeight + 65); + // this.svg = d3Selection.select("#graph") // size definitions for the svg this.color = d3Scale.scaleOrdinal(d3ScaleChromatic.schemeCategory10); // chooses a scheme category for node colours + const markerBoxWidth = 10; + const markerBoxHeight = 8; this.svg.append('defs').append('marker') // marker/ arrow settings .attr("id", 'arrowhead') - .attr('viewBox', '-0 -5 10 10') //coordinate system - .attr('refX', this.nodeRadius + this.nodeBorder) // arrow position and dimensions + .attr('viewBox', [0, -4, markerBoxWidth, markerBoxHeight]) //coordinate system + .attr('refX', 2*markerBoxWidth) // arrow position and dimensions .attr('refY', 0) .attr('orient', 'auto') - .attr('markerWidth', 13) - .attr('markerHeight', 13) + .attr('markerWidth', 1.5*markerBoxWidth) + .attr('markerHeight', 1.5*markerBoxHeight) .attr('xoverflow', 'visible') - .append('svg:path') - .attr('d', 'M 0,-5 L 10 ,0 L 0,5') - .attr('fill', '#999') + .append('path') + .attr('d', 'M 0,-4 L 10,0 L 0,4') + .attr('fill', '#555') .style('stroke', 'none'); // Define elemennt containers. Important nodes after links so that nodes are rendered on top @@ -125,19 +128,19 @@ export class GraphVisualizationComponent implements AfterViewInit { * Draws the graph, executed first on AfterContentInit while setSimulation() and on every doubleclick on a node */ updateSimulation(): void { - this.nodes = this.nodesContainer.selectAll(".nodes").data(this.data.nodes, function (d){return d.id;}); + this.nodes = this.nodesContainer.selectAll(".nodes") + .data(this.data.nodes, (d) => this.convertSpecialChars(d.id)); const nodeEnter= this.nodes.enter().append("circle") .attr("class", "nodes") + .attr("id", (d) => this.convertSpecialChars(d.id)) .attr("r", (d) => this.nodeRadius) .attr("cx", (d) => { return d.x; }) .attr("cy", (d) => { return d.y; }); nodeEnter.on('mouseover', this.mouseover); nodeEnter.on('mouseout', this.mouseout); - nodeEnter.style("fill", this.setNodeStyle) - .style("stroke-width", this.nodeBorder) - .style("stroke", (d) => { return this.color(d.group); }); // set different node colours for each node-group + nodeEnter.style("fill", this.setNodeStyle); nodeEnter.on('dblclick', this.nodeDoubleClick); nodeEnter.call(d3Drag.drag() // reactions when dragging a node .on("start", this.dragstarted) @@ -151,7 +154,7 @@ export class GraphVisualizationComponent implements AfterViewInit { const linkEnter=this.links.enter() .append("line") - .style("stroke", "#aaa") + .style("stroke", "#555") .attr("class", "links") .attr("x2", (d) => { return d.source.x; }) .attr("y2", (d) => { return d.source.y; }) @@ -179,10 +182,7 @@ export class GraphVisualizationComponent implements AfterViewInit { const textEnter=this.texts.enter().append("text") .attr("class", "nodeTexts") .data(this.data.nodes, function (d){return d.id;}) - .attr("font-weight", function (d) { - if (d.group == 1) { return "bold"; } - else { return "normal"; } - }) + .attr("font-weight", "normal") .text((d) => d.name); // get text from data this.texts=this.texts.merge(textEnter); @@ -258,13 +258,17 @@ export class GraphVisualizationComponent implements AfterViewInit { } + private convertSpecialChars(iri: string): string { + return iri.replace(/([://#.])+/g,"_"); + } + /** * Function executed on a drag start */ - dragstarted = (d: d3Force.SimulationNodeDatum) => { + dragstarted = (event: d3Drag.D3DragEvent, d: d3Force.SimulationNodeDatum): void => { if (!d3Transition.active(d as d3Selection.BaseType)) this.simulation.alphaTarget(0.3).restart(); - d.fx = d.x; - d.fy = d.y; + d.fx = event.x; + d.fy = event.y; } /** @@ -276,50 +280,50 @@ export class GraphVisualizationComponent implements AfterViewInit { d.fy = event.y; } - dragended(d) { - + dragended= (event: d3Drag.D3DragEvent, d: d3Force.SimulationNodeDatum): void => { + if (!d3Transition.active(d as d3Selection.BaseType)) this.simulation.alphaTarget(.03); + d.fx = null; + d.fy = null; } /** * Mouseover function, executed when mouse over node. The radius of the node will increase. * @param d node under the cursor */ - mouseover(event: MouseEvent, d: D3Node) { + mouseover = (event: MouseEvent, d: D3Node): void => { + this.increaseNodeRadius(d); + } - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - // d3Selection.select(d.id) - // .transition() - // .duration(2) - // .attr('r', 20 + 12); - } - else { - d3Selection.select(d.id).transition() - .duration(2) - .attr('r', 20 + 6); - } + private increaseNodeRadius(d: D3Node): void { + const elementId = `#${this.convertSpecialChars(d.id)}`; + this.nodesContainer.select(elementId).transition() + .duration(200) + .attr('r', this.nodeRadius * 1.5); } + /** * Mousout function, executed when mouse cursor leaves the node. Decreases radius to origin. * @param d node wich is left by the cursor after mouseover */ - mouseout(d: D3Node): void { - if (d.group == 1) { // module-nodes belong to group 1 and are displayed by a bigger node - d3Selection.select(d.id).transition() - .attr('r', 20 + 8); - } - else { - d3Selection.select(d.id).transition() - .attr('r', 20); - } + mouseout = (event: MouseEvent, d: D3Node): void => { + this.decreaseNodeRadius(d); } + private decreaseNodeRadius(d: D3Node): void { + const elementId = `#${this.convertSpecialChars(d.id)}`; + this.nodesContainer.select(elementId).transition() + .duration(200) + .attr('r', this.nodeRadius); + } + + /** * Doubleclick function, executed on every doubleclick on a node *@param d The clicked node */ nodeDoubleClick = (e: MouseEvent, d: D3Node)=> { - const nodeId: string = v4(); - const testNode = new D3Node(nodeId , nodeId.substring(0,4), 3); + const nodeId: string = "a" + v4(); + const testNode = new D3Node(nodeId , nodeId.substring(0,4), NodeType.Skill); const testLink = new D3Link(d, testNode, "test"); this.data.links.push(testLink); this.data.nodes.push(testNode); @@ -327,13 +331,14 @@ export class GraphVisualizationComponent implements AfterViewInit { } /** Returns the color of the inner circle of a node */ - setNodeStyle(d): string{ - if (d.group == 1) { - return "black"; - } - else { - return "whitesmoke"; + setNodeStyle = (d: D3Node) => { + const typeColor = this.typeColors.get(d.type); + if(!typeColor) { + const newTypeColor = this.color(Math.random() * 10); + this.typeColors.set(d.type, newTypeColor); } + + return this.typeColors.get(d.type); } } diff --git a/frontend/src/modules/graph-visualization/node-creator.service.ts b/frontend/src/modules/graph-visualization/node-creator.service.ts index 5db2a717..768d2037 100644 --- a/frontend/src/modules/graph-visualization/node-creator.service.ts +++ b/frontend/src/modules/graph-visualization/node-creator.service.ts @@ -1,8 +1,7 @@ import { Injectable } from '@angular/core'; -import { lastValueFrom, map, Observable } from 'rxjs'; +import { map, Observable } from 'rxjs'; import { ModuleService } from '../../shared/services/module.service'; -import * as d3Force from 'd3-force'; -import { D3Link, D3Node } from './D3GraphData'; +import { D3CapabilityNode, D3InputNode, D3Link, D3ModuleNode, D3Node, D3OutputNode, D3SkillNode, NodeType } from './D3GraphData'; @Injectable({ providedIn: 'root' @@ -19,102 +18,43 @@ export class NodeCreatorService { // const receivedData = await (lastValueFrom(this.moduleService.getAllModules())); const $receivedData$ = this.moduleService.getAllModules(); - console.log("received data"); - console.log($receivedData$); - const nodeData= {nodes:[], links:[]}; - const idMap= new Map(); // Map for saving Node-IDs - let id=0; // ID start value + const id=0; // ID start value return $receivedData$.pipe(map(data => { data.forEach(receivedModule => { //loop over all modules - id++; - idMap.set(receivedModule, id); // assigns an ID for each module-node - nodeData.nodes.push({ // adds a node for each module - "id" : idMap.get(receivedModule), - "name": receivedModule.getLocalName(), - "group": 1 // assings node-groups (here: node colour) - }); + const moduleNode = new D3ModuleNode(receivedModule.iri, receivedModule.getLocalName()); + nodeData.nodes.push(moduleNode); receivedModule.capabilities.forEach(capability=>{ //loop over all capabilities of current module - id++; - idMap.set(capability, id); // assigns an ID for each capability-node - nodeData.nodes.push({ // adds a node for each capability of current module - "id" : idMap.get(capability), - "name": capability.getLocalName(), - "group": 2 - }); - nodeData.links.push({ // adds a link between capability and module - "source": idMap.get(receivedModule), // defines source and target of the link. Important for the direction of the link/arrow - "target": idMap.get(capability), - "type": "has_capability" // adds a link description - }); + const capNode = new D3CapabilityNode(capability.iri, capability.getLocalName()); + nodeData.nodes.push(capNode); + nodeData.links.push(new D3Link(moduleNode, capNode, "hasCapability")); capability.inputs.forEach(input=>{ - id++; - idMap.set(input, id); // assigns an ID for each input-node - nodeData.nodes.push({ // adds a node for each input of current capability - "id": idMap.get(input), - "name": input.getLocalName(), - "group": 3 - }); - nodeData.links.push({ // adds a link between capability and input - "source": idMap.get(capability), - "target": idMap.get(input), - "type": "has_input" - }); - id++; - nodeData.nodes.push({ // adds a node for rdf:type of current input - "id": id, // adds an ID for the type node (not saved in idMap) - "name": input.type, - "group": 6 - }); - nodeData.links.push({ // adds a link between rdf:type and input - "source": idMap.get(input), - "target": id, //assigns an ID for each type-node (not saved in idMap) - "type": "rdf:type" - }); + const inputNode = new D3InputNode(input.iri, input.getLocalName()); + nodeData.nodes.push(inputNode); + nodeData.links.push(new D3Link(capNode, inputNode, "hasInput")); + + const inputTypeNode = new D3Node(input.type, input.type, NodeType.None); + nodeData.nodes.push(inputTypeNode); + nodeData.links.push(new D3Link(inputNode, inputTypeNode, "rdf:type")); }); capability.outputs.forEach(output=>{ - id++; - idMap.set(output,id); // assigns an ID for each output-node - nodeData.nodes.push({ // adds a node for each output of current capability - "id" : idMap.get(output), - "name": output.getLocalName(), - "group": 4 - }); - nodeData.links.push({ // adds a link between capability and output - "source": idMap.get(capability), - "target": idMap.get(output), - "type": "has_output" - }); - id++; - nodeData.nodes.push({ // adds a node for rdf:type of current output - "id": id, - "name": output.type, - "group": 6 - }); - nodeData.links.push({ // adds a link between rdf:type and input - "source": idMap.get(output), - "target": id, - "type": "rdf:type" - }); + const outputNode = new D3OutputNode(output.iri, output.getLocalName()); + nodeData.nodes.push(outputNode); + nodeData.links.push(new D3Link(capNode, outputNode, "hasInput")); + + const outputTypeNode = new D3Node(output.type, output.type, NodeType.None); + nodeData.nodes.push(outputTypeNode); + nodeData.links.push(new D3Link(outputNode, outputTypeNode, "rdf:type")); }); capability.skills.forEach(skill=>{ - id++; - idMap.set(skill, id); // adds a node for each skill of current capability - nodeData.nodes.push({ - "id" : idMap.get(skill), - "name": skill.getLocalName(), - "group": 5 - }); - nodeData.links.push({ // adds a link between current capability and current skill node - "source": idMap.get(capability), - "target": idMap.get(skill), - "type": "executable_via_Skill" - }); + const skillNode = new D3SkillNode(skill.iri, skill.getLocalName()); + nodeData.nodes.push(skillNode); + nodeData.links.push(new D3Link(capNode, skillNode, "executable_via_Skill")); }); }); From c92d254c9ef2b9f114e356698dc1c6494d441813 Mon Sep 17 00:00:00 2001 From: Aljosha Koecher Date: Tue, 23 Aug 2022 11:19:13 +0200 Subject: [PATCH 74/74] Fixes manual registration --- .../capability-registration.component.ts | 12 +++++------ .../module-registration.component.ts | 21 +++---------------- .../src/shared/services/capability.service.ts | 5 +++-- .../src/shared/services/module.service.ts | 12 +++-------- frontend/src/shared/services/skill.service.ts | 5 +++-- 5 files changed, 18 insertions(+), 37 deletions(-) diff --git a/frontend/src/modules/capabilities/capability-registration/capability-registration.component.ts b/frontend/src/modules/capabilities/capability-registration/capability-registration.component.ts index bf85b38c..699881da 100644 --- a/frontend/src/modules/capabilities/capability-registration/capability-registration.component.ts +++ b/frontend/src/modules/capabilities/capability-registration/capability-registration.component.ts @@ -1,15 +1,15 @@ import { Component, OnInit } from '@angular/core'; @Component({ - selector: 'app-capability-registration', - templateUrl: './capability-registration.component.html', - styleUrls: ['./capability-registration.component.scss'] + selector: 'app-capability-registration', + templateUrl: './capability-registration.component.html', + styleUrls: ['./capability-registration.component.scss'] }) export class CapabilityRegistrationComponent implements OnInit { - constructor() { } + constructor() { } - ngOnInit() { - } + ngOnInit() { + } } diff --git a/frontend/src/modules/production-modules/module-registration/module-registration.component.ts b/frontend/src/modules/production-modules/module-registration/module-registration.component.ts index 161409eb..56563c91 100644 --- a/frontend/src/modules/production-modules/module-registration/module-registration.component.ts +++ b/frontend/src/modules/production-modules/module-registration/module-registration.component.ts @@ -6,24 +6,9 @@ import { HttpClient } from "@angular/common/http"; selector: 'app-module-registration', templateUrl: './module-registration.component.html', styleUrls: ['./module-registration.component.scss'] - + }) export class ModuleRegistrationComponent { - constructor(private httpClient: HttpClient, - private moduleService: ModuleService,) { } - - ontologyString="Enter Ontology here"; - errMessage: any; - saveOntology() { - console.log(this.ontologyString); - this.moduleService.addModule(this.ontologyString).subscribe(null, - (err) => this.errMessage = err.error.message, - () => { - this.errMessage = ""; - this.ontologyString="Ontology registered"; //Variablenwert wird nicht in ontologyString gespeichert - } - ); - - } -} \ No newline at end of file + constructor() { } +} diff --git a/frontend/src/shared/services/capability.service.ts b/frontend/src/shared/services/capability.service.ts index f9ff0c0a..686647c3 100644 --- a/frontend/src/shared/services/capability.service.ts +++ b/frontend/src/shared/services/capability.service.ts @@ -1,7 +1,7 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; import { CapabilityDto } from '@shared/models/capability/Capability'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { map } from 'rxjs/operators'; import { SkillService } from './skill.service'; import { Capability } from '../models/Capability'; @@ -55,7 +55,8 @@ export class CapabilityService { addCapability(ontologyString: string): Observable> { const apiURL = `${this.apiRoot}/capabilities`; - return this.http.post(apiURL, ontologyString); + const headers = new HttpHeaders({"content-type": "text/turtle"}); + return this.http.post(apiURL, ontologyString, {headers: headers}); } addMtpCapability(ontologyFile: File): Observable{ diff --git a/frontend/src/shared/services/module.service.ts b/frontend/src/shared/services/module.service.ts index 8490a551..2b5ebb67 100644 --- a/frontend/src/shared/services/module.service.ts +++ b/frontend/src/shared/services/module.service.ts @@ -1,5 +1,5 @@ import { Injectable } from "@angular/core"; -import { HttpClient } from "@angular/common/http"; +import { HttpClient, HttpHeaders } from "@angular/common/http"; import { Observable, Observer } from "rxjs"; import { ProductionModuleDto } from "@shared/models/production-module/ProductionModule"; import { map, take } from 'rxjs/operators'; @@ -84,16 +84,10 @@ export class ModuleService { } addModule(ontologyString: string): Observable> { const apiURL = `${this.apiRoot}/modules`; - return this.http.post(apiURL, ontologyString); + const headers = new HttpHeaders({"content-type": "text/turtle"}); + return this.http.post(apiURL, ontologyString, {headers: headers}); } - // addMtpModule(ontologyFile: File): Observable{ - // const apiURL= `${this.apiRoot}/MtpModules`; - // console.log("Module posted (mtp)"); - // return this.http.post(apiURL, ontologyFile); - // const testString="testtesttest"; - // //return this.http.post(apiURL,testString); - // } addMtpModule(ontologyFile: File): Observable{ const apiURL = `${this.apiRoot}/Mtp`; const formData= new FormData; diff --git a/frontend/src/shared/services/skill.service.ts b/frontend/src/shared/services/skill.service.ts index c1005f09..0e16adcd 100644 --- a/frontend/src/shared/services/skill.service.ts +++ b/frontend/src/shared/services/skill.service.ts @@ -1,6 +1,6 @@ import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; -import { HttpClient } from '@angular/common/http'; +import { HttpClient, HttpHeaders } from '@angular/common/http'; import { map } from 'rxjs/operators'; import { SkillDto } from '@shared/models/skill/Skill'; import { Skill } from '../models/Skill'; @@ -71,7 +71,8 @@ export class SkillService { } addSkill(ontologyString: string): Observable> { const apiURL = `${this.apiRoot}/skills`; - return this.http.post(apiURL, ontologyString); + const headers = new HttpHeaders({"content-type": "text/turtle"}); + return this.http.post(apiURL, ontologyString, {headers: headers}); }