【问题标题】:Cannot get labels to work with D3-tile projection无法使标签与 D3 平铺投影一起使用
【发布时间】:2018-11-26 05:29:51
【问题描述】:

我正在尝试将一些简单的国家标签添加到 D3 矢量地图中,该地图位于 D3 平铺栅格地图之上。标签正在按预期创建,但我无法在地图上正确投影它们。 D3-tile 中的投影有点混乱(我的意思是它不像在“正常”矢量图上那样工作,我不明白)。

我有created a jsfiddle,我在其中创建地图,然后尝试投影它们,以便它们随着用户交互移动。

未能实现这一点的代码位在这里:

  d3.selectAll(".country_labels")   
    .attr("transform", function(d) {return "translate(" + path.centroid(d) + ")"})  

更新

我怀疑我在这个问题上的问题与我 raised earlier today on here 的问题相似。我还注意到一个类似的问题was raised here too

我已经取得了一些进展,并整理了这个新的fiddle。标签现在都在地图上,但漂浮在几内亚湾周围,接近地理坐标 [0,0]。对我来说,这意味着它们可能已正确投影,但变焦并未按预期发挥作用。这里的问题是这个脚本中有三种不同类型的坐标:

  1. 地理坐标 - 这些是起点并且始终是固定的
  2. “d3-tile”坐标。适合单个像素的像素,因此总是非常接近于零
  3. 像素坐标 - 这些对应于屏幕上的实际坐标

【问题讨论】:

    标签: javascript d3.js


    【解决方案1】:

    这与您的其他问题类似,只是它是在正投影和缩放而不是反转。 (我在更新之前开始写这个,但必须运行,我会继续使用你的原始代码)。

    与路径一样,您可以按预期附加标签:

    country_labels.selectAll("text")
      .data(collection.features)
      .enter().append("text")
      .attr("x", function(d){return path.centroid(d)[0];})
      .attr("y", function(d){return path.centroid(d)[1];})
      .attr("dx", -40)
      .text(function(d){ return d.properties.name })
      .style("fill", "#aeaeaf") 
      .style("font-size", "15px")
    

    这里有一个问题,因为大多数 d3-tile 示例的投影,包括你的,使用 1/tau 的 d3-projection 比例,世界被投影在 1 个像素的空间内,所以 dx值等于 40 个世界,这在应用缩放时不起作用,所以让我们放下那部分

    现在您或多或少地像路径一样附加特征,但问题在于缩放处理:

    d3.selectAll(".country_labels") 
      .attr("transform", function(d) {return "translate(" + path.centroid(d) + ")"})    
    

    路径被给予类似的处理:

    vector
        .attr("transform", "translate(" + [transform.x, transform.y] + ")scale(" + transform.k + ")")
        .style("stroke-width", 1 / transform.k); 
    

    但这里有几个不同之处:

    1. 与文本相比,您正在对路径应用不同的变换(缩放和平移):对于文本,没有对当前缩放变换的引用,而是仅使用锚定的投影在 0,0 处,所有特征都位于一个像素的区域内(并且锚定在 0,0 处的基线将在 y=0 处,文本将大部分不在视野范围内)。如果你检查 svg,你会看到文本,只是在错误的位置。

    2. 路径在放大时会减小笔画宽度(当我们缩放 svg 时,笔画宽度本身会增加),同样适用于文本,因此即使文本定位正确,它也会非常非常大(比浏览器的大多数屏幕都大)。

    我们可以解决这个问题的一种方法是我们在文本的 x/y 坐标上应用缩放变换,而不是元素本身(这也会缩放文本大小,这样我们就不需要调整文本大小完全):

    country_labels.selectAll("文本") .attr("x", function(d){return transform.apply(path.centroid(d))[0];}) .attr("y", function(d){return transform.apply(path.centroid(d))1;})

    inversion from svg pixel to lat/long 一样,我们执行相同的动作,但顺序相反:应用投影,然后应用缩放。

    这是updated fiddle


    但是,我有一个坏消息 - 标签的位置正好位于您现在告诉它们的位置。但它们不是你想要它们在的地方(俗话说,编程最好的事情是代码完全按照你说的做,编程最糟糕的事情是代码做到底是什么意思?)。

    您正在使用路径质心来放置标签,这有时适用于某些功能,但并非始终有效。以美国为例,使用墨卡托投影的美国质心不在美国,因为它位于阿拉斯加和美国本土 48 个州之间(对不起,夏威夷,这里没有太大的吸引力)。加拿大的质心部分位于北冰洋,在许多数据集中(这并不令人惊讶),在使用质心作为文本锚点时,由于法属圭亚那,法国被标记在大西洋中部。

    您可以使用.style("text-anchor","middle") 稍微改善视觉外观,它至少将标签居中(对于较小或公平的国家非常有用),但最终的质心位置并不理想。

    我将结束:注释是制图的祸根。

    但是,有希望,here's 我见过的更有希望的未来之一。

    【讨论】:

    • 安德鲁 - 你的答案再次完美。当我说你真的在 D3 瓷砖上散发出大量的光时,我确信我不仅仅是在为自己说话。快速提问 - 一旦转换(缩放到绑定或用户平移/缩放)完成,您如何修改小提琴以重新计算所有标签位置?根据浏览器的不同,我发现标签确实会拖累性能并使动画抖动。
    • 也感谢您对质心放置的讨论 - 我发现这非常有用,我也对您分享的链接感到惊讶。
    • (抱歉耽搁了,一直在这里打开和关闭计算机),是的,转换这么多标签可能会影响性能。特别想到一个选项:使用 d3.geoContains 和一个动态生成的 geojson 代表视口(找到当前和下一个缩放状态的 svg 每个角的纬度/经度)来查看不需要的标签完全绘制,只绘制和过渡那些需要的。这将减少过渡元素的数量并加快速度。
    • 我会查看 d3.geoContains - 我以前没有听说过这个!我现在想到的解决方案更简单......只需在用户交互时禁用所有标签/单击有界缩放并在转换完成后重新启用它们。这样,标签只计算一次,并在过渡结束时计算。
    • 这可能是最简单的方法,我本来打算推荐它,但被其他选择所困扰。
    猜你喜欢
    • 1970-01-01
    • 2019-09-14
    • 1970-01-01
    • 2013-06-24
    • 1970-01-01
    • 2010-10-26
    • 1970-01-01
    • 2011-01-07
    • 2015-11-21
    相关资源
    最近更新 更多