<h1>Computer and Mathematical Occupations: May 2014 <a href="http://www.bls.gov/oes/current/oes150000.htm" target="blank">[source]</a></h1>
$stroke-width: 4;
$scale: 2;

.svg {
  position: absolute;
  top: 50%; left: 50%;
  transform: translate3d(-50%,-50%, 0);
  g {
    cursor: pointer;
    polygon { 
      stroke: white; 
      stroke-width: $stroke-width;
      transform: scale(1);
      opacity: 0.5;
      transition: opacity 200ms ease-in;
    }
    text { 
      text-anchor: middle;
      alignment-baseline: middle;
      transition: opacity 200ms ease-in;
    }
    text:not(.data) {
      fill: white;
    }
    text.data {
      fill: hsl(180,60%,60);
      font-weight: 100; 
      &.title { 
        font-size: 20px;
        fill: hsl(180,60%,30);
      }
    }
    .state-name, .data { opacity: 0; }
    .state-abbr { opacity: 1; }
    &.hover {
      .state-name, .data { animation: fade-in 300ms ease-in 200ms forwards; }
      .state-abbr { animation: fade-out 300ms ease-in 200ms forwards; }
      polygon {
        opacity: 1;
        transition: fill 200ms ease-in;
        animation: scale-polygon 300ms ease-in forwards;
      }
    }
  }
}

@keyframes scale-polygon {
  from { transform: scale(1); stroke-width: $stroke-width; }
  to   { transform: scale($scale); stroke-width: $stroke-width / $scale; }
}

@keyframes fade-in  { to { opacity: 1; } }
@keyframes fade-out { to { opacity: 0; } }

h1 {
  color: #999;
  font-weight: 100;
  text-align: center;
  font-size: 1em;
  position: fixed;
  top: 0;
  left: 0; width: 100%;
  z-index: 1;
  background: rgba(255,255,255,0.8);
  padding: 0.6em 0;
  a { 
    color: hsl(180,60%,70); 
    text-decoration: none; 
    font-size: 0.7em;
  }
}
View Compiled
// launchpad
function initializeMap() {
  // creating base svg
  var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");

  // hexagon shape variables
  var hex_di  = 100,
      // radius
      hex_rad = hex_di / 2,
      // apothem
      hex_apo = hex_rad * Math.cos(Math.PI / 6),
      // matrix defining state placement
      states_grid = usStateMatrix(),
      // data
      states_data = usStateData(),
      // rows we'll generate
      rows 		= states_grid.length,
      // columns we'll generate
      cols 		= states_grid[0].length,
      // stroke width around hexagon
      stroke  = 4,
      // the hover state zoom scale
      scale   = 2,
      // initial x
      x				= hex_rad * scale / 2 + stroke * scale,
      // initial y
      y				= hex_rad * scale + stroke * scale,
      // side length in pixels
      side    = Math.sin(Math.PI / 6) * hex_rad,
      // height of map in pixels
      height	= (hex_di - side) * rows + side + hex_rad * scale + stroke * scale,
      // width of map in pixels
      width		= (hex_apo * 2) * cols + hex_rad * scale + stroke * scale;
  

  // svg attributes
  svg.setAttribute("class", "svg");
  svg.setAttribute("width", "100%");
  svg.setAttribute("height", "100%");
  svg.setAttribute("viewBox", "0 0 " + width + " " + height);
  
  // loop variables
  var offset = false,
      // parsing state data
      states = states_data.states,
      data = states_data.data,
      // initial state index
      state_index = 0;
  
  // getting range of data defaults
  var hourly_mean_wage_max = 0,
      hourly_mean_wage_min = 100,
      annual_mean_wage_max = 0,
      annual_mean_wage_min = 100000,
      jobs_per_1000_max = 0,
      jobs_per_1000_min = 10;
  
  // for each data find max and min
  for(var d = 0; d < data.length; d++) {
    hourly_mean_wage_max = Math.max(hourly_mean_wage_max, data[d].hourly_mean_wage);
    hourly_mean_wage_min = Math.min(hourly_mean_wage_min, data[d].hourly_mean_wage);
    annual_mean_wage_max = Math.max(annual_mean_wage_max, data[d].annual_mean_wage);
    annual_mean_wage_min = Math.min(annual_mean_wage_min, data[d].annual_mean_wage);
    jobs_per_1000_max = Math.max(jobs_per_1000_max, data[d].jobs_per_1000);
    jobs_per_1000_min = Math.min(jobs_per_1000_min, data[d].jobs_per_1000);
  }
  
  // getting differences in range
  var hourly_mean_wage_dif = hourly_mean_wage_max - hourly_mean_wage_min,
      annual_mean_wage_dif = annual_mean_wage_max - annual_mean_wage_min,
      jobs_per_1000_dif = jobs_per_1000_max - jobs_per_1000_min;
  
  // draw grid
  for(var i = 0; i < states_grid.length; i++) {
    var loop_x = offset ? hex_apo * 2 : hex_apo;
    
    var loc_x = x; 
    for(var s = 0; s < states_grid[i].length; s++) {
      // grid plot in 0 and 1 array
      var grid_plot = states_grid[i][s];

      // if we have a plot in the grid
      if (grid_plot != 0) {
        // get the state
        var state = states[state_index];
        
        // lookup data for state
        for(var d = 0; d < data.length; d++) {
          if (data[d].state == state.abbr) {
            state.data = data[d];
          }
        }

        // ratio for fill on polygon
        var ratio = (state.data.annual_mean_wage - annual_mean_wage_min) / annual_mean_wage_dif;    
        
        // create the hex group
        var hexGroup = getHexGroup(svg, loc_x + loop_x , y, hex_rad, state, ratio, width, state.data);
      
        // have to reappend element on hover for stacking
        hexGroup.addEventListener("mouseenter", function () {
          var self = this;
          self.setAttribute("class", "hover");
          self.remove();
          svg.appendChild(self);
        });
        // clear class
        hexGroup.addEventListener("mouseleave", function () {
          this.setAttribute("class", "");
        });
        
        // append the hex to our svg
        svg.appendChild(hexGroup);
        // increase the state index reference
        state_index++;
      }

      // move our x plot to next hex position
      loc_x += hex_apo * 2; 
    }
    // move our y plot to next row position
    y += hex_di * 0.75; 
    // toggle offset per row
    offset = !offset;
  }

  // add svg to DOM
  document.body.appendChild(svg);
}

