My d3 graph is looking great - it's beautiful apart from one thing - the x position of my circles does not seem to correspond very closely with the x values given in my array.
I have an array of objects which are like this:
{
x: 2020,
cx: 0,
colour: "#F25F5C",
y1: 0,
y2: 200,
rad: 10,
amt: 5000
},
I have two sets of circles. The first is positioned really close to the xaxis and indicates an exact date, and their colours match those of larger circles, whose radii vary across a larger scale and I apply d3.force to these to position them more effectively.
The trouble is that the larger circles should be positioned with their centres close to values in the array given by (x, y2). They are not though, and my forceX doesn't seem to be working.
Here is my complete code:
import React, {useState, useEffect} from 'react';
import * as d3 from 'd3';
import './App.css';
function App() {
const [maxYear, setMaxYear] = useState();
const [data, setData] = useState(
[{
x: 2020,
colour: "#69306D",
y1: 0,
y2: 50,
rad: 10,
amt: 2000
},
{
x: 2020,
colour: "#247BA0",
y1: 0,
y2: 100,
rad: 10,
amt: 5000
},
{
x: 2020,
colour: "#3F762C",
y1: 0,
y2: 150,
rad: 10,
amt: 7500
},
{
x: 2020,
colour: "#F25F5C",
y1: 0,
y2: 200,
rad: 10,
amt: 5000
},
{
x: 2022,
colour: "#0C3957",
y1: 0,
y2: 250,
rad: 10,
amt: 9000
},
{
x: 2055,
colour: "#BF802F",
y1: 0,
y2: 300,
rad: 10,
amt: 25000
}
]
);
const thisYear = new Date().getFullYear()
const initialiseData = () => {
const svg = d3.select( "svg" );
const pxX = svg.attr( "width" );
const pxY = svg.attr( "height" );
let tickLabelOffset = 170;
let minDotX = Math.min.apply(Math, data.map(function(o) { return o.y1; }))
if (minDotX < -20) {
tickLabelOffset += minDotX + 20;
}
const makeScale = ( arr, accessor, range ) => {
// console.log("RANGE", accessor, range)
return d3.scaleLinear()
.domain( d3.extent( arr, accessor ) )
.range( range )
.nice()
}
//SCALES//
//-------------------------------------------------------------------------------------------------//
const scX = makeScale( data, d => d.x, [0, pxX - 200]);
const scY = d3.scaleLinear()
.domain([0, 100])
.range( [0, 100] );
const rad = d3.scaleLinear() //scale for radius of small dots on xaxis
.domain(d3.extent(data, d => d.rad))
.range([3, 10]);
const amt = d3.scaleLinear() //scale for radius of large circle .domain(d3.extent(data, d => d.amt))
.domain(d3.extent(data, d => d.amt))
.range([20, 150]);
//-------------------------------------------------------------------------------------------------//
//END OF SCALES//
//stacks small dots on x axis
for (let dotindex=0; dotindex<data.length; dotindex++) {
if (data[dotindex - 1]) {
if (data[dotindex - 1].x === data[dotindex].x) {
data[dotindex].y1 = data[dotindex -1].y1 -20
}
}
}
//creates array of multiples of ten for x axis labels
let tickTens = [];
for (let i=thisYear; i<maxYear; i++) {
if (i % 10 === 0) {
tickTens.push(i)
}
}
//maps array of multiples of ten to values at ticks on x axis
const g = d3.axisBottom( scX ).tickValues(
tickTens.map((tickVal) => {
return tickVal
})
)
//Groups data into arrays by goal year
const _data = data.reduce(
(r, v, _, __, k = v["x"]) => ((r[k] || (r[k] = [])).push(v), r),
[]
)
//CREATE X AXIS//
//---------------------------------------------------------------------------//
svg.append( "g" )
.attr( "transform", "translate(" + 50 + "," + (pxY - 200) + ")")
.call( g )
.selectAll(".tick text")
.attr("fill", "#7A7A7A")
svg.selectAll(".domain")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "2px")
.attr( "transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick line")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "4px")
.attr( "transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll( ".tick text")
.attr("font-size", 20)
.attr( "transform", "translate(" + 50 + "," + tickLabelOffset + ")")
.attr("font-weight", "bold")
.attr("dy", "0.5em")
svg.selectAll("circle")
.data(data)
.enter()
.append("g")
.attr("class", "circles")
.append("circle")
.attr( "transform", "translate(" + 100 + "," + 650 + ")")
.attr("fill", "white")
.attr("stroke", d => d.colour)
.attr("stroke-width", "2px")
.attr("cx", d => scX(d.x))
.attr("cy", d => scY(d.y1))
.attr("r", d => rad(d.rad));
//END OF CREATE X AXIS//
//---------------------------------------------------------------------------//
//CREATE LARGE CIRCLES//
//---------------------------------------------------------------------------//
const ticked = () => {
goalAmounts
.attr("cx", (d => (2 * d.x)))
.attr("cy", d => d.y2);
}
let i = 0;
const goalAmounts = svg.selectAll("circle .circles")
.append( "g" )
.attr("class", "goalAmounts")
.data(data.sort(function(a, b) {
return a.amt - b.amt;
}).reverse())
.enter()
.append("circle")
.attr( "transform", "translate(" + 650 + "," + 0 + ")")
.attr("fill", d => {
return d.colour}
)
.attr("cx", d => (d.x))
.attr("cy", (d, index) => {
let _y = scY(d.y2)
if (_data[d.x].length > 1 && _data[i-1]) {
i++
_y = _data[d.x][i -1].y2
} else {
i = 0
}
return _y
})
.attr("r", d => amt(d.amt));
d3.forceSimulation(data)
// .force('charge', d3.forceManyBody().strength(-200))
.force('x', scX(d3.forceX().x(function(d) {
return d.x
})))
.force("y", d3.forceY(d => (d.y2 * Math.random())))
.force('collision', d3.forceCollide().radius(d => amt(d.amt/2)))
.on("tick", ticked);
//END OF CREATE LARGE CIRCLES//
//---------------------------------------------------------------------------//
}
useEffect(() => {
if (data) {
setMaxYear(Math.max.apply(Math, data.map(function(o) { return o.x; })))
}
if (maxYear) {
initialiseData();
}
}, [data,maxYear])
return (
<div className="App">
<svg id="demo1" width="1200" height="700" style={{background: "white"}}/>
</div>
);
}
export default App;
Please see how the code works so far with this sandbox: d3Sandbox
Here is the code for a working version I think?
import React, {useState, useEffect} from 'react';
import * as d3 from 'd3';
import './App.css';
function App() {
const [maxYear, setMaxYear] = useState();
const [data, setData] = useState(
[{
x1: 2020,
colour: "#69306D",
y1: 0,
y2: 50,
rad: 10,
amt: 2000
},
{
x1: 2021,
colour: "#247BA0",
y1: 0,
y2: 100,
rad: 10,
amt: 5000
},
{
x1: 2030,
colour: "#3F762C",
y1: 0,
y2: 150,
rad: 10,
amt: 7500
},
{
x1: 2020,
colour: "#F25F5C",
y1: 0,
y2: 200,
rad: 10,
amt: 5000
},
{
x1: 2022,
colour: "#0C3957",
y1: 0,
y2: 250,
rad: 10,
amt: 9000
},
{
x1: 2055,
colour: "#BF802F",
y1: 0,
y2: 300,
rad: 10,
amt: 25000
}
]
);
const thisYear = new Date().getFullYear()
const initialiseData = () => {
const svg = d3.select( "svg" );
const pxX = svg.attr( "width" );
const pxY = svg.attr( "height" );
let tickLabelOffset = 170;
let minDotX = Math.min.apply(Math, data.map(function(o) { return o.y1; }))
if (minDotX < -20) {
tickLabelOffset += minDotX + 20;
}
const makeScale = ( arr, accessor, range ) => {
// console.log("RANGE", accessor, range)
return d3.scaleLinear()
.domain( d3.extent( arr, accessor ) )
.range( range )
.nice()
}
//SCALES//
//-------------------------------------------------------------------------------------------------//
const scX = makeScale( data, d => d.x1, [0, pxX - 200]);
const scX2 = d3.scaleLinear()
.domain([2020, 2080])
.range( [0, pxX - 200] );
const scY = d3.scaleLinear()
.domain([0, 100])
.range( [0, 100] );
const rad = d3.scaleLinear() //scale for radius of small dots on xaxis
.domain(d3.extent(data, d => d.rad))
.range([3, 10]);
const amt = d3.scaleLinear() //scale for radius of large circle .domain(d3.extent(data, d => d.amt))
.domain(d3.extent(data, d => d.amt))
.range([20, 150]);
//-------------------------------------------------------------------------------------------------//
//END OF SCALES//
//stacks small dots on x axis
let _newData = data;
for (let dotindex=0; dotindex<data.length; dotindex++) {
if (_newData[dotindex - 1]) {
if (_newData[dotindex - 1].x1 === _newData[dotindex].x1) {
_newData[dotindex].y1 = _newData[dotindex -1].y1 -20
}
}
}
setData(_newData)
console.log("DATA: ", data)
//creates array of multiples of ten for x axis labels
let tickTens = [];
for (let i=thisYear; i<maxYear; i++) {
if (i % 10 === 0) {
tickTens.push(i)
}
}
//maps array of multiples of ten to values at ticks on x axis
const g = d3.axisBottom( scX ).tickValues(
tickTens.map((tickVal) => {
return tickVal
})
)
//Groups data into arrays by goal year
const _data = data.reduce(
(r, v, _, __, k = v["x"]) => ((r[k] || (r[k] = [])).push(v), r),
[]
)
//CREATE X AXIS//
//---------------------------------------------------------------------------//
svg.append( "g" )
.attr( "transform", "translate(" + 50 + "," + (pxY - 200) + ")")
.call( g )
.selectAll(".tick text")
.attr("fill", "#7A7A7A")
svg.selectAll(".domain")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "2px")
.attr( "transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll(".tick line")
.attr("stroke", "#BDBDBD")
.attr("stroke-width", "4px")
.attr( "transform", "translate(" + 50 + "," + 150 + ")")
svg.selectAll( ".tick text")
.attr("font-size", 20)
.attr( "transform", "translate(" + 50 + "," + tickLabelOffset + ")")
.attr("font-weight", "bold")
.attr("dy", "0.5em")
svg.selectAll("circle")
.data(data)
.enter()
.append("g")
.attr("class", "xDots")
.append("circle")
.attr( "transform", "translate(" + 100 + "," + 650 + ")")
.attr("fill", "white")
.attr("stroke", d => d.colour)
.attr("stroke-width", "2px")
.attr("cx", d => scX(d.x1))
.attr("cy", d => scY(d.y1))
.attr("r", d => rad(d.rad));
//END OF CREATE X AXIS//
//---------------------------------------------------------------------------//
//CREATE LARGE CIRCLES//
//---------------------------------------------------------------------------//
const ticked = () => {
goalAmounts
.attr("cx", (d => d.x))
.attr("cy", d => d.y);
}
let i = 0;
const goalAmounts = svg.selectAll()
.data(data.sort(function(a, b) {
return a.amt - b.amt;
}).reverse())
.enter()
.append("g")
.attr("class", "goals")
.append("circle")
.attr( "transform", "translate(" + 200 + "," + 100 + ")")
.attr("fill", d => {
return d.colour}
)
.attr("cx", d => {
// console.log("DX1, ", d.x1, (d.x1)
return d.x1
})
.attr("cy", (d, index) => {
let _y = scY(d.y2)
}
)
.attr("r", d => amt(d.amt));
d3.forceSimulation(data)
// .force('charge', d3.forceManyBody().strength(-20))
.force('x', (d3.forceX().x(function(d) {
return scX2(d.x1)
})))
.force("y", d3.forceY(d => (d.y2 * Math.random())))
.force('collision', d3.forceCollide().radius(d => amt(d.amt) + 10))
.on("tick", ticked);
//END OF CREATE LARGE CIRCLES//
//---------------------------------------------------------------------------//
}
useEffect(() => {
if (data) {
console.log("DATA: ", data)
setMaxYear(Math.max.apply(Math, data.map(function(o) { return o.x1; })))
}
if (maxYear) {
console.log("MAXYEAR", maxYear)
initialiseData();
}
}, [data, maxYear])
return (
<div className="App">
<svg id="demo1" width="1200" height="700" style={{background: "white"}}/>
</div>
);
}
export default App;
Related
I am stuck badly while trying to show negative values in sunburst chart. whenever I change the value in json file as negative the slice just gets hidden. I want the arc to show even if my arc has negative value.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="//d3js.org/d3.v7.min.js"></script>
</head>
<body>
<script>
chart = () => {
var color = d3.scaleOrdinal()
.range(['red', 'green', 'lightgreen']);
const root = partition(data);
root.each(d => d.current = d);
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, width])
.style("font", "11px sans-serif")
// .style('fill', 'black')
console.log(svg)
const g = svg.append("g")
.attr("transform", `translate(${width / 2.5},${width / 2})`);
const path = g.append("g")
.selectAll("path")
.data(root.descendants().slice(1))
.join("path")
.attr('fill', function (d, i, value) {
if (d.data.value > 10) {
return 'green'
} else if (d.data.value < 5) {
return 'red'
}
else if (d.data.value < 1) {
return 'lightgreen'
}
return color(i)
})
.attr("fill-opacity", d => arcVisible(d.current) ? (d.value ? 0.7 : 0.4) : 0)
.attr("d", d => arc(d.current));
path.filter(d => d.children)
.style("cursor", "pointer")
.on("click", clicked);
path.append("title")
.text(d => `${d.ancestors().map(d => d.data.name).reverse().join("-")} : ${format(d.value)} points`);
path.append("info")
.text(d => `${d.ancestors().map(d => d.data.size).reverse().join("/")}\n${format(d.value)}`);
const label = g.append("g")
.attr("pointer-events", "none")
.attr("text-anchor", "middle")
.style("user-select", "none")
.selectAll("text")
.data(root.descendants().slice(1))
.join("text")
.attr("dy", "0.35em")
.attr("fill-opacity", d => +labelVisible(d.current))
.attr("transform", d => labelTransform(d.current))
.text(d => d.data.name + '\n' + d.data.value);
const parent = g.append("circle")
.datum(root)
.attr("r", radius)
.attr("fill", "green")
.attr("pointer-events", "all")
.on("click", clicked);
function clicked(event, p) {
parent.datum(p.parent || root);
root.each(d => d.target = {
x0: Math.max(0, Math.min(1, (d.x0 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
x1: Math.max(0, Math.min(1, (d.x1 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
y0: Math.max(0, d.y0 - p.depth),
y1: Math.max(0, d.y1 - p.depth)
});
const t = g.transition().duration(1050);
// Transition the data on all arcs, even the ones that aren’t visible,
// so that if this transition is interrupted, entering arcs will start
// the next transition from the desired position.
path.transition(t)
.tween("data", d => {
const i = d3.interpolate(d.current, d.target);
return t => d.current = i(t);
})
.filter(function (d) {
return +this.getAttribute("fill-opacity") || arcVisible(d.target);
})
.attr("fill-opacity", d => arcVisible(d.target) ? (d.children ? 0.6 : 0.4) : 0)
.attrTween("d", d => () => arc(d.current));
label.filter(function (d) {
return +this.getAttribute("fill-opacity") || labelVisible(d.target);
}).transition(t)
.attr("fill-opacity", d => +labelVisible(d.target))
.attrTween("transform", d => () => labelTransform(d.current));
}
function arcVisible(d) {
return d.y1 <= 3 && d.y0 >= 1 && d.x1 > d.x0;
}
function labelVisible(d) {
return d.y1 <= 3 && d.y0 >= 1 && (d.y1 - d.y0) * (d.x1 - d.x0) > 0.03;
}
function labelTransform(d) {
const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
const y = (d.y0 + d.y1) / 2 * radius;
return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
}
return svg.node();
}
adder = 10
data = {
"name": "KSE100",
"children": [
{
"name": "TEXTILE",
'value': 10,
"children": [
{
"name": "COST",
"value": 4,
// 'points': 4
},
{
"name": "COOT",
"value": 3,
// 'points': 5
},
{
"name": "AHTM",
"value": "-3",
// 'points': 5
},
]
},
{
"name": "OIL",
'value': 8,
"children": [
{
"name": "PSO",
"value": 2,
//'points': -20
},
{
"name": "BYCO",
"value": 6,
// 'points': -20
},
{
"name": "MPCL",
"value": 2,
//'points': 10
},
]
},
/
]
}
partition = data => {
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.value - a.value);
return d3.partition()
.size([2 * Math.PI, root.height + 1])
(root);
}
// color = d3.scaleOrdinal(d3.quantize(d3.interpolateRainbow, data.children.length + 1))
format = d3.format(",d")
width = 900
radius = width / 10
arc = d3.arc()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.padAngle(d => Math.min((d.x1 - d.x0) / 2, 0.005))
.padRadius(radius * 1.5)
.innerRadius(d => d.y0 * radius)
.outerRadius(d => Math.max(d.y0 * radius, d.y1 * radius - 1))
const svg = d3.select('body').append(chart)
</script>
</body>
</html>
I have been stuck in this since ages, I would be really thankfull if any of you can help me with this.
I am recieving this error:
*Uncaught TypeError: d3__WEBPACK_IMPORTED_MODULE_4__.drag(...).origin is not a function*
113 | .data(targetPoints)
114 | .enter().append("circle")
115 | .attr("class", "handle")
> 116 | .attr("transform", function(d) { return "translate(" + d + ")"; })
| ^ 117 | .attr("r", 7)
118 | .call(d3.drag()
119 | .origin(function() {
I am trying to make a React app where I can upload a picture, which then later can be dragged and prespective transformed using SVG. I am building it off this code https://bl.ocks.org/mbostock/10571478Z.
...
const DoorPreviewer = ({doorHook}) => {
const {background} = doorHook;
useEffect(() => {
var margin = {top: 50, right: 280, bottom: 50, left: 280},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var transform = ["", "-webkit-", "-moz-", "-ms-", "-o-"].reduce(
function(p, v) { return v + "transform" in document.body.style ? v : p; }
) + "transform";
var sourcePoints = [[0, 0], [width, 0], [width, height], [0, height]],
targetPoints = [[0, 0], [width, 0], [width, height], [0, height]];
d3.select("body").selectAll("svg")
.data(["transform", "flat"])
.enter().append("svg")
.attr("id", function(d) { return d; })
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var svgTransform = d3.select("#transform")
.style(transform + "-origin", margin.left + "px " + margin.top + "px 0");
var svgFlat = d3.select("#flat");
svgTransform.select("g").append("image")
.attr("xlink:href", "sailboat.png")
.attr("width", width)
.attr("height", height);
svgTransform.select("g").selectAll(".line--x")
.data(d3.range(0, width + 1, 40))
.enter().append("line")
.attr("class", "line line--x")
.attr("x1", function(d) { return d; })
.attr("x2", function(d) { return d; })
.attr("y1", 0)
.attr("y2", height);
svgTransform.select("g").selectAll(".line--y")
.data(d3.range(0, height + 1, 40))
.enter().append("line")
.attr("class", "line line--y")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", function(d) { return d; })
.attr("y2", function(d) { return d; });
var handle = svgFlat.select("g").selectAll(".handle")
.data(targetPoints)
.enter().append("circle")
.attr("class", "handle")
.attr("transform", function(d) { return "translate(" + d + ")"; })
.attr("r", 7)
.call(d3.drag()
.origin(function() {
var d = d3.select(this)
return {x: d[0], y: d[1]}; })
.on("drag", dragged));
function dragged(evt, d) {
d3.select(this).attr("transform", "translate(" + (d[0] = evt.x) + "," + (d[1] = evt.y) + ")");
transformed();
}
function transformed() {
for (var a = [], b = [], i = 0, n = sourcePoints.length; i < n; ++i) {
var s = sourcePoints[i], t = targetPoints[i];
a.push([s[0], s[1], 1, 0, 0, 0, -s[0] * t[0], -s[1] * t[0]]);
b.push(t[0]);
a.push([0, 0, 0, s[0], s[1], 1, -s[0] * t[1], -s[1] * t[1]]);
b.push(t[1]);
}
var X = solve(a, b, true), matrix = [
X[0], X[3], 0, X[6],
X[1], X[4], 0, X[7],
0, 0, 1, 0,
X[2], X[5], 0, 1
].map(function(x) {
return d3.format(x)(6);
});
svgTransform.style(transform, "matrix3d(" + matrix + ")");
}
}, [])
return (
<DoorPreviewBackground bg={background}>
</DoorPreviewBackground>
);
}
export {Display};
Does anyone know why this happens?
I am trying to add data points to my line chart with multiple y axes. Click here for my fiddle.
//after restructuring dataset array
var data = [{
data: [{
x: 0,
y: 0
}, {
x: 10,
y: 10
}, {
x: 20,
y: 20
}, {
x: 30,
y: 30
}, {
x: 40,
y: 40
}],
yAxis: 0,
}, {
data: [{
x: 0,
y: 0
}, {
x: 10,
y: 200
}, {
x: 20,
y: 300
}, {
x: 30,
y: 400
}, {
x: 40,
y: 500
}],
yAxis: 1,
}];
const margin = {
left: 20,
right: 20,
top: 20,
bottom: 80
};
const svg = d3.select('svg');
svg.selectAll("*").remove();
const width = 200 - margin.left - margin.right;
const height = 200 - margin.top - margin.bottom;
const g = svg.append('g').attr('transform', `translate(${80},${margin.top})`);
//************* Axes and Gridlines ***************
const xAxisG = g.append('g');
const yAxisG = g.append('g');
xAxisG.append('text')
.attr('class', 'axis-label')
.attr('x', width / 3)
.attr('y', -10)
.style('fill', 'black')
.text(function(d) {
return "X Axis";
});
yAxisG.append('text')
.attr('class', 'axis-label')
.attr('id', 'yAxisLabel0')
.attr('x', -height / 2)
.attr('y', -15)
.attr('transform', `rotate(-90)`)
.style('text-anchor', 'middle')
.style('fill', 'black')
.text(function(d) {
return "Y Axis 1";
});
// interpolator for X axis -- inner plot region
var x = d3.scaleLinear()
.domain([0, d3.max(xValueArray)])
.range([0, width])
.nice();
var yScale = new Array();
for (var i = 0; i < 2; i++) {
// interpolator for Y axis -- inner plot region
var y = d3.scaleLinear()
.domain([0, d3.max(arr[i])])
.range([0, height])
.nice();
yScale.push(y);
}
const xAxis = d3.axisTop()
.scale(x)
.ticks(5)
.tickPadding(2)
.tickSize(-height)
const yAxis = d3.axisLeft()
.scale(yScale[0])
.ticks(5)
.tickPadding(2)
.tickSize(-width);
yAxisArray = new Array();
yAxisArray.push(yAxis);
for (var i = 1; i < 2; i++) {
var yAxisSecondary = d3.axisLeft()
.scale(yScale[i])
.ticks(5)
yAxisArray.push(yAxisSecondary);
}
svg.append("g")
.attr("class", "x axis")
.attr("transform", `translate(80,${height-80})`)
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("id", "ySecAxis0")
.attr("transform", "translate(80,20)")
.call(yAxis);
var translation = 50;
var textTranslation = 0;
var yLabelArray = ["Y Axis 1", "Y Axis 2"];
//loop starts from 1 as primary y axis is already plotted
for (var i = 1; i < 2; i++) {
svg.append("g")
.attr("transform", "translate(" + translation + "," + 20 + ")")
.attr("id", "ySecAxis" + i)
.call(yAxisArray[i]);
yAxisG.append('text')
.attr('x', -height / 2)
.attr('y', -60)
.attr('transform', `rotate(-90)`)
.attr("id", "yAxisLabel" + i)
.style('text-anchor', 'middle')
.style('fill', 'black')
.text(yLabelArray[i]);
translation -= 40;
textTranslation += 40;
}
//************* Lines and Data Points ***************
var colors = ["blue", "red"];
var thisScale;
var line = d3.line()
.x(d => x(d.x))
.y(d => thisScale(d.y))
.curve(d3.curveLinear);
var paths = g.selectAll("foo")
.data(data)
.enter()
.append("path");
paths.attr("stroke", function (d,i){return colors[i]})
.attr("d", d => {
thisScale = yScale[d.yAxis]
return line(d.data);
})
.attr("stroke-width", 2)
.attr("id", function (d,i){return "line" + i})
.attr("fill", "none");
var points = g.selectAll("dot")
.data(data)
.enter()
.append("circle");
points.attr("cx", function(d) { return x(d.x)} )
.attr("cy", function(d,i) { return yScale[i](d.y); } )
.attr("r", 3)
.attr("class", function (d,i){return "blackDot" + i})
.attr("clip-path", "url(#clip)")
Right now the console log is showing these errors: Error: attribute cx: Expected length, "NaN". Error: attribute cy: Expected length, "NaN". It seems like I am not attributing the correct cx and cy to points, but I can't figure out what I am doing wrongly. Any help is greatly appreciated!
Your data structure is an array of objects, each one containing an inner array with the real coordinates for the circles. Therefore, that single enter selection will not work.
With minimal refactoring, my solution here is appending groups according to the objects, and then, for each one, appending circles according to the inner arrays. For that cumbersome yScale to work you cannot rely on the circle's indices anymore, so I'm using a local variable here:
var pointsGroup = g.selectAll(null)
.data(data)
.enter()
.append("g")
.attr("fill", function(d, i) {
local.set(this, yScale[i])
return colors[i];
});
var points = pointsGroup.selectAll(null)
.data(function(d) {
return d.data
})
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d.x)
})
.attr("cy", function(d, i) {
return local.get(this)(d.y);
})
//etc...
Here is the code with those changes:
var local = d3.local();
var xValueArray = [0, 10, 20, 30, 40];
var arr = [
[0, 10, 20, 30, 40],
[0, 200, 300, 400, 500]
];
var dataset = [
[{
x: 0,
y: 0
}, {
x: 10,
y: 10
}, {
x: 20,
y: 20
}, {
x: 30,
y: 30
}, {
x: 40,
y: 40
}],
[{
x: 0,
y: 0
}, {
x: 10,
y: 200
}, {
x: 20,
y: 300
}, {
x: 30,
y: 400
}, {
x: 40,
y: 500
}]
];
var data = [];
for (var i = 0; i < 2; i++) {
data.push({
"data": dataset[i],
"yAxis": i
})
}
console.log(data);
//after restructuring dataset array
var data = [{
data: [{
x: 0,
y: 0
}, {
x: 10,
y: 10
}, {
x: 20,
y: 20
}, {
x: 30,
y: 30
}, {
x: 40,
y: 40
}],
yAxis: 0,
}, {
data: [{
x: 0,
y: 0
}, {
x: 10,
y: 200
}, {
x: 20,
y: 300
}, {
x: 30,
y: 400
}, {
x: 40,
y: 500
}],
yAxis: 1,
}];
const margin = {
left: 20,
right: 20,
top: 20,
bottom: 80
};
const svg = d3.select('svg');
svg.selectAll("*").remove();
const width = 200 - margin.left - margin.right;
const height = 200 - margin.top - margin.bottom;
const g = svg.append('g').attr('transform', `translate(${80},${margin.top})`);
//************* Axes and Gridlines ***************
const xAxisG = g.append('g');
const yAxisG = g.append('g');
xAxisG.append('text')
.attr('class', 'axis-label')
.attr('x', width / 3)
.attr('y', -10)
.style('fill', 'black')
.text(function(d) {
return "X Axis";
});
yAxisG.append('text')
.attr('class', 'axis-label')
.attr('id', 'yAxisLabel0')
.attr('x', -height / 2)
.attr('y', -15)
.attr('transform', `rotate(-90)`)
.style('text-anchor', 'middle')
.style('fill', 'black')
.text(function(d) {
return "Y Axis 1";
});
// interpolator for X axis -- inner plot region
var x = d3.scaleLinear()
.domain([0, d3.max(xValueArray)])
.range([0, width])
.nice();
var yScale = new Array();
for (var i = 0; i < 2; i++) {
// interpolator for Y axis -- inner plot region
var y = d3.scaleLinear()
.domain([0, d3.max(arr[i])])
.range([0, height])
.nice();
yScale.push(y);
}
const xAxis = d3.axisTop()
.scale(x)
.ticks(5)
.tickPadding(2)
.tickSize(-height)
const yAxis = d3.axisLeft()
.scale(yScale[0])
.ticks(5)
.tickPadding(2)
.tickSize(-width);
yAxisArray = new Array();
yAxisArray.push(yAxis);
for (var i = 1; i < 2; i++) {
var yAxisSecondary = d3.axisLeft()
.scale(yScale[i])
.ticks(5)
yAxisArray.push(yAxisSecondary);
}
svg.append("g")
.attr("class", "x axis")
.attr("transform", `translate(80,${height-80})`)
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.attr("id", "ySecAxis0")
.attr("transform", "translate(80,20)")
.call(yAxis);
var translation = 50;
var textTranslation = 0;
var yLabelArray = ["Y Axis 1", "Y Axis 2"];
//loop starts from 1 as primary y axis is already plotted
for (var i = 1; i < 2; i++) {
svg.append("g")
.attr("transform", "translate(" + translation + "," + 20 + ")")
.attr("id", "ySecAxis" + i)
.call(yAxisArray[i]);
yAxisG.append('text')
.attr('x', -height / 2)
.attr('y', -60)
.attr('transform', `rotate(-90)`)
.attr("id", "yAxisLabel" + i)
.style('text-anchor', 'middle')
.style('fill', 'black')
.text(yLabelArray[i]);
translation -= 40;
textTranslation += 40;
}
//************* Mouseover ***************
var tooltip = d3.select("body")
.append("div")
.style("opacity", 0)
.attr("class", "tooltip")
.style("background-color", "white")
.style("border", "solid")
.style("border-width", "1px")
.style("border-radius", "5px")
.style("padding", "10px")
.style("position", "absolute")
var mouseover = function(d) {
tooltip
.html("x: " + d.x + "<br/>" + "y: " + d.y)
.style("opacity", 1)
.style("left", (d3.mouse(this)[0] + 90) + "px")
.style("top", (d3.mouse(this)[1]) + "px")
}
// A function that change this tooltip when the leaves a point: just need to set opacity to 0 again
var mouseleave = function(d) {
tooltip
.transition()
.duration(200)
.style("opacity", 0)
}
//************* Lines and Data Points ***************
var colors = ["blue", "red"];
var thisScale;
var line = d3.line()
.x(d => x(d.x))
.y(d => thisScale(d.y))
.curve(d3.curveLinear);
var paths = g.selectAll("foo")
.data(data)
.enter()
.append("path");
paths.attr("stroke", function(d, i) {
return colors[i]
})
.attr("d", d => {
thisScale = yScale[d.yAxis]
return line(d.data);
})
.attr("stroke-width", 2)
.attr("id", function(d, i) {
return "line" + i
})
.attr("fill", "none");
var pointsGroup = g.selectAll(null)
.data(data)
.enter()
.append("g")
.attr("fill", function(d, i) {
local.set(this, yScale[i])
return colors[i];
});
var points = pointsGroup.selectAll(null)
.data(function(d) {
return d.data
})
.enter()
.append("circle")
.attr("cx", function(d) {
return x(d.x)
})
.attr("cy", function(d, i) {
return local.get(this)(d.y);
})
.attr("r", 3)
.attr("class", function(d, i) {
return "blackDot" + i
})
.attr("clip-path", "url(#clip)")
.on("mouseover", mouseover)
.on("mouseleave", mouseleave)
//plot lines (hard-coding)
/*var lineFunction1 = d3.line()
.x(function(d) {
return x(d.x);
})
.y(function(d) {
return yScale[0](d.y);
})
.curve(d3.curveLinear);
var lineFunction2 = d3.line()
.x(function(d) {
return x(d.x);
})
.y(function(d) {
return yScale[1](d.y);
})
.curve(d3.curveLinear);
var path1 = g.append("path")
.attr("class", "path" + 0)
.attr("id", "line" + 0)
.attr("d", lineFunction1(data[0]))
.attr("stroke", "blue")
.attr("stroke-width", 2)
.attr("fill", "none")
.attr("clip-path", "url(#clip)");
var path2 = g.append("path")
.attr("class", "path" + 1)
.attr("id", "line" + 1)
.attr("d", lineFunction2(data[1]))
.attr("stroke", "red")
.attr("stroke-width", 2)
.attr("fill", "none")
.attr("clip-path", "url(#clip)");*/
//plot lines and points using for loop
/*for (var i = 0; i < 2; i++) {
var lineFunction = d3.line()
.x(function(d) {
return x(d.x);
})
.y(function(d) {
return yScale[i](d.y);
})
.curve(d3.curveLinear);
var paths = g.append("path")
.attr("class", "path" + i)
.attr("id", "line" + i)
.attr("d", lineFunction(data[i]))
.attr("stroke", colors[i])
.attr("stroke-width", 2)
.attr("fill", "none")
.attr("clip-path", "url(#clip)")
//plot a circle at each data point
g.selectAll(".dot")
.data(data[i])
.enter().append("circle")
.attr("cx", function(d) { return x(d.x)} )
.attr("cy", function(d) { return yScale[i](d.y); } )
.attr("r", 3)
.attr("class", "blackDot" + i)
.attr("clip-path", "url(#clip)")
.on("mouseover", mouseover)
.on("mouseleave", mouseleave)
}*/
//************* Legend ***************
var legend = svg.selectAll(".legend")
.data(data)
.enter().append("g")
legend.append("rect")
.attr("x", width + 65)
.attr("y", function(d, i) {
return 30 + i * 20;
})
.attr("width", 18)
.attr("height", 4)
.style("fill", function(d, i) {
return colors[i];
})
legend.append("text")
.attr("x", width + 60)
.attr("y", function(d, i) {
return 30 + i * 20;
})
.attr("dy", ".35em")
.style("text-anchor", "end")
.text(function(d, i) {
return "Value" + (i + 1);
})
.on("click", function(d, i) {
// Determine if current line is visible
let opacity = d3.select("#line" + i).style("opacity");
let newOpacity;
if (opacity == 0) {
newOpacity = 1;
} else {
newOpacity = 0
}
d3.select("#line" + i).style("opacity", newOpacity);
d3.selectAll(".blackDot" + i).style("opacity", newOpacity);
d3.select("#ySecAxis" + i).style("opacity", newOpacity);
d3.select("#yAxisLabel" + i).style("opacity", newOpacity);
});
//************* Zoom & Brush***************
const margin2 = {
left: 80,
right: 0,
top: 80,
bottom: 0
};
const height2 = height - margin2.top - margin2.bottom;
var xZoom = d3.scaleLinear().range([0, width]);
var yZoom = d3.scaleLinear().range([height2, 0]);
var xAxis2 = d3.axisTop(xZoom);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var clip = svg.append("defs").append("svg:clipPath")
.attr("id", "clip")
.append("svg:rect")
.attr("width", width)
.attr("height", height)
.attr("x", 0)
.attr("y", 0);
xZoom.domain(x.domain());
yZoom.domain(y.domain());
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + 125 + ")");
context.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return;
var s = d3.event.selection || xZoom.range();
x.domain(s.map(xZoom.invert, xZoom));
svg.select(".x.axis").call(xAxis);
//svg.select(".path0").attr("d", lineFunction1(data[0]));
//svg.select(".path1").attr("d", lineFunction2(data[1]));
for (var i = 0; i < 2; i++) {
//svg.select(".path" + i).attr("d", lineFunction(data[i]));
g.selectAll(".blackDot" + i)
.attr("cx", function(d) {
return x(d.x);
})
.attr("cy", function(d) {
return yScale[i](d.y);
})
.attr("r", 3)
}
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return;
var t = d3.event.transform;
x.domain(t.rescaleX(xZoom).domain());
svg.select(".x.axis").transiton(t).call(xAxis);
//svg.select(".path0").transiton(t).attr("d", lineFunction1(data[0]));
//svg.select(".path1").transiton(t).attr("d", lineFunction2(data[1]));
for (var i = 0; i < 2; i++) {
//svg.select(".path" + i).attr("d", lineFunction(data[i]));
g.selectAll(".blackDot" + i)
.attr("cx", function(d) {
return x(d.x);
})
.attr("cy", function(d) {
return yScale[i](d.y);
})
.attr("r", 3)
}
}
.xy_chart {
position: relative;
left: 70px;
top: 100px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
<svg class="xy_chart"></svg>
Pay attention to the fact that one of the circles has an incorrect cy value. So, I'd suggest you to change your y scale approach.
I am learning d3.js, and I have this problem:
The following code in d3 basically draws a bar chart, with an update button that is sorting the data once in a descending order and once in ascending order. Also, numeric labels appear on the bars.
I would like to transition the numeric label from the current value to the updated value. For example, if the first bar has a numeric label of 20, and the new updated value after sorting is 100, I would like this label to transition from 20 to 100 as (20, 21, ..., 100) during the specific transition time, and vice versa, if the original label is 100 and the updated value is 20 the transition goes as 100, 99, ..., 20.
I know I can just transition the numeric value with the bar, but I would like to know how to do the transition from the current numeric value to the new update value as an exercise.
const data = [
{key: 0, value: 50},
{key: 1, value: 20},
{key: 2, value: 100},
{key: 3, value: 30},
{key: 4, value: 40},
{key: 5, value: 70}
]
// const dataset = [50, 20, 100, 30, 40]
const svgWidth = 800;
const svgHeight = 400;
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.rangeRound([0, svgWidth])
.paddingInner(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, svgHeight]);
const svg = d3.select('#chart')
.append('svg')
.attr('width', svgWidth)
.attr('height', svgHeight);
let bars = svg.selectAll('rect').data(data, d => d.key);
let labels = svg.selectAll('text').data(data);
bars.enter()
.append('rect')
.each(function(d){return this._old = d;})
.attr('width', xScale.bandwidth)
.attr('height', d => yScale(d.value))
.attr('fill', d => `rgb(${d.value}, ${d.value * 2}, ${d.value * 3})`)
.attr('x', (d, i) => xScale(i))
.attr('y', d => svgHeight - yScale(d.value))
.attr('stroke', 'black')
.attr('stroke-width', 3)
labels.enter()
.append('text')
.attr('x', (d, i) => xScale(i) + (xScale.bandwidth() / 2))
.attr('y', d => svgHeight - yScale(d.value) + 20)
.attr('font-size', 20)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.text(d => d.value);
let asc = false;
d3.select('button').on('click', () => {
if(!asc){
data.sort((a,b) => b.value - a.value );
}else{
data.sort((a,b) => a.value - b.value );
};
asc = !asc;
bars = svg.selectAll('rect').data(data, d => d.key);
labels = svg.selectAll('text').data(data);
bars
.transition()
.delay((d, i) => (i * 10))
.duration(3000)
.each(function(d){return this._old = d;})
.attr('x', (d, i) => xScale(i))
.attr('height', d => yScale(d.value))
.attr('y', d => svgHeight - yScale(d.value));
labels
.transition()
.delay((d, i) => (i * 10))
.duration(3000)
.tween("text", function(d) {
var that = this;
var i = d3.interpolate(0, d.value); // Number(d.percentage.slice(0, -1))
return function(t) {
d3.select(that).text(i(t).toFixed(0));
}
})
.attr('y', d => svgHeight - yScale(d.value) + 20);
})
I found the "tween" function included in the above code for a similar but not exactly the same question. I don't know how to make the interpolation start from the current value instead of 0. I know I need somehow to store the old value, and access it in the tween, but not sure how.
Another question regarding the tween function: why do we assign var that = this and select that in the returned function?
Thanks in advance
You can get the current value for each text by different ways.
For instance, with vanilla JavaScript:
var current = +(this.textContent);
Or using a D3 getter:
var current = +(d3.select(this).text());
Here is your code with that change:
const data = [{
key: 0,
value: 50
},
{
key: 1,
value: 20
},
{
key: 2,
value: 100
},
{
key: 3,
value: 30
},
{
key: 4,
value: 40
},
{
key: 5,
value: 70
}
]
// const dataset = [50, 20, 100, 30, 40]
const svgWidth = 800;
const svgHeight = 400;
const xScale = d3.scaleBand()
.domain(d3.range(data.length))
.rangeRound([0, svgWidth])
.paddingInner(0.1);
const yScale = d3.scaleLinear()
.domain([0, d3.max(data, d => d.value)])
.range([0, svgHeight]);
const svg = d3.select('body')
.append('svg')
.attr('width', svgWidth)
.attr('height', svgHeight);
let bars = svg.selectAll('rect').data(data, d => d.key);
let labels = svg.selectAll('text').data(data);
bars.enter()
.append('rect')
.each(function(d) {
return this._old = d;
})
.attr('width', xScale.bandwidth)
.attr('height', d => yScale(d.value))
.attr('fill', d => `rgb(${d.value}, ${d.value * 2}, ${d.value * 3})`)
.attr('x', (d, i) => xScale(i))
.attr('y', d => svgHeight - yScale(d.value))
.attr('stroke', 'black')
.attr('stroke-width', 3)
labels.enter()
.append('text')
.attr('x', (d, i) => xScale(i) + (xScale.bandwidth() / 2))
.attr('y', d => svgHeight - yScale(d.value) + 20)
.attr('font-size', 20)
.attr('text-anchor', 'middle')
.attr('fill', 'white')
.text(d => d.value);
let asc = false;
d3.select('button').on('click', () => {
if (!asc) {
data.sort((a, b) => b.value - a.value);
} else {
data.sort((a, b) => a.value - b.value);
};
asc = !asc;
bars = svg.selectAll('rect').data(data, d => d.key);
labels = svg.selectAll('text').data(data);
bars
.transition()
.delay((d, i) => (i * 10))
.duration(3000)
.each(function(d) {
return this._old = d;
})
.attr('x', (d, i) => xScale(i))
.attr('height', d => yScale(d.value))
.attr('y', d => svgHeight - yScale(d.value));
labels
.transition()
.delay((d, i) => (i * 10))
.duration(3000)
.tween("text", function(d) {
var current = +(d3.select(this).text());
var that = this;
var i = d3.interpolate(current, d.value); // Number(d.percentage.slice(0, -1))
return function(t) {
d3.select(that).text(i(t).toFixed(0));
}
})
.attr('y', d => svgHeight - yScale(d.value) + 20);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<button>Click</button>
<br>
I have created 2 level sunburst chart on http://test.smokethis.com/wheel/ which is working perfectly.
But i want to validate my chart to show only 10 child of a particular parent and an additional node called "More child".
The behaviour on "More child" will be same as on click on parent. Which reveal all child of parent.
Image link of current situation
Image link of Resultant behaviour.Which i want
var d3Data = JSON.parse(jQuery('body .Jsondata').text())[0];
const m0 = {
id: "123",
variables: [
{
name: "chart",
inputs: ["partition","data","d3","DOM","width","color","arc","format","radius"],
value: (function(partition,data,d3,DOM,width,color,arc,format,radius)
{
var formatNumber = d3.format(",d");
var b = {
w: 140, h: 30, s: 3, t: 10
};
const root = partition(data);
root.each(d => d.current = d);
const svg = d3.select(DOM.svg(width, width))
.style("width", "100%")
.style("height", "auto")
.style("font", "20px sans-serif");
const g = svg.append("g")
.attr("transform", `translate(${width / 2},${width / 2})`);
const path = g.append("g")
.selectAll("path")
.data(root.descendants().slice(1))
.enter().append("path")
.attr("fill", d => { while (d.depth > 1) d = d.parent; console.log('d value--',d); return (typeof d.data.color != 'undefined' && d.data.color != '') ? d.data.color : color(d.data.name); })
.attr("fill-opacity", d => arcVisible(d.current) ? (d.children ? 0.6 : 0.4) : 0)
.attr("d", d => arc(d.current));
path.filter(d => d.children)
.style("cursor", "pointer")
.on("click", clicked);
path.append("title")
.text(d => `${d.ancestors().map(d => d.data.name).reverse().join("/")}\n${format(d.value)}`);
const label = g.append("g")
.attr("pointer-events", "none")
.attr("text-anchor", "middle")
.style("user-select", "none")
.selectAll("text")
.data(root.descendants().slice(1))
.enter().append("text")
.attr("dy", "0.35em")
.attr("fill-opacity", d => +labelVisible(d.current))
.attr("transform", d => labelTransform(d.current))
.text(d => d.data.name);
const parent = g.append("circle")
.datum(root)
.attr("r", radius)
.attr("fill", "none")
.attr("pointer-events", "all")
.on("click", clicked);
initializeBreadcrumbTrail();
return svg.node();
function clicked(p) {
updateBreadcrumbs(getParents(p),p.value);
var level = p.ancestors().map(p => p.data.name).reverse();
// var str = '';
// if(level.length > 1){
// for(var i=0;i<level.length;i++){
// str += '<span class="str_att show-pointer" style="background-color: #8cb125;margin-right:10px;border-radius: 5px;padding: 5px 10px;">'+level[i]+'</span>';
// }
// }
//jQuery('.breadCrumb').html(str);
console.log('level-=-',level);
parent.datum(p.parent || root);
root.each(d => d.target = {
x0: Math.max(0, Math.min(1, (d.x0 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
x1: Math.max(0, Math.min(1, (d.x1 - p.x0) / (p.x1 - p.x0))) * 2 * Math.PI,
y0: Math.max(0, d.y0 - p.depth),
y1: Math.max(0, d.y1 - p.depth)
});
const t = g.transition().duration(750);
//console.log('p',p);
// Transition the data on all arcs, even the ones that aren’t visible,
// so that if this transition is interrupted, entering arcs will start
// the next transition from the desired position.
path.transition(t)
.tween("data", d => {
const i = d3.interpolate(d.current, d.target);
return t => d.current = i(t);
})
.filter(function(d) {
return +this.getAttribute("fill-opacity") || arcVisible(d.target);
})
.attr("fill-opacity", d => arcVisible(d.target) ? (d.children ? 0.6 : 0.4) : 0)
.attrTween("d", d => () => arc(d.current));
label.filter(function(d) {
return +this.getAttribute("fill-opacity") || labelVisible(d.target);
}).transition(t)
.attr("fill-opacity", d => +labelVisible(d.target))
.attrTween("transform", d => () => labelTransform(d.current));
}
function initializeBreadcrumbTrail() {
// Add the svg area.
var trail = d3.select(".breadCrumb").append("svg:svg")
.attr("width", width)
.attr("height", width/10)
.attr("id", "trail");
// Add the label at the end, for the percentage.
trail.append("svg:text")
.attr("id", "endlabel")
.style("fill", "#000");
}
function updateBreadcrumbs(nodeArray, percentageString) {
// Data join; key function combines name and depth (= position in sequence).
var g = d3.select("#trail")
.selectAll("g").on("click", clicked)
.data(nodeArray, function(x) { return percentageString + x.data.name + x.depth; });
// Add breadcrumb and label for entering nodes.
var entering = g.enter().append("svg:g");
entering.append("svg:polygon")
.attr("points", breadcrumbPoints)
.style("fill", function(x) { return color(x.data.name); });
entering.append("svg:text")
.attr("x", (b.w + b.t) / 2)
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(function(x) { return x.data.name; });
entering.attr("transform", function(x, i) { return "translate(" + i* (b.w + b.s) + ", 0)"; });
// Remove exiting nodes.
g.exit().remove();
// Now move and update the percentage at the end.
d3.select("#trail").select("#endlabel")
.attr("x", (nodeArray.length + 0.5) * (b.w + b.s))
.attr("y", b.h / 2)
.attr("dy", "0.35em")
.attr("text-anchor", "middle")
.text(percentageString);
// Make the breadcrumb trail visible, if it's hidden.
d3.select("#trail")
.style("visibility", "");
}
function breadcrumbPoints(x, i) {
var points = [];
points.push("0,0");
points.push(b.w + ",0");
points.push(b.w + b.t + "," + (b.h / 2));
points.push(b.w + "," + b.h);
points.push("0," + b.h);
if (i > 0) { // Leftmost breadcrumb; don't include 6th vertex.
points.push(b.t + "," + (b.h / 2));
}
return points.join(" ");
}
function arcVisible(d) {
return d.y1 <= 3 && d.y0 >= 1 && d.x1 > d.x0;
}
function labelVisible(d) {
return d.y1 <= 3 && d.y0 >= 1 && (d.y1 - d.y0) * (d.x1 - d.x0) > 0.03;
}
function labelTransform(d) {
const x = (d.x0 + d.x1) / 2 * 180 / Math.PI;
const y = (d.y0 + d.y1) / 2 * radius;
return `rotate(${x - 90}) translate(${y},0) rotate(${x < 180 ? 0 : 180})`;
}
function getParents(a){
var nodeArray = [a];
while(a.parent){
nodeArray.push(a.parent);
a = a.parent
}
return nodeArray.reverse();
}
}
)
},
{
name: "data",
value: (function(){
return d3Data;
})
},
{
name: "partition",
inputs: ["d3"],
value: (function(d3){
// console.log('partition',d3);
return(
data => {
const root = d3.hierarchy(data)
.sum(d => d.size)
.sort((a, b) => b.value - a.value);
console.log('data',d3.partition().size([2 * Math.PI, root.height+1])(root));
return d3.partition()
.size([2 * Math.PI, root.height+1])
(root);
}
)
})
},
{
name: "color",
inputs: ["d3","data"],
value: (function(d3,data){return (
d3.scaleOrdinal().range(d3.quantize(d3.interpolateRainbow, data.children.length + 1))
)})
},
{
name: "format",
inputs: ["d3"],
value: (function(d3){return(
d3.format(",d")
)})
},
{
name: "width",
value: (function(){return(
932
)})
},
{
name: "radius",
inputs: ["width"],
value: (function(width){return(
width / 6
)})
},
{
name: "arc",
inputs: ["d3","radius"],
value: (function(d3,radius){return(
d3.arc()
.startAngle(d => d.x0)
.endAngle(d => d.x1)
.padAngle(d => Math.min((d.x1 - d.x0) / 2, 0.005))
.padRadius(radius * 1.5)
.innerRadius(d => d.y0 * radius)
.outerRadius(d => Math.max(d.y0 * radius, d.y1 * radius - 1))
)})
},
{
name: "d3",
inputs: ["require"],
value: (function(require){return(
require("https://d3js.org/d3.v5.min.js")
)})
}
]
};
const notebook = {
id: "123",
modules: [m0]
};
function loadData(){
$.ajax({
type:'GET',
url:'http://shopnz.in/wp-admin/admin-ajax.php?action=wp_ajax_list_items&sunburst=sunburstcat',
success: function(response){
console.log('response',response[0]);
console.log('data-=-=',data);
data = response[0];
}
});
}
export default notebook;
[{"name":"Balance","term_id":"588","slug":"balance","parent":"0","has_children":true,"color":"#aac62d","children":[{"name":"Aroma","term_id":"589","slug":"aroma","parent":"588","has_children":true,"color":"#aac62d","children":[{"name":"Earth","term_id":"593","slug":"earth","parent":"589","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Fruit","term_id":"594","slug":"fruit","parent":"589","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Herb","term_id":"595","slug":"herb","parent":"589","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Pungent","term_id":"596","slug":"pungent","parent":"589","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Spice","term_id":"597","slug":"spice","parent":"589","has_children":false,"size":"1000","color":"#aac62d"}]},{"name":"Health","term_id":"590","slug":"health","parent":"588","has_children":true,"color":"#aac62d","children":[{"name":"Body","term_id":"598","slug":"body","parent":"590","has_children":true,"color":"#aac62d","children":[{"name":"Fatigue","term_id":"613","slug":"fatigue","parent":"598","has_children":true,"color":"#aac62d","children":[{"name":"Sleep","term_id":"616","slug":"sleep","parent":"613","has_children":false,"size":"1000","color":"#aac62d"}]},{"name":"PainRelief","term_id":"614","slug":"painrelief","parent":"598","has_children":true,"color":"#aac62d","children":[{"name":"Spasms","term_id":"617","slug":"spasms","parent":"614","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Relaxed","term_id":"618","slug":"relaxed","parent":"614","has_children":false,"size":"1000","color":"#aac62d"}]},{"name":"Digestion","term_id":"615","slug":"digestion","parent":"598","has_children":true,"color":"#aac62d","children":[{"name":"Appetite","term_id":"619","slug":"appetite","parent":"615","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Nausea","term_id":"620","slug":"nausea","parent":"615","has_children":false,"size":"1000","color":"#aac62d"}]}]},{"name":"Mind","term_id":"599","slug":"mind","parent":"590","has_children":true,"color":"#aac62d","children":[{"name":"Behavior","term_id":"621","slug":"behavior","parent":"599","has_children":true,"color":"#aac62d","children":[{"name":"Sedative","term_id":"623","slug":"sedative","parent":"621","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Energetic","term_id":"624","slug":"energetic","parent":"621","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Focus","term_id":"625","slug":"focus","parent":"621","has_children":false,"size":"1000","color":"#aac62d"}]},{"name":"Mood","term_id":"622","slug":"mood","parent":"599","has_children":true,"color":"#aac62d","children":[{"name":"Creative","term_id":"626","slug":"creative","parent":"622","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Uplifted","term_id":"627","slug":"uplifted","parent":"622","has_children":false,"size":"1000","color":"#aac62d"}]}]}]},{"name":"Medical","term_id":"591","slug":"medical","parent":"588","has_children":true,"color":"#aac62d","children":[{"name":"Condition","term_id":"600","slug":"condition","parent":"591","has_children":true,"color":"#aac62d","children":[{"name":"Cancer","term_id":"602","slug":"cancer","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"PMS","term_id":"603","slug":"pms","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Arthritis","term_id":"628","slug":"arthritis","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Asthma","term_id":"629","slug":"asthma","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Cachexia","term_id":"630","slug":"cachexia","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Crohn\u2019s Disease","term_id":"631","slug":"crohns-disease","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Depression (currently a 'symptom')","term_id":"632","slug":"depression-currently-a-symptom","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Epilepsy","term_id":"633","slug":"epilepsy","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Gastrointestinal Disorder","term_id":"634","slug":"gastrointestinal-disorder","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Hypertension","term_id":"635","slug":"hypertension","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Parkinson\u2019s","term_id":"636","slug":"parkinsons","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Phantom Limb Pain","term_id":"637","slug":"phantom-limb-pain","parent":"600","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Spinal Cord Injury","term_id":"638","slug":"spinal-cord-injury","parent":"600","has_children":false,"size":"1000","color":"#aac62d"}]},{"name":"Symptom","term_id":"601","slug":"symptom","parent":"591","has_children":true,"color":"#aac62d","children":[{"name":"Anxiety","term_id":"604","slug":"anxiety","parent":"601","has_children":true,"color":"#aac62d","children":[{"name":"ACDC","term_id":"612","slug":"acdc","parent":"604","has_children":false,"size":"1000","color":"#aac62d"}]},{"name":"Depresssion","term_id":"605","slug":"depresssion","parent":"601","has_children":false,"size":"1000","color":"#aac62d"}]}]},{"name":"Terpenes","term_id":"592","slug":"terpenes","parent":"588","has_children":true,"color":"#aac62d","children":[{"name":"Humulene","term_id":"606","slug":"humulene","parent":"592","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Limonene","term_id":"607","slug":"limonene","parent":"592","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Linalool","term_id":"608","slug":"linalool","parent":"592","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Myrcene","term_id":"609","slug":"myrcene","parent":"592","has_children":false,"size":"1000","color":"#aac62d"},{"name":"Pinene","term_id":"610","slug":"pinene","parent":"592","has_children":true,"color":"#aac62d","children":[{"name":"Romulan","term_id":"611","slug":"romulan","parent":"610","has_children":false,"size":"1000","color":"#aac62d"}]}]}]}]
This is my data.