构建具有着色条件的地图

Building a map with coloring condition

我正在尝试构建一个不完全是 dc.js 中的等值线的等值线。我想做的是根据着色条件为地图着色,最终这也会与其他图表和过滤器交互。我的 csv 看起来像这样:

country,id,condition,value
AU,1,yes,19
US,2,no,23
US,2,no,30
US,2,no,4    
IN,3,yes,14
SG,4,yes,2
NZ,5,no,6
NZ,5,no,20

到目前为止,这是我的方法,生成出现次数。

var ndx = crossfilter(data)
var countryDimension = ndx.dimension(function (d){
  return d.country
});

var colors = d3.scale.ordinal().domain(['yes','no']).range(["green","blue"])
worldMap.width(mapWidth)
.height(mapHeight)
  .dimension(countryDimension)
  .group(countryDimension.group())
  .projection(project)
  .colors(colors)
  .colorCalculator(function(d){
    return d ? worldMap.colors()(d) : '#d8d8d8';
  })
  .overlayGeoJson(geoJson.features, "id", function(d){
    return d.id;
  })
  .title(function(d){
    return 'Country: ' + d.key + '\nCondition: ' + d.value;
  });

我对 d3 和 dc.js 这个神奇的世界还很陌生。尽管我一直在阅读文档和论坛,但我不知道如何制作地图,条件 'yes' 的国家为绿色,条件 'no' 的国家为蓝色。所以几乎如果我做 console.log(d.value) 它应该 return 'yes' 或 'no'。我不明白我与我的 'group' 有什么关系。

如果每个国家每次在数据中列出时都具有相同的 condition 值,那么从某种意义上说,数据就是 denormalized。这很好,因为交叉过滤器最适合处理单个数据数组。

当然这意味着等值线不会响应刷其他图表,因为该值不受当前过滤的行数的影响。但它将能够过滤其他图表。

计数是

有几种方法可以做到这一点。一种方法是计算是的次数,并根据次数设置值:

var yesnoGroup = countryDimension.group().reduceSum(function(d) {
    return d.condition === 'yes' ? 1 : 0;
});
worldMap.valueAccessor(function(kv) {
    return kv.value ? 'yes' : 'no';
})

获取第一个值

然而,这可能会导致国家在被其他图表过滤掉时变成蓝色。所以你也可以使用这样的 "grab first value and hold onto it" 策略:

var yesnoGroup = countryDimension.group().reduce(
    function(p, v) { // add
        return v.condition;
    },
    function(p, v) { // remove
        return p; // ignore remove event
    },
    function() { // initialize
        return null; // no value
    });

使用交叉过滤器有点丑陋和奇怪,但这只是因为交叉过滤器希望数据对减少的值有一些影响,而这里没有。

编辑:三种状态

根据下面的对话,我了解到您实际上在寻找三种状态:否、零和是。 (这比上面的解决方案更有意义,但我会把它们留给后代。)这里有两种完全不同的方法来解决 no/zero/yes 问题。

这两种解决方案都使用以下 three-way 色标:

var colors = d3.scale.ordinal().domain(['no', 'zero', 'yes']).range(["blue", "grey", "green"])

No/zero/yes 作为 negative/positive 数字

这很聪明也很简单:我们将把每个否记为 -1,将每个是记为 +1。如果总和为零,我们将绘制为灰色。这里唯一需要注意的是,如果数据中存在矛盾,您可能会得到一个错误的零。但这可能比虚假的否或是更好 (?)

var nozeroyesGroup = countryDimension.group().reduceSum(function(d) {
  return d.condition === 'no' ? -1 : d.condition === 'yes' : +1 : 0;
});
worldMap.valueAccessor(function(kv) {
    return kv.value < 0 ? 'no' : kv.value > 0 ? 'yes' : 'zero';
})

No/yes极性

我们也可以分别记住计数和极性。这可能更安全但也可能更慢。 (除非您的数据很大,否则您不会注意到。)它有点复杂。有点偏好问题。

var nozeroyesGroup = countryDimension.group().reduce(
    function(p, v) { // add
        if(p.polarity && p.polarity != v.condition)
            console.warn('inconsistent');
        p.polarity = v.condition;
        ++p.count;
        return p;
    },
    function(p, v) { // remove
        if(p.polarity != v.condition || p.count <= 0)
            console.warn('inconsistent');
        --p.count;
        return p;
    },
    function() { // initialize
        return {count: 0, polarity: null}; // no value
    });
worldMap.valueAccessor(function(kv) {
    return kv.value.count ? kv.value.polarity : 'zero';
})