Appending lines to D3 bar chart - absolutely nothing happens - javascript

I have an otherwise fine working grouped bar chart script to which I'm trying to add simple reference lines. The relevant code:
//Set up margins and dimensions according to http://bl.ocks.org/3019563
var margin = {top: 20, right: 10, bottom: 20, left: 30},
width = 810 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
/* Set up the primary x scale */
var x0 = d3.scale.ordinal()
.rangeRoundBands([0, width], .1)
.domain(data.map(function (d) {
return options.xPrimaryScaleAccessor(d);
}));
/* Set up the secondary x scale */
var x1 = d3.scale.ordinal()
.domain(xSecondaryScaleValues)
.rangeRoundBands([0, x0.rangeBand()]);
/* Set up the y scale as a linear (continous) scale with a total range of 0 - full height and a domain of 0-100 */
var y = d3.scale.linear()
.range([height, 0])
.domain([0, 100]);
/* Set up a color space of 20 colors */
var color = d3.scale.category20();
/* Set up the x axis using the primary x scale and align it to the bottom */
var xAxis = d3.svg.axis()
.scale(x0)
.orient("bottom");
/* Set up the y axis using the y scale and align it to the left */
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
/* Create an SVG element and append it to the body, set its dimensions, append a <g> element to
* it and apply a transform translating all coordinates according to the margins set up. */
var svg = d3.select(options.target).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 a space for definitions
var defs = svg.append("defs");
setupDropShadowFilter(defs, 3, 3, 3); //Sets up a gaussian blur filter with id 'drop-shadow'
/* Append a <g> element to the chart and turn it into a representation of the x axis */
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
/* Append a <g> element to the chart and turn it into a representation of the y axis */
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(options.yLabel);
var dataArr = y.ticks(yAxis.ticks());
/* Draw the reference lines */
svg.selectAll("line")
.data(dataArr)
.enter().append("line")
.attr("x1", 0)
.attr("x2", width)
.attr("y1", y)
.attr("y2", y)
.style("stroke", "#ccc");
/* Set up the bar groups */
var group = svg.selectAll(".group")
.data(data)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x0(options.xPrimaryScaleAccessor(d)) + ",0)"; });
/* Draw the bars */
group.selectAll("rect")
.data(options.valueAccessor)
.enter().append("rect")
.attr("width", x1.rangeBand())
.attr("x", function(d) { return x1(d.label); })
.attr("y", function(d) { return y(d.value); })
.attr('rx', options.barCornerRadius)
.attr('ry', options.barCornerRadius)
.attr("height", function(d) { return height - y(d.value); })
.style("fill", function(d) { return getStripedPattern(defs, color(d.label)); //Sets up a pattern and returns its ID })//Todo: fill with pattern instead. see http://tributary.io/tributary/2929255
.style("filter", "url(#drop-shadow)");
/* Draw a legend */
var legend = svg.selectAll(".legend")
.data(xSecondaryScaleValues)
.enter().append("g")
.attr("class", "legend")
.attr("transform", function(d, i) { return "translate(0," + (xSecondaryScaleValues.length-i-.25) * (height/xSecondaryScaleValues.length) + ")"; });
legend.append("rect")
.attr("x", width - 9)
.attr("width", 18)
.attr("height", 18)
.style("fill", color);
legend.append("text")
.attr("y", 9)
//.attr("dy", ".35em")
.attr("transform", "translate(" + (width - 6) + ",-8)rotate(-90)" )
.style("text-anchor", "start")
.text(function(d) { return d; });
EDIT: I have also tried to append rect elements instead with hardcoded coordinates and dimensions, but those also didn't make it to the DOM.
EDIT 2: More or less full code now included.
Basically, nothing happens. No lines are appended and there are no errors in the console. The dataArr is a plain array of numbers and y(number) is confirmed to return good values in the output range.
I think (and debug suggests) that the chain dies at the append() stage, possibly because .enter() return something useless.
Console log after .data():
Console log after .enter():
Console log after .append():
I've been stuck on this for a good while now, so grateful for any ideas about what may go wrong. I'm sure I'm overlooking something obvious...

The problem is that the code that generates the axes appends line elements to the SVG. As it is run before appending the reference lines, calling svg.selectAll("line").data(...) matches the existing lines with the data. There are more lines than data elements, so no new elements need to be added and the .enter() selection is empty.
There are a few ways to fix this. You could move the code that generates the reference lines further up. You add a g element that contains these lines. You could have a special class for these lines and adjust the selector accordingly. Or you could provide a custom matching function to .data().

Related

D3.js v3 Dot Plot Histogram Duplicating Values

I am building a dot plot histogram with d3.js v3 and I have pretty much finished everything up - except for whatever reason some of my data points are duplicating (certain circles repeating themselves - not all of them, just some). I tried tweaking the axis parameters, as well as the data itself [deleted rows with null values, etc]- however sadly to no avail.
Any help would be immensely appreciated.
Here's my relevant code:
<div id="dotHappy"></div>
var data = d3.csv('happy_dot_modified.csv', function(data) {
data.forEach(function(d) {
d["city"] = d["city"];
d["Happy"] = +d["Happy"];
d["thc"] = +d["thc"];
});
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 1560 - margin.left - margin.right,
height = 1260 - margin.top - margin.bottom;
I tried this coder block but it wasn't working. (Not sure if this is even what's giving me the issue anyways - perhaps not).
// var x = d3.scale.linear()
// .range([0, width]);
So I went with this:
var x = d3.scale.ordinal()
.rangePoints([0, width])
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("#dotHappy")
.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 chart = svg.append("g")
.attr("id", "chart");
Also tried tweaking this, which may or may not even be part of the problem.
x.domain(data.map(d => d.Happy));
y.domain([5, 33]);
// y.domain(data.map(d => d.city));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
// .append("text")
.attr("class", "label")
.attr("x", width)
.attr("y", -6)
.style("text-anchor", "end")
.text("Happy");
svg.append("g")
.attr("class", "y axis")
// .attr("transform", "translate(0," + width + ")")
.call(yAxis)
// .append("text")
.attr("class", "label")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("THC");
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("transform", function(d) {
return "translate(" + x(d.Happy) + ".0)";
});
var dots = groups.selectAll("circle")
.data(function(d) {
return d3.range(1, +d.thc + 1)
// return d3.range(d.thc)
})
.enter().append("circle")
.transition().duration(1000)
.attr("class", "dot")
.attr("r", 10)
.attr("cy", function(d) {
return y(d)
})
.style("fill", "blue")
.style("opacity", 1);
})
Here is a snapshot of my csv file:
city. |. Happy. | thc
Boston. 37. 23
NYC. 22. 30
Chicago. 88. 5
Following is a screenshot of what it currently looks like. So in this case, the tooltip displaying the text box 'The Sister' should be only for one circle (because it should only be one data point), however if you hover over the other 10 orange circles below it, it's all the same - indicating it has been repeated 11 times total:
Actually, all of the circles are repeating vertically. You may not see them all because the repeated circles are being overlapped by other colored circles as these other circles get drawn. For example, the yellow data point "The Sister" is repeating all the way down to the bottom, but the data points below the yellow ones, in blue, pink, green, blue, etc., drew themselves on top of the yellow repeats.
The culprit is this code:
.selectAll("circle")
.data(function(d) {
return d3.range(1, +d.thc + 1)
// return d3.range(d.thc)
})
.enter().append("circle")
which, if you don't want it to repeat, should have been just one line:
.append("circle")
To explain what happened, this code:
var groups = svg.selectAll(".groups")
.data(data)
.enter()
.append("g")
.attr("class", "groups") //NOTE: you should add this line since you have 'selectAll(".groups")'
.attr("transform", function(d) {
return "translate(" + x(d.Happy) + ".0)";
});
already creates a g element for every row in the csv file. And for every g, you created an array using d3.range(1, +d.thc + 1), and appended a circle for each item in that array.
As an example, let's take the row representing "The Sister" data point that has a THC of 33. For that one data point, the code creates one <g>, inside of which it binds the array [1, 2, 3, ..., 33], and therefore appends 33 circles to the <g> element, with the cy attribute between y(1) and y(33).
Now, the question that follows is that, you specified a domain with a minimum of 5 with y.domain([5, 33]). Yet the data-bounded array, generated with d3.range, always begins with 1 and increments up to the value of THC. So some of the values in the array (1,2,3, and 4) always fall outside the y-axis, but d3 was able to translate it to a proper y-position. Is that possible? By default, yes, d3.scale extrapolates when the data is outside of the domain.
By default, clamping is disabled, such that if a value outside the input domain is passed to the scale, the scale may return a value outside the output range through linear extrapolation. For example, with the default domain and range of [0,1], an input value of 2 will return an output value of 2.

D3 chart: While using d3.axisLeft(y) method, how do I prevent Y-Axis grid-lines from using the entire chart width

First of all, I apologize for such a long post and my absolute beginner knowledge about D3 framework.
I have managed to draw the following chart using D3 framework.
However, I am not been able to accomplish the following things:
1) Cannot draw Y-Axis vertical sideline
2) Cannot make the Y-Axis ticks to align centered. At the moment they all are left-aligned
3) Cannot make the color of X-Axis baseline same as Y-Axis gridlines
4) Cannot make the label "Growth Target (7)" center-aligned Right now it is left-aligned
1) Y-Axis Vertical Sideline:
The text Growth Target (7) is within the chart. In order to make the gridlines to stop before this, I have used this (approach 1)
// ******************* << Custom Y axis grid lines Begins
var yGrid = svg.selectAll(".hgrid")
.data(yTicks)
.enter()
.append("g")
.attr("transform", function(d){
return "translate(0, " + y(d) + ")"
});
yGrid.append("line")
.attr("class", "hgrid")
.attr("x1", 0)
// shorten growth target line width to give room for the label
.attr("x2", width - growth_target_width_adjusted);
yGrid.append("text")
.text(function(d){return d; })
.attr("class", "hgrid-label")
// update X and Y positions of the label
.attr("x", -15)
.attr("y", "0.3em");
d3.selectAll(".hgrid-label")
.each(function (d, i) {
d3.select(this).style("font-size", 13);
});
// ******************* Custom Y axis grid lines Ends >>
Rather than this (approach 2):
// gridlines in y axis function
function draw_yAxis_gridlines() {
return d3.axisLeft(y)
.tickValues(yTicks);
}
// add the Y grid lines
svg.append("g")
.attr("class", "grid axis yAxis")
.call(draw_yAxis_gridlines()
.tickSize(-width)
);
The above approach 2, however, creates the Y-Axis baseline but the gridlines are using the entire chart area and overlapping the text "Growth Target (7)".
Is there any way that I can control the width of each Y-Axis line using approach 2? If not, how can I draw the Y-Axis baseline using approach 1?
2) Y-Axis ticks to align center
Currently, the Y-Axis tick values (0, 5, 10...) are left-aligned. Is there a way to make them centered?
3) Color of X-Axis baseline same as Y-Axis gridlines
In my chart above, the X-Axis color is black while the gridline colors are light gray. I need to make the X-Axis color light gray as well. What is the right way to do this?
4) Center aligning the label "Growth Target (7)"
What is the right way to make the two lines of the label centered-aligned? Right now the lines are aligned left to each other.
The desired output needs to be like this:
Here is the complete chart script that I have now:
function barColor(data_month, current_month) {
if( parseInt(data_month) >= current_month)
return "#008600";
else
return "#c4c4c4";
}
function draw_gp_chart(data_str) {
var chart_container = jQuery("#ecbg_unitary");
jQuery(chart_container).html("");
var w = parseInt(jQuery(chart_container).width());
var h = 375;
var barPadding = 2;
// set the dimensions and margins of the graph
var margin = {top: 30, right: 20, bottom: 30, left: 40};
var width = w - margin.left - margin.right;
var height = h - margin.top - margin.bottom;
var growth_target_width_adjusted = margin.left + margin.right;
var svg = d3.select("#ecbg_unitary")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + ", 10)");
var data = data_str;
// set the ranges (less width of chart to allow room for Growth Target label)
var x = d3.scaleBand().range([0, width - growth_target_width_adjusted]).padding(0.2);
var y = d3.scaleLinear().range([height, 0]);
// Scale the range of the data in the domains
x.domain(data.map(function (d) {
return d.month;
}));
var y_domain_upperBound = d3.max(data, function (d) {
return d.points;
});
y_domain_upperBound = Math.round(y_domain_upperBound / 10) * 10 + 10;
y.domain([0, y_domain_upperBound]);
// Create Y-Axis tick array to draw grid lines
var yTicks = [];
var tickInterval = 5;
for (var i = 0; i <= y_domain_upperBound; i = i + tickInterval) {
yTicks.push(i);
}
// gridlines in y axis function
function draw_yAxis_gridlines() {
return d3.axisLeft(y)
.tickValues(yTicks);
}
// ******************* << Growth Target line Begins
var targetGoalArr = [parseInt(jQuery("#ecbg_unitary_growth_target").val())];
var target = svg.selectAll(".targetgoal")
.data(targetGoalArr)
.enter()
.append("g")
.attr("transform", function(d){
return "translate(0, " + y(d) + ")"
});
target.append("line")
.attr("class", "targetgoal")
.attr("x1", 0)
// shorten growth target line width to give room for the label
.attr("x2", width - growth_target_width_adjusted);
// Adding two SVG text elements since SVG text element does not support text-wrapping.
// ref: https://stackoverflow.com/a/13254862/1496518
// Option 2: may be tried later - https://bl.ocks.org/mbostock/7555321
// Text element 1
target.append("text")
.text(function(d){return "Growth " })
.attr("class", "tglebal")
// update X position of the label
.attr("x", width - growth_target_width_adjusted + 5)
.attr("y", "-0.3em");
// Text element 2
target.append("text")
.text(function(d){return "Target (" + d + ")" })
.attr("class", "tglebal")
// update X position of the label
.attr("x", width - growth_target_width_adjusted + 5)
.attr("y", "1em");
d3.selectAll(".tglebal")
.each(function (d, i) {
d3.select(this).style("font-size", 12);
});
// ******************* Growth Target line Ends >>
// ******************* << Custom Y axis grid lines Begins
var yGrid = svg.selectAll(".hgrid")
.data(yTicks)
.enter()
.append("g")
.attr("transform", function(d){
return "translate(0, " + y(d) + ")"
});
yGrid.append("line")
.attr("class", "hgrid")
.attr("x1", 0)
// shorten growth target line width to give room for the label
.attr("x2", width - growth_target_width_adjusted);
yGrid.append("text")
.text(function(d){return d; })
.attr("class", "hgrid-label")
// update X and Y positions of the label
.attr("x", -15)
.attr("y", "0.3em");
d3.selectAll(".hgrid-label")
.each(function (d, i) {
d3.select(this).style("font-size", 13);
});
// ******************* Custom Y axis grid lines Ends >>
// append the rectangles for the bar chart
svg.selectAll("rect")
.data(data)
.enter().append("rect")
.attr("x", function (d) {
return x(d.month);
})
.attr("width", x.bandwidth())
.attr("y", function (d) {
return y(d.points);
})
.attr("height", function (d) {
return height - y(d.points);
})
//.attr("height", function(d) {return d.points;})
.attr("fill", function (d) {
return barColor(d.data_month_number, d.current_month_number)
});
// column labels
svg.selectAll(".colvalue")
.data(data)
.enter()
.append("text")
.attr("class", "colvalue")
.text(function (d) {
return d.points;
})
.attr("text-anchor", "middle")
.attr("x", function (d, i) {
return x(d.month) + x.bandwidth() / 2;
})
//.attr("x", function(d) { return x(d.month); })
.attr("y", function (d) {
//return h - (d.points * 4) - 10;
return y(d.points) - 10;
})
.attr("font-family", "Roboto")
.attr("font-size", "13px")
.attr("font-weight", "bold")
.attr("fill", "#606668");
// add the x Axis
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
// text label (axis name) for the x axis
var xAxisNameHeightFromTop = height + margin.bottom + 20;
svg.append("g")
.attr("class", "x-axis-name")
.append("text")
.attr("transform", "translate(" + width / 2 + ", " + xAxisNameHeightFromTop + ")")
.style("text-anchor", "middle")
.text("MONTH");
// text label for the y axis
svg.append("g")
.attr("class", "y-axis-name")
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("SALES UNITS");
// add the Y grid lines
/*svg.append("g")
.attr("class", "grid axis yAxis")
.call(draw_yAxis_gridlines()
.tickSize(-width)
);*/
d3.selectAll(".yAxis>.tick>text")
.each(function (d, i) {
d3.select(this).style("font-size", 13);
});
}

