如何调整D3中的点的缩放大小? [英] How do I adjust zoom size for a point in D3?

查看:956
本文介绍了如何调整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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