【问题标题】:D3 v4 invert functionD3 v4 反转功能
【发布时间】:2017-07-30 12:43:33
【问题描述】:

我正在尝试使用逆投影将 JPG 底图投影到正交投影上。我已经能够让它在 D3 的 v3 中工作,但我在 D3 的 v4 中遇到了问题。出于某种原因,v4 将源图像的边缘作为背景(而不是我指定的黑色背景)。 v4 中的逆投影是否存在任何已知问题或对此有任何修复?

  • D3 v4 JSBin Link

    <title>Final Project</title>
    <style>
    
    canvas {
      background-color: black;
    } 
    
    </style>
    <body>
      <div id="canvas-image-orthographic"></div>
      <script src="//d3js.org/d3.v4.min.js"></script>
    <script>
    
    // Canvas element width and height
    var width = 960,
        height = 500;
    
    // Append the canvas element to the container div
    var div = d3.select('#canvas-image-orthographic'),
        canvas = div.append('canvas')
            .attr('width', width)
            .attr('height', height);
    
    // Get the 2D context of the canvas instance
    var context = canvas.node().getContext('2d');
    
    // Create and configure the Equirectangular projection
    var equirectangular = d3.geoEquirectangular()
        .scale(width / (2 * Math.PI))
        .translate([width / 2, height / 2]);
    
    // Create and configure the Orthographic projection
    var orthographic = d3.geoOrthographic()
       .scale(Math.sqrt(2) * height / Math.PI)
       .translate([width / 2, height / 2])
       .clipAngle(90);
    
    // Create the image element
    var image = new Image(width, height);
    image.crossOrigin = "Anonymous";
    image.onload = onLoad;
    image.src = 'https://tatornator12.github.io/classes/final-project/32908689360_24792ca036_k.jpg';
    
    // Copy the image to the canvas context
    function onLoad() {
    
        // Copy the image to the canvas area
        context.drawImage(image, 0, 0, image.width, image.height);
    
        // Reads the source image data from the canvas context
        var sourceData = context.getImageData(0, 0, image.width, image.height).data;
    
        // Creates an empty target image and gets its data
        var target = context.createImageData(image.width, image.height),
            targetData = target.data;
    
        // Iterate in the target image
        for (var x = 0, w = image.width; x < w; x += 1) {
            for (var y = 0, h = image.height; y < h; y += 1) {
    
                // Compute the geographic coordinates of the current pixel
                var coords = orthographic.invert([x, y]);
    
                // Source and target image indices
                var targetIndex,
                    sourceIndex,
                    pixels;
    
                // Check if the inverse projection is defined
                if ((!isNaN(coords[0])) && (!isNaN(coords[1]))) {
    
                    // Compute the source pixel coordinates
                    pixels = equirectangular(coords);
    
                    // Compute the index of the red channel
                    sourceIndex = 4 * (Math.floor(pixels[0]) + w * Math.floor(pixels[1]));
                    sourceIndex = sourceIndex - (sourceIndex % 4);
    
                    targetIndex = 4 * (x + w * y);
                    targetIndex = targetIndex - (targetIndex % 4);
    
                    // Copy the red, green, blue and alpha channels
                    targetData[targetIndex]     = sourceData[sourceIndex];
                    targetData[targetIndex + 1] = sourceData[sourceIndex + 1];
                    targetData[targetIndex + 2] = sourceData[sourceIndex + 2];
                    targetData[targetIndex + 3] = sourceData[sourceIndex + 3];
    
            }
    
        }
    }
    
      // Clear the canvas element and copy the target image
        context.clearRect(0, 0, image.width, image.height);
        context.putImageData(target, 0, 0);
    
    }
    
    
    </script>
    

【问题讨论】:

    标签: javascript d3.js canvas projection inverse


    【解决方案1】:

    问题是反转函数不是一对一的。我知道有两种方法可以解决这个问题。一,计算构成投影的圆盘面积并跳过该半径之外的像素。或者两个(我在下面使用),计算你的坐标的正向投影,看看它们是否与你开始时的 x,y 坐标相匹配:

    if ( 
      (Math.abs(x - orthographic(coords)[0]) < 0.5 ) &&
      (Math.abs(y - orthographic(coords)[1]) < 0.5 ) 
     ) 
    

    基本上这个要求是[x,y] 等于projection(projection.invert([x,y]))。通过确保该语句相等(或接近相等),则像素确实在投影盘中。这是必需的,因为多个 svg 点可以表示给定的纬度,但 projection() 只返回您想要的。

    上面的代码块中有一个舍入误差的容差因子,只要正向投影在原始 x,y 坐标的半个像素内,它就会被绘制(看起来效果很好):

    我有一个更新的 bin here(点击运行,我取消选中自动运行)。

    与计算投影圆盘的半径相比,这自然是计算量更大的过程(但该方法仅限于投影到圆盘上的投影)。

    question 的两个答案或许可以进一步解释——它们涵盖了这两种方法。

    【讨论】:

    • 非常感谢!我必须更清楚地阅读您的答案以及您提供的其他 SO 问题。
    • 在您的帮助下,我已经能够将拖动功能(使用 versor)应用于画布图像的正交投影。我还能够覆盖点的 SVG(代表过去任务的“着陆点”)。但是,我无法使用 SVG 拖动功能来处理这些点。我有一个新的 JSBin 设置 here。关于为什么会出现这种情况的任何想法?我能够用画布绘制点,但我发现为点创建工具提示太困难了。
    • 你可以无视!我只是通过更新每个拖动功能中的地图路径来让它工作:)
    猜你喜欢
    • 2017-06-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-24
    • 1970-01-01
    • 2018-08-21
    • 2019-05-03
    相关资源
    最近更新 更多