nvd3 line plus bar chart with variable circle sizes - nvd3.js

I am using nvd3 line plus bar chart and am trying to change the size of the circles in the line chart. (Each circle with a different radius on the line chart)
To make the points visible on the line chart, I used the following CSS code -
#test-plot .nv-linePlusBar .nv-point
{
fill: #ff5f00;
fill-opacity: 0.6;
}
I am calling the addRadius method after the chart is drawn.
function addRadius() {
for (var i = 0; i < plotdata[0].values.length ; i++) {
var rad = parseFloat(plotdata[0].values[i].r);
$('#test-plot .nv-point-' + i).attr('r', rad);
}
}
setTimeout(addRadius, 500);
This code worked for older version of nvd3 but not version 1.8.1 or higher.
I saw some solutions here but they do not work for the latest versions of nvd3.

Triy with the following CSS:
.nvd3 .nv-groups .nv-point {
stroke-opacity: 0.5 !important;
}
and change the selector in the addRadius() function:
function addRadius() {
var dt = data[1].values;
console.log(dt, dt.length)
for (var i = 0; i < dt.length; i++) {
// just an example
var w = 20 * dt[i][1] / 600;
$('.nvd3 .nv-groups .nv-point-' + i).css('stroke-width', w);
}
}
Check this snippet:
var data = [{
"key": "Quantity",
"bar": true,
"values": [
[1301544000000, 2000],
[1304136000000, 2500],
[1306814400000, 1800],
[1309406400000, 2100],
[1312084800000, 2100],
[1314763200000, 2800]
]
}, {
"key": "Price",
"values": [
[1301544000000, 348.5075, 20],
[1304136000000, 350.13, 30],
[1306814400000, 347.83, 10],
[1309406400000, 335.67, 5],
[1312084800000, 390.48, 40],
[1314763200000, 384.83, 10]
]
}]
$(document).ready(function() {
nv.addGraph(function() {
var chart = nv.models.linePlusBarChart()
.focusEnable(false)
.margin({
top: 30,
right: 60,
bottom: 50,
left: 70
})
.x(function(d, i) {
return i
})
.y(function(d) {
return d[1]
})
.color(d3.scale.category10().range());
chart.xAxis
.showMaxMin(false)
.tickFormat(function(d) {
var dx = data[0].values[d] && data[0].values[d][0] || 0;
return d3.time.format('%x')(new Date(dx))
});
chart.y1Axis
.tickFormat(d3.format(',f'));
chart.y2Axis
.tickFormat(function(d) {
return '$' + d3.format(',f')(d)
});
chart.bars.forceY([0]);
d3.select('#chart svg')
.datum(data)
.transition().duration(500)
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
});
function addRadius() {
var dt = data[1].values;
console.log(dt, dt.length)
for (var i = 0; i < dt.length; i++) {
//var rad = parseFloat(data[1].values[i].r);
//$('.nvd3 .nv-groups .nv-point-' + i).attr('r', rad);
var w = dt[i][2];
console.log("w" + i, w);
$('.nvd3 .nv-groups .nv-point-' + i).css('stroke-width', w);
}
}
setTimeout(addRadius, 500);
#chart svg {
height: 400px;
}
.nvd3 .nv-groups .nv-point {
stroke: red;
fill: red;
stroke-opacity: 0.5 !important;
/*stroke-width: 20px;*/
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://rawgit.com/novus/nvd3/master/build/nv.d3.js"></script>
<link href="https://rawgit.com/novus/nvd3/master/build/nv.d3.css" rel="stylesheet"/>
<div id="chart">
<svg></svg>
</div>
Fiddle: http://jsfiddle.net/beaver71/cr8ejg0n/

Related

d3 - Update Width and X for smooth transition in Horizontal Stacked Bar Chart

Edit: Not sure why the bars are not showing up! Just after how to get the update animation to work on this visualization, using width and x to update the bars after an arc is selected!
I've got a horizontal stacked bar chart that I am trying to update smoothly as seen in this verticle example (https://bl.ocks.org/HarryStevens/7e3ec1a6722a153a5d102b6c42f4501d). I am struggling to get it to work. I have tried using CSS and transitioning the 'width' attribute but it doesn't work as intended. I was wondering if someone might be able to help.
How it works is that if you select one of the arcs, the data would be recalculated. Can you aid me?
var w = 800;
var h = 800;
const svg = d3.select('#radial-chart')
.append('svg')
.attr('width', w)
.attr('height', h);
const arc = d3.arc()
const handleMouseOver = (d, i, n) => {
svg.selectAll('path')
.transition().duration(300)
.style('opacity', 0.35);
d3.select(n[i])
.transition().duration(300)
.style('opacity', 0.35)
}
const handleMouseOut = (d, i, n) => {
svg.selectAll('path')
.transition().duration(300)
.style('opacity', 0.35)
.style('stroke-width', 0)
}
var arcData = [{
domain: '1',
innerRadius: 0,
outerRadius: (h / 1.75),
startAngle: Math.PI * -20 / 180,
endAngle: Math.PI * 20 / 180
},
{
domain: '2',
innerRadius: 0,
outerRadius: (h / 2),
startAngle: Math.PI * 20 / 180,
endAngle: Math.PI * 60 / 180
}
];
var colorScale = d3.scaleOrdinal()
.domain(["1", "2"])
.range(["#E8A82B", "#000000"]);
const slices = arcData.map(d => arc(d));
svg.selectAll('path')
.data(slices)
.enter()
.append('path')
.attr('transform', 'translate(325,550)')
.attr("d", d => d)
.attr("id", (d, i) => "arc" + i)
.style("fill", (d, i) => colorScale(i))
.style("z-index", 100)
.style("opacity", 0.5)
// .attr('class', 'selected')
.on('mouseover', handleMouseOver)
.on('mouseout', handleMouseOut)
.on('click', function(d, i) {
d3.select(this).classed("selected", d3.select(this).classed("selected") ? false : true)
d3.select('image#arc' + i).classed("selected", d3.select(this).classed("selected") ? true : false)
})
//Bar
const bar1 = d3.select('#bar1').append('svg').attr('width', 800).attr('height', 175);
var colorScale = d3.scaleOrdinal()
.range(["#02029D", "#223AAF", '#446FBD', '#669CCB']);
const x = d3.scaleLinear()
.range([0, 800]);
const y = d3.scaleLinear()
.rangeRound([200])
var n_sumSeventyk = 500,
n_sum71kto149k = 600,
n_sum150kto249k = 200,
n_sum250k = 100
if ($('path#arc0').hasClass('selected') === true) {
var n_sumSeventyk = n_sumSeventyk - 100,
n_sum71kto149k = n_sum71kto149k - 300,
n_sum150kto249k = n_sum150kto249k - 25,
n_sum250k = n_sum250k - 65
} else {}
if ($('path#arc1').hasClass('selected') === true) {
var n_sumSeventyk = n_sumSeventyk - 75,
n_sum71kto149k = n_sum71kto149k - 200,
n_sum150kto249k = n_sum150kto249k - 90,
n_sum250k = n_sum250k - 80
} else {};
var bar1Data = [{
position: 1,
label: '70k',
value: n_sumSeventyk
},
{
position: 2,
label: '71K - 149K',
value: n_sum71kto149k
},
{
position: 3,
label: '71K - 149K',
value: n_sum150kto249k
},
{
position: 4,
label: '71K - 149K',
value: n_sum250k
}
];
//sort bars based on value
bar1Data = bar1Data.sort(function(a, b) {
return d3.descending(a.value, b.value);
})
d3.interval(function() {
var n_sumSeventyk = 600;
if ($('path#arc0').hasClass('selected') === true) {
var n_sumSeventyk = n_sumSeventyk - 100
var bar1Data = [{
position: 1,
label: '70k',
value: n_sumSeventyk
},
{
position: 2,
label: '71K - 149K',
value: n_sum71kto149k
},
{
position: 3,
label: '71K - 149K',
value: n_sum150kto249k
},
{
position: 4,
label: '71K - 149K',
value: n_sum250k
},
];
} else {
var n_sumSeventyk = n_sumSeventyk
var bar1Data = [{
position: 1,
label: '70k',
value: n_sumSeventyk,
},
{
position: 2,
label: '71K - 149K',
value: n_sum71kto149k
},
{
position: 3,
label: '71K - 149K',
value: n_sum150kto249k
},
{
position: 4,
label: '71K - 149K',
value: n_sum250k
},
];
}
update(bar1Data)
}, 400)
update(bar1Data)
;
bar1.append("text")
.attr("x", (400))
.attr("y", 80)
.attr("text-anchor", "middle")
.style('font-family', 'marsBook')
.style("font-size", "24px")
.text("Household Income")
function update(bar1Data) {
x.domain([0, d3.sum(bar1Data, d => d.value)])
y.domain([0, d3.max(bar1Data).value])
//Join data to rects
const rects = bar1.selectAll('rect')
.data(bar1Data)
rects.exit().remove();
rects.attr('width', (d) => {
return x(d.value)
})
.attr('height', 200)
.attr('class', (d, i) => {
return 'bar' + i
})
.attr('x', function(d, i) {
return x(sum(bar1Data.map((e) => e.value), 0, i));
})
.attr('y', 100)
rects.enter()
.append('rect')
.attr('width', (d) => {
return x(d.value)
})
.attr('height', 200)
.attr('x', (d, i) => {
// Note that I use `.map` here to take only the `.value` attribute
// from the data and create an array of numbers. Then I pass it to `x`
// to be transformed (could also be the other way around if you'd like,
// so sum(bar1Data.map((e) => x(e.value)), 0, i)
return x(sum(bar1Data.map((e) => e.value), 0, i));
})
.attr('y', 100)
.style("fill", (d, i) => colorScale(i))
function sum(array, start, end) {
var total = 0;
for (var i = start; i < end; i++) total += array[i];
return total;
}
console.log(bar1Data)
}
}
/* Header */
html {
font-family: Arial, Helvetica, sans-serif;
}
body {
margin: 0;
padding: 0;
}
* {
box-sizing: border-box;
}
header {
width: 100%;
top: 0;
margin: 0;
padding-top: 0;
padding: 0.781vw 0;
padding-bottom: 0;
background-color: transparent;
}
p {
font-family: MarsBook;
margin: 0;
}
ul {
display: flex;
flex-direction: row;
align-items: center;
list-style: none;
width: 90%;
padding: 0;
margin: 0;
z-index: 0;
background-color: transparent;
}
header h1 {
color: #0000a0;
margin-left: 2vw;
margin-right: 0.5vw;
font-family: marsBold;
flex-wrap: nowrap;
white-space: nowrap;
font-size: 2vw;
}
header p {
font-size: 2.5vw;
color: #00dcfa;
}
header .subtitle {
margin-left: 0.5vw;
font-family: marsBold;
font-size: 2vw;
flex-wrap: nowrap;
white-space: nowrap;
}
.container {
display: flex;
flex-direction: row;
}
#bars {
display: flex;
flex-direction: column;
margin-left: 150px;
margin-top: -10px;
z-index: 100000000000000000000
}
#bars h1 {
align-content: center;
text-align: center;
}
.selected {
opacity: 1!important
}
image#arc0,
image#arc1,
image#arc2,
image#arc3,
image#arc4,
image#arc5,
image#arc6,
image#arc7,
image#arc8 {
opacity: 0.35
}
image#arc0:hover,
image#arc1:hover,
image#arc2:hover,
image#arc3:hover,
image#arc4:hover,
image#arc5:hover,
image#arc6:hover,
image#arc7:hover,
image#arc8:hover {
opacity: 1
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<div id='radial-chart'></div>
<div id='bar1'></div>
I did it using CSS on the rects on the dom.
rect {
transition: width 0.3s, x 0.3s ease;
-webkit-transition: x 0.3s, width 0.3s ease;
-moz-transition: width 0.3s, x 0.3s ease;
}

