D3.js组合条形图和折线图x轴错位问题

时间:2017-04-19 15:52:43

标签: d3.js bar-chart axis linechart stacked-chart

我创建了一个组合条形和线条的图表。我需要更改折线图x轴的定义,以使线与条形图中的条完全对齐,这意味着线必须从水平到垂直,反之亦然在条形结束处另一个人开始了。

运行以下代码段可以帮助您查看我所指的轻微移位/错位。



var app = {};

app.allBarsDatasets = [
    {
        "xAxisTickValue": "10-1",
        "barValue": 17
    },
    {
        "xAxisTickValue": "10-2",
        "barValue": 17
    },
    {
        "xAxisTickValue": "10-3",
        "barValue": 17
    }
];

app.allBarsDatasets2 = [
    [
        {
            "xAxisTickValue": "10-1",
            "barValue": 10
        },
        {
            "xAxisTickValue": "10-2",
            "barValue": 6
        },
        {
            "xAxisTickValue": "10-3",
            "barValue": 7
        }
    ],
    [
        {
            "xAxisTickValue": "10-1",
            "barValue": 6
        },
        {
            "xAxisTickValue": "10-2",
            "barValue": 8
        },
        {
            "xAxisTickValue": "10-3",
            "barValue": 10
        }
    ]
];

app.allLinesDatasets =
    {
        "points": [
            {
                "x": 1,
                "y": 10
            },
            {
                "x": 2,
                "y": 8
            },
            {
                "x": 3,
                "y": 14
            }
        ],
        "color": "blue"
    };
    
app.busStopsWaitTimes = {
    "1": {
        "days": {
            "we": {
                "10-1": [
                    17,
                    14,
                    14,
                    4,
                    8,
                    13,
                    11,
                    3,
                    2,
                    14,
                    14,
                    8,
                    9,
                    1,
                    9,
                    9,
                    9,
                    17,
                    1,
                    20
                ],
                "10-2": [
                    13,
                    12,
                    3,
                    5,
                    18,
                    14,
                    17,
                    5,
                    9,
                    12,
                    19,
                    3,
                    8,
                    9,
                    20,
                    3,
                    14,
                    5,
                    7,
                    13
                ],
                "10-3": [
                    18,
                    8,
                    8,
                    7,
                    10,
                    20,
                    16,
                    17,
                    6,
                    13,
                    5,
                    11,
                    11,
                    14,
                    18,
                    17,
                    11,
                    17,
                    4,
                    3
                ]
            }
        },
        "name": "Adderley"
    }
};

app.populateBusStopsWaitSelectionForm = function () {    
        let stopOptions = `<option value="">Select a stop</option>`;
        $.each(app.busStopsWaitTimes, function (idx, stop) {
            stopOptions += `<option value={"stopId":${idx}}>${stop.name}</option>`;
        });        
        $("#busStopAnalysis_Stops").html(stopOptions);
}

app.populateBusStopsWaitSelectionForm();

$("#busStopAnalysis_Stops").change(function() {
    let values = $("#busStopAnalysis_Stops").val();
    if (values !== "") {
        values = JSON.parse(values);
        let daysOptions = `<option value="">Select a day</option>`;
        if ("we" in app.busStopsWaitTimes[values.stopId].days) {
            daysOptions += `<option value={"dayKey":"we"}>Wednesday</option>`
        }
        $("#busStopAnalysis_Days").html(daysOptions);
    } else {
        $("#busStopAnalysis_Days").html("<option>Please select a route</option>");
    }                       
});

$("#drawBusStopAnalysisChart").on("click", function (evt) {
    evt.preventDefault();
    
    const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
    const dayInfo = JSON.parse($("#busStopAnalysis_Days").val());
    if (stopInfo !== "" || dayInfo !== "") {
        const allBarsDatasets = [];
        const allBarsDatasets2 = [[],[]]
        const allLinesdatasets = [];
        const linePoints = [];
        let i = 1;
        let demoValue = 1;
        $.each(app.busStopsWaitTimes[stopInfo.stopId]["days"][dayInfo.dayKey], function (idx, timeslot) {
            timeslot.sort(function (a,b) {
                return a - b;
            });
            
            let percentile25th = timeslot[parseInt(timeslot.length / 4)];
            let percentile50th = timeslot[parseInt(timeslot.length / 2)];
            let percentile75th = timeslot[parseInt((timeslot.length / 4) * 3)];
            let percentile100th = timeslot[timeslot.length - 1];
            
            allBarsDatasets.push({
                xAxisTickValue: idx,
                barValue: percentile100th
            });
            allBarsDatasets2[0].push({
                xAxisTickValue: idx,
                barValue: percentile25th
            });
            allBarsDatasets2[1].push({
                xAxisTickValue: idx,
                barValue: percentile75th - percentile25th
            });
            
            linePoints.push({x : i, y : (percentile75th - ((percentile75th - percentile25th) / 2))});
            demoValue = demoValue + 1;
            i++;
        });
        
        allLinesdatasets.push({points:linePoints,color:"blue"});

        app.drawBusStopAnalysisOneDayChart(allBarsDatasets, allBarsDatasets2, allLinesdatasets);
        
    }
});

