2017-03-09 79 views
1

我想使用反投影将JPG底图投影到正投影上。我已经能够在D3的v3中使用它,但是我在D3的v4中遇到了问题。出于某种原因,v4将源图像的边缘作为背景(而不是我指定的黑色背景)。是否有任何已知的v4逆向投影问题或针对此问题的任何修复?D3 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> 
    

回答

2

的问题是,反转功能不是一一对应的。我知道有两种方法可以解决问题。一,计算组成投影的光盘区域,并跳过该半径之外的像素。或两个(其中下面我使用),计算你的坐标的前方突出,并查看它们是否匹配的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坐标的半个像素内,它将被绘制(这似乎工作得很好) :

enter image description here

我有一个更新斌here(点击运行,我未选中自动运行)。当然,与计算投影光盘的半径(但该方法仅限于投影到光盘的投影)相比,这是计算上涉及的计算过程。

question的两个答案可能能够进一步解释 - 他们涵盖了两种方法。

+0

非常感谢!我必须更清楚地阅读你的答案,以及你提供的其他SO问题。 – Lprox5

+0

自从您的帮助以来,我已经能够将一个拖曳功能(使用versor)应用于画布图像的正投影。我也可以叠加一个SVG点(代表过去任务的“着陆点”)。但是,我无法使用SVG拖动功能来处理这些问题。我有一个新的JSBin设置[在这里](https://jsbin.com/moconod/edit?html,output)。任何想法为什么这可能是这种情况?我能够用画布绘制点,但是我发现为点创建工具提示太困难了。 – Lprox5

+0

你可以不理会!我只是通过更新每个拖动功能中的地图路径来实现它的工作:) – Lprox5