// run the initialization script
initializeMap();

// individual hex calculations
function getHexGroup(svg,x,y,r,state,ratio,width,data) {
  var svgNS  = svg.namespaceURI, // svgNS for creating svg elements
  		group	 = document.createElementNS(svgNS, "g"),
  		hex 	 = document.createElementNS(svgNS, "polygon"),
  		abbr 	 = document.createElementNS(svgNS, "text"),
  		name 	 = document.createElementNS(svgNS, "text"),
      pi_six = Math.PI/6,
      cos_six = Math.cos(pi_six),
      sin_six = Math.sin(pi_six);

  // hexagon polygon points
  var hex_points = [
    [x, y - r].join(","),
    [x + cos_six * r, y - sin_six * r].join(","),
    [x + cos_six * r, y + sin_six * r].join(","),
    [x, y + r].join(","),
    [x - cos_six * r, y + sin_six * r].join(","),
    [x - cos_six * r, y - sin_six * r].join(",")
  ]
  
  // hexagon fill based on ratio
  var fill = "hsl(180,60%," + ((1 - ratio) * 60 + 20) + "%)";
    
  hex.setAttribute("points", hex_points.join(" "));
  hex.setAttribute("fill", fill);
  hex.style.webkitTransformOrigin = hex.style.transformOrigin = x + 'px ' + y + 'px';
  
  abbr.setAttribute("class", "state-abbr");
  abbr.setAttribute("x", x);
  abbr.setAttribute("y", y);
  abbr.textContent = state.abbr;
  
  name.setAttribute("class", "state-name");
  name.setAttribute("x", x);
  name.setAttribute("y", y);
  name.textContent = state.name;
  
  // loop through data points
  var index = 1,
      // lineheight of data text
      line_height = 20,
      // starting y of data
      start_y = 60;
  
  for(var key in data) {
    var text = document.createElementNS(svgNS, "text");
    text.setAttribute("x", width / 2);
    text.setAttribute("y", index * line_height + start_y);
    if(key != 'state') {
      text.setAttribute("class", "data");
      text.textContent = keyToName(key) + ": " + data[key];
    } else {
      text.setAttribute("class", "data title");
      text.textContent = state.name;
    }
    group.appendChild(text);
    index++;
  }
  
  group.appendChild(hex);  
  group.appendChild(abbr);  
  group.appendChild(name);  
  
  return group;
}

function keyToName(str) {
  return str.replace(/_/g,' ')
    .replace(/\w\S*/g, function(txt) {
      return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase();
  });
}

function usStateMatrix() {
  return [
    [1,0,0,0,0,0,0,0,0,0,0,1],
    [0,0,0,0,0,0,0,0,0,1,1,0],
    [0,1,1,1,1,1,0,1,0,1,1,1],
    [0,1,1,1,1,1,1,1,1,1,1,0],
    [0,1,1,1,1,1,1,1,1,1,1,0],
    [0,1,1,1,1,1,1,1,1,1,0,0],
    [0,0,0,1,1,1,1,1,1,0,0,0],
    [1,0,0,0,1,0,0,1,0,0,0,0]
  ]
}

