D3.js: Import file changing with a slider

2019-08-01 02:34发布

I am building a slider on a D3.js visualization.

I want the user to select a year on the slider and the slider to retrieve the data from a file, that in the name has the year that it was selected in the slider.

For that, I have added in the app.js file:

function update(year){
     slider.property("value", year);
     d3.select(".year").text(year);
     // us_data.style("fill", function(d) {
     //     return color(d.properties.years[year][0].rate)
     // });
}


var slider = d3.select(".slider")
    .append("input")
    .attr("type", "range")
    .attr("min", 2009)
    .attr("max", 2013)
    .attr("step", 1)
    .on("input", function() {
         var year = this.value;
         update(year);
     });

update(2013);

As I intend to read the year selected selected in the slider in order to pickup data from a file with the name of the year, I have added an id to the h2 that will change when the slider moves on index.html:

<h2 id="vardatayear" class="year"></h2>
        <div class="slider"></div>

Considering that the "vardatayear" will influence the file that I want to import, I have created a variable to retrieve that value, and/or if it comes empty, retrieve 2013 (by default):

var honeyyear = document.getElementById("vardatayear").value || 2013;

Being my import as the following:

d3.json( `data/HoneyProduction-${honeyyear}.json`, function( honey_data ){...

I am inspiring my code in this slider, but when I change the year, the map stays the same, as the change in the slider is not affecting the file path in the import. Any guidance in the process is appreciated.


Index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>VI - Gonçalo Peres</title>

    <link rel="stylesheet" type="text/css" href="css/style.css">
    <link rel="stylesheet" type="text/css"
          href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
</head>
<body>
    <nav class="navbar navbar-default">
        <div class="container">
            <a class="navbar-brand" href="#"><img id="logo" src="img/logo.png"></a>
            <br>
            <div id="mySidenav" class="sidenav">
                <a href="javascript:void(0)" class="closebtn" onclick="closeNav()">&times;</a>
                <a href="#">Contexto</a>
                <a href="#">1.ª Codificação Visual</a>
                <a href="#">2.ª Codificação Visual</a>
                <a href="#">3.ª Codificação Visual</a>
            </div>
        </div>
        <span style="font-size:30px;cursor:pointer" onclick="openNav()">&#9776;</span>
        <br>
        <br>
    </nav>
    <div id="intro">
        <h2>1.ª Codificação Visual</h2>
        <span>Lorem ipsum.</span><br>
        <span>Lorem Ipsum.</span><br>
    </div>
    <div id="legenda">
        <h4>Legenda</h4>
        <span>Lorem ipsum.</span><br>
        <span>Lorem ipsum.</span><br>
    </div>
    <br>
    <div id="content">
        <h1>U.S. Department of Agriculture - Honey Production, 2009-2013</h1>
        <h2 id="vardatayear" class="year"></h2>
        <div class="slider"></div>
        <div id="map"></div>
    </div>
    <div id="chart"></div>
    <div id="buttons">
        <button type="button" class="panning up"><i class="fa fa-arrow-up"></i></button>
        <button type="button" class="panning down"><i class="fa fa-arrow-down"></i></button>
        <button type="button" class="panning left"><i class="fa fa-arrow-left"></i></button>
        <button type="button" class="panning right"><i class="fa fa-arrow-right"></i></button>
        <button type="button" class="zooming in"><i class="fa fa-plus"></i></button>
        <button type="button" class="zooming out"><i class="fa fa-minus"></i></button>
    </div>

    <div id="note">
        <span>Gonçalo Peres | <b><a class="website" href="http://goncaloperes.com/" target="_blank">GoncaloPeres.com</a></b></span><br>
        <span>Data from: <a class="website" href="https://public.enigma.com/datasets/u-s-department-of-agriculture-honey-production-2013/41cf2441-e96f-4113-a02f-402d167a9cd8" target="_blank">Enigma Public</a></span>
    </div>

    <script src="https://d3js.org/d3.v4.js"></script>
    <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
    <script src="js/app.js"></script>
</body>

app.js

//  Define width and height
var chart_width     =   800;
var chart_height    =   600;


// Define the color scale
var color           =   d3.scaleQuantize().range([
    "#f7fbff",
    "#deebf7",
    "#c6dbef",
    "#9ecae1",
    "#6baed6",
    "#4292c6",
    "#2171b5",
    "#08519c",
    "#08306b"
]);


// Navbar
function openNav() {
    document.getElementById("mySidenav").style.width = "100%";
}

function closeNav() {
    document.getElementById("mySidenav").style.width = "0";
}


// Define the Tooltip
var tooltip = d3.select("body").append("div")
    .attr("class", "tooltip");


// Define the Map Projection
var projection      =   d3.geoAlbersUsa()
    .translate([ 0,0 ]);
var path            =   d3.geoPath( projection );


// Create SVG
var svg             =   d3.select("#chart")
    .append("svg")
    .attr("width", chart_width)
    .attr("height", chart_height);


// Zoom
var zoom_map        =   d3.zoom()
    .scaleExtent([ 0.5, 3.0 ])
    .translateExtent([
        [ -1000, -500 ],
        [ 1000, 500 ]
    ])
    .on( 'zoom', function(){
    // console.log( d3.event );
    var offset      =   [
        d3.event.transform.x,
        d3.event.transform.y
    ];
    var scale       =   d3.event.transform.k * 1100;

    projection.translate( offset )
        .scale( scale );

    svg.selectAll( 'path' )
        .transition()
        .attr( 'd', path );

    svg.selectAll( 'circle' )
        .transition()
        .attr( "cx", function(d) {
            return projection([d.longitude, d.latitude])[0];
        })
        .attr( "cy", function(d) {
            return projection([d.longitude, d.latitude])[1];
        });
});

var map             =   svg.append( 'g' )
    .attr( 'id', 'map' )
    .call( zoom_map )
    .call(
        zoom_map.transform,
        d3.zoomIdentity
            .translate( chart_width / 2, chart_height / 2 )
            .scale( 1 )
    );

map.append( 'rect' )
    .attr( 'x', 0 )
    .attr( 'y', 0 )
    .attr( 'class', "mapsvg")
    .attr( 'width', chart_width )
    .attr( 'height', chart_height )
    .attr( 'opacity', 0 );


// Load Data
var honeyyear = document.getElementById("vardatayear").value || 2013;

// if(!honeyyear){
//     honeyyear  = 2013
// }

// Select what are we analyzing
// var honeyattribute = document.getElementById('some_input_id').value;

d3.json( `data/HoneyProduction-${honeyyear}.json`, function( honey_data ){

    color.domain([
        d3.min( honey_data, function(d){
            return d.average_price_per_pound;
        }),
        d3.max( honey_data, function(d){
            return d.average_price_per_pound;
        })
    ]);

    // Load GeoJson Data
    d3.json( 'data/us.json', function( us_data ){
        us_data.features.forEach(function(us_e, us_i){
            honey_data.forEach(function(h_e,h_i){
                if( us_e.properties.name !== h_e.state ){
                    return null;
                }

                us_data.features[us_i].properties.average_price_per_pound   =   parseFloat(h_e.average_price_per_pound);
            });
        });


        // Bind Data
        map.selectAll( 'path' )
            .data( us_data.features )
            .enter()
            .append( 'path' )
            .attr( 'd', path )
            .attr( 'fill', function( d ){
                var average_price_per_pound         =   d.properties.average_price_per_pound;
                return average_price_per_pound ? color( average_price_per_pound ) : '#525252';
            })
            .attr( 'stroke', '#fff' )
            .attr( 'stroke-width', 1 )
            .on("mouseover", function(d) {
                tooltip.transition()
                    .duration(250)
                    .style("opacity", 1);
                tooltip.html("<strong>" + d.properties.name + "</strong>" + "<br/>" + "Average price per pound (cents): " +
                    (d.properties.average_price_per_pound))
                    .style("left", (d3.event.pageX + 15) + "px")
                    .style("top", (d3.event.pageY - 28) + "px");
            })
            .on("mouseout", function(d) {
                tooltip.transition()
                    .duration(250)
                    .style("opacity", 0);
            })
            .append('title')

            // // For the simple tooltip
            // .text(d => d.properties.average_price_per_pound);

        // draw_cities();

    });



    // Legend
    const x = d3.scaleLinear()
        .domain(d3.extent(color.domain()))
        .rangeRound([500, 750]);

    const g = svg.append("g")
        .attr( "class", "legend" )
        .attr("transform", "translate(0,40)");

    g.selectAll("rect")
        .data(color.range().map(d => color.invertExtent(d)))
        .enter().append("rect")
        .attr("height", 8)
        .attr("x", d => x(d[0]))
        .attr("width", d => x(d[1]) - x(d[0]))
        .attr("fill", d => color(d[0]));

    g.append("text")
        .attr("class", "caption")
        .attr("x", x.range()[0])
        .attr("y", -6)
        .attr("fill", "#fff")
        .attr("text-anchor", "start")
        .attr("font-weight", "bold")
        .text('Average Price per Pound (cents)');

    g.call(d3.axisBottom(x)
        .tickSize(13)
        .tickFormat(( honey_data, function(d){
            return d.average_price_per_pound;
        }),)
        .tickValues(color.range().slice(1).map(d => color.invertExtent(d)[0])))
        .select(".domain")
        .remove();

    // svg.append("g")
    //     .selectAll("path")
    //     .data(honey_data, function(d){
    //         return d.average_price_per_pound;
    //     })
    //     .enter().append("path")
    //     .attr("fill", d => color(d.average_price_per_pound))
    //     .attr("d", path)
    //     .append("title")
    //     .text(d => (d.average_price_per_pound));
    //
    // svg.append("path")
    //     .datum(honey_data, function(d){
    //         return d.average_price_per_pound;
    //     })
    //     .attr("fill", "none")
    //     .attr("stroke", "white")
    //     .attr("stroke-linejoin", "round")
    //     .attr("d", path);


});

// Add most popular cities across the US with a circle changing accordingly to the size of the population
function draw_cities(){
    d3.json( 'data/us-city.json', function( city_data ){
        map.selectAll("circle")
            .data(city_data)
            .enter()
            .append( "circle" )
            .style( "fill", "#9D497A" )
            .style( "opacity", 0.8 )
            .attr( 'cx', function( d ){
                return projection([ d.longitude, d.latitude ])[0];
            })
            .attr( 'cy', function( d ){
                return projection([ d.longitude, d.latitude ])[1];
            })
            .attr( 'r', function(d){
                return Math.sqrt( parseInt(d.population) * 0.00005 );
            })
            .append( 'title' )
            .text(function(d){
                return d.city;
            });
    });
}


// Panning
d3.selectAll( '#buttons button.panning' ).on( 'click', function(){
    var x           =   0;
    var y           =   0;
    var distance    =   100;
    var direction   =   d3.select( this ).attr( 'class' ).replace( 'panning ', '' );

    if( direction === "up" ){
        y           +=  distance; // Increase y offset
    }else if( direction === "down" ){
        y           -=  distance; // Decrease y offset
    }else if( direction === "left" ){
        x           +=  distance; // Increase x offset
    }else if( direction === "right" ){
        x           -=  distance; // Decrease x offset
    }

    map.transition()
        .call( zoom_map.translateBy, x, y );
});


// Zooming
d3.selectAll( '#buttons button.zooming' ).on( 'click', function(){
    var scale       =   1;
    var direction   =   d3.select(this).attr("class").replace( 'zooming ', '' );

    if( direction === "in" ){
        scale       =  1.25;
    }else if(direction === "out"){
        scale       =  0.75;
    }

    map.transition()
        .call(zoom_map.scaleBy, scale);
});


// Slider
function update(year){
     slider.property("value", year);
     d3.select(".year").text(year);
     // honeyyear.style("fill", function(d) {
     //      return color(d.properties.years[year][0].rate)
     };


var slider = d3.select(".slider")
    .append("input")
    .attr("type", "range")
    .attr("min", 2009)
    .attr("max", 2013)
    .attr("step", 1)
    .on("input", function() {
         var year = this.value;
         update(year);
     });

update(2013);

1条回答
淡お忘
2楼-- · 2019-08-01 03:13

d3.json and other fetching tools from D3 makes the fetch and returns a promise that you can use to update your data based on the slider input event. Check this fiddle to see how it works.

http://jsfiddle.net/9rkc0nuL/

const getData = value => d3.json(`https://jsonplaceholder.typicode.com/todos/${value}`)
const slider = document.getElementById('start')

slider.addEventListener('input', event => getData(event.target.value).then(
  data => {
    const display = document.getElementById('display')
    display.innerText = data.title
  }
))

If you are looking for the specific solution to your problem this piece of code should solve it:

let updateYear = year => {
    slider.attr("value", year);
    d3.select(".year").text(year);
}

var slider = d3.select(".slider")
    .append("input")
    .attr("type", "range")
    .attr("min", 2009)
    .attr("max", 2013)
    .attr("step", 1)
    .on("input", function() {
        const value = this.value
        updateYear(this.value)
        d3.json(`data/HoneyProduction-${value}.json`)
            .then(data => {
                // Do whatever you need with your data
            })         
     });

updateYear(2013)
查看更多
登录 后发表回答