block by zanarmstrong a40f50dc6a16844d5346

practicing making a map

Full Screen

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

.subunit.SWE { fill: #8fb1e6; }
.subunit.NOW { fill: white; }
.subunit.FIN { fill: white; }
.subunit.DNK { fill: white; }
.subunit.DNB { fill: white; }

.place,
.place-label {
  fill: #444;
}

.subunit-label {
  fill: black;
  fill-opacity: .5;
  font-size: 20px;
  font-weight: 300;
  text-anchor: middle;
}

.subunit-boundary {
  fill: none;
  stroke: #777;
}


text {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 10px;
  pointer-events: none;
}

</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>
<script>

var width = 960,
    height = 1160;

var svg = d3.select("body").append("svg")
    .attr("width", width)
    .attr("height", height);

d3.json("scandinavia.json", function(error, sweden) {
  	if (error) return console.error(error);

	var subunits = topojson.feature(sweden, sweden.objects.subunits)

	// remove other territories from naming, etc
    var featureData = topojson.feature(sweden, sweden.objects.subunits).features
    	.filter(function(d){
    		if (Array('DNK', 'FIN', 'NOW', 'SWE', 'DNB').indexOf(d.id)> -1){return d}
    	});

    // define our projection - and where to crop the world
	var projection = d3.geo.albers()
    .center([15, 63])
    .rotate([0, 0])
    .parallels([65, 70])
    .scale(3000)
    .translate([width / 2, height / 2]);

    // generate path, using the albers projection defined above
	var path = d3.geo.path()
    .projection(projection);
  
  	// draw paths, note that this still includes things like svalbard,
  	// but it's out of the range of the map
  	svg.append("path")
    .datum(subunits)
    .attr("d", path);

    // define sub-units, so that we can use classes to style
    svg.selectAll(".subunit")
    .data(featureData)
    .enter().append("path")
    .attr("class", function(d) { return "subunit " + d.id; })
    .attr("d", path);

    // define the boundary of countries so we can style
    svg.append("path")
    .datum(featureData)
    .attr("d", path)
    .attr("class", "subunit-boundary");

    // introduce places (cities)
    svg.append("path")
    .datum(topojson.feature(sweden, sweden.objects.places))
    .attr("d", path)
    .attr("class", "place");

    // label the cities
    svg.selectAll(".place-label")
    .data(topojson.feature(sweden, sweden.objects.places).features)
  	.enter().append("text")
    .attr("class", "place-label")
    .attr("transform", function(d) {return "translate(" + projection(d.geometry.coordinates) + ")"; })
    .attr("dy", ".35em")
    .text(function(d) { return d.properties.name; });

    // locate the labels
    svg.selectAll(".place-label")
    .attr("x", function(d) { return d.geometry.coordinates[0] > 15.8 ? 6 : -6; })
    .style("text-anchor", function(d) { return d.geometry.coordinates[0] > 15.8 ? "start" : "end"; });

    // label the countries
    svg.selectAll(".subunit-label")
    .data(featureData.filter(function(d) {if(d.id != 'DNB'){return d}}))
  	.enter().append("text")
    .attr("class", function(d) {return "subunit-label " + d.id; })
    .attr("transform", function(d) {
    	var centerLabel = path.centroid(d);
    	if(d.id == 'NOW'){centerLabel = [centerLabel[0] - 30, centerLabel[1] + 130]}; 
    	if(d.id == 'SWE'){centerLabel = [centerLabel[0] - 30, centerLabel[1] + 15]}; 
    	console.log(centerLabel, path.centroid(d), d)
    	return "translate(" + centerLabel + ")"; })
    .attr("dy", ".35em")
    .text(function(d) {return d.properties.name; });

});

</script>

README.txt

Goal: practice getting data from other sources and making a map using d3 and topo.json

Code adapted from Mike Bostock's example: "Let's make a map": http://bost.ocks.org/mike/map/.  
Much of the code was copied directly.  
Adapted where appropriate to get data about Scandinavia and to highlight only Sweden on the map.  

One challenge was handling the many very small territories under Scandinavian rule around the world, 
with different country codes.  

Source data from Natural Earth Data: http://www.naturalearthdata.com/

ogr2ogr from GDAL and topoJSON from node.js were used to process data.

# to select only Swedish place names
ogr2ogr \
  -f GeoJSON \
  -where "ISO_A2 IN ('SE')" \
  places.json \
  ne_10m_populated_places.shp

# to get geo data for Sweden, Norway, Denmark, and Finland
ogr2ogr \
  -f GeoJSON \
  -where "ADM0_A3 IN ('SWE', 'NOR', 'DNK', 'FIN')" \
  subunits.json \
  ne_10m_admin_0_map_subunits.shp

# to combine that two files into one
topojson \
  -o scandinavia.json \
  --id-property SU_A3 \
  --properties name=NAME \
  -- \
  subunits.json \
  places.json
  
Note that original dbf files are corrupted for non-English characters. In this instance, I corrected the spellings manually.  More information here: http://www.naturalearthdata.com/forums/topic/bad-adm1name-encoding-in-version-3-0-0-and-missing-diacritics-in-name/