{"id":48,"date":"2016-11-29T18:22:16","date_gmt":"2016-11-29T18:22:16","guid":{"rendered":"http:\/\/vargas-solar.com\/bigdata-visualisation\/?page_id=48"},"modified":"2016-11-29T19:07:24","modified_gmt":"2016-11-29T19:07:24","slug":"creating-a-map-with-d3","status":"publish","type":"page","link":"http:\/\/vargas-solar.com\/bigdata-visualisation\/creating-a-map-with-d3\/","title":{"rendered":"Creating a Map with D3"},"content":{"rendered":"<p style=\"text-align: justify;\">The objective of this exercise is to show you how to create a map using <a href=\"http:\/\/d3js.org\/\">D3.js<\/a> and <a href=\"https:\/\/github.com\/topojson\/topojson\">TopoJSON<\/a>, \u00a0a lighter version of <a href=\"https:\/\/en.wikipedia.org\/wiki\/GeoJSON\">GeoJSON<\/a>.<\/p>\n<p style=\"text-align: justify;\"><strong>Requirements<\/strong><\/p>\n<ul style=\"text-align: justify;\">\n<li><a href=\"https:\/\/nodejs.org\/\">Node<\/a><\/li>\n<li><a href=\"http:\/\/www.gdal.org\/\">Geospatial Data Abstraction Library<\/a> (GDAL)<\/li>\n<li><a href=\"http:\/\/www.naturalearthdata.com\/\">Natural Earth<\/a> data (provided during the course)<\/li>\n<li><a href=\"https:\/\/stedolan.github.io\/jq\/\">JQ<\/a>\u00a0\u00a0JSON processor<\/li>\n<li><a href=\"https:\/\/github.com\/topojson\/topojson\">TopoJSON<\/a> (v. 1x) \u00a0&amp;\u00a0<a href=\"https:\/\/github.com\/indexzero\/http-server\">http-server<\/a> (node packages)<\/li>\n<\/ul>\n<h3 style=\"text-align: justify;\"><strong>Obtaining and preparing the data<\/strong><\/h3>\n<p style=\"text-align: justify;\">The first step for creating a map is to obtain earth data. A great resource for this is <a href=\"http:\/\/www.naturalearthdata.com\/\">Natural Earth<\/a>. In this exercise, we will create a map of United Kingdom using the following Natural Earth datasets:<\/p>\n<ul style=\"text-align: justify;\">\n<li><a href=\"http:\/\/www.naturalearthdata.com\/downloads\/10m-cultural-vectors\/10m-admin-0-details\/\">Admin 0 &#8211; Details &#8211; map subunits<\/a> (scale 1:10m)<\/li>\n<li><a href=\"http:\/\/www.naturalearthdata.com\/downloads\/10m-cultural-vectors\/10m-populated-places\/\">Populated Places<\/a> (scale 1:10m)<\/li>\n<\/ul>\n<p style=\"text-align: justify;\">The former contains polygons representing subdivisions of 197 countries; the latter names and locations of populated places.<\/p>\n<blockquote><p><em>For simplicity, we are providing both datasets as part of the exercise material.<\/em><\/p><\/blockquote>\n<p style=\"text-align: justify;\">The Natural Earth datasets contain several files which are not adapted for the web. The most important of these files is called <strong>shapefile<\/strong> (\u2018.shp\u2019). Thus, the next step consists in transforming the shapefiles (\u2018.shp\u2019) into GeoJSON as follows:<\/p>\n<pre><strong>ogr2ogr \\\r\n \u00a0 -f GeoJSON \\\r\n \u00a0 -where \"ADM0_A3 IN ('GBR', 'IRL')\" \\\r\n \u00a0 subunits.json \\\r\n \u00a0data\/map_subunits\/ne_10m_admin_0_map_subunits.shp<\/strong><\/pre>\n<p style=\"text-align: justify;\">This instruction uses the <strong>ogr2ogr <\/strong>tool of the GDAL library and creates a file called <strong>subunits.json<\/strong> containing only the polygons of UK and Ireland. The filter is done by passing a conditional SQL expression in \u2014<em>where<\/em> option. In the filter expression, ADM0 refers to Admin-0, the highest level administrative boundaries, and A3 refers to <a href=\"http:\/\/en.wikipedia.org\/wiki\/ISO_3166-1_alpha-3\">ISO 3166-1 alpha-3<\/a> country codes. Although the objective of the exercise is to map only the United Kingdom, we need the information about <a href=\"http:\/\/en.wikipedia.org\/wiki\/Ireland\">Ireland<\/a> to portray land accurately. Otherwise, we would give the impression that Northern Ireland is an island.<\/p>\n<p style=\"text-align: justify;\">The <strong>subunits.json<\/strong> file contains more information than the polygons delimitating UK and Ireland. You can see this by navigating through the GeoJSON object contained in the file:<\/p>\n<pre><strong>jq '.features[0].properties' subunits.json<\/strong><\/pre>\n<p style=\"text-align: justify;\">This instruction projects the attributes associated to a country subdivision (<em>i.e., subunit<\/em>) using <strong>JQ query expression<\/strong>.<\/p>\n<blockquote><p><em>The JQ has a lot of nice features. For instance, the following example collects all the attributes (keys) of the objects contained in the GeoJSON object:<\/em><\/p>\n<p><em>\u00a0 \u00a0 \u00a0jq &#8216;.. | objects | keys[]&#8217; subunits.json<\/em><\/p>\n<p><em>See the manual for more information.<\/em><\/p><\/blockquote>\n<p style=\"text-align: justify;\">The map we are creating will include the names of the main cities of United Kingdom. This information is contained in the <strong>ne_10m_populated_places.shp<\/strong>. Thus, the next step consists in transforming and filtering the populated places file keeping only the information related to UK.<\/p>\n<pre>ogr2ogr \\\r\n\u00a0 -f GeoJSON \\\r\n\u00a0 -where \"ISO_A2 = 'GB' AND SCALERANK &lt; 8\" \\\r\n\u00a0 places.json \\\r\n\u00a0 data\/populated_places\/ne_10m_populated_places.shp<\/pre>\n<p style=\"text-align: justify;\">The places\u2019 properties are (somewhat arbitrarily) different, so the where clause of this instructions refers to <strong>ISO_A2<\/strong> instead of <strong>ADM0_A3<\/strong>. The SCALERANK filter further whittles the labels down to major cities.<\/p>\n<h3 style=\"text-align: justify;\"><strong>File reduction<\/strong><\/h3>\n<p style=\"text-align: justify;\">GeoJSON files are usually too big for the web because they contain a lot of redundant information. TopoJSON in contrast, an extension of GeoJSON, only contains information about the topology, reducing the file size up to 80% (see figure below).<\/p>\n<p style=\"text-align: justify;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-53\" src=\"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/imgserv-filesize-300x58.png\" alt=\"imgserv-filesize\" width=\"574\" height=\"111\" srcset=\"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/imgserv-filesize-300x58.png 300w, http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/imgserv-filesize-768x148.png 768w, http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/imgserv-filesize-624x121.png 624w, http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/imgserv-filesize.png 781w\" sizes=\"auto, (max-width: 574px) 100vw, 574px\" \/><\/p>\n<p style=\"text-align: justify;\">The following instruction shows you how to transform a GeoJSON file into TopoJSON:<\/p>\n<pre><strong>topojson \\<\/strong>\r\n<strong>\u00a0 -o uk.json \\<\/strong>\r\n<strong>\u00a0 --id-property SU_A3 \\<\/strong>\r\n<strong>\u00a0 --properties name=NAME \\<\/strong>\r\n<strong>\u00a0 -- \\<\/strong>\r\n<strong>\u00a0 subunits.json \\<\/strong>\r\n<strong>\u00a0 places.json<\/strong><\/pre>\n<p style=\"text-align: justify;\">This instruction combine the <strong>subunits.json<\/strong> and <strong>places.json<\/strong> files into a single one called <strong>uk.json<\/strong>. This step includes a minor transformation to fix the inconsistencies in the datasets, renaming the <strong>NAME<\/strong> property to <strong>name<\/strong>, and promoting the <strong>SU_A3<\/strong> property to the object id. You can see the resulting ids as follows:<\/p>\n<pre><strong>jq '.objects.subunits.geometries[].id' uk.json<\/strong><\/pre>\n<p style=\"text-align: justify;\">You can also see confirm the storage space occupied by each of the GeoJSON and TopoJSON files as follows:<\/p>\n<pre><strong>ls -lh *.json<\/strong><\/pre>\n<h3 style=\"text-align: justify;\"><strong>Creating the Web Page<\/strong><\/h3>\n<p style=\"text-align: justify;\">Once you have the data ready you can start plotting the map. For this, you will start with the <strong>index.html<\/strong> file which contains the following code:<\/p>\n<pre>&lt;!DOCTYPE html&gt;\r\n&lt;meta charset=\"utf-8\"&gt;\r\n&lt;style&gt;\r\n\/* CSS goes here. *\/\r\n&lt;\/style&gt;\r\n&lt;body&gt;\r\nSimple UK map\r\n&lt;script src=\"lib\/d3.v3.min.js\" charset=\"utf-8\"&gt;&lt;\/script&gt;\r\n&lt;script src=\"lib\/topojson.v1.min.js\"&gt;&lt;\/script&gt;\r\n&lt;script&gt;\r\n\/* JavaScript goes here. *\/\r\n&lt;\/script&gt;<\/pre>\n<p style=\"text-align: justify;\">Start local web server to view the web page.<\/p>\n<pre>http-server -p 8008<\/pre>\n<p style=\"text-align: justify;\">When you load this html file (<a href=\"http:\/\/localhost:8008\">http:\/\/localhost:8008<\/a>) you will only get the string \u201c<strong>Simple UK map<\/strong>\u201d. For loading the data into the map replace the <strong><em>\/* JavaScript goes here. *\/<\/em><\/strong> part with the following code:<\/p>\n<pre><strong>d3.json(\"uk.json\", function(error, uk) {\r\n \u00a0 if (error) return console.error(error);\r\n \u00a0 console.log(uk);\r\n });<\/strong><\/pre>\n<p style=\"text-align: justify;\">Now, if you peek at your\u00a0<a href=\"https:\/\/developers.google.com\/chrome-developer-tools\/docs\/console\">JavaScript console<\/a>, you should see a topology object containing the boundaries of UK\u2019 subdivisions.<\/p>\n<p style=\"text-align: justify;\"><img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-52\" src=\"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/Browser-Console-300x154.png\" alt=\"browser-console\" width=\"575\" height=\"295\" srcset=\"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/Browser-Console-300x154.png 300w, http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/Browser-Console-768x395.png 768w, http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/Browser-Console-624x321.png 624w, http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-content\/uploads\/sites\/38\/2016\/11\/Browser-Console.png 943w\" sizes=\"auto, (max-width: 575px) 100vw, 575px\" \/><\/p>\n<h3 style=\"text-align: justify;\"><strong>Displaying polygons<\/strong><\/h3>\n<p style=\"text-align: justify;\">There are a variety of ways to render two-dimensional geometry in a browser, but the two main standards are <a href=\"http:\/\/www.w3.org\/Graphics\/SVG\/\">SVG<\/a> and <a href=\"http:\/\/www.whatwg.org\/specs\/web-apps\/current-work\/multipage\/the-canvas-element.html\">Canvas<\/a>. <a href=\"https:\/\/github.com\/mbostock\/d3\/wiki\/3.0\">D3 3.0<\/a> supports both. For this exercise, we will use SVG because it can be easily styled via <a href=\"http:\/\/www.w3.org\/Style\/CSS\/\">CSS<\/a>.<\/p>\n<p style=\"text-align: justify;\">To create the root SVG element copy\/paste the following code into the script:<\/p>\n<pre><strong>var width = 960,<\/strong>\r\n<strong>\u00a0 \u00a0 height = 1160;<\/strong>\r\n\r\n<strong>var svg = d3.select(\"body\").append(\"svg\")<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"width\", width)<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"height\", height);<\/strong><\/pre>\n<p style=\"text-align: justify;\">The recommended place to put this code is at the top of the script rather than inside the d3.json callback. That is because d3.json is\u00a0<em>asynchronous<\/em> (i.e., the rest of the page will render while the TopoJSON file is downloaded). Creating the empty SVG root when the page first loads avoids distracting reflow when the geometry finally arrives.<\/p>\n<p style=\"text-align: justify;\">There are 2 more things required for rendering a geography: a\u00a0<a href=\"https:\/\/github.com\/mbostock\/d3\/wiki\/Geo-Projections\">projection<\/a>\u00a0and a\u00a0<a href=\"https:\/\/github.com\/mbostock\/d3\/wiki\/Geo-Paths\">path generator<\/a>.<\/p>\n<p style=\"text-align: justify;\">The <em>projection\u00a0<\/em>maps\u00a0spherical coordinate to the Cartesian plane, which is required to display spherical geometry on a 2D screen (you can skip this if this is the future and you\u2019re using a 3D holographic display); the path generator takes the projected 2D geometry and formats it appropriately for SVG or Canvas.<\/p>\n<p style=\"text-align: justify;\">For creating a map replace the code inside the d3.json callback like so:<\/p>\n<pre><strong>d3.json(\"uk.json\", function(error, uk) {<\/strong>\r\n<strong>\u00a0 if (error) return console.error(error);<\/strong>\r\n<strong>  svg.append(\"path\")<\/strong>\r\n<strong>\u00a0 \u00a0 \u00a0 .datum(topojson.feature(uk, uk.objects.subunits))<\/strong>\r\n<strong>\u00a0 \u00a0 \u00a0 .attr(\"d\", d3.geo.path().projection(d3.geo.mercator()));<\/strong>\r\n<strong>});<\/strong><\/pre>\n<p style=\"text-align: justify;\">You should now see a small, black and familiar speck:<\/p>\n<p style=\"text-align: justify;\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-medium wp-image-225\" src=\"http:\/\/espinosa-oviedo.com\/big-data-visualization\/wp-content\/uploads\/sites\/7\/2016\/11\/Map.png\" alt=\"map\" width=\"204\" height=\"183\" \/><\/p>\n<p style=\"text-align: justify;\">Recall from earlier the two closely-related JSON geographic data formats: GeoJSON and TopoJSON. While our data can be stored more efficiently in TopoJSON, we must convert <em>back<\/em> to GeoJSON for display. Lets break this step out to make it explicit:<\/p>\n<pre><strong>var subunits = topojson.feature(uk, uk.objects.subunits);<\/strong><\/pre>\n<p style=\"text-align: justify;\">Similarly, we can extract the definition of the projection to make the code clearer:<\/p>\n<pre><strong>\u00a0var projection = d3.geo.mercator()<\/strong>\r\n<strong>\u00a0 \u00a0 .scale(500)<\/strong>\r\n<strong>\u00a0 \u00a0 .translate([width \/ 2, height \/ 2]);<\/strong><\/pre>\n<p style=\"text-align: justify;\">And likewise, the path generator:<\/p>\n<pre><strong>var path = d3.geo.path()<\/strong>\r\n<strong>\u00a0 \u00a0 .projection(projection);<\/strong><\/pre>\n<p style=\"text-align: justify;\">And the path element, to which we <a href=\"https:\/\/github.com\/mbostock\/d3\/wiki\/Selections#wiki-datum\">bind<\/a> the GeoJSON data, and then use <a href=\"https:\/\/github.com\/mbostock\/d3\/wiki\/Selections#wiki-attr\">selection.attr<\/a> to set the &#8220;d&#8221; attribute to the formatted path data:<\/p>\n<pre><strong>svg.append(\"path\")<\/strong>\r\n<strong>\u00a0 \u00a0 .datum(subunits)<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"d\", path);<\/strong><\/pre>\n<p style=\"text-align: justify;\">With the code structured like this, we can now change the projection to something more suitable for the United Kingdom. The <a href=\"http:\/\/en.wikipedia.org\/wiki\/Albers_projection\">Albers equal-area conic projection<\/a> is a good choice, with standard parallels of 50\u00b0N and 60\u00b0N. To position the map, we <a href=\"https:\/\/bl.ocks.org\/4282586\">rotate longitude<\/a> by +4.4\u00b0 and set the center to 0\u00b0W 55.4\u00b0N, giving an effective origin of <a href=\"https:\/\/maps.google.com\/maps?q=4.4%C2%B0W+55.4%C2%B0N&amp;z=7\">4.4\u00b0W 55.4\u00b0N<\/a>, somewhere in a field in Scotland.<\/p>\n<pre><strong>var projection = d3.geo.albers()<\/strong>\r\n<strong>\u00a0 \u00a0 .center([0, 55.4])<\/strong>\r\n<strong>\u00a0 \u00a0 .rotate([4.4, 0])<\/strong>\r\n<strong>\u00a0 \u00a0 .parallels([50, 60])<\/strong>\r\n<strong>\u00a0 \u00a0 .scale(6000)<\/strong>\r\n<strong>\u00a0 \u00a0 .translate([width \/ 2, height \/ 2]);<\/strong><\/pre>\n<p style=\"text-align: justify;\">Your map should now look much better.<\/p>\n<h3 style=\"text-align: justify;\"><strong>Styling Polygons<\/strong><\/h3>\n<p style=\"text-align: justify;\">As mentioned before, a benefit of SVG is that it can be stylized with CSS. In what follows, you will add color to countries\u2019 subdivisions using style rules that assign a value to the <a href=\"http:\/\/www.w3.org\/TR\/SVG\/painting.html#FillProperty\">fill property<\/a>. However, you must give first each country its own path element rather than sharing one. Without distinct path elements, we have no way to assign distinct colors.<\/p>\n<p style=\"text-align: justify;\">Within the uk.json TopoJSON file, the Admin-0 map subunits are represented as a <a href=\"http:\/\/geojson.org\/geojson-spec.html#feature-collection-objects\">feature collection<\/a>. By pulling out the features array, we can compute a <a href=\"https:\/\/bost.ocks.org\/mike\/join\/\">data join<\/a> and create a path element for each feature:<\/p>\n<pre>\/\/replace previous svg.append with this code\r\n<strong>svg.selectAll(\".subunit\")<\/strong>\r\n<strong>\u00a0 .data(subunits.features)<\/strong>\r\n<strong>\u00a0 .enter().append(\"path\")<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"class\", function(d) { return \"subunit \" + d.id; })<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"d\", path);<\/strong><\/pre>\n<p style=\"text-align: justify;\">We can also compute the &#8220;class&#8221; attribute as a function of data, so that in addition to the subunit class, each country has a three-letter class corresponding to its ISO-3166 alpha-3 country code. These allow us to apply separate fill styles for each country:<\/p>\n<pre><strong>.subunit.SCT { fill: #ddc; }<\/strong>\r\n<strong>.subunit.WLS { fill: #cdd; }<\/strong>\r\n<strong>.subunit.NIR { fill: #cdc; }<\/strong>\r\n<strong>.subunit.ENG { fill: #dcd; }\r\n.subunit.SCT { fill: #ddc; }<\/strong>\r\n<strong>.subunit.IRL { display: none; }<\/strong><\/pre>\n<h3 style=\"text-align: justify;\"><strong>Displaying Boundaries<\/strong><\/h3>\n<p style=\"text-align: justify;\">To apply the finishing touch to the polygons, we need a few lines. These are two boundary lines representing the borders of England with Scotland and Wales, respectively, and the Irish coastline.<\/p>\n<p style=\"text-align: justify;\">We\u2019ll use <a href=\"https:\/\/github.com\/mbostock\/topojson\/blob\/v0.0.6\/topojson.js#L101\">topojson.mesh<\/a> to compute the boundaries from the topology. This requires two arguments: the <em>topology<\/em> and a constituent geometry <em>object<\/em>. An optional <em>filter<\/em> can reduce the set of returned boundaries, taking two arguments <em>a<\/em> and <em>b<\/em> representing the two features on either side of the boundary. For exterior boundaries such as coastlines, <em>a<\/em> and <em>b<\/em> are the same. Thus by filtering on a\u00a0===\u00a0b or a\u00a0!==\u00a0b, we obtain exterior or interior boundaries exclusively.<\/p>\n<pre><strong>svg.append(\"path\")<\/strong>\r\n<strong>\u00a0 \u00a0 .datum(topojson.mesh(uk, uk.objects.subunits, function(a, b) { return a !== b &amp;&amp; a.id !== \"IRL\"; }))<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"d\", path)<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"class\", \"subunit-boundary\");<\/strong><\/pre>\n<p style=\"text-align: justify;\">That leaves only the Irish coastline, an exterior boundary:<\/p>\n<pre><strong>svg.append(\"path\")<\/strong>\r\n<strong>\u00a0 \u00a0 .datum(topojson.mesh(uk, uk.objects.subunits, function(a, b) { return a === b &amp;&amp; a.id === \"IRL\"; }))<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"d\", path)<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"class\", \"subunit-boundary IRL\");<\/strong><\/pre>\n<p style=\"text-align: justify;\">Add a bit of style:<\/p>\n<pre><strong>.subunit-boundary {<\/strong>\r\n<strong>\u00a0 fill: none;<\/strong>\r\n<strong>\u00a0 stroke: #777;<\/strong>\r\n<strong>\u00a0 stroke-dasharray: 2,2;<\/strong>\r\n<strong>\u00a0 stroke-linejoin: round;<\/strong>\r\n<strong>}<\/strong>\r\n\r\n<strong>.subunit-boundary.IRL {<\/strong>\r\n<strong>\u00a0 stroke: #aaa;<\/strong>\r\n<strong>}<\/strong><\/pre>\n<p style=\"text-align: justify;\">And voil\u00e0!<\/p>\n<h3 style=\"text-align: justify;\"><strong>Displaying Places<\/strong><\/h3>\n<p style=\"text-align: justify;\">As with the country polygons, the populated places are a feature collection, so we can again convert from TopoJSON to GeoJSON and use d3.geo.path to render:<\/p>\n<pre><strong>svg.append(\"path\")<\/strong>\r\n<strong>\u00a0 \u00a0 .datum(topojson.feature(uk, uk.objects.places))<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"d\", path)<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"class\", \"place\");<\/strong><\/pre>\n<p style=\"text-align: justify;\">This will draw a small circle for each city. We can adjust the radius by setting <a href=\"https:\/\/github.com\/mbostock\/d3\/wiki\/Geo-Paths#wiki-pointRadius\">path.pointRadius<\/a>, and assign styles via CSS.<\/p>\n<pre style=\"text-align: justify;\">text {\r\n font-family: \"Helvetica Neue\", Helvetica, Arial, sans-serif;\r\n font-size: 10px;\r\n pointer-events: none;\r\n}<\/pre>\n<p style=\"text-align: justify;\">We <em>also<\/em> want labels, so we need a data join to create text elements. By computing the transform property by projecting the place\u2019s coordinates, we translate the labels into the desired position:<\/p>\n<pre><strong>svg.selectAll(\".place-label\")<\/strong>\r\n<strong>\u00a0 \u00a0 .data(topojson.feature(uk, uk.objects.places).features)<\/strong>\r\n<strong>\u00a0 .enter().append(\"text\")<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"class\", \"place-label\")<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"transform\", function(d) { return \"translate(\" + projection(d.geometry.coordinates) + \")\"; })<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"dy\", \".35em\")<\/strong>\r\n<strong>\u00a0 \u00a0 .text(function(d) { return d.properties.name; });<\/strong><\/pre>\n<p style=\"text-align: justify;\">Labeling a map well can be challenging, especially if you want to position labels automatically. We\u2019ve mostly ignored the problem for our simple map, though it helps that we earlier filtered labels by SCALERANK. A convenient trick is to use right-aligned labels on the left side of the map, and left-aligned labels on the right side of the map, here using 1\u00b0W as the threshold:<\/p>\n<pre><strong>svg.selectAll(\".place-label\")<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"x\", function(d) { return d.geometry.coordinates[0] &gt; -1 ? 6 : -6; })<\/strong>\r\n<strong>\u00a0 \u00a0 .style(\"text-anchor\", function(d) { return d.geometry.coordinates[0] &gt; -1 ? \"start\" : \"end\"; });<\/strong><\/pre>\n<h3 style=\"text-align: justify;\"><strong>Country Labels<\/strong><\/h3>\n<p style=\"text-align: justify;\">Our map is missing a critical component: we haven\u2019t labeled the countries! We could have used Natural Earth\u2019s <a href=\"http:\/\/www.naturalearthdata.com\/http\/www.naturalearthdata.com\/download\/10m\/cultural\/ne_10m_admin_0_label_points.zip\">Admin-0 label points<\/a>, but we can just as easily compute country label points using the <a href=\"https:\/\/github.com\/mbostock\/d3\/wiki\/Geo-Paths#wiki-centroid\">projected centroid<\/a>:<\/p>\n<pre><strong>svg.selectAll(\".subunit-label\")<\/strong>\r\n<strong>\u00a0 \u00a0 .data(topojson.feature(uk, uk.objects.subunits).features)<\/strong>\r\n<strong>\u00a0 .enter().append(\"text\")<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"class\", function(d) { return \"subunit-label \" + d.id; })<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"transform\", function(d) { return \"translate(\" + path.centroid(d) + \")\"; })<\/strong>\r\n<strong>\u00a0 \u00a0 .attr(\"dy\", \".35em\")<\/strong>\r\n<strong>\u00a0 \u00a0 .text(function(d) { return d.properties.name; });<\/strong><\/pre>\n<p style=\"text-align: justify;\">The country labels are styled larger to distinguish them from city labels. By making them partly transparent, country labels are relegated to the background and city labels are more legible:<\/p>\n<pre><strong>.subunit-label {<\/strong>\r\n<strong>\u00a0 fill: #777;<\/strong>\r\n<strong>\u00a0 fill-opacity: .5;<\/strong>\r\n<strong>\u00a0 font-size: 20px;<\/strong>\r\n<strong>\u00a0 font-weight: 300;<\/strong>\r\n<strong>\u00a0 <\/strong><strong>text-anchor: middle;<\/strong>\r\n<strong>}<\/strong><\/pre>\n<p style=\"text-align: justify;\">And there we have it.<\/p>\n<h3 style=\"text-align: justify;\">Sources<\/h3>\n<ul>\n<li style=\"text-align: justify;\"><a href=\"http:\/\/www.tnoda.com\/blog\/2013-12-07\">Interactive Map with d3.js<\/a><\/li>\n<li style=\"text-align: justify;\"><a href=\"http:\/\/vda-lab.github.io\/2015\/03\/creating-a-map-in-d3\">Creating a map in D3<\/a><\/li>\n<li style=\"text-align: justify;\"><a href=\"https:\/\/bost.ocks.org\/mike\/map\/\">Let\u2019s make a map<\/a><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>The objective of this exercise is to show you how to create a map using D3.js and TopoJSON, &nbsp;a lighter version of GeoJSON. Requirements Node Geospatial Data Abstraction Library (GDAL) Natural Earth data (provided during the course) JQ&nbsp;&nbsp;JSON processor TopoJSON (v. 1x) &nbsp;&amp;&nbsp;http-server (node packages) Obtaining and preparing the data The first step for creating [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-48","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-json\/wp\/v2\/pages\/48","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-json\/wp\/v2\/comments?post=48"}],"version-history":[{"count":5,"href":"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-json\/wp\/v2\/pages\/48\/revisions"}],"predecessor-version":[{"id":65,"href":"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-json\/wp\/v2\/pages\/48\/revisions\/65"}],"wp:attachment":[{"href":"http:\/\/vargas-solar.com\/bigdata-visualisation\/wp-json\/wp\/v2\/media?parent=48"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}