D3: Cannot add text element to graph

Everything seems to be working as expected except for the text element. I can't seem to get text elements to append to my g element. Here is my code so far. I've inspected the DOM in a chrome browser, but I don't see any text elements and I'm not sure why. I was using this site as a sort of guide: https://www.dashingd3js.com/svg-text-element.
Also, I know the elements should stack on each other since they all share the same x and y position, I'm just trying to get the elements to appear first.
var svg = d3.select('svg'),
margin = {top: 60, right: 20, bottom: 45, left: 60},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
var x = d3.scaleBand().rangeRound([0, width]).padding(.1);
var y = d3.scaleLinear().range([height, 0]);
var g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
x.domain(test.map(function(d) { return d.level; }));
y.domain([0, d3.max(test, function(d) { return d.time; })]);
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.attr("writing-mode", "tb-rl")
.call(d3.axisBottom(x).tickSize(0).tickPadding(10));
g.append("g")
.attr("class", "axis axis--y")
.call(d3.axisLeft(y).ticks(10))
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 9)
.attr("dy", "0.71em")
.attr("text-anchor", "end")
.text("Frequency")
g.selectAll(".bar")
.data(test)
.enter()
.append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.level); })
.attr("y", function(d) { return y(d.time); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.time); })
var text = g.selectAll("text")
.data(test).enter()
.append("text");
var textLabels = text.attr("x", 100)
.attr("y", 100)
.text("testing")
.attr("fill", "blue")
your selection g.selectAll("text") will include text from the axes you appended to the g element earlier in the code, so your "enter" won't have anything in it. D3 compares the incoming to data to the items in the selection, and if you don't specify a key, will do a simple comparison on the number of elements in each, and then add (enter) and remove (exit) accordingly.
If you change your selection to something that you know won't be on in DOM yet (ie an empty selection), for example g.selectAll(".label"), then when you append data, the enter selection will contain your new text labels.

