block by emeeks e08bf6e5bc77179be90a

Ch. 7, Fig. 17 - D3.js in Action

Full Screen

This is the code for Chapter 7, Figure 17 from D3.js in Action showing how to load TopoJSON data using topojson.js and how to merge features using topojson.merge(). In this example, every country with a centroid above 0° latitude is merged into a single multipolygon and rendered with a gray fill and thick black border.

index.html

<html>
<head>
  <title>D3 in Action Chapter 7 - Example 8</title>
  <meta charset="utf-8" />
<script src="https://d3js.org/d3.geo.projection.v0.min.js" type="text/JavaScript"></script>
<script src="https://d3js.org/queue.v1.min.js" type="text/JavaScript"></script>
<script src="//d3js.org/colorbrewer.v1.min.js" type="text/JavaScript"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js"></script>
</head>
<style>
  svg {
    height: 500px;
    width: 500px;
    border: 1px solid gray;
  }
  
  .countries {
    fill: red;
    fill-opacity: .5;
    stroke: black;
    stroke-width: 1px;
  }

</style>
<body>

<div id="viz">
  <svg>
  </svg>
</div>
<div id="controls" />
</body>
  <footer>
    
<script>
    queue()
    .defer(d3.json, "world.topojson")
    .defer(d3.csv, "cities.csv")
    .await(function(error, file1, file2) { createMap(file1, file2); });
    
  function createMap(countries1, cities) {
    expCountries = countries1;
    var countries = topojson.feature(countries1, countries1.objects.countries)
    width = 500;
    height = 500;
    projection = d3.geo.mollweide()
    .scale(120)
    .translate([width / 2, height / 2])
    .center([20,0])

    geoPath = d3.geo.path().projection(projection);
    
    featureSize = d3.extent(countries.features, function(d) {return geoPath.area(d)});
    countryColor = d3.scale.quantize().domain(featureSize).range(colorbrewer.Reds[7]);

    var graticule = d3.geo.graticule();
    
    d3.select("svg").append("path")
    .datum(graticule)
    .attr("class", "graticule line")
    .attr("d", geoPath)
    .style("fill", "none")
    .style("stroke", "lightgray")
    .style("stroke-width", "1px");

    d3.select("svg").append("path")
    .datum(graticule.outline)
    .attr("class", "graticule outline")
    .attr("d", geoPath)
    .style("fill", "none")
    .style("stroke", "black")
    .style("stroke-width", "1px");

    d3.select("svg").selectAll("path.countries").data(countries.features)
    .enter()
    .append("path")
    .attr("d", geoPath)
    .attr("class", "countries")
    .style("fill", function(d) {return countryColor(geoPath.area(d))})
    .style("stroke-width", 1)
    .style("stroke", "black")
    .style("opacity", .5)

    d3.select("svg").selectAll("circle").data(cities)
    .enter()
    .append("circle")
    .style("fill", "black")
    .style("stroke", "white")
    .style("stroke-width", 1)
    .attr("r", 3)
    .attr("cx", function(d) {return projection([d.y,d.x])[0]})
    .attr("cy", function(d) {return projection([d.y,d.x])[1]})
    
    mergeAt(0);
    
    function mergeAt(boundingBox) {
      
    var filteredCountries = countries1.objects.countries.geometries.filter(function(d) {
      thisCenter = d3.geo.centroid(
      topojson.feature(expCountries, d)
      );
      return thisCenter[1] > boundingBox ? true : null;
    }
      )
    
  d3.select("svg").insert("g", "circle")
        .datum(topojson.merge(countries1, filteredCountries))
        .insert("path")
        .style("fill", "gray")
        .style("stroke", "black")
        .style("stroke-width", "2px")
        .attr("d", geoPath)
    }
  }
  
</script>
  </footer>

</html>

cities.csv

"label","population","country","x","y"
"San Francisco", 750000,"USA",37,-122
"Fresno", 500000,"USA",36,-119
"Lahore",12500000,"Pakistan",31,74
"Karachi",13000000,"Pakistan",24,67
"Rome",2500000,"Italy",41,12
"Naples",1000000,"Italy",40,14
"Rio",12300000,"Brazil",-22,-43
"Sao Paolo",12300000,"Brazil",-23,-46