代码之家  ›  专栏  ›  技术社区  ›  Gonçalo Peres

在d3.js中的Choropleth地图上添加图例

  •  2
  • Gonçalo Peres  · 技术社区  · 5 年前

    我正在d3.js中构建一个合唱团地图。

    考虑到量化尺度将值分为几个离散的桶,根据每个桶所属的桶为每个桶指定颜色。正如我们可以从下面的代码中得到的,我已经决定传递一个颜色数组,知道scale函数然后为每种颜色创建一个bucket。

    var color = d3.scaleQuatize().range([
        'rgb(247,251,255)', 'rgb(222,235,247)', 'rgb(198,219,239)',
        'rgb(158,202,225)', 'rgb(107,174,214)', 'rgb(66,146,198)',
        'rgb(33,113,181)', 'rgb(8,81,156)', 'rgb(8,48,107)'
    ]);
    

    由于我打算基于scaleQuantize从数据中创建的“buckets”构建一个传说,所以关于如何实现这一点的任何建议都是值得赞赏的。

    我想复制的传说是: https://beta.observablehq.com/@mbostock/d3-choropleth


    索引文件

    <!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://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
    
    </head>
    <body>
        <nav class="navbar navbar-default">
            <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>
            <span style="font-size:30px;cursor:pointer" onclick="openNav()">&#9776; Menu</span>
            <br>
            <br>
            <div class="container">
                <a class="navbar-brand" href="#"><img id="logo" src="img/logo.png"></a>
            </div>
        </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>
        <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 href="http://goncaloperes.com/" target="_blank">GoncaloPeres.com</a></b></span><br>
            <span>Data from: <a 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>
    </html>
    

    App.JS

    // Width and height
    var chart_width     =   800;
    var chart_height    =   600;
    var color           =   d3.scaleQuantize().range([
         'rgb(247,251,255)', 'rgb(222,235,247)', 'rgb(198,219,239)',
         'rgb(158,202,225)', 'rgb(107,174,214)', 'rgb(66,146,198)',
         'rgb(33,113,181)', 'rgb(8,81,156)', 'rgb(8,48,107)'
    ]);
    
    
    //Navbar
    function openNav() {
        document.getElementById("mySidenav").style.width = "100%";
    }
    
    function closeNav() {
        document.getElementById("mySidenav").style.width = "0";
    }
    
    
    // Projection
    var projection      =   d3.geoAlbersUsa()
        .translate([ 0,0 ]);
    var path            =   d3.geoPath( projection );
        // .projection( projection );
    
    // Create SVG
    var svg             =   d3.select("#chart")
        .append("svg")
        .attr("width", chart_width)
        .attr("height", chart_height);
    
    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 * 2000;
    
        projection.translate( offset )
            .scale( scale );
    
        svg.selectAll( 'path' )
            .transition()
            .attr( 'd', path );
    
        svg.selectAll( 'circle' )
            .transition()
            .attr( "cx", function(d) {
                return projection([d.lon, d.lat])[0];
            })
            .attr( "cy", function(d) {
                return projection([d.lon, d.lat])[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( 'width', chart_width )
        .attr( 'height', chart_height )
        .attr( 'opacity', 0 );
    
    // Data
    d3.json( 'data/USDepartmentofAgriculture-HoneyProduction-2013_properties.json', function( honey_data ){
        color.domain([
            d3.min( honey_data, function(d){
                return d.honey_producing_colonies;
            }),
            d3.max( honey_data, function(d){
                return d.honey_producing_colonies;
            })
        ]);
    
        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);
                });
            });
    
            // console.log(us_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 );
    
            // draw_cities();
        });
    });
    
    // function draw_cities(){
    //     d3.json( 'data/us-cities.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.lon, d.lat ])[0];
    //             })
    //             .attr( 'cy', function( d ){
    //                 return projection([ d.lon, d.lat ])[1];
    //             })
    //             .attr( 'r', function(d){
    //                 return Math.sqrt( parseInt(d.population) * 0.00005 );
    //             })
    //             .append( 'title' )
    //             .text(function(d){
    //                 return d.city;
    //             });
    //     });
    // }
    
    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 );
    });
    
    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);
    });
    

    样式表

    #intro {
        position: absolute;
        top: 250px;
        left: 125px;
        width: 180px;
        text-align: left;
        color: #B5B5B5;
    }
    
    #intro h2{
        font-family: Oswald;
        font-size: 25px;
        font-weight: 300;
        text-align: center;
        color: white;
        -webkit-margin-before: 0.5em;
        -webkit-margin-after: 0.3em;
    }
    
    #legenda {
        position: absolute;
        top: 250px;
        right: 125px;
        width: 180px;
        text-align: left;
        color: #B5B5B5;
    }
    
    #legenda h4{
        font-family: Oswald;
        font-size: 20px;
        font-weight: 300;
        text-align: center;
        color: white;
        -webkit-margin-before: 0.5em;
        -webkit-margin-after: 0.3em;
    }
    
    body {
        font-size: 11px;
        font-weight: 400;
        font-family: 'Open Sans', sans-serif;
        text-align: center;
        vertical-align: middle;
        background: #111;
        fill: white;
        color: white;
        cursor:default;
    }
    
    .navbar-brand {
        height: 60px;
        padding: 5px 0px;
    }
    
    #chart{
        width: 800px;
        height: 600px;
        background-color: #111;
        margin: 25px auto;
    }
    #buttons{
        width: 800px;
        margin: 25px auto;
        text-align: center;
    }
    button{
        background-color: white;
        color: black;
        width: 100px;
        padding: 10px;
        font-size: 20px;
        text-align: center;
        border: 0;
        outline: 0;
        transition: all .2s linear;
        cursor: pointer;
    }
    button:hover{
        background-color: #50B98C;
    }
    
    #note {
        top: -10px;
        left: 10px;
        font-size: 12px;
        font-weight: 300;
        color: #6E6E6E;
        text-align: center;
    }
    
    a {
        color: #6E6E6E;
    }
    
    
    .sidenav {
        height: 100%;
        width: 0;
        position: fixed;
        z-index: 1;
        top: 0;
        left: 0;
        background-color: black;
        overflow-x: hidden;
        transition: 0.5s;
        padding-top: 60px;
        text-align:center;
    }
    
    .sidenav a {
        padding: 8px 8px 8px 32px;
        text-decoration: none;
        font-size: 25px;
        color: #818181;
        display: block;
        transition: 0.3s;
    
    }
    
    .sidenav a:hover{
        color: #f1f1f1;
    }
    
    .sidenav .closebtn {
        position: absolute;
        top: 0;
        right: 25px;
        font-size: 36px;
        margin-left: 50px;
    }
    
    @media screen and (max-height: 450px) {
        .sidenav {padding-top: 15px;}
        .sidenav a {font-size: 18px;}
    }
    
    1 回复  |  直到 5 年前
        1
  •  1
  •   Gonçalo Peres    5 年前

    通过将d3.json的结尾更新为以下内容解决:

        const x = d3.scaleLinear()
            .domain(d3.extent(color.domain()))
            .rangeRound([600, 860]);
    
        const g = svg.append("g")
            .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);