Centering D3 chart + adding responsiveness

A little background, I'm still fairly new to JS and development in general, so I may be missing something obvious. I got this chart working pretty well using d3, but I cannot get the positioning right no matter what I do. I've tried manipulating it with CSS and it just doesn't seem to behave in a logical way. I set display to block and margins to auto and it didn't affect it at all. The only way I can change the positioning is adjusting the margins in the d3 code, but that doesn't do very much for responsiveness. I've also tried using text-align and that didn't work either. What I'm trying to do is center it and have it scale larger as the screen size increases. This should all be easy to do in CSS in theory, but it just doesn't seem to work at all. Thanks for any help.
Here is the JS code:
// Set the dimensions of the canvas / graph
var margin = {top: 20, right: 0, bottom: 70, left: 70},
width = 300 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%-m/%-d/%Y").parse;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(10);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// chart area fill
var area = d3.svg.area()
.x(function(d) { return x(d.Date); })
.y0(height)
.y1(function(d) { return y(d.Forecast); });
// Define the line
var valueline = d3.svg.line()
.interpolate("cardinal")
.x(function(d) { return x(d.Date); })
.y(function(d) { return y(d.Orders); });
var valueline2 = d3.svg.line()
.interpolate("cardinal")
.x(function(d) { return x(d.Date); })
.y(function(d) { return y(d.Forecast); });
// Adds the svg canvas
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 + ")");
// Get the data
d3.csv("csv/Forecast.csv", function(error, data) {
data.forEach(function(d) {
d.Date = parseDate(d.Date);
d.Orders = +d.Orders;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.Date; }));
y.domain([0, d3.max(data, function(d) { return d.Orders; })]);
// Area
svg.append("path")
.datum(data)
.attr("class", "area")
.attr("d", area);
// Add the valueline path.
svg.append("path")
.attr("class", "line")
.attr("d", valueline2(data))
.style("stroke", "#A7A9A6");
svg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-65)");
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
});
JSFIDDLES
Responsive & Centered:
https://jsfiddle.net/sladav/6fyjhmne/3/
Not Responsive: https://jsfiddle.net/sladav/7tp5vdkr/
For responsiveness, take advantage of the SVG viewBox:
Some links:
https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/viewBox
Using ViewBox to resize svg depending on the window size
Setting up viewBox:
var margin = {top: 100, right: 150, bottom: 100, left: 150}
var outerWidth = 1600,
outerHeight = 900;
var width = outerWidth - margin.right - margin.left,
height = outerHeight - margin.top - margin.bottom;
d3.select(".plot-div").append("svg")
.attr("class", "plot-svg")
.attr("width", "100%")
.attr("viewBox", "0 0 " + outerWidth + " " + outerHeight)
.append("g")
.attr("class", "plot-space")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")"
);
Points of note in the above:
SVG is put in a div -- I'll adjust the size and position of the div rather than the svg
SVG width is set as % of parent/div, not absolute.
Everything you draw in SVG now is with respect to outerWidth x outerHeight and unitless parameters are rescaled by viewBox.
Taking a look at the rect in my JSFIDDLE example...
d3.select(".plot-svg").append("rect")
.attr("x", 0)
.attr("y", 3*outerHeight/4)
.attr("width", 800)
.attr("height", outerHeight/4)
.attr("fill", "grey")
Resize your window and the rectangle will always fill 1/2 the svg because 800/1600 (note: 1600 is outerWidth).
For adjusting position/centering:
Manipulate the div containing your SVG/chart to position it how you want to. In my example it takes up 50% of the page and is centered because of margin: auto.
.plot-div{
width: 50%;
display: block;
margin: auto;
}
Whatever methods you use to scale/position your div, your chart should follow suit.

