From b29ffd060536aa1f20debc3563a4ade443f518f5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C5=82=20Romaszkin?= Date: Wed, 24 Jun 2020 13:21:25 +0200 Subject: [PATCH] UPDATE 2/2: add graph --- .../src/app/_functions/maked3hierarchy.ts | 2 - .../visualize-forum.component.html | 1 + .../visualize-forum.component.scss | 5 + .../visualize-forum.component.ts | 107 +++++++++++++++++- 4 files changed, 109 insertions(+), 6 deletions(-) diff --git a/frontend/src/app/_functions/maked3hierarchy.ts b/frontend/src/app/_functions/maked3hierarchy.ts index 2bb5d41..0a30cb4 100644 --- a/frontend/src/app/_functions/maked3hierarchy.ts +++ b/frontend/src/app/_functions/maked3hierarchy.ts @@ -4,12 +4,10 @@ import { Post } from '../_interfaces/post'; * Returns hierearchy-like array specified for D3 graph */ export default function makeHierarchy(data: Post[]) { - console.log('In function!'); const tree: Post[] = []; const childOf: any = {}; data.forEach((element) => { const { id, parent } = element; - console.log(parent); childOf[id] = childOf[id] || []; element.children = childOf[id]; if (parent !== 0) { diff --git a/frontend/src/app/main-view/visualize-forum/visualize-forum.component.html b/frontend/src/app/main-view/visualize-forum/visualize-forum.component.html index e69de29..07e181b 100644 --- a/frontend/src/app/main-view/visualize-forum/visualize-forum.component.html +++ b/frontend/src/app/main-view/visualize-forum/visualize-forum.component.html @@ -0,0 +1 @@ +
diff --git a/frontend/src/app/main-view/visualize-forum/visualize-forum.component.scss b/frontend/src/app/main-view/visualize-forum/visualize-forum.component.scss index e69de29..9d69745 100644 --- a/frontend/src/app/main-view/visualize-forum/visualize-forum.component.scss +++ b/frontend/src/app/main-view/visualize-forum/visualize-forum.component.scss @@ -0,0 +1,5 @@ +::ng-deep .link { + fill: none; + stroke: #000; + stroke-width: 2px; +} diff --git a/frontend/src/app/main-view/visualize-forum/visualize-forum.component.ts b/frontend/src/app/main-view/visualize-forum/visualize-forum.component.ts index cca30d9..822bb08 100644 --- a/frontend/src/app/main-view/visualize-forum/visualize-forum.component.ts +++ b/frontend/src/app/main-view/visualize-forum/visualize-forum.component.ts @@ -1,9 +1,16 @@ -import { Component, OnInit, OnDestroy } from '@angular/core'; +import { + Component, + OnInit, + OnDestroy, + ViewChild, + ElementRef, +} from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { GetVisualizationDataService } from '../../_services/get-visualization-data.service'; import { Subscription } from 'rxjs'; -import { concatMap } from 'rxjs/operators'; +import { concatMap, finalize } from 'rxjs/operators'; import { Post } from 'src/app/_interfaces/post'; +import * as d3 from 'd3'; import maked3hierarchy from '../../_functions/maked3hierarchy'; @Component({ @@ -14,6 +21,9 @@ import maked3hierarchy from '../../_functions/maked3hierarchy'; export class VisualizeForumComponent implements OnInit, OnDestroy { private subscription: Subscription; private data: Post[]; + private hierarchizedData: Post[]; + @ViewChild('tree', { read: ElementRef }) + private treeContainer: ElementRef; constructor( private route: ActivatedRoute, @@ -24,12 +34,101 @@ export class VisualizeForumComponent implements OnInit, OnDestroy { this.subscription = this.route.params .pipe(concatMap((params) => this.getDataService.getDiscussion(params.id))) .subscribe((result) => { - this.data = maked3hierarchy(result.posts); - console.log(this.data); + this.initializeData(result.posts); + this.generateGraph(); }); } ngOnDestroy(): void { this.subscription.unsubscribe(); } + + private initializeData(data: Post[]): void { + this.data = data; + this.hierarchizedData = maked3hierarchy(data); + } + + private generateGraph(): void { + /* ToDo: Add option to change sizes */ + const margin = { top: 50, right: 90, bottom: 30, left: 90 }; + const width = 660 - margin.left - margin.right; + const height = 500 - margin.top - margin.bottom; + + const hierarchizedNodes = d3.hierarchy(this.hierarchizedData[0]); + d3.tree().size([width, height])(hierarchizedNodes); + const element = this.treeContainer.nativeElement; + + /* Clear previous graph */ + d3.select(element).select('svg').remove(); + + /* Create SVG */ + const svg = d3 + .select(element) + .append('svg') + .attr('width', width + margin.left + margin.right) + .attr('height', height + margin.top + margin.bottom); + + const group = svg + .append('g') + .attr('transform', `translate(${margin.left}, ${margin.top})`); + + const gLink = group.append('g').attr('class', 'links'); + const gNode = group.append('g').attr('class', 'nodes'); + + /* Custom tooltip */ + const tooltip = d3 + .select(element) + .append('div') + .attr('class', 'tooltip') + .style('position', 'absolute') + .style('opacity', 0); + + /* Select all nodes and pass data into them */ + const node = gNode + .selectAll('g.nodes') + .data(hierarchizedNodes.descendants()); + + /* Select all links and pass data into them */ + const link = gLink.selectAll('g.links').data(hierarchizedNodes.links()); + + /* 'Enter' into node, style it properly and create tooltip on hover */ + const nodeEnter = node.enter().append('g').classed('node', true); + nodeEnter + .append('circle') + .attr('cx', (d: any) => d.x) + .attr('cy', (d: any) => d.y) + .attr('r', 25) + .style('fill', '#fff') + .style('stroke', '#ccc'); + + nodeEnter + .append('text') + .attr('x', (d: any) => d.x) + .attr('y', (d: any) => d.y) + .attr('text-anchor', 'middle') + .text((d: any) => d.data.id); + + nodeEnter + .on('mouseover', (d) => { + tooltip.transition().duration(400).style('opacity', 1); + tooltip + .html(`Wiadomość:
${d.data.message}`) + .style('width', '15rem') + .style('background-color', '#fff') + .style('border', '1px solid black') + .style('left', `${d3.event.pageX + 40}px`) + .style('top', `${d3.event.pageY - 10}px`); + }) + .on('mouseout', function (d) { + tooltip.transition().duration(400).style('opacity', 0); + }); + + /* 'Enter' into links and style them properly */ + const linkEnter = link.enter().append('line').classed('link', true); + linkEnter + .attr('x1', (d: any) => d.source.x) + .attr('y1', (d: any) => d.source.y) + .attr('x2', (d: any) => d.target.x) + .attr('y2', (d: any) => d.target.y); + } }