d3js多个堆积条形图线条光标出现在错误的图表中

时间:2017-04-18 15:34:32

标签: javascript d3.js bar-chart stacked-chart stacked-area-chart

我正在创建两个相关的堆积条形图。两个图表都必须包含一个行光标。顶部图表的光标工作正常,但底部图表中没有显示行光标。更糟糕的是,当我将鼠标放在底部图表上方时,顶部图表中的线条光标会移动。什么需要修复a线条光标出现在底部图表中,是否不影响顶部图表或与顶部图表中的图表同步?

注意:在我的代码中说我不尊重“不要重复自己的原则”之前,请注意肯特贝克说:“首先,让它工作,然后做对,最后让它快速工作” 。另外,请随意跳过数据创建部分。 这是我的代码:

            
   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;
        $.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)];
            
            allBarsDatasets.push({
                xAxisTickValue: idx,
                barValue: Percentile75th
            });
            allBarsDatasets2[0].push({
                xAxisTickValue: idx,
                barValue: Percentile25th
            });
            allBarsDatasets2[1].push({
                xAxisTickValue: idx,
                barValue: Percentile75th - Percentile25th
            });
            
            linePoints.push({x : i, y : Percentile50th});
            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.1, 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 z = d3.scale.category10();

    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 = z(i);
                x = "#686868";
            }
            return x;
         });
        
    var mouseG = d3.select("#busStopAnalysis_Charts").append("g")
        .attr("class", "mouse-over-effects");

    // this is the vertical line
    svg.append("path")
        .attr("class", "mouse-line")
        .style("stroke", "black")
        .style("stroke-width", "1px")
        .style("opacity", "1");
        
    var tooltip = d3.select("#busStopAnalysis_Charts")
        .append('div')
        .attr('id', 'tooltip');
        
    $("#tooltip").css('display', 'none');
        
    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;
            });        
        })
        .on("mouseover", function (d) {
            var mouse = d3.mouse(this);
            
            console.log("first chart");
            console.log(mouse);
            
            d3.select("#tooltip")
                .style("left", mouse[0] + "px")
                .style("top", mouse[1] + "px")
                .style("width", "auto")
                .style("height", "auto")
                .html("Day: " + $("#busStopAnalysis_Days option:selected").text() + "<br>Time Range: " + d.x + "<br>Avg Wait: " + d.y);

            $("#tooltip").css("display", "");
            
            app.drawAllDaysStopAnalysisChart(d);       

        })
        .on("mouseout", function() {
            $("#tooltip").css("display", "none");
        })
        .on("click", function (d) {
            app.drawAllDaysStopAnalysisChart(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 yMin = app.allLinesdatasets.reduce(function(pv,cv){
        var currentYMin = cv.points.reduce(function(pv,cv){
                return Math.min(pv,cv.y);
            },100)
                return Math.min(pv,currentYMin);
            },100);
    var yMax = app.allLinesdatasets.reduce(function(pv,cv){
        var currentYMax = cv.points.reduce(function(pv,cv){
                return Math.max(pv,cv.y);
            },0)
                return Math.max(pv,currentYMax);
            },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";
            });
    });
    
}

