如何调整D3中的点的缩放大小? [英] How do I adjust zoom size for a point in D3?
问题描述
这可能是你做错了的经典案例,但是我所有的搜索都没有任何帮助。
这里是我的场景:
我使用albersUSA地图投影结合国家和县GeoJson文件绘制一切。
我也有自己创建的城市文件,包含每个州的主要城市。坐标是准确的,一切看起来不错。
>
当用户点击给定的状态时,我隐藏所有状态形状,然后计算所需的变换,以获得该状态的县形状,以适合我的视口。然后我将该变换应用到所有必要的县形状,以获得缩放视图。我的代码如下:
function CalculateTransform(objectPath)
{
var results =''
//定义视口的边界/点
var mapDimensions = getMapViewportDimensions();
var baseWidth = mapDimensions [0];
var baseHeight = mapDimensions [1];
var centerX = baseWidth / 2;
var centerY = baseHeight / 2;
//获取对象路径的边界框,并基于视口计算质心和缩放因子
//。
var bbox = objectPath.getBBox();
var centroid = [bbox.x + bbox.width / 2,bbox.y + bbox.height / 2];
var zoomScaleFactor = baseHeight / bbox.height;
var zoomX = -centroid [0];
var zoomY = -centroid [1];
//如果状态的宽度大于高度,则缩放
//该属性,以便状态仍然适合视口。
if(bbox.width> bbox.height){
zoomScaleFactor = baseHeight / bbox.width;
}
//计算将对象路径从当前位置移动到
//视口中心的距离。
var augmentX = - (centroid [0] - centerX);
var augmentY = - (centroid [1] - centerY);
//我们的转换逻辑包括:
// 1.将状态移动到屏幕中心。
// 2.根据我们预期的规模移动状态。
// 3.缩放状态。
// 4.将状态移回到适合缩放的状态。
var transform =translate(+(augmentX)+,+(augmentY)+)+
+)+
scale(+ zoomScaleFactor +)+
translate(+(zoomX)+,+(zoomY)+)
返回结果;
}
...和绑定函数
//为指定的状态加载县数据。
d3.json(jsonUrl,function(json){
if(json === undefined || json == null || json.features.length == 0)
{
logging.error(无法检索县结构数据);
showMapErrorMessage(无法检索县结构数据);
返回false;
}
else
{
counties.selectAll(path)
.data(json.features)
.enter()
.append(path)
.attr(id,function(d,i){
returncounty_+ d.properties.GEO_ID
})
.attr(data-id (d,i){return d.properties.GEO_ID})
.attr(data-name,function(d,i){return countyLookup [d.properties.GEO_ID]})
。 attr(data-stateid,function(d,i){return d.properties.STATE})
.attr(d,path);
//显示所有县为指定的状态和应用缩放变换。
d3.selectAll(countySelector).attr(visibility,visible);
d3.selectAll(countySelector).attr(transform,stateTransform);
//显示指定状态的所有城市并应用zoom transform
d3.selectAll(citySelector).attr(visibility,visible);
d3.selectAll(citySelector).attr(transform,stateTransform);
}
});
这里工作正常,除了真的很小的状态,缩放因子大得多, p>
是否有一种方法可以强制点的大小是一个固定的大小(例如15px半径),即使在转换发生后?
这是因为你正在设置一个比例变换,而不是缩放位置。您可以看到差异这里基本上它是之间的区别:
//粗线,因为它们被缩放
var bottom = svg.append('g')。attr('transform','scale + scale +','+ scale +')');
bottom.selectAll('circle')
.data(data)
.enter()。append('circle')
.attr('cx',function ){return dx;})
.attr('cy',function(d){return dy;});
和
//线条粗细很漂亮
var top = svg.append('g');
top.selectAll('circle')
.data(data)
.enter()。append('circle')
.attr('cx',function ){return dx * scale;})
.attr('cy',function(d){return dy * scale;});
使用映射可能你最好的解决方案是计算你的偏移和比例,你的投影函数 - 你想直接修改投影后的x和y值。如果您正确更新投影函数,则不必执行其他操作即可对地图应用适当的缩放。
This could be a classic case of "you're doing it wrong", but all of my searching to date hasn't warranted any help.
Here's my scenario:
I'm using an albersUSA map projection in conjunction with the national and county GeoJson files to draw everything.
I also have a self created "cities" file that contains major cities for each state. The coordinates are accurate and everything looks good.
When a user clicks on a given state, I hide all state shapes and then calculate the transform needed to get the county shapes for that state to fit within my viewport. I then apply that transform to all the necessary county shapes in order to get the "zoomed" view. My code is as follows:
function CalculateTransform(objectPath)
{
var results = '';
// Define bounds/points of viewport
var mapDimensions = getMapViewportDimensions();
var baseWidth = mapDimensions[0];
var baseHeight = mapDimensions[1];
var centerX = baseWidth / 2;
var centerY = baseHeight / 2;
// Get bounding box of object path and calculate centroid and zoom factor
// based on viewport.
var bbox = objectPath.getBBox();
var centroid = [bbox.x + bbox.width / 2, bbox.y + bbox.height / 2];
var zoomScaleFactor = baseHeight / bbox.height;
var zoomX = -centroid[0];
var zoomY = -centroid[1];
// If the width of the state is greater than the height, scale by
// that property instead so that state will still fit in viewport.
if (bbox.width > bbox.height) {
zoomScaleFactor = baseHeight / bbox.width;
}
// Calculate how far to move the object path from it's current position to
// the center of the viewport.
var augmentX = -(centroid[0] - centerX);
var augmentY = -(centroid[1] - centerY);
// Our transform logic consists of:
// 1. Move the state to the center of the screen.
// 2. Move the state based on our anticipated scale.
// 3. Scale the state.
// 4. Move the state back to accomodate for the scaling.
var transform = "translate(" + (augmentX) + "," + (augmentY) + ")" +
"translate(" + (-zoomX) + "," + (-zoomY) + ")" +
"scale(" + zoomScaleFactor + ")" +
"translate(" + (zoomX) + "," + (zoomY) + ")";
return results;
}
...and the binding function
// Load county data for the state specified.
d3.json(jsonUrl, function (json) {
if (json === undefined || json == null || json.features.length == 0)
{
logging.error("Failed to retrieve county structure data.");
showMapErrorMessage("Unable to retrieve county structure data.");
return false;
}
else
{
counties.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("id", function (d, i) {
return "county_" + d.properties.GEO_ID
})
.attr("data-id", function (d, i) { return d.properties.GEO_ID })
.attr("data-name", function (d, i) { return countyLookup[d.properties.GEO_ID] })
.attr("data-stateid", function (d, i) { return d.properties.STATE })
.attr("d", path);
// Show all counties for state specified and apply zoom transform.
d3.selectAll(countySelector).attr("visibility", "visible");
d3.selectAll(countySelector).attr("transform", stateTransform);
// Show all cities for the state specified and apply zoom transform
d3.selectAll(citySelector).attr("visibility", "visible");
d3.selectAll(citySelector).attr("transform", stateTransform);
}
});
This works fine here, except for really small states, the zoom factor is much larger, and the circles get distored.
Is there a way to force the size of the points to be a fixed size (say a 15px radius) even after the transform occurs?
This is happening because you are setting a scale transform instead of scaling the positions. You can see the difference here Basically it is the difference between:
// Thick lines because they are scaled too
var bottom = svg.append('g').attr('transform', 'scale('+scale+','+scale+')');
bottom.selectAll('circle')
.data(data)
.enter().append('circle')
.attr('cx', function(d) { return d.x; })
.attr('cy', function(d) { return d.y; });
and
// line thicknesses are nice and thin
var top = svg.append('g');
top.selectAll('circle')
.data(data)
.enter().append('circle')
.attr('cx', function(d) { return d.x * scale; })
.attr('cy', function(d) { return d.y * scale; });
With mapping probably you best solution is to compute your offset and scale as you do and then add them into your projection function - you want to directly modify the post-projection x and y values. If you update your projection function properly you should not have to do anything else to apply the appropriate zoom to your map.
这篇关于如何调整D3中的点的缩放大小?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!