I'm new to d3js and i'm trying to manipulate a simple graph with 2 axis and some rect to show some data.
I've set the range of data to my y axis with some object name. This object has also a type "technical" or "canonical".
I'm trying to replace this "technical" or "canonical" with a bootstrap's glyphicon.
I've tried to replace the datas from the range with a internal text containing the proper glyphicon but without success
//datas is the data structure containing my chart datas.
//objects will be the array use for the domain
var objects = datas.map(function (d) {
return d.object + getExchangeObjectType(d.type);
});
var margin = {top: 40, right: 20, bottom: 200, left: 400},
width = 1200 - margin.left - margin.right,
height = (objects.length*30) - margin.top - margin.bottom;
var canonical = "<span class='glyphicon glyphicon-copyright-mark'></span>";
var technical = "<span class='glyphicon glyphicon-wrench'></span>";
function getExchangeObjectType(type){
if (type == 'Technical')
return technical;
else
return canonical;
}
//datas is the data structure containing my chart datas.
//objects will be the array use for the domain
var objects = datas.map(function (d) {
return d.object + getExchangeObjectType(d.type);
});
var x = d3.scale.ordinal().rangePoints([0, width]);
var y = d3.scale.ordinal().rangeBands([height, 0],.1,.1);
// define x & y axis
var xAxis = d3.svg.axis()
.scale(x)
.orient("top")
.ticks(percents.length)
;
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(objects.length)
;
// define the domain of datas
y.domain(objects);
x.domain(percents);
Here is the svg part:
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 + ")");
// draw x axis
svg.append("g")
.attr("class", "x axis")
.call(xAxis)
.selectAll("text")
.attr("x",20)
.style("text-anchor", "end")
;
// draw y axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.selectAll("text")
.style("text-anchor", "end")
;
svg.selectAll("bar")
.data(datas)
.enter().append("rect")
.style("fill", function (d) { return getColor(d.value);})
.attr("y", function(d){return d.object + getExchangeObjectType(d.type);})
.attr("height", y.rangeBand())
.attr("x", 0 )
.attr("width", function(d) { return ( d.value * width) / 100 ; })
;
I've found out my solution use the unicode !
I've added my type in the label text and then replace it with an unicode :
.text(function(d){
var oldText=d.split(":");
if (oldText[1]=="Canonical")
return oldText[0]+" \ue194";
else
return oldText[0]+" \ue136"
})
Related
I have a dataset that has 3 fields a name, a min value (value2) and a max value (value) that represent a range.
//The data
var data =[{"name": 'Scotty', "value2":0, "value":17},
{"name":'Dick', "value2":10, "value":17},
{"name":'James', "value2":5, "value":null},
{"name":'Max', "value2":2, "value":9}]
Currently I have it represented with a bar chart using this code that works ok except in cases where the points value2 and value are very close or there is a null value.
//Chart size parameters
var margin = {top: 20, right: 30, bottom: 90, left: 40},
width = 830 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//Chart axis
var x = d3.scaleBand()
.domain(data.map(function(d) { return d.name; }))
.range([2, width])
.scaleBand(0.10);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) { return d.value; })])
.range([height, 0]);
var xAxis = d3.axisBottom(x);
var yAxis = d3.axisLeft(y);
//Initialize chart
var chart = d3.select("#mychart").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 + ")");
//Adding both axis
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
//Where the box is drawn
chart.selectAll(".box")
.data(data)
.enter().append("rect")
.attr("class", "box")
.attr("x", function(d) { return x(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return y(d.value2) - y(d.value); }) //Plot range
.attr("width", x.bandwidth()); //spacing for bars
Is there a way to plot the data with the following:
if two points are present plot both of them with a path connecting them
if one point is present with a null in field value only plot the one point
here's my d3.js function to visualize a bar graph:
function barGraph(data1)
{
// console(data1.count);
var i = 0;
data1.forEach(function(d){
while(i>0){
d.avspeed= +d.avspeed;
d.duration = +d.duration;
i--;
}
})
//console.log(data1.avspeed);
// console.log(data1.avspeed);
var margin = {top: 40, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//console.log(data1.avspeed);
console.log("hey1");
var formatPercent = d3.format("");
console.log("hey2");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
console.log("hey");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatPercent);
console.log("hey3");
var tip = d3.tip()
.attr('class', 'd3-tip')
.offset([-10, 0])
.html(function(d) {
return "<strong>Avg Speed:</strong> <span style='color:red'>" + d.avspeed + "</span>";
})
var svg = d3.select("#rightside").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 + ")");
svg.call(tip);
console.log("heyllo ");
x.domain(data1.map(function(d) { return d.tripid; }));
y.domain([0, d3.max(data1, function(d) { return d.avspeed; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Avg Speed");
svg.selectAll(".bar")
.data(data1)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.tripid); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.avspeed); })
.attr("height", function(d) { return height - y(d.avspeed); })
.on('mouseover', tip.show)
.on('mouseout', tip.hide)
console.log(d.avspeed);
console.log("hello234");
function type(d) {
d.avspeed = +d.avspeed;
return d;
}
}
It displays a graph based on the selected region on the map dynamically. If i select new region, another graph is being created below the old graph. I want the old graph to clear and new graph to be in place of old graph. How do I achieve that.
I am new to d3.js
Thanks in advance.
You can clear away anything inside of the parent container with .html("") while creating your svg:
var svg = d3.select("#rightside").html("").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 + ")");
remove also works but I prefer this slightly terser way of doing it.
Before appending the new avg, just call d3.select('svg').remove(); That will remove the first svg on the page (I'm assuming you only have one). If you don't have an svg, it won't fail, so you can always call it.
I'm just getting into using d3, and relatively novice in js still. I'm trying to set up a page of log file visualizations for monitoring some servers. Right now I'm focusing on getting a line chart of CPU utilization, where I can focus on specific time periods (So an x-zoom only). I am able to do a static charts easily, but when it comes to the zooming the examples are going over my head and I can't seem to make them work.
This is the static example I followed to get things up and running, and this is the zoom example I've been trying to follow.
My input csv is from a rolling set of log files (which will not be labled on the first row), each row looks like this:
datetime,server,cpu,memory,disk,network_in,network_out
So far what I've been able to get on the zoom looks like this:
var parseDate = d3.time.format("%Y-%m-%d %H:%M:%S").parse;
// define dimensions of graph
var margin = {top: 20, right: 80, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(-height, 0)
.tickPadding(6);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickSize(-width)
.tickPadding(6);
// Define how we will access the information needed from each row
var line = d3.svg.line()
.interpolate("step-after")
.x(function(d) { return x(d[0]); })
.y(function(d) { return y(d[2]); });
// Insert an svg element into the document for each chart
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 + ")");
// Declare zoom handler
var zoom = d3.behavior.zoom().on("zoom", draw);
// Open the log and extract the information
d3.text("log.csv", function(text) {
var data = d3.csv.parseRows(text).map(function(row) {
return row.map(function(value, index) {
if (index == 0) {
return parseDate(value);
}
else if (index > 1) {
return +value;
}
else {
return value;
}
});
});
// Set the global minimum and maximums
x.domain(d3.extent(data, function(d) { return d[0]; }));
y.domain(d3.extent(data, function(d) { return d[2]; }));
zoom.x(x);
// Finally, we have the data parsed, and the parameters of the charts set, so now we
// fill in the charts with the lines and the labels
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.attr("text-anchor", "end")
.text("Percent (%)");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
svg.append("text")
.attr("x", margin.left)
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.text('all');
svg.append("rect")
.attr("class", "pane")
.attr("width", width)
.attr("height", height)
.call(zoom);
svg.select("path.line").data([data]);
draw();
});
function draw() {
svg.select("g.x.axis").call(xAxis);
svg.select("g.y.axis").call(yAxis);
svg.select("path.line").attr("d", line);
}
What this gives me is a very sluggish chart that can be zoomed and panned, but it does not clip off the line at the ends of the chart. I've tried adding in the clipping elements described in the example, but that ends up fully erasing my line every time.
Thanks for any help or direction
I am trying to essentially rotate this horizontal bar chart into a vertical bar chart, but can't figure out how to do so. I can create a normal column chart, but once I try to put in the negative values and compute the y and height, all hell breaks loose. Here's my fiddle. (At least I was able to create the y-axis (I think).)
What am I doing wrong here?
var data = [{"letter":"A",'frequency':10},{"letter":"B","frequency":-5},{"letter":"C","frequency":7}];
var margin = {top: 20, right: 20, bottom: 30, left: 40}, width = 750 - margin.left - margin.right, height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangeRoundBands([0, width], .1);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
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 + ")");
var x0 = Math.max(-d3.min(data), d3.max(data));
x.domain(data.map(function(d) { return d.letter; }));
y.domain([d3.min(data, function(d) { return d.frequency; }), d3.max(data, function(d) { return d.frequency; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.selectAll(".bar")
.data(data).enter().append("rect")
.attr("class", "bar")
.attr("x", function(d, i) { return x(d.letter); })
.attr("y", function(d, i) { return x(Math.min(0, d.frequency));})
.attr("width", x.rangeBand())
.attr("height", function(d) { return Math.abs(x(d.frequency) - x(0)); });
Looks like there are two problems here:
The typos: .attr("y", function(d, i) { return x(...);}) should now be .attr("y", function(d, i) { return y(...);}). Same is true for the scales in your height attribute.
The change from a 0 base on the X axis to a 0 base on the Y axis. With a zero-based bar on the X axis, the x attribute of the bar is x(0). With a 0 based bar on the Y axis, the y attribute of the bar is not y(0), but y(value) (because the "base" of the bar is no longer the leading edge of the rectangle) - so in this code you need to use Math.max(0, value) (which will give y(value) for positive values) instead of Math.min(0, value):
svg.selectAll(".bar")
// ...snip...
.attr("y", function(d, i) { return y(Math.max(0, d.frequency));})
.attr("height", function(d) { return Math.abs(y(d.frequency) - y(0)); });
See updated fiddle: http://jsfiddle.net/pYZn8/5/
Is it possible to put D3 code into a function and then call the function?
For example, I am interested in using this histogram code
http://bl.ocks.org/3048450
If I put code in a function and call like
function hist(bin, data) {
//the D3 histogram plotting code
// Generate an Irwin–Hall distribution of 10 random variables.
var values = d3.range(1000).map(d3.random.irwinHall(10));
// A formatter for counts.
var formatCount = d3.format(",.0f");
var margin = {top: 10, right: 30, bottom: 30, left: 30},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([0, 1])
.range([0, width]);
// Generate a histogram using twenty uniformly-spaced bins.
var data = d3.layout.histogram()
.bins(x.ticks(20))
(values);
var y = d3.scale.linear()
.domain([0, d3.max(data, function(d) { return d.y; })])
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
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 + ")");
var bar = svg.selectAll(".bar")
.data(data)
.enter().append("g")
.attr("class", "bar")
.attr("transform", function(d) { return "translate(" + x(d.x) + "," + y(d.y) + ")"; });
bar.append("rect")
.attr("x", 1)
.attr("width", x(data[0].dx) - 1)
.attr("height", function(d) { return height - y(d.y); });
bar.append("text")
.attr("dy", ".75em")
.attr("y", 6)
.attr("x", x(data[0].dx) / 2)
.attr("text-anchor", "middle")
.text(function(d) { return formatCount(d.y); });
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
}
hist(...); //call the function
it doesn't plot. Why is that?
I found the cause of the bug. I misspell "function" as "function"
Where are you including and running that code? If you're including and runningit in the <head> element, then when the script executes the browser will not be aware of the <body> element. d3.select("body") will return an empty selection, and therefore there will be nothing to which to append and svg element.
Try putting the script within the <body> or use a library like jQuery (i.e., $(document).ready()) to ensure that the document has been loaded before executing your script.