在地图上使用不同域的不同度量的数据可视化

时间:2017-12-29 14:19:16

标签: d3.js colors data-visualization

我有一张欧洲地图,按国家和不同的措施划分,使用choropletic地图表示。 根据选定的单选按钮,地图将根据csv文件中的值进行着色。

以下是代码:

的index.html

<!DOCTYPE html>
<html lang="en">

<head>
   <meta charset="utf-8">
   <script src="https://d3js.org/d3.v4.min.js" charset="utf-8"></script>
   <script src="https://d3js.org/topojson.v2.min.js"></script>
   <script src="//code.jquery.com/jquery-latest.min.js" type="text/javascript"></script>
   <link rel="stylesheet" type="text/css" href="./map.css" media="screen" />
</head>

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

   <div id="radio-container">
      <form id="radio-selector">
         <input type="radio" name="radio-selector" id="rb1" value="m1" checked />
         <label for="rb1">Measure 1</label>
         <br>
         <input type="radio" name="radio-selector" id="rb1" value="m2" />
         <label for="rb2">Measure 2</label>
         <br>
         <input type="radio" name="radio-selector" id="rb1" value="m3" />
         <label for="rb3">Measure 3</label>
      </form>
   </div>

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

</html>

map.js

var csvValue = [];

var projection = d3.geoMercator()
   .scale(500)
   .translate([200, 700])

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

var width = 700;
var height = 400;

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

// to color countries
var colors = d3.scaleLinear()
   .domain([0, 100]) 
   .range(["#131313", "#ba3c28"]); 

var measSelected = document.querySelector('input[name=radio-selector]:checked').value;


var pathToNuts0 = 'https://gist.githubusercontent.com/rveciana/5919944/raw/2fef6be25d39ebeb3bead3933b2c9380497ddff4/nuts0.json';

d3.queue()
   .defer(d3.json, pathToNuts0)
   .defer(d3.csv, './data.csv')
   .await(makeMap); 

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

   csvValue = d3.nest()
      .key(function(d) {
         return d.MEASURE;
      })
      .object(data);

   var country = topojson.feature(nuts0, nuts0.objects.nuts0);

   // create map
   svg.selectAll("path")
      .data(country.features)
      .enter().append("path")
      .attr("class", "country")
      .attr("id", function(d) {
         return "country" + d.properties.nuts_id;
      })
      .attr("d", path);

   colorCountries();
}

function colorCountries() {
   svg.selectAll("path")
      .attr("fill", function(d) {
         var col = +getColor(d.properties.nuts_id);
         return colors(col); 
      });
}

var getColor = function(nutsId) {
   measSelected = document.querySelector('input[name=radio-selector]:checked').value;
   var complete = csvValue[measSelected].slice();
   var selectedValue = complete.find(function(tupla) {
      return tupla.ID_NUT == nutsId;
   });
   if (selectedValue == null) {
      return -1;
   } 
   else {
      var value = selectedValue.VALUE;
      return value;
   }
}


function measures() {
    var measSelected = document.querySelector('input[name="radio-selector"]:checked').value; 
}

var updateRadio = function() {
    measSelected = $('input[name=radio-selector]:checked', '#desses').val(); 
    colorCountries(); 
    measures();
}
$("#radio-selector").on("change", updateRadio);

data.csv

ID_NUT,MEASURE,VALUE
AT,m1,97.1
AT,m2,74
AT,m3,28.53
BE,m1,98
BE,m2,97.1
BE,m3,8
BG,m1,94.5
BG,m2,56
BG,m3,38.42
CY,m1,99.32
CY,m2,91
CY,m3,23.42
CZ,m1,98.5
CZ,m2,4
CZ,m3,64.51
DE,m1,97
DE,m2,2
DE,m3,78.77
DK,m1,96.8
DK,m2,95
DK,m3,86.95
EE,m1,95.8
EE,m2,79
EE,m3,84.10
EL,m1,96.4
EL,m2,68
EL,m3,42.78
ES,m1,93.9
ES,m2,69
ES,m3,95.4
FI,m1,97.8
FI,m2,36
FI,m3,98.65
FR,m1,97.9
FR,m2,74
FR,m3,99.75
HR,m1,99.1
HR,m2,39
HR,m3,63.78
HU,m1,96.12
HU,m2,84
HU,m3,81
IE,m1,98.55
IE,m2,89
IE,m3,69.4
IT,m1,99.65
IT,m2,40
IT,m3,75.93
LT,m1,97.45
LT,m2,56
LT,m3,93.67
LU,m1,97.63
LU,m2,19
LU,m3,31.48
LV,m1,95.24
LV,m2,71
LV,m3,39
MT,m1,96.52
MT,m2,85
MT,m3,93
NL,m1,98
NL,m2,39
NL,m3,88.88
PL,m1,99.10
PL,m2,77
PL,m3,15
PT,m1,94.15
PT,m2,95
PT,m3,15
RO,m1,97
RO,m2,71
RO,m3,74
SE,m1,89.4
SE,m2,92
SE,m3,69.64
SI,m1,97.86
SI,m2,52
SI,m3,74.78
SK,m1,98
SK,m2,85
SK,m3,88
UK,m1,99.4
UK,m2,100
UK,m3,97

