D3选择导致难以理解的重叠线图 - 我该如何修复它?

时间:2016-07-26 02:33:42

标签: d3.js leaflet

我正尝试通过Leaflet.D3SvgOverlay实用程序库使用Leaflet叠加层在D3地图上绘制路径。

我检查了我在Python中使用mplleaflet编写的路由函数的输出,并得到了非常整洁的输出(responsible block)。

我写了一个D3的东西应该得到相同的输出。然而,我得到了一条非常锯齿状的线条,经过仔细的视觉检查,结果却是以奇怪的方式交织在一起的几条线:

enter image description here

将绘图更改为一个圆而不是一个路径表明这是由于某种奇怪的排列点:

enter image description here

您可以自己试用负责的代码in this block

然而在这两种情况下(有效的mplleaflet情节和没有的D3情节)我只是试图绘制相同的坐标系列:

[[-73.98208, 40.76529], [-73.98225, 40.76476], [-73.98232, 40.76457], [-73.98238, 40.76441], [-73.98239, 40.76438], [-73.98241, 40.76434], [-73.98245, 40.76423], [-73.98249, 40.76412], [-73.98252, 40.76405], [-73.98254, 40.76402], [-73.98257, 40.76396], [-73.98281, 40.76351], [-73.98288, 40.76339], [-73.98293, 40.7633], [-73.983, 40.76318], [-73.98321, 40.76282], [-73.98326, 40.76273], [-73.98332, 40.76264], [-73.9836, 40.76219], [-73.98368, 40.76207], [-73.98387, 40.76178], [-73.98411, 40.76145], [-73.98452, 40.7608], [-73.98456, 40.76072], [-73.98465, 40.76052], [-73.98487, 40.76013], [-73.98487, 40.76013], [-73.98453, 40.75999], [-73.98418, 40.75984], [-73.98418, 40.75984], [-73.98441, 40.75952], [-73.98461, 40.7592], [-73.98507, 40.75858], [-73.98526, 40.75833], [-73.98553, 40.75796], [-73.98568, 40.75778], [-73.986, 40.75734], [-73.98648, 40.7567], [-73.98695, 40.75603], [-73.98695, 40.75603], [-73.98644, 40.75582], [-73.98644, 40.75582], [-73.98668, 40.75505], [-73.98691, 40.75434], [-73.98713, 40.75362], [-73.98723, 40.75332], [-73.98732, 40.75303], [-73.98735, 40.75291], [-73.98754, 40.75218], [-73.98761, 40.75187], [-73.98762, 40.75183], [-73.98771, 40.75145], [-73.98774, 40.7512], [-73.98783, 40.7507], [-73.98789, 40.75036], [-73.98798, 40.74988], [-73.98804, 40.74968], [-73.98809, 40.74957], [-73.98814, 40.74948], [-73.9883, 40.74925], [-73.9883, 40.74924], [-73.98829, 40.74923], [-73.98819, 40.74919], [-73.98804, 40.74912], [-73.98802, 40.74912], [-73.98802, 40.74911], [-73.98801, 40.7491], [-73.98803, 40.749], [-73.98805, 40.74898], [-73.98807, 40.74896], [-73.98808, 40.74894], [-73.9881, 40.74881], [-73.98812, 40.74869], [-73.98813, 40.74863], [-73.98814, 40.74858], [-73.98817, 40.74845], [-73.9882, 40.74831], [-73.98824, 40.74811], [-73.98836, 40.74754], [-73.98839, 40.74735], [-73.98841, 40.74721], [-73.98843, 40.74713], [-73.98844, 40.74707], [-73.98849, 40.74679], [-73.98858, 40.74604], [-73.98867, 40.74582], [-73.98872, 40.74559], [-73.98877, 40.7453], [-73.9889, 40.74455], [-73.98894, 40.74435], [-73.98899, 40.74412], [-73.98903, 40.74392], [-73.98904, 40.7438], [-73.98905, 40.74368], [-73.98911, 40.74337], [-73.98915, 40.74316], [-73.98917, 40.74303], [-73.98919, 40.74294], [-73.98924, 40.74261], [-73.98924, 40.74257], [-73.98924, 40.74253], [-73.98923, 40.74252], [-73.98922, 40.7425], [-73.98902, 40.74231], [-73.98902, 40.74231], [-73.98906, 40.74225], [-73.98931, 40.7419], [-73.9894, 40.7418], [-73.98959, 40.74155], [-73.9901, 40.74087], [-73.99053, 40.74025], [-73.99096, 40.73967], [-73.99139, 40.73908], [-73.99181, 40.73849], [-73.99222, 40.73792], [-73.99267, 40.73732], [-73.99315, 40.73669], [-73.99347, 40.73624], [-73.99352, 40.73618], [-73.99363, 40.73602], [-73.99416, 40.7353], [-73.99459, 40.73467], [-73.99507, 40.73403], [-73.99552, 40.73344], [-73.99594, 40.73284], [-73.99638, 40.73225], [-73.99668, 40.73185], [-73.99698, 40.73141], [-73.99698, 40.73141], [-73.9971, 40.73147], [-73.9985, 40.73216], [-73.99863, 40.73222], [-73.99863, 40.73222], [-73.99873, 40.73211], [-73.99913, 40.73164], [-73.99962, 40.73105], [-73.99962, 40.73105], [-73.99886, 40.73067], [-73.99886, 40.73067], [-73.99903, 40.73046]]

