d3.js - 如何为 d3.js 地球标记添加高度,模仿图钉?
问题描述
当在地球仪上有标记时,标记平放在表面上。
尽管标记旋转看不见的那一刻可能会出现麻烦;有没有办法给这个标记高度?
我试图在针上画一个点,而不是地球表面上的一个点,伸出地球表面一点点。
不是这个:
-----o-----
但是这个:
o
_____|_____
模仿其中之一:
当前标记绘制如下:
const width = 220;
const height = 220;
const config = {
speed: 0.025,
verticalTilt: 10,
horizontalTilt: -10
}
let locations = [];
const svg = d3.select('svg')
.attr('width', width).attr('height', height);
const markerGroup = svg.append('g');
const projection = d3.geoOrthographic();
const initialScale = projection.scale(99.5).translate([100, 100]);
const path = d3.geoPath().projection(projection);
const center = [width / 2, height / 2];
drawGlobe();
drawGraticule();
enableRotation();
const locationData = [
{"latitude": -33.8688, "longitude": 151.2093}
];
function drawGlobe() {
d3.queue()
.defer(d3.json, 'https://raw.githubusercontent.com/cszang/dendrobox/master/data/world-110m2.json')
.await((error, worldData) => {
svg.selectAll(".segment")
.data(topojson.feature(worldData, worldData.objects.countries).features)
.enter().append("path")
.attr("class", "segment")
.attr("d", path)
.style("stroke", "silver")
.style("stroke-width", "1px")
.style("fill", (d, i) => 'silver')
.style("opacity", ".5");
locations = locationData;
drawMarkers();
});
}
function drawGraticule() {
const graticule = d3.geoGraticule()
.step([10, 10]);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path)
.style("fill", "#fff")
.style("stroke", "#ececec");
}
function enableRotation() {
d3.timer(function(elapsed) {
projection.rotate([config.speed * elapsed - 120, config.verticalTilt, config.horizontalTilt]);
svg.selectAll("path").attr("d", path);
drawMarkers();
});
}
function drawMarkers() {
const markers = markerGroup.selectAll('circle')
.data(locations);
markers
.enter()
.append('circle')
.merge(markers)
.attr('cx', d => projection([d.longitude, d.latitude])[0])
.attr('cy', d => projection([d.longitude, d.latitude])[1])
.attr('fill', d => {
const coordinate = [d.longitude, d.latitude];
gdistance = d3.geoDistance(coordinate, projection.invert(center));
return gdistance > 1.55 ? 'none' : 'tomato';
})
// 1.57
.attr('r', 3);
markerGroup.each(function() {
this.parentNode.appendChild(this);
});
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<svg></svg>
解决方案
以此答案为灵感,您可以创建第二个投影,相当于第一个,但具有更大的scale
值。这将在地球上实际点的正上方投影一个点,就好像它悬挂在它上面一样。这使您可以从头开始画一条线,并从各个角度查看它。它甚至可以与您的隐藏标记逻辑一起使用。
const width = 220;
const height = 220;
const config = {
speed: 0.025,
verticalTilt: 10,
horizontalTilt: -10
}
let locations = [];
const svg = d3.select('svg')
.attr('width', width).attr('height', height);
const markerGroup = svg.append('g');
const projection = d3.geoOrthographic()
.scale(99.5)
.translate([100, 100]);
const markerProjection = d3.geoOrthographic()
.scale(108)
.translate(projection.translate());
const path = d3.geoPath().projection(projection);
const center = [width / 2, height / 2];
drawGlobe();
drawGraticule();
enableRotation();
const locationData = [
{"latitude": -33.8688, "longitude": 151.2093}
];
function drawGlobe() {
d3.queue()
.defer(d3.json, 'https://raw.githubusercontent.com/cszang/dendrobox/master/data/world-110m2.json')
.await((error, worldData) => {
svg.selectAll(".segment")
.data(topojson.feature(worldData, worldData.objects.countries).features)
.enter().append("path")
.attr("class", "segment")
.attr("d", path)
.style("stroke", "silver")
.style("stroke-width", "1px")
.style("fill", (d, i) => 'silver')
.style("opacity", ".5");
locations = locationData;
drawMarkers();
});
}
function drawGraticule() {
const graticule = d3.geoGraticule()
.step([10, 10]);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path)
.style("fill", "#fff")
.style("stroke", "#ececec");
}
function enableRotation() {
d3.timer(function(elapsed) {
projection.rotate([config.speed * elapsed - 120, config.verticalTilt, config.horizontalTilt]);
markerProjection.rotate(projection.rotate());
svg.selectAll("path").attr("d", path);
drawMarkers();
});
}
function drawMarkers() {
const markers = markerGroup.selectAll('.marker')
.data(locations);
const newMarkers = markers
.enter()
.append('g')
.attr('class', 'marker')
newMarkers.append("line");
newMarkers.append("circle")
.attr("r", 3);
newMarkers.merge(markers)
.selectAll("line")
.attr("x1", d => projection([d.longitude, d.latitude])[0])
.attr("y1", d => projection([d.longitude, d.latitude])[1])
.attr("x2", d => markerProjection([d.longitude, d.latitude])[0])
.attr("y2", d => markerProjection([d.longitude, d.latitude])[1])
.attr('stroke', d => {
const coordinate = [d.longitude, d.latitude];
gdistance = d3.geoDistance(coordinate, markerProjection.invert(center));
return gdistance > (Math.PI / 2) ? 'none' : 'black';
})
newMarkers
.merge(markers)
.selectAll("circle")
.attr('cx', d => markerProjection([d.longitude, d.latitude])[0])
.attr('cy', d => markerProjection([d.longitude, d.latitude])[1])
.attr('fill', d => {
const coordinate = [d.longitude, d.latitude];
gdistance = d3.geoDistance(coordinate, markerProjection.invert(center));
return gdistance > (Math.PI / 2) ? 'none' : 'tomato';
})
markerGroup.each(function() {
this.parentNode.appendChild(this);
});
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<svg></svg>
推荐阅读
- reactjs - 将多个 React 应用程序导入一个主 React 应用程序
- facebook - Facebook APIs - 寻找一种解决方法来为整个 ACT_ID 而不是单个广告对象实施异步洞察请求
- c++ - Mat sum 函数在多线程中使用并发向量
- kubernetes - 同一 Kubernetes 集群中从一个 pod 到另一个 pod 的内部 API 调用超时
- android - 如何删除 admob 应用广告投放限制?
- python - 在 Django 中管理大量固定装置的迁移以进行单元测试
- reactjs - Azure Devops Extension 开发中的 Azure Devops 公式设计系统“面板”组件
- php - 未捕获的错误:类“GuzzleHttp\Promise\EachPromise”
- security - 有没有一种安全的方法来编译电子应用程序(没有 npm 安全问题)?
- java - JavaFx CSS TableView 仅更改不透明度文本