Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

d3 v4 padding inner set to .001 doesn't actually result in small padding between ranges

Tags:

html

css

d3.js

Is there a way to minimize the padding between ranges in a grouped horizontal bar chart?

I am building a horizontal bar chart directive for AngularJS and at the moment I am pretty close but it's the spacing that I am not exactly happy with. If there is a better way to build the vertical ranges I would very much appreciate any tips. Here's my result so far:

angular.module('MissionControlApp').directive('d3GroupedHorizontalBarChart2', ['d3', '$timeout', function(d3, $timeout) {
    return {
        restrict: 'E',
        scope: {
            data: '=',
            onClick: '&d3OnClick'
        },
        link: function(scope, ele) {
            var refreshScope = function() {
                scope.$apply();
            };

            var svg = d3.select(ele[0])
                .append("svg")
                .attr("width", "100%");

            // on window resize, re-render d3 canvas
            window.onresize = function() {
                return scope.$apply();
            };
            scope.$watch(function(){
                    return angular.element(window)[0].innerWidth;
                }, function(){
                    return scope.render(scope.data);
                }
            );

            // watch for data changes and re-render
            scope.$watch("data", function(newVals) {
                if(!newVals) return;
                return scope.render(newVals);
            }, true);

            // define render function for grouped bar charts
            scope.render = function(data){
                if(!data) return;

                // remove all previous items before render
                svg.selectAll("*").remove();

                // setup variables
                var margin = {top: 25, right: 40, bottom: 70, left: 150};
                var width = d3.select(ele[0])._groups[0][0].offsetWidth - margin.left - margin.right;
                var height = (scope.data.length * 60);

                svg.attr('height', height + margin.top + margin.bottom);

                var y0 = d3.scaleBand()
                    .rangeRound([0, height])
                    .paddingInner(0.01);

                var y1 = d3.scaleBand()
                    .padding(0.01);

                var x = d3.scaleLinear()
                    .rangeRound([0, width]);

                var color = d3.scaleLinear()
                    .domain([0, 25, 50, 75, 100])
                    .range(["#51b75d", "#90eb9d","#ffff8c","#f5c93f","#c45c44"])
                    .interpolate(d3.interpolateHcl);

                var xAxis = d3.axisBottom(x)
                    .tickSizeInner(-(height-5))
                    .tickPadding(8);

                var keys = d3.keys(data[0]).filter(function(key) { return key !== "user"; });
                y0.domain(data.map(function(d) { return d.user; }));
                y1.domain(keys).rangeRound([0, y0.bandwidth()]);
                x.domain([0, 100]);

                // Define bars
                var bar = svg.selectAll(".bar")
                    .data(data)
                    .enter().append("g")
                    .attr("transform", function(d) { return "translate(" + margin.left + "," + (y0(d.user) + (y0.bandwidth()/2) + margin.top - y1.bandwidth()) + ")"; });

                var barEnter = bar.selectAll("rect")
                    .data(function(d) { return d.values; })
                    .enter();

                barEnter.append("rect")
                    .attr("height", y1.bandwidth())
                    .attr("y", function(d) {return y1(d.name); })
                    .attr("x", 0)
                    .attr("value", function(d){return d.name;})
                    .attr("width", 0)
                    .attr("fill", function(d) { return color(d.value); })
                    .on("mouseover", function() { d3.select(this).style("cursor", "pointer");})
                    .on("mouseout", function() { d3.select(this).style("cursor", "default");})
                    .on("click", function(d){
                        scope.onClick({item: d});
                        d3.select(".selectedBlueFill").classed("selectedBlueFill", false);
                        d3.select(this).classed("selectedBlueFill", true);
                        $timeout(refreshScope, 0, false); // flush the digest cycle
                    })
                    .transition()
                    .duration(1000)
                    .attr("width", function(d) { return x(d.value); });

                barEnter.append("text")
                    .attr("fill", "#000")
                    .attr("y", function(d){return y1(d.name) + (y1.bandwidth() / 2);})
                    .attr("x", function(d){return x(d.value);})
                    .attr("dx", 5)
                    .attr("dy", ".35em")
                    .text(function(d){return parseFloat(d.value).toFixed(0) + "%";})
                    .attr("fill-opacity", 0)
                    .transition()
                    .duration(1500)
                    .attr("fill-opacity", 1);

                // Set up x axis
                svg.append("g")
                    .attr("class", "axisHorizontal")
                    .attr("transform", "translate(" + margin.left + "," + (height + margin.top) + ")")
                    .call(xAxis);

                // Set up y axis
                svg.append("g")
                    .attr("class", "y axis")
                    .attr("transform", "translate(" + margin.left + "," + margin.top + ")")
                    .call(d3.axisLeft(y0));

                // Draw the legend
                // Create the gradient for the legend
                svg.append("defs")
                    .append("linearGradient")
                    .attr("id", "legend-traffic")
                    .attr("x1", "0%").attr("y1", "0%")
                    .attr("x2", "100%").attr("y2", "0%")
                    .selectAll("stop")
                    .data(color.range())
                    .enter().append("stop")
                    .attr("offset", function(d,i) { return i/(color.range().length-1); })
                    .attr("stop-color", function(d) { return d; });

                // Legend variables
                var legendWidth = width * 0.6;
                var legendHeight = 10;

                // Legend container
                var legendSvg = svg.append('g')
                    .attr("class", "legendWrapper")
                    .attr("transform", "translate(" + ((width + margin.left + margin.right)/2) + "," + (height + margin.top + margin.bottom) + ")");

                // Draw the rectangle
                legendSvg.append("rect")
                    .attr("class", "legendRect")
                    .attr("x", -legendWidth/2)
                    .attr("y", -30)
                    .attr("width", legendWidth)
                    .attr("height", legendHeight)
                    .attr("fill", "url(#legend-traffic)");

                // Append title
                legendSvg.append("text")
                    .attr("class", "legendTitle")
                    .attr("x", 0)
                    .attr("y", -35)
                    .attr("text-anchor", "middle")
                    .text("Worksets Opened %");

                // Set scale for x-axis
                var xScale = d3.scaleLinear()
                    .range([0, legendWidth])
                    .domain([0,100]);

                // Define x-axis
                var legendAxis = d3.axisBottom(xScale).ticks(5);

                // Set up x-axis
                legendSvg.append("g")
                    .attr("class", "axisLegend")
                    .attr("transform", "translate(" + (-legendWidth/2) + "," + (legendHeight-30) + ")")
                    .call(legendAxis);
            };
        }
    };
}]);

However the result I am getting is with large inner spacing. I am setting the .paddingInner property to 0.001 and still pretty much end up with large spacing......ideas?

enter image description here

like image 718
konrad Avatar asked Feb 06 '18 20:02

konrad


1 Answers

The idea would be to make groups, where each group hold unique bars for the group.

Now once you have the group you can alter the between distance with some maths, as shown below in snippet below:

var bar = chart
    .selectAll("g")
    .data(zippedData)
    .enter().append("g")
    .attr("transform", function(d, i) {
      //here barHeight is the width of the bars.
      return "translate(" + spaceForLabels + "," 
        + (i * barHeight + gapBetweenGroups * (0.5 + Math.floor(i/data.series.length))) + ")";
    });

By changing value of gapBetweenGroups you can regulate the distance of the groups.

Working code here

like image 106
Cyril Cherian Avatar answered Nov 25 '22 10:11

Cyril Cherian