如何使用d3、javascript和json文件将地图划分为邮政编码区域?

5
我正在尝试创建一个纽约地图,该地图具有可以根据人口普查数据进行着色的邮政编码区域(例如,如果大部分是白人,则将区域涂成红色,如果大部分是非白人,则将其涂成蓝色)。我只是使用了在此处找到的其中一个形状文件之一(https://data.cityofnewyork.us/Business/Zip-Code-Boundaries/i8iw-xf4u/data)。
我将shp文件转换为geojson,然后再转换为topojson文件。
如果有人能够查看下面的代码,并让我知道如何处理此事,我将不胜感激。
代码:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
</style>
<body>
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
<script src="//d3js.org/topojson.v1.min.js"></script>

<script>

var width = 500,
    height = 500;

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

  var projection = d3.geo.albers()
   .center([0,40.7])
   .rotate([74,0])
   .translate([width/2,height/2])
   .scale(65000);

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

d3.json("zipcode.json", function(error, uk) {
    console.log(uk)
    console.log(uk.objects)
    console.log(uk.objects.zipcode)
  if (error) return console.error(error);
  var subunits = topojson.feature(uk, uk.objects.zipcode);

    svg.append("path")
        .datum(subunits)
        .attr("d", path);
});

输出:

输入图像描述

我代码的最后一部分(以及第一部分)是仿照 https://bost.ocks.org/mike/map/ 编写的。我理解我正在尝试“选择”某些特征数组中的所有内容,以便从我使用的json文件中创建路径。在我的数据中,有一个坐标数组,我正在尝试访问并使用它。我的代码没有抛出任何错误,所以我不知道该去哪里调试。

此外,在这一步中,我应该给路径创建的区域着色还是在创建路径之后才能着色?

1个回答

13

这个答案使用d3 v3,并考虑到了人口普查区域而不是邮政编码(反映出原始编辑的意图,但原则仍然相同)

选择器在添加特征中的作用:

我明白我正在尝试从json文件中“选择所有”某种特征数组,以便创建路径。

与其从json文件中选择某些内容,不如选择DOM中的元素。D3将数据绑定到存在的特征上,将它们绑定到存在的特征上,对于没有绑定数据的特征元素,则产生一个enter()选择集合,对于在DOM中选择过多的元素,则产生一个exit()选择集合。

这就是为什么最初的数据附加语句selectAll(type).data(data)被跟随着通常会有一个.enter()语句。enter返回必须添加到DOM的元素:

svg.selectAll(".tract")
    // bind data to the selection
    .data(topojson.feature(uk, uk.objects.nyct2010).features)
  .enter()
    // set properties for the new elements:
    .append("path") 
    .attr("class", "tract")
    .attr("d", path);

如果您正在更新数据——比如在地图中按年份显示某些属性,如果地理要素的数量不变(很可能是这种情况),则不需要使用.enter(),您只需设置数据,然后修改属性即可。如果新数据数组中的元素数量与旧数组相同,则enter()选择将为空。

此方法的初始附加通常假定所有数据项都会附加到enter选择器中,因为选择器为空,这会导致许多人感到困扰 (a) (b) (c) (d) (e) (f).

当采用替代方法时:

svg.append('path')
  .datum(subunits)
  .attr('d',path')

您只需添加一个包含所有要素的路径元素,这使得单独设置区域样式变得不可能。相比之下,顶部方法为json中的每个元素附加一个路径。

设置地图属性:

您可能难以将每个路径的类设置为d.coordinates。使用topojson.feature(data, data.objects.features).features从您的topojson返回geojson。每个要素的坐标属性是一个数组,这可能无法与类声明配合使用。

但是,您的方法是正确的。嵌入式函数可以轻松设置属性:

var color = d3.scale.category20();

svg.selectAll("path")
  .data(subunits) // from the question code.
  .enter()
  .append('path')
  .attr('fill',function(d,i) { return color(i); })
  .attr("d", path);

使用这个方法我得到了:

输入图像描述

(示例代码)

但是,让我们看一下上面内联函数中的 d ( .attr('fill',function(d,i) { console.log(d); return color(i); }) )。它是一个地理JSON对象:

Object { type: "Feature", properties: Object, geometry: Object }

如果您看不到任何属性(它始终会有一个properties属性,但可能为空或仅包含方法),那么有一些坏消息,这些属性为空。因此,没有包含可显示数据的属性-例如地图着色的属性。数据中也没有标识符。这使得无法将外部数据连接到每个要素,并且要素中没有数据可显示。Topojson不压缩属性,因此如果文件文本中存在它们,则应该能够看到它们:

..."Polygon","properties":{"CTLabel":"1223","BoroCode":"4","BoroName":"Queens","CT2010":"...

显示地理特征的属性

你需要找到一个具有属性的地理数据集。没有属性的特征可能非常适合作为背景,但对于其他所有内容来说却不太有用。

我在这里找到了2010年人口普查区划的数据源(链接)。我下载了该文件并在mapshaper.org中将其转换为topojson格式以便使用(请确保将所有文件都拖放到窗口中,包括数据和投影数据)。数据已经被投影(到纽约州平面),所以您应该通过在控制台中输入proj wgs84 来取消或进行投影处理。这个答案可以帮助理解投影和取消投影数据以及d3方面的问题。

我正在使用的文件具有属性BoroCode,我将使用它来展示全部区域图。

svg.selectAll("path")
   .data(topojson.feature(data, data.objects.nyct2010).features)
   .enter()
   .append('path')
   .attr('fill',function(d) {return color(d.properties.BoroCode); })
   .attr("d", path);

这给了我:

enter image description here

(block)

将数据与要素连接

许多形状文件、topojsons、geosjons、要素类等不包括许多属性。这些包含地理坐标的文件通常会根据在每个数据源中共享的标识符进行数据连接,连接到包含属性/属性(但不包含坐标)的文件。

有一个很好的示例,展示了实际操作中的内容,尽管这里有更好的解释。我将使用其中为数不多的一些文件之一(相对较快和免费)来进行人口普查区标识符。人口普查信息通常很好,因为它包含标准化标识符。该文件是包含可支配收入数据的csv文件。

现在有了共享的标识符,我们可以显示地理形状,并根据csv中的收入值为其分配颜色。

一旦载入了两个文件,我将创建一个词典:

var lookup = {};
income.forEach(function(d) { lookup[d.tractID] = +d.disposable_income; });

然后我会展示这些功能,几乎与上面相同:

svg.selectAll("path")
   .data(topojson.feature(data, data.objects.nyct2010).features)
   .enter()
   .append('path')
   .attr('fill',function(d) { return color(lookup[parseInt(d.properties.CT2010)] ); })
   .attr("d", path);

因为我在Excel中修改了csv,丢失了csv中的前导零,所以我使用了parseInt函数,它从我的geojson标识符中删除了前导零。

最终结果看起来像这样:

enter image description here

(代码块)

如果您查看了代码块,您会发现我将d3.csv嵌套在d3.json的回调函数内部。每个函数都是异步的,因此除非我们使用像queue.js这样的库,否则我们需要等待json加载完成后再加载csv。这种嵌套的方法解决了在加载csv之前可能会对要素进行样式设置的问题

这应该涵盖如何根据增量、地理/拓扑json中的属性和/或通过将非空间来源的数据连接到空间形状来着色地图。请参阅代码块以实现颜色比例尺,如果没有合适的比例尺,color(i)将不返回任何内容。


1
希望这可以帮到你,我会再仔细看一遍并稍微修改一下,等我清醒些 - 但我认为它本身应该是不错的。 - Andrew Reid

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接