d3.js比例符号图:根据数据值设置圆的半径

2024-05-24

我正在遵循这个关于如何使用 d3.js 和 mapbox 制作地图的精彩示例:

https://franksh.com/posts/d3-mapboxgl/ https://franksh.com/posts/d3-mapboxgl/

它工作得很好,除了我想知道如何使用这个例子,使圆圈成比例符号,即圆圈的半径将反映数据值。

<div id="map"></div>
<script>
  mapboxgl.accessToken = "YOUR_TOKEN";
  var map = new mapboxgl.Map({
    container: "map",
    style: "mapbox://styles/mapbox/streets-v9",
    center: [-74.5, 40.0],
    zoom: 9
  });


  var container = map.getCanvasContainer();
  var svg = d3
      .select(container)
      .append("svg")
      .attr("width", "100%")
      .attr("height", "500")
      .style("position", "absolute")
      .style("z-index", 2);

function project(d) {
  return map.project(new mapboxgl.LngLat(d[0], d[1]));
}

#Lat, long, and value
var data = [
  [-74.5, 40.05, 23],
  [-74.45, 40.0, 56],
  [-74.55, 40.0, 1],
  [-74.85, 40.0, 500],
];

var dots = svg
  .selectAll("circle")
  .data(data)
  .enter()
  .append("circle")
  .attr("r", 20)
  .style("fill", "#ff0000");

function render() {
  dots
    .attr("cx", function (d) {
      return project(d).x;
    })
    .attr("cy", function (d) {
      return project(d).y;
    });
}

map.on("viewreset", render);
map.on("move", render);
map.on("moveend", render);
render(); // Call once to render

</script>

CSS:

#map {
  position: relative;
  z-index: 0;
  width: 100%;
  height: 500px;
} 

如果我根据实际数据值设置半径,有些圆超级大,有些圆超级小:

.attr("r", function(d) { return d[2]; }) //trying to make radius a function of the actual value

同样,应用乘数效果也不好(同样,有些点超级大,有些点超级小):

  .attr("r", function(d) { return d[2] * 2; })

我认为我需要做的是将数据域映射到表示半径的范围(即使用 d3 线性比例),但我不完全确定这里的最佳实践是什么。我见过使用的例子geoPath.pointRadius(function(d))但我认为这并不适用于这里,因为我没有在这个特定示例中使用路径生成器。


您可能希望使用 pointRadius,我认为它不太常见,并且取决于用例更麻烦,但更重要的是,它不能解决缩放问题:您仍然需要指定半径,并且当前的缩放不会产生期望的结果。

圆的面积与半径的平方成正比,这意味着线性刻度并不理想。在这两次尝试中,您基本上都使用了简单的线性比例尺,并且在这两次尝试中,大圆圈都比最小圆圈大 250,000 倍。

我们可以使用 d3.scaleLinear 创建一个更受限制的范围,以便最小的圆的大小更接近较大的圆:

let scale = d3.scaleLinear().domain([0,500]).range([2,20]);
let data = [1, 10, 100, 250, 500];

let g = d3.select("body").append("svg")
  .selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform", (d,i)=>"translate("+(i*50+50)+", 50)")
  
g.append("circle")
  .attr("r", scale)
  .attr("fill", "#ccc")
  
g.append("text")
  .text(d=>d)
  .attr("fill","black")
  .attr("text-anchor","middle")
  .attr("dy", -20);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

但它仍然受到影响,因为该值与值区域不成比例,而是与半径成比例。好吧,半径几乎与值成正比,但由于我们范围的下限是 2,所以这不是真的。我已经用比例范围的下限设置了半径的最小值,以确保接近零的值仍然可见(否则最小的圆基本上是不可见的,因为它的半径为 1/25 像素)。

相反,考虑到圆的面积与半径的平方成正比,我们可以使用平方根比例来使圆的面积(更)与其值成比例:

let scale = d3.scaleSqrt().domain([0,500]).range([2,20]);
let data = [1, 10, 100, 250, 500];

let g = d3.select("body").append("svg")
  .selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform", (d,i)=>"translate("+(i*50+50)+", 50)")
  
g.append("circle")
  .attr("r", scale)
  .attr("fill", "#ccc")
  
g.append("text")
  .text(d=>d)
  .attr("fill","black")
  .attr("text-anchor","middle")
  .attr("dy", -20);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

这里我们的区域更接近与基础值成比例(同样,由于我们的范围不是从 0 开始,这并不完全正确)。我们可以将范围的下限设置为零,这将导致数据在面积方面更加诚实的表示,但是,根据您想要传达的内容,将最小半径保留在上面一点可能是合适的零,如果我们将其更改为零,我们实际上会丢失最小值(1):

let scale = d3.scaleSqrt().domain([0,500]).range([0,20]);
let data = [1, 10, 100, 250, 500];

let g = d3.select("body").append("svg")
  .selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform", (d,i)=>"translate("+(i*50+50)+", 50)")
  
g.append("circle")
  .attr("r", scale)
  .attr("fill", "#ccc")
  
g.append("text")
  .text(d=>d)
  .attr("fill","black")
  .attr("text-anchor","middle")
  .attr("dy", -20);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

我们可以更进一步,使用找到第三个或更高根的比例,这将失去比例性,但可能有助于显示域下部的值差异,同时均匀化域上部的值:

let scale = d3.scalePow().exponent(.25).domain([0,500]).range([0,20]);
let data = [1, 10, 100, 250, 500];

let g = d3.select("body").append("svg")
  .selectAll("g")
  .data(data)
  .enter()
  .append("g")
  .attr("transform", (d,i)=>"translate("+(i*50+50)+", 50)")
  
g.append("circle")
  .attr("r", scale)
  .attr("fill", "#ccc")
  
g.append("text")
  .text(d=>d)
  .attr("fill","black")
  .attr("text-anchor","middle")
  .attr("dy", -20);
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>

当然还有更多的尺度,但对于圆来说,通常平方根尺度是合适的,特别是用零作为域和范围的下限,因为圆与值成比例。然而,当然存在使用不同尺度、域和/或范围的非零边界的情况。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

d3.js比例符号图:根据数据值设置圆的半径 的相关文章

随机推荐