2

I'm trying to create a map similar to that of Mike Bostock.

This is my JSON file (it represents Europe divided into NUTS 2 regions). Its structure is:

{
    "type": "Topology",
    "objects":
    {
        "nuts2":
        {
            "type": "GeometryCollection",
            "bbox": [-63.15345500000001, -21.387309500000015, 55.83662850000002, 71.18531800099998],
            "geometries": [
            {
                "type": "Polygon",
                "properties":
                {
                    "nuts_id": "AT11",
                    "name": "Burgenland (AT)",
                    "population": 286691
                },
                "id": "AT11",
                "arcs": [
                    [0, 1, 2, 3, 4]
                ]
            },
            {
                "type": "Polygon",
                "properties":
                {
                    "nuts_id": "AT12",
                    "name": "Niederösterreich",
                    "population": 1618592
                },
                "id": "AT12",
                "arcs": [
                    [5, 6, 7, -4, 8, 9, 10],
                    [11]
                ]
            },
            {
                "type": "Polygon",
                "properties":
                {
                    "nuts_id": "AT13",
                    "name": "Wien",
                    "population": 1741246
                },
                "id": "AT13",
                "arcs": [
                    [-12]
                ]
            },
            {
                "type": "Polygon",
                "properties":
                {
                    "nuts_id": "AT21",
                    "name": "Kärnten",
                    "population": 555473
                },
                "id": "AT21",
                "arcs": [
                    [12, 13, 14, 15, 16, 17, 18]
                ]
            },
            ...

Then I have this CSV file that represents the vaccination coverage in Italy over several years and for different antigens. Its structure is:

ID;NUTS2-ID;NUTS2-NAME;YEAR;ANTIGEN;COVERAGE-PERC
1;IT;Italy;2000;POL;96,6
2;IT;Italy;2000;DIF;
3;IT;Italy;2000;TET;
4;IT;Italy;2000;PER;
5;IT;Italy;2000;DT-DTP3;95,3
6;IT;Italy;2000;EP B;94,1
7;IT;Italy;2000;HIB;54,7
8;IT;Italy;2000;MOR;
9;IT;Italy;2000;PAR;
10;IT;Italy;2000;ROS;
11;IT;Italy;2000;VAR;
12;IT;Italy;2000;M-MPR1-MPRV;
13;IT;Italy;2000;M-MPR1;74,1
14;IT;Italy;2001;POL;95,8
15;IT;Italy;2001;DIF;
16;IT;Italy;2001;TET;
17;IT;Italy;2001;PER;
18;IT;Italy;2001;DT-DTP3;95,9
19;IT;Italy;2001;EP B;94,5
20;IT;Italy;2001;HIB;70,2
21;IT;Italy;2001;MOR;
22;IT;Italy;2001;PAR;
23;IT;Italy;2001;ROS;
24;IT;Italy;2001;VAR;
25;IT;Italy;2001;M-MPR1-MPRV;
26;IT;Italy;2001;M-MPR1;76,9
27;IT;Italy;2002;POL;95,9
28;IT;Italy;2002;DIF;
29;IT;Italy;2002;TET;
30;IT;Italy;2002;PER;
31;IT;Italy;2002;DT-DTP3;96,8
32;IT;Italy;2002;EP B;95,4
33;IT;Italy;2002;HIB;83,4
34;IT;Italy;2002;MOR;
35;IT;Italy;2002;PAR;
36;IT;Italy;2002;ROS;
37;IT;Italy;2002;VAR;
38;IT;Italy;2002;M-MPR1-MPRV;
39;IT;Italy;2002;M-MPR1;80,8
40;IT;Italy;2003;POL;96,6
41;IT;Italy;2003;DIF;
42;IT;Italy;2003;TET;
43;IT;Italy;2003;PER;
44;IT;Italy;2003;DT-DTP3;96,6
45;IT;Italy;2003;EP B;95,4
46;IT;Italy;2003;HIB;90,4
47;IT;Italy;2003;MOR;
48;IT;Italy;2003;PAR;
49;IT;Italy;2003;ROS;
50;IT;Italy;2003;VAR;
51;IT;Italy;2003;M-MPR1-MPRV;
52;IT;Italy;2003;M-MPR1;83,9
53;IT;Italy;2004;POL;96,8
54;IT;Italy;2004;DIF;
55;IT;Italy;2004;TET;
56;IT;Italy;2004;PER;
57;IT;Italy;2004;DT-DTP3;96,6
58;IT;Italy;2004;EP B;96,3
59;IT;Italy;2004;HIB;93,8
60;IT;Italy;2004;MOR;
61;IT;Italy;2004;PAR;
62;IT;Italy;2004;ROS;
63;IT;Italy;2004;VAR;
64;IT;Italy;2004;M-MPR1-MPRV;
65;IT;Italy;2004;M-MPR1;85,7
66;IT;Italy;2005;POL;96,5
67;IT;Italy;2005;DIF;
68;IT;Italy;2005;TET;
69;IT;Italy;2005;PER;
70;IT;Italy;2005;DT-DTP3;96,2
71;IT;Italy;2005;EP B;95,7
72;IT;Italy;2005;HIB;94,7
73;IT;Italy;2005;MOR;
74;IT;Italy;2005;PAR;
75;IT;Italy;2005;ROS;
76;IT;Italy;2005;VAR;
77;IT;Italy;2005;M-MPR1-MPRV;
78;IT;Italy;2005;M-MPR1;87,3
79;IT;Italy;2006;POL;96,5
80;IT;Italy;2006;DIF;
81;IT;Italy;2006;TET;
82;IT;Italy;2006;PER;
83;IT;Italy;2006;DT-DTP3;96,6
84;IT;Italy;2006;EP B;96,3
85;IT;Italy;2006;HIB;95,5
86;IT;Italy;2006;MOR;
87;IT;Italy;2006;PAR;
88;IT;Italy;2006;ROS;
89;IT;Italy;2006;VAR;
90;IT;Italy;2006;M-MPR1-MPRV;
91;IT;Italy;2006;M-MPR1;88,3
92;IT;Italy;2007;POL;96,7
93;IT;Italy;2007;DIF;
94;IT;Italy;2007;TET;
95;IT;Italy;2007;PER;
96;IT;Italy;2007;DT-DTP3;96,7
97;IT;Italy;2007;EP B;96,5
98;IT;Italy;2007;HIB;96
99;IT;Italy;2007;MOR;
100;IT;Italy;2007;PAR;
101;IT;Italy;2007;ROS;
102;IT;Italy;2007;VAR;
103;IT;Italy;2007;M-MPR1-MPRV;
104;IT;Italy;2007;M-MPR1;89,6
105;IT;Italy;2008;POL;96,3
106;IT;Italy;2008;DIF;
107;IT;Italy;2008;TET;
108;IT;Italy;2008;PER;
109;IT;Italy;2008;DT-DTP3;96,7
110;IT;Italy;2008;EP B;96,1
111;IT;Italy;2008;HIB;95,7
112;IT;Italy;2008;MOR;
113;IT;Italy;2008;PAR;
114;IT;Italy;2008;ROS;
115;IT;Italy;2008;VAR;
116;IT;Italy;2008;M-MPR1-MPRV;
117;IT;Italy;2008;M-MPR1;90,1
118;IT;Italy;2009;POL;96,1
119;IT;Italy;2009;DIF;
120;IT;Italy;2009;TET;
121;IT;Italy;2009;PER;
122;IT;Italy;2009;DT-DTP3;96,2
123;IT;Italy;2009;EP B;95,8
124;IT;Italy;2009;HIB;95,6
125;IT;Italy;2009;MOR;
126;IT;Italy;2009;PAR;
127;IT;Italy;2009;ROS;
128;IT;Italy;2009;VAR;
129;IT;Italy;2009;M-MPR1-MPRV;89,9
130;IT;Italy;2009;M-MPR1;
131;IT;Italy;2010;POL;96,3
132;IT;Italy;2010;DIF;
133;IT;Italy;2010;TET;
134;IT;Italy;2010;PER;
135;IT;Italy;2010;DT-DTP3;96,4
136;IT;Italy;2010;EP B;95,8
137;IT;Italy;2010;HIB;94,6
138;IT;Italy;2010;MOR;
139;IT;Italy;2010;PAR;
140;IT;Italy;2010;ROS;
141;IT;Italy;2010;VAR;
142;IT;Italy;2010;M-MPR1-MPRV;90,6
143;IT;Italy;2010;M-MPR1;
144;IT;Italy;2011;POL;96,1
145;IT;Italy;2011;DIF;
146;IT;Italy;2011;TET;
147;IT;Italy;2011;PER;
148;IT;Italy;2011;DT-DTP3;96,3
149;IT;Italy;2011;EP B;96
150;IT;Italy;2011;HIB;95,6
151;IT;Italy;2011;MOR;
152;IT;Italy;2011;PAR;
153;IT;Italy;2011;ROS;
154;IT;Italy;2011;VAR;
155;IT;Italy;2011;M-MPR1-MPRV;90,1
156;IT;Italy;2011;M-MPR1;
157;IT;Italy;2012;POL;96,1
158;IT;Italy;2012;DIF;
159;IT;Italy;2012;TET;
160;IT;Italy;2012;PER;
161;IT;Italy;2012;DT-DTP3;96,2
162;IT;Italy;2012;EP B;96
163;IT;Italy;2012;HIB;94,8
164;IT;Italy;2012;MOR;
165;IT;Italy;2012;PAR;
166;IT;Italy;2012;ROS;
167;IT;Italy;2012;VAR;
168;IT;Italy;2012;M-MPR1-MPRV;90
169;IT;Italy;2012;M-MPR1;
170;IT;Italy;2013;POL;95,74
171;IT;Italy;2013;DIF;95,75
172;IT;Italy;2013;TET;95,81
173;IT;Italy;2013;PER;95,68
174;IT;Italy;2013;DT-DTP3;
175;IT;Italy;2013;EP B;95,65
176;IT;Italy;2013;HIB;94,91
177;IT;Italy;2013;MOR;90,35
178;IT;Italy;2013;PAR;90,3
179;IT;Italy;2013;ROS;90,3
180;IT;Italy;2013;VAR;33,19
181;IT;Italy;2013;M-MPR1-MPRV;
182;IT;Italy;2013;M-MPR1;
183;IT;Italy;2014;POL;94,71
184;IT;Italy;2014;DIF;94,71
185;IT;Italy;2014;TET;94,82
186;IT;Italy;2014;PER;94,64
187;IT;Italy;2014;DT-DTP3;
188;IT;Italy;2014;EP B;94,61
189;IT;Italy;2014;HIB;94,31
190;IT;Italy;2014;MOR;86,74
191;IT;Italy;2014;PAR;86,67
192;IT;Italy;2014;ROS;86,69
193;IT;Italy;2014;VAR;36,64
194;IT;Italy;2014;M-MPR1-MPRV;
195;IT;Italy;2014;M-MPR1;
196;IT;Italy;2015;POL;93,43
197;IT;Italy;2015;DIF;93,35
198;IT;Italy;2015;TET;93,56
199;IT;Italy;2015;PER;93,33
200;IT;Italy;2015;DT-DTP3;
201;IT;Italy;2015;EP B;93,2
202;IT;Italy;2015;HIB;93,03
203;IT;Italy;2015;MOR;85,29
204;IT;Italy;2015;PAR;85,23
205;IT;Italy;2015;ROS;85,22
206;IT;Italy;2015;VAR;30,73
207;IT;Italy;2015;M-MPR1-MPRV;
208;IT;Italy;2015;M-MPR1;
209;IT;Italy;2016;POL;93,33
210;IT;Italy;2016;DIF;93,56
211;IT;Italy;2016;TET;93,72
212;IT;Italy;2016;PER;93,55
213;IT;Italy;2016;DT-DTP3;
214;IT;Italy;2016;EP B;92,98
215;IT;Italy;2016;HIB;93,05
216;IT;Italy;2016;MOR;87,26
217;IT;Italy;2016;PAR;87,2
218;IT;Italy;2016;ROS;87,19
219;IT;Italy;2016;VAR;46,06
220;IT;Italy;2016;M-MPR1-MPRV;
221;IT;Italy;2016;M-MPR1;
222;ITC1;Piemonte;2000;POL;97,2
223;ITC1;Piemonte;2000;DIF;
224;ITC1;Piemonte;2000;TET;
225;ITC1;Piemonte;2000;PER;
226;ITC1;Piemonte;2000;DT-DTP3;97,2
227;ITC1;Piemonte;2000;EP B;97,2
228;ITC1;Piemonte;2000;HIB;44
229;ITC1;Piemonte;2000;MOR;
230;ITC1;Piemonte;2000;PAR;
231;ITC1;Piemonte;2000;ROS;
232;ITC1;Piemonte;2000;VAR;
233;ITC1;Piemonte;2000;M-MPR1-MPRV;
234;ITC1;Piemonte;2000;M-MPR1;67,6
235;ITC1;Piemonte;2001;POL;97,2
236;ITC1;Piemonte;2001;DIF;
237;ITC1;Piemonte;2001;TET;
238;ITC1;Piemonte;2001;PER;
239;ITC1;Piemonte;2001;DT-DTP3;97,2
240;ITC1;Piemonte;2001;EP B;97,2
241;ITC1;Piemonte;2001;HIB;66,8
242;ITC1;Piemonte;2001;MOR;
243;ITC1;Piemonte;2001;PAR;
244;ITC1;Piemonte;2001;ROS;
245;ITC1;Piemonte;2001;VAR;
246;ITC1;Piemonte;2001;M-MPR1-MPRV;
247;ITC1;Piemonte;2001;M-MPR1;73,2
248;ITC1;Piemonte;2002;POL;96,9
249;ITC1;Piemonte;2002;DIF;
250;ITC1;Piemonte;2002;TET;
251;ITC1;Piemonte;2002;PER;
252;ITC1;Piemonte;2002;DT-DTP3;96,9
253;ITC1;Piemonte;2002;EP B;96,9
254;ITC1;Piemonte;2002;HIB;72,4
255;ITC1;Piemonte;2002;MOR;
256;ITC1;Piemonte;2002;PAR;
257;ITC1;Piemonte;2002;ROS;
258;ITC1;Piemonte;2002;VAR;
259;ITC1;Piemonte;2002;M-MPR1-MPRV;
260;ITC1;Piemonte;2002;M-MPR1;79,3
261;ITC1;Piemonte;2003;POL;97,1
262;ITC1;Piemonte;2003;DIF;
263;ITC1;Piemonte;2003;TET;
264;ITC1;Piemonte;2003;PER;
265;ITC1;Piemonte;2003;DT-DTP3;97
266;ITC1;Piemonte;2003;EP B;95,9
267;ITC1;Piemonte;2003;HIB;79,7
268;ITC1;Piemonte;2003;MOR;
269;ITC1;Piemonte;2003;PAR;
270;ITC1;Piemonte;2003;ROS;
271;ITC1;Piemonte;2003;VAR;
272;ITC1;Piemonte;2003;M-MPR1-MPRV;
273;ITC1;Piemonte;2003;M-MPR1;84,4
274;ITC1;Piemonte;2004;POL;97,1
275;ITC1;Piemonte;2004;DIF;
276;ITC1;Piemonte;2004;TET;
277;ITC1;Piemonte;2004;PER;
278;ITC1;Piemonte;2004;DT-DTP3;97
279;ITC1;Piemonte;2004;EP B;96,8
...

What I would like to do is show the map of Europe (using the JSON file) and color the Italian regions using the CSV file. The idea is to use radio buttons so that the user can select which antigen he wants to view.

First of all I started trying to visualize the map and I had several problems. I tried two ways: a basic one that loads only the JSON and another way that loads the CSV file and the JSON file.

This is the first solution I've tested (map.js):

// Create SVG element
var width = 700;
var height = 600;

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

var projection = d3.geoMercator()
    .scale(width / 2 / Math.PI)
    //.scale(100)
    .translate([width / 2, height / 2]);

var path = d3.geoPath()
    .projection(projection);

// Load map from file
d3.json("./data/GeoJSON/nuts2.json", function(error, euNuts2) {
    if (error) {
        return console.error(error);
        throw error;
    }

    // Create path based on map data
    svg.append("path")
        // Adjust topology reference objects.nuts2
        .datum(topojson.feature(euNuts2, euNuts2.objects.nuts2))
        .attr("d", path(euNuts2));
});

It doesn't generate errors but it doesn't display anything. Why? Is it a problem of projections?

This is the second solution I tested (map.js):

var width = 700;
var height = 600;

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

var projection = d3.geoMercator()
    .scale(width / 2 / Math.PI)
    //.scale(100)
    .translate([width / 2, height / 2]);

var path = d3.geoPath()
    .projection(projection);

d3.queue()
    .defer(d3.json, '../data/GeoJSON/nuts2.json')
    .defer(d3.csv, '../data/csv/Coverage Italy 24 months.csv')
    .await(makeMap); // when data arrives call makeMap function

function makeMap(error, coverageItaly) {
    if (error) {
        console.log("*** ERROR LOADING FILES: " + error + " ***");
    }

}; // end makeMap function 

Here the problem is the path of the CSV file that is not found, but to me it seems correct.

The files are saved in this way:

data dir
|_ csv dir
   |_ Coverage Italy 24 months.csv
|_ GeoJSON dir
   |_ nuts2.json

script dir
|_ map.js

style dir
|_ map.css

index.html

My html file is (index.html):

<head>
    <meta charset="utf-8">
    <title>Map</title>

    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="//d3js.org/topojson.v2.min.js"></script>

    <link rel="stylesheet" type="text/css" href="./style/map.css" media="screen" />
</head>

<body>

    <div class="chart-title">Vaccination coverage in Europe</div>

    <div id="container-map"></div>

    <script src="./script/map.js"></script>

</body>

I don't understand exactly what the problems are. I have read the D3js API, I have looked at numerous examples and I cann't solve them. Can someone help me?

  • and need 'map.css` for full kit)) Fast scan: first solution `map.js` - ('topojson' is undefined) for second solution - one moment... – Akubik Dec 24 '17 at 23:44

1 Answers1

1

BIG JSON, need only IT or all EU? ... ok.. run with full JSON (steps from simple to difficult)

#index.html

Add to top <!DOCTYPE html>
Correct this <script src="https://d3js.org/topojson.v2.min.js"></script>

#map.js (in JavaScript part)

Step one and two:

var width = 700;
var height = 600;

var svg = d3.select("#container-map").append("svg")
  .attr("width", width)
  .attr("height", height);

var projection = d3.geoMercator().scale(1000).center([25, 45]);
var path = d3.geoPath(projection);

//var pathtonuts2 = '../data/GeoJSON/nuts2.json';
//var pathtocoverageIT =  '../data/csv/Coverage Italy 24 months.csv';
var pathtonuts2 = 'https://gist.githubusercontent.com/rveciana/5919944/raw/2fef6be25d39ebeb3bead3933b2c9380497ddff4/nuts2.json';
var pathtocoverageIT = '';



d3.queue()
  .defer(d3.json, pathtonuts2)
  //  .defer(d3.csv, pathtocoverageIT)
  .await(makeMap); // when data arrives call makeMap function

function makeMap(error, euNuts2, coverageItaly) {
  if (error) {
    console.log("*** ERROR LOADING FILES: " + error + " ***");
    throw error;
  }
  var cantons = topojson.feature(euNuts2, euNuts2.objects.nuts2);

  svg.append("g")
    .attr("class", "area")
    .selectAll("path")
    .data(cantons.features)
    .enter().append("path")
    .attr("d", path)
    .append("title")
    .text(function(d) {
      return d.id + '\n' + d.properties.name;
    });
}; // end makeMap function
.area {
  fill: steelblue;
}

.area :hover {
  fill: red;
}
<!DOCTYPE html>

<head>
  <meta charset="utf-8">
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://d3js.org/topojson.v2.min.js"></script>
</head>

<body>
  <div class="chart-title">Vaccination coverage in Europe <br>
    <select name="dataset">
    <option value="2000">2000</option>
    <option value="2001">2001</option>
    <option value="2002">2002</option>
    </select> Selector is disable now!</div>

  <div id="container-map"></div>

</body>

Step three and four:

work with JSON OK... now begin think about CSV... (data of coverage IT)

and work CSV OK...with any delimiter , or ; or other

and now work with DB(CSV) ...

var width = 700;
var height = 600;

var svg = d3.select("#container-map").append("svg")
  .attr("width", width)
  .attr("height", height);

var projection = d3.geoMercator().scale(1000).center([25, 45]);
var path = d3.geoPath(projection);

//var pathtonuts2 = './data/GeoJSON/nuts2.json';
//var pathtocoverageIT =  './data/csv/Coverage Italy 24 months.csv';

// for test
var pathtonuts2 = 'https://gist.githubusercontent.com/rveciana/5919944/raw/2fef6be25d39ebeb3bead3933b2c9380497ddff4/nuts2.json';
var pathtocoverageIT = 'https://dl.dropboxusercontent.com/s/r8mpix4aqytvp31/Coverage%20Italy%2024%20months.csv';
   
//Parser CSV with any delimiter    
var dsv = function(url, delimiter, callback) {
  d3.text(url, function(text) {
    var data = d3.dsvFormat(delimiter).parse(text);
    callback(null, data);
  });
}

var menu = d3.select("#years_list");

d3.queue()
  .defer(d3.json, pathtonuts2)
  .defer(dsv, pathtocoverageIT, ",")
  .await(makeMap); // when data arrives call makeMap function

function makeMap(error, euNuts2, coverageItaly) {
    if (error) {
        console.log("*** ERROR LOADING FILES: " + error + " ***");
        throw error;
    }
      var cantons = topojson.feature(euNuts2, euNuts2.objects.nuts2);

      var nested_years = d3.nest()
        .key(function(d) { return d.YEAR}).sortKeys(d3.ascending)
        .rollup(function(leaves) { return leaves.length; })
        .entries(coverageItaly);

      map_titles.nested_data = d3.nest()
        .key(function(d) { return d["NUTS2_ID"] })
        .key(function(d) { return d.YEAR })
        .key(function(d) { return d.ANTIGEN})
        .map(coverageItaly);

      menu.append("select")
        .selectAll("option")
        .data(nested_years)
        .enter()
        .append("option")
        .attr("value", function(d) {return d.key;})
        .text(function(d) {return d.key; });

      menu.select("select")
        .on("change", function() {
          map_titles.year = this.value;
          d3.selectAll(".canton_title")
           .text(function (d){return map_titles(d);})});

      svg.append("g")
        .attr("class", "area")
        .selectAll("path")
        .data(cantons.features)
        .enter()
        .append("path")
        .attr("d", path)
        .append("title")
        .attr("class", "canton_title")
        .text(function (d){return map_titles(d);});
}; // end makeMap function 


function map_titles(canton_title){
      var title = canton_title.id + '\n' + canton_title.properties.name + '\n' + map_titles.year + '\n\nRegion vaccination coverage (in %)\n';
      var canton = map_titles.nested_data.get(canton_title.id);
      
      if (map_titles.year === undefined)
        map_titles.year = menu.select("option").text();

      if (canton !== undefined) 
        canton.get(map_titles.year).each(function(d, key) {
     if (key.length < 8) key += '\t';   
       title += key + '\t-\t' + d[0]["COVERAGE_PERC"] + '\n';
      });
      return title;
};
.area {
  fill: steelblue;
}

.area :hover {
  fill: red;
}
<!DOCTYPE html>

<head>
  <meta charset="utf-8">
  <title>Map</title>

  <script src="https://d3js.org/d3.v4.min.js"></script>
  <script src="https://d3js.org/topojson.v2.min.js"></script>

  <!--    <link rel="stylesheet" type="text/css" href="./style/map.css" media="screen" />
-->

</head>

<body>
  <div class="chart-title">Vaccination coverage in Europe</div>
  <div id="years_list"></div>
  <div id="container-map"></div>

  <script src="./script/map.js"></script>
</body>

Beta version is OK... may be not optimal, but for example...
next step is painting, but need formula of colors of regions

Step five:

var width = 700;
var height = 600;

var svg = d3.select("#container-map").append("svg")
    .attr("width", width)
    .attr("height", height);

var projection = d3.geoMercator().scale(1000).center([25,45]);
var path = d3.geoPath(projection);

//var pathtonuts2 = './data/GeoJSON/nuts2.json';
//var pathtocoverageIT =  './data/csv/Coverage Italy 24 months.csv';

// for test
    var pathtonuts2 = 'https://gist.githubusercontent.com/rveciana/5919944/raw/2fef6be25d39ebeb3bead3933b2c9380497ddff4/nuts2.json';
var pathtocoverageIT = 'https://dl.dropboxusercontent.com/s/r8mpix4aqytvp31/Coverage%20Italy%2024%20months.csv';

//Parser CSV with any delimiter    
var dsv = function (url, delimiter, callback){
  d3.text(url, function(text) {
    var data = d3.dsvFormat(delimiter).parse(text);
    callback(null, data);
  });
}

var regionInfo = {"properties":{"name":"Italy"}, "id" : "IT"};

var menuYear = d3.select("#years_list");
var menuAntigen = d3.select("#antigen_list");
var nestedData;
var mymap = svg.append("g");

/////////////SCALE//////////////
var x = d3.scaleLinear()
    .domain([0, 100])
    .rangeRound([500, 700]);

var color = d3.scaleThreshold()
    .domain(d3.range(0, 101, 25))
    .range(d3.schemeRdYlGn[5]);

var g = svg.append("g")
    .attr("class", "key")
    .attr("transform", "translate(-20,20)");

//Scale colors
g.selectAll("rect")
    .data(color.range().map(function(d) {
      d = color.invertExtent(d);
      if (d[0] == null) d[0] = x.domain()[0];
      if (d[1] == null) d[1] = x.domain()[1];
      return d;
    }))
    .enter().append("rect")
    .attr("height", 8)
    .attr("x", function(d) { return x(d[0]); })
    .attr("width", function(d) { return x(d[1]) - x(d[0]); })
    .attr("fill", function(d) { return color(d[0]); });

g.append("text")
    .attr("class", "caption")
    .attr("x", x.range()[0])
    .attr("y", -6)
    .attr("fill", "#000")
    .attr("text-anchor", "start")
    .attr("font-weight", "bold")
    .text("Vaccination coverage");

//Scale digital 
g.call(d3.axisBottom(x)
    .tickSize(13)
    .tickFormat(function(x, i) { return i ? x : x + "%"; })
    .tickValues(color.domain()))
    .select(".domain")
    .remove();

////////////SCALE END////////////////////

legend = g.append("g")
           .attr("transform", function(d, i) { return "translate(" + x([x.domain()[0]]) + "," + i * 15 + ")"; });

var scaleStepSize = (x([x.domain()[1]]) - x([x.domain()[0]])) / (color.domain().length - 1);

legend.append("rect")
      .attr("y", 42)
      .attr("width", scaleStepSize)
      .attr("height", 8)
      .style("fill", "steelblue");

legend.append("text")
      .attr("dx", scaleStepSize + 10)
      .attr("y", 50)
      .attr("fill", "#000")
      .attr("text-anchor", "start")
      .attr("font-weight", "bold")
      .text("- no info about region");

d3.queue()
    .defer(d3.json, pathtonuts2)
    .defer(dsv, pathtocoverageIT, ",")
    .await(makeMap); // when data arrives call makeMap function

function makeMap(error, euNuts2, coverageItaly) {
    if (error) {
        console.log("*** ERROR LOADING FILES: " + error + " ***");
        throw error;
    }
    var cantons = topojson.feature(euNuts2, euNuts2.objects.nuts2);

    nestedData = d3.nest()
        .key(function(d) { return d['NUTS2_ID'] })
        .key(function(d) { return d.YEAR })
        .key(function(d) { return d.ANTIGEN})
        .map(coverageItaly);

    mapTitles.year = menuGen(menuYear, 'd.YEAR');
    mapColors.antigen = menuGen(menuAntigen, 'd.ANTIGEN');

    mymap
        .attr("class", "area")
        .selectAll("path")
        .data(cantons.features)
        .enter()
        .append("path")
        .attr("d", path)
        .attr("class",function (d){if(nestedData.has(d.id)) return "dataPresent";}) 
        .attr("fill", function (d){return mapColors(d)})
        .on("mouseover", function (){makeMap.currentcolor = d3.select(this).attr("fill"); d3.select(this).attr("fill", "hotpink");})
        .on("mouseout", function (){d3.select(this).attr("fill", makeMap.currentcolor);})  
        .append("title")
        .text(function (d){return mapTitles(d);});

legend.append("g")
            .selectAll("g")
            .data(mapTitles(regionInfo).split('\n'))
            .enter()
            .each(function(data,y){
               legend.append("g")
               .attr("class", "generalInfoRow")
               .selectAll("text")
               .data(d3.range(0, 4))
               .enter()
               .datum(function(d){return data.split('\t')})
               .append("text")
               
               .attr("x", function(d, x){return x * 50})
               .attr("y", 100 + y * 15)
               .attr("fill", "#000")
               .attr("text-anchor", "start")
               .attr("font-weight", "bold")
               .text(function(d, i){return d[i];});
             });

function menuGen(menu, field){
      var data = d3.nest()
        .key(function(d) { return eval(field)}).sortKeys(d3.ascending)
        .rollup(function(leaves) { return leaves.length; })
        .entries(coverageItaly);

      menu.append("select")
        .selectAll("option")
        .data(data)
        .enter()
        .append("option")
        .attr("value", function(d) {return d.key;})
        .text(function(d) {return d.key; });

      menu.select("select")
        .on("change", function() {
          var dataPresentArea = d3.selectAll(".dataPresent");
          if (menu == menuYear) {
            mapTitles.year = this.value;
            dataPresentArea.select("title").text(function (d){return mapTitles(d)});
            d3.selectAll(".generalInfoRow")
              .datum(mapTitles(regionInfo).split('\n'))
               .each(function(currentRow,i){ 
                 d3.selectAll(this.childNodes)
                  .datum(currentRow[i].split('\t'))
                  .text(function(d, i){return d[i]});});
            } else { 
            mapColors.antigen = this.value;
          }
          dataPresentArea.attr("fill", function (d){return mapColors(d)});
       });
  return menu.select("option").text();
  }
}; // end makeMap function 

function mapColors(area){
      var colorArea = "lightgray";
      if (nestedData && nestedData.has(area.id)) {
        var cantonInfo = nestedData.get(area.id)
          .get(mapTitles.year)
          .get(mapColors.antigen);
        percent = cantonInfo[0]["COVERAGE_PERC"];
        colorArea = (isNaN(parseFloat(percent))) ? "steelblue" : color(percent); 
      }
      return colorArea;
}

function mapTitles(area){
      var title = area.id + '\n' + area.properties.name + '\n' + mapTitles.year + '\n\n';
      if (nestedData && nestedData.has(area.id)) { 
         title += 'Region vaccination coverage (in %)\n';
         nestedData
      .get(area.id)
      .get(mapTitles.year)
      .each(function(d, key) {
            if (key.length < 8) key += '\t';
        title += key + '\t-\t' + d[0]["COVERAGE_PERC"] + '\n';
        });
      }
      return title;
}
<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <title>Map</title>

    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
    <script src="https://d3js.org/topojson.v2.min.js"></script>

</head>

<body>
    <div class="chart-title">Vaccination coverage in Europe</div>
    <div> <span id="years_list"></span><span id="antigen_list"></span></div>
    <div id="container-map"></div>
<!--
    <script src="./script/map.js"></script>
-->
</body>

enter image description here Draft copy )) for test HAPPY NEW YEAR!

Update 1 (2020):

Dropbox or Author deleted original CSV Coverage Italy 24 months.csv file and the project stopped working :-(

now work color fill map for little part of Italy - Piemonte

(BD CSV inside in HTML)

<pre id="data-IT">
ID;NUTS2-ID;NUTS2-NAME;YEAR;ANTIGEN;COVERAGE-PERC
1;IT;Italy;2000;POL;96,6
...
</pre>

and acccess to DB(CSV in HTML)

var data = d3.dsvFormat(delimiter).parse(d3.select("pre#data-IT").text());

Thanks for good idea @AmeliaBR CSV file n HTML over D3

var width = 700;
var height = 600;

var svg = d3.select("#container-map").append("svg")
    .attr("width", width)
    .attr("height", height);

var projection = d3.geoMercator().scale(1000).center([25,45]);
var path = d3.geoPath(projection);

//var pathtonuts2 = './data/GeoJSON/nuts2.json';
//var pathtocoverageIT =  './data/csv/Coverage Italy 24 months.csv';

// for test
    var pathtonuts2 = 'https://gist.githubusercontent.com/rveciana/5919944/raw/2fef6be25d39ebeb3bead3933b2c9380497ddff4/nuts2.json';
var pathtocoverageIT = 'https://dl.dropboxusercontent.com/s/r8mpix4aqytvp31/Coverage%20Italy%2024%20months.csv';

//Parser CSV with any delimiter from file
/*
var dsv = function (url, delimiter, callback){
  d3.text(url, function(text) {
    var data = d3.dsvFormat(delimiter).parse(text);
    callback(null, data);
  });
}
*/

//Parser CSV with any delimiter from HTML

var dsv = function (url, delimiter, callback){
  
    var data = d3.dsvFormat(delimiter).parse(d3.select("pre#data-IT").text());
    callback(null, data);
  
}

var regionInfo = {"properties":{"name":"Italy"}, "id" : "IT"};

var menuYear = d3.select("#years_list");
var menuAntigen = d3.select("#antigen_list");
var nestedData;
var mymap = svg.append("g");

/////////////SCALE//////////////
var x = d3.scaleLinear()
    .domain([0, 100])
    .rangeRound([500, 700]);

var color = d3.scaleThreshold()
    .domain(d3.range(0, 101, 25))
    .range(d3.schemeRdYlGn[5]);

var g = svg.append("g")
    .attr("class", "key")
    .attr("transform", "translate(-20,20)");

//Scale colors
g.selectAll("rect")
    .data(color.range().map(function(d) {
      d = color.invertExtent(d);
      if (d[0] == null) d[0] = x.domain()[0];
      if (d[1] == null) d[1] = x.domain()[1];
      return d;
    }))
    .enter().append("rect")
    .attr("height", 8)
    .attr("x", function(d) { return x(d[0]); })
    .attr("width", function(d) { return x(d[1]) - x(d[0]); })
    .attr("fill", function(d) { return color(d[0]); });

g.append("text")
    .attr("class", "caption")
    .attr("x", x.range()[0])
    .attr("y", -6)
    .attr("fill", "#000")
    .attr("text-anchor", "start")
    .attr("font-weight", "bold")
    .text("Vaccination coverage");

//Scale digital 
g.call(d3.axisBottom(x)
    .tickSize(13)
    .tickFormat(function(x, i) { return i ? x : x + "%"; })
    .tickValues(color.domain()))
    .select(".domain")
    .remove();

////////////SCALE END////////////////////

legend = g.append("g")
           .attr("transform", function(d, i) { return "translate(" + x([x.domain()[0]]) + "," + i * 15 + ")"; });

var scaleStepSize = (x([x.domain()[1]]) - x([x.domain()[0]])) / (color.domain().length - 1);

legend.append("rect")
      .attr("y", 42)
      .attr("width", scaleStepSize)
      .attr("height", 8)
      .style("fill", "steelblue");

legend.append("text")
      .attr("dx", scaleStepSize + 10)
      .attr("y", 50)
      .attr("fill", "#000")
      .attr("text-anchor", "start")
      .attr("font-weight", "bold")
      .text("- no info about region");

d3.queue()
    .defer(d3.json, pathtonuts2)
    .defer(dsv, pathtocoverageIT, ";")
    .await(makeMap); // when data arrives call makeMap function

function makeMap(error, euNuts2, coverageItaly) {
    if (error) {
        console.log("*** ERROR LOADING FILES: " + error + " ***");
        throw error;
    }
    var cantons = topojson.feature(euNuts2, euNuts2.objects.nuts2);

    nestedData = d3.nest()
        .key(function(d) { return d['NUTS2-ID']})
        .key(function(d) { return d.YEAR })
        .key(function(d) { return d.ANTIGEN})
        .map(coverageItaly);
    mapTitles.year = menuGen(menuYear, 'd.YEAR');
    mapColors.antigen = menuGen(menuAntigen, 'd.ANTIGEN');

    mymap
        .attr("class", "area")
        .selectAll("path")
        .data(cantons.features)
        .enter()
        .append("path")
        .attr("d", path)
        .attr("class",function (d){if(nestedData.has(d.id)) return "dataPresent";}) 
        .attr("fill", function (d){return mapColors(d)})
        .on("mouseover", function (){makeMap.currentcolor = d3.select(this).attr("fill"); d3.select(this).attr("fill", "hotpink");})
        .on("mouseout", function (){d3.select(this).attr("fill", makeMap.currentcolor);})  
        .append("title")
        .text(function (d){return mapTitles(d);});

legend.append("g")
            .selectAll("g")
            .data(mapTitles(regionInfo).split('\n'))
            .enter()
            .each(function(data,y){
               legend.append("g")
               .attr("class", "generalInfoRow")
               .selectAll("text")
               .data(d3.range(0, 4))
               .enter()
               .datum(function(d){return data.split('\t')})
               .append("text")
               
               .attr("x", function(d, x){return x * 50})
               .attr("y", 100 + y * 15)
               .attr("fill", "#000")
               .attr("text-anchor", "start")
               .attr("font-weight", "bold")
               .text(function(d, i){return d[i];});
             });

function menuGen(menu, field){
      
      var data = d3.nest()
        .key(function(d) { return eval(field)}).sortKeys(d3.ascending)
        .rollup(function(leaves) { return leaves.length; })
        .entries(coverageItaly);

      menu.append("select")
        .selectAll("option")
        .data(data)
        .enter()
        .append("option")
        .attr("value", function(d) {return d.key;})
        .text(function(d) {return d.key; });

      menu.select("select")
        .on("change", function() {
          var dataPresentArea = d3.selectAll(".dataPresent");
          if (menu == menuYear) {
            mapTitles.year = this.value;
            dataPresentArea.select("title").text(function (d){return mapTitles(d)});
            d3.selectAll(".generalInfoRow")
              .datum(mapTitles(regionInfo).split('\n'))
               .each(function(currentRow,i){ 
                 d3.selectAll(this.childNodes)
                  .datum(currentRow[i].split('\t'))
                  .text(function(d, i){return d[i]});});
            } else { 
            mapColors.antigen = this.value;
          }
          dataPresentArea.attr("fill", function (d){return mapColors(d)});
       });
  return menu.select("option").text();
  }
}; // end makeMap function 

function mapColors(area){
      var colorArea = "lightgray";
            
      if (nestedData && nestedData.has(area.id)) {
        var cantonInfo = nestedData.get(area.id)
          .get(mapTitles.year)
          .get(mapColors.antigen);
         
        percent = cantonInfo[0]["COVERAGE-PERC"];
        colorArea = (isNaN(parseFloat(percent))) ? "steelblue" : color(percent); 
      }
      return colorArea;
}

function mapTitles(area){
      var title = area.id + '\n' + area.properties.name + '\n' + mapTitles.year + '\n\n';
      if (nestedData && nestedData.has(area.id)) { 
         title += 'Region vaccination coverage(in %)\n';
         nestedData
      .get(area.id)
      .get(mapTitles.year)
      .each(function(d, key) {
            if (key.length < 7) key += '\t';
        title += key + '\t-\t' + d[0]["COVERAGE-PERC"] + '\n';
        });
      }
      return title;
}
pre {
    display:none;
}
<!DOCTYPE html>
<head>
    <meta charset="utf-8">
    <title>Map</title>

    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
    <script src="https://d3js.org/topojson.v2.min.js"></script>

</head>

<body>
    <div class="chart-title">Vaccination coverage in Europe</div>
    <div> <span id="years_list"></span><span id="antigen_list"></span></div>
    <div id="container-map"></div>
<!--
    <script src="./script/map.js"></script>
-->
</body>

<pre id="data-IT">
ID;NUTS2-ID;NUTS2-NAME;YEAR;ANTIGEN;COVERAGE-PERC
1;IT;Italy;2000;POL;96,6
2;IT;Italy;2000;DIF;
3;IT;Italy;2000;TET;
4;IT;Italy;2000;PER;
5;IT;Italy;2000;DT-DTP3;95,3
6;IT;Italy;2000;EP B;94,1
7;IT;Italy;2000;HIB;54,7
8;IT;Italy;2000;MOR;
9;IT;Italy;2000;PAR;
10;IT;Italy;2000;ROS;
11;IT;Italy;2000;VAR;
12;IT;Italy;2000;M-MPR1-MPRV;
13;IT;Italy;2000;M-MPR1;74,1
14;IT;Italy;2001;POL;95,8
15;IT;Italy;2001;DIF;
16;IT;Italy;2001;TET;
17;IT;Italy;2001;PER;
18;IT;Italy;2001;DT-DTP3;95,9
19;IT;Italy;2001;EP B;94,5
20;IT;Italy;2001;HIB;70,2
21;IT;Italy;2001;MOR;
22;IT;Italy;2001;PAR;
23;IT;Italy;2001;ROS;
24;IT;Italy;2001;VAR;
25;IT;Italy;2001;M-MPR1-MPRV;
26;IT;Italy;2001;M-MPR1;76,9
27;IT;Italy;2002;POL;95,9
28;IT;Italy;2002;DIF;
29;IT;Italy;2002;TET;
30;IT;Italy;2002;PER;
31;IT;Italy;2002;DT-DTP3;96,8
32;IT;Italy;2002;EP B;95,4
33;IT;Italy;2002;HIB;83,4
34;IT;Italy;2002;MOR;
35;IT;Italy;2002;PAR;
36;IT;Italy;2002;ROS;
37;IT;Italy;2002;VAR;
38;IT;Italy;2002;M-MPR1-MPRV;
39;IT;Italy;2002;M-MPR1;80,8
40;IT;Italy;2003;POL;96,6
41;IT;Italy;2003;DIF;
42;IT;Italy;2003;TET;
43;IT;Italy;2003;PER;
44;IT;Italy;2003;DT-DTP3;96,6
45;IT;Italy;2003;EP B;95,4
46;IT;Italy;2003;HIB;90,4
47;IT;Italy;2003;MOR;
48;IT;Italy;2003;PAR;
49;IT;Italy;2003;ROS;
50;IT;Italy;2003;VAR;
51;IT;Italy;2003;M-MPR1-MPRV;
52;IT;Italy;2003;M-MPR1;83,9
53;IT;Italy;2004;POL;96,8
54;IT;Italy;2004;DIF;
55;IT;Italy;2004;TET;
56;IT;Italy;2004;PER;
57;IT;Italy;2004;DT-DTP3;96,6
58;IT;Italy;2004;EP B;96,3
59;IT;Italy;2004;HIB;93,8
60;IT;Italy;2004;MOR;
61;IT;Italy;2004;PAR;
62;IT;Italy;2004;ROS;
63;IT;Italy;2004;VAR;
64;IT;Italy;2004;M-MPR1-MPRV;
65;IT;Italy;2004;M-MPR1;85,7
222;ITC1;Piemonte;2000;POL;97,2
223;ITC1;Piemonte;2000;DIF;
224;ITC1;Piemonte;2000;TET;
225;ITC1;Piemonte;2000;PER;
226;ITC1;Piemonte;2000;DT-DTP3;97,2
227;ITC1;Piemonte;2000;EP B;97,2
228;ITC1;Piemonte;2000;HIB;44
229;ITC1;Piemonte;2000;MOR;
230;ITC1;Piemonte;2000;PAR;
231;ITC1;Piemonte;2000;ROS;
232;ITC1;Piemonte;2000;VAR;
233;ITC1;Piemonte;2000;M-MPR1-MPRV;
234;ITC1;Piemonte;2000;M-MPR1;67,6
235;ITC1;Piemonte;2001;POL;97,2
236;ITC1;Piemonte;2001;DIF;
237;ITC1;Piemonte;2001;TET;
238;ITC1;Piemonte;2001;PER;
239;ITC1;Piemonte;2001;DT-DTP3;97,2
240;ITC1;Piemonte;2001;EP B;97,2
241;ITC1;Piemonte;2001;HIB;66,8
242;ITC1;Piemonte;2001;MOR;
243;ITC1;Piemonte;2001;PAR;
244;ITC1;Piemonte;2001;ROS;
245;ITC1;Piemonte;2001;VAR;
246;ITC1;Piemonte;2001;M-MPR1-MPRV;
247;ITC1;Piemonte;2001;M-MPR1;73,2
248;ITC1;Piemonte;2002;POL;96,9
249;ITC1;Piemonte;2002;DIF;
250;ITC1;Piemonte;2002;TET;
251;ITC1;Piemonte;2002;PER;
252;ITC1;Piemonte;2002;DT-DTP3;96,9
253;ITC1;Piemonte;2002;EP B;96,9
254;ITC1;Piemonte;2002;HIB;72,4
255;ITC1;Piemonte;2002;MOR;
256;ITC1;Piemonte;2002;PAR;
257;ITC1;Piemonte;2002;ROS;
258;ITC1;Piemonte;2002;VAR;
259;ITC1;Piemonte;2002;M-MPR1-MPRV;
260;ITC1;Piemonte;2002;M-MPR1;79,3
261;ITC1;Piemonte;2003;POL;97,1
262;ITC1;Piemonte;2003;DIF;
263;ITC1;Piemonte;2003;TET;
264;ITC1;Piemonte;2003;PER;
265;ITC1;Piemonte;2003;DT-DTP3;97
266;ITC1;Piemonte;2003;EP B;95,9
267;ITC1;Piemonte;2003;HIB;79,7
268;ITC1;Piemonte;2003;MOR;
269;ITC1;Piemonte;2003;PAR;
270;ITC1;Piemonte;2003;ROS;
271;ITC1;Piemonte;2003;VAR;
272;ITC1;Piemonte;2003;M-MPR1-MPRV;
273;ITC1;Piemonte;2003;M-MPR1;84,4
274;ITC1;Piemonte;2004;POL;97,1
275;ITC1;Piemonte;2004;DIF;
276;ITC1;Piemonte;2004;TET;
277;ITC1;Piemonte;2004;PER;
278;ITC1;Piemonte;2004;DT-DTP3;97
279;ITC1;Piemonte;2004;EP B;96,8
</pre>

PS copy this examle in JSFiddle

Akubik
  • 500
  • 2
  • 9
  • Thank you so much for your help. I understood why the csv file path was wrong. Now the map is displayed correctly. But the page is loading very slowly. I think because the json file is very large, it's possible? My only idea is to convert the json file into topojson, could it improve the situation or are there better ideas? –  Dec 26 '17 at 14:26
  • `json` very large because contains all regions in EU, (may be need only Italy? or not) and now i think about CSV with delimiter `;` – Akubik Dec 26 '17 at 14:58
  • you reloaded file CSV? in your question CSV format is `ID;NUTS2-ID;... 1;IT;...` but now CSV format in dropbox `ID,NUTS2_ID,... 1;IT;...` i.e A) Now delimeter `;` replaced to `,` B) in keys of fields CSV char `-` replaced to `_`. Please, correct your first question... And CSV file contains fields of region "IT" - is this general information for all regions? – Akubik Dec 29 '17 at 11:45