2017-04-19 99 views
0

我已经创建了合并条形图和直线的图表。在折线图X轴的定义中,我需要更改哪些线条与条形图中的条形完美对齐,这意味着线条必须从水平走向垂直,反之亦然。另一个开始。D3.js组合条形图和折线图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"; 
 
      }); 
 
    }); 
 
    
 
}
#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; 
 
      }
<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>

回答

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); });