米勒投影像素

时间:2018-04-20 16:25:56

标签: coordinates map-projections

我尝试使用Miller Projection将坐标转换为像素。 我的方法如下:

function millerProjection(lat, lng) {

        // Create sec() function //
        function sec(value) {
            return 1/Math.cos(value);
        }

        // Create fucntion to change degree to radian //
        function toRadian(value) {
            return value * Math.PI / 180;
        }

        lng = toRadian(lng);
        lat = toRadian(lat);

        // Miller Projection
        // var x = lng;
        // var y = 1.25 * Math.log(Math.tan(Math.PI / 4 + 0.4 * (lat)));

        // Mercator Projection
        // var x  = lng;
        // var y = Math.log(Math.tan(lat) + sec(lat));

        var mapSet = {
            leftLong: toRadian(-180),
            rightLong: toRadian(180),
            topLat: toRadian(90),
            bottomLat: toRadian(-90),
            imageWidth: 2057,
            imageHeight: 1512,
        }

        var x = (lng - mapSet.leftLong) * (mapSet.imageWidth / (mapSet.rightLong - mapSet.leftLong));
        var y = (mapSet.topLat - lat) * (mapSet.imageHeight / (mapSet.topLat - mapSet.bottomLat));

        console.log(`Miller Projection X: ${x} -- Y: ${y}`);

        return { x: x,  y: y };

    }

我将此图片用作地图: https://upload.wikimedia.org/wikipedia/commons/5/5f/Miller_projection_SW.jpg

显然如果我使用0,0坐标则标记正确的位置。 如果我给它任何其他坐标它不起作用。地图可能是问题,还是我使用的逻辑存在问题?

1 个答案:

答案 0 :(得分:1)

此:

  var x = (lng - mapSet.leftLong) * (mapSet.imageWidth / (mapSet.rightLong - mapSet.leftLong));
  var y = (mapSet.topLat - lat) * (mapSet.imageHeight / (mapSet.topLat - mapSet.bottomLat));

假设纬度和经度都有线性x和y变换,但这仅适用于经度。您可以在参考图像中看到纬度之间的不同间距。

让我们回到您评论的投影并使用它,因为它是正确的,但需要缩放和翻译:

function millerProjection(lat, lng) {

  // Create sec() function
  function sec(value) {
    return 1/Math.cos(value);
  }

 // Create fucntion to change degree to radians
 function toRadian(value) {
   return value * Math.PI / 180;
 }
 
 lng = toRadian(lng);
 lat = toRadian(lat);

 // Miller Projection
 var x = lng;
 var y = 1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
 
 return [x,y];
 
}

console.log(millerProjection(90,180));
console.log(millerProjection(-90,-180));

x轴的输出范围为-π至+π,y轴的范围约为0.733倍。

现在我们可以扩展和翻译。我会先缩放并稍后翻译,但反之亦然。

要缩放,我们需要知道边界框或输出范围的宽度或高度。方面是固定的,所以如果不指定两者,这是最简单的,而是从另一个中确定一个。如果我们不均衡地拉伸输出,我们就不再有米勒了。

考虑到宽度的尺寸,我们可以使用以下内容进行缩放:

var scale = width / Math.PI / 2; 

我们希望看到每个弧度需要多少像素。然后我们可以通过比例多次投影输出来获得缩放值。使用上述内容,我们还可以使用d3的地理平台模块库来验证我们的投影:

function millerProjection(lat, lng) {

  // Create sec() function
  function sec(value) {
    return 1/Math.cos(value);
  }

 // Create fucntion to change degree to radians
 function toRadian(value) {
   return value * Math.PI / 180;
 }
 
 lng = toRadian(lng);
 lat = toRadian(lat);

 // Miller Projection
 var x = lng;
 var y = -1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
 
 var width = 360;
 var scale = width/Math.PI/2;
 x *= scale;
 y *= scale;
 
 return [x,y];
 
}

//// 
// set up reference scale:
var width = 360;
var height = width / Math.PI / 2.303412543376391; // aspect ratio

// set d3 projection for reference:
var d3Miller = d3.geoMiller()
 .fitSize([360,180*0.7331989845], {"type": "Polygon","coordinates": [[[180, 90], [-180, 90], [-90, -180], [180, 90]]] })
	.translate([0,0]);
  
// compare the two:
console.log("90N,180W:")
console.log("Miller:  ", ...millerProjection(90,-180));
console.log("d3Miller:", ...d3Miller([-180,90]));

console.log("90S,180E:")
console.log("Miller:  ",...millerProjection(-90,180));
console.log("d3Miller:", ...d3Miller([180,-90]));

console.log("90S,180E:")
console.log("Miller:  ",...millerProjection(-57,162));
console.log("d3Miller:", ...d3Miller([162,-57]));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>

我采取了纬度的负值(基于你的例子),因为虽然投影的地理坐标在底部有y = 0 - y值随着向上移动而增加。相反,像(大多数?)图像的顶部y = 0 - y值随着向下移动而增加。 D3预计后一种约定,所以我没有为参考函数

做这件事

看起来不错。我们的数据现在在x轴上的范围为-width/2width/2,在y轴上的范围为约0.733倍。让我们翻译数据,使其占据左下坐标[0,0]和右上坐标[宽,宽* 0.733]的边界框。这相当容易,在我们缩放数据后,我们将width/2添加到x值,将width/2*0.733(或稍微更准确地说,width/2/*0.7331989845)添加到y值:

function millerProjection(lat, lng) {

  // Create sec() function
  function sec(value) {
    return 1/Math.cos(value);
  }

 // Create fucntion to change degree to radians
 function toRadian(value) {
   return value * Math.PI / 180;
 }
 
 lng = toRadian(lng);
 lat = toRadian(lat);

 // Miller Projection
 var x = lng;
 var y = -1.25*Math.log(Math.tan(Math.PI/4+0.4*(lat)));
 
 var width = 2057;
 var scale = width/Math.PI/2;
 x *= scale;
 y *= scale;
 
 x += width/2;
 y += width/2*0.7331989845
 
 return [x,y];
 
}
  
// compare the two:
console.log("90N,180W:")
console.log("Miller:  ", ...millerProjection(90,-180));

console.log("90S,180E:")
console.log("Miller:  ",...millerProjection(-90,180));

console.log("45N,45E:")
console.log("Miller:  ",...millerProjection(45,45));

console.log("90S,180E:")
console.log("Miller:  ",...millerProjection(-57,162));
.as-console-wrapper { max-height: 100% !important; top: 0; }
<script src="https://d3js.org/d3-array.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>

当然,如果您正在进行图像处理,其中顶部应该是y = 0并且如果向下移动则y值会增加,请在进行任何操作之前翻转纬度的符号。