Skip to content

Instantly share code, notes, and snippets.

@LemoNode
Last active May 21, 2020 13:02
Show Gist options
  • Save LemoNode/a9dc1a454fdc80ff2a738a9990935e9d to your computer and use it in GitHub Desktop.
Save LemoNode/a9dc1a454fdc80ff2a738a9990935e9d to your computer and use it in GitHub Desktop.

Revisions

  1. LemoNode revised this gist Nov 16, 2018. 1 changed file with 0 additions and 9 deletions.
    9 changes: 0 additions & 9 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,9 +0,0 @@
    This is a multi line chart.

    #### Added Features:

    1. [d3.bisector](https://github.com/d3/d3-array/blob/master/README.md#bisector)
    2. Legend
    3. Average temperature

    All data is visualized through a csv file.
  2. LemoNode revised this gist Nov 16, 2018. 1 changed file with 194 additions and 293 deletions.
    487 changes: 194 additions & 293 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,324 +1,225 @@
    <!DOCTYPE html>
    <head>
    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/d3.v5.min.js"></script>
    <style>

    body {
    margin:auto;
    width: 885px;
    font: 10px arial;
    padding: 25px;
    margin:auto;
    width: 885px;
    font: 10px arial;
    padding: 25px;
    }
    .axis--y line {
    opacity:.2;
    .y-axis line {
    opacity:.2;
    }
    .axis--y path {
    display:none;
    .y-axis path {
    display:none;
    }
    .line {
    fill: none;
    stroke-width: 1.5px;
    opacity:.75;
    fill: none;
    stroke-width: 1.5px;
    opacity:.75;
    }
    .overlay {
    fill: none;
    pointer-events: all;
    fill: none;
    pointer-events: all;
    }
    .lineHoverText {
    text-shadow:
    -2px -2px 0 #fff,
    2px -2px 0 #fff,
    -2px 2px 0 #fff,
    2px 2px 0 #fff;
    text-shadow:
    -2px -2px 0 #fff,
    2px -2px 0 #fff,
    -2px 2px 0 #fff,
    2px 2px 0 #fff;
    }
    .hoverCircle {
    opacity:.75;
    opacity: .75;
    }

    </style>
    </head>

    <body>

    <!-- Selectbox -->
    <font style="font-size:11px;">Change metric:</font>
    <select id="selectbox">
    <option selected value="_1">Fahrenheit</option>
    <option value="_2">Celsius</option>
    <option value="_1">Fahrenheit</option>
    <option value="_2">Celsius</option>
    </select>

    <!-- Chart -->
    <div id="chart"></div>
    <svg id="chart" width="850" height="410"></svg>

    <script>

    // Init
    var parseTime = d3.timeParse("%Y%m%d"),
    formatDate = d3.timeFormat("%Y-%m-%d"),
    bisectDate = d3.bisector(function(d) { return d.date; }).left,
    formatValue = d3.format(",.0f"),
    formatMetric = function(d) { return "º" + formatValue(d); };

    // Chart measurements
    var margin = {top: 20, right: 165, bottom: 40, left: 45},
    width = 850 - margin.left - margin.right,
    height = 410 - margin.top - margin.bottom;

    var x = d3.scaleTime().range([0, width]),
    y = d3.scaleLinear().range([height, 0]),
    z = d3.scaleOrdinal(d3.schemeCategory10);

    var xAxis = d3.axisBottom(x)
    .tickFormat(d3.timeFormat("%B"));

    var yAxis = d3.axisLeft(y)
    .tickSize(-width);

    var line = d3.line()
    .curve(d3.curveCardinal)
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.temperature); });

    var g = d3.select("#chart").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 + ")");

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

    let legend = g.append("g")
    .attr("text-anchor", "start")
    .selectAll("g")
    .data([["New York"],["San Francisco"],["Austin"]])
    .enter().append("g")
    .attr("transform", function(d, i) { return "translate(0," + i * 60 + ")"; });

    legend.append("rect")
    .attr("x", width + 30)
    .attr("width", 12)
    .attr("height", 19)
    .attr("fill", z);

    legend.append("text")
    .attr("x", width + 50)
    .attr("font-size", 11)
    .attr("y", 9.5)
    .attr("dy", "0.32em")
    .text(function(d) { return d; });

    var durations = 0;

    let afterLoad = () => durations = 750;

    d3.select("#selectbox").on('change', update); // Change category

    var METRIC;

    update();

    function update() {

    METRIC = d3.select('#selectbox').property('value');

    d3.csv("data.csv", function(d, _, columns) {
    d.date = parseTime(d.date);
    for (var i = 1, n = columns.length, c; i < n; ++i)
    d[c = columns[i]] = +d[c];
    return d;
    }, function(error, data) {
    if (error) throw error;

    var avgCopy = []

    // Average Temperatures
    var avgNY = d3.sum(data, function(d) {
    return d["New York" + METRIC];
    }) / data.length;
    var avgAU = d3.sum(data, function(d) {
    return d["Austin" + METRIC];
    }) / data.length;
    var avgSF = d3.sum(data, function(d) {
    return d["San Francisco" + METRIC];
    }) / data.length;

    avgCopy.push(avgNY, avgAU, avgSF)

    console.log(avgCopy)

    let keys = data.columns.slice(1, 4);

    let copy = [];

    keys.forEach(function(t) {
    t = t.slice(0, -2) // Slice last two letters
    copy.push(t) // Push sliced strings into copy array
    });

    var cities = copy.map(function(id) {
    return {
    id: id,
    values: data.map(function(d) {
    return {date: d.date, temperature: d[id+METRIC]};
    })
    };
    });

    x.domain(d3.extent(data, function(d) { return d.date; }));

    y.domain([
    d3.min(cities, function(c) {
    return d3.min(c.values, function(d) {
    return d.temperature;
    });
    }),
    d3.max(cities, function(c) {
    return d3.max(c.values, function(d) {
    return d.temperature;
    });
    })
    ]);

    z.domain(cities.map(function(c) { return c.id; }));

    g.selectAll(".axis.axis--y").transition()
    .duration(durations)
    .call(yAxis);

    g.selectAll(".axis.axis--x").transition()
    .duration(durations)
    .call(xAxis);

    // ============================
    // ========= Line Path ========
    // ============================

    var city = g.selectAll(".cities")
    .data(cities);

    city = city
    .enter()
    .append("path")
    .attr("class", "line cities")
    .style("stroke", function(d) { return z(d.id); })
    .merge(city);

    city.transition()
    .duration(durations)
    .attr("d", function(d) { return line(d.values); })

    // ============================
    // ======== Focus Line ========
    // ============================

    var focus = g.append("g")
    .attr("class", "focus")
    .style("display", "none");

    focus.append("line").attr("class", "lineHover")
    .style("stroke", "#999")
    .attr("stroke-width",1)
    .style("shape-rendering", "crispEdges")
    .style("opacity", 0.5)
    .attr("y1",-height)
    .attr("y2",0);

    focus.append("text").attr("class", "lineHoverDate")
    .attr("text-anchor","middle")
    .attr("font-size",12);

    var labels = focus.selectAll(null)
    .data(copy)
    .enter()
    .append("text")
    .attr("class", "lineHoverText")
    .style("fill", function(d) {return z(d);})
    .attr("font-size",12)
    .attr("dy", function(_,i){
    return 1 + i * 2 + "em"
    });

    var circles = focus.selectAll(null)
    .data(copy)
    .enter()
    .append("circle")
    .style("fill", function(d) {return z(d);})
    .attr("class", "hoverCircle")
    .attr("r", 2.5);

    g.append("rect")
    .attr("class", "overlay")
    .attr("width", width -1) // -1 added
    .attr("height", height)
    .on("mouseover", function() { focus.style("display", null); })
    .on("mouseout", function() { focus.style("display", "none"); })
    .on("mousemove", mousemove);

    function mousemove() {
    var x0 = x.invert(d3.mouse(this)[0]),
    i = bisectDate(data, x0, 1),
    d0 = data[i - 1],
    d1 = data[i],
    d = x0 - d0.date > d1.date - x0 ? d1 : d0;

    focus.select(".lineHover")
    .attr("transform", "translate(" + x(d.date) + "," + height + ")");

    focus.select(".lineHoverDate")
    .attr("transform",
    "translate(" + x(d.date) + "," + (height + margin.bottom) + ")")
    .text(formatDate(d.date));

    circles
    .attr("cy", function(e) {
    return y(d[e + METRIC])
    })
    .attr("cx", x(d.date) );

    labels.attr("transform", "translate(" + (x(d.date) + 15) + "," + height/2.5 + ")")
    .text(function(e) {
    return e + " " + formatMetric(d[e + METRIC])
    });

    // Horizontal text position shift
    if (x(d.date) > width - width/4) {
    focus.selectAll("text.lineHoverText")
    .attr("x", -130);
    } else {
    focus.selectAll("text.lineHoverText")
    .attr("x", 15);
    }
    }

    // ============================
    // ========= Average ==========
    // ============================

    var avg = g.selectAll(".avg")
    .data(avgCopy);
    avg = avg
    .enter()
    .append("text")
    .attr("class", "avg")
    .attr("transform", function(d, i) {
    return "translate(0," + i * 60 + ")";
    })
    .attr("x",width + 50)
    .attr("y",40)
    .attr("font-weight","bold")
    .attr("font-size",12)
    .merge(avg);

    avg.transition()
    .duration(durations)
    .text( function(d) { return "Avg. " + formatMetric(d); })

    afterLoad(); // Change transition duration

    })
    d3.csv("data.csv").then(d => chart(d))

    function chart(data) {

    var keys = data.columns.slice(1);

    var parseTime = d3.timeParse("%Y%m%d"),
    formatDate = d3.timeFormat("%Y-%m-%d"),
    bisectDate = d3.bisector(d => d.date).left,
    formatValue = d3.format(",.0f");

    data.forEach(function(d) {
    d.date = parseTime(d.date);
    return d;
    })

    var svg = d3.select("#chart"),
    margin = {top: 15, right: 35, bottom: 15, left: 35},
    width = +svg.attr("width") - margin.left - margin.right,
    height = +svg.attr("height") - margin.top - margin.bottom;

    var x = d3.scaleTime()
    .rangeRound([margin.left, width - margin.right])
    .domain(d3.extent(data, d => d.date))

    var y = d3.scaleLinear()
    .rangeRound([height - margin.bottom, margin.top]);

    var z = d3.scaleOrdinal(d3.schemeCategory10);

    var line = d3.line()
    .curve(d3.curveCardinal)
    .x(d => x(d.date))
    .y(d => y(d.degrees));

    svg.append("g")
    .attr("class","x-axis")
    .attr("transform", "translate(0," + (height - margin.bottom) + ")")
    .call(d3.axisBottom(x).tickFormat(d3.timeFormat("%b")));

    svg.append("g")
    .attr("class", "y-axis")
    .attr("transform", "translate(" + margin.left + ",0)");

    var focus = svg.append("g")
    .attr("class", "focus")
    .style("display", "none");

    focus.append("line").attr("class", "lineHover")
    .style("stroke", "#999")
    .attr("stroke-width", 1)
    .style("shape-rendering", "crispEdges")
    .style("opacity", 0.5)
    .attr("y1", -height)
    .attr("y2",0);

    focus.append("text").attr("class", "lineHoverDate")
    .attr("text-anchor", "middle")
    .attr("font-size", 12);

    var overlay = svg.append("rect")
    .attr("class", "overlay")
    .attr("x", margin.left)
    .attr("width", width - margin.right - margin.left)
    .attr("height", height)

    update(d3.select('#selectbox').property('value'), 0);

    function update(input, speed) {

    var copy = keys.filter(f => f.includes(input))

    var cities = copy.map(function(id) {
    return {
    id: id,
    values: data.map(d => {return {date: d.date, degrees: +d[id]}})
    };
    });

    y.domain([
    d3.min(cities, d => d3.min(d.values, c => c.degrees)),
    d3.max(cities, d => d3.max(d.values, c => c.degrees))
    ]).nice();

    svg.selectAll(".y-axis").transition()
    .duration(speed)
    .call(d3.axisLeft(y).tickSize(-width + margin.right + margin.left))

    var city = svg.selectAll(".cities")
    .data(cities);

    city.exit().remove();

    city.enter().insert("g", ".focus").append("path")
    .attr("class", "line cities")
    .style("stroke", d => z(d.id))
    .merge(city)
    .transition().duration(speed)
    .attr("d", d => line(d.values))

    tooltip(copy);
    }

    function tooltip(copy) {

    var labels = focus.selectAll(".lineHoverText")
    .data(copy)

    labels.enter().append("text")
    .attr("class", "lineHoverText")
    .style("fill", d => z(d))
    .attr("text-anchor", "start")
    .attr("font-size",12)
    .attr("dy", (_, i) => 1 + i * 2 + "em")
    .merge(labels);

    var circles = focus.selectAll(".hoverCircle")
    .data(copy)

    circles.enter().append("circle")
    .attr("class", "hoverCircle")
    .style("fill", d => z(d))
    .attr("r", 2.5)
    .merge(circles);

    svg.selectAll(".overlay")
    .on("mouseover", function() { focus.style("display", null); })
    .on("mouseout", function() { focus.style("display", "none"); })
    .on("mousemove", mousemove);

    function mousemove() {

    var x0 = x.invert(d3.mouse(this)[0]),
    i = bisectDate(data, x0, 1),
    d0 = data[i - 1],
    d1 = data[i],
    d = x0 - d0.date > d1.date - x0 ? d1 : d0;

    focus.select(".lineHover")
    .attr("transform", "translate(" + x(d.date) + "," + height + ")");

    focus.select(".lineHoverDate")
    .attr("transform",
    "translate(" + x(d.date) + "," + (height + margin.bottom) + ")")
    .text(formatDate(d.date));

    focus.selectAll(".hoverCircle")
    .attr("cy", e => y(d[e]))
    .attr("cx", x(d.date));

    focus.selectAll(".lineHoverText")
    .attr("transform",
    "translate(" + (x(d.date)) + "," + height / 2.5 + ")")
    .text(e => e + " " + "º" + formatValue(d[e]));

    x(d.date) > (width - width / 4)
    ? focus.selectAll("text.lineHoverText")
    .attr("text-anchor", "end")
    .attr("dx", -10)
    : focus.selectAll("text.lineHoverText")
    .attr("text-anchor", "start")
    .attr("dx", 10)
    }
    }

    var selectbox = d3.select("#selectbox")
    .on("change", function() {
    update(this.value, 750);
    })
    }

    </script>
  3. LemoNode revised this gist Jan 14, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    This is a multi line chart.

    # Added Features:
    #### Added Features:

    1. [d3.bisector](https://github.com/d3/d3-array/blob/master/README.md#bisector)
    2. Legend
  4. LemoNode revised this gist Jan 14, 2018. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,6 +1,6 @@
    This is a multi line chart.

    Added Features:
    # Added Features:

    1. [d3.bisector](https://github.com/d3/d3-array/blob/master/README.md#bisector)
    2. Legend
  5. LemoNode revised this gist Jan 14, 2018. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -2,8 +2,8 @@ This is a multi line chart.

    Added Features:

    1. [d3.bisector](https://github.com/d3/d3-array/blob/master/README.md#bisector)
    2. Legend
    3. Average temperature
    1. [d3.bisector](https://github.com/d3/d3-array/blob/master/README.md#bisector)
    2. Legend
    3. Average temperature

    All data is visualized through a csv file.
  6. LemoNode revised this gist Jan 14, 2018. 1 changed file with 6 additions and 0 deletions.
    6 changes: 6 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,9 @@
    This is a multi line chart.

    Added Features:

    1. [d3.bisector](https://github.com/d3/d3-array/blob/master/README.md#bisector)
    2. Legend
    3. Average temperature

    All data is visualized through a csv file.
  7. LemoNode revised this gist Jan 14, 2018. 2 changed files with 138 additions and 57 deletions.
    4 changes: 0 additions & 4 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,7 +1,3 @@
    This is a multi line chart.

    TODO:

    * Add legend

    All data is visualized through a csv file.
    191 changes: 138 additions & 53 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -4,26 +4,36 @@
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <style>
    body {
    margin:auto;
    width: 885px;
    font: 10px arial;
    padding: 25px;
    margin:auto;
    width: 885px;
    font: 10px arial;
    padding: 25px;
    }
    .axis--y line {
    opacity:.2;
    opacity:.2;
    }
    .axis--y path {
    display:none;
    display:none;
    }
    .line {
    fill: none;
    stroke-width: 1.5px;
    opacity:.8;
    opacity:.75;
    }
    .overlay {
    fill: none;
    pointer-events: all;
    }
    .lineHoverText {
    text-shadow:
    -2px -2px 0 #fff,
    2px -2px 0 #fff,
    -2px 2px 0 #fff,
    2px 2px 0 #fff;
    }
    .hoverCircle {
    opacity:.75;
    }
    </style>
    </head>

    @@ -42,36 +52,36 @@
    <script>

    // Init
    var parseTime = d3.timeParse("%Y%m%d"),
    bisectDate = d3.bisector(function(d) { return d.date; }).left,
    formatValue = d3.format(",.0f"),
    formatMetric = function(d) { return "º" + formatValue(d); };
    var parseTime = d3.timeParse("%Y%m%d"),
    formatDate = d3.timeFormat("%Y-%m-%d"),
    bisectDate = d3.bisector(function(d) { return d.date; }).left,
    formatValue = d3.format(",.0f"),
    formatMetric = function(d) { return "º" + formatValue(d); };

    // Chart measurements
    var margin = {top: 20, right: 45, bottom: 30, left: 45},
    var margin = {top: 20, right: 165, bottom: 40, left: 45},
    width = 850 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;
    height = 410 - margin.top - margin.bottom;

    var x = d3.scaleTime().range([0, width]),
    y = d3.scaleLinear().range([height, 0]),
    y2 = d3.scaleLinear().range([height, 0]),
    z = d3.scaleOrdinal(d3.schemeCategory10);
    var xAxis = d3.axisBottom(x)
    .tickFormat(d3.timeFormat("%B"));
    .tickFormat(d3.timeFormat("%B"));

    var yAxis = d3.axisLeft(y)
    .tickSize(-width);
    .tickSize(-width);

    var line = d3.line()
    .curve(d3.curveCardinal)
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.temperature); });
    var g = d3.select("#chart").append("svg")
    .attr("width", width + margin.left + margin.right)
    .attr("height", height + margin.top + margin.bottom)
    .append("g")
    .append("g")
    .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

    g.append("g")
    @@ -80,8 +90,25 @@
    g.append("g")
    .attr("class", "axis axis--y");

    g.append("g")
    .attr("class", "city")
    let legend = g.append("g")
    .attr("text-anchor", "start")
    .selectAll("g")
    .data([["New York"],["San Francisco"],["Austin"]])
    .enter().append("g")
    .attr("transform", function(d, i) { return "translate(0," + i * 60 + ")"; });

    legend.append("rect")
    .attr("x", width + 30)
    .attr("width", 12)
    .attr("height", 19)
    .attr("fill", z);

    legend.append("text")
    .attr("x", width + 50)
    .attr("font-size", 11)
    .attr("y", 9.5)
    .attr("dy", "0.32em")
    .text(function(d) { return d; });

    var durations = 0;

    @@ -103,16 +130,33 @@
    d[c = columns[i]] = +d[c];
    return d;
    }, function(error, data) {
    if (error) throw error;
    if (error) throw error;

    var avgCopy = []

    // Average Temperatures
    var avgNY = d3.sum(data, function(d) {
    return d["New York" + METRIC];
    }) / data.length;
    var avgAU = d3.sum(data, function(d) {
    return d["Austin" + METRIC];
    }) / data.length;
    var avgSF = d3.sum(data, function(d) {
    return d["San Francisco" + METRIC];
    }) / data.length;

    avgCopy.push(avgNY, avgAU, avgSF)

    let keys = data.columns.slice(1, 5); // This works for some reason...
    console.log(avgCopy)

    let keys = data.columns.slice(1, 4);

    let copy = [];

    keys.forEach(function(t) {
    t = t.slice(0, -2) // Slice last two letters
    copy.push(t) // Push sliced strings into copy array
    })
    });

    var cities = copy.map(function(id) {
    return {
    @@ -152,13 +196,13 @@
    // ========= Line Path ========
    // ============================

    var city = g.selectAll(".city")
    var city = g.selectAll(".cities")
    .data(cities);

    city = city
    .enter()
    .append("path")
    .attr("class", "line city")
    .attr("class", "line cities")
    .style("stroke", function(d) { return z(d.id); })
    .merge(city);

    @@ -182,27 +226,32 @@
    .attr("y1",-height)
    .attr("y2",0);

    focus.append("text")
    focus.append("text").attr("class", "lineHoverDate")
    .attr("text-anchor","middle")
    .attr("font-size",12);

    var labels = focus.selectAll(null)
    .data(copy)
    .enter()
    .append("text")
    .attr("class", "lineHoverText")
    .attr("font-size",11)
    .attr("x", 10)
    .attr("dy", "1em");

    focus.append("text")
    .attr("class", "lineHoverText2")
    .attr("font-size",11)
    .attr("x", 10)
    .attr("dy", "2.5em");

    focus.append("text")
    .attr("class", "lineHoverText3")
    .attr("font-size",11)
    .attr("x", 10)
    .attr("dy", "4em");
    .style("fill", function(d) {return z(d);})
    .attr("font-size",12)
    .attr("dy", function(_,i){
    return 1 + i * 2 + "em"
    });

    var circles = focus.selectAll(null)
    .data(copy)
    .enter()
    .append("circle")
    .style("fill", function(d) {return z(d);})
    .attr("class", "hoverCircle")
    .attr("r", 2.5);

    g.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("width", width -1) // -1 added
    .attr("height", height)
    .on("mouseover", function() { focus.style("display", null); })
    .on("mouseout", function() { focus.style("display", "none"); })
    @@ -218,19 +267,55 @@
    focus.select(".lineHover")
    .attr("transform", "translate(" + x(d.date) + "," + height + ")");

    focus.select("text.lineHoverText")
    .attr("transform", "translate(" + (x(d.date) + 10) + "," + height/2.5 + ")")
    .text("New york " + formatMetric(d["New York" + METRIC]) );
    focus.select(".lineHoverDate")
    .attr("transform",
    "translate(" + x(d.date) + "," + (height + margin.bottom) + ")")
    .text(formatDate(d.date));

    focus.select("text.lineHoverText2")
    .attr("transform", "translate(" + (x(d.date) + 10) + "," + height/2.5 + ")")
    .text("San Francisco " + formatMetric(d["San Francisco" + METRIC]) );

    focus.select("text.lineHoverText3")
    .attr("transform", "translate(" + (x(d.date) + 10) + "," + height/2.5 + ")")
    .text("Austin " + formatMetric(d["Austin" + METRIC]) );
    circles
    .attr("cy", function(e) {
    return y(d[e + METRIC])
    })
    .attr("cx", x(d.date) );

    labels.attr("transform", "translate(" + (x(d.date) + 15) + "," + height/2.5 + ")")
    .text(function(e) {
    return e + " " + formatMetric(d[e + METRIC])
    });

    // Horizontal text position shift
    if (x(d.date) > width - width/4) {
    focus.selectAll("text.lineHoverText")
    .attr("x", -130);
    } else {
    focus.selectAll("text.lineHoverText")
    .attr("x", 15);
    }
    }

    // ============================
    // ========= Average ==========
    // ============================

    var avg = g.selectAll(".avg")
    .data(avgCopy);
    avg = avg
    .enter()
    .append("text")
    .attr("class", "avg")
    .attr("transform", function(d, i) {
    return "translate(0," + i * 60 + ")";
    })
    .attr("x",width + 50)
    .attr("y",40)
    .attr("font-weight","bold")
    .attr("font-size",12)
    .merge(avg);

    avg.transition()
    .duration(durations)
    .text( function(d) { return "Avg. " + formatMetric(d); })

    afterLoad(); // Change transition duration

    })
  8. LemoNode revised this gist Jan 14, 2018. 1 changed file with 0 additions and 1 deletion.
    1 change: 0 additions & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -2,7 +2,6 @@ This is a multi line chart.

    TODO:

    * Add bisector
    * Add legend

    All data is visualized through a csv file.
  9. LemoNode revised this gist Jan 14, 2018. 1 changed file with 79 additions and 22 deletions.
    101 changes: 79 additions & 22 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -1,48 +1,39 @@
    <!DOCTYPE html>
    <head>

    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <style>

    body {
    margin:auto;
    width: 885px;
    font: 10px arial;
    padding: 25px;
    }
    .axis--y line {
    shape-rendering: crispEdges;
    opacity:.2;
    }
    .axis--y path {
    display:none;
    }
    .axis--y2 line,
    .axis--x line {
    shape-rendering: crispEdges;
    }
    .city {
    .line {
    fill: none;
    stroke-width: 1.5px;
    opacity:.8;
    }
    .line {
    .overlay {
    fill: none;
    stroke-width: 1.5px;
    opacity:.8;
    pointer-events: all;
    }
    </style>

    </head>

    <body>

    <!-- Selectbox -->
    <font style="font-size:11px;">Change metric:</font>
    <select id="selectbox">
    <option selected value="_1">Fahrenheit</option>
    <option value="_2">Celsius</option>
    <option selected value="_1">Fahrenheit</option>
    <option value="_2">Celsius</option>
    </select>

    <!-- Chart -->
    @@ -52,11 +43,13 @@

    // Init
    var parseTime = d3.timeParse("%Y%m%d"),
    formatValue = d3.format(",.0f");
    bisectDate = d3.bisector(function(d) { return d.date; }).left,
    formatValue = d3.format(",.0f"),
    formatMetric = function(d) { return "º" + formatValue(d); };

    // Chart measurements
    var margin = {top: 20, right: 145, bottom: 30, left: 40},
    width = 865 - margin.left - margin.right,
    var margin = {top: 20, right: 45, bottom: 30, left: 45},
    width = 850 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

    var x = d3.scaleTime().range([0, width]),
    @@ -84,7 +77,6 @@
    g.append("g")
    .attr("class","axis axis--x")
    .attr("transform", "translate(0," + height + ")");

    g.append("g")
    .attr("class", "axis axis--y");

    @@ -115,8 +107,6 @@

    let keys = data.columns.slice(1, 5); // This works for some reason...

    console.log(keys)

    let copy = [];

    keys.forEach(function(t) {
    @@ -133,8 +123,6 @@
    };
    });

    console.log(copy)

    x.domain(d3.extent(data, function(d) { return d.date; }));

    y.domain([
    @@ -160,6 +148,10 @@
    .duration(durations)
    .call(xAxis);

    // ============================
    // ========= Line Path ========
    // ============================

    var city = g.selectAll(".city")
    .data(cities);

    @@ -174,6 +166,71 @@
    .duration(durations)
    .attr("d", function(d) { return line(d.values); })

    // ============================
    // ======== Focus Line ========
    // ============================

    var focus = g.append("g")
    .attr("class", "focus")
    .style("display", "none");

    focus.append("line").attr("class", "lineHover")
    .style("stroke", "#999")
    .attr("stroke-width",1)
    .style("shape-rendering", "crispEdges")
    .style("opacity", 0.5)
    .attr("y1",-height)
    .attr("y2",0);

    focus.append("text")
    .attr("class", "lineHoverText")
    .attr("font-size",11)
    .attr("x", 10)
    .attr("dy", "1em");

    focus.append("text")
    .attr("class", "lineHoverText2")
    .attr("font-size",11)
    .attr("x", 10)
    .attr("dy", "2.5em");

    focus.append("text")
    .attr("class", "lineHoverText3")
    .attr("font-size",11)
    .attr("x", 10)
    .attr("dy", "4em");

    g.append("rect")
    .attr("class", "overlay")
    .attr("width", width)
    .attr("height", height)
    .on("mouseover", function() { focus.style("display", null); })
    .on("mouseout", function() { focus.style("display", "none"); })
    .on("mousemove", mousemove);

    function mousemove() {
    var x0 = x.invert(d3.mouse(this)[0]),
    i = bisectDate(data, x0, 1),
    d0 = data[i - 1],
    d1 = data[i],
    d = x0 - d0.date > d1.date - x0 ? d1 : d0;

    focus.select(".lineHover")
    .attr("transform", "translate(" + x(d.date) + "," + height + ")");

    focus.select("text.lineHoverText")
    .attr("transform", "translate(" + (x(d.date) + 10) + "," + height/2.5 + ")")
    .text("New york " + formatMetric(d["New York" + METRIC]) );

    focus.select("text.lineHoverText2")
    .attr("transform", "translate(" + (x(d.date) + 10) + "," + height/2.5 + ")")
    .text("San Francisco " + formatMetric(d["San Francisco" + METRIC]) );

    focus.select("text.lineHoverText3")
    .attr("transform", "translate(" + (x(d.date) + 10) + "," + height/2.5 + ")")
    .text("Austin " + formatMetric(d["Austin" + METRIC]) );
    }

    afterLoad(); // Change transition duration

    })
  10. LemoNode revised this gist Jan 14, 2018. 1 changed file with 0 additions and 0 deletions.
    Binary file modified thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  11. LemoNode revised this gist Jan 14, 2018. 1 changed file with 0 additions and 0 deletions.
    Binary file added thumbnail.png
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  12. LemoNode created this gist Jan 14, 2018.
    1 change: 1 addition & 0 deletions .block
    Original file line number Diff line number Diff line change
    @@ -0,0 +1 @@
    license: gpl-3.0
    8 changes: 8 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,8 @@
    This is a multi line chart.

    TODO:

    * Add bisector
    * Add legend

    All data is visualized through a csv file.
    275 changes: 275 additions & 0 deletions data.csv
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,275 @@
    date,New York_1,San Francisco_1,Austin_1,New York_2,San Francisco_2,Austin_2
    20120101,46,53,55,8,12,13
    20120102,43,53,48,6,12,9
    20120103,30,49,41,-1,9,5
    20120104,19,52,48,-7,11,9
    20120105,32,52,54,0,11,12
    20120106,41,49,61,5,9,16
    20120107,47,51,59,8,11,15
    20120108,46,56,52,8,13,11
    20120109,34,52,54,1,11,12
    20120110,39,51,47,4,11,8
    20120111,40,49,49,4,9,9
    20120112,45,51,48,7,11,9
    20120113,40,53,40,4,12,4
    20120114,30,52,43,-1,11,6
    20120115,23,49,45,-5,9,7
    20120116,22,45,65,-6,7,18
    20120117,39,43,68,4,6,20
    20120118,43,45,47,6,7,8
    20120119,26,47,57,-3,8,14
    20120120,32,51,61,0,11,16
    20120121,27,53,54,-3,12,12
    20120122,25,48,56,-4,9,13
    20120123,39,52,54,4,11,12
    20120124,48,49,52,9,9,11
    20120125,43,52,61,6,11,16
    20120126,37,53,55,3,12,13
    20120127,48,50,50,9,10,10
    20120128,43,50,52,6,10,11
    20120129,40,53,44,4,12,7
    20120130,38,51,49,3,11,9
    20120131,43,50,62,6,10,17
    20120201,50,50,64,10,10,18
    20120202,45,51,61,7,11,16
    20120203,37,51,70,3,11,21
    20120204,40,52,61,4,11,16
    20120205,36,53,48,2,12,9
    20120206,39,54,44,4,12,7
    20120207,43,54,51,6,12,11
    20120208,36,51,49,2,11,9
    20120209,36,53,45,2,12,7
    20120210,38,53,54,3,12,12
    20120211,36,52,44,2,11,7
    20120212,29,50,36,-2,10,2
    20120213,33,49,44,1,9,7
    20120214,39,48,52,4,9,11
    20120215,42,49,68,6,9,20
    20120216,39,52,54,4,11,12
    20120217,46,49,53,8,9,12
    20120218,41,51,56,5,11,13
    20120219,39,47,50,4,8,10
    20120220,38,48,53,3,9,12
    20120221,37,49,61,3,9,16
    20120222,45,53,68,7,12,20
    20120223,50,54,69,10,12,21
    20120224,42,55,59,6,13,15
    20120225,42,51,47,6,11,8
    20120226,36,47,47,2,8,8
    20120227,40,45,61,4,7,16
    20120228,45,47,67,7,8,19
    20120229,40,49,70,4,9,21
    20120301,41,48,62,5,9,17
    20120302,36,48,72,2,9,22
    20120303,47,50,59,8,10,15
    20120304,44,55,51,7,13,11
    20120305,38,48,55,3,9,13
    20120306,32,48,61,0,9,16
    20120307,43,49,67,6,9,19
    20120308,51,49,72,11,9,22
    20120309,47,51,46,8,11,8
    20120310,37,49,46,3,9,8
    20120311,42,50,56,6,10,13
    20120312,48,48,61,9,9,16
    20120313,52,53,68,11,12,20
    20120314,60,55,71,16,13,22
    20120315,47,55,72,8,13,22
    20120316,44,54,72,7,12,22
    20120317,48,48,71,9,9,22
    20120318,48,47,71,9,8,22
    20120319,53,45,73,12,7,23
    20120320,57,49,63,14,9,17
    20120321,57,51,60,14,11,16
    20120322,57,51,62,14,11,17
    20120323,61,48,61,16,9,16
    20120324,55,49,62,13,9,17
    20120325,48,46,64,9,8,18
    20120326,49,49,66,9,9,19
    20120327,39,54,65,4,12,18
    20120328,49,54,69,9,12,21
    20120329,56,52,69,13,11,21
    20120330,46,54,73,8,12,23
    20120331,42,56,73,6,13,23
    20120401,45,51,75,7,11,24
    20120402,48,50,75,9,10,24
    20120403,51,52,77,11,11,25
    20120404,61,50,67,16,10,19
    20120405,50,47,71,10,8,22
    20120406,48,47,70,9,8,21
    20120407,51,49,73,11,9,23
    20120408,55,50,71,13,10,22
    20120409,58,51,70,14,11,21
    20120410,55,53,69,13,12,21
    20120411,49,52,69,9,11,21
    20120412,51,53,74,11,12,23
    20120413,53,50,73,12,10,23
    20120414,55,50,76,13,10,24
    20120415,62,51,74,17,11,23
    20120416,62,51,63,17,11,17
    20120417,69,53,67,21,12,19
    20120418,59,53,65,15,12,18
    20120419,54,55,67,12,13,19
    20120420,56,55,68,13,13,20
    20120421,58,58,65,14,14,18
    20120422,52,52,65,11,11,18
    20120423,51,55,70,11,13,21
    20120424,49,57,67,9,14,19
    20120425,52,57,75,11,14,24
    20120426,50,55,77,10,13,25
    20120427,51,53,77,11,12,25
    20120428,47,54,77,8,12,25
    20120429,54,54,77,12,12,25
    20120430,51,53,77,11,12,25
    20120501,57,52,77,14,11,25
    20120502,53,50,77,12,10,25
    20120503,53,52,79,12,11,26
    20120504,57,53,80,14,12,27
    20120505,57,53,82,14,12,28
    20120506,56,56,79,13,13,26
    20120507,54,55,79,12,13,26
    20120508,57,52,70,14,11,21
    20120509,59,52,69,15,11,21
    20120510,61,53,71,16,12,22
    20120511,59,53,69,15,12,21
    20120512,64,49,72,18,9,22
    20120513,65,52,72,18,11,22
    20120514,64,56,72,18,13,22
    20120515,62,53,67,17,12,19
    20120516,63,51,69,17,11,21
    20120517,64,51,72,18,11,22
    20120518,61,52,73,16,11,23
    20120519,62,52,77,17,11,25
    20120520,66,54,75,19,12,24
    20120521,62,52,76,17,11,24
    20120522,63,53,78,17,12,26
    20120523,66,56,77,19,13,25
    20120524,64,54,80,18,12,27
    20120525,65,52,81,18,11,27
    20120526,69,52,82,21,11,28
    20120527,71,52,80,22,11,27
    20120528,74,52,80,23,11,27
    20120529,75,52,82,24,11,28
    20120530,72,52,81,22,11,27
    20120531,72,53,82,22,12,28
    20120601,67,54,77,19,12,25
    20120602,68,54,81,20,12,27
    20120603,67,52,82,19,11,28
    20120604,61,55,81,16,13,27
    20120605,58,53,83,14,12,28
    20120606,61,54,83,16,12,28
    20120607,66,53,82,19,12,28
    20120608,68,54,77,20,12,25
    20120609,72,55,77,22,13,25
    20120610,72,60,82,22,16,28
    20120611,69,57,86,21,14,30
    20120612,66,55,85,19,13,29
    20120613,66,53,76,19,12,24
    20120614,67,53,84,19,12,29
    20120615,68,54,84,20,12,29
    20120616,67,57,83,19,14,28
    20120617,64,55,82,18,13,28
    20120618,61,52,82,16,11,28
    20120619,66,53,82,19,12,28
    20120620,77,55,81,25,13,27
    20120621,88,53,80,31,12,27
    20120622,82,54,81,28,12,27
    20120623,77,55,83,25,13,28
    20120624,75,55,85,24,13,29
    20120625,70,56,87,21,13,31
    20120626,65,57,88,18,14,31
    20120627,73,57,89,23,14,32
    20120628,77,56,86,25,13,30
    20120629,79,56,85,26,13,29
    20120630,84,58,81,29,14,27
    20120701,81,58,78,27,14,26
    20120702,82,56,83,28,13,28
    20120703,80,56,83,27,13,28
    20120704,77,55,84,25,13,29
    20120705,86,54,84,30,12,29
    20120706,79,54,84,26,12,29
    20120707,83,54,86,28,12,30
    20120708,81,52,84,27,11,29
    20120709,77,53,83,25,12,28
    20120710,76,53,77,24,12,25
    20120711,76,52,77,24,11,25
    20120712,75,52,80,24,11,27
    20120713,77,53,81,25,12,27
    20120714,79,54,80,26,12,27
    20120715,78,54,81,26,12,27
    20120716,79,54,77,26,12,25
    20120717,83,56,80,28,13,27
    20120718,84,57,81,29,14,27
    20120719,75,57,80,24,14,27
    20120720,68,58,83,20,14,28
    20120721,68,57,85,20,14,29
    20120722,72,56,83,22,13,28
    20120723,75,56,84,24,13,29
    20120724,82,57,83,28,14,28
    20120725,78,57,84,26,14,29
    20120726,77,56,84,25,13,29
    20120727,79,55,84,26,13,29
    20120728,77,55,83,25,13,28
    20120729,72,55,85,22,13,29
    20120730,72,55,84,22,13,29
    20120731,73,55,86,23,13,30
    20120801,75,55,86,24,13,30
    20120802,77,54,85,25,12,29
    20120803,79,53,85,26,12,29
    20120804,79,54,86,26,12,30
    20120805,81,57,84,27,14,29
    20120806,80,58,81,27,14,27
    20120807,75,58,86,24,14,30
    20120808,77,57,86,25,14,30
    20120809,78,55,86,26,13,30
    20120810,77,54,88,25,12,31
    20120811,78,53,85,26,12,29
    20120812,78,52,87,26,11,31
    20120813,78,53,88,26,12,31
    20120814,76,53,88,24,12,31
    20120815,76,56,87,24,13,31
    20120816,75,57,86,24,14,30
    20120817,77,55,86,25,13,30
    20120818,72,56,84,22,13,29
    20120819,70,54,76,21,12,24
    20120820,71,53,80,22,12,27
    20120821,73,53,80,23,12,27
    20120822,74,54,78,23,12,26
    20120823,74,55,79,23,13,26
    20120824,76,54,81,24,12,27
    20120825,76,53,84,24,12,29
    20120826,73,56,83,23,13,28
    20120827,74,58,82,23,14,28
    20120828,79,58,84,26,14,29
    20120829,74,57,85,23,14,29
    20120830,73,55,87,23,13,31
    20120831,77,55,82,25,13,28
    20120901,80,55,84,27,13,29
    20120902,75,53,83,24,12,28
    20120903,73,53,85,23,12,29
    20120904,73,52,86,23,11,30
    20120905,77,54,85,25,12,29
    20120906,74,56,85,23,13,29
    20120907,76,56,85,24,13,29
    20120908,77,56,81,25,13,27
    20120909,69,56,74,21,13,23
    20120910,67,55,71,19,13,22
    20120911,64,56,75,18,13,24
    20120912,68,55,82,20,13,28
    20120913,69,54,80,21,12,27
    20120914,70,55,70,21,13,21
    20120915,69,54,71,21,12,22
    20120916,66,52,70,19,11,21
    20120917,67,54,72,19,12,22
    20120918,72,54,73,22,12,23
    20120919,67,56,72,19,13,22
    20120920,62,55,71,17,13,22
    20120921,64,55,72,18,13,22
    20120922,65,55,73,18,13,23
    20120923,65,52,75,18,11,24
    20120924,60,54,78,16,12,26
    20120925,63,53,78,17,12,26
    20120926,68,53,79,20,12,26
    20120927,69,52,76,21,11,24
    20120928,68,52,77,20,11,25
    20120929,62,53,75,17,12,24
    20120930,62,55,71,17,13,22
    183 changes: 183 additions & 0 deletions index.html
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,183 @@
    <!DOCTYPE html>
    <head>

    <meta charset="utf-8">
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <style>

    body {
    margin:auto;
    width: 885px;
    font: 10px arial;
    padding: 25px;
    }
    .axis--y line {
    shape-rendering: crispEdges;
    opacity:.2;
    }
    .axis--y path {
    display:none;
    }
    .axis--y2 line,
    .axis--x line {
    shape-rendering: crispEdges;
    }
    .city {
    fill: none;
    stroke-width: 1.5px;
    opacity:.8;
    }
    .line {
    fill: none;
    stroke-width: 1.5px;
    opacity:.8;
    }
    </style>

    </head>

    <body>

    <!-- Selectbox -->
    <font style="font-size:11px;">Change metric:</font>
    <select id="selectbox">
    <option selected value="_1">Fahrenheit</option>
    <option value="_2">Celsius</option>
    </select>

    <!-- Chart -->
    <div id="chart"></div>

    <script>

    // Init
    var parseTime = d3.timeParse("%Y%m%d"),
    formatValue = d3.format(",.0f");

    // Chart measurements
    var margin = {top: 20, right: 145, bottom: 30, left: 40},
    width = 865 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

    var x = d3.scaleTime().range([0, width]),
    y = d3.scaleLinear().range([height, 0]),
    y2 = d3.scaleLinear().range([height, 0]),
    z = d3.scaleOrdinal(d3.schemeCategory10);

    var xAxis = d3.axisBottom(x)
    .tickFormat(d3.timeFormat("%B"));

    var yAxis = d3.axisLeft(y)
    .tickSize(-width);

    var line = d3.line()
    .curve(d3.curveCardinal)
    .x(function(d) { return x(d.date); })
    .y(function(d) { return y(d.temperature); });

    var g = d3.select("#chart").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 + ")");

    g.append("g")
    .attr("class","axis axis--x")
    .attr("transform", "translate(0," + height + ")");

    g.append("g")
    .attr("class", "axis axis--y");

    g.append("g")
    .attr("class", "city")

    var durations = 0;

    let afterLoad = () => durations = 750;

    d3.select("#selectbox").on('change', update); // Change category

    var METRIC;

    update();

    function update() {

    METRIC = d3.select('#selectbox').property('value');

    d3.csv("data.csv", function(d, _, columns) {
    d.date = parseTime(d.date);
    for (var i = 1, n = columns.length, c; i < n; ++i)
    d[c = columns[i]] = +d[c];
    return d;
    }, function(error, data) {
    if (error) throw error;

    let keys = data.columns.slice(1, 5); // This works for some reason...

    console.log(keys)

    let copy = [];

    keys.forEach(function(t) {
    t = t.slice(0, -2) // Slice last two letters
    copy.push(t) // Push sliced strings into copy array
    })

    var cities = copy.map(function(id) {
    return {
    id: id,
    values: data.map(function(d) {
    return {date: d.date, temperature: d[id+METRIC]};
    })
    };
    });

    console.log(copy)

    x.domain(d3.extent(data, function(d) { return d.date; }));

    y.domain([
    d3.min(cities, function(c) {
    return d3.min(c.values, function(d) {
    return d.temperature;
    });
    }),
    d3.max(cities, function(c) {
    return d3.max(c.values, function(d) {
    return d.temperature;
    });
    })
    ]);

    z.domain(cities.map(function(c) { return c.id; }));

    g.selectAll(".axis.axis--y").transition()
    .duration(durations)
    .call(yAxis);

    g.selectAll(".axis.axis--x").transition()
    .duration(durations)
    .call(xAxis);

    var city = g.selectAll(".city")
    .data(cities);

    city = city
    .enter()
    .append("path")
    .attr("class", "line city")
    .style("stroke", function(d) { return z(d.id); })
    .merge(city);

    city.transition()
    .duration(durations)
    .attr("d", function(d) { return line(d.values); })

    afterLoad(); // Change transition duration

    })
    }

    </script>
    </body>