如何使用d3,javascript和json文件将地图划分为邮政编码? [英] How to divide a map into zipcodes using d3, javascript, and a json file?

查看:82
本文介绍了如何使用d3,javascript和json文件将地图划分为邮政编码?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试创建一个邮政编码区域的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/.我了解我正在尝试从正在使用的json文件中选择全部"某种特征数组以创建路径.在我的数据中,有一个坐标数组,我正在尝试访问和使用.我的代码没有抛出任何错误,所以我不确定要在哪里调试.

还,我应该在此步骤中还是在创建路径后为路径创建的区域上色?

解决方案

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

所选内容在添加功能中的作用:

我了解我正在尝试全选"某种功能 我用来创建路径的json文件中的数组.

您不是在json文件中选择内容,而是在DOM中选择元素. D3将json中的数据绑定到它们所存在的要素,在不存在的要素上生成enter()选择,在与JSON数据相关的多余DOM元素被选择时生成exit()选择.

这就是为什么通常在.enter()语句之后加上selectAll(type).data(data)语句对数据进行初始附加的原因. 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()选择实际上将为空.

使用此方法的初始附加通常假定selectAll语句为空,因此数据中的所有项目都附加enter选择,这使很多人感到不安(a )( 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);

使用它,我得到:

(阻止)

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

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

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

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

显示地理特征的属性

您需要找到具有属性的地理数据集.缺少属性的功能可能对背景很有用,但对其他所有功能则用处不大.

我找到了2010年人口普查资料的来源此处.我下载了shapefile并将其转换为 mapshaper.org 的topojson(请确保将所有文件复制到窗口中-拖放-以便传输数据和投影数据).数据已经被投影(到纽约州飞机),因此您应该通过在控制台中键入proj wgs84将其取消投影/投影"到WGS84. 答案可能会有所帮助理解预测/非预测数据和d3

文件我正在使用的属性BoroCode将用于在Choropleth类型显示器中显示:

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

这给了我

(阻止)

将数据加入要素

许多shapefile,topojson,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时使用了parseInt,并且丢失了csv中的前导零,parseInt从我的geojson标识符中删除了前导零.

最终结果如下:

(阻止)

如果看一下该块,您会看到我将d3.csv嵌套在d3.json回调函数中.它们中的每一个都是异步的,因此除非我们使用像queue.js这样的库,否则我们需要等到json加载后再加载csv.这种嵌套方法解决了在csv加载之前可能对样式进行样式设置的问题

这应该涵盖如何根据geo/topo json中的增量,属性和/或通过将非空间源中的数据连接到空间形状来为地图着色.请参见有关实现色标的块,如果没有正确的色标,则color(i)将不会返回任何内容.

I'm trying to create a nyc map with zipcode areas I can color in based on census data (like color an area red if majority white or blue if majority nonwhite). I am simply using one of the shape files I found online from here ( https://data.cityofnewyork.us/Business/Zip-Code-Boundaries/i8iw-xf4u/data ).

I converted the shp file to a geojson and then a topojson file.

I'd appreciate it if someone could look at my code below, and let me know how I can go about doing this.

Code:

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

Output:

The last part of my code (and the first part) is modeled after https://bost.ocks.org/mike/map/. 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. In my data, there's a coordinates array, which I am trying to access and use. My code doesn't throw any errors so I'm not sure where to look to debug.

Also, I'm I supposed to color the areas the paths create in this step or after I create the paths?

解决方案

This answer uses d3 v3 and considers census tracts rather than zip codes (reflecting the original edit, but the principles remain the same)

The selection's role in adding features:

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.

Rather than selecting something from the json file, you are selecting elements in the DOM. D3 will bind the data in the json to the features where they exist, produce an enter() selection where they don't, and produce an exit() selection where there are excess DOM elements selected in relation to the json data.

This is why the initial appending of data with a selectAll(type).data(data) statement is followed with an .enter() statement generally. The enter returns the elements that must be added to the 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);

If you were updating the data - say showing some year by year property in your maps, you wouldn't need the .enter() if the number of geographic features was constant (likely the case), you would just set the data and then modify the properties. If the number of elements in your new data array is the same as the old one, then the enter() selection will actually be empty.

The intial append with this method generally assumes the selectAll statement is empty, so that all items in the data are appended with the enter selection, this causes many people a lot of grief (a) (b) (c) (d) (e) (f).

When using the alternate approach:

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

You just append one path element encompassing all features, which makes styling individual areas impossible. In contrast, the top approach appends one path for each element in your json.

Setting map attributes:

You may have difficulty in setting the the class of each path to d.coordinates. Using topojson.feature(data, data.objects.features).features returns geojson from your topojson. The coordinates property of each feature is an array - which might not won't work with a class declaration.

But, you have the right approach. An inline function can set attributes easily:

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

Using this I get:

(block)

But, let's look at d in that inline function above ( .attr('fill',function(d,i) { console.log(d); return color(i); }) ). It is a geojson object:

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

If you don't see any properties (it'll always have a properties property, but it might be empty or contain only methods), you have some bad news, the properties are empty. Consequently, there are no properties containing data that can be displayed - eg coloring the map. There are also no identifiers in the data. This makes joining outside data to each feature impossible and there is no data in the feature to show. Topojson doesn't compress properties so you should be able to see them if they are present in the text of the file:

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

Showing properties of geographical features

You'll need to find a geographical dataset that has properties. Property-less features might be great for backgrounds, but less useful for everything else.

I found a source of the 2010 census tracts 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 answer might help in terms of understanding projected/unprojected data and d3

The file I'm working with has the property BoroCode which I'll use to display in a choropleth type display:

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

This gives me:

(block)

Joining data to features

Many shapefiles, topojsons, geosjons, feature classes etc don't include many properties/attributes. These files containing geographic coordinates are often joined to files that contain properties/attributes (but no coordinates) in a data join based on an identifier shared in each data source.

There is an excellent example here on that in practice, though a better explanation might be here. I'll use the one of the few files I could find (relatively quickly and free) with census tract identifiers. Census information is generally great as it contains standardized identifiers. This file is a csv containing disposable income data.

Now with the shared identifier, we can show the geographic shapes and assign colors to them based on the income values in the csv.

Once both files are loaded, I'll make a dictionary:

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

and then I'll show the features, almost the same as above:

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

I used parseInt as I modified the csv's in Excel and lost the leading zeros in the csv, parseInt drops the leading zeros from my geojson identifiers.

The end result looks something like:

(block)

If you took a look at the block, you'll see I nested d3.csv inside the d3.json callback function. Each of these is asynchronous, so unless we use a library like queue.js we'll need to wait until the json is loaded before loading the csv. This nested approach solves the problem of potentially styling the features before the csv is loaded

This should cover how to color a map either based on increment, property within the geo/topo json and/or by joining the data from a non-spatial source to the spatial shapes. See the blocks for the implementation of a color scale, without a proper scale color(i) won't return anything.

这篇关于如何使用d3,javascript和json文件将地图划分为邮政编码?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