As the title say, I am creating a bar chart using D3Js. What do I have to change in the following snippet for the bars to start at the beginning of the X-Axis?
var allBarsDatasets = [
{
"xAxisTickValue": "10-1",
"barValue": 18
},
{
"xAxisTickValue": "10-2",
"barValue": 17
},
{
"xAxisTickValue": "10-3",
"barValue": 17
},
{
"xAxisTickValue": "10-4",
"barValue": 19
},
{
"xAxisTickValue": "11-1",
"barValue": 19
},
{
"xAxisTickValue": "11-2",
"barValue": 14
},
{
"xAxisTickValue": "11-3",
"barValue": 18
},
{
"xAxisTickValue": "11-4",
"barValue": 12
},
{
"xAxisTickValue": "12-1",
"barValue": 16
},
{
"xAxisTickValue": "12-2",
"barValue": 15
},
{
"xAxisTickValue": "12-3",
"barValue": 14
},
{
"xAxisTickValue": "12-4",
"barValue": 15
},
{
"xAxisTickValue": "13-1",
"barValue": 16
},
{
"xAxisTickValue": "13-2",
"barValue": 17
},
{
"xAxisTickValue": "13-3",
"barValue": 12
},
{
"xAxisTickValue": "13-4",
"barValue": 17
},
{
"xAxisTickValue": "14-1",
"barValue": 17
},
{
"xAxisTickValue": "14-2",
"barValue": 16
},
{
"xAxisTickValue": "14-3",
"barValue": 14
},
{
"xAxisTickValue": "14-4",
"barValue": 15
},
{
"xAxisTickValue": "15-1",
"barValue": 15
},
{
"xAxisTickValue": "15-2",
"barValue": 19
},
{
"xAxisTickValue": "15-3",
"barValue": 17
},
{
"xAxisTickValue": "15-4",
"barValue": 12
},
{
"xAxisTickValue": "16-1",
"barValue": 13
},
{
"xAxisTickValue": "16-2",
"barValue": 17
},
{
"xAxisTickValue": "16-3",
"barValue": 17
},
{
"xAxisTickValue": "16-4",
"barValue": 19
},
{
"xAxisTickValue": "17-1",
"barValue": 18
},
{
"xAxisTickValue": "17-2",
"barValue": 13
},
{
"xAxisTickValue": "17-3",
"barValue": 17
},
{
"xAxisTickValue": "17-4",
"barValue": 16
},
{
"xAxisTickValue": "18-1",
"barValue": 10
},
{
"xAxisTickValue": "18-2",
"barValue": 15
},
{
"xAxisTickValue": "18-3",
"barValue": 17
},
{
"xAxisTickValue": "18-4",
"barValue": 17
},
{
"xAxisTickValue": "19-1",
"barValue": 18
},
{
"xAxisTickValue": "19-2",
"barValue": 14
},
{
"xAxisTickValue": "19-3",
"barValue": 16
},
{
"xAxisTickValue": "19-4",
"barValue": 13
},
{
"xAxisTickValue": "20-1",
"barValue": 15
},
{
"xAxisTickValue": "20-2",
"barValue": 17
},
{
"xAxisTickValue": "20-3",
"barValue": 14
},
{
"xAxisTickValue": "20-4",
"barValue": 16
},
{
"xAxisTickValue": "21-1",
"barValue": 18
},
{
"xAxisTickValue": "21-2",
"barValue": 14
},
{
"xAxisTickValue": "21-3",
"barValue": 17
},
{
"xAxisTickValue": "21-4",
"barValue": 15
},
{
"xAxisTickValue": "22-1",
"barValue": 16
},
{
"xAxisTickValue": "22-2",
"barValue": 17
},
{
"xAxisTickValue": "22-3",
"barValue": 16
},
{
"xAxisTickValue": "22-4",
"barValue": 16
},
{
"xAxisTickValue": "23-1",
"barValue": 16
},
{
"xAxisTickValue": "23-2",
"barValue": 16
},
{
"xAxisTickValue": "23-3",
"barValue": 15
},
{
"xAxisTickValue": "23-4",
"barValue": 16
},
{
"xAxisTickValue": "24-1",
"barValue": 15
},
{
"xAxisTickValue": "24-2",
"barValue": 15
},
{
"xAxisTickValue": "24-3",
"barValue": 17
},
{
"xAxisTickValue": "24-4",
"barValue": 13
},
{
"xAxisTickValue": "6-1",
"barValue": 16
},
{
"xAxisTickValue": "6-2",
"barValue": 16
},
{
"xAxisTickValue": "6-3",
"barValue": 16
},
{
"xAxisTickValue": "6-4",
"barValue": 15
},
{
"xAxisTickValue": "7-1",
"barValue": 12
},
{
"xAxisTickValue": "7-2",
"barValue": 18
},
{
"xAxisTickValue": "7-3",
"barValue": 15
},
{
"xAxisTickValue": "7-4",
"barValue": 18
},
{
"xAxisTickValue": "8-1",
"barValue": 16
},
{
"xAxisTickValue": "8-2",
"barValue": 15
},
{
"xAxisTickValue": "8-3",
"barValue": 17
},
{
"xAxisTickValue": "8-4",
"barValue": 16
},
{
"xAxisTickValue": "9-1",
"barValue": 17
},
{
"xAxisTickValue": "9-2",
"barValue": 14
},
{
"xAxisTickValue": "9-3",
"barValue": 18
},
{
"xAxisTickValue": "9-4",
"barValue": 18
}
];
// TODO: format labels and values
// rotate x-axis ticks; if number user shorter format
// use shorter format for y-axis values
// correctly position x-axis and y-axis labels
$("#chartDiv").html("");
var barColor = '#384a60';
// calculate total frequency by state for all segment.
var fD = allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
/* var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 600 - margin.left - margin.right,
height = 310 - margin.top - margin.bottom; */
var margin = {top: 20, right: 100, bottom: 30, left: 100},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var padding = 100;
//create svg for histogram.
var svg = d3.select("#chartDiv").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 + ")");
// create function for x-axis mapping.
var x = d3.scale.ordinal().rangeRoundBands([0, width], 0.1)
.domain(fD.map(function(d) { return d[0]; }));
// Add x-axis to the histogram svg.
svg.append("g").attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.svg.axis()
.scale(x)
.orient("bottom")
.innerTickSize(-height)
.outerTickSize(0)
.tickPadding(10));
// create function for y-axis mapping.
var yMin = d3.min(fD.map(function(d) { return d[1]; }));
var yMax = d3.max(fD.map(function(d) { return d[1]; }));
var y = d3.scale.linear().range([height, 0])
.domain([0, d3.max(fD, function(d) { return d[1]; })]);
var yScaleGridLines = d3.scale.linear()
.domain([yMin, yMax])
.range([height, 0]);
var yAxisGridLines = d3.svg.axis()
.scale(yScaleGridLines)
.orient("left")
.innerTickSize(-width)
.outerTickSize(0)
.tickPadding(10);
svg.append("g")
.attr("class", "y axis")
.call(yAxisGridLines);
// Create bars for histogram to contain rectangles and freq labels.
var bars = svg.selectAll(".bar").data(fD).enter()
.append("g").attr("class", "bar");
//create the rectangles.
bars.append("rect")
.attr("x", function(d) { return x(d[0]); })
.attr("y", function(d) { return y(d[1]); })
.attr("width", x.rangeBand())
.attr("height", function(d) { return height - y(d[1]); })
.attr('fill',barColor);
#chartDiv .axis path,
#chartDiv .axis line{
fill: none;
stroke: black;
}
#chartDiv .line{
fill: none;
stroke: blue;
stroke-width: 2px;
}
#chartDiv .tick text{
font-size: 12px;
}
#chartDiv .tick line{
opacity: 0.2;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="chartDiv">
</div>
At first I thought it was a data issue, but I don't see anything odd in it. I also, tried removing attributes from the section where I set up the X-Axis, but it didn't work.
Thanks #AndrewReid for putting me on the right track. I figured the meaning of each arguments based on your comment to my question; the second argument is the space between bars and the third one is the padding between bars and the beginning and end of the X-Axis. The following values worked for my needs d3.scale.ordinal().rangeRoundBands([0, width], 0.1, 0)
Related
I want to create a Stacked bar chart like http://bl.ocks.org/mbostock/3886208 . But I don't want to use CSV file.
How can I change this sample by using JSON data?
Anyone could help?
Your question is a bit vague but let's say we convert that CSV file to JSON data as:
var data = [{
"State": "VT",
"Under 5 Years": 32635,
"5 to 13 Years": 62538,
"14 to 17 Years": 33757,
"18 to 24 Years": 61679,
"25 to 44 Years": 155419,
"45 to 64 Years": 188593,
"65 Years and Over": 86649
}, {
"State": "VA",
"Under 5 Years": 522672,
"5 to 13 Years": 887525,
"14 to 17 Years": 413004,
"18 to 24 Years": 768475,
"25 to 44 Years": 2203286,
"45 to 64 Years": 2033550,
"65 Years and Over": 940577
},
...
Then you would need to fix the pre-processing (to get the keys and totals) as:
// fix pre-processing
var keys = [];
for (key in data[0]){
if (key != "State")
keys.push(key);
}
data.forEach(function(d){
d.total = 0;
keys.forEach(function(k){
d.total += d[k];
})
});
Then the rest of the example falls into place:
<!DOCTYPE html>
<style>
.axis .domain {
display: none;
}
</style>
<svg width="960" height="500"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
margin = {
top: 20,
right: 20,
bottom: 30,
left: 40
},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(0.05)
.align(0.1);
var y = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var data = [{
"State": "VT",
"Under 5 Years": 32635,
"5 to 13 Years": 62538,
"14 to 17 Years": 33757,
"18 to 24 Years": 61679,
"25 to 44 Years": 155419,
"45 to 64 Years": 188593,
"65 Years and Over": 86649
}, {
"State": "VA",
"Under 5 Years": 522672,
"5 to 13 Years": 887525,
"14 to 17 Years": 413004,
"18 to 24 Years": 768475,
"25 to 44 Years": 2203286,
"45 to 64 Years": 2033550,
"65 Years and Over": 940577
}, {
"State": "WA",
"Under 5 Years": 433119,
"5 to 13 Years": 750274,
"14 to 17 Years": 357782,
"18 to 24 Years": 610378,
"25 to 44 Years": 1850983,
"45 to 64 Years": 1762811,
"65 Years and Over": 783877
}, {
"State": "WV",
"Under 5 Years": 105435,
"5 to 13 Years": 189649,
"14 to 17 Years": 91074,
"18 to 24 Years": 157989,
"25 to 44 Years": 470749,
"45 to 64 Years": 514505,
"65 Years and Over": 285067
}, {
"State": "WI",
"Under 5 Years": 362277,
"5 to 13 Years": 640286,
"14 to 17 Years": 311849,
"18 to 24 Years": 553914,
"25 to 44 Years": 1487457,
"45 to 64 Years": 1522038,
"65 Years and Over": 750146
}, {
"State": "WY",
"Under 5 Years": 38253,
"5 to 13 Years": 60890,
"14 to 17 Years": 29314,
"18 to 24 Years": 53980,
"25 to 44 Years": 137338,
"45 to 64 Years": 147279,
"65 Years and Over": 65614
}];
// fix pre-processing
var keys = [];
for (key in data[0]){
if (key != "State")
keys.push(key);
}
data.forEach(function(d){
d.total = 0;
keys.forEach(function(k){
d.total += d[k];
})
});
data.sort(function(a, b) {
return b.total - a.total;
});
x.domain(data.map(function(d) {
return d.State;
}));
y.domain([0, d3.max(data, function(d) {
return d.total;
})]).nice();
z.domain(keys);
g.append("g")
.selectAll("g")
.data(d3.stack().keys(keys)(data))
.enter().append("g")
.attr("fill", function(d) {
return z(d.key);
})
.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("x", function(d) {
return x(d.data.State);
})
.attr("y", function(d) {
return y(d[1]);
})
.attr("height", function(d) {
return y(d[0]) - y(d[1]);
})
.attr("width", x.bandwidth());
g.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
g.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y).ticks(null, "s"))
.append("text")
.attr("x", 2)
.attr("y", y(y.ticks().pop()) + 0.5)
.attr("dy", "0.32em")
.attr("fill", "#000")
.attr("font-weight", "bold")
.attr("text-anchor", "start")
.text("Population");
var legend = g.append("g")
.attr("font-family", "sans-serif")
.attr("font-size", 10)
.attr("text-anchor", "end")
.selectAll("g")
.data(keys.slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) {
return "translate(0," + i * 20 + ")";
});
legend.append("rect")
.attr("x", width - 19)
.attr("width", 19)
.attr("height", 19)
.attr("fill", z);
legend.append("text")
.attr("x", width - 24)
.attr("y", 9.5)
.attr("dy", "0.32em")
.text(function(d) {
return d;
});
</script>
I'm fresh bee for D3-visulization. Currently working with creating D3 tree layout visualization for data lineage. In a data lineage flow, a child node can be derived from more than one parent. Here is the example. In below example, a 'DevLead' may work with 2 managers.
var data = [
{ "name": "Director", "parent": "null", "depth": 0 },
{ "name": "Manager1", "parent": "Director", "depth": 1 },
{ "name": "Manager2", "parent": "Director", "depth": 1 },
{ "name": "DevLead", "parent": "Manager1", "depth": 2 },
{ "name": "DevLead", "parent": "Manager2", "depth": 2 }
];
Getting output refer below image attached.
I'd like to see 'DevLead' children should show only one, and there should be a derivation from 'Manager1' and 'Manager2'. Could any one help with this.
D3 Tree Layout does not exactly supports multiple parents
What Can you do?
Use network graph instead - downside is that node positioning is
hard
I had similar requirements and tried building network graph similar
with tree layout, but when there are many nodes, it gets messy ...
you can check it on
codepen
use hack on tree layout - draw additional link from other node
check this example
another hack using hidden nodes - jsfiddle
Also, I think, these links will help you further :
Family Tree in d3.js
d-tree library - data with multiple parents
If you go with first option, here, you can play with this snippet by removing and adding nodes in data
<!DOCTYPE html>
<html >
<head>
<meta charset="UTF-8">
<link rel="shortcut icon" type="image/x-icon" href="https://production-assets.codepen.io/assets/favicon/favicon-8ea04875e70c4b0bb41da869e81236e54394d63638a1ef12fa558a4a835f1164.ico" />
<link rel="mask-icon" type="" href="https://production-assets.codepen.io/assets/favicon/logo-pin-f2d2b6d2c61838f7e76325261b7195c27224080bc099486ddd6dccb469b8e8e6.svg" color="#111" />
<title>CodePen - A Pen by dato</title>
</head>
<body translate="no" >
<script src='https://d3js.org/d3.v3.min.js'></script>
<script>
var width = window.innerWidth - 20,
height = window.innerHeight - 20,
radius = 30;
var min_zoom = 0.1;
var max_zoom = 7;
var zoom = d3.behavior.zoom().scaleExtent([min_zoom, max_zoom])
var fill = d3.scale.category20();
var force = d3.layout.force()
.charge(-8000)
.linkDistance(200)
.size([width, height]);
force.drag().on("dragstart", dragstarted)
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var chart = svg.append('g');
var json = {
"nodes": [{
"name": "node0"
}, {
"name": "node1"
}, {
"name": "node2"
}, {
"name": "node3"
}, {
"name": "node4"
}, {
"name": "node5"
}, {
"name": "node6"
}, {
"name": "node7"
}, {
"name": "node8"
}, {
"name": "node9"
}, {
"name": "node10"
}, {
"name": "node11"
}, {
"name": "node12"
}, {
"name": "node13"
}, {
"name": "node14"
}, {
"name": "node15"
}, {
"name": "node16"
}, {
"name": "node17"
}, {
"name": "node18"
}, {
"name": "node19"
}, {
"name": "node20"
}, {
"name": "node21"
}, {
"name": "node22"
}, {
"name": "node23"
}, {
"name": "node24"
}, {
"name": "node25"
}, {
"name": "node26"
}, {
"name": "node27"
}, {
"name": "node28"
}, {
"name": "node29"
}, {
"name": "node30"
}, {
"name": "node31"
}, {
"name": "node32"
}, {
"name": "node33"
}, {
"name": "node34"
}, {
"name": "node35"
}, {
"name": "node36"
}, {
"name": "node37"
}, {
"name": "node38"
}, {
"name": "node39"
}, {
"name": "node40"
}, {
"name": "node41"
}, {
"name": "node42"
}, {
"name": "node43"
}, {
"name": "node44"
}, {
"name": "node45"
}, {
"name": "node46"
}, {
"name": "node47"
}, {
"name": "node48"
}, {
"name": "node49"
}, {
"name": "node50"
}, {
"name": "node51"
}, {
"name": "node52"
}, {
"name": "node53"
}, {
"name": "node54"
}, {
"name": "node55"
}, {
"name": "node56"
}, {
"name": "node57"
}, {
"name": "node58"
}, {
"name": "node59"
}, {
"name": "node60"
}, {
"name": "node61"
}, {
"name": "node62"
}, {
"name": "node63"
}, {
"name": "node64"
}, {
"name": "node65"
}, {
"name": "node66"
}, {
"name": "node67"
}, {
"name": "node68"
}, {
"name": "node69"
}, {
"name": "node70"
}, {
"name": "node71"
}, {
"name": "node72"
}, {
"name": "node73"
}, {
"name": "node74"
}, {
"name": "node75"
}, {
"name": "node76"
}, {
"name": "node77"
}, {
"name": "node78"
}, {
"name": "node79"
}, {
"name": "node80"
}, {
"name": "node81"
}, {
"name": "node82"
}, {
"name": "node83"
}, {
"name": "node84"
}, {
"name": "node85"
}, {
"name": "node86"
}, {
"name": "node87"
}, {
"name": "node88"
}, {
"name": "node89"
}, {
"name": "node90"
}, {
"name": "node91"
}, {
"name": "node92"
}, {
"name": "node93"
}, {
"name": "node94"
}, {
"name": "node95"
}, {
"name": "node96"
}, {
"name": "node97"
}, {
"name": "node98"
}, {
"name": "node99"
}],
"links": [ {
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}, {
"source": 1,
"target": 3
}, {
"source": 1,
"target": 4
}, {
"source": 2,
"target": 5
}, {
"source": 2,
"target": 6
}, {
"source": 3,
"target": 7
}, {
"source": 3,
"target": 8
}, {
"source": 4,
"target": 9
}, {
"source": 4,
"target": 10
}, {
"source": 5,
"target": 11
}, {
"source": 5,
"target": 12
}, {
"source": 6,
"target": 13
}, {
"source": 6,
"target": 14
}, {
"source": 7,
"target": 15
}, {
"source": 7,
"target": 16
}, {
"source": 8,
"target": 17
}, {
"source": 8,
"target": 18
}, {
"source": 9,
"target": 19
}, {
"source": 9,
"target": 20
}, {
"source": 10,
"target": 21
}, {
"source": 10,
"target": 22
}, {
"source": 11,
"target": 23
}, {
"source": 11,
"target": 24
}, {
"source": 12,
"target": 25
}, {
"source": 12,
"target": 26
}, {
"source": 13,
"target": 27
}, {
"source": 13,
"target": 28
}, {
"source": 14,
"target": 29
}, {
"source": 14,
"target": 30
}, {
"source": 15,
"target": 31
}, {
"source": 15,
"target": 32
}, {
"source": 16,
"target": 33
}, {
"source": 16,
"target": 34
}, {
"source": 17,
"target": 35
}, {
"source": 17,
"target": 36
}, {
"source": 18,
"target": 37
}, {
"source": 18,
"target": 38
}, {
"source": 19,
"target": 39
}, {
"source": 19,
"target": 40
}, {
"source": 20,
"target": 41
}, {
"source": 20,
"target": 42
}, {
"source": 21,
"target": 43
}, {
"source": 21,
"target": 44
}, {
"source": 22,
"target": 45
}, {
"source": 22,
"target": 46
}, {
"source": 23,
"target": 47
}, {
"source": 23,
"target": 48
}, {
"source": 24,
"target": 49
}, {
"source": 24,
"target": 50
}, {
"source": 25,
"target": 51
}, {
"source": 25,
"target": 52
}, {
"source": 26,
"target": 53
}, {
"source": 26,
"target": 54
}, {
"source": 27,
"target": 55
}, {
"source": 27,
"target": 56
}, {
"source": 28,
"target": 57
}, {
"source": 28,
"target": 58
}, {
"source": 29,
"target": 59
}, {
"source": 29,
"target": 60
}, {
"source": 30,
"target": 61
}, {
"source": 30,
"target": 62
}, {
"source": 31,
"target": 63
}, {
"source": 31,
"target": 64
}, {
"source": 32,
"target": 65
}, {
"source": 32,
"target": 66
}, {
"source": 33,
"target": 67
}, {
"source": 33,
"target": 68
}, {
"source": 34,
"target": 69
}, {
"source": 34,
"target": 70
}, {
"source": 35,
"target": 71
}, {
"source": 35,
"target": 72
}, {
"source": 36,
"target": 73
}, {
"source": 36,
"target": 74
}, {
"source": 37,
"target": 75
}, {
"source": 37,
"target": 76
}, {
"source": 38,
"target": 77
}, {
"source": 38,
"target": 78
}, {
"source": 39,
"target": 79
}, {
"source": 39,
"target": 80
}, {
"source": 40,
"target": 81
}, {
"source": 40,
"target": 82
}, {
"source": 41,
"target": 83
}, {
"source": 41,
"target": 84
}, {
"source": 42,
"target": 85
}, {
"source": 42,
"target": 86
}, {
"source": 43,
"target": 87
}, {
"source": 43,
"target": 88
}, {
"source": 44,
"target": 89
}, {
"source": 44,
"target": 90
}, {
"source": 45,
"target": 91
}, {
"source": 45,
"target": 92
}, {
"source": 46,
"target": 93
}, {
"source": 46,
"target": 94
}, {
"source": 47,
"target": 95
}, {
"source": 47,
"target": 96
}, {
"source": 48,
"target": 97
}, {
"source": 48,
"target": 98
}, {
"source": 49,
"target": 99
},{
"source": 0,
"target": 99
}]
}
var link = chart.selectAll("line")
.data(json.links)
.enter()
.append("line")
.attr("stroke", function(d) {
return 'blue'
})
var node = chart.selectAll("circle")
.data(json.nodes)
.enter().append("circle")
.attr("r", radius - .75)
.style("fill", function(d) {
return fill(d.group);
})
.style("stroke", function(d) {
return d3.rgb(fill(d.group)).darker();
})
.on('mouseover', d => console.log(d))
.call(force.drag);
function dragstarted() {
d3.event.sourceEvent.stopPropagation();
}
zoom.on("zoom", function(d) {
var evt = d3.event;
debugger;
/*
var dcx = (window.innerWidth/2-d.x*zoom.scale());
var dcy = (window.innerHeight/2-d.y*zoom.scale());
*/
var dcx = evt.translate[0]
var dcy = evt.translate[1]
zoom.translate([dcx, dcy]);
chart.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");;
});
force
.nodes(json.nodes)
.links(json.links)
.on("tick", tick)
.start();
svg.call(zoom)
function tick(e) {
console.log(e)
var k = 6 * e.alpha;
// Push sources up and targets down to form a weak tree.
link
.each(function(d,i) {
d.source.y -= k * 60, d.target.y += k * 100;
/*
if(i%2==1){
d.source.x -= 0.4/k
}else{
d.source.x += 0.4/k
}
*/
})
.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
}
</script>
</body>
</html>
use hack on tree layout - draw additional link from other node
check this example
If a hack is the solution you're looking for the implementation below might be helpful. It's a d3 collapsable tree based on Rob Schmueckers Blog Multiple Parent Nodes D3.js example which can handle multiple parent links and the basic tree events. It's typically split into index.html building the html structure, a stylesheet style.css and the actual script tree.js
index.html :
Define a div with id tree_view later holding the tree. Call tree.js to create the tree.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 collapsable multiple parents tree</title>
<link rel="stylesheet" type="text/css" href="style.css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
</head>
<body>
<div id="tree_view"></div> <!-- div holding tree -->
<script src="tree.js"></script> <!-- script to create tree -->
</body>
</html>
style.css :
#tree_view {
width: 100%;
height: 100%;
margin-top: 30px;
}
/* basic node */
.node {
cursor: pointer;
text-anchor: start;
}
/* rectangle node */
.node rect {
stroke: gray;
stroke-width: 1.5px;
}
/* node text */
.node text {
font: 12px sans-serif;
}
/* standard links (.link) and multiparent links (.mpLink) */
.link, .mpLink {
fill: none;
stroke: #ccc;
}
tree.js : (full code below)
Basically you need to define a new link object for each additional link you want to add. Each link needs a source and target node. The backup nodes are necessary for event handling:
let link = new Object();
link.source = pairNode1;
link.target = pairNode2;
link._source = pairNode1; // backup source
link._target = pairNode2; // backup target
additionalLinks.push(link)
Now you can handle all additional links in the update process (updateTree(source)):
// ======== add additional links (mpLinks) ========
let mpLink = svg.selectAll("path.mpLink")
.data(additionalLinks);
mpLink.enter().insert("path", "g")
.attr("class", "mpLink")
.attr("x", nodeWidth / 2)
.attr("y", nodeHeight / 2)
.attr("d", function (d) {
var o = { x: source.x0, y: source.y0 };
return diagonal({ source: o, target: o });
});
mpLink.transition()
.duration(duration)
.attr("d", diagonal)
.attr("stroke-width", 1.5)
mpLink.exit().transition()
.duration(duration)
.attr("d", function (d) {
let o = { x: source.x, y: source.y };
return diagonal({ source: o, target: o });
})
.remove();
and define the on event behavior in method click(d).
The full tree.js code:
// plot properties
let root;
let tree;
let diagonal;
let svg;
let duration = 750;
let treeMargin = { top: 0, right: 20, bottom: 20, left: 20 };
let treeWidth = window.innerWidth - treeMargin.right - treeMargin.left;
let treeHeight = window.innerHeight - treeMargin.top - treeMargin.bottom;
let treeDepth = 5;
let maxTextLength = 90;
let nodeWidth = maxTextLength + 20;
let nodeHeight = 36;
let scale = 1;
// tree data
let data = [
{
"name": "Root",
"parent": "null",
"children": [
{
"name": "Level 2: A",
"parent": "Top Level",
"children": [
{
"name": "A1",
"parent": "Level 2: A"
},
{
"name": "A2",
"parent": "Level 2: A"
}
]
},
{
"name": "Level 2: B",
"parent": "Top Level"
}
]
}
];
// additional (multiparent) links data array
let additionalLinks = []
/**
* Initialize tree properties
* #param {Object} treeData
*/
function initTree(treeData) {
// init
tree = d3.layout.tree()
.size([treeWidth, treeHeight]);
diagonal = d3.svg.diagonal()
.projection(function (d) { return [d.x + nodeWidth / 2, d.y + nodeHeight / 2]; });
svg = d3.select("div#tree_view")
.append("svg")
.attr("width", treeWidth + treeMargin.right + treeMargin.left)
.attr("height", treeHeight + treeMargin.top + treeMargin.bottom)
.attr("transform", `translate(${treeMargin.left},${treeMargin.top})scale(${scale},${scale})`);
root = treeData[0];
root.x0 = treeHeight / 2;
root.y0 = 0;
// fill additionalLinks array
let pairNode1 = tree.nodes(root).filter(function(d) {
return d['name'] === 'Level 2: B';
})[0];
let pairNode2 = tree.nodes(root).filter(function(d) {
return d['name'] === 'A2';
})[0];
let link = new Object();
link.source = pairNode1;
link.target = pairNode2;
link._source = pairNode1; // backup source
link._target = pairNode2; // backup target
additionalLinks.push(link)
// update
updateTree(root);
d3.select(self.frameElement).style("height", "500px");
// add resize listener
window.addEventListener("resize", function (event) {
resizeTreePlot();
});
}
/**
* Perform tree update. Update nodes and links
* #param {Object} source
*/
function updateTree(source) {
let i = 0;
let nodes = tree.nodes(root).reverse();
let links = tree.links(nodes);
nodes.forEach(function (d) { d.y = d.depth * 80; });
// ======== add nodes and text elements ========
let node = svg.selectAll("g.node")
.data(nodes, function (d) { return d.id || (d.id = ++i); });
let nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function (d) { return `translate(${source.x0},${source.y0})`; })
.on("click", click);
nodeEnter.append("rect")
.attr("width", nodeWidth)
.attr("height", nodeHeight)
.attr("rx", 2)
.style("fill", function(d) { return d._children ? "#ace3b5": "#f4f4f9"; });
nodeEnter.append("text")
.attr("y", nodeHeight / 2)
.attr("x", 13)
.attr("dy", ".35em")
.text(function (d) { return d.name; })
.style("fill-opacity", 1e-6);
let nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function (d) { return `translate(${d.x},${d.y})`; });
nodeUpdate.select("rect")
.attr("width", nodeWidth)
.style("fill", function(d) { return d._children ? "#ace3b5": "#f4f4f9"; });
nodeUpdate.select("text").style("fill-opacity", 1);
let nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function (d) { return `translate(${source.x},${source.y})`; })
.remove();
nodeExit.select("rect")
.attr("width", nodeWidth)
.attr("rx", 2)
.attr("height", nodeHeight);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// ======== add links ========
let link = svg.selectAll("path.link")
.data(links, function (d) { return d.target.id; });
link.enter().insert("path", "g")
.attr("class", "link")
.attr("x", nodeWidth / 2)
.attr("y", nodeHeight / 2)
.attr("d", function (d) {
var o = { x: source.x0, y: source.y0 };
return diagonal({ source: o, target: o });
});
link.transition()
.duration(duration)
.attr("d", diagonal)
link.exit().transition()
.duration(duration)
.attr("d", function (d) {
let o = { x: source.x, y: source.y };
return diagonal({ source: o, target: o });
})
.remove();
// ======== add additional links (mpLinks) ========
let mpLink = svg.selectAll("path.mpLink")
.data(additionalLinks);
mpLink.enter().insert("path", "g")
.attr("class", "mpLink")
.attr("x", nodeWidth / 2)
.attr("y", nodeHeight / 2)
.attr("d", function (d) {
var o = { x: source.x0, y: source.y0 };
return diagonal({ source: o, target: o });
});
mpLink.transition()
.duration(duration)
.attr("d", diagonal)
.attr("stroke-width", 1.5)
mpLink.exit().transition()
.duration(duration)
.attr("d", function (d) {
let o = { x: source.x, y: source.y };
return diagonal({ source: o, target: o });
})
.remove();
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
/**
* Handle on tree node clicked actions
* #param {Object} d node
*/
function click(d) {
// update regular links
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
// update additional links
additionalLinks.forEach(function(link){
let sourceVisible = false;
let targetVisible = false;
tree.nodes(root).filter(function(n) {
if(n["name"] == link._source.name){
sourceVisible = true;
}
if(n["name"] == link._target.name){
targetVisible = true;
}
});
if(sourceVisible && targetVisible){
link.source = link._source;
link.target = link._target;
}
else if(!sourceVisible && targetVisible
|| !sourceVisible && !targetVisible){
link.source = d;
link.target = link.source;
}
else if(sourceVisible && !targetVisible){
link.source = link._source;
link.target = link.source;
}
});
// define more links behavior here...
updateTree(d);
}
/**
* Update tree dimension
*/
function updateTreeDimension() {
tree.size([treeWidth, treeHeight]);
svg.attr("width", treeWidth + treeMargin.right + treeMargin.left)
.attr("height", treeHeight + treeMargin.top + treeMargin.bottom)
.attr("transform", `translate(${treeMargin.left},${treeMargin.top})scale(${scale},${scale})`);
}
/**
* Resize the tree using current window dimension
*/
function resizeTreePlot() {
treeWidth = 0.9 * window.innerWidth - treeMargin.right - treeMargin.left;
treeHeight = (treeDepth + 2) * nodeHeight * 2;
updateTreeDimension();
updateTree(root);
}
// plot tree
initTree(data);
updateTree(root);
Here you can find the full demo. You can also fork the project.
I wanted to create a function who
return the altitude of the profile with a mouseover
When I have the informations in my .json, it's easy, here for exemple, the point refer to {"d": 1508, "a": 77"}. I use this function:
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDist(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.distance > d1.distance - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.distance) + "," + y(d.altitude) + ")");
focus.select("text").text(d.altitude);
}
But, I also would like, if possible, to calculate the altitude when I have 2 point too far apart. For exemple, I have this profile for {"d": 1539, "a": 58}, {"d": 1550, "a": 158}. So, I return the altitude for d=1539 and d=1550, but, can I return the altitude for d=1546 thanks to my profile?
Best Regards, Braz Damien.
codepen.io/Onchman/pen/dNpeaP Here is the code on codepen, I don't know how to add json from an external ressource, so, i tried to add it directly in the JavaScript part.
Referring to my code in this answer and applying it to your code, your mousemove function would be re-written like this:
function mousemove() {
var mouse = d3.mouse(this);
var beginning = 0,
end = areaPath.getTotalLength(),
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
pos = areaPath.getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
focus.attr("transform","translate(" + mouse[0] + "," + pos.y +")");
focus.select("text").text(y.invert(pos.y).toFixed(2));
}
Full running code:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
font-family: Lato;
font-size: 13px;
fill: black;
}
.grid path,
.grid line {
fill: none;
stroke: rgba(0, 0, 0, 0.25);
shape-rendering: crispEdges;
}
.area {
fill: darkorange;
stroke: rgba(0, 0, 0, 1);
}
.marker.client .marker-bg,
.marker.client path {
fill: rgba(255, 127, 0, 0.8);
stroke: rgba(255, 127, 0, 0.8);
stroke-width: 3;
}
.marker.server .marker-bg,
.marker.server path {
fill: rgba(0, 153, 51, 0.8);
stroke: rgba(0, 153, 51, 0.8);
stroke-width: 3;
}
.marker path {
fill: none;
}
.legend text,
.marker text {
fill: black;
font-weight: bold;
}
.marker text {
text-anchor: middle;
}
.overlay {
fill: none;
pointer-events: all;
}
.focus circle {
fill: none;
stroke: steelblue;
}
</style>
</head>
<body>
<script>
function profile(rawData) {
var margin = {
top: 20,
right: 20,
bottom: 60,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
innerwidth = width - margin.left - margin.right,
innerheight = height - margin.top - margin.bottom;
var bisectDist = d3.bisector(function(d) {
return d.distance;
}).left;
var x = d3.scaleLinear().range([0, width]);
var y = d3.scaleLinear().range([height, 0]);
var xAxis = d3.axisBottom().scale(x);
var yAxis = d3.axisLeft().scale(y);
var area = d3.area().x(function(d) {
return x(d.distance);
}).y0(height).y1(function(d) {
return y(d.altitude);
})
var svg = d3.select("body").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 + ")");
//
// d3.json('data.json', function(error, rawData) {
// if (error) {
// console.error(error);
// return;
// }
var data = rawData.map(function(d) {
return {
altitude: d.a,
distance: d.d
};
});
// data.sort(function(a, b) {
// return a.distance - b.distance;
// });
x.domain(d3.extent(data, function(d) {
return d.distance;
}));
y.domain([0, d3.max(data, function(d) {
return d.altitude;
})]);
var x_grid = d3.axisBottom().scale(x).tickSize(-height).tickFormat("");
var y_grid = d3.axisLeft().scale(y).tickSize(-width).tickFormat("");
var areaPath = svg.append('svg:path')
.datum(data)
.attr("class", "area")
.attr("d", area)
.node();
svg.append("svg:g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.append("text")
.attr("transform", "translate(0," + margin.top * 2 + ")")
.attr("x", width - (margin.right + margin.left))
.text("Distance (km)");
svg.append("svg:g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "translate(70,0)")
.attr("transform", "rotate(-90)")
.attr("y", -45).attr("dy", ".71em")
.style("text-anchor", "end")
.text("Altitude (m)");
svg.append("g")
.attr("class", "x grid")
.attr("transform", "translate(0," + height + ")")
.call(x_grid);
svg.append("g")
.attr("class", "y grid")
.call(y_grid);
var focus = svg.append("g")
.attr("class", "focus")
.style("display", "none");
focus.append("circle")
.attr("r", 4.5);
focus.append("text")
.attr("x", 9)
.attr("dy", ".35em");
svg.append("rect")
.attr("class", "overlay")
.attr("width", width)
.attr("height", height)
.on("mouseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", mousemove);
function mousemove() {
/*
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDist(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.distance > d1.distance - x0 ? d1 : d0;
focus.attr("transform", "translate(" + x(d.distance) + "," + y(d.altitude) + ")");
focus.select("text").text(d.altitude);
*/
var mouse = d3.mouse(this);
var beginning = 0,
end = areaPath.getTotalLength(),
target = null;
while (true){
target = Math.floor((beginning + end) / 2);
pos = areaPath.getPointAtLength(target);
if ((target === end || target === beginning) && pos.x !== mouse[0]) {
break;
}
if (pos.x > mouse[0]) end = target;
else if (pos.x < mouse[0]) beginning = target;
else break; //position found
}
focus.attr("transform","translate(" + mouse[0] + "," + pos.y +")");
focus.select("text").text(y.invert(pos.y).toFixed(2));
}
var markerjson = [{
"id": "1",
"name": "Depart - Vielle-Aure",
"type": "CP",
"description": "Départ",
"icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
"distance": "1488",
"altitude": "145"
}, {
"id": "2",
"name": "CP1 - Col de Portet",
"type": "CP",
"description": "1er CP",
"icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
"distance": "1496",
"altitude": "37"
}, {
"id": "3",
"name": "CP2 - Artigues",
"type": "CP",
"description": "2ème CP",
"icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
"distance": "1504",
"altitude": "145"
}, {
"id": "4",
"name": "CP3 - Col De Sencours",
"type": "CP",
"description": "3ème CP",
"icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
"distance": "1512",
"altitude": "137"
}, {
"id": "5",
"name": "CP4 - Hautacam",
"type": "CP",
"description": "4ème CP",
"icon": "../item_icons/iconCust/map-marker-checkpoint.svg",
"distance": "1521",
"altitude": "45"
}];
/*
d3.json('markersjson', function(error, markerData) {
if (error) {
console.error(error);
return;
}
*/
/*
markerData = markerjson;
var markers = markerData.map(function(marker) {
return {
id: marker.id,
name: marker.name,
type: marker.type,
description: marker.description,
icon: marker.icon,
distance: marker.distance,
};
});
markers.forEach(function(marker, i) {
setTimeout(function() {
setItem(marker, data, svg, innerheight, x, y);
}, 1000 + 500 * i);
});
*/
// });
//});
}
function setItem(marker, data, svg, innerheight, x, y) {
altitude = getAltitude(data, marker);
var radius = 20,
xPos = x(marker.distance) - radius - 3,
yPosStart = innerheight - radius - 3,
yPosEnd = y(altitude) - radius * 2;
var markerG = svg.append('g')
.attr('class', 'marker ' + marker.type.toLowerCase())
.attr('transform', 'translate(' + xPos + ', ' + yPosStart + ')')
.attr('opacity', 0);
markerG.transition()
.duration(1000)
.attr('transform', 'translate(' + xPos + ', ' + yPosEnd + ')')
.attr('opacity', 1);
markerG.append("svg:image")
.attr('class', 'marker-bg')
.attr("xlink:href", "cp-x_15_31.png")
.attr("x", "2")
.attr("width", "40")
.attr("height", "40");
markerG.append('text')
.attr('x', radius)
.attr('y', radius * 0.9)
markerG.append('text')
.attr('x', radius)
.attr('y', radius * 1.5)
.attr("transform", "translate(0,15)")
.text(marker.name);
}
function getAltitude(data, marker) {
var i = 0;
while (i < data.length) {
if (data[i].distance == marker.distance) {
return data[i].altitude;
} else {
if (i < data.length) {
if ((data[i].distance < marker.distance) && (marker.distance < data[i + 1].distance)) {
return ((data[i].altitude + data[i + 1].altitude) / 2)
}
} else {
if ((data[i - 1].distance < marker.distance) && (marker.distance < data[i].distance)) {
return ((data[i - 1].altitude + data[i].altitude) / 2)
}
}
}
i++;
}
}
function removeItem(marker, svg, innerheight, x) {
markerG.clear;
}
var profilejson = [{
"d": 1488,
"a": 145
}, {
"d": 1489,
"a": 132
}, {
"d": 1490,
"a": 70
}, {
"d": 1491,
"a": 115
}, {
"d": 1492,
"a": 44
}, {
"d": 1493,
"a": 117
}, {
"d": 1494,
"a": 9
}, {
"d": 1495,
"a": 64
}, {
"d": 1496,
"a": 37
}, {
"d": 1497,
"a": 145
}, {
"d": 1498,
"a": 14
}, {
"d": 1499,
"a": 86
}, {
"d": 1500,
"a": 119
}, {
"d": 1501,
"a": 200
}, {
"d": 1502,
"a": 23
}, {
"d": 1503,
"a": 85
}, {
"d": 1504,
"a": 145
}, {
"d": 1505,
"a": 49
}, {
"d": 1506,
"a": 145
}, {
"d": 1507,
"a": 58
}, {
"d": 1509,
"a": 124
}, {
"d": 1510,
"a": 69
}, {
"d": 1511,
"a": 14
}, {
"d": 1512,
"a": 137
}, {
"d": 1513,
"a": 45
}, {
"d": 1514,
"a": 114
}, {
"d": 1515,
"a": 186
}, {
"d": 1516,
"a": 219
}, {
"d": 1517,
"a": 199
}, {
"d": 1518,
"a": 223
}, {
"d": 1519,
"a": 28
}, {
"d": 1520,
"a": 185
}, {
"d": 1521,
"a": 45
}, {
"d": 1522,
"a": 63
}, {
"d": 1523,
"a": 18
}, {
"d": 1524,
"a": 144
}, {
"d": 1525,
"a": 17
}, {
"d": 1526,
"a": 99
}, {
"d": 1527,
"a": 214
}, {
"d": 1528,
"a": 237
}, {
"d": 1530,
"a": 194
}, {
"d": 1531,
"a": 186
}, {
"d": 1532,
"a": 19
}, {
"d": 1533,
"a": 200
}, {
"d": 1534,
"a": 23
}, {
"d": 1535,
"a": 185
}, {
"d": 1536,
"a": 45
}, {
"d": 1537,
"a": 249
}, {
"d": 1538,
"a": 145
}, {
"d": 1539,
"a": 58
}, {
"d": 1550,
"a": 158
}];
profile(profilejson);
/*
$.ajax({
url: 'profilejson',
method: 'GET',
success: function(data) {
console.log("data =", data);
profile(data);
},
error: function(data) {
console.log("err" + data)
}
});
*/
</script>
</body>
</html>
here is my code and i want to change the color of my link.
i have tried so many techniques but no effect.
please kindly help me out.
im using asp.net
<%# Page Language="C#" AutoEventWireup="true" CodeBehind="WebForm1.aspx.cs" Inherits="D3js_demo.WebForm1" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<script type="text/javascript" src="http://d3js.org/d3.v2.min.js"></script>
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script>
<title>Weighted Citation Graph</title>
<style>
path.link {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
circle {
fill: #ccc;
stroke: #333;
stroke-width: 1.5px;
}
text {
font: 10px sans-serif;
pointer-events: none;
}
text.shadow {
stroke: #fff;
stroke-width: 3px;
stroke-opacity: .8;
}
body {
background-color: white;
margin: 0px;
}
.graphContainer {
text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white, 1px 1px 0 white;
}
</style>
</head>
<body>
<form id="form1" runat="server">
<div>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
$(function () {
var color = d3.scale.category20();
var data = {
"nodes": [
{ "id": 0, "name": "paper1", "citation": 5, "group": 1 },
{ "id": 1, "name": "paper2", "citation": 8, "group": 2 },
{ "id": 2, "name": "paper3", "citation": 12, "group": 3 },
{ "id": 3, "name": "paper4", "citation": 25, "group": 4 },
{ "id": 4, "name": "paper5", "citation": 15, "group": 5 },
{ "id": 5, "name": "paper6", "citation": 5, "group": 1 },
{ "id": 6, "name": "paper7", "citation": 8, "group": 2 },
{ "id": 7, "name": "paper8", "citation": 12, "group": 3 },
{ "id": 8, "name": "paper9", "citation": 25, "group": 4 },
{ "id": 9, "name": "paper10", "citation": 15, "group": 5 }
],
"links": [
{ "source": 0, "target": 1, "name": "A-B-1", "value": 8 , "grouo": 1},
{ "source": 0, "target": 1, "name": "A-B-2", "value": 24, "grouo": 2 },
{ "source": 0, "target": 2, "name": "A-C-1", "value": 100, "grouo": 1 },
{ "source": 0, "target": 2, "name": "A-C-3", "value": 44, "grouo": 2 },
{ "source": 2, "target": 3, "name": "A-D-1", "value": 169, "grouo": 1 },
{ "source": 2, "target": 3, "name": "A-D-2", "value": 80, "grouo": 2 },
{ "source": 2, "target": 4, "name": "A-E-1", "value": 16, "grouo": 1 },
{ "source": 2, "target": 4, "name": "A-E-5", "value": 200, "grouo": 2 },
{ "source": 4, "target": 5, "name": "A-B-1", "value": 8, "grouo": 1 },
{ "source": 4, "target": 5, "name": "A-B-2", "value": 24, "grouo": 2 },
{ "source": 5, "target": 6, "name": "A-C-1", "value": 12, "grouo": 1 },
{ "source": 5, "target": 6, "name": "A-C-3", "value": 44, "grouo": 2 },
{ "source": 5, "target": 7, "name": "A-D-1", "value": 125, "grouo": 1 },
{ "source": 5, "target": 7, "name": "A-D-2", "value": 225, "grouo": 2 },
{ "source": 7, "target": 8, "name": "A-E-1", "value": 36, "grouo": 1 },
{ "source": 7, "target": 8, "name": "A-E-5", "value": 81, "grouo": 2 },
{ "source": 8, "target": 3, "name": "A-C-1", "value": 9, "grouo": 1 },
{ "source": 8, "target": 3, "name": "A-C-3", "value": 16, "grouo": 2 },
{ "source": 8, "target": 9, "name": "A-D-1", "value": 50, "grouo": 1 },
{ "source": 8, "target": 9, "name": "A-D-2", "value": 100, "grouo": 2 }
]
};
// used to store the number of links between two nodes.
// mLinkNum[data.links[i].source + "," + data.links[i].target] = data.links[i].linkindex;
var mLinkNum = {};
// sort links first
sortLinks();
// set up linkIndex and linkNumer, because it may possible multiple links share the same source and target node
setLinkIndexAndNum();
var w = 960,
h = 500;
var force = d3.layout.force()
.nodes(d3.values(data.nodes))
.links(data.links)
.size([w, h])
.linkDistance(200)
.charge(-1000)
.on("tick", tick)
.start();
var svg = d3.select(".graphContainer").append("svg:svg")
.attr("width", w)
.attr("height", h);
var path = svg.append("svg:g")
.selectAll("line")
.data(force.links())
.enter().append("svg:path")
.attr("class", "link")
.style("stroke-width", function (d) { return Math.sqrt(d.value); });
var circle = svg.append("svg:g")
.selectAll("circle")
.data(force.nodes())
.enter().append("svg:circle")
.attr("r", function (d) { return (d.citation); })
.style("fill", function (d) { return color(d.group); })
.call(force.drag);
var text = svg.append("svg:g")
.selectAll("g")
.data(force.nodes())
.enter().append("svg:g");
// A copy of the text with a thick white stroke for legibility.
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.attr("class", "shadow")
.text(function (d) { return d.name; });
text.append("svg:text")
.attr("x", 8)
.attr("y", ".31em")
.text(function (d) { return d.name; });
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
path.attr("d", function (d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
// get the total link numbers between source and target node
var lTotalLinkNum = mLinkNum[d.source.id + "," + d.target.id] || mLinkNum[d.target.id + "," + d.source.id];
if (lTotalLinkNum > 1) {
// if there are multiple links between these two nodes, we need generate different dr for each path
dr = dr / (1 + (1 / lTotalLinkNum) * (d.linkindex - 1));
}
// generate svg path
return "M" + d.source.x + "," + d.source.y +
"A" + dr + "," + dr + " 0 0 1," + d.target.x + "," + d.target.y +
"A" + dr + "," + dr + " 0 0 0," + d.source.x + "," + d.source.y;
});
// Add tooltip to the connection path
path.append("svg:title")
.text(function (d, i) { return d.name; });
circle.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
text.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
// sort the links by source, then target
function sortLinks() {
data.links.sort(function (a, b) {
if (a.source > b.source) {
return 1;
}
else if (a.source < b.source) {
return -1;
}
else {
if (a.target > b.target) {
return 1;
}
if (a.target < b.target) {
return -1;
}
else {
return 0;
}
}
});
}
//any links with duplicate source and target get an incremented 'linknum'
function setLinkIndexAndNum() {
for (var i = 0; i < data.links.length; i++) {
if (i != 0 &&
data.links[i].source == data.links[i - 1].source &&
data.links[i].target == data.links[i - 1].target) {
data.links[i].linkindex = data.links[i - 1].linkindex + 1;
}
else {
data.links[i].linkindex = 1;
}
// save the total number of links between two nodes
if (mLinkNum[data.links[i].target + "," + data.links[i].source] !== undefined) {
mLinkNum[data.links[i].target + "," + data.links[i].source] = data.links[i].linkindex;
}
else {
mLinkNum[data.links[i].source + "," + data.links[i].target] = data.links[i].linkindex;
}
}
}
});
</script>
</div>
<div id="graphContainer" class="graphContainer"></div>
</form>
</body>
</html>
If you want to color them according to the stroke-width and using the same color scale, just change your var path:
var path = svg.append("svg:g")
.selectAll("line")
.data(force.links())
.enter().append("svg:path")
.attr("class", "link")
.style("stroke-width", function (d) { return Math.sqrt(d.value); })
.style("stroke", function(d){
return color(d.value)
});
This is how it looks like: https://jsfiddle.net/4xt5v51m/
I'm using the function d3.svg.symbol() to plot symbols on my Line Chart.
I wrote this code but nothing happens.
The part to generate the symbols is:
svg.selectAll("path")
.data(donnees)
.enter().append("path")
.attr("transform", function(d) { return "translate(" + xScale(d.xValue) + "," + yScale(d.yValue) + ")"; })
.attr("d", d3.svg.symbol());
First, lets talk about your data. You have an object with a key and a value structure. You've given d3 something it can work with by iterating your object like:
for (var i in donnees) {
svg.append("path")
...
While this works, we can do better. Say we try:
var data = d3.entries(donnees);
which returns:
[Object ,Object ,Object]
key: "Cbio" key: "Cres"
value: Array[10] value: Array[10]
Now we are talking about some data d3 will really like. So with that lets rewrite your line drawing.
First, create a group for each line:
var lineGroup = svg.selectAll(".line")
.data(data)
.enter()
.append("g")
.attr("class", "line");
Now, all the line path:
lineGroup
.append("path")
.attr("d", function(d){
return line(d.value);
})
.attr("fill", "none")
.attr("stroke", function(d,i){
return colors(i);
})
.attr("stroke-width", 1);
And finally, lets do some symbols:
var types = ["circle","cross","diamond"];
lineGroup
.selectAll("symbol")
.data(function(d){
return d.value; //<-- this is a subselection, and allows you to build a symbol for each datapoint of your line
})
.enter()
.append("path")
.attr("transform", function(d) {
return "translate(" + xScale(d.xValue) + "," + yScale(d.yValue) + ")";
})
.attr("d", function(d,i,j){ //<-- the j here is the parent selection, each lineGroup
return d3.svg.symbol()
.type(types[j])(d); //<-- fancy up our symbols
})
.attr("fill", function(d,i,j){
return colors(j);
});
Full code:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
}
</style>
</head>
<body>
<script>
var w = 500,
h = 400,
padding = 30;
var donnees = {
"Cbio": [{
"xValue": 1,
"yValue": 21
}, {
"xValue": 20,
"yValue": 150
}, {
"xValue": 40,
"yValue": 200
}, {
"xValue": 60,
"yValue": 150
}, {
"xValue": 80,
"yValue": 125
}, {
"xValue": 100,
"yValue": 100
}, {
"xValue": 120,
"yValue": 90
}, {
"xValue": 140,
"yValue": 80
}, {
"xValue": 160,
"yValue": 80
}, {
"xValue": 180,
"yValue": 40
}],
"Cres": [{
"xValue": 1,
"yValue": 350
}, {
"xValue": 20,
"yValue": 30
}, {
"xValue": 40,
"yValue": 20
}, {
"xValue": 60,
"yValue": 30
}, {
"xValue": 80,
"yValue": 30
}, {
"xValue": 100,
"yValue": 30
}, {
"xValue": 120,
"yValue": 20
}, {
"xValue": 140,
"yValue": 30
}, {
"xValue": 160,
"yValue": 30
}, {
"xValue": 180,
"yValue": 30
}],
"tsol84": [{
"xValue": 1,
"yValue": 10
}, {
"xValue": 20,
"yValue": 15
}, {
"xValue": 40,
"yValue": 20
}, {
"xValue": 60,
"yValue": 25
}, {
"xValue": 80,
"yValue": 30
}, {
"xValue": 100,
"yValue": 25
}, {
"xValue": 120,
"yValue": 25
}, {
"xValue": 140,
"yValue": 25
}, {
"xValue": 160,
"yValue": 30
}, {
"xValue": 180,
"yValue": 25
}]
};
var svg = d3.select("body")
.append("svg")
.attr("width", w)
.attr("height", h);
var data = d3.entries(donnees);
var xScale = d3.scale.linear()
.domain([0,
d3.max(d3.max(data, function(d) {
return d.value;
}), function(d){
return d.xValue;
})
])
.range([padding, w - padding]);
var yScale = d3.scale.linear()
.domain([0, d3.max(d3.max(data, function(d) {
return d.value;
}), function(d){
return d.yValue;
})
])
.range([h - padding, padding]);
var xAxis = d3.svg.axis()
.scale(xScale)
.orient('bottom');
var yAxis = d3.svg.axis()
.scale(yScale)
.orient('left');
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + (h - padding) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + padding + ",0)")
.call(yAxis);
var line = d3.svg.line()
.x(function(d) {
return xScale(d.xValue)
})
.y(function(d) {
return yScale(d.yValue)
});
var colors = d3.scale.category20();
var lineGroup = svg.selectAll(".line")
.data(data)
.enter()
.append("g")
.attr("class", "line");
lineGroup
.append("path")
.attr("d", function(d){
return line(d.value);
})
.attr("fill", "none")
.attr("stroke", function(d,i){
return colors(i);
})
.attr("stroke-width", 1);
var types = ["circle","cross","diamond"];
lineGroup
.selectAll("symbol")
.data(function(d){
return d.value;
})
.enter()
.append("path")
.attr("transform", function(d) {
return "translate(" + xScale(d.xValue) + "," + yScale(d.yValue) + ")";
})
.attr("d", function(d,i,j){
return d3.svg.symbol()
.type(types[j])(d);
})
.attr("fill", function(d,i,j){
return colors(j);
});
</script>
</body>
</html>