function usStateData() {
  return {
    states: [
      { abbr: "AK", name: "Alaska" },
      { abbr: "ME", name: "Maine"},

      { abbr: "VT", name: "Vermont" },
      { abbr: "NH", name: "New Hampshire"},

      { abbr: "WA", name: "Washington" },
      { abbr: "MT", name: "Montana" },
      { abbr: "ND", name: "North Dakota" },
      { abbr: "MN", name: "Minnesota" },
      { abbr: "WI", name: "Wisconsin" },
      { abbr: "MI", name: "Michigan" },
      { abbr: "NY", name: "New York" },
      { abbr: "MA", name: "Massachusetts" },
      { abbr: "RI", name: "Rhode Island"},

      { abbr: "ID", name: "Idaho" },
      { abbr: "WY", name: "Wyoming" },
      { abbr: "SD", name: "South Dakota" },
      { abbr: "IA", name: "Iowa" },
      { abbr: "IL", name: "Illinois" },
      { abbr: "IN", name: "Indiana" },
      { abbr: "OH", name: "Ohio" },
      { abbr: "PA", name: "Pennsylvania" },
      { abbr: "NJ", name: "New Jersey" },
      { abbr: "CT", name: "Connecticut"},

      { abbr: "OR", name: "Oregon" },
      { abbr: "NV", name: "Nevada" },
      { abbr: "CO", name: "Colorado" },
      { abbr: "NE", name: "Nebraska" },
      { abbr: "MO", name: "Missouri" },
      { abbr: "KY", name: "Kentucky" },
      { abbr: "WV", name: "West Virgina" },
      { abbr: "VA", name: "Virginia" },
      { abbr: "MD", name: "Maryland" },
      { abbr: "DE", name: "Delaware"},

      { abbr: "CA", name: "California" },
      { abbr: "UT", name: "Utah" },
      { abbr: "NM", name: "New Mexico" },
      { abbr: "KS", name: "Kansas" },
      { abbr: "AR", name: "Arkansas" },
      { abbr: "TN", name: "Tennessee" },
      { abbr: "NC", name: "North Carolina" },
      { abbr: "SC", name: "South Carolina" },
      { abbr: "DC", name: "District of Columbia"},

      { abbr: "AZ", name: "Arizona" },
      { abbr: "OK", name: "Oklahoma" },
      { abbr: "LA", name: "Louisiana" },
      { abbr: "MS", name: "Mississippi" },
      { abbr: "AL", name: "Alabama" },
      { abbr: "GA", name: "Georgia"},

      { abbr: "HI", name: "Hawaii" },
      { abbr: "TX", name: "Texas" },
      { abbr: "FL", name: "Florida" }
    ],
    // Computer and Mathematical Occupations May 2014
    // http://www.bls.gov/oes/current/oes150000.htm
    data: [
      { state: "AL", hourly_mean_wage: 25.6, annual_mean_wage: 53250, jobs_per_1000: 0.348 },
      { state: "AK", hourly_mean_wage: 28.78, annual_mean_wage: 59870, jobs_per_1000: 0.284 },
      { state: "AZ", hourly_mean_wage: 30.93, annual_mean_wage: 64340, jobs_per_1000: 0.988 },
      { state: "AR", hourly_mean_wage: 28.27, annual_mean_wage: 58810, jobs_per_1000: 0.432 },
      { state: "CA", hourly_mean_wage: 38.23, annual_mean_wage: 79520, jobs_per_1000: 1.255 },
      { state: "CO", hourly_mean_wage: 29.71, annual_mean_wage: 61800, jobs_per_1000: 1.231 },
      { state: "CT", hourly_mean_wage: 33.03, annual_mean_wage: 68710, jobs_per_1000: 0.803 },
      { state: "DE", hourly_mean_wage: 39.15, annual_mean_wage: 81440, jobs_per_1000: 0.917 },
      { state: "DC", hourly_mean_wage: 37.84, annual_mean_wage: 78710, jobs_per_1000: 1.845 },
      { state: "FL", hourly_mean_wage: 29.19, annual_mean_wage: 60720, jobs_per_1000: 0.989 },
      { state: "GA", hourly_mean_wage: 35.96, annual_mean_wage: 74790, jobs_per_1000: 0.739 },
      { state: "HI", hourly_mean_wage: 35.46, annual_mean_wage: 73760, jobs_per_1000: 0.557 },
      { state: "ID", hourly_mean_wage: 24.18, annual_mean_wage: 50300, jobs_per_1000: 1.243 },
      { state: "IL", hourly_mean_wage: 31.33, annual_mean_wage: 65160, jobs_per_1000: 0.714 },
      { state: "IN", hourly_mean_wage: 25.96, annual_mean_wage: 53990, jobs_per_1000: 0.666 },
      { state: "IA", hourly_mean_wage: 29.43, annual_mean_wage: 61200, jobs_per_1000: 0.569 },
      { state: "KS", hourly_mean_wage: 27.83, annual_mean_wage: 57880, jobs_per_1000: 0.692 },
      { state: "KY", hourly_mean_wage: 24.62, annual_mean_wage: 51220, jobs_per_1000: 0.494 },
      { state: "LA", hourly_mean_wage: 24, annual_mean_wage: 49920, jobs_per_1000: 0.343 },
      { state: "ME", hourly_mean_wage: 24.81, annual_mean_wage: 51600, jobs_per_1000: 0.639 },
      { state: "MD", hourly_mean_wage: 36.28, annual_mean_wage: 75460, jobs_per_1000: 1.548 },
      { state: "MA", hourly_mean_wage: 37.1, annual_mean_wage: 77170, jobs_per_1000: 1.192 },
      { state: "MI", hourly_mean_wage: 29.21, annual_mean_wage: 60760, jobs_per_1000: 0.527 },
      { state: "MN", hourly_mean_wage: 31.91, annual_mean_wage: 66380, jobs_per_1000: 1.151 },
      { state: "MS", hourly_mean_wage: 26.51, annual_mean_wage: 55140, jobs_per_1000: 0.279 },
      { state: "MO", hourly_mean_wage: 27.71, annual_mean_wage: 57630, jobs_per_1000: 0.571 },
      { state: "MT", hourly_mean_wage: 23.44, annual_mean_wage: 48750, jobs_per_1000: 1.195 },
      { state: "NE", hourly_mean_wage: 27.67, annual_mean_wage: 57550, jobs_per_1000: 1.017 },
      { state: "NV", hourly_mean_wage: 28.26, annual_mean_wage: 58780, jobs_per_1000: 0.56 },
      { state: "NH", hourly_mean_wage: 26.9, annual_mean_wage: 55950, jobs_per_1000: 1.254 },
      { state: "NJ", hourly_mean_wage: 33.55, annual_mean_wage: 69780, jobs_per_1000: 0.609 },
      { state: "NM", hourly_mean_wage: 28.98, annual_mean_wage: 60280, jobs_per_1000: 0.439 },
      { state: "NY", hourly_mean_wage: 36.15, annual_mean_wage: 75180, jobs_per_1000: 1.017 },
      { state: "NC", hourly_mean_wage: 30.95, annual_mean_wage: 64370, jobs_per_1000: 0.709 },
      { state: "ND", hourly_mean_wage: 24.24, annual_mean_wage: 50410, jobs_per_1000: 0.488 },
      { state: "OH", hourly_mean_wage: 29.44, annual_mean_wage: 61230, jobs_per_1000: 0.803 },
      { state: "OK", hourly_mean_wage: 25.84, annual_mean_wage: 53740, jobs_per_1000: 0.443 },
      { state: "OR", hourly_mean_wage: 33.29, annual_mean_wage: 69250, jobs_per_1000: 1.708 },
      { state: "PA", hourly_mean_wage: 29.67, annual_mean_wage: 61710, jobs_per_1000: 0.694 },
      { state: "RI", hourly_mean_wage: 33.3, annual_mean_wage: 69260, jobs_per_1000: 0.74 },
      { state: "SC", hourly_mean_wage: 27.7, annual_mean_wage: 57620, jobs_per_1000: 0.451 },
      { state: "SD", hourly_mean_wage: 29.82, annual_mean_wage: 62020, jobs_per_1000: 0.664 },
      { state: "TN", hourly_mean_wage: 27.39, annual_mean_wage: 56980, jobs_per_1000: 0.503 },
      { state: "TX", hourly_mean_wage: 32.21, annual_mean_wage: 66990, jobs_per_1000: 0.85 },
      { state: "UT", hourly_mean_wage: 28.12, annual_mean_wage: 58490, jobs_per_1000: 1.559 },
      { state: "VT", hourly_mean_wage: 31.53, annual_mean_wage: 65570, jobs_per_1000: 1.577 },
      { state: "VA", hourly_mean_wage: 38.79, annual_mean_wage: 80690, jobs_per_1000: 1.233 },
      { state: "WA", hourly_mean_wage: 39.62, annual_mean_wage: 82420, jobs_per_1000: 1.659 },
      { state: "WV", hourly_mean_wage: 20.64, annual_mean_wage: 42940, jobs_per_1000: 0.371 },
      { state: "WI", hourly_mean_wage: 27.14, annual_mean_wage: 56450, jobs_per_1000: 0.759 },
      { state: "WY", hourly_mean_wage: 25.06, annual_mean_wage: 52130, jobs_per_1000: 0.444 }
    ]
  }
}

External CSS

This Pen doesn't use any external CSS resources.

External JavaScript

  1. https://codepen.io/jakealbaugh/pen/WvNjZB.js