app.drawBusStopAnalysisOneDayChart = function (allBarsDatasets, allBarsDatasets2, allLinesdatasets) {
        app.allLinesdatasets = allLinesdatasets;
    
    $("#busStopAnalysis_OneDayChart").html("");
    var barColor = '#384a60';
    
    // calculate total frequency by state for all segment.
    // var fD = app.allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});
    var fD = allBarsDatasets.map(function(d){return [d.xAxisTickValue,d.barValue];});

    var margin = {top: 20, right: 100, bottom: 30, left: 100},
        width = 960 - margin.left - margin.right,
        height = 500 - margin.top - margin.bottom;    
    

        var padding = 100;
            
        //create svg for histogram.
        var svg = d3.select("#busStopAnalysis_OneDayChart").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 function for x-axis mapping.
        var x = d3.scale.ordinal().rangeRoundBands([0, width], 0)
                .domain(fD.map(function(d) { return d[0]; }));

        // Add x-axis to the histogram svg.
        svg.append("g").attr("class", "x axis")
            .attr("transform", "translate(0," + height + ")")
            .call(d3.svg.axis()
                .scale(x)
                .orient("bottom")
                .innerTickSize(-height)
                .outerTickSize(0)
                .tickPadding(10));
        
        // create function for y-axis mapping.
        var yMin = 0;
        
        var yMax = d3.max(fD.map(function(d) { return d[1]; }));
        
        var y = d3.scale.linear().range([height, 0])
            .domain([0, d3.max(fD, function(d) { return d[1]; })]);
        
        var yScaleGridLines = d3.scale.linear()
            .domain([0, yMax])
            .range([height, 0]);
            
        var yAxisGridLines = d3.svg.axis()
            .scale(yScaleGridLines)
            .orient("left")
            .innerTickSize(-width)
            .outerTickSize(0)
            .tickPadding(10);

        svg.append("g")
          .attr("class", "y axis")
          .call(yAxisGridLines);
    
    // You would think d3 draws bar by bar but it draws level by level
    // therefore you need to create stacks which are sub-arrays whose contents
    // are arrays of elements at the same level
    // to achieve that
    // call stack,
    // call map and iterate over each array
    // call map iterate over all elements within an array while creating points based on values to visualize    
    var layers = d3.layout.stack() (
        allBarsDatasets2.map(
            function(barDataset) {
                return barDataset.map(
                    function(d) {
                        return {x: d.xAxisTickValue, y:d.barValue};
                    }
                    
                )
            }
        )
    );
    
    var layer = svg.selectAll(".layer")
        .data(layers)
        .enter().append("g")
        .attr("class", "layer")
        .style("fill", function(d, i) { 
            var x;
            if (i === 0) {
                x = "transparent";
            } else {
                x = "#686868";
            }
            return x;
         });
        
        
        
    layer.selectAll("rect")
        .data(function (d) { return d; })
        .enter().append("rect")
        .attr("x", function (d) { return x(d.x); })
        .attr("y", function (d) { return y(d.y + d.y0); })
        .attr("height", function (d) { return y(d.y0) - y(d.y + d.y0); })
        .attr("width", x.rangeBand() - 1)
        .on("mousemove", function (d) {
        
            var mouse = d3.mouse(this);
            // move the vertical line
            d3.select(".mouse-line")
              .attr("d", function() {
                var d = "M" + mouse[0] + "," + height;
                d += " " + mouse[0] + "," + 0;
                return d;
            });        
        });

    
    // Beginning of line things drawing   
    // Add min and max x and y borrowed from weird lines            
    var xMin = app.allLinesdatasets.reduce(function(pv,cv){
        var currentXMin = cv.points.reduce(function(pv,cv){
                return Math.min(pv,cv.x);
            },100)
                return Math.min(pv,currentXMin);
            },100);
    var xMax = app.allLinesdatasets.reduce(function(pv,cv){
        var currentXMax = cv.points.reduce(function(pv,cv){
                return Math.max(pv,cv.x);
            },0)
                return Math.max(pv,currentXMax);
            },0);
        
    var yScaleGridLines = d3.scale.linear()
        .domain([0, yMax])
        .range([height, 0]);
        
    var yAxisGridLines = d3.svg.axis()
        .scale(yScaleGridLines)
        .orient("left")
        .innerTickSize(-width)
        .outerTickSize(0)
        .tickPadding(10);
            
    var xScaleGridLines = {};
    
    xScaleGridLines = d3.scale.linear()
        .domain([xMin, xMax])
        .range([0, width]);

    var xAxisGridLines = d3.svg.axis()
            .scale(xScaleGridLines)
            .orient("bottom")
            .innerTickSize(-height)
            .outerTickSize(0)
            .tickPadding(10);           

    var lineGridLines = d3.svg.line()
        .interpolate('step-after')
        .x(function(d) { return xScaleGridLines(d.x); })
        .y(function(d) { return yScaleGridLines(d.y); });
      
    $.each(app.allLinesdatasets, function (idx, dataset) {         
        svg.append("path")
            .data([dataset.points])
            .attr("class", "line")
            .attr("d", lineGridLines)
            .style("stroke", function(){
                // return dataset.color;
                return "#FF9900";
            });
    });
    
}
&#13;
#busStopAnalysis_Charts .axis path,
            #busStopAnalysis_Charts .axis line{
                fill: none;
                stroke: black;
            }
            #busStopAnalysis_Charts .line{
                fill: none;
                stroke: blue;
                stroke-width: 2px;
            }
            #busStopAnalysis_Charts .tick text{
                font-size: 12px;
            }
            #busStopAnalysis_Charts .tick line{
                opacity: 0.2;
            }
            #busStopAnalysis_Charts #tooltip {   
                position: absolute;           
                text-align: center;
                color: white;
                padding: 10px 10px 10px 10px;
                display: inline-block;
                font: 12px sans-serif;        
                background-color: #384a60;
                border: 3px solid #2f3e50;
                -webkit-border-radius: 30px;
                -moz-border-radius: 30px;
                border-radius: 30px;
                -webkit-box-shadow: 2px 2px 4px #888;
                -moz-box-shadow: 2px 2px 4px #888;
                box-shadow: 2px 2px 4px #888;
            }
            #busStopAnalysis_Charts #tooltip.hidden {
                display: none;
            }
            #busStopAnalysis_Charts #tooltip p {
                margin: 0;
                font-family: sans-serif;
                font-size: 16px;
                line-height: 20px;
            }
