javascript - d3 geo 在单击/缩放时更改 topojson 文件
问题描述
我在 D3.js 中构建了世界地图出于性能考虑,我使用的是 world-atlas 110m 版本,但我想在放大时增加地图细节。
当我单击美国时,我还想更改投影,因此也想更改 topojson 文件。(即使用geoAlbersUsa(),渲染美国各州)
我有一些非常基本的功能来改变点击时的地图投影,但我正在努力改变如何改变 topojson 文件。
import * as d3 from "d3";
import { feature } from 'topojson';
var projections = [
{name: "mercator", value: d3.geoMercator()},
{name: "orthograpic", value: d3.geoOrthographic()},
{name: "Us", value: d3.geoAlbersUsa()} ]
let projection = projections[1].value;
const svg = d3.select('svg');
const graticule = d3.geoGraticule();
let pathGenerator = d3.geoPath(projection);
let country;
const g = svg.append('g');
g.append('path')
.attr('class', 'sphere')
.attr('d', pathGenerator({type: 'Sphere'}));
g.append('path')
.datum(graticule)
.attr("class", "graticule")
.attr("d", pathGenerator);
g.call(d3.drag()
.on("drag", (event, d) => {
//if (projection === projections[1].value){
const rotate = projection.rotate()
const k = 75 / projection.scale()
projection.rotate([
rotate[0] + event.dx * k,
rotate[1] - event.dy * k
])
pathGenerator = d3.geoPath().projection(projection)
svg.selectAll(".graticule").attr("d", pathGenerator)
svg.selectAll(".country").attr("d", pathGenerator)
//}
//else{return}
}));
function update(){
pathGenerator = d3.geoPath().projection(projection)
svg.selectAll("path").attr("d", pathGenerator)
svg.selectAll("path").transition().duration(750)
svg.selectAll(".sphere").attr("d", pathGenerator({type:'Sphere'}))
}
Promise.all([
d3.tsv('https://unpkg.com/world-atlas@1.1.4/world/110m.tsv'),
d3.json('https://unpkg.com/world-atlas@1.1.4/world/110m.json'),
d3.json("states-albers-10m.json")
]).then(([tsvData, topoJSONdata, usTopoJson]) => {
const countryName = tsvData.reduce((accumulator, d) => { //for each..
accumulator[d.iso_n3] = d.name;
return accumulator;
})
const countries = feature(topoJSONdata, topoJSONdata.objects.countries);
console.log(projection)
g.selectAll('.country').data(countries.features)
.enter().append('path')
.attr('class', 'country')
.attr('d', pathGenerator)
.on("click", function(event,d) {
country = countryName[d.id];
if(country === "United States" && projection !== projections[0].value){
console.log(projection);
projection = projections[0].value
update();
}
else{
projection = projections[1].value
update();
}
})
})
g.call(d3.zoom().on('zoom', (event) => {
g.attr('transform', event.transform, )
}))
现场演示:https ://vizhub.com/Glebenator/810613a0a8584310abdcbcdaa78a368a
解决方案
如果您想通过缩放更改功能/数据源/任何内容,您需要获取当前缩放状态,该状态包含在传递给缩放事件侦听器的事件中(或 d3 v6 之前的 d3.event)。
这为我们提供了一个相对简单的缩放监听器:
.on("zoom", function(event) {
// if zoomed in sufficiently:
if(event.transform.k > 4) {
/* draw the detailed features */
}
// otherwise:
else {
/* draw the undetailed features */
}
// update the paths:
features.attr("d",path)
})
下面我将它与一些语义缩放相结合,您可以更轻松地应用投影剪辑范围,这将阻止路径生成器在所需范围之外绘制要素。它仍然会投射他们的观点,但不必绘制它们。还有其他方法可以获得性能提升,但我将把它们留到另一天。语义缩放使用缩放的平移和原始投影平移(后者需要缩放缩放比例),并结合缩放和投影比例:
projection.translate([t.x+240*t.k,t.y+120*t.k]).scale(baseScale*t.k);
我下面的示例并不喜欢它如何加载数据,它加载一个基础层,然后加载一个更详细的文件。加载更详细的文件后,它会应用缩放行为,允许您放大更详细的功能集(大陆与国家)。有很多方法你可能想要做到这一点,我的例子只是说明性的。
对于优化,您可以通过存储先前的缩放值并仅在新值超过阈值时调用重新渲染/更新函数来查看缩放级别是否超过阈值。
var svg = d3.select("body").append("svg")
.attr("width", 480)
.attr("height", 240);
var baseScale = 480/Math.PI/2;
var projection = d3.geoMercator()
.scale(baseScale)
.translate([240,120])
.clipExtent([[0,0],[480,240]])
var path = d3.geoPath(projection);
d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2/land-110m.json").then(function(land) {
// draw world initially:
var world = topojson.feature(land, land.objects.land);
var features = update([world]).attr("d",path);
d3.json("https://cdn.jsdelivr.net/npm/world-atlas@2/countries-110m.json").then(function(countries) {
// get a more detailed set of features:
var detail = topojson.feature(countries, countries.objects.countries).features;
// apply a zoom behavior:
var zoom = d3.zoom()
.on("zoom", function(event) {
var t = event.transform;
// Semantic zoom using the projection:
projection.translate([t.x+240*t.k,t.y+120*t.k]).scale(baseScale*t.k);
// if zoomed in sufficiently:
if(t.k > 1) {
// draw the detailed features:
features = update(detail);
}
// otherwise:
else {
// draw the undetailed features:
features = update([world]);
}
// update the paths:
features.attr("d",path)
})
svg.call(zoom);
})
})
function update(data) {
return svg.selectAll("path").data(data).join("path");
}
path {
stroke-width: 1px;
stroke: black;
fill: #aaa;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.2.0/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/topojson/3.0.2/topojson.min.js"></script>
推荐阅读
- vue.js - Vuepress 自定义主题和 postcss
- bash - $ mvn -v – 找不到命令
- javascript - 从 MongoDB 集合中的数组中获取特定对象到车把表中
- c# - C# Web 请求与 LXD 交互
- reactjs - Material-UI 中使用 Styled-Components 的媒体查询
- python - 在 Pandas 中对列进行词形还原时出现语法错误
- c++ - 将 char 数组转换为时间结构
- r - 在R中的美国地图上的2个邮政编码之间画一条线
- python - 未找到:/products/images/DSroadmap.png 未找到:/products/images/codewall.jpg [26/Jan/2020 22:13:36]
- python-3.x - 在使用机器人框架时,尝试在设置部分添加资源时遇到导入错误消息?