D3.js Choropleth 使用 JSON 中的数据

D3.js Choropleth using data in JSON

我正在尝试制作类似于 Mike Bostock 的 Choropleth (https://bl.ocks.org/mbostock/4060606) 的地图。

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

.counties {
  fill: none;
}

.states {
  fill: none;
  stroke: #fff;
  stroke-linejoin: round;
}

</style>
<svg width="960" height="600"></svg>
<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>
<script>

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

var unemployment = d3.map();

var path = d3.geoPath();

var x = d3.scaleLinear()
    .domain([1, 10])
    .rangeRound([600, 860]);

var color = d3.scaleThreshold()
    .domain(d3.range(2, 10))
    .range(d3.schemeBlues[9]);

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

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("Unemployment rate");

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

d3.queue()
    .defer(d3.json, "https://d3js.org/us-10m.v1.json")
    .defer(d3.tsv, "unemployment.tsv", function(d) { 
      console.log(d);
      unemployment.set(d.id, +d.rate); })
    .await(ready);

function ready(error, us) {
  if (error) throw error;

  svg.append("g")
      .attr("class", "counties")
    .selectAll("path")
    .data(topojson.feature(us, us.objects.counties).features)
    .enter().append("path")
      .attr("fill", function(d) { return color(d.rate = unemployment.get(d.id)); })
      .attr("d", path)
    .append("title")
      .text(function(d) { return d.rate + "%"; });

  svg.append("path")
      .datum(topojson.mesh(us, us.objects.states, function(a, b) { return a !== b; }))
      .attr("class", "states")
      .attr("d", path);
}

</script>

我不想使用 TSV 文件,而是想对我的数据使用 JSON。我已经尝试弄清楚如何将脚本转换为使用 JSON,但没有成功。目前,我正在使用 JSON 作为示例结构:

[{"id": "01001", "rate": 5.1},
{"id": "01003", "rate": 4.9},
{"id": "01005", "rate": 8.6},
{"id": "01007", "rate": 6.2},
{"id": "01009", "rate": 5.1},
{"id": "01011", "rate": 7.1}]

最终,我想下拉到 select 要显示的内容 "column",甚至弄清楚如何过滤年份范围。我的最终 JSON 结构如下:

[{
   "ID": "02",
   "Volume": "NULL",
   "SubCategory": "Other Housing",
   "Category": "Use",
   "Type": "PAB",
   "date": "2013-12-31",
   "State": "Ohio"
 },
 {
   "ID": "02",
   "Volume": 24800000,
   "SubCategory": "Student Loans",
   "Category": "Use",
   "Type": "PAB",
   "date": "2013-12-31",
   "State": "Ohio"
 },
 {
   "ID": "02",
   "DateExcelNo": "12/31/14",
   "Volume": 440200000,
   "SubCategory": "Mort Rev Bonds",
   "Category": "Use",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Ohio"
 },
 {
   "ID": "02",
   "Volume": 1205000000,
   "SubCategory": "Total Cap",
   "Category": "VolumeCapacity",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Ohio"
 },
 {   "ID": "01",
   "Volume": "NULL",
   "SubCategory": "Other Housing",
   "Category": "Use",
   "Type": "PAB",
   "date": "2013-12-31",
   "State": "Connecticut"
 },
 {
   "ID": "01",
   "Volume": 24800000,
   "SubCategory": "Student Loans",
   "Category": "Use",
   "Type": "PAB",
   "date": "2013-12-31",
   "State": "Connecticut"
 },
 {
   "ID": "01",
   "DateExcelNo": "12/31/14",
   "Volume": 440200000,
   "SubCategory": "Mort Rev Bonds",
   "Category": "Use",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Connecticut"
 },
 {
   "ID": "01",
   "Volume": 1205000000,
   "SubCategory": "Total Cap",
   "Category": "VolumeCapacity",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Connecticut"
 },
 {
   "ID": "01",
   "Volume": 663400000,
   "SubCategory": "Total Carryforward to Next Year",
   "Category": "Carryfwd",
   "Type": "PAB",
   "date": "2014-12-31",
   "State": "Connecticut"
 }]

这里的问题很简单:d3.json接受行函数。

我相信你认为,鉴于 JSON 你在你的问题中分享,你可以简单地改变这一行...

.defer(d3.tsv, "unemployment.tsv", function(d) { unemployment.set(d.id, +d.rate); })

...为此:

.defer(d3.json, "unemployment.json", function(d) { unemployment.set(d.id, +d.rate); })

但是,这将不起作用,因为您不能将行函数(第二个逗号后的所有内容都是行函数)与 d3.json.

一起使用

解决方法:

创建您的 JSON 文件(此处命名为 "unemployment.json")并在队列中执行此操作:

.defer(d3.json, "unemployment.json")

然后,在ready函数中,加载JSON作为第三个参数(这里我称之为un):

function ready(error, us, un) {

最后,填充您的 d3.map():

un.forEach(function(d) { 
    unemployment.set(d.id, +d.rate); 
});

这是更新后的 bl.ocks(当然,只有 6 个县有颜色,因为我使用的 JSON 只有 6 个数据点):https://bl.ocks.org/anonymous/211bf9775e61c5b07935536ed1926225

PS:在 S.O 上每个问题只问 一个问题 是一种很好的做法。因此,我建议你post另一个关于下拉问题的问题。