D3中是否有“轴相等”?

时间:2019-12-10 07:13:56

标签: javascript d3.js math svg data-visualization

我正在使用D3和Python创建一些绘图。使用Python,我可以做到轴相等,如图1所示:

With Axis Equal in Python

我想在使用D3的SVG中实现相同的目标。请参考图2,轴不相等:

Without Axis Equal in Javascript d3

我正在使用的轴刻度如下:

var xScale = d3.scaleLinear()
    .range([0, width])
    .domain([minX, maxX]);

var yScale = d3.scaleLinear()
    .range([height, 0])
    .domain([minY, maxY]);

var xAxis = d3.axisBottom(xScale).ticks(5),
    yAxis = d3.axisLeft(yScale).ticks(5);

谁能告诉我如何使轴与D3相等?

1 个答案:

答案 0 :(得分:4)

简短答案:不,D3中没有“轴相等” 。 D3的级别很低,它只是方法的集合,因此您几乎必须手动 ...

好消息是,您解决问题所需的只是一些数学运算来计算新范围。

作为引言,由于这是一个带有标签的问题,因此“轴相等” Matlab之类的某些程序中使用的术语,... >

  

沿每个轴的数据单元使用相同的长度。

可以从视觉上更好地解释它。看看这张具有不同域和范围(source)的图片:

enter image description here

简短介绍之后,让我们回到您的问题上。假设此简单代码生成了两个轴:

const minX = 0,
  minY = 0,
  maxX = 120,
  maxY = 50,
  width = 500,
  height = 300,
  paddings = [10, 10, 20, 30];

const xScale = d3.scaleLinear()
  .range([paddings[3], width - paddings[1]])
  .domain([minX, maxX]);

const yScale = d3.scaleLinear()
  .range([height - paddings[2], paddings[0]])
  .domain([minY, maxY]);

const svg = d3.select("svg")
  .attr("width", width)
  .attr("height", height);

svg.append("g")
  .attr("transform", `translate(0,${height - paddings[2]})`)
  .call(d3.axisBottom(xScale).tickSizeInner(-height + paddings[2] + paddings[0]));

svg.append("g")
  .attr("transform", `translate(${paddings[3]},0)`)
  .call(d3.axisLeft(yScale).tickValues(xScale.ticks()).tickSizeInner(-width + paddings[3] + paddings[1]));
svg {
  border: 2px solid tan;
}

line {
  stroke-dasharray: 2, 2;
  stroke: gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

我们可以清楚地看到,单位之间的间距不相等。对于创建等距单位的轴,我们可以做两件事:  1.更改域;  2.更改范围。

根据your comment,不能更改域。因此,让我们更改范围。

我们需要的是计算每个用户空间的单位数,并使用其中最大的一个(x或y)来更改相反比例的范围。

例如,给定上面片段的比例:

const xStep = Math.abs((xScale.domain()[1] - xScale.domain()[0]) / (xScale.range()[1] - xScale.range()[0]));

const yStep = Math.abs((yScale.domain()[1] - yScale.domain()[0]) / (yScale.range()[1] - yScale.range()[0]));

xStepyStep大,这说明x轴每像素有更多单位。因此,我们将更改y轴范围:

yScale.range([yScale.range()[0], yScale.range()[0] - (yScale.domain()[1] - yScale.domain()[0]) / xStep]);

这是演示:

const minX = 0,
  minY = 0,
  maxX = 120,
  maxY = 50,
  width = 500,
  height = 300,
  paddings = [10, 10, 20, 30];

const xScale = d3.scaleLinear()
  .range([paddings[3], width - paddings[1]])
  .domain([minX, maxX]);

const yScale = d3.scaleLinear()
  .range([height - paddings[2], paddings[0]])
  .domain([minY, maxY]);

const xStep = Math.abs((xScale.domain()[1] - xScale.domain()[0]) / (xScale.range()[1] - xScale.range()[0]));

const yStep = Math.abs((yScale.domain()[1] - yScale.domain()[0]) / (yScale.range()[1] - yScale.range()[0]));

yScale.range([yScale.range()[0], yScale.range()[0] - (yScale.domain()[1] - yScale.domain()[0]) / xStep]);

const svg = d3.select("svg")
  .attr("width", width)
  .attr("height", height);

svg.append("g")
  .attr("transform", `translate(0,${height - paddings[2]})`)
  .call(d3.axisBottom(xScale).tickSizeInner(-height + paddings[2] + yScale.range()[1]));

svg.append("g")
  .attr("transform", `translate(${paddings[3]},0)`)
  .call(d3.axisLeft(yScale).tickValues(yScale.ticks().filter(function(d){return !(+d%10)})).tickSizeInner(-width + paddings[3] + paddings[1]));
svg {
  border: 2px solid tan;
}

line {
  stroke-dasharray: 2, 2;
  stroke: gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<svg></svg>

使用网格线可以看到,现在单位等距分布。

最后,请记住,以编程方式确定要更改的比例尺,重新调整正确的范围(例如,您的比例尺从底部到顶部),将轴居中等都是完全不同的问题。