Q:

Add a legend on a Choropleth map in D3.JS

I am building a Choropleth map in D3.js.
Considering that a quantize scale divides values into several discrete buckets, assigning a color to each based on which bucket it falls into. As one can get from the code bellow, I have decided to pass an array of colors, knowing that the scale function then creates a bucket for each color.
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)'
]);

As I intend to build a legend based on the "buckets" that scaleQuantize creates from the data, any advice on how to do this appreciated.
The legend that I am trying to reproduce is this one: https://beta.observablehq.com/@mbostock/d3-choropleth

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://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);
});

style.css
#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;}
}

A:

Solved by updating the end of the d3.json with the following:
    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);