D3 Force-directed graph - how to filter nodes and associated links?

I'm new to d3 (and javascript) and this is my first time posting a question on Stackoverflow. Please let me know if my question is unclear or inappropriate. I would be very appreciated if you give any advice or help.
I'am having a difficulty with filtering nodes in force directed graph. I have searched and used the code posted on other question (D3 force-directed graph - filter nodes and associated links) to hide nodes and its associated links. However, I wasn't able to hide nodes whose links are all invisible.
Here's what I'm trying to do:
If all the links with that node are invisible, the node should also be invisible. Otherwise, if any link related to that node is visible, the node should be visible. I have found this code from jsfiddle.net (zhanghuancs/cuYu8/) but I was not able to use this code on mine.(I cound't figure out how to link jsfiddle code here.)
Could anyone help me?
Here's my code:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>Force Layout</title>
<script type="text/javascript" src="http://code.jquery.com/jquery-2.1.0.js"></script>
<script type="text/javascript" src="http://code.jquery.com/ui/1.11.0/jquery-ui.min.js"></script>
<link rel="stylesheet" type="text/css" href="http://code.jquery.com/ui/1.11.0/themes/smoothness/jquery-ui.css">
<script type="text/javascript" src="http://d3js.org/d3.v3.min.js"></script>
<script type="text/javascript" src="https://labratrevenge.com/d3-tip/javascripts/d3.tip.v0.6.3.js"></script>
<style id="compiled-css" type="text/css">
.node {
stroke: #fff;
stroke-width: 0.5px;
}
.link {
stroke: #999;
stroke-opacity: .1;
}
d3-tip {
line-height: 1;
color: black;
}
div {
display: inline-block;
}
form {
display: inline-block;
}
svg {
border: solid 1px blue;
}
body,
.container {
background-color: white;
margin: 5px;
}
.graphContainer {
text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white, 1px 1px 0 white;
}
#sidebar{
position: absolute;
z-index: 2;
background-color: #FFF;
padding: 10px;
margin: 5px;
border: 1px solid #6895b4;
min-height: 3px;
width: 100px;
top:45px;
right:220px;
}
.item-group {
margin-bottom: 5px;
}
.item-group .item-label {
width: 90px;
text-align: right;
font-family: Arial,sans-serif;
font-size: 14px;
font-weight: bold;
position: relative;
min-height: 1px;
margin-top: 5px;
display: inline;
padding-right: 5px;
font-size: .90em;
}
.checkbox-interaction-group {
margin-left: 10px;
margin-top: 5px;
clear: both;
}
.checkbox-container {
display: block;
min-height: 30px;
vertical-align: middle;
margin-left: 10px;
}
.checkbox-container label{
display:inline;
margin-bottom: 0px;
}
</style>
</head>
<body>
<div id="container" class="container">
<div id="sidebar" style="display: none;">
<div class="item-group">
<label class="item-label">Filter</label>
<div id="filterContainer" class="filterContainer checkbox-interaction-group"></div>
</div>
</div>
<div id="graphContainer" class="graphContainer">
<script type="application/json" id="patent">
{
"nodes":[
{"label": "label1","title":"label1","group": "group1", "type": "label1", "s":1},
{"label": "label2","title":"label2","group": "group1", "type": "label2","s":1},
{"label": "label3","title":"label3","group": "group1", "type": "label3","s":1},
{"id":"5712454", "title": "title1", "group": "group2", "s":0},
{"id":"5497941", "title": "title2", "group": "group2", "s":0},
{"id":"5517952", "title": "title3", "group": "group2", "s":0},
{"id":"4854277", "title": "title4", "group": "group2", "s":0},
{"id":"9556782", "title": "title5", "group": "group2", "s":0}
],
"links":[
{"source": 3, "target": 0, "value": 1},
{"source": 3, "target": 1, "value": 1},
{"source": 3, "target": 2, "value": 1},
{"source": 4, "target": 0, "value": 1},
{"source": 5, "target": 2, "value": 1},
{"source": 6, "target": 1, "value": 1},
{"source": 7, "target": 2, "value": 1},
{"source": 7, "target": 0, "value": 1},
{"source": 6, "target": 2, "value": 1},
{"source": 5, "target": 1, "value": 1}
]
}
</script>
<!-- TODO: Missing CoffeeScript 2 -->
<script type="text/javascript">
//Constants for the SVG
var width = window.innerWidth,
height = window.innerHeight-47;
//Set up the colour scale
var color = d3.scale.category10();
//Set up the force layout
var force = d3.layout.force()
.charge(-200)
.linkDistance(20)
.size([width, height]);
// Set up zoom behavior
var zoom = d3.behavior.zoom().scaleExtent([0.1,5]).on("zoom",redraw);
//Append a SVG to the body of the html page. Assign this SVG as an object to svg
var svg = d3.select("body")
.append("svg")
.attr("width", width-240)
.attr("height", height)
.call(zoom)
.on("dblclick.zoom",null)
.append('g');
var svg2 = d3.select("body")
.append("svg")
.attr("width", 200)
.attr("height", height);
//Set up tooltip
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function (d) {
return d.title + "</span>";
})
svg.call(tip);
//Read the data from the mis element
var patent = document.getElementById('patent').innerHTML;
graph = JSON.parse(patent);
//Creates the graph data structure out of the json data
force.nodes(graph.nodes)
.links(graph.links)
.start()
.friction(0.5);
//Create all the line svgs but without locations yet
var link = svg.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function (d) {return Math.sqrt(d.value);
});
//Do the same with the circles for the nodes - no
var node = svg.selectAll(".node")
.data(graph.nodes)
.enter().append("path")
.attr("class", "node")
.attr("d", d3.svg.symbol()
.type(function(d) { return d3.svg.symbolTypes[d.s]; }))
.style("fill", function (d) {return color(d.group);})
.call(force.drag)
.on('dblclick', connectedNodes)
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
.on("click", function(d){
if (d3.event.shiftKey) {
var url = "https://patents.google.com/patent/US"+ d.id
alert("Redirecting you to " + url)
window.open(url,"", "width=800,height=800");
}
});
var label = svg.selectAll(".mytext")
.data(graph.nodes)
.enter()
.append("text")
.text(function (d) { return d.label; })
.style("text-anchor", "middle")
.style("fill","gray")
.style("font-family", "Arial")
.style("font-size", 8);
//Zoom and Pan function
function redraw() {
svg.attr("transform",
"translate(" + d3.event.translate + ")"
+ " scale(" + d3.event.scale + ")");
}
var drag = force.drag()
.on("dragstart", function(d) {
d3.event.sourceEvent.stopPropagation();
});
//Now we are giving the SVGs co-ordinates - the force layout is generating the co-ordinates which this code is using to update the attributes of the SVG elements
force.on("tick", function () {
link.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("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
label.attr("x", function(d){ return d.x; })
.attr("y", function (d) {return d.y - 10; });
});
//Highlight function
//Toggle stores whether the highlighting is on
var toggle = 0;
//Create an array logging what is connected to what
var linkedByIndex = {};
for (i = 0; i < graph.nodes.length; i++) {
linkedByIndex[i + "," + i] = 1;
};
graph.links.forEach(function (d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
//This function looks up whether a pair are neighbours
function neighboring(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
function connectedNodes() {
if (toggle == 0) {
//Reduce the opacity of all but the neighbouring nodes
d = d3.select(this).node().__data__;
node.style("opacity", function (o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
});
label.style("opacity", function (o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
});
link.style("opacity", function (o) {
return d.index==o.source.index | d.index==o.target.index ? 1 : 0.1;
});
//Reduce the op
toggle = 1;
} else {
//Put them back to opacity=1
node.style("opacity", 1);
link.style("opacity", 1);
label.style("opacity",1);
toggle = 0;
}
}
//Search function
var optArray = [];
for (var i = 0; i < graph.nodes.length; i++) {
optArray.push(graph.nodes[i].title);
}
optArray = optArray.sort();
$(function () {
$("#search").autocomplete({
source: optArray
});
});
window.searchNode = searchNode;
function searchNode() {
//find the node
var selectedVal = document.getElementById('search').value;
if (selectedVal == "none") {
node.style("stroke", "white").style("stroke-width", "1");
} else {
var selected = node.filter(function (d, i) {
return d.title != selectedVal;
});
selected.style("opacity", "0");
link.style("opacity", "0");
label.style("opacity", "0");
d3.selectAll(".node, .link").transition()
.duration(2000)
.style("opacity", 1);
label.transition()
.duration(2000)
.style("opacity",1);
var selectedNode = node
.filter(function (d, i) { return d.title == selectedVal; })
.datum();
var scale = zoom.scale();
var desiredPosition = { x: (width-240)/2, y: height/2}; // constants, set to svg center point
zoom.translate([desiredPosition.x - selectedNode.x*scale, desiredPosition.y - selectedNode.y*scale]);
zoom.event(svg);
}
}
//Legend
var legend = svg2.selectAll(".legend")
.data(color.domain())
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("x", 5)
.attr("y",5)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("x", 30)
.attr("y", 13)
.attr("dy", ".35em")
.style("text-anchor", "start")
.text(function(d) { return d });
// call method to create filter
createFilter();
// method to create filter
function createFilter(){
d3.select(".filterContainer").selectAll("div")
.data(["label1","label2","label3"])
.enter()
.append("div")
.attr("class", "checkbox-container")
.append("label")
.each(function(d) {
// create checkbox for each data
d3.select(this).append("input")
.attr("type", "checkbox")
.attr("id", function(d) {return "chk_" + d;})
.attr("checked", true)
.on("click", function(d, i) {
// register on click event
var lVisibility = this.checked? "visible":"hidden";
filterGraph(d, lVisibility);
})
d3.select(this).append("span")
.text(function(d){return d;});
});
$("#sidebar").show(); // show sidebar
}
var hidden_nodes =[];
// Method to filter graph
function filterGraph(aType, aVisibility){
// change the visibility of the node
// if all the links with that node are invisibile, the node should also be invisible
// otherwise if any link related to that node is visibile, the node should be visible
// change the visibility of the connection link
node.style("visibility", function(o) {
var lOriginalVisibility = $(this).css("visibility");
if (o.type == aType) {
if (aVisibility == "hidden")
{
hidden_nodes.push(o.title);
}
else
{
index = hidden_nodes.indexOf(o.title);
if (index > -1)
{
hidden_nodes.splice(index, 1);
}
}
}
return o.type === aType ? aVisibility : lOriginalVisibility;
});
label.style("visibility", function(o) {
var lOriginalVisibility = $(this).css("visibility");
if (o.type == aType) {
if (aVisibility == "hidden")
{
hidden_nodes.push(o.title);
}
else
{
index = hidden_nodes.indexOf(o.title);
if (index > -1)
{
hidden_nodes.splice(index, 1);
}
}
}
return o.type === aType ? aVisibility : lOriginalVisibility;
});
link.attr("display", function (o) {
////Here the structure of the the link can vary, sometimes it is o["source"]["name"], sometimes it is o["source"]["name"], check it out before you fill in.
var source_name = o["source"]["title"];
var target_name = o["target"]["title"];
var result = hidden_nodes.indexOf(source_name) != -1 || hidden_nodes.indexOf(target_name) != -1 ? "none" : "auto"
return result;
})
;
}
</script>
</div>
</body>
</html>

d3 on click on circle pause and resume transition of marker along line

I would like help to correct my code to click the marker circle element to pause or resume transition of this element along the line. My code moves marker along a line and I can pause and resume this transition using on click on button element but I would like to be able to click on the marker circle itself, not the button. I have used various references including :
http://www.nytimes.com/interactive/2013/09/25/sports/americas-cup-course.html
http://jsfiddle.net/meetamit/UJuWX/3/
http://jsfiddle.net/Y62Hq/2/
D3 tween - pause and resume controls
I would ultimately like to be able animate a marker along a geo path, pause and resume this at points along the path and click through on these points.
this is my code so far:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Need help</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v3.min.js"></script>
<style type="text/css">
body{
font-family:"Helvetica Neue", Helvetica, sans-serif;
color: red;
}
button {
position: absolute;
top: 15px;
left: 10px;
background: #004276;
padding-right: 26px;
border-radius: 2px;
cursor: pointer;
}
circle {
fill: steelblue;
stroke: pink;
stroke-width: 3px;
}
.point{
fill:green;
}
.line{
fill: none;
stroke: red;
stroke-width: 4;
stroke-dasharray: 4px,8px;
}
</style>
</head>
<body>
<button>Start</button>
<script>
var w = 960,
h = 500;
var duration = 10000;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
var line = d3.line()
.x(function(d){return (d)[0];})
.y(function(d){return (d)[1];});
var data =
[
[480, 200],
[580, 400],
[680, 100],
[780, 300],
[180, 300],
[280, 100],
[380, 400]
];
//path to animate
var linepath = svg.append("path")
.data([data])
.attr("d", line)
.attr('class', 'line')
.attr("d", function(d){
console.log(this);
return line(d)
});
var points = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 7)
.attr("transform", function(d) { return "translate(" + (d) + ")"; })
.attr("class", "point");
var pauseValues = {
lastTime: 0,
currentTime: 0
};
var marker = svg.append("circle")
.attr("r", 19)
.attr("transform", "translate(" + (data[0]) + ")")
.on('click', function(d,i){
d3.select(this)
.style("fill", "orange")
.transition()
});
function transition() {
marker.transition()
.duration(duration - (duration * pauseValues.lastTime))
.attrTween("transform", translateAlong(linepath.node()))
.on("end", function(){
pauseValues = {
lastT: 0,
currentT: 0
};
transition()
});
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
t += pauseValues.lastTime;
var p = path.getPointAtLength(t * l);
pauseValues.currentTime = t;
return "translate(" + p.x + "," + p.y + ")";
};
};
}
d3.select('button').on('click',function(d,i){
var self = d3.select(this);
if (self.text() == "Pause"){
self.text('Start');
marker.transition()
.duration(0);
setTimeout(function(){
pauseValues.lastTime = pauseValues.currentTime;
}, 100);
}else{
self.text('Pause');
transition();
}
});
</script>
</body>
</html>
To check if the circle is moving in the click function use d3.active(), which...
... returns null if there is no such active transition on the specified node.
Like this:
.on('click', function(d, i) {
if (d3.active(this)) {
marker.transition();
setTimeout(function() {
pauseValues.lastTime = pauseValues.currentTime;
}, 100);
} else {
transition();
}
});
Here is your code with that change:
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-queue.v3.min.js"></script>
<style type="text/css">
body {
font-family: "Helvetica Neue", Helvetica, sans-serif;
color: red;
}
button {
position: absolute;
top: 15px;
left: 10px;
background: #004276;
padding-right: 26px;
border-radius: 2px;
cursor: pointer;
}
circle {
fill: steelblue;
stroke: pink;
stroke-width: 3px;
}
.point {
fill: green;
}
.line {
fill: none;
stroke: red;
stroke-width: 4;
stroke-dasharray: 4px, 8px;
}
</style>
<body>
<button>Start</button>
<script>
var w = 960,
h = 500;
var duration = 10000;
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("height", h);
var line = d3.line()
.x(function(d) {
return (d)[0];
})
.y(function(d) {
return (d)[1];
});
var data = [
[480, 200],
[580, 400],
[680, 100],
[780, 300],
[180, 300],
[280, 100],
[380, 400]
];
//path to animate
var linepath = svg.append("path")
.data([data])
.attr("d", line)
.attr('class', 'line')
.attr("d", function(d) {
return line(d)
});
var points = svg.selectAll("circle")
.data(data)
.enter()
.append("circle")
.attr("r", 7)
.attr("transform", function(d) {
return "translate(" + (d) + ")";
})
.attr("class", "point");
var pauseValues = {
lastTime: 0,
currentTime: 0
};
var marker = svg.append("circle")
.attr("r", 19)
.attr("transform", "translate(" + (data[0]) + ")")
.on('click', function(d, i) {
if (d3.active(this)) {
marker.transition();
setTimeout(function() {
pauseValues.lastTime = pauseValues.currentTime;
}, 100);
} else {
transition();
}
});
function transition() {
marker.transition()
.duration(duration - (duration * pauseValues.lastTime))
.attrTween("transform", translateAlong(linepath.node()))
.on("end", function() {
pauseValues = {
lastT: 0,
currentT: 0
};
transition()
});
}
function translateAlong(path) {
var l = path.getTotalLength();
return function(d, i, a) {
return function(t) {
t += pauseValues.lastTime;
var p = path.getPointAtLength(t * l);
pauseValues.currentTime = t;
return "translate(" + p.x + "," + p.y + ")";
};
};
}
d3.select('button').on('click', function(d, i) {
var self = d3.select(this);
if (self.text() == "Pause") {
self.text('Start');
marker.transition()
.duration(0);
setTimeout(function() {
pauseValues.lastTime = pauseValues.currentTime;
}, 100);
} else {
self.text('Pause');
transition();
}
});
</script>
</body>

How to rotate the text labels for the x Axis of a nvd3.js line chart

I am new to nvd3 and d3 js.I am creating a line chart using nvd3. Now i want to rotate the text labels for the x Axis of the line chart.How to implement this?
I tried using
var xTicks = d3.selectAll('g.tick');
xTicks
.selectAll('text')
.attr('transform', function(d,i,j) { return ' rotate(-90 0,0)' }) ;
but was unsuccessful
My current code is
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link href="../build/nv.d3.css" rel="stylesheet" type="text/css">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.2/d3.min.js" charset="utf-8"></script>
<script src="../build/nv.d3.js"></script>
<style>
text {
font: 12px sans-serif;
}
svg {
display: block;
}
html, body, #chart1, svg {
margin: 0px;
padding: 0px;
height: 100%;
width: 100%;
}
.dashed {
stroke-dasharray: 5,5;
}
</style>
<script type="text/javascript">
function download()
{
img = new Image(),
serializer = new XMLSerializer(),
svgStr = serializer.serializeToString(document.getElementById('svg'));
img.src = 'data:image/svg+xml;base64,'+window.btoa(svgStr);
// You could also use the actual string without base64 encoding it:
//img.src = "data:image/svg+xml;utf8," + svgStr;
var canvas = document.createElement("canvas");
var w=3000;
var h=3000;
canvas.width = w;
canvas.height = h;
canvas.getContext("2d").drawImage(img,0,0,w,h);
var imgURL = canvas.toDataURL("image/png");
var dlLink = document.createElement('a');
dlLink.download = "image";
dlLink.href = imgURL;
dlLink.dataset.downloadurl = ["image/png", dlLink.download, dlLink.href].join(':');
document.body.appendChild(dlLink);
dlLink.click();
document.body.removeChild(dlLink);
}
</script>
</head>
<body class='with-3d-shadow with-transitions'>
<div style="position:absolute; top: 0; left: 0;">
<button onclick="randomizeFillOpacity();">Randomize fill opacity</button>
<button onclick="expandLegend();">Expand/Contract Legend</button>
<script>
var expandLegend = function() {
var exp = chart.legend.expanded();
chart.legend.expanded(!exp);
chart.update();
}
</script>
</div>
<div id="chart1" width="100%" height='100%'></div>
<button onclick="download()">Download</button>
<script>
// Wrapping in nv.addGraph allows for '0 timeout render', stores rendered charts in nv.graphs, and may do more in the future... it's NOT required
var chart;
var data;
var randomizeFillOpacity = function() {
var rand = Math.random(0,1);
for (var i = 0; i < 100; i++) { // modify sine amplitude
data[4].values[i].y = Math.sin(i/(5 + rand)) * .4 * rand - .25;
}
data[4].fillOpacity = rand;
chart.update();
};
nv.addGraph(function() {
chart = nv.models.lineChart()
.options({
transitionDuration: 300,
useInteractiveGuideline: true
})
;
// chart sub-models (ie. xAxis, yAxis, etc) when accessed directly, return themselves, not the parent chart, so need to chain separately
chart.xAxis
.axisLabel("Time (s)")
.tickFormat(d3.format(',.1f'))
.staggerLabels(true)
;
chart.yAxis
.axisLabel('Voltage (v)')
.tickFormat(function(d) {
if (d == null) {
return 'N/A';
}
return d3.format(',.2f')(d);
})
;
data = sinAndCos();
d3.select('#chart1').append('svg')
.datum(data)
.attr("id","svg")
.attr("height","1000")
.attr("width","1000")
.call(chart);
nv.utils.windowResize(chart.update);
return chart;
});
function sinAndCos() {
var /*sin = [],
sin2 = [],*/
cos = [],
/* rand = [],*/
rand2 = []
;
for (var i = 0; i < 100; i++) {
cos.push({x: i, y: .5 * Math.cos(i/10)});
rand2.push({x: i, y: Math.cos(i/10) + Math.random() / 10 })
}
return [
{
values: cos,
key: "Cosine Wave",
color: "#2ca02c"
},
{
values: rand2,
key: "Random Cosine",
color: "#667711",
strokeWidth: 3.5,
fillOpacity: .1,
classed: 'dashed',
area: true,
}
];
}
</script>
</body>
</html>
This will work:
chart.xAxis
.rotateLabels(-45)