/********************************************************************
* Append and Draw Second Chart
********************************************************************/
app.drawAllDaysStopAnalysisChart = function drawAllDateStopAnalysis (timeRange) {

        const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
        
        const allBarsDatasets = [];
        const allBarsDatasets2 = [[],[]]
        const allLinesdatasets = [];
        const linePoints = [];
        if (stopInfo !== "" || dayInfo !== "") {
            let i = 1;
            $.each(app.busStopsWaitTimes[stopInfo.stopId]["days"], function (idx, day) {
                const stopRangeWaitTimeInfo = day[timeRange.x];
                stopRangeWaitTimeInfo.sort(function (a,b) {
                    return a - b;
                });
                
                let Percentile25th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 4)];
                let Percentile50th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 2)];
                let Percentile75th = stopRangeWaitTimeInfo[parseInt((stopRangeWaitTimeInfo.length / 4) * 3)];
                
                allBarsDatasets.push({
                    xAxisTickValue: idx,
                    barValue: Percentile75th
                });
                allBarsDatasets2[0].push({
                    xAxisTickValue: idx,
                    barValue: Percentile25th
                });
                allBarsDatasets2[1].push({
                    xAxisTickValue: idx,
                    barValue: Percentile75th - Percentile25th
                });
                
                linePoints.push({x : i, y : Percentile50th});
                i++;
            });
            
            allLinesdatasets.push({points:linePoints,color:"orange"});
            
        }
    
    
    $("#busStopAnalysis_AllDaysChart").html("");
    var barColor = '#384a60';
    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_AllDaysChart").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.1, 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([yMin, 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);
  
    var layers = d3.layout.stack() (
        allBarsDatasets2.map(
            function(barDataset) {
                return barDataset.map(
                    function(d) {
                        return {x: d.xAxisTickValue, y:d.barValue};
                    }
                    
                )
            }
        )
    );
    
    var z = d3.scale.category10();

    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 = "#FF9900";
            }
            return x;
         });
         
    // append a g for all the mouse over nonsense
    var mouseG = svg.append("g")
        .attr("class", "mouse-over-effects");

    // this is the vertical line
    svg.append("path")
        .attr("class", "mouse-line")
        .style("stroke", "grey")
        .style("stroke-width", "1px")
        .style("opacity", "1");
        
    var tooltip = d3.select("#tooltip");
        
    $("#tooltip").css('display', 'none');
        
    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;
            });        
        })
        .on("mouseover", function (d) {
            var mouse = d3.mouse(this);
            console.log("second chart");
            console.log(mouse);
            
            tooltip.style("left", mouse[0] + "px")
                .style("top", mouse[1] + "px")
                .style("width", "auto")
                .style("height", "auto")
                .html("Day: " + $("#busStopAnalysis_Days option:selected").text() + "<br>Time Range: " + d.x + "<br>Avg Wait: " + d.y);

            $("#tooltip").css("display", "");

        })
        .on("mouseout", function() {
            $("#tooltip").css("display", "none");
        });
    
    // 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 = 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 yMin = allLinesdatasets.reduce(function(pv,cv){
        var currentYMin = cv.points.reduce(function(pv,cv){
                return Math.min(pv,cv.y);
            },100)
                return Math.min(pv,currentYMin);
            },100);
    var yMax = allLinesdatasets.reduce(function(pv,cv){
        var currentYMax = cv.points.reduce(function(pv,cv){
                return Math.max(pv,cv.y);
            },0)
                return Math.max(pv,currentYMax);
            },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 "#FF3300";
            });
    });
    
}
            
            
            
            #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 个答案:

答案 0 :(得分:0)

问题是我将行光标作为具有相同名称的类附加到两个图表。我想d3很难确定我试图制作哪一个动画,所以它总是选择第一个。 修复是用ID替换类,如下面的代码片段所示。

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;
        $.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)];
            
            allBarsDatasets.push({
                xAxisTickValue: idx,
                barValue: Percentile75th
            });
            allBarsDatasets2[0].push({
                xAxisTickValue: idx,
                barValue: Percentile25th
            });
            allBarsDatasets2[1].push({
                xAxisTickValue: idx,
                barValue: Percentile75th - Percentile25th
            });
            
            linePoints.push({x : i, y : Percentile50th});
            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.1, 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 z = d3.scale.category10();

    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;
         });
        
    var mouseG = d3.select("#busStopAnalysis_Charts").append("g")
        .attr("id", "mouse-over-effects-one-day-chart");

    const lineCursorId = "mouse-line-one-day-chart";
    // this is the vertical line
    svg.append("path")
        .attr("id", lineCursorId)
        .style("stroke", "black")
        .style("stroke-width", "1px")
        .style("opacity", "1");
        
        
    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);
            app.drawStopAnalysisLineCursor(mouse, "#" + lineCursorId, height);     
        })
        .on("click", function (d) {
            app.drawAllDaysStopAnalysisChart(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 yMin = app.allLinesdatasets.reduce(function(pv,cv){
        var currentYMin = cv.points.reduce(function(pv,cv){
                return Math.min(pv,cv.y);
            },100)
                return Math.min(pv,currentYMin);
            },100);
    var yMax = app.allLinesdatasets.reduce(function(pv,cv){
        var currentYMax = cv.points.reduce(function(pv,cv){
                return Math.max(pv,cv.y);
            },0)
                return Math.max(pv,currentYMax);
            },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";
            });
    });
    
}