Cannot get line to draw on log scale y-axis, but can on linear

I am trying to draw a simple line chart in D3. I am able to draw the data line with a linear scale on the Y-axis (ignore the fact that there are no ticks):
Here is the code and a relevant JSFiddle for this:
var margin = { top: 10, right: 245, bottom: 30, left: 100 },
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// Set the X and Y scale (e.g., linear vs. log) and the domains
var x = d3.scale.linear().domain([-95,-74]).range([0, width]);
// ******* CHANGE linear() to log() *************************** //
var y = d3.scale.linear().domain([0.000010,1]).range([height, 0]);
// Set the locations of the axis (e.g., bottom and left)
var xAxis = d3.svg.axis().scale(x).orient("bottom");
var yAxis = d3.svg.axis().scale(y).orient("left").ticks(0, ".1");;
// Set up some lines and related colors
var color = d3.scale.category10();
var line = d3.svg.line().x(function (d) { return x(d.power); }).y(function (d) { return y(d.ber); });
// Set the graph to be drawn in the respective div (grapharea), and set dimensions
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 + ")");
// Append the X and Y axis to the actual SVG graph
svg.append("g").attr("class", "y axis").call(yAxis);
svg.append("g").attr("class", "x axis").attr("transform", "translate(0," + height + ")").call(xAxis);
// Parse the JSON data response
var jsonString = '[{"power":-91.0,"ber":0.0},{"power":-92.0,"ber":0.0},{"power":-93.0,"ber":1E-07},{"power":-94.0,"ber":6.5E-06},{"power":-95.0,"ber":0.000147}]';
var jsonData = JSON.parse(jsonString);
jsonData.forEach(function (d) {
d.ber = d.ber;
d.power = d.power;
});
// Set the data properly
x.domain(d3.extent(jsonData, function(d) { return d.power; }));
y.domain(d3.extent(jsonData, function(d) { return d.ber; }));
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("BER");
svg.append("path")
.datum(jsonData)
.attr("class", "line")
.attr("d", line);
If all I change is linear() to log() on the variable y, then I get a resulting log scale graph but my data line no longer shows up (relevant JSFiddle):
Does anyone know what I am doing wrong for the data line to not show up on the log scale graph?
You have zeros in your data. The log of zero is not defined. You'll need to use a different type of scale.

Categories