D3.js + Google Maps - Vietnam: area, population and population density in 2011 by province

by Kieran Dang

HTML

<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.1.0/css/font-awesome.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js"></script>
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp&amp;libraries=visualization,places,geometry&amp;key=AIzaSyDEs_f9851pMMMKuusSpwovQjoBHVkOk5E&amp;language=vi"></script>
<div id="wrapper">
  <div class="scatterplot-wrapper"></div>
  <div class="legend-wrapper">
    <h1>Population density</h1>
    <div class="index-level-bar">
      <div class="bar-legend"></div>
      <div class="bar-label">Lower denisty</div>
      <div class="bar-label second">Higher denisty</div>
      <div style="clear: both;"></div>
    </div>
  </div>
  <div id="info">
    <div id="header-info">
      <span>Area, population and population density in 2011 by province (https://www.gso.gov.vn)</span>
      <table id="feature-info">
        <tbody>
          <tr>
            <th class="name" style="width:25%;">Name</th>
            <th class="population" style="width:20%;">Population</th>
            <th class="area" style="width:15%;">Area</th>
            <th class="density" style="width:15%;">Density</th>
            <th class="capital" style="width:25%;">Capital</th>
          </tr>
        </tbody>
      </table>
    </div>
    <div id="loading" class="hidden">
      <a class="fa fa-spinner"></a>
    </div>
  </div>
  <div id="map-canvas"></div>
</div>

CSS

#wrapper {
  padding-top: 15px;
}

.bar-label {
  float: left;
  margin-top: 5px;
  line-height: 12px;
  font-size: 12px;
  font-family: 'Helvetica';
}

.bar-label.second {
  float: right;
}

table {
  display: table;
  border-collapse: separate;
  border-spacing: 2px;
  border-color: grey;
  padding: 0px;
}

th {
  font-size: 18px;
  font-weight: bold;
  text-align: left;
}

th {
  font-size: 16px;
  font-family: sans-serif;
  font-weight: normal;
  text-align: left;
}

th.name {
  color: #E7BA52;
}

th.area {
  color: #FC9E27;
}

th.population {
  color: #3A7FA3;
}

th.density {
  color: #B5CF6B;
}

th.capital {
  color: #D6616B;
}

th,
td {
  color: #555;
}

.h2,
h2 {
  font-size: 30px;
}

.h1,
.h2,
.h3,
h1,
h2,
h3 {
  margin-top: 20px;
  margin-bottom: 10px;
  color: #555;
}

.scatterplot-wrapper {
  position: fixed;
  z-index: 2;
  left: 15px;
  bottom: 300px;
  width: 270px;
  text-align: center;
}

.axis path,
.axis line {
  fill: none;
  stroke-width: 1px;
  stroke: #e7e7e7;
  stroke-opacity: .5;
  shape-rendering: crispEdges;
}

.axis text {
  line-height: 12px;
  font-size: 12px;
  font-family: 'Helvetica';
  fill: #666;
}

.label {
  line-height: 12px;
  font-size: 12px;
  font-family: 'Helvetica';
}

.scatterplot-wrapper circle {
  fill-opacity: .75;
}

.scatterplot-wrapper circle:hover {
  fill-opacity: 1;
}

.bubble.highlight {
  stroke: #3A7FA3;
  fill-opacity: .75;
}

.legend-wrapper {
  position: fixed;
  z-index: 2;
  left: 15px;
  bottom: 150px;
  width: 270px;
  text-align: center;
}

.legend-wrapper h1 {
  font: 22px 'oswaldregular';
}

#map-canvas {
  height: 600px;
  width: 900px;
  margin-left: 290px;
}

#info {
  height: 90px;
}

#header-info {
  margin-left: 290px;
}

#feature-info {
  width: 650px;
}

#loading {
  width: 50px;
  margin: 0 auto;
}

.states {
  fill: #ccc;
  stroke: #fff;
  stroke-linejoin: round;
}

.feature,
.legend {
  cursor: pointer;
}


/* On mouse hover, lighten state color */

path:hover {
  fill-opacity:...

JavaScript

if (!google.maps.Polygon.prototype.getBounds) {
  google.maps.Polygon.prototype.getBounds = function(latLng) {
    var bounds = new google.maps.LatLngBounds();
    var paths = this.getPaths();
    var path;

    for (var p = 0; p < paths.getLength(); p++) {
      path = paths.getAt(p);
      for (var i = 0; i < path.getLength(); i++) {
        bounds.extend(path.getAt(i));
      }
    }

    return bounds;
  }
}

if (!google.maps.Polyline.prototype.getBounds) {
  google.maps.Polyline.prototype.getBounds = function(latLng) {
    var bounds = new google.maps.LatLngBounds();
    var path = this.getPath();
    for (var i = 0; i < path.getLength(); i++) {
      bounds.extend(path.getAt(i));
    }
    return bounds;
  }
}

var _gp = {
  map: null,
  infoWindow: new google.maps.InfoWindow,

  // Strict bounds, by Geocoder (sw, ne)
  /*
  var geocoder = new google.maps.Geocoder();
  geocoder.geocode({
      'address' : 'Viet Nam'
  }, function (results, status) {
      if (status == google.maps.GeocoderStatus.OK) {
          var viewport = results[0].geometry.viewport;
          console.log(viewport);
      }
  });
  */
  strictBounds: new google.maps.LatLngBounds(
    new google.maps.LatLng(8.412729, 102.14441),
    new google.maps.LatLng(23.393395, 109.468977)),

  rangeColor: function(className) {
    var colors = {
      'q0-9': '#F7FCF0',
      'q1-9': '#E0F3DB',
      'q2-9': '#CCEBC5',
      'q3-9': '#A8DDB5',
      'q4-9': '#BBF3FF',
      'q5-9': '#A1D9FF',
      'q6-9': '#87BFFF',
      'q7-9': '#6EA6E7',
      'q8-9': '#558DCE'
    };

    return colors[className];
  },

  initialize: function(options) {
    var self = this;

    self.tooltip = d3.select('body').append('div')
      .attr('class', 'tooltip')
      .style('opacity', 0);

    self.quantiles = d3.scale.quantile()
      .range(d3.range(9).map(function(i) {
        return 'q' + i + '-9';
      }));

    var legend = d3.select('.bar-legend').append('svg')
      .attr('width', 240)
     ...