&#13;
<div id="busStopAnalysisChartArea_Form">
            <div id="busStopAnalysisChartArea_Form_TableRow">
            <div id="busStopAnalysisChartArea_Form_Stop">
              <label for="family" class="control-label"></label>
              <select class="form-control dataset-column" style="width:auto;" id="busStopAnalysis_Stops"></select>
            </div>
            <div id="busStopAnalysisChartArea_Form_Days">
              <label for="family" class="control-label"></label>
                <div>
                <select class="form-control dataset-column" style="width:auto;float:left;" id="busStopAnalysis_Days"></select>
                <a href="" id="drawBusStopAnalysisChart" title="Draw the chart">draw the chart</a>
              </div>
            </div>
            </div>
            </div>
            <div id="busStopAnalysis_Charts">
            <div id="busStopAnalysis_OneDayChart"></div>
            <div id="busStopAnalysis_AllDaysChart"></div>
            <div>
            <script src="https://d3js.org/d3.v3.min.js"></script>
            <script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
&#13;
&#13;
&#13;

1 个答案:

答案 0 :(得分:1)

尝试改变:

var lineGridLines = d3.svg.line()
    .interpolate('step-after')
    .x(function(d) { return xScaleGridLines(d.x); })
    .y(function(d) { return yScaleGridLines(d.y); });

到:

 var lineGridLines = d3.svg.line()
        .interpolate('step-after')
        .x(function(d) { return xScaleGridLines(d.x) - x.rangeBand()/2; })
        .y(function(d) { return yScaleGridLines(d.y); });