如何嵌套 GeoJSON / TopoJSON 几何图形或使用 D3 嵌套生成的路径?
How can I nest GeoJSON / TopoJSON geometries OR nest the generated paths with D3?
问题:
我正在尝试创建一个交互式美国地图,其中显示了州、县和国界。县根据数据加阴影,将鼠标悬停在一个州上应该突出显示该州的所有县,并且该州应该是可点击的。我想通过一个 SVG 来实现这一点,该 SVG 在州形状内部、美国形状内部具有县形状。
我可以根据 CENSUS 县形状文件生成县地图,并且可以通过使用 TopoJSON 命令行准备文件并在 D3 中使用以下代码,根据外部 CSV 中的数据对州进行着色:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 600;
var path = d3.geo.path()
.projection(d3.geo.albersUsa());
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("counties_pa.json", function(error, us) {
if (error) return console.error(error);
var color = d3.scale.threshold()
.domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
.range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);
svg.append('g').attr('class','counties').selectAll("path").data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter().append('path').attr('d',path).attr('style',function(d){return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);});
});
</script>
这在视觉上基本上是可以接受的(除非它没有离散的州/国界)- 但在功能上是不够的。为了将 CSS 应用于状态悬停上的县,这些县需要位于一个州形状内,或以某种方式分组。
我尝试过的:
- 在命令行中使用 topojson-merge 将县合并到州形状中,然后分别渲染州形状 - 这有助于具有离散的州边界 - 但我还没有想出一种方法来将县嵌套到各自的状态形状。
我现在在做什么:
以某种方式组合州 TopoJSON 文件和县 TopoJSON 文件并将县嵌套在各州中,然后使用 D3 进行渲染。
以某种方式使用 d3 获取非嵌套的州和县数据,并将其嵌套在客户端级别的客户端上。
最后我想了解最有效和最快速的渲染过程来实现我想要的功能。
提前感谢您的帮助。
我对你的数据源下了赌注,这就是你想要实现的目标:http://bl.ocks.org/benlyall/55bc9474e6d531a1c1fe
基本上,我使用以下命令行生成了一个 TopoJSON 文件:
topojson -o counties_pa.json --id-property=+GEOID -p -e POP01.txt --id-property=+STCOU -p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp
对此的一些解释:
-o counties_pa.json
设置输出文件的名称
--id-property=+GEOID
将在输入文件中使用 属性 作为每个输出几何 的 id
-p
表示包含输入文件中的所有属性
-e POP01.txt
将从文件 POP01.txt
中提取外部数据。此文件是从 http://www.census.gov/support/USACdataDownloads.html#POP 提供的 POP01.xls
电子表格生成的 csv 文件
--id-property=+STCOU
表示来自外部文件 (POP01.txt
) 的 id 属性 在 STCOU 列中。这用于匹配输入文件中的匹配 id
s(如上所述,它们在 GEOID
属性 中)
-p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP
在输出文件中明确列出了我想要的属性,因此不会包含任何额外的内容。 POP010210D 是 2010 年人口普查的人口列名,所以我只是用它来演示。
cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp
是两个输入文件。一种用于县形状,一种用于州形状。它们将分别添加到以其文件名命名的单独属性中的输出文件中。
我是这样做的,因为您似乎是根据人口密度为您的县域着色,所以人口和面积都需要在输出文件中。人口是从 POP01
电子表格中提取的,并根据 GEOID
链接到每个县(这只是州编号与县编号相结合)。
我只是在寻找一种快速简便的方法来重新创建您的数据集,然后向其中添加州边界,这样我就可以 post 找到答案。不确定这与您的原始数据有多接近,但它似乎可以用于演示目的。
据此,我将上面的代码更新为:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
}
path.state {
fill: none;
stroke: black;
stroke-width: .5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 600;
var path = d3.geo.path()
.projection(d3.geo.albersUsa());
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("counties_pa.json", function(error, us) {
if (error) return console.error(error);
var color = d3.scale.threshold()
.domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
.range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);
svg.append('g')
.attr('class','counties')
.selectAll("path")
.data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter()
.append('path')
.attr('d', path)
.attr("id", function(d) { return "county-" + d.id; })
.attr("data-state", function(d) { return d.properties.state; })
.attr('style',function(d) {
return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);
})
.on("mouseover", hoverCounty)
.on("mouseout", outCounty);
svg.append('g')
.attr('class', 'states')
.selectAll("path")
.data(topojson.feature(us, us.objects.cb_2014_us_state_20m).features).enter()
.append("path")
.attr("class", "state")
.attr("id", function(d) { return "state-" + d.id; })
.attr("d", path);
});
function hoverCounty(county) {
d3.selectAll("path[data-state='" + county.properties.state + "']").style("opacity", .5);
}
function outCounty(county) {
d3.select(".counties").selectAll("path").style("opacity", null);
}
</script>
新的有趣的代码是:
为每个县添加一个data-state
属性以确定它属于哪个州:
.attr("data-state", function(d) { return d.properties.state; })
添加状态边界(我在 topojson
命令行中将状态合并到 TopoJSON 文件中)
svg.append('g')
.attr('class', 'states')
.selectAll("path")
.data(topojson.feature(us, us.objects.cb_2014_us_state_20m).features).enter()
.append("path")
.attr("class", "state")
.attr("id", function(d) { return "state-" + d.id; })
.attr("d", path);
});
添加了悬停处理程序,这样您就可以看到我是如何确定将县分组为州的:
function hoverCounty(county) {
d3.selectAll("path[data-state='" + county.properties.state + "']").style("opacity", .5);
}
function outCounty(county) {
d3.select(".counties").selectAll("path").style("opacity", null);
}
将这些悬停处理程序绑定到每个县,以便它们在适当的时间执行:
.on("mouseover", hoverCounty)
.on("mouseout", outCounty);
问题:
我正在尝试创建一个交互式美国地图,其中显示了州、县和国界。县根据数据加阴影,将鼠标悬停在一个州上应该突出显示该州的所有县,并且该州应该是可点击的。我想通过一个 SVG 来实现这一点,该 SVG 在州形状内部、美国形状内部具有县形状。
我可以根据 CENSUS 县形状文件生成县地图,并且可以通过使用 TopoJSON 命令行准备文件并在 D3 中使用以下代码,根据外部 CSV 中的数据对州进行着色:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 600;
var path = d3.geo.path()
.projection(d3.geo.albersUsa());
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("counties_pa.json", function(error, us) {
if (error) return console.error(error);
var color = d3.scale.threshold()
.domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
.range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);
svg.append('g').attr('class','counties').selectAll("path").data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter().append('path').attr('d',path).attr('style',function(d){return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);});
});
</script>
这在视觉上基本上是可以接受的(除非它没有离散的州/国界)- 但在功能上是不够的。为了将 CSS 应用于状态悬停上的县,这些县需要位于一个州形状内,或以某种方式分组。
我尝试过的:
- 在命令行中使用 topojson-merge 将县合并到州形状中,然后分别渲染州形状 - 这有助于具有离散的州边界 - 但我还没有想出一种方法来将县嵌套到各自的状态形状。
我现在在做什么:
以某种方式组合州 TopoJSON 文件和县 TopoJSON 文件并将县嵌套在各州中,然后使用 D3 进行渲染。
以某种方式使用 d3 获取非嵌套的州和县数据,并将其嵌套在客户端级别的客户端上。
最后我想了解最有效和最快速的渲染过程来实现我想要的功能。
提前感谢您的帮助。
我对你的数据源下了赌注,这就是你想要实现的目标:http://bl.ocks.org/benlyall/55bc9474e6d531a1c1fe
基本上,我使用以下命令行生成了一个 TopoJSON 文件:
topojson -o counties_pa.json --id-property=+GEOID -p -e POP01.txt --id-property=+STCOU -p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp
对此的一些解释:
-o counties_pa.json
设置输出文件的名称--id-property=+GEOID
将在输入文件中使用 属性 作为每个输出几何 的 -p
表示包含输入文件中的所有属性-e POP01.txt
将从文件POP01.txt
中提取外部数据。此文件是从 http://www.census.gov/support/USACdataDownloads.html#POP 提供的 --id-property=+STCOU
表示来自外部文件 (POP01.txt
) 的 id 属性 在 STCOU 列中。这用于匹配输入文件中的匹配id
s(如上所述,它们在GEOID
属性 中)-p population=+POP010210D,area=ALAND,state=+STATEFP,county=+COUNTYFP
在输出文件中明确列出了我想要的属性,因此不会包含任何额外的内容。 POP010210D 是 2010 年人口普查的人口列名,所以我只是用它来演示。cb_2014_us_county_20m.shp cb_2014_us_state_20m.shp
是两个输入文件。一种用于县形状,一种用于州形状。它们将分别添加到以其文件名命名的单独属性中的输出文件中。
id
POP01.xls
电子表格生成的 csv 文件
我是这样做的,因为您似乎是根据人口密度为您的县域着色,所以人口和面积都需要在输出文件中。人口是从 POP01
电子表格中提取的,并根据 GEOID
链接到每个县(这只是州编号与县编号相结合)。
我只是在寻找一种快速简便的方法来重新创建您的数据集,然后向其中添加州边界,这样我就可以 post 找到答案。不确定这与您的原始数据有多接近,但它似乎可以用于演示目的。
据此,我将上面的代码更新为:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
path {
fill: none;
stroke-linejoin: round;
stroke-linecap: round;
}
path.state {
fill: none;
stroke: black;
stroke-width: .5px;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script src="http://d3js.org/topojson.v1.min.js"></script>
<script>
var width = 960,
height = 600;
var path = d3.geo.path()
.projection(d3.geo.albersUsa());
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("counties_pa.json", function(error, us) {
if (error) return console.error(error);
var color = d3.scale.threshold()
.domain([1, 10, 50, 100, 500, 1000, 2000, 5000])
.range(["#fff7ec", "#fee8c8", "#fdd49e", "#fdbb84", "#fc8d59", "#ef6548", "#d7301f", "#b30000", "#7f0000"]);
svg.append('g')
.attr('class','counties')
.selectAll("path")
.data(topojson.feature(us, us.objects.cb_2014_us_county_20m).features).enter()
.append('path')
.attr('d', path)
.attr("id", function(d) { return "county-" + d.id; })
.attr("data-state", function(d) { return d.properties.state; })
.attr('style',function(d) {
return 'fill:'+color(d.properties.population / d.properties.area * 2.58999e6);
})
.on("mouseover", hoverCounty)
.on("mouseout", outCounty);
svg.append('g')
.attr('class', 'states')
.selectAll("path")
.data(topojson.feature(us, us.objects.cb_2014_us_state_20m).features).enter()
.append("path")
.attr("class", "state")
.attr("id", function(d) { return "state-" + d.id; })
.attr("d", path);
});
function hoverCounty(county) {
d3.selectAll("path[data-state='" + county.properties.state + "']").style("opacity", .5);
}
function outCounty(county) {
d3.select(".counties").selectAll("path").style("opacity", null);
}
</script>
新的有趣的代码是:
为每个县添加一个
data-state
属性以确定它属于哪个州:.attr("data-state", function(d) { return d.properties.state; })
添加状态边界(我在
topojson
命令行中将状态合并到 TopoJSON 文件中)svg.append('g') .attr('class', 'states') .selectAll("path") .data(topojson.feature(us, us.objects.cb_2014_us_state_20m).features).enter() .append("path") .attr("class", "state") .attr("id", function(d) { return "state-" + d.id; }) .attr("d", path); });
添加了悬停处理程序,这样您就可以看到我是如何确定将县分组为州的:
function hoverCounty(county) { d3.selectAll("path[data-state='" + county.properties.state + "']").style("opacity", .5); } function outCounty(county) { d3.select(".counties").selectAll("path").style("opacity", null); }
将这些悬停处理程序绑定到每个县,以便它们在适当的时间执行:
.on("mouseover", hoverCounty) .on("mouseout", outCounty);