检查控制台显示D3地块一遍又一遍地重叠在一起,指出了我的某种错误:

enter image description here

我的问题是: this code我在哪里搞砸了,我该如何解决?

1 个答案:

答案 0 :(得分:2)

这里有两个主要问题。

首先,每次用户缩放时,您都会向overlay元素添加一个新行。这是多条线在彼此之上的原因。标准d3方法仅在基础数据更改时更新画布,因此更好的方法是仅在输入新数据时更新路径。请注意使用selectAllenter()方法:

    // Paints a single sampler path.
    function paintPath(linearray) {

        // Define x and y conversions.
        var line = d3.svg.line()
                .x(function(d) { return proj.latLngToLayerPoint(d).x})
                .y(function(d) { return proj.latLngToLayerPoint(d).y});

        var updateSelection = sel.selectAll('path').data([linearray]);
        updateSelection.enter()
                .append("path")
                .attr({
                    "class": "sample-line",
                    "d": line,
                    "fill": "transparent",
                    "stroke": "steelblue",
                    "stroke-width": 0.1,
                    "shape-rendering": "crispEdges"
                })
    }

第二个问题是latLngToPoint功能。从文档中,这个函数:

  

将给定缩放的地理坐标投影到像素中   坐标

当您缩小时,映射到像素的位置不是很精确(因此您看到的锯齿状线条)。因为当用户放大时你没有删除这一行,所以附加的附加行覆盖了这条初始的锯齿线,这条线也会被放大。在更接近的缩放级别,到像素的映射更接近地反映真实的坐标,并且你得到了更好的线条。这解释了您所看到的奇怪的锯齿状叠加。

这会产生一些问题,因为仅仅调用.enter()不会更新该行,因为基础数据没有改变。一个简单的解决方案(在性能方面可能不完美)就是每次用户放大时重新绘制线条:

    // Paints a single sampler path.
    function paintPath(linearray) {

        // Define x and y conversions.
        var line = d3.svg.line()
                .x(function(d) { return proj.latLngToLayerPoint(d).x})
                .y(function(d) { return proj.latLngToLayerPoint(d).y});

        sel.selectAll('path').remove();
        var updateSelection = sel.selectAll('path').data([linearray]);
        updateSelection.enter()
                .append("path")
                .attr({
                    "class": "sample-line",
                    "d": line,
                    "fill": "transparent",
                    "stroke": "steelblue",
                    "stroke-width": 0.1,
                    "shape-rendering": "crispEdges"
                })
    }

更好的解决方案可能是使用latLngToLayerPoint返回的数据作为您的D3 data元素。这样,当您放大到更高分辨率时,您将能够更新线条。我会让你实现这个。

要在缩放时保持恒定的线宽,可以使用“矢量效果”:“非缩放中风”。请注意笔划宽度的变化。这几乎是我将用于CSS的内容。

.attr({
       "class": "sample-line",
       "d": line,
       "fill": "transparent",
       "stroke": "steelblue",
       "stroke-width": 1,
       "shape-rendering": "crispEdges",
       "vector-effect": "non-scaling-stroke"
      })

编辑:忽略我对性能的评论。似乎即使没有删除和重新出现的行(即只保留第一行),当你在地图上滚动时仍然会有一些滞后。这可能是传单方面的一个问题。

Edit2:请注意,我还更改了paintPathSampler以删除对paintPath的第二次调用:

            // Paints all of the paths.
            function paintPathSampler() {
                d3.json("path_sampler.json", function (data) {
                    paintPath(data[0]);
                  //  paintPath(data[1]);
//                     paintPointPath(data[0]);

                });
            }