Modifying ticksize when resizing the chart - d3.js

I am using the trick of sizing the ticks on my axes in order to render a grid effect on my chart. The problem I have is that I am not able to work out how to resize the ticks on my y axis when the width of the chart changes due to a window resize.
The code that I have to initialize my chart is as follows:
this._xRange = d3.time.scale().range([0, chartWidth]);
this._yRange = d3.scale.linear().range([chartHeight, 0]);
this._xAxis = d3.svg.axis().scale(this._xRange)
.orient("bottom")
.tickSize(-chartHeight)
.tickSubdivide(true);
this._yAxis = d3.svg.axis().scale(this._yRange)
.orient("left")
.tickSize(-chartWidth)
.tickSubdivide(true);
When the chart is resized, I am recalculating the width and height of the chart and attempting to redraw the axes. For the y axis I am using the following code:
// Redraw the y axis
this._yAxis = d3.svg.axis().scale(this._yRange)
.orient("left")
.tickSize(-chartWidth)
.tickSubdivide(true);
this._chart.call(this._yAxis.orient("left"));
This is failing with the following exception in the browser console:
DOMException: Failed to execute 'insertBefore' on 'Node': The node before
which the new node is to be inserted is not a child of this node. "Error:
Failed to execute 'insertBefore' on 'Node': The node before which the new
node is to be inserted is not a child of this node.

I believe that I have hit upon a solution. It appears that I need to remove the existing y axis, create a new generator and then add the new generator to the chart. I am using the following code to do this:
d3.select(".y.ResourceStatsAxis").remove();
this._yAxis = d3.svg.axis().scale(this._yRange)
.orient("left")
.tickSize(-chartWidth)
.tickSubdivide(true);
this._chart.append("g")
.attr("class", "y ResourceStatsAxis")
.call(this._yAxis);

Related

JavaFX Line Chart NumberAxis with strict upper bound, need padding of one tick like CategoryAxis does automatically