代码是正确的,它有效并且不会产生错误。 我想解决的问题是颜色问题。

在csv文件中,所有值都在[0, 100]范围内,因为它们代表百分比。 如在csv中所见,与m1对应的值是非常高的值(>=90),而引用m2m3的值变化很大。 如果我只使用其域为[0, 100]的一个色标(如代码中所写),那么骑到m1的瞄准线图不是很重要。 我该如何解决这个问题?

我想要做的是对所有三个测量使用单个色标,但要确保值之间的差异,即使是最小的差异。

我的问题是理论而不是实践。如果有更好的话,我不在乎代码。 我真的很想要一个想法级别的解决方案,我该如何处理并解决这个问题?

由于

  

关于主题的问题:在这种情况下如何使用代码段堆栈?如何添加外部文件(data.csv)?

----------

我修改了我的代码:

var colors = d3.scaleQuantile()
   .domain([0, 100]) 
   .range(["#131313", "#241715", "#341b17", "#451f19", "#56231b", "#67281e", "#772c20", "#883022", "#993424", "#a93826", "#ba3c28"]); 

不幸的是,我没有得到任何视觉效果(甚至错误)。 对于scaleLinear()scaleQuantile(),结果如下: enter image description here

其他问题。使用这种技术(如果我理解根据数据域正确拟合)我会为每个度量设置不同的传说吗?

我的数据采用以下格式:

ID,COUNTRY,YEAR,A,B,C
AL,Albania,2000,98,50,10
AL,Albania,2001,41,2,14
AL,Albania,2002,75,51,10
DE,Germany,2000,74,21,25
DE,Germany,2001,46,2,48
DE,Germany,2002,74,81,90
...

所以我没有像var data = [..., ...]这样的简单数组。

1 个答案:

答案 0 :(得分:3)

您的问题有多种解决方案。但是,在这种情况下,&#34;正确的&#34; 解决方案正在使用足够的比例。

在谈论足够的规模之前,让我们仔细研究你的问题并讨论一些连续的规模方法。

您的问题

正如您在问题中所解释的那样,问题是您的数据严重偏向域的一端。

为了形象化,我创建了这个简单的数据集......

var data = [2, 30, 60, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99];

...从0转到100,但&#34;偏向右边&#34;

鉴于您的颜色,这是使用简单线性刻度的结果:

&#13;
&#13;
var data = [0, 30, 60, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 100];
var scale = d3.scaleLinear()
  .domain([0, 100])
  .range(["#131313", "#ba3c28"])
var div = d3.select("body").selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .style("background-color", d => scale(d))
&#13;
div {
  width: 20px;
  height: 20px;
  display: inline-block;
  margin: 4px;
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
&#13;
&#13;

如您所知,我们无法轻易看到大多数值的差异。

您也可以尝试让您的域名出现偏差。例如,使用具有高指数的功率刻度:

&#13;
&#13;
var data = [0, 30, 60, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 100];
var scale = d3.scalePow()
  .exponent(10)
  .domain([0, 100])
  .range(["#131313", "#ba3c28"])
var div = d3.select("body").selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .style("background-color", d => scale(d))
&#13;
div {
  width: 20px;
  height: 20px;
  display: inline-block;
  margin: 4px;
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
&#13;
&#13;

这种方法的问题在于,它将是一个反复试验的游戏,直到你获得正确的指数,这对于每个数据集都是不同的......

所以,让我们放弃连续刻度并使用&#34;正确&#34; 刻度:

分位数刻度

不是连续比例,而是用于查看人口差异的最佳比例是分位数比例。根据{{​​3}}:

  

分位数刻度将采样输入域映射到离散范围。该域名被认为是连续的,因此该量表将接受任何合理的输入值;但是,域被指定为一组离散的样本值。输出范围(基数)中的值的数量决定了将从域计算的分位数的数量。要计算分位数,对域进行排序,并将其视为一组离散值。 (强调我的)

因此,第一步是创建范围数组。让我们创建一个包含10种颜色的数组。根据您的颜色,它将是:

["#131313", "#241715", "#341b17", "#451f19", "#56231b", "#67281e", "#772c20", "#883022", "#993424", "#a93826", "#ba3c28"]

然后,使用该范围,我们创建分位数比例:

var scale = d3.scaleQuantile()
  .domain(data)
  .range(["#131313", "#241715", "#341b17", "#451f19", "#56231b", "#67281e", "#772c20", "#883022", "#993424", "#a93826", "#ba3c28"])

这是演示:

&#13;
&#13;
var data = [0, 30, 60, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 100];
var scale = d3.scaleQuantile()
  .domain(data)
  .range(["#131313", "#241715", "#341b17", "#451f19", "#56231b", "#67281e", "#772c20", "#883022", "#993424", "#a93826", "#ba3c28"])
var div = d3.select("body").selectAll(null)
  .data(data)
  .enter()
  .append("div")
  .style("background-color", d => scale(d));
&#13;
div {
  width: 20px;
  height: 20px;
  display: inline-block;
  margin: 4px;
}
&#13;
<script src="https://d3js.org/d3.v4.min.js"></script>
&#13;
&#13;
&#13;