dc.js Stretch GeoChoroplethChart to Full Screen - javascript

I'm trying to create a world map using d3.geoChoroplethChart. My problem is that I can't stretch it to the full screen. I'm setting the width of the chart, and svg is picking up the setting, but the underlying g element does not. I assume it uses the projection together with the set width to calculate its size.
The projection I'm using
var projection = d3.geo.mercator()
.center([0, 55])
.scale(180);
Here is chart code
mapChart.width(2000)
.height(1000)
.dimension(countryDim)
.group(countryDimGroup)
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
.projection(projection)
.valueAccessor(function(d) { return d.value; })
.overlayGeoJson(africaJson.features, "Countries", function(d) {
return d.properties.admin.toUpperCase();
});
I use geoJSON from here http://geojson-maps.kyd.com.au/
Am I missing something?

Related

How to avoid overlapping and stack long text legends in D3 graph?

I have D3 graph base on Multi-line graph 3 with v7: Legend, this sample contains few and shorts legends for the graph. In my sample I want to increase the length for legends and stack the data if is necessary, I want to avoid overlapping in the legends,
https://bl.ocks.org/d3noob/d4a9e3e45094e89808095a47da19808d
dataNest.forEach(function(d,i) {
svg.append("path")
.attr("class", "line")
.style("stroke", function() { // Add the colours dynamically
return d.color = color(d.key); })
.attr("d", priceline(d.value));
// Add the Legend
svg.append("text")
.attr("x", (legendSpace/2)+i*legendSpace) // space legend
.attr("y", height + (margin.bottom/2)+ 5)
.attr("class", "legend") // style the legend
.style("fill", function() { // Add the colours dynamically
return d.color = color(d.key); })
.text(d.key);
});
There are two possible solutions I can think of, shown in this JSFiddle.
First, if it is acceptable that the legend is not part of the svg, then realize the legend with a simple unordered list in a container next to the svg. This is probably the best when there is a varying number of legend entries and there are no restrictions considering the styling via css. The browser takes care of varying lengths and does an automatic line break.
Second, if the legend has to be a part of the svg, one can use the getBBox()-method that determines the coordinates of the smallest rectangle around an object inside an svg.
In a first step select all the legend entries that have been rendered and get the bounding boxes:
const bbox = svg.selectAll(".legend")
.nodes()
.map(legend_entry => legend_entry.getBBox());
With this array and the width of the svg, we can calculate the positions for each legend entry:
bbox.reduce((pos, box) => {
let left, right, line;
if (pos.length === 0) {
left = 0;
line = 1;
} else {
/* The legend entry starts where the last one ended. */
left = pos[pos.length - 1].right;
line = pos[pos.length - 1].line;
}
/* Cumulative width of legend entries. */
right = left + box.width;
/* If right end of legend entry is outside of svg, make a line break. */
if (right > svg_width) {
line = line + 1;
left = 0;
right = box.width;
}
pos.push({
left: left,
right: right,
line: line,
});
return pos;
}, []);
Margins and paddings have to be included manually in the calculation of the positions. Of course, one could obtain the maximum width of all legend entries and make them all the same width with center alignment as in the d3noob example.
In the JSFiddle, I realized this repositioning by first rendering a hidden legend and then an additional one that is visible. It is of course possible to use only one legend, but I would not to take any chances that the process of repositioning is visible in the rendered document.

Vertical d3.svg.area?

I'm trying to make a vertical area chart, with a line in it's right borders. Line is being drawn perfectly ( top to bottom ), but the area is being drawn on right side of the path, it should be on the left.
I started with a horizontal chart, it worked then. See jsfiddle. I've also tried rotating the path but cannot get desired result.
var xScale = d3.scale.linear().range([0, sm_width]),
yScale = d3.time.scale().range([0, sm_height]);
var area = d3.svg.area().x(function(d) {
return xScale(xValue(d));
})
.y0(sm_height).y1(function(d) {
return yScale(yValue(d));
});
var line = d3.svg.line().x(function(d) {
return xScale(xValue(d));
})
.y(function(d) {
return yScale(yValue(d));
});
It might not help you though, what about using css like below
.area{
fill:white;
}
svg{
background:#ccc;
}
Solution was to rotate the chart itself as Cool Blue mentioned in the comment.

dc.js responive geojson US map

