如何将给定坐标转换为“直径”x的边界框?

例如,我有十进制格式的纬度和经度(而不是像Lat = 44.1°9.5’30’那样的度数 – 小时 – 分钟)。 要搜索附近的对象,我必须将搜索“半径”指定为具有四个值的矩形:

north = 44.1; south = -9.9; east = -22.4; west = 55.2; 

是否有公式或经验法则如何将十进制纬度/经度值转换为矩形边界框,以便给定的纬度/经度位于该框的中心?

我必须使用WGS84椭球算法来摆弄自己,还是有问题的开放式解决方案?

我确实遇到了这个问题并且解决方案不是那么直接,但好消息是经过大量的工作(以及来自SO和Google的大量帮助)之后我想我已经破解了它。

有许多库,比如Proj4,提供了大量的算法来执行所需的转换,但是它很冷,我发现它有点令人困惑,最后编写了我自己的代码(我总是想知道它是如何工作的) 。

我的解决方案基于ECEF并且它的工作原理如下……我确信你已经想到了,纬度线总是相同的距离(10度到20度之间的距离与20度之间的距离相同)和30),但经度线汇合在两极相遇。 因此,赤道10度和20度经度之间的距离远远大于极点附近(极点处为0)。 因此,您可以轻松计算出2度纬度之间的米数,但要以经度进行此操作,您必须考虑纬度。 在赤道附近,1度纬度与1度长的距离几乎相同,所以如果我们投影的地图有中心(0,0),我们可以简单地将lat和long乘以常数来得到距离任何给定点的地图中心。 所以我的算法有效地旋转地球,直到地图的实际中心为0,0。

所以说中心真的是(50.52,-4.82) – 这是我的情况。 想象一下,你正在拿着一个地球仪并在可见中心的正下方0长0俯视它。 我们需要做的是将我们的地球仪直接(0,0)直接放在我们的下方并向西旋转(向右),直到(0,-4.82)低于我们。 然后我们向南(向下)旋转地球,直到(50.52,-4.82)低于我们。 作为第三步,我们可能希望顺时针或逆时针旋转它以校正地图相对于真北的方向(如果真北在地图上是直的,或者如果您感兴趣的是距离不是轴承,你不需要这样做)从概念上讲,这就是我们需要做的,但这与我们的算法有什么关系? 答案是变换(类),我们以三个旋转角度进行输入。 这个类有一个公共函数,给定一个纬度/经度对,在旋转后将在地球上返回一个新的纬度/长度对。 一旦我们知道地球的半径,我们就可以将这对新坐标转换为x和y坐标,表示距我们地图原点的距离。

我在这里应该提到的是,地球在赤道处比在极地处更宽,但处理这个问题的数学计算是不值得的。 然而,你计算你的x,y坐标,因为地球不平坦,它们总是略微出来,对我来说,下面给出的代码可以完成这项工作。 如果你的地图非常靠近两极,我怀疑这个算法的结果可能变得非常不准确 – 基本上纬度/长度在极点上并不能很好地工作(只需从上面看看谷歌地球)。

MapTransform类要求您设置一些东西。 setRadius(1000); 设置变换以使用半径为1000(单位)的球体setBody(“EARTH”); 用地球的平均半径(以米为单位)setRotation(x,y,z)设置变换; 将变换设置为绕Z轴旋转z度,Y轴旋转y度,然后旋转X轴x度。 – 基本上,给定你的中心点(lat,long)并且假设地图上的真北是直线上升,你需要以下:setRotation(0,lat,-long); – 旋转顺序在这里是非常重要的,并且基于坐标系(回顾你正在握住的地球),其中Z轴与地球的旋转一致,Y轴旋转地球的最近表面/向下,X轴是你正在观察的轴 – 希望这是有道理的,这是一个难以描述的概念 – 参见旋转矩阵

鉴于您需要将lat / long映射到特定点的米,以上应该是您所需要的。

函数getMapPosition(lat,long)将返回一个double [],其中包含x,y的地图单位(米,如果半径以米为单位),来自您的原点

在将坐标应用于特定地图图块方面,我的课程更进一步……

setMapOrigin(x,y); 设置地图的旋转原点(旋转后观察者正下方的点)相对于地图左下角的位置。 名义上应该以米为单位(当然,如果您使用setBody(“EARTH”);)但需要与指定半径的单位相同。 setMapSize(w,h); 设置地图的大小(以米为单位)或您决定使用的单位。