Uncaught TypeError: Cannot read property 'length' of undefined

I am trying to get some data in my map, however I have the following error:
Uncaught TypeError: Cannot read property 'length' of undefined.
This is my code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>D3 Test</title>
<script type="text/javascript" src="http://localhost/webserver/d3/d3.js"></script>
<script type="text/javascript" src="http://localhost/webserver/topojson/topojson.js"></script>
<style type="text/css">
div.bar {
display: inline-block;
width: 20px;
height: 75px;
background-color: teal;
margin-right: 2px;
}
.pumpkin {
fill: rgba(128, 0, 128, 0.75);
stroke: yellow;
stroke-width: 5;
}
.apple {
fill: rgba(0, 255, 0, 0.55);
stroke: green;
stroke-width: 15;
}
.orange {
fill: rgba(255, 255, 0, 0.55);
stroke: orange;
stroke-width: 10;
}
.subunit { fill: #cdc; }
.subunit-label {
fill: #777;
fill-opacity: .25;
font-size: 30px;
font-weight: 300;
text-anchor: middle;}
.provincie {fill: none; }
.Utrecht {fill: #ddd; }
.Zuid-Holland {fill: #dde; }
.Noord-Holland {fill: #dee; }
.Drenthe {fill: #aae; }
.Gelderland {fill: #eee; }
.Friesland {fill: #ddc; }
.Groningen {fill: #dcc; }
.Limburg {fill: #ccc; }
.Noord-Brabant {fill: #ddb; }
.Overijssel {fill: #dbb; }
.Zeeland {fill: #bbb; }
</style>
</head>
<body>
<script type="text/javascript">
var width = 960, height = 860;
var projection = d3.geo.albers()
.center([6, 49.40])
.rotate([0, -1.9])
.parallels([50, 60])
.scale(11000)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
//Define quantize scale to sort data values into buckets of color
var color = d3.scale.quantize()
.range(["rgb(237,248,233)","rgb(186,228,179)","rgb(116,196,118)","rgb(49,163,84)","rgb(0,109,44)"]);
//Load in data
d3.csv("http://localhost/webserver/data/beroepsbevolking.csv", function(data) {
//Set input domain for color scale
color.domain([
d3.min(data, function(d) { return d.value; }),
d3.max(data, function(d) { return d.value; })
]);
d3.json("http://localhost/webserver/data/nl2.json", function(error, nl) {
svg.selectAll(".subunit")
.data(topojson.object(nl, nl.objects.subunits).geometries)
.enter().append("path")
.attr("class", function(d) { return "subunit " + d.id; })
.attr("d", path);
svg.selectAll(".subunit-label")
.data(topojson.object(nl, nl.objects.subunits).geometries)
//svg.selectAll(".provincie")
.data(topojson.object(nl, nl.objects.provincies).geometries)
.enter().append("path")
// .attr("class", function(d) { return "provincie " + d.properties.name; })
.attr("d", path);
//Merge the ag. data and GeoJSON
//Loop through once for each ag. data value
d3.json("http://localhost/webserver/data/nl2.json" ,function(json) {
for (var i = 0; i < data.length; i++) {
//Grab provincie name
var dataProvincie = data[i].provincie;
//Grab data value, and convert from string to float
var dataValue = parseFloat(data[i].value);
//Find the corresponding provincie inside the GeoJSON
for (var j = 0; j < json.features.length; j++) {
var jsonProvincie = json.features[j].properties.name;
if (dataProvincie == jsonProvincie) {
//Copy the data value into the JSON
json.features[j].properties.value = dataValue;
//Stop looking through the JSON
break;
}
}
}
//Bind data and create one path per GeoJSON feature
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path)
.style("fill", function(d) {
//Get data value
var value = d.properties.value;
if (value) {
//If value exists…
return color(value);
} else {
//If value is undefined…
return "#ccc";
}
});
});
});
});
</script>
</body>
</html>
It goes wrong at line 115:
for (var j = 0; j < json.features.length; j++) {
Why?
Your JSON doesn't have a features field. In addition, you've missed an argument in the d3.json function -- in your second call, json will actually be bound to the error. Change
d3.json("http://localhost/webserver/data/nl2.json" ,function(json) {
to
d3.json("http://localhost/webserver/data/nl2.json" ,function(error, json) {
The second call to d3.json seems unnecessary -- at that point, you have the data in nl2.json already in the nl variable.
The name of the parameter is "json", not "data".
Make them the same name and it should work.
Also; get a hint from the error message; "data" is undefined.

Resources