So I'm creating a Line Chart with an undetermined number of XYChart.Series'. The xAxis is a CategoryAxis and the yAxis is a NumberAxis. The problem I have is that I need to have an upper bound max of 100 on the yAxis as it represents %. When a series is plotted which hits the top 100, the circle point is clipped by the boundaries of the chart area. See the picture.
MyChart
The same thing does NOT happen with the xAxis natively it seems (because I didn't set an upper bound?) and I'd like to achieve the same effect where it looks like there is an extra tick of padding between the upper bound and the chart's edge, but without a tick label. Setting the upper bound on the yAxis to 105 makes it look like upper bound IS 105, which is not an acceptable workaround.
This is how I instantiate the axes:
this.xAxis = new CategoryAxis();
this.yAxis = new NumberAxis("Percent", 0.0, 100.0, 10.0);
The Line chart is built like so and then added to the parent node which is the center of a BorderLayout:
LineChart<String, Number> lineChart = new LineChart<>(this.xAxis, this.yAxis);
List<XYChart.Series<String, Number>> seriesList = null;
if (seriesMap.containsKey(this.unitNameForChart)) {
seriesList = seriesMap.get(this.unitNameForChart);
}
else {
seriesList = buildTimeStepSeries();
}
lineChart.getData().addAll(seriesList);
...
I've tried adding padding/insets directly to the LineChart and yAxis yet nothing worked out. I'm open for CSS or direct coding solutions.

d3.js / without using the call method, you can view the x-axis?

Current code
/ Call method used
var svg = d3.select("#hoge").append("svg")
.attr("width", 600).attr("height", 400)
var xScale = d3.scale.linear()
.domain([0, 100])
.range([0, 400]);
svg.append("g")
.attr("class", "axis")
.call(d3.svg.axis()
.scale(xScale)
);
I want to know
/ Call method unused
var svg = d3.select("#hoge").append("svg")
.attr("width", 600).attr("height", 400)
var xScale = d3.scale.linear()
.domain([0, 100])
.range([0, 400]);
svg.append("g")
.attr("class", "axis");
d3.svg.axis(svg).scale(xScale);
If you do not use the call method
The x-axis can not be displayed?
d3.svg.axis() creates and returns a function that appends the SVG elements to display the axis. It does not actually append anything. If you don't call the function that is returned, those elements will not be added. This is a common pattern in d3.js so it is important to understand.
Let's say you create these two variables for the axis function and the axis group element:
var axisFunction = d3.svg.axis().scale(xScale);
var axisGroup = svg.append("g")
.attr("class", "axis");
The following two ways of calling the axis function are equivalent:
axisFunction(axisGroup);
axisGroup.call(axisFunction);
The call syntax exists simply to enable method chaining like you have in your current code example.
The call operator is identical to invoking a function by hand; but it makes it easier to use method chaining.
https://github.com/mbostock/d3/wiki/Selections#call
Mike Bostock's article Towards Reusable Charts does a good job of explaining this pattern. The pattern described in the article to create the reusable chart is used for a variety of items in d3.js such as d3.svg.axis.

D3 Redraw Y-axes dynamically

I would like to create a dynamic graph with multiple (linear) axes. After the axes have been drawn, I would like (as new data arrives) to change the Data Domain and redraw/update the axes. Can I select the existing axis with D3 and do this or do I have to explicitly save each axis in my code? I hope my question is not confusing.
// init all Y-Axis
$.each(chart.YAxes, function (index) {
var yScale, yAxis;
yScale = d3.scale.linear().range([chartHeight, 0]);
yScale.domain([this.YMin, this.YMax]);
yAxis = d3.svg.axis()
.scale(yScale)
.ticks(10, this.Title)
.orient("left");
d3Chart.append("g")
.attr("class", "yAxis " + "y" + this.ID)
.call(yAxis);
......
// update Y-Axis (after new data arrives...)
var myYAxis = d3.select(".y" + yAxis.ID);
var myScale = myYAxis. **// get Scale ??**
myScale.domain([newYMin, newYMax]);
d3Chart.select(".y" + yAxis.ID)
.transition().duration(300).ease("sin-in-out")
.call(myYAxis);
thx...!
You need to keep references to the axis object so that you can call it again. Selecting the DOM element that contains it and calling that won't work. There are lots of examples on how to update axes, e.g. in this question.

Is there a malformation in my GeoJSON or a bug in my D3 code?

I am new to d3 and GeoJson. I have this GeoJSON which displays on geojson lint. I am trying to display the GeoJSON as a map with the following code:
var width = 960,
height = 1160;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var projection = d3.geo.transverseMercator().translate([width / 2, height / 2]).scale(150).center(-90.088, 29.957);
var path = d3.geo.path().projection(projection);
d3.json("orleans.json", function(json) {
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("d", path);
});
The second to last line .attr("d", path) is throwing the following error deep in the d3 stack trace:
Error: Problem parsing d="MNaN,NaNLNaN,NaNL8.76110196153104,1051.238898038469L8.76110196153104,108.76110196153104L951.238898038469,108.76110196153104L951.238898038469,1051.238898038469LNaN,NaNLNaN,NaNLNaN,NaNLNaN,NaNLNaN,NaNLNaN,NaNZMNaN,NaNLNaN,NaNLNaN,NaNZMNaN,NaNLNaN,NaNLNaN,NaNZMNaN
Is there a problem with my GeoJSON? Or a problem with how I am using the GeoJSON with D3? It seems like this error comes from malformed input to D3 -- but my GeoJSON lints. So maybe this is a problem with my D3 syntax and not my GeoJSON?
The GeoJSON does seem like it is getting loaded properly. When I console.log(json) right after the call
to d3.json("orleans.json", function(json) it shows the following:
There are two problems with your code:
The .center() function takes a single array argument and not two.
The transverse mercator projection doesn't support antimeridian cutting.
To fix, pass an array instead of two numbers and restrict the center to -90 degrees or less. Complete demo here.

How do you set the width of the legend of a Microsoft .Net chart control?

The legend currently auto-sizes according to the max width of the labels it contains, which is throwing my layout. I need to set a width or min-width. Here is my legend creation method:
public Legend CreateLegend()
{
var legend = new Legend();
legend.Enabled = true;
legend.Font = new Font("Arial", 11F);
legend.ForeColor = Color.FromArgb(102, 102, 102);
return legend;
}
It seems like anything I do which accesses the Position object for the legend (defines X an Y coords, width and height) makes the legend disappear completely.
So legend.Position.Width = 5 would make it disappear...
Okay, turns out that if you do not define all four properties of the Position object, it sets them to zero...so height was zero...so it disappeared. Awesome.

Resources