/********************************************************************
* Append and Draw Second Chart
********************************************************************/
app.drawAllDaysStopAnalysisChart = function drawAllDateStopAnalysis (timeRange) {

        const stopInfo = JSON.parse($("#busStopAnalysis_Stops").val());
        
        const allBarsDatasets = [];
        const allBarsDatasets2 = [[],[]]
        const allLinesdatasets = [];
        const linePoints = [];
        if (stopInfo !== "" || dayInfo !== "") {
            let i = 1;
            $.each(app.busStopsWaitTimes[stopInfo.stopId]["days"], function (idx, day) {
                const stopRangeWaitTimeInfo = day[timeRange.x];
                stopRangeWaitTimeInfo.sort(function (a,b) {
                    return a - b;
                });
                
                let Percentile25th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 4)];
                let Percentile50th = stopRangeWaitTimeInfo[parseInt(stopRangeWaitTimeInfo.length / 2)];
                let Percentile75th = stopRangeWaitTimeInfo[parseInt((stopRangeWaitTimeInfo.length / 4) * 3)];
                
                allBarsDatasets.push({
                    xAxisTickValue: idx,
                    barValue: Percentile75th
                });
                allBarsDatasets2[0].push({
                    xAxisTickValue: idx,
                    barValue: Percentile25th
                });
                allBarsDatasets2[1].push({
                    xAxisTickValue: idx,
                    barValue: Percentile75th - Percentile25th
                });
                
                linePoints.push({x : i, y : Percentile50th});
                i++;
            });
            
            allLinesdatasets.push({points:linePoints,color:"orange"});
            
        }
    
    
    $("#busStopAnalysis_AllDaysChart").html("");
    var barColor = '#384a60';
    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_AllDaysChart").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.1, 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([yMin, 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);
  
    var layers = d3.layout.stack() (
        allBarsDatasets2.map(
            function(barDataset) {
                return barDataset.map(
                    function(d) {
                        return {x: d.xAxisTickValue, y:d.barValue};
                    }
                    
                )
            }
        )
    );
    
    var z = d3.scale.category10();

    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 = "#FF9900";
            }
            return x;
         });
         
    // append a g for all the mouse over nonsense
    var mouseG = svg.append("g")
        .attr("id", "mouse-over-effects-all-day-chart");

    let lineCursorId = "mouse-line-all-day-chart"; 
    // this is the vertical line
    svg.append("path")
        .attr("id", lineCursorId)
        .style("stroke", "grey")
        .style("stroke-width", "1px")
        .style("opacity", "1");
        
    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);
          app.drawStopAnalysisLineCursor(mouse, "#" + lineCursorId, height)       
        });
    
    // 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 = 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 yMin = allLinesdatasets.reduce(function(pv,cv){
        var currentYMin = cv.points.reduce(function(pv,cv){
                return Math.min(pv,cv.y);
            },100)
                return Math.min(pv,currentYMin);
            },100);
    var yMax = allLinesdatasets.reduce(function(pv,cv){
        var currentYMax = cv.points.reduce(function(pv,cv){
                return Math.max(pv,cv.y);
            },0)
                return Math.max(pv,currentYMax);
            },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 "#FF3300";
            });
    });
    
}

app.drawStopAnalysisLineCursor = function (mouse, mouseLineId, height) {
    // move the vertical line
    d3.select(mouseLineId)
      .attr("d", function() {
        var d = "M" + (mouse[0] -2) + "," + height;
        d += " " + (mouse[0] - 2) + "," + 0;
        return d;
    }); 
}
#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;
}
<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>