I am beginner in d3 v3. I created multiple donut charts. When I move my mouse over a slice, I get the tooltip that appears. But I would also like the slice to grow a little bit.
I have already tried several codes but I can't get there. I wonder if the problem may be related to d3 tip
this is my js file :
//Source : http://bl.ocks.org/mbostock/1305337
var m = 15,
r = 80,
z = d3.scale.ordinal()
.range(["#50FFC5", "#54E868", "#54CCE8", "#6395FF"]);
var pie = d3.layout.pie()
.value(function (d) {
return +d.count;
})
.sort(function (a, b) {
return b.count - a.count;
});
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r);
var radius = d3.scale.linear()
.range([10, r])
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(function (d) {
return d.data.genre + ": " + d.data.count;
})
.direction('s');
d3.csv("../CSV/genreHameau.csv", function (error, hameau) {
if (error) throw error;
var datas = d3.nest()
.key(function (d) {
return d.origine;
})
.entries(hameau);
datas.forEach(function (d) {
totalOrigin = d3.sum(d.values, function (d) {
return +d.count;
})
d.values.forEach(function (dd) {
dd.totalOrigin = totalOrigin
})
})
datas.sort(function (a, b) {
return d3.descending(a.values[0].totalOrigin, b.values[0].totalOrigin)
})
// définir le radius / rayon des arcs (rendre fonction de 'count')
var max = d3.max(datas, function (d) {
return d.values[0].totalOrigin
})
var min = d3.min(datas, function (d) {
return d.values[0].totalOrigin
})
radius.domain([min, max])
arc
.innerRadius(function (d) {
return radius(d.data.totalOrigin) / 2
})
.outerRadius(function (d) {
return radius(d.data.totalOrigin)
})
function size(d) {
return radius(d.values[0].totalOrigin) + m
}
var div = d3.select("body").selectAll("div")
.data(datas)
.enter().append("div") // http://code.google.com/p/chromium/issues/detail?id=98951
.style("display", "inline-block")
.style("width", function (d) {
return 2 * size(d) + "px"
})
.style("height", function (d) {
return 2 * size(d) + "px"
})
.style("min-width", "80px")
.style("min-height", "80px")
div.append("span")
.attr("class", "nomhameau")
.text(function (d) {
return d.key;
})
.append("span")
.attr("class", "nombrehameau")
.text(function (d) {
return " (" + d.values[0].totalOrigin + ")"
});
var svg = div.append("svg")
//.attr("width", (r + m) * 2)
//.attr("height", (r + m) * 2)
.attr("width", function (d) {
return 2 * size(d)
})
.attr("height", function (d) {
return 2 * size(d)
})
.append("g")
.attr("transform", function (d) {
return "translate (" + size(d) + "," + size(d) + ")"
});
svg.call(tip);
var g = svg.selectAll("g")
.data(function (d) {
return pie(d.values);
})
.enter().append("g")
.on("mouseover", tip.show)
.on("mouseout", tip.hide)
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
return z(d.data.genre);
})
.append("title")
});
Thank you for your help
I would do it this way:
Just like you created the arc, create a new one with a greater radius, either inner, outer or both, something like:
var arcHighlight = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r*1.1);
Then you add mouseover and mouseout events to the slice and modify the slice doing something like:
// Add a colored arc path, with a mouseover title showing the count.
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return z(d.data.carrier); })
// new code
.on('mouseover', function(d) {
d3.select(this)
.transition()
.attr('d', arcHighlight(d));
})
.on('mouseout', function(d) {
d3.select(this)
.transition()
.attr('d', arc(d));
})
.append("title")
.text(function(d) { return d.data.carrier + ": " + d.data.count; });
The transition is optional, but it looks fancier ;)
I am trying to implement tooltips and a zoom on a graph, the tooltips work fine however I am having issues with the zoom function. I get an error on the debugger that says: Uncaught TypeError: zoomed.x is not a function. This occurs at the line 'return zoomed.x(x);' I am not sure how to change the code to get it to work.
//Builder for REal time flow chart
define((function () {
var categories = [
{
Name: "MediaServiceIndexes",
Title: "Media",
Style: "background-color:#ffffff;font-color:blue;",
Text: "Here are the top media companies with whom our customers shop.",
TitleStyle: "margin-left:0px"
},
];
var url = App.SiteUrl + "/Data/RequestDataFrom";
var svg, width, numberChecker;
var patterns = d3.scale.ordinal()
.range([
"url(#blue1)", "url(#yellow1)",
"url(#blue2)", "url(#yellow2)",
"url(#blue3)", "url(#yellow3)",
"url(#blue4)", "url(#yellow4)",
"url(#blue5)", "url(#yellow5)"
]);
var grayFill = "url(#gray)";
var colors = d3.scale.category10();
var getValues = function (values) {
var result = [];
try {
values.forEach(function (item) {
var o = {
Month: moment(item.month + "01", "YYYYMMDD"),
Value: parseFloat(item.change_in_market_share, 10)
};
var vendor = item.vendor.replace(/'/g, "");
//var e = result.find(function (it) { return it.Vendor == vendor; });
var e = false;
for (var x in result) {
if (result.hasOwnProperty(x) && typeof result[x] != "function") {
if (result[x].Vendor == vendor) {
e = (result[x]);
}
}
}
if (e) {
e.Values.push(o);
} else {
result.push({
Visible: true,
Vendor: vendor,
Values: [o]
});
}
});
result.forEach(function (item, i) {
item.Index = i;
item.Values.sort(function (a, b) {
return a.Month - b.Month;
});
});
} catch (e) {
result = null;
};
return result;
};
var createChart = function (chartElement, cd) {
var chartData = cd; //chart data is being passed in using cd via createChart function
//chart gives the location of the chart,
var chart = function (el, data) {
var margin = {
top: 20,
right: 180,
bottom: 50,
left: 110
};
var elem = el;
var chartEl = chartElement;
var fillData = [];
var defs,
gs,
height,
line,
maxDays,
minDays,
minValue,
maxValue,
x,
xAxis,
y,
yAxis;
var initialiseData = function (dataValues) {
minDays = d3.min(dataValues,
function (m) {
return d3.min(m.Values,
function (d) {
return d.Month;
});
});
maxDays = d3.max(dataValues,
function (m) {
return d3.max(m.Values,
function (d) {
return d.Month;
});
});
minValue = d3.min(chartData,
function (m) {
return d3.min(m.Values,
function (d) {
return d.Value;
});
});
maxValue = d3.max(chartData,
function (m) {
return d3.max(m.Values,
function (d) {
return d.Value;
});
});
console.log('min days: ' + minDays);
console.log('max days: ' + maxDays);
console.log('min value: ' + minValue);
console.log('max value: ' + maxValue);
dataValues.forEach(function (item) {
var nu = $.extend(true, {}, item);
nu.Values.push({ Month: maxDays, Value: minValue });
nu.Values.push({ Month: minDays, Value: minValue });
fillData.push(nu);
});
};
//initialise scales
var configSize = function () {
if (isNaN(numberChecker) === true) {
numberChecker = $(chartEl).width();
width = numberChecker;
}
width = 750;
height = 500 - margin.top - margin.bottom;
x = d3.scale.linear()
.range([0, width])
.domain([minDays, maxDays]);
y = d3.scale.linear()
.range([height, 0])
.domain([minValue, maxValue]);
$('.adjustmentZoom .tick text').attr('y', '25');
//initialise axis
xAxis = d3.svg.axis()
.scale(x)
.tickFormat(function (d, i) {
if (Math.floor(d) !== d) {
} else {
return moment(d).format("MMM YY");
}
})
.orient('bottom');
yAxis = d3.svg.axis()
.scale(y)
.tickFormat(function (d) {
return d3.round(d, 3) + "%"
})
.orient('left');
$('#clipper rect').attr('width', width);
$('.zoom-panel').attr('width', width);
};
var svgTransform = function(d) {
return "translate(" + x(d.Month) + "," + y(d.Value) + ")";
};
var zoomed = function () {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.selectAll(".datapoint").attr("transform", svgTransform);
};
//the path generator for the line chart
var initialise = function () {
line = d3.svg.line()
.interpolate(
'cardinal')
.x(function (d) {
return x(d.Month);
})
.y(function (d) {
return y(d.Value);
});
var zoomBeh = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([
1,
500
])
.on('zoom', zoomed);
//elem is window
svg = d3.select(elem).append('div').attr('id', 'scatter').append('svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('viewBox', '0 0 1000 550')
.attr('preserveAspectRatio', 'xMinYMin meet')
.append('g')
.attr("class", "line-container")
.attr('transform',
function () {
if (window.innerWidth > 650) {
var marginWidth = margin.left;
}
else if (window.innerWidth <= 650 && window.innerWidth > 549) {
var marginWidth = margin.left + 30;
}
else if (window.innerWidth <= 549) {
var marginWidth = margin.left + 50;
}
return 'translate(' + marginWidth + ',' + margin.top + ')'
}).call(zoomBeh);
svg.append('rect')
.attr('class', 'zoom-panel')
.attr('width', width)
.attr('height', height)
.call(zoomBeh);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis)
.selectAll('text');
svg.append('text')
.attr('x', 400)
.attr('y', (height + 70))
.style('text-anchor', 'middle')
.attr('class', 'xTitle')
.style('font-weight', '500')
.text('Date by month');
svg.append('g').attr('class', 'y axis').attr('transform', 'translate(0,0)').style('text-anchor', 'end').call(yAxis);
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', -100)
.attr('x', -200)
.attr('dy', '1em')
.attr('class', 'yTitle')
.style('text-anchor', 'middle')
.style('font-weight', '500')
.text('Share prices in percentage');
//zoom.scaleExtent([
// 1,
// moment(maxDays).diff(minDays, "months")
//]);
}
var tooltip = function () {
svg = d3.select(elem)
.append('div')
.attr('class', 'tooltip')
.style('opacity', 0);
};
//draw calls drawData which will draw the lines of the chart
var draw = function () {
var drawData = function (dat, className, baseFill, area) {
//var selection (supermarket)
var supermarket, supermarketEnter;
supermarket = svg.selectAll('.' + className)
.data(dat,
function (c) {
return c.Vendor;
});
//var new selection (supermarketEnter)
supermarketEnter = supermarket.enter()
.append('g')
.attr('class', className)
.attr('data-vendor', function(d) { return d.Vendor; })
.attr('width', width)
.attr('height', height);
supermarketEnter.append('path');
//update new selection
supermarketEnter.attr('clip-path', 'url(#clipper)').attr('class', 'line').attr('id', function (d) { return d.Vendor; });
supermarketEnter
.selectAll("circle")
.data(function (d) {
return d.Values;
})
.enter()
.append('circle')
.attr('class', 'datapoint')
.attr('r', 4)
.style('fill',
function (d, i, j) {
return dat[j].Visible ? baseFill(j) : grayFill;
})
.attr('transform',
function (d) {
return 'translate(' + x(d.Month) + ',' + y(d.Value) + ')';
})
.on('mouseover',
function (d, i, j) {
d3.select('.tooltip').style('opacity', '1');
d3.select('.tooltip')
.html(dat[j].Vendor +
'<br/> (' +
moment(d.Month).format("MMM YYYY") +
', ' +
d.Value.toPrecision(2) +
'% )')
.style('left',
function () {
if (window.innerWidth >= 1200) {
var newWidth = d3.event.pageX -
($(chartEl).width() / 2) +
'px'
} else if (window.innerWidth < 1200) {
var newWidth = d3.event.pageX - ($(chartEl).width() / 10) + 'px'
}
return newWidth;
})
.style('top', (d3.event.pageY) - 300 + 'px');
})
.on("mouseout",
function (d) {
d3.select('.tooltip')
.style('transition', '500')
.style('opacity', 0)
.style('color', d3.select(this).style('fill'));
});
supermarket.select('path')
.transition()
.duration(500)
.attr('d',
function (d) {
return line(d.Values);
});
var path = supermarket.select('path')
.style('stroke', function (d, i) {
return (d.Visible ? baseFill(d.Index) : grayFill);
});
if (area) {
path.style('fill', function (d, i) { return (d.Visible ? baseFill(d.Index) : grayFill); });
}
supermarket.exit().remove();
supermarket.order();
}
svg.selectAll('.x.axis').call(xAxis);
svg.selectAll('.y.axis').call(yAxis);
drawData(chartData, 'supermarket', patterns, false);
return zoomed.x(x);
};
var render = function () {
configSize();
draw();
};
//insertion of drawLegend into chart function so that this will draw as well as the lines of the chart.
var drawLegend = function (dat, className) {
//DATA JOIN
//Join new data with old elements, if any.
var supermarket, supermarketEnter;
supermarket = svg.selectAll('legend_' + className)
.data(dat, function (c) { return c.Vendor; });
//UPDATE
//Update old elements as needed.
supermarket.attr('class', 'update');
//ENTER + UPDATE
//After merging the entered elements with the update selection, apply operations to both.
supermarketEnter = supermarket.enter().append('g')
.attr('class', 'legend_' + className)
.attr('data-vendor', function (d) { return d.Vendor; });
supermarketEnter
.append('text')
.attr('class', 'supermarket-name')
.attr('data-vendor', function (d) { return d.Vendor; });//
supermarket.select('text.supermarket-name')
.attr('x', width + 25)
.attr('y', function (d, i) { return (i * 1.25) * 20; })
.attr('dy', '.35em')
.text(function (d) {
return d.Vendor;
})
.on('click', function (d, i, j) {
dat[i].Visible = !dat[i].Visible;
var newOpacity = dat[i].Visible ? 1 : 0;
d3.select("[data-vendor='" + dat[i].Vendor + "']").style('opacity', newOpacity);
});
supermarketEnter.append('rect')
.attr('class', 'supermarket-dot');
supermarket.select('rect.supermarket-dot')
.attr('x', width + 10).attr('y', function (d, i) {
return ((i * 1.25) * 20) - 5;
})
.attr('width', 12)
.attr('height', 12)
.style('fill', function (d, i) {
return patterns(d.Index);
});
//EXIT
//Remove old elements as needed.
supermarket.exit().remove();
};
//insertion of drawLegend into chart function originally placed after configSize(); draw(); };
var toggleLines = function () {
var dat = $(this.closest("g")).data();
var obj = chartData.find(function (item) { return item.Vendor === dat.vendor; });
if (obj.Visible) {
obj.Visible = false;
} else {
obj.Visible = true;
}
chartData.sort(function (a, b) {
if (a.Visible === b.Visible) return 0;
if (a.Visible && !b.Visible) return 1;
return -1;
});
draw();
};
initialiseData(data);
configSize();
initialise();
draw();
drawLegend(chartData, 'supermarket');
tooltip();
$('.adjustmentZoom .tick text').attr('y', '25');
return {
Draw: render
};
};
var c = chart(chartElement, chartData);
//draw draws the lines of the chart. chart function draws the space arou
$(window).on("resize", function () {
c.Draw();
$('.adjustmentZoom .tick text').attr('y', '25');
});
}
var initialiseElement = function () {
categories.forEach(function (item) {
$(".slidesIntroduction").append('<div class="">\
<div class="insight-chart chart tradingData ' + item.Name + '" style="' + item.Style + '">\
<h3 class="chartTitle" ><br />\All ' + item.Title + '</h3>\
<p style="' + item.TitleStyle + '">\
<br />\
' + item.Text + '\
</p>\
</div>\
<//div>');
});
};
var renderInsightData = function () {
initialiseElement();
categories.forEach(function (item) {
if (item.DataValues) {
createChart("." + item.Name, item.DataValues);
}
});
};
var startWork = function () {
var catCount = 0;
var startRequest = function () {
App.Modules.ServerComms.PollForData({
Url: url,
Data: { src: categories[catCount].Name },
Success: processResponse,
});
};
var processResponse = function (response) {
categories[catCount].DataValues = getValues(response);
catCount++;
if (catCount < categories.length) {
startRequest();
} else {
renderInsightData();
$(".spinner").removeClass("spinner");
}
};
startRequest();
};
return {
Name: "Introduction",
Init: function () { },
Start: startWork
}
})());
//Builder for REal time flow chart
define((function () {
var categories = [
{
Name: "MediaServiceIndexes",
Title: "Media",
Style: "background-color:#ffffff;font-color:blue;",
Text: "Here are the top media companies with whom our customers shop.",
TitleStyle: "margin-left:0px"
},
];
var url = App.SiteUrl + "/Data/RequestDataFrom";
var svg, width, numberChecker;
var patterns = d3.scale.ordinal()
.range([
"url(#blue1)", "url(#yellow1)",
"url(#blue2)", "url(#yellow2)",
"url(#blue3)", "url(#yellow3)",
"url(#blue4)", "url(#yellow4)",
"url(#blue5)", "url(#yellow5)"
]);
var grayFill = "url(#gray)";
var colors = d3.scale.category10();
var getValues = function (values) {
var result = [];
try {
values.forEach(function (item) {
var o = {
Month: moment(item.month + "01", "YYYYMMDD"),
Value: parseFloat(item.change_in_market_share, 10)
};
var vendor = item.vendor.replace(/'/g, "");
//var e = result.find(function (it) { return it.Vendor == vendor; });
var e = false;
for (var x in result) {
if (result.hasOwnProperty(x) && typeof result[x] != "function") {
if (result[x].Vendor == vendor) {
e = (result[x]);
}
}
}
if (e) {
e.Values.push(o);
} else {
result.push({
Visible: true,
Vendor: vendor,
Values: [o]
});
}
});
result.forEach(function (item, i) {
item.Index = i;
item.Values.sort(function (a, b) {
return a.Month - b.Month;
});
});
} catch (e) {
result = null;
};
return result;
};
var createChart = function (chartElement, cd) {
var chartData = cd; //chart data is being passed in using cd via createChart function
//chart gives the location of the chart,
var chart = function (el, data) {
var margin = {
top: 20,
right: 180,
bottom: 50,
left: 110
};
var elem = el;
var chartEl = chartElement;
var fillData = [];
var defs,
gs,
height,
line,
maxDays,
minDays,
minValue,
maxValue,
x,
xAxis,
zoomBeh,
y,
yAxis;
var initialiseData = function (dataValues) {
minDays = d3.min(dataValues,
function (m) {
return d3.min(m.Values,
function (d) {
return d.Month;
});
});
maxDays = d3.max(dataValues,
function (m) {
return d3.max(m.Values,
function (d) {
return d.Month;
});
});
minValue = d3.min(chartData,
function (m) {
return d3.min(m.Values,
function (d) {
return d.Value;
});
});
maxValue = d3.max(chartData,
function (m) {
return d3.max(m.Values,
function (d) {
return d.Value;
});
});
console.log('min days: ' + minDays);
console.log('max days: ' + maxDays);
console.log('min value: ' + minValue);
console.log('max value: ' + maxValue);
dataValues.forEach(function (item) {
var nu = $.extend(true, {}, item);
nu.Values.push({ Month: maxDays, Value: minValue });
nu.Values.push({ Month: minDays, Value: minValue });
fillData.push(nu);
});
};
//initialise scales
var configSize = function () {
if (isNaN(numberChecker) === true) {
numberChecker = $(chartEl).width();
width = numberChecker;
}
width = 750;
height = 500 - margin.top - margin.bottom;
x = d3.scale.linear()
.range([0, width])
.domain([minDays, maxDays]);
y = d3.scale.linear()
.range([height, 0])
.domain([minValue, maxValue]);
$('.adjustmentZoom .tick text').attr('y', '25');
//initialise axis
xAxis = d3.svg.axis()
.scale(x)
.tickFormat(function (d, i) {
if (Math.floor(d) !== d) {
} else {
return moment(d).format("MMM YY");
}
})
.orient('bottom');
yAxis = d3.svg.axis()
.scale(y)
.tickFormat(function (d) {
return d3.round(d, 3) + "%"
})
.orient('left');
$('#clipper rect').attr('width', width);
$('.zoom-panel').attr('width', width);
};
//the path generator for the line chart
var initialise = function () {
line = d3.svg.line()
.interpolate(
'cardinal')
.x(function (d) {
return x(d.Month);
})
.y(function (d) {
return y(d.Value);
});
zoomBeh = d3.behavior.zoom()
.x(x)
.y(y)
.scaleExtent([
1,
500
])
.on('zoom', zoomed);
//elem is window
svg = d3.select(elem).append('div').attr('id', 'scatter').append('svg')
.attr('width', '100%')
.attr('height', '100%')
.attr('viewBox', '0 0 1000 550')
.attr('preserveAspectRatio', 'xMinYMin meet')
.append('g')
.attr("class", "line-container")
.attr('transform',
function () {
if (window.innerWidth > 650) {
var marginWidth = margin.left;
}
else if (window.innerWidth <= 650 && window.innerWidth > 549) {
var marginWidth = margin.left + 30;
}
else if (window.innerWidth <= 549) {
var marginWidth = margin.left + 50;
}
return 'translate(' + marginWidth + ',' + margin.top + ')'
}).call(zoomBeh).on("dblclick.zoom", null);
svg.append('rect')
.attr('class', 'zoom-panel')
.attr('width', width)
.attr('height', height)
.call(zoomBeh);
svg.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis)
.selectAll('text');
svg.append('text')
.attr('x', 400)
.attr('y', (height + 70))
.style('text-anchor', 'middle')
.attr('class', 'xTitle')
.style('font-weight', '500')
.text('Date by month');
svg.append('g').attr('class', 'y axis').attr('transform', 'translate(0,0)').style('text-anchor', 'end').call(yAxis);
svg.append('text')
.attr('transform', 'rotate(-90)')
.attr('y', -100)
.attr('x', -200)
.attr('dy', '1em')
.attr('class', 'yTitle')
.style('text-anchor', 'middle')
.style('font-weight', '500')
.text('Share prices in percentage');
//zoom.scaleExtent([
// 1,
// moment(maxDays).diff(minDays, "months")
//]);
}
//draw calls drawData which will draw the lines of the chart
var draw = function () {
var drawData = function (dat, className, baseFill, area) {
//var selection (supermarket)
var supermarket, supermarketEnter;
supermarket = svg.selectAll('.' + className)
.data(dat,
function (c) {
return c.Vendor;
});
//var new selection (supermarketEnter)
supermarketEnter = supermarket.enter()
.append('g')
.attr('class', className)
.attr('data-vendor', function(d) { return d.Vendor; })
.attr('width', width)
.attr('height', height);
supermarketEnter.append('path');
//update new selection
supermarketEnter.attr('clip-path', 'url(#clipper)').attr('class', 'line').attr('id', function (d) { return d.Vendor; });
supermarket.select('path')
.transition()
.attr('class', 'line-data')
.duration(500)
.attr('d',
function (d) {
return line(d.Values);
});
var path = supermarket.select('path')
.style('stroke', function (d, i) {
return (d.Visible ? baseFill(d.Index) : grayFill);
});
if (area) {
path.style('fill', function (d, i) { return (d.Visible ? baseFill(d.Index) : grayFill); });
}
supermarketEnter
.selectAll("circle")
.data(function (d) {
return d.Values;
})
.enter()
.append('circle')
.attr('class', 'datapoint')
.attr('r', 4)
.style('fill',
function (d, i, j) {
return dat[j].Visible ? baseFill(j) : grayFill;
})
.attr('transform',
function (d) {
return 'translate(' + x(d.Month) + ',' + y(d.Value) + ')';
})
.on('mouseover',
function (d, i, j) {
d3.select('.tooltip').style('opacity', '1');
d3.select('.tooltip')
.html(dat[j].Vendor +
'<br/> (' +
moment(d.Month).format("MMM YYYY") +
', ' +
d.Value.toPrecision(2) +
'% )')
.style('left',
function () {
if (window.innerWidth >= 1200) {
var newWidth = d3.event.pageX -
($(chartEl).width() / 2) +
'px'
} else if (window.innerWidth < 1200) {
var newWidth = d3.event.pageX - ($(chartEl).width() / 10) + 'px'
}
return newWidth;
})
.style('top', (d3.event.pageY) - 300 + 'px');
})
.on("mouseout",
function (d) {
d3.select('.tooltip')
.style('transition', '500')
.style('opacity', 0)
.style('color', d3.select(this).style('fill'));
});
supermarket.exit().remove();
supermarket.order();
}
svg.selectAll('.x.axis').call(xAxis);
svg.selectAll('.y.axis').call(yAxis);
drawData(chartData, 'supermarket', patterns, false);
};
var render = function () {
configSize();
draw();
};
//insertion of drawLegend into chart function so that this will draw as well as the lines of the chart.
var drawLegend = function (dat, className) {
//DATA JOIN
//Join new data with old elements, if any.
var supermarket, supermarketEnter;
supermarket = svg.selectAll('legend_' + className)
.data(dat, function (c) { return c.Vendor; });
//UPDATE
//Update old elements as needed.
supermarket.attr('class', 'update');
//ENTER + UPDATE
//After merging the entered elements with the update selection, apply operations to both.
supermarketEnter = supermarket.enter().append('g')
.attr('class', 'legend_' + className)
.attr('data-vendor', function (d) { return d.Vendor; });
supermarketEnter
.append('text')
.attr('class', 'supermarket-name')
.attr('data-vendor', function (d) { return d.Vendor + "-legend"; });//
supermarket.select('text.supermarket-name')
.attr('x', width + 25)
.attr('y', function (d, i) { return (i * 1.25) * 20; })
.attr('dy', '.35em')
.text(function (d) {
return d.Vendor;
})
.on('click', function (d, i, j) {
dat[i].Visible = !dat[i].Visible;
var newOpacity = dat[i].Visible ? 1 : 0;
d3.select("[data-vendor='" + dat[i].Vendor + "']").style('opacity', newOpacity);
});
supermarketEnter.append('rect')
.attr('class', 'supermarket-dot');
supermarket.select('rect.supermarket-dot')
.attr('x', width + 10).attr('y', function (d, i) {
return ((i * 1.25) * 20) - 5;
})
.attr('width', 12)
.attr('height', 12)
.style('fill', function (d, i) {
return patterns(d.Index);
});
//EXIT
//Remove old elements as needed.
supermarket.exit().remove();
d3.select(elem).append('div').attr('class', 'tooltip').style('opacity', 0);
return createChart().chart().initialise().zoomBeh.x(x);
};
var svgTransform = function (d) {
return "translate(" + x(d.Month) + "," + y(d.Value) + ")";
};
var lineTransform = function(d) {
return line(d.Values);
}
var zoomed = function () {
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
svg.selectAll(".datapoint").attr("transform", svgTransform);
svg.selectAll(".line-data").attr("d", lineTransform);
};
//insertion of drawLegend into chart function originally placed after configSize(); draw(); };
var toggleLines = function () {
var dat = $(this.closest("g")).data();
var obj = chartData.find(function (item) { return item.Vendor === dat.vendor; });
if (obj.Visible) {
obj.Visible = false;
} else {
obj.Visible = true;
}
chartData.sort(function (a, b) {
if (a.Visible === b.Visible) return 0;
if (a.Visible && !b.Visible) return 1;
return -1;
});
draw();
};
initialiseData(data);
configSize();
initialise();
draw();
drawLegend(chartData, 'supermarket');
tooltip();
$('.adjustmentZoom .tick text').attr('y', '25');
return {
Draw: render
};
};
var c = chart(chartElement, chartData);
//draw draws the lines of the chart. chart function draws the space arou
$(window).on("resize", function () {
c.Draw();
$('.adjustmentZoom .tick text').attr('y', '25');
});
}
var initialiseElement = function () {
categories.forEach(function (item) {
$(".slidesIntroduction").append('<div class="">\
<div class="insight-chart chart tradingData ' + item.Name + '" style="' + item.Style + '">\
<h3 class="chartTitle" ><br />\All ' + item.Title + '</h3>\
<p style="' + item.TitleStyle + '">\
<br />\
' + item.Text + '\
</p>\
</div>\
<//div>');
});
};
var renderInsightData = function () {
initialiseElement();
categories.forEach(function (item) {
if (item.DataValues) {
createChart("." + item.Name, item.DataValues);
}
});
};
var startWork = function () {
var catCount = 0;
var startRequest = function () {
App.Modules.ServerComms.PollForData({
Url: url,
Data: { src: categories[catCount].Name },
Success: processResponse,
});
};
var processResponse = function (response) {
categories[catCount].DataValues = getValues(response);
catCount++;
if (catCount < categories.length) {
startRequest();
} else {
renderInsightData();
$(".spinner").removeClass("spinner");
}
};
startRequest();
};
return {
Name: "Introduction",
Init: function () { },
Start: startWork
}
})());
I've constructed a D3 visualization that was working on my local machine. However, now i've exported to my server, the code breaks and throws several errors:
Error: invalid value for <circle> attribute transform="translate"(NaN,NaN)"
Error: invalid value for <text> attribute transform="translate"(NaN,NaN)"
Error: invalid value for <circle> attribute r="NaN"
I've had these errors before with similar code and was able to solve them. However, i cannot grasp what is going wrong. Any suggestions? Thanx!
function drawBubbles() {
var margin = 20,
diameter = 740;
var color = d3.scale.linear()
.domain([-1, 10])
.range(["hsl(152,80%,80%)", "hsl(228,30%,40%)"])
.interpolate(d3.interpolateHcl);
var pack = d3.layout.pack()
.size([diameter - margin, diameter - margin])
.value(function (d) { return d.size; })
var svg = d3.select("form").append("svg")
.attr("width", 1280)
.attr("height", 800)
.append("g")
.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
d3.json("../Resources/output.json", function (error, root) {
if (error) throw error;
var focus = root,
nodes = pack.nodes(root),
view;
var circle = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("class", function (d) { return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root"; })
.style("fill", function (d) { return d.children ? color(d.depth) : null; })
.on("click", function (d) { if (focus !== d) zoom(d), d3.event.stopPropagation(); });
var text = svg.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.style("fill-opacity", function (d) { return d.parent === root ? 1 : 0; })
.style("display", function (d) { return d.parent === root ? "inline" : "none"; })
.text(function (d) { return d.name; });
var node = svg.selectAll("circle,text");
d3.select("form")
.on("click", function () { zoom(root); });
zoomTo([root.x, root.y, root.r * 2 + margin]);
function zoom(d) {
var focus0 = focus; focus = d;
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween("zoom", function (d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function (t) { zoomTo(i(t),d); };
});
transition.selectAll("text")
.filter(function (d) { return d.parent === focus || this.style.display === "inline"; })
.style("fill-opacity", function (d) { return d.parent === focus ? 1 : 0; })
.each("start", function (d) { if (d.parent === focus) this.style.display = "inline"; })
.each("end", function (d) { if (d.parent !== focus) this.style.display = "none"; });
}
function zoomTo(v) {
var k = diameter / v[2]; view = v;
console.log(d.x)
console.log(d.y)
console.log(d.r)
console.log(k)
node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
circle.attr("r", function(d) { return d.r * k; });
}
});
d3.select(self.frameElement).style("height", diameter + "px"); }
I had exactly this issue - although most browsers seemed to treat NaN as zero and so not complain, but Chrome wasn't happy at all.
For me I found the packed nodes collection returned from the packer had NaNs for zero value items passed in (which my data could contain). I just looped all the nodes after the pack call and set them to zero. Something like this should do it (it should probably be a recursive call to avoid duplication, but you get the idea!)...
// Replace any zero value items with zero x/y/r values
nodes.forEach( function (currNode) {
if (!currNode.count) {
currNode.x = 0;
currNode.y = 0;
currNode.r = 0;
currNode.children.forEach( function (currChild) {
if (!currChild.count) {
currChild.x = 0;
currChild.y = 0;
currChild.r = 0;
}
});
}
});
Hope this helps you
So I am working with the d3 fisheye plugin, and I am having some pretty basic problems.
I implemented this very basic code, pretty much a direct copy from here https://github.com/d3/d3-plugins/tree/master/fisheye
fisheye = d3.fisheye.circular()
.radius(200)
.distortion(2);
//initialize fisheye
chart.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
dataPoint.each(function(d) { d.fisheye = fisheye(d); })
.attr('y', function(d){ return d.fisheye.y; })
.attr('x', function(d){ return d.fisheye.x; });
});
But d.fisheye.x and d.fisheye.y are undefined. In fact, looking at fisheye(d), it returns:
{x: undefined, y: undefined, z: 1}
On the other hand, d3.mouse(this) is properly returning an array.
Does any one have suggestion on why this might be occurring?
More code: by the way, the code is like this because it is inside a ext-js panel, so each function (drawWords is a property of this object). It is kind of complicated which is why I hesitated to post it all, and this is still not all the code, but the relevant part I think. I didn't include the initialization of any of the other global variables, or helper functions.
//imagine some sort of onload function
onLoad: function () {
this.drawWords();
this.animateVis();
}
,drawWords: function () {
toolObject = this;
var h = this.body.getHeight(),
w = this.body.getWidth();
//initialize word text
this.dataPoint = this.chart.selectAll('text')
.data(toolObject.termometerData, function (d) {return d.word;})
.enter().append('text')
.attr('class', 'points')
.attr('id', function(d) {return d.word + '-point';})
.attr('x', function() {
return toolObject.xScale(toolObject.shiftCount);
})
.attr('y', function (d) {
return toolObject.fanVertical(d, toolObject.shiftCount);
})
.attr('transform', function (d) {
var thisXPosition = toolObject.xScale(toolObject.shiftCount),
thisYPosition = toolObject.fanVertical(d, toolObject.shiftCount);
return 'translate(0, 0) rotate(-20 ' + thisXPosition + ' ' + thisYPosition + ')';
})
.attr('text-anchor', 'middle')
.attr('fill-opacity', function (d) {return toolObject.opacityScale(0);})
.text(function(d) {return d.word;});
this.applyFisheye();
}
,fisheye: d3.fisheye.circular()
.radius(200)
.distortion(2)
,applyFisheye: function () {
var toolObject = this;
//initialize fisheye
this.chart.on("mousemove", function() {
fisheye.focus(d3.mouse(this));
toolObject.dataPoint.each(function(d) { d.fisheye = toolObject.fisheye(d); })
.attr('y', function(d){ return d.fisheye.y; })
.attr('x', function(d){ return d.fisheye.x; })
.attr('transform', function(d){
return 'translate(0, 0) rotate(-20 ' + d.fisheye.x + ' '+ d.fisheye.y + ')';
});
});
}
,animateVis: function () {
var toolObject = this;
var h = this.body.getHeight(),
w = this.body.getWidth();
var tick;
if(this.animationIdArray.length < 1){
tick = setInterval(function(){
animate();
}, this.duration);
this.animationIdArray.push(tick);
}
function animate() {
if(toolObject.shiftCount < toolObject.numDataPoints){
toolObject.shiftCount++;
//animate words
toolObject.dataPoint.transition()
.duration(toolObject.duration)
.ease('linear')
.attr('x', function(d){ return toolObject.xScale(toolObject.shiftCount - 1); })
.attr('y', function(d){ return toolObject.fanVertical(d, toolObject.shiftCount - 1); })
.attr('transform', function(d){
var thisXPosition = toolObject.xScale(toolObject.shiftCount - 1),
thisYPosition = toolObject.fanVertical(d, toolObject.shiftCount - 1);
return 'translate(0, 0) rotate(-20 ' + thisXPosition + ' '+ thisYPosition + ')';
})
.attr('fill-opacity', function(d){
return toolObject.opacityScale(d.series[toolObject.shiftCount - 1].freq);
});
toolObject.applyFisheye();
}else{
clearInterval(tick);
toolObject.animationIdArray.shift();
}
}
}
fisheye assumes that the x and y coordinates of your objects are defined by keys named "x" and "y". It's probably enough (but possibly overkill, depending how often this code is called) to use
this.dataPoint
.each(function(d) {
d.x = toolObject.xScale(toolObject.shiftCount);
d.y = toolObject.fanVertical(d, toolObject.shiftCount)
.attr('x', function(d) { return d.x; })
.attr('y', function(d) { return d.y; });
when you //initialize word text
I would like to highlight the edges of a graph when the connected nodes are hovered.
I took inspiration from the Bundle example:
However the on() function is not giving the d object to the onmouse function:
d3.json("graph_file.json", function(json) {
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll("line.link")
.data(json.links)
.enter().append("line")
.attr("class", function(d) { return "link source-" + d.source.key + " target-" + d.target.key; })
.style("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.selectAll("circle.node")
.data(json.nodes)
.enter().append("circle")
.attr("class", "node")
.attr("r", function(d) { return d.connec; })
.style("fill", function(d) { return color(d.group); })
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.call(force.drag);
...
function mouseover(d) {
svg.selectAll("line.link.target-" + d.key)
.classed("target", true);
svg.selectAll("line.link.source-" + d.key)
.classed("source", true);
}
any help appreciated.
Ultimately I was not able to reproduce your problem! The only thing that caught me at first was when I defined my mouseover and mouseout functions after setting them in .on(...) In that case they were undefined for the call and no mouse handlers were set - so the functions were simply never called.
Anyway, you can see what I tried here. The code:
var w = 400,
h = 400;
var vis = d3.select("svg").attr("width", 800).attr("height", 800);
var nodes = [];
var links = [];
for (var i = 0; i < 30; i++) {
var node = {
label: "node " + i,
value: Math.random(),
key: i
};
nodes.push(node);
};
for (var i = 0; i < nodes.length; i++) {
for (var j = 0; j < i; j++) {
if (Math.random() > .95) links.push({
source: nodes[i],
target: nodes[j],
weight: Math.random()
});
}
};
var force = d3.layout.force().size([w, h]).nodes(nodes).links(links);
force.start();
var link = vis.selectAll("line.link").data(links).enter().append("line").style("stroke", "#CCC").attr("class", function(d) {
return "link source-" + d.source.key + " target-" + d.target.key;
});
var mouseover = function(d) {
txt.text(JSON.stringify(d));
//txt.text("line.link.target-" + d.key);
vis.selectAll("line.link.target-" + d.key).classed("target", true).style("stroke", '#F00');
vis.selectAll("line.link.source-" + d.key).classed("source", true).style("stroke", '#F00');
}
var mouseout = function(d) {
vis.selectAll("line.link.target-" + d.key).classed("target", false).style("stroke", "#CCC");
vis.selectAll("line.link.source-" + d.key).classed("source", false).style("stroke", "#CCC");
}
var node = vis.selectAll("circle.node").data(force.nodes()).enter().append("circle").attr("class", "node").attr("r", 5).style("fill", function(d) {
return d3.rgb(55 * d.value, 255 * d.value, 155 * d.value)
}).style("stroke", "#FFF").style("stroke-width", 3).on("mouseover", mouseover).on("mouseout", mouseout).call(force.drag);
var txt = vis.append('text').attr({
transform: 'translate(5,400)'
}).text("Node Info");
var updateLink = function() {
this.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;
});
}
var updateNode = function() {
this.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
force.on("tick", function() {
node.call(updateNode);
link.call(updateLink);
});