Testing with following sample code:
http://dc-js.github.io/dc.js/vc/
I would like to make this map responsive to the container/window size.
However, examples I have seen utilized topojson and SVG to scale the map.
http://techslides.com/demos/d3/d3-worldmap-boilerplate.html
Is this not possible with Geojson to update the map size in a similar fashion?
d3.json("../geo/us-states.json", function (statesJson) {
usChart.width(990)
.height(500)
.dimension(states)
.group(stateRaisedSum)
.colors(d3.scale.quantize().range(["#E2F2FF", "#C4E4FF", "#9ED2FF", "#81C5FF", "#6BBAFF", "#51AEFF", "#36A2FF", "#1E96FF", "#0089FF", "#0061B5"]))
.colorDomain([0, 200])
.colorCalculator(function (d) { return d ? usChart.colors()(d) : '#ccc'; })
.overlayGeoJson(statesJson.features, "state", function (d) {
return d.properties.name;
})
.title(function (d) {
return "State: " + d.key + "\nTotal Amount Raised: " + numberFormat(d.value ? d.value : 0) + "M";
});
You can follow the exact same approach as the one you linked to (techSlides).
It consists in replacing the whole svg by a new one re-drawn after a resize of the window. On redraw, the only thing that changes is the width and height you use as dimensions of the svg element.
Whether your data is GeoJson ot TopoJson doesn't affect this.
A smoother variant of this technique is to resize the existing svg element on resize of the window, without doing a redraw. Just add an SVG viewBox and preserveAspectRatio="xMinYMin", so that projection inside the SVG is kept.
Example in this blog, and its javascript.

Target in barchart in dc.js

I'm using dc.js for charting, and it is great.
I'm currently trying to create barchart with target-lines for each bar, similar to bullet-charts, see http://bl.ocks.org/mbostock/4061961. I only need the target-line (+value); I can create the stacked bars perfectly.
I currently use a composite chart, where the target is a lineChart with interpolation set to step-before.
composite
.width(768)
.height(480)
.x(d3.scale.linear().domain([0,20]))
.yAxisLabel("The Y Axis")
.legend(dc.legend().x(80).y(20).itemHeight(13).gap(5))
.renderHorizontalGridLines(true)
.compose([
dc.barChart(composite)
.dimension(dim)
.colors('red')
.group(grp1, "Top Line"),
dc.lineChart(composite)
.dimension(dim)
.colors('blue')
.group(grp2, "Bottom Line")
.interpolate("step-before")
])
.brushOn(false)
.render();
However, this creates ugly vertical lines. Is there a better approach?
Sorry for answering my own question :) I currently have this (with some modificatios to dc.js's barchart, as it is really buggy calculating barwidth etc.:)
composite
.width(300)
.height(480)
.x(d3.scale.ordinal().domain(['01M','03M','06M','12M'] ))
.xUnits(dc.units.ordinal)
.compose([
dc.barChart(composite)
.dimension(dim)
.group(perf , "CatA")
.stack(local, "CatB")
.stack(akzo , "CatC")
.colors(d2acolors)
.gap(0)
,
dc.barChart(composite)
.dimension(dim)
.padding(16)
.group(perf , "Perf")
.colors("black")
.gap(0)
,
dc.barChart(composite)
.dimension(dim)
.colors("black")
.padding(8)
.target(true)
.gap(0)
.group(dateGroup, "Target")
.valueAccessor(function(p) {
return targets[p.key];
})
])
.yAxisLabel("Number of vendors")
.xAxisLabel("Inactive for")
.legend(dc.legend().x(540).y(10))
.colors(d2acolors)
.render();
The targets are basically also barcharts, but the bottom of the bar is rendered 5 px below the top, so it becomes a line. The 'padding' setting reduces the width of the bars on either side by the number of pixels that is entered.
I will try to publisch the changes I made in the dc.js's barchart somewhere.

Resizing D3 force layout

Is it possible to resize a force layout in d3.js? For example, I have a layout defined by
var force = d3.layout.force()
.nodes(documents.nodes)
.linkDistance(0)
.friction(.2)
.size([width, height]);
force.start();
and I wish to resize it later in the project. Is this possible?
Shortly, on a resize event you should change the attributes of your svg object (it changes also the center of gravity) and resume force calculations:
window.onresize = function() {
width = window.innerWidth, height = window.innerHeight;
svg.attr('width', width).attr('height', height);
force.size([width, height]).resume();
};
An example for the d3 force resize is provided here.
According to the documentation, the .size() parameter of the force layout affects only the center of gravity and initial distribution of nodes, not the space available. You will have to enforce any constraints on that yourself, e.g. in the tick function:
force.on("tick", function() {
node.attr("cx", function(d) { return Math.min(maxX, Math.max(minX, d.x)); })
.attr("cy", function(d) { return Math.min(maxY, Math.max(minY, d.y)); });
}

Categories

Resources