<div id="content"></div>
div#choropleth{
  text-align:center;
  align-items:center;
}
.county{
  stroke: rgba(11,156,49, .2)
}
#title{
  font-size:1.5rem;
}

#tooltip{
  position:absolute;
  background:rgba(0, 0, 0, 0.6);
  color:white;
  padding:5px;
  border-radius:10px;
}
a:hover{
  text-decoration:none;
}
#about{
  max-width:992px;
}
#source a{
  fill:blue;
}
#choropleth{
  overflow-x:scroll;
}
// code by : theTradeCoder
// show app
class App extends React.Component{
  render(){
    return (
    <div id="app">
        <Header />
        <ChoroplethMap />
        <About />
        <Footer />           
    </div>
    )
  }
}
// Header code
const Header =()=>{return (
<header className="bg-dark p-2 mb-4">
  <div className='d-flex justify-content-center container'>
    <h1 className="text-light">D3js Choropleth Map</h1>
  </div>
</header>
)};

// Footer code
const Footer =()=>{return (
<footer className="bg-dark p-3 mt-4">
  <div className='d-flex justify-content-center align-items-center container'>
    <span className="text-light text-center">Developer :
      <a href="https://www.linkedin.com/in/thetradecoder/" className="nav-link">theTradeCoder</a>        
      </span>
  </div>
</footer>
)};

// About code
const About = () =>{
  return(
  <div id="about" className="container mt-3 pt-3 bt-3 mb-3">
      <h2>About : </h2>
      <p><a href="https://freecodecamp.com">- freeCodeCamp Data Visualization Project</a><br/>
      - Data Visualization with Choropleth Map <br/>
      - A D3js and Reactjs Project
      </p>
  </div>
  )
};

// ChoroplethMap code
// d3js data presentation 
class ChoroplethMap extends React.Component{
  render(){
    const urlEdu = "https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/for_user_education.json";
    const urlCounty = "https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/counties.json";
    d3.queue()
    .defer(d3.json, urlEdu)
    .defer(d3.json, urlCounty)
    .await((err, edu, county)=>{
      const w = 992;
      const h = 650;
      const pad = 60;
      const svg = d3.select('#choropleth')
      .append('svg')
      .attr('width', w)
      .attr('height', h)
      .style('background-color', '#DDE8DA');
      
     const tooltip = d3.select('#choropleth')
     .append('div')
     .attr('id','tooltip')
     .style('opacity', 0);
      
      const path = d3.geoPath();
      const bachelorHigherHighLow = d3.extent(edu.map(d=>d.bachelorsOrHigher));
     // console.log(bachelorHigherHighLow);
      const low = bachelorHigherHighLow[0];
      const high = bachelorHigherHighLow[1];
      
      const topoData = topojson.feature(county, county.objects.counties).features;
      
      const colors = d3.scaleThreshold()
      .domain(d3.range(low, high, (high-low)/9))
      .range(d3.schemeGreens[9]);
      
      svg.append('g')
      .selectAll('path')
      .data(topoData)
      .enter()
      .append('path')
      .attr('class', 'county')
      .attr('d', path)      
      .attr('fill', (d)=>{
        let eduMatch = edu.filter((data)=>data.fips==d.id);
        if(eduMatch[0]){
          return colors(eduMatch[0].bachelorsOrHigher)
        }
        return 0;
      })
      .attr('data-fips', (d)=>d.id)
      .attr('data-education', (d)=>{
        let eduMatch = edu.filter((data)=>data.fips==d.id);
        if(eduMatch[0]){
          return eduMatch[0].bachelorsOrHigher
        }
        return 0;
      })
      .on('mouseover', (d,i)=>{
        tooltip.style('opacity', 1)
        .style('left', (d3.event.pageX+30)+'px')
        .style('top', (d3.event.pageY-20)+'px')
        .attr('data-education', ()=>{
         let eduMatch = edu.filter(data=>data.fips==d.id);
          if(eduMatch[0]){
            return eduMatch[0].bachelorsOrHigher;
          }
          return 0;
        })
        .html(()=>{
          let eduMatch = edu.filter(data=>data.fips==d.id);
          if(eduMatch[0]){return `${eduMatch[0].area_name}, ${eduMatch[0].state}<br/> ${eduMatch[0].bachelorsOrHigher}%`}
          return 0;
        })
        
      })
      .on('mouseout', (d)=>{
        tooltip.style('opacity', 0)
        .style('left', 0)
        .style('top', 0)
       });
      
      
      svg.append('text')
      .attr('id', 'title')
      .text('US Educational Attainment')
      .attr('x', w/3)
      .attr('y', 25);
      
      
      svg.append('text')
      .attr('id', 'description')
      .text("Percentage of adults age 25 and older with a bachelor\'s degree or higher (2010-2014)")
      .attr('x', w/5)
      .attr('y', 40);
      
       // set color scale
      const xScale =  d3.scaleLinear()
      .domain([low, high])
      .range([pad, w/2-pad]);
      
      const xAxis = d3.axisBottom(xScale)
      .tickSize(25)
      .tickFormat(d=>Math.round(d)+'%')
      .tickValues(colors.domain());
      
      svg.append('g')
      .attr('id', 'x-axis')
      .attr('transform', 'translate('+(w/3)+','+(h-pad)+')')
      .call(xAxis)
      .select('.domain')
      .remove();

      
      // set legend om color scale
      svg.append('g')
      .attr('id', 'legend')
      .selectAll('rect')
      .data(colors.range().map(d=>d))
      .enter()
      .append('rect')
      .attr('height', 15)
      .attr('width', (d, i)=>(w/2-pad*2)/9) // 9 taken from schemeGreens 
      .attr('fill', d=>d)
      .attr('x', (d,i)=>(pad+w/3)+(i*((w/2-pad*2)/9)))
      .attr('y', h-pad);
      
      svg.append('g')
      .attr('id', 'source')
      .append('text')
      .html('Data Source: <a href="https://cdn.freecodecamp.org/testable-projects-fcc/data/choropleth_map/for_user_education.json">U.S. Education Data</a>')
      .attr('x', pad)
      .attr('y', h-pad+10)
      
    }) // closing await 
   
     
    
    return(
    <div id="choropleth">
    </div>
    )
  }
}

ReactDOM.render(<App />, document.getElementById('content'))
View Compiled

External CSS

  1. https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.5.0/css/bootstrap.min.css

External JavaScript

  1. https://cdnjs.cloudflare.com/ajax/libs/d3/5.16.0/d3.min.js
  2. https://cdnjs.cloudflare.com/ajax/libs/d3-queue/3.0.7/d3-queue.min.js
  3. https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js
  4. https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js
  5. https://d3js.org/d3.v4.min.js
  6. https://d3js.org/topojson.v2.min.js
  7. https://d3js.org/d3-scale-chromatic.v1.min.js
  8. https://cdn.freecodecamp.org/testable-projects-fcc/v1/bundle.js