How to fix map boundaries on d3 cartographic raster reprojection?

后端 未结 2 568
被撕碎了的回忆
被撕碎了的回忆 2021-01-02 20:31

I try to use the raster reprojection of a map following this example. If I change the example kavrayskiy7 projection by the Azimuthal Equidistant projection,

2条回答
  •  没有蜡笔的小新
    2021-01-02 21:00

    While I'm pretty sure you are using the projection.inverse function correctly, relying on :

    if (λ > 180 || λ < -180 || φ > 90 || φ < -90) { i += 4; continue; }
    

    to clip the projection will always fail as projection.inverse appears to always return angles within 180 degrees east/west. While there may be a way to modify the projection itself to return values greater than 180 degrees, it is likely more difficult than other approaches (And honestly, that goes well beyond any answer I can give).

    Likewise, using an SVG path to represent the world outline and then using that as the basis to determine if a point should be drawn is probably complicating the matter.

    Instead, assuming a circular disc, you can easily compute the radius of the disc and from there determine if a pixel should be drawn:

    var edge = {};
    var center = {};
    
    edge.x = projection([180 - 1e-6, 0])[0];
    edge.y = projection([180 - 1e-6, 0])[1];
    
    center.x = width/2;
    center.y = height/2;
    
    var radius = Math.pow( Math.pow(center.x - edge.x,2) + Math.pow(center.y - edge.y,2) , 0.5 )
    

    Using the radius of the disc, we can then compute if a pixel falls on the disc or beyond it in the for loop:

    for (var y = 0, i = -1; y < height; ++y) {
        for (var x = 0; x < width; ++x) {
    
        var p = projection.invert([x, y]), λ = p[0], φ = p[1];
    
            if (Math.pow( Math.pow(center.x-x,2) + Math.pow(center.y-y,2), 0.5) < radius) {
    
                if ( λ > 180 || λ < -180 || φ > 90 || φ < -90 ) { i += 4; continue; }
                    var q = ((90 - φ) / 180 * dy | 0) * dx + ((180 + λ) / 360 * dx | 0) << 2;
                    targetData[++i] = sourceData[q];
                    targetData[++i] = sourceData[++q];
                    targetData[++i] = sourceData[++q];
                    targetData[++i] = 255;
            }
            else {
                targetData[++i] = 0;
                targetData[++i] = 0;
                targetData[++i] = 0;
                targetData[++i] = 0;
            }
        }
    }
    

    Together, these gave me:

    For aesthetic effect, it might be worthwhile trimming the radius down by a certain percent. Of course for different projections this approach may be either difficult or impossible.

    I've put the code into a bl.ock here (I moved it to d3 v4 in the process, partly to see if the behavior of projection.inverse is the same).

    For the third part of your question, you could try d3's graticule (graticule.outline) function for some info on how d3 gets the boundary profile of a projection.

提交回复
热议问题