我在http://bl.ocks.org/patricksurry/6621971的顶部构建了一个可视化。
基本上我在地图上添加了 d3.geo.circle 和 d3.svg.arc 。
我观察到的是当我平移/缩放地图时,圆圈保持不变,但弧线消失了。
当我检查chrome中的元素时,我看到弧路径的属性'd'消失了,但是对于圆路径,它已适当更新。
任何人都可以帮助我理解为什么更新的投影应用于圆路径元素而不是弧形。有没有办法强制重新投射弧而不必删除并重新创建它们?
更新 1:由于此问题似乎难以重新创建,并且jsfiddle不允许上传地理数据文件,因此我在此处发布源代码:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
background-color: lavender;
border: 1px solid black;
}
path {
fill: oldlace;
stroke: #666;
stroke-width: .5px;
}
path.circle {
fill: red;
stroke: #666;
stroke-width: .5px;
}
path.arc1 {
fill: green;
}
</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 = 1600,
height = 400,
rotate = 60, // so that [-60, 0] becomes initial center of projection
maxlat = 83; // clip northern and southern poles (infinite in mercator)
var projection = d3.geo.mercator()
.rotate([rotate,0])
.scale(1) // we'll scale up to match viewport shortly.
.translate([width/2, height/2]);
// find the top left and bottom right of current projection
function mercatorBounds(projection, maxlat) {
var yaw = projection.rotate()[0],
xymax = projection([-yaw+180-1e-6,-maxlat]),
xymin = projection([-yaw-180+1e-6, maxlat]);
return [xymin,xymax];
}
// set up the scale extent and initial scale for the projection
var b = mercatorBounds(projection, maxlat),
s = width/(b[1][0]-b[0][0]),
scaleExtent = [s, 10*s];
projection.scale(scaleExtent[0]);
var zoom = d3.behavior.zoom()
.scaleExtent(scaleExtent)
.scale(projection.scale())
.translate([0,0]) // not linked directly to projection
.on("zoom", redraw);
var path = d3.geo.path()
.projection(projection);
var svg = d3.selectAll('body')
.append('svg')
.attr('width',width)
.attr('height',height)
.attr('id', 'svg')
.call(zoom);
d3.json("js/data/world-110m2.json", function ready(error, world) {
// adding geo paths
svg.selectAll('path')
.data(topojson.feature(world, world.objects.countries).features)
.enter().append('path')
// adding a circle
svg.append("path")
.datum(d3.geo.circle().angle(2).origin([-10, 0]))
.attr("d", path)
.attr("class", "circle");
redraw();
// adding a pie arc
var r = 10;
var p = Math.PI * 2;
var arc1 = d3.svg.arc()
.innerRadius(r - 5)
.outerRadius(r)
.startAngle(0);
var arcData = JSON.parse('[{ "lon" : "0", "lat":"0", "endAngle":"6.4" }]');
var arcs1 = svg.selectAll("path.arc1");
arcs1 = arcs1.data(arcData)
.enter()
.append("path")
.attr("class", "arc1")
.attr("fill", "green")
.attr("transform", function(d, i) { return "translate(" + projection([d.lon, d.lat])[0] + ", " + projection([d.lon, d.lat])[1] + ")"; })
.attr("d", arc1);
});
// track last translation and scale event we processed
var tlast = [0,0],
slast = null;
function redraw() {
if (d3.event) {
var scale = d3.event.scale,
t = d3.event.translate;
console.log(d3.event.scale + " [" +d3.event.translate + "]");
// if scaling changes, ignore translation (otherwise touch zooms are weird)
if (scale != slast) {
projection.scale(scale);
} else {
var dx = t[0]-tlast[0],
dy = t[1]-tlast[1],
yaw = projection.rotate()[0],
tp = projection.translate();
// use x translation to rotate based on current scale
projection.rotate([yaw+360.*dx/width*scaleExtent[0]/scale, 0, 0]);
// use y translation to translate projection, clamped by min/max
var b = mercatorBounds(projection, maxlat);
if (b[0][1] + dy > 0) dy = -b[0][1];
else if (b[1][1] + dy < height) dy = height-b[1][1];
projection.translate([tp[0],tp[1]+dy]);
}
// save last values. resetting zoom.translate() and scale() would
// seem equivalent but doesn't seem to work reliably?
slast = scale;
tlast = t;
}
svg.selectAll('path').attr('d', path);
}
</script>
答案 0 :(得分:0)
我终于弄清楚出了什么问题。我本来应该对弧元素进行转换。所以基本上,在redraw()方法中我做了:
var scaleRatio = 1;
function redraw() {
if (d3.event) {
var scale = d3.event.scale,
t = d3.event.translate;
//console.log(d3.event.scale + " [" +d3.event.translate + "]");
// if scaling changes, ignore translation (otherwise touch zooms are weird)
if (scale != slast) {
projection.scale(scale);
} else {
var dx = t[0]-tlast[0],
dy = t[1]-tlast[1],
yaw = projection.rotate()[0],
tp = projection.translate();
// use x translation to rotate based on current scale
projection.rotate([yaw+360.*dx/width*scaleExtent[0]/scale, 0, 0]);
// use y translation to translate projection, clamped by min/max
var b = mercatorBounds(projection, maxlat);
if (b[0][1] + dy > 0) dy = -b[0][1];
else if (b[1][1] + dy < height) dy = height-b[1][1];
projection.translate([tp[0],tp[1]+dy]);
}
// save last values. resetting zoom.translate() and scale() would
// seem equivalent but doesn't seem to work reliably?
if(slast==null)
scaleRatio=1;
else
scaleRatio = scaleRatio * (scale/slast);
console.log(slast+'-' + scaleRatio);
slast = scale;
tlast = t;
}
svg.selectAll('path').attr('d', path);
svg.selectAll("path.arc1")
.attr("transform", function(d, i) { return "translate(" + projection([d.lon, d.lat])[0] + ", " + projection([d.lon, d.lat])[1] + ")scale(" + scaleRatio + ")" })
.attr("d", arc1);
}
但是,与d3.geo.path元素不同,我仍然知道svg.path元素为什么需要显式重新投影的知识差距。希望有人帮助我。