如何将给定坐标转换为"直径"的边界框。 X?

时间:2011-12-10 19:31:09

标签: ios geolocation gps coordinates geocoding

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

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

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

我是否一定要使用WGS84椭球算法来解决这个问题,还是有问题的开放式解决方案?

4 个答案:

答案 0 :(得分:2)

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

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

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

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

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

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

鉴于您需要将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);
        }
    }
}

答案 1 :(得分:0)

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

难道你不能通过平均直尺的尺寸来近似“框”的中心,如笛卡尔坐标:

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

答案 2 :(得分:0)

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

答案 3 :(得分:0)

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

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; 
}