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

How to divide a map into zipcodes using d3, javascript, and a json file?

我正在尝试创建一个 nyc 地图,其中包含我可以根据人口普查数据在其中着色的邮政编码区域(例如,如果多数是白色,则将区域着色为红色,如果大多数非白人,则将区域着色为蓝色)。我只是使用我从这里在线找到的形状文件之一 (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/ 建模的。我知道我正在尝试 "Select All" 我正在使用的 json 文件中的某种特征数组,以创建路径。在我的数据中,有一个坐标数组,我正在尝试访问和使用它。我的代码没有抛出任何错误,所以我不确定去哪里调试。

此外,我应该在这一步或在创建路径之后为路径创建的区域着色吗?

此答案使用 d3 v3 并考虑人口普查区域而不是邮政编码(反映原始编辑,但原则保持不变)

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

I understand I am trying to "Select All" of the some sort of feature array from the json file I am using in order to create paths.

您选择的不是 json 文件中的内容,而是 DOM 中的元素。 D3 会将 json 中的数据绑定到它们存在的特征,在它们不存在的地方生成一个 enter() 选择,并在其中选择了多余的 DOM 元素的地方生成一个 exit() 选择与 json 数据的关系。

这就是为什么最初使用 selectAll(type).data(data) 语句追加数据后通常会跟随 .enter() 语句的原因。输入 returns 必须添加到 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() 选择实际上为空。

这种方法的初始追加一般假设selectAll语句为空,这样数据中的所有项都追加了enter selection,这让很多人很伤心(a) (b) (c) (d) () ().

使用替代方法时:

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

您只需附加一个包含所有功能的路径元素,这使得无法对单个区域进行样式设置。相反,顶级方法为 json 中的每个元素附加一个路径。

设置地图属性:

您可能难以将每个路径的 class 设置为 d.coordinates。使用来自你的拓扑json的topojson.feature(data, data.objects.features).featuresreturns geojson。每个特征的坐标 属性 是一个数组 - 这可能不适用于 class 声明。

但是,你的方法是对的。一个内联函数可以轻松设置属性:

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);

使用这个我得到:

(block)

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

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

如果你没有看到任何属性(它总是有一个属性属性,但它可能是空的或只包含方法),你有一些坏消息,属性是空的。因此,没有包含可以显示的数据的属性 - 例如,着色地图。数据中也没有标识符。这使得将外部数据连接到每个特征变得不可能,并且特征中没有数据可显示。 Topojson 不压缩属性,因此如果它们存在于文件文本中,您应该能够看到它们:

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

显示地理特征的属性

您需要找到具有属性的地理数据集。 属性-less 功能可能对背景很有用,但对其他一切都不太有用。

我找到了 2010 年人口普查的来源 here. I downloaded the shapefile and converted it to topojson at mapshaper.org (be sure to copy all the files into the window - drag and drop - so that the data and the projection data is transfered). The data is already projected (to the New York State Plane), so you should unproject/'project' it to WGS84 by typing proj wgs84 in the console. This 可能有助于理解 projected/unprojected 数据和 d3

我正在使用的 file 具有 属性 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);

这给了我:

(block)

将数据连接到特征

许多 shapefile、topojsons、geosjons、特征 classes 等不包括许多 properties/attributes。这些包含地理坐标的文件通常会根据每个数据源中共享的标识符在数据连接中连接到包含 properties/attributes(但没有坐标)的文件。

有一个很好的例子 here on that in practice, though a better explanation might be here. I'll use the one of the few files 我可以找到(相对快速且免费)人口普查区域标识符。人口普查信息通常很好,因为它包含标准化标识符。此文件是包含可支配收入数据的 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 时使用了 parseInt 并丢失了 csv 中的前导零,parseInt 从我的地理区域中删除了前导零json 标识符。

最终结果类似于:

(block)

如果你看一下代码块,你会发现我在 d3.json 回调函数中嵌套了 d3.csv。其中每一个都是异步的,因此除非我们使用像 queue.js 这样的库,否则我们需要等到 json 被加载后再加载 csv。这种嵌套方法解决了在加载 csv 之前可能设置特征样式的问题

这应该包括如何根据增量为地图着色,属性 在 geo/topo json and/or 中加入来自非空间源的数据到空间形状。请参阅用于实现色标的块,没有适当的色标 color(i) 将不会 return 任何东西。