最后,setBitmapSize(w,h)允许您描述投影地图的位图大小(以像素为单位)。 在我的应用程序中,我有一个地图区域的位图表示,并使用变换提供我的位图上应该绘制点的像素的精确坐标。 但是,这不是您提出的问题的一部分,因此您可能不需要它。

真的希望这会有所帮助 – 似乎和我一个月前看到的所有例子一样冗长和复杂。

 import java.text.DecimalFormat; public class MapTransform { private double circumference; private RotationMatrix rotationMatrix; private double originX; private double originY; private double mapWidth; private double mapHeight; private int bitmapWidth; private int bitmapHeight; public MapTransform() { this.circumference = 0; this.rotationMatrix = new RotationMatrix(); this.rotationMatrix.makeIdentity(); this.originX = 0; this.originY = 0; this.mapWidth = 0; this.mapHeight = 0; this.bitmapWidth = 0; this.bitmapHeight = 0; } public void setCircumference(double circumference) { this.circumference = circumference; } public void setRadius(double radius) { this.circumference = 2 * Math.PI * radius; } public void setBody(String body) { if (body.toUpperCase().equals("EARTH")) { setRadius(6371009); //mean radius of the earth in metres // setRadius(6378137); //equatorial radius of the earth in metres // setRadius(6356752); //polar radius of the earth in metres } else { setRadius(0); } } public void setRotation(double xRotateDegrees, double yRotateDegrees, double zRotateDegrees) { RotationMatrix xMatrix = new RotationMatrix(); RotationMatrix yMatrix = new RotationMatrix(); RotationMatrix zMatrix = new RotationMatrix(); xMatrix.makeRotateX(Math.toRadians(xRotateDegrees)); yMatrix.makeRotateY(Math.toRadians(yRotateDegrees)); zMatrix.makeRotateZ(Math.toRadians(zRotateDegrees)); this.rotationMatrix = zMatrix.concatenate(yMatrix).concatenate(xMatrix); } public void setMapOrigin(double originX, double originY) { this.originX = originX; this.originY = originY; } public void setMapSize(double width, double height) { this.mapWidth = width; this.mapHeight = height; } public void setBitmapSize(int width, int height) { this.bitmapWidth = width; this.bitmapHeight = height; } public double[] getMapPosition(double[] geoPosition) { return getMapPosition(geoPosition[0], geoPosition[1]); } public double[] getMapPosition(double latitude, double longitude) { // convert the GeoPosition into an NVector NVector vec = new NVector(latitude, longitude); // rotate the vector in 3D vec = rotationMatrix.transform(vec); // convert the vector into 2D units by applying circumference to latitude/longitude and adding origins double x = vec.getLongitude() * this.circumference / 360; double y = vec.getLatitude() * this.circumference / 360; // return a MapPosition return new double[] {x, y}; } public float[] getPixelPosition(double[] mapPosition) { return getPixelPosition(mapPosition[0], mapPosition[1]); } public float[] getPixelPosition(double mapX, double mapY) { // apply origin and scale based on map and bitmap widths float x = (float) ((this.originX + mapX) * this.bitmapWidth / this.mapWidth); // apply origin and scale based on map and bitmap heights, but invert to measure from top left instead of bottom left float y = (float) (this.bitmapHeight - (this.originY + mapY) * this.bitmapHeight / this.mapHeight); return new float[] {x, y}; } public class RotationMatrix { String name = ""; public double array [][] = {{0,0,0},{0,0,0},{0,0,0}}; public RotationMatrix() {} public RotationMatrix(String name) { this.name = name; } public void makeIdentity() { for(int x = 0; x <= 2; x++) { for (int y = 0; y <= 2; y++) { array[x][y] = (x == y)? 1: 0; } } } public void makeRotateX(double thetaRadians) { double cosTheta = Math.cos(thetaRadians); double sinTheta = Math.sin(thetaRadians); makeIdentity(); array[1][1] = cosTheta; array[2][1] = -sinTheta; array[1][2] = sinTheta; array[2][2] = cosTheta; } public void makeRotateY(double thetaRadians) { double cosTheta = Math.cos(thetaRadians); double sinTheta = Math.sin(thetaRadians); makeIdentity(); array[0][0] = cosTheta; array[2][0] = sinTheta; array[0][2] = -sinTheta; array[2][2] = cosTheta; } public void makeRotateZ(double thetaRadians) { double cosTheta = Math.cos(thetaRadians); double sinTheta = Math.sin(thetaRadians); makeIdentity(); array[0][0] = cosTheta; array[1][0] = -sinTheta; array[0][1] = sinTheta; array[1][1] = cosTheta; } public NVector transform(NVector vec) { NVector vec2 = new NVector(); vec2.x = vec.x * array[0][0] + vec.y * array[1][0] + vec.z * array[2][0]; vec2.y = vec.x * array[0][1] + vec.y * array[1][1] + vec.z * array[2][1]; vec2.z = vec.x * array[0][2] + vec.y * array[1][2] + vec.z * array[2][2]; return vec2; } public void output() { if (this.name != null && this.name.length() == 0) { System.out.println(this.name + "-------"); } DecimalFormat df = new DecimalFormat("0.00"); for(int y = 0; y <= 2; y++) { String out = "| "; double test = 0; for(int x = 0; x <= 2; x++) { String f = df.format(array[x][y]); if (f.length() < 5) f = " " + f; out += f + " "; test = test + array[x][y] * array[x][y]; } if (test > 0.99 && test < 1.01) {test = 1.0;} out += "| (=" + test + ")"; System.out.println(out); } System.out.println(); } public RotationMatrix concatenate(RotationMatrix m2) { RotationMatrix outputMatrix = new RotationMatrix(); for(int x = 0; x <= 2; x++) { for(int y = 0; y <=2; y++) { outputMatrix.array[x][y] = 0; for (int q = 0; q <= 2; q++) { outputMatrix.array[x][y] += this.array[x][q] * m2.array[q][y]; } } } return outputMatrix; } } public class NVector { double x; double y; double z; public NVector() { this.x = 0; this.y = 0; this.z = 0; } public NVector(double x, double y, double z) { this.x = x; this.y = y; this.z = z; } public NVector(double latitude, double longitude) { setLatitudeLongitude(latitude, longitude); } public NVector(double[] geoPosition) { setLatitudeLongitude(geoPosition[0], geoPosition[1]); } private void setLatitudeLongitude(double latitude, double longitude) { double latitudeRadians = Math.toRadians(latitude); double longitudeRadians = Math.toRadians(longitude); double cosLatitude = Math.cos(latitudeRadians); double cosLongitude = Math.cos(longitudeRadians); double sinLatitude = Math.sin(latitudeRadians); double sinLongitude = Math.sin(longitudeRadians); this.x = cosLatitude * cosLongitude; this.y = cosLatitude * sinLongitude; this.z = sinLatitude; } public double getLatitude() { return Math.toDegrees(Math.atan2(this.z, Math.sqrt(this.x * this.x + this.y * this.y))); } public double getLongitude() { return Math.toDegrees(Math.atan2(this.y, this.x)); } public double[] getGeoPosition() { double[] geoPosition = new double[] {this.getLatitude(), this.getLongitude()}; return geoPosition; } public void output() { output(""); } public void output(String name) { if (name != null && name.length() == 0) { System.out.println("NVector: " + name); } DecimalFormat df = new DecimalFormat("0.00"); String vector = df.format(this.x) + "," + df.format(this.y) + "," + df.format(this.z); String coords = ""; try { coords = df.format(Math.toDegrees(this.getLatitude())) + "N " + df.format(Math.toDegrees(this.getLongitude())) + "E"; } catch(Exception e) { coords = "(coords unknown)"; } System.out.println("(" + vector + ") at " + coords); } } } 

如果你在谈论地球仪上的坐标,在歧管上是否真的没有明确的“矩形边界框”定义?

难道你不能通过平均直尺的尺寸来近似“盒子”的中心,就像在笛卡尔坐标中一样:

 x_center = x_left + (x_right - x_left) / 2 y_center = y_bottom + (y_top - y_bottom) / 2 

为什么不从中心点做一个范围/方位来定义方框角落的纬度/经度(如果这就是你要问的那个)? 使用45度,135度,225度,315度的四个轴承。 请访问此网站,了解“目的地点范围/承载”: http : //www.movable-type.co.uk/scripts/latlong.html

另一个,更短的回答我的另一篇文章,如果你只需要距离我在SO上发现的原点的另一个纬度的距离(惊喜)。 原始答案在这里 ,但我相信您正在寻找的代码是……

 public static double distFrom(double lat1, double lng1, double lat2, double lng2) { double earthRadius = 6371009; //mean radius of the earth in metres double dLat = Math.toRadians(lat2-lat1); double dLng = Math.toRadians(lng2-lng1); double a = Math.sin(dLat/2) * Math.sin(dLat/2) + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * Math.sin(dLng/2) * Math.sin(dLng/2); double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); double dist = earthRadius * c; return dist; }