add d3 data formatting & update map view with two links repr

This commit is contained in:
axolotle
2021-03-01 10:12:48 +01:00
parent 52a6f23f8c
commit 9ffc391722
6 changed files with 138 additions and 52 deletions
+2 -2
View File
@@ -1,10 +1,10 @@
<template>
<div id="app">
<header>
<!-- <header>
<h1>
<router-link :to="{ name: 'home' }">En Français</router-link>
</h1>
</header>
</header> -->
<main id="main">
<router-view />
+32 -36
View File
@@ -1,5 +1,8 @@
<template>
<svg width="100%" height="100%" ref="svg">
<svg
width="100%" height="100%"
ref="svg" :id="id"
>
<path
v-for="(item, index) in links"
:key="`link_${index}`"
@@ -7,21 +10,22 @@
stroke="black"
/>
<circle
v-for="(node, index) in nodes"
:key="index"
:cx="node.x"
:cy="node.y"
:class="node.data.class"
:r="node.children ? 7 : 5"
>
<title>{{ node.data.name }}</title>
</circle>
<template v-for="(node, index) in nodes">
<circle
:key="'circle' + index"
:cx="node.x"
:cy="node.y"
:class="node.data.class + (node.data.first ? ' first' : '')"
:r="node.children ? 7 : 5"
>
<title>{{ node.data.title }}</title>
</circle>
<text :key="'text' + index" :x="node.x + 7" :y="node.y-7">{{ node.data.id }}</text>
</template>
</svg>
</template>
<script>
import { hierarchy } from 'd3-hierarchy'
import { line } from 'd3-shape'
import { selectAll } from 'd3-selection'
import { drag } from 'd3-drag'
@@ -39,29 +43,20 @@ export default {
name: 'NodeMap',
props: {
data: { type: Object, required: true }
nodes: { type: Array, required: true },
links: { type: Array, required: true },
id: { type: String, default: 'map' }
},
data () {
return {
width: 100,
height: 100,
h: hierarchy({}),
simulation: forceSimulation(),
lineGenerator: line().x(node => node.x).y(node => node.y)
}
},
computed: {
nodes () {
return this.h.descendants()
},
links () {
return this.h.links()
}
},
methods: {
updateSize () {
const { width, height } = this.$el.getBoundingClientRect()
@@ -71,16 +66,11 @@ export default {
init () {
this.updateSize()
const h = hierarchy(this.data)
this.nodes.forEach(node => {
this.$set(node, 'x', this.width * 0.5)
this.$set(node, 'y', this.height * 0.5)
})
h.descendants().forEach(node =>
Object.assign(node, {
x: this.width * 0.5,
y: this.height * 0.5
})
)
this.h = h
this.setupForces()
// Wait for DOM update so d3 can select
this.$nextTick(this.initListeners)
@@ -88,7 +78,8 @@ export default {
initListeners () {
// TODO replace with vue events ?
selectAll('circle')
selectAll(`#${this.id} circle`)
.data(this.nodes)
.call(drag()
.on('start', this.onNodeDragStart)
@@ -100,8 +91,8 @@ export default {
setupForces () {
this.simulation
.nodes(this.nodes)
.force('link', forceLink(this.links).distance(100))
.force('charge', forceManyBody().strength(-350))
.force('link', forceLink(this.links).id(d => d.id).distance(50))
.force('charge', forceManyBody().strength(-150))
.force('x', forceX())
.force('y', forceY())
.force('center', forceCenter(this.width * 0.5, this.height * 0.5))
@@ -142,6 +133,11 @@ path {
stroke: grey;
}
text {
font-size: 0.7rem;
user-select: none;
}
@each $id in map-keys($families){
.family-#{$id} {
fill: setColorFromId($id);
+75
View File
@@ -0,0 +1,75 @@
import { hierarchy } from 'd3-hierarchy'
function flatten (arr, accumulator = []) {
return arr.reduce((acc, child) => {
acc.push(child)
return child.children ? flatten(child.children, acc) : acc
}, accumulator)
}
function getLinked (text) {
const types = ['text_en_rebond', 'text_produits', 'text_de_depart']
return types.reduce((acc, type) => {
// Handle `null` and `undefined`
return text[type] ? [...acc, ...text[type]] : acc
}, [])
}
export function toSingleManyData (rawData) {
rawData.first = true
const h = hierarchy(rawData, d => getLinked(d))
h.each(node => {
if (node.parent && node.children) {
const id = node.parent.data.id
node.children = node.children.filter(child => child.data.id !== id)
}
})
const nodes = h.descendants()
const links = h.links()
nodes.forEach(node => {
Object.assign(node.data, {
type: node.data.__typename.toLowerCase(),
class: 'family-' + node.data.familles[0].id
})
})
return { nodes, links }
}
export function toManyManyData (rawData) {
rawData.first = true
const h = hierarchy(rawData, d => getLinked(d))
h.each(node => {
if (node.parent && node.children) {
const id = node.parent.data.id
node.children = node.children.filter(child => child.data.id !== id)
}
})
const ids = []
const nodes = []
const links = []
flatten(h.descendants()).filter(node => {
if (!ids.includes(node.data.id)) {
ids.push(node.data.id)
return true
}
return false
}).forEach(({ data, children, depth }) => {
nodes.push({ id: data.id, data })
if (children) {
children.forEach(child => {
links.push({ source: data.id, target: child.data.id })
})
}
})
nodes.forEach(node => {
Object.assign(node.data, {
type: node.data.__typename.toLowerCase(),
class: 'family-' + node.data.familles[0].id
})
})
return { nodes, links }
}
+23 -7
View File
@@ -1,14 +1,17 @@
<template>
<div id="map">
<node-map v-if="data" :data="data" />
<div>
{{ data }}
</div>
<node-map class="node-map" v-if="data" v-bind="one" />
<node-map
class="node-map" v-if="data2" v-bind="two"
id="ùmqsldùml"
/>
</div>
</template>
<script>
import { toSingleManyData, toManyManyData } from '@/helpers/d3Data'
import NodeMap from '@/components/NodeMap'
export default {
name: 'Home',
@@ -18,14 +21,22 @@ export default {
data () {
return {
text: null,
data: null
id: 39,
data: null,
data2: null,
one: { nodes: null, links: null },
two: { nodes: null, links: null }
}
},
created () {
this.$store.dispatch('GET_TREE', 17).then((data) => {
this.$store.dispatch('GET_TREE', this.id).then((data) => {
this.data = data
this.one = toSingleManyData(data)
})
this.$store.dispatch('GET_TREE', this.id).then((data) => {
this.data2 = data
this.two = toManyManyData(data)
})
}
}
@@ -35,5 +46,10 @@ export default {
#map {
height: 100%;
width: 100%;
display: flex;
flex-basis: 50%;
}
.node-map {
width: 50vw;
}
</style>
+5 -5
View File
@@ -8,13 +8,13 @@ const routes = [
{
name: 'home',
path: '/',
component: Home
},
{
name: 'map',
path: '/map',
component: () => import(/* webpackChunkName: "node-map" */ '../pages/Map')
},
// {
// name: 'map',
// path: '/map',
// component: () => import(/* webpackChunkName: "node-map" */ '../pages/Map')
// },
{
name: 'notfound',
path: '/404',
+1 -2
View File
@@ -27,8 +27,7 @@ export default new Vuex.Store({
'GET_TREE' ({ dispatch }, id) {
return api.post('', { query: print(TextdepartRecursive), variables: { id } }).then(({ data }) => {
const text = data.data.textref
return parse(text, text.id)
return data.data.textref
})
}
}