Skip to content

Instantly share code, notes, and snippets.

@lloydroc
Last active March 19, 2018 04:40
Show Gist options
  • Select an option

  • Save lloydroc/c615008f180ca98ff417d40f6bd4cc08 to your computer and use it in GitHub Desktop.

Select an option

Save lloydroc/c615008f180ca98ff417d40f6bd4cc08 to your computer and use it in GitHub Desktop.

Revisions

  1. lloydroc revised this gist Mar 19, 2018. 1 changed file with 132 additions and 22 deletions.
    154 changes: 132 additions & 22 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -11,23 +11,19 @@
    }
    .peak-line-label {
    font-size: 8pt;
    stroke: red;
    color: red;
    }
    .paths {
    fill: none;
    stroke-width: 2;
    stroke: blue;
    }
    .paths-selected {
    fill: none;
    stroke-width: 4;
    stroke: blue;
    }
    .points {
    fill: blue;
    }
    .point-selected {
    fill: blue;
    }
    .point-callout {
    fill: grey;
    @@ -41,37 +37,73 @@
    <svg width="900" height="300" id="timeseries"></svg>
    </body>
    <script src="https://d3js.org/d3.v5.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.21.0/moment.min.js"></script>
    <script>
    var values = [],
    endTime = Date.now(),
    startTime = endTime - 24*30*60*60*1000,
    n = 50;
    step = (endTime-startTime)/n;
    for(var i=0;i<n;i++) {
    values.push({ t: i*step+startTime, y: Math.random()*120});
    var avg = [],
    peak = [],
    endTime = moment().valueOf(),
    startTime = moment().subtract(1,'months').valueOf(),
    n = 31*2,
    step = (endTime-startTime)/n,
    standardAvgWorkDay = gaussian(60,5),
    standardAvgWeekendDay = gaussian(10,1),
    colors = d3.scaleOrdinal(d3.schemeSet1);
    var svg = d3.select("#timeseries"),
    margin = {top: 20, right: 20, bottom: 20, left: 20},
    width = svg.attr('width')-margin.left-margin.right,
    height = svg.attr('height')-margin.top-margin.bottom;

    var g,x,y;

    function simulateData() {
    for(var i=0;i<n;i++) {
    var time = moment(i*step+startTime);
    console.log(time.day());
    if(time.day() == 6 || time.day() == 7) {
    avgPoint = { t: time.valueOf(), y: standardAvgWeekendDay()}
    peak.push({ t: time.valueOf(), y: avgPoint.y+Math.random()*2});
    } else {
    avgPoint = { t: time.valueOf(), y: standardAvgWorkDay()}
    peak.push({ t: time.valueOf(), y: avgPoint.y+Math.random()*85});
    }
    avg.push(avgPoint);
    }
    }
    drawSvg(startTime,endTime,values);

    function drawSvg(startTime,endTime,data) {
    var svg = d3.select("#timeseries"),
    margin = {top: 20, right: 20, bottom: 20, left: 20},
    width = svg.attr('width')-margin.left-margin.right,
    height = svg.attr('height')-margin.top-margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
    simulateData();
    drawChart();
    drawSeries(peak,colors(6),0,'Peak');
    drawSeries(avg,colors(2),1,'Avg');

    var x = d3.scaleTime()
    function drawChart() {
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    x = d3.scaleTime()
    .domain([startTime, endTime])
    .range([0, width])
    .nice();

    var y = d3.scaleTime()
    y = d3.scaleLinear()
    .domain([0, 150])
    .range([height,0])
    .nice();

    var labels = g.append('g');

    labels.append('text')
    .text('Noise Exposure [dB]')
    .attr('x',width/2-30)
    .attr('y',0)
    .classed('chart-labels',true);

    g.append("g")
    .attr("class", "axis axis--y")
    .attr("transform", "translate(5,0)")
    .call(d3.axisLeft(y));

    g.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + y(0) + ")")
    .attr("transform", "translate(5," + y(0) + ")")
    .call(d3.axisBottom(x));

    var line = d3.line()
    @@ -84,6 +116,31 @@
    .y(function(d) { return y(0); })
    .curve(d3.curveCatmullRom.alpha(0.5));

    var t = d3.transition()
    .duration(2000)
    .ease(d3.easeElasticOut);

    var peakLineData = [{t: startTime, y: 140},{t: endTime, y: 140}];
    var peakLine = g.append('g').selectAll('peakLine').data(peakLineData);
    peakLine.enter().append('path').classed('peak-line',true).attr('d',line(peakLineData));
    var peakLineText = g.append('text')
    .text('Peak Sound Pressure 140dB')
    .attr('x',x(endTime)-120)
    .attr('y',y(142))
    .classed('peak-line-label',true);
    }

    function drawSeries(data,color,legendIndex,legendLabel) {
    var line = d3.line()
    .x(function(d) { return x(d.t); })
    .y(function(d) { return y(d.y); })
    .curve(d3.curveCatmullRom.alpha(0.5));

    var lineZero = d3.line()
    .x(function(d) { return x(d.t); })
    .y(function(d) { return y(0); })
    .curve(d3.curveCatmullRom.alpha(0.5));

    var t = d3.transition()
    .duration(2000)
    .ease(d3.easeElasticOut);
    @@ -95,9 +152,11 @@
    .attr('x',x(endTime)-120)
    .attr('y',y(142))
    .classed('peak-line-label',true);

    var paths = g.append('g').selectAll('paths').data(data);
    paths.enter().append('path')
    .classed('paths',true)
    .attr('stroke',color)
    .on('mouseover', function(d) {
    d3.select(this).classed('paths-selected',true);
    var thisColor = d3.select(this).attr('stroke');
    @@ -115,6 +174,7 @@
    .attr('cx',function(d) { return x(d.t)})
    .attr('cy',function(d) { return y(0)})
    .attr('class','points')
    .attr('fill',color)
    .on('mouseover', function(d) {
    d3.select(this).attr('class','point-selected').attr('r',6);
    var thisColor = d3.select(this).attr('stroke');
    @@ -169,6 +229,56 @@
    .transition(t)
    .attr('cx',function(d) { return x(d.t)})
    .attr('cy',function(d) { return y(d.y)})

    var legend = g.append('g');
    var legendRect = legend.selectAll('legendRect').data([{x: 10+legendIndex*75, y: 0}])
    legendRect.enter()
    .append('rect')
    .attr('width',20)
    .attr('height',10)
    .attr('x',function(d) { return d.x})
    .attr('y',function(d) { return d.y})
    .attr('rx',3)
    .attr('ry',3)
    .attr('fill',color)
    var legendText = legend.selectAll('legendText').data([{x: 32+legendIndex*75, y: 10}])
    legendText.enter()
    .append('text')
    .attr('x',function(d) { return d.x})
    .attr('y',function(d) { return d.y})
    .style('color',color)
    .text(legendLabel)
    .classed('legend-text',true)
    }

    // returns a gaussian random function with the given mean and stdev.
    function gaussian(mean, stdev) {
    var y2;
    var use_last = false;
    return function() {
    var y1;
    if(use_last) {
    y1 = y2;
    use_last = false;
    }
    else {
    var x1, x2, w;
    do {
    x1 = 2.0 * Math.random() - 1.0;
    x2 = 2.0 * Math.random() - 1.0;
    w = x1 * x1 + x2 * x2;
    } while( w >= 1.0);
    w = Math.sqrt((-2.0 * Math.log(w))/w);
    y1 = x1 * w;
    y2 = x2 * w;
    use_last = true;
    }

    var retval = mean + stdev * y1;
    if(retval > 0)
    return retval;
    return -retval;
    }
    }

    </script>
  2. lloydroc created this gist Mar 17, 2018.
    175 changes: 175 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,175 @@
    <html>
    <head>
    <meta charset="utf-8">
    </head>
    <style>
    .peak-line {
    fill: none;
    stroke-width: 1;
    stroke: red;
    stroke-dasharray: 2,2;
    }
    .peak-line-label {
    font-size: 8pt;
    stroke: red;
    }
    .paths {
    fill: none;
    stroke-width: 2;
    stroke: blue;
    }
    .paths-selected {
    fill: none;
    stroke-width: 4;
    stroke: blue;
    }
    .points {
    fill: blue;
    }
    .point-selected {
    fill: blue;
    }
    .point-callout {
    fill: grey;
    }
    .text-callout {
    font-size: 8pt;
    }

    </style>
    <body>
    <svg width="900" height="300" id="timeseries"></svg>
    </body>
    <script src="https://d3js.org/d3.v5.js"></script>
    <script>
    var values = [],
    endTime = Date.now(),
    startTime = endTime - 24*30*60*60*1000,
    n = 50;
    step = (endTime-startTime)/n;
    for(var i=0;i<n;i++) {
    values.push({ t: i*step+startTime, y: Math.random()*120});
    }
    drawSvg(startTime,endTime,values);

    function drawSvg(startTime,endTime,data) {
    var svg = d3.select("#timeseries"),
    margin = {top: 20, right: 20, bottom: 20, left: 20},
    width = svg.attr('width')-margin.left-margin.right,
    height = svg.attr('height')-margin.top-margin.bottom,
    g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    var x = d3.scaleTime()
    .domain([startTime, endTime])
    .range([0, width])
    .nice();

    var y = d3.scaleTime()
    .domain([0, 150])
    .range([height,0])
    .nice();

    g.append("g")
    .attr("class", "axis axis--x")
    .attr("transform", "translate(0," + y(0) + ")")
    .call(d3.axisBottom(x));

    var line = d3.line()
    .x(function(d) { return x(d.t); })
    .y(function(d) { return y(d.y); })
    .curve(d3.curveCatmullRom.alpha(0.5));

    var lineZero = d3.line()
    .x(function(d) { return x(d.t); })
    .y(function(d) { return y(0); })
    .curve(d3.curveCatmullRom.alpha(0.5));

    var t = d3.transition()
    .duration(2000)
    .ease(d3.easeElasticOut);
    var peakLineData = [{t: startTime, y: 140},{t: endTime, y: 140}];
    var peakLine = g.append('g').selectAll('peakLine').data(peakLineData);
    peakLine.enter().append('path').classed('peak-line',true).attr('d',line(peakLineData));
    var peakLineText = g.append('text')
    .text('Peak Sound Pressure 140dB')
    .attr('x',x(endTime)-120)
    .attr('y',y(142))
    .classed('peak-line-label',true);
    var paths = g.append('g').selectAll('paths').data(data);
    paths.enter().append('path')
    .classed('paths',true)
    .on('mouseover', function(d) {
    d3.select(this).classed('paths-selected',true);
    var thisColor = d3.select(this).attr('stroke');
    })
    .on('mouseout', function(d) {
    d3.select(this).classed('paths',true);
    })
    .attr('d',lineZero(data))
    .transition(t)
    .attr('d',line(data))

    var points = g.append('g').selectAll('points').data(data);
    points.enter().append('circle')
    .attr('r',4)
    .attr('cx',function(d) { return x(d.t)})
    .attr('cy',function(d) { return y(0)})
    .attr('class','points')
    .on('mouseover', function(d) {
    d3.select(this).attr('class','point-selected').attr('r',6);
    var thisColor = d3.select(this).attr('stroke');
    var callout = g.append('g').classed('callout',true)

    function smartLabelX(d) {
    var xv = x(d.t)-110;
    if(xv < 0) return 0;
    if(xv+220>x(endTime)) return x(endTime)-220;
    return xv;
    }

    var calloutRect = callout.selectAll('point-callout').data([d])
    calloutRect.enter()
    .append('rect')
    .attr('width',220)
    .attr('height',50)
    .attr('x',smartLabelX)
    .attr('y',function(d) { return y(d.y)-60})
    .attr('rx',5)
    .attr('ry',5)
    .classed('point-callout',true);
    var triData = [
    {x: x(d.t)-9, y: y(d.y)-10},
    {x: x(d.t)+0, y: y(d.y)},
    {x: x(d.t)+9, y: y(d.y)-10}
    ];
    var triLine = d3.line()
    .x(function(d) { ;return d.x; })
    .y(function(d) { return d.y; });
    var calloutTri = callout.selectAll('point-callout-tri').data(triData)
    calloutTri.enter()
    .append('path')
    .attr('d',triLine(triData))
    .classed('point-callout',true);

    function createCalloutText(d,i) {
    return i==0 ? new Date(d.t) : parseFloat(d.y).toFixed(1)+'dB';
    }

    var calloutText = callout.selectAll('text-callout').data([d,d]).enter()
    .append('text')
    .attr("x",function(d,i) { return i==0 ? smartLabelX(d)+10 : Math.min(x(d.t)-20,x(endTime)-100)})
    .attr("y",function(d,i) { return i==0 ? y(d.y)-40 : y(d.y) - 20})
    .text(createCalloutText)
    .classed('text-callout',function(d,i) { return i==0 ? true : false})
    })
    .on('mouseout', function(d) {
    d3.select(this).attr('class','points').attr('r',4);
    g.selectAll('.callout').remove();
    })
    .transition(t)
    .attr('cx',function(d) { return x(d.t)})
    .attr('cy',function(d) { return y(d.y)})
    }

    </script>
    </html>