如何将给定坐标转换成“直径”的边界框? X? [英] How to translate a given coordinate into a bounding box of "diameter" x?

查看:77
本文介绍了如何将给定坐标转换成“直径”的边界框? X?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

例如,我有一个十进制格式的纬度和经度(与lat = 44.1°9.5′30′之类的度-时-分相反)。要搜索附近的物体,我必须将搜索半径指定为具有四个值的矩形:

For example, I have a latitude and longitude in decimal format (as opposed to Degrees-Hours-Minutes like lat=44.1° 9.5' 30''). To search for nearby objects, I must specify the search "radius" as a rectangle with four values:

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

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

Is there a formula or rule of thumb how to convert decimal lat/long values into a rectangular bounding box, such that the given latitude/longitude is in the center of that box?

我是否必须摆弄WGS84椭球算法,或者是否有开放的解决方案?

Must I fiddle around myself with a WGS84 ellipsoid algorithm or are there open solutions to the problem?

推荐答案

我确实遇到了这个问题,解决方案并不是那么简单,但是好消息是,经过很多

I had exactly this problem and the solution isn't that straight forward, but the good news is that after a lot of work (and a great deal of help from SO and Google) I think I've cracked it.

Prob4之类的库很多,它们提供了许多功能,例如Proj4。算法来执行所需的转换,但结果却让我感到有些困惑,并最终编写了自己的代码(我一直很想知道事情的工作原理)。

There are lots of libraries around such as Proj4 which offer a multitude of algorithms to perform the required transformations, but coming at it cold I found it all a bit confusing and ended up writing my own code (I always like to know how things work).

我的解决方案基于 ECEF ,它的工作原理如下...
As我确定你已经可以看出,纬度线总是相距相同的距离(10度和20度之间的距离与20度和30度之间的距离相同),但经度线会聚在两极。因此,赤道经度在10度和20度之间的距离远大于两极附近(在两极处为0)。
因此,您可以轻松算出2个纬度之间的距离,但要使用经度,则必须考虑纬度。
在赤道附近1度的纬度与1度的纬度几乎相同,因此,如果我们要投影的地图的中心为(0,0),我们可以将lat和long乘以一个常数即可从地图中心获取任意给定点的米。
因此,我的算法有效地旋转了地球,直到地图的实际中心位于0,0。

My solution is based on ECEF and it works like this... As I'm sure you've figured out, lines of latitude are always the same distance apart (the distance between 10 degrees and 20 degrees is the same as that between 20 and 30), but lines of longitude converge to meet at the poles. So the distance between 10 degrees and 20 degrees longitude at the equator is much larger than near the poles (and is 0 at the poles). So you can easily work out how many metres between 2 degrees of latitude, but to do this with longitude you have to take the latitude into account. Near the equator 1 degree of lat is pretty much the same distance as 1 degree of long, so if the map we're projecting had it's centre (0, 0) we can simply multiply lat and long by a constant to get metres from the map centre for any given point. So my algorithm effectively rotates the globe until the actual centre of the map is at 0, 0.

因此,说中心实际上位于(50.52,- 4.82)-这就是我的情况。
假设您拿着一个地球仪,然后在可见中心正下方0纬度,0长的位置向下看它。
我们需要做的是将当前(0,0)正下方的地球仪向西(向右)旋转,直到(0,-4.82)在我们下方。
然后,我们将地球向南(向下)旋转,直到(50.52,-4.82)在我们下方。
第三步,然后我们可能要顺时针或逆时针旋转它,以校正地图相对于真实北向的方向(如果真实北向在您的地图上笔直或您感兴趣的全部在距离不受限制的情况下,您将不需要执行此操作)
因此,从概念上讲,这就是我们需要做的,但这与我们的算法有什么关系?
答案是一个变换(类),在该变换中我们以三个旋转角度进给。此类具有一个公共函数,给定一个经纬度对,旋转后将在地球上返回该点的新经纬度对。
然后,我们就知道了地球的半径,可以将这对新坐标转换为x和y坐标,代表与地图原点的距离。

So say the centre is really at (50.52, -4.82) - which it is in my case. Imagine you're holding a globe and looking down on it with 0 lat, 0 long directly below you in the visible centre. What we need to do is take our globe which currently has (0, 0) directly below us and rotate it in a westward (to the right) direction until (0, -4.82) is below us. Then we rotate the globe southward (down) until (50.52, -4.82) is below us. As a third step, we may then want to rotate it clockwise or anti-clockwise to correct for the orientation of the map with respect to true north (if true north is straight up on your map or if all you are interested in is distance not bearing, you won't need to do this) So conceptually that's what we need to do, but how does that relate to our algorithm? The answer is a transform (class) where we feed in three angles of rotation. This class has a public function which, given a lat/long pair, will return a new lat/long pair of that point on the globe after rotation. And once we've done that, knowing the radius of the earth, we can convert this new pair into x and y coordinates, representing the distance from our map origin.

在这里我要提到的是,赤道的地球比两极的地球要宽,但是解决这一问题的数学方法根本不值得打扰。但是,您可以计算出x,y坐标,因为地球不平坦,所以它们总是略微偏斜,对我而言,下面的代码可以完成这项工作。
如果您的地图非常靠近两极,我怀疑此算法的结果可能会变得非常不准确-纬度/经度在两极上并不是很有效(只需从上方看一下Google Earth) 。

I should mention here that the earth is wider at the equator than it is at the poles, but the maths to deal with this is quite simply not worth the bother. However you calculate your x, y coords they will always be slightly out since the earth is not flat and for me, the code presented below does the job. If your map is very close to the poles I suspect the results from this algorith may become quite inaccurate - basically lat/long doesn't really work very well at the poles (just take a look at google earth from above).

MapTransform类要求您进行一些设置。
setRadius(1000);设置转换以使用半径为1000(单位)
setBody( EARTH)的球体;用地球的平均半径(以米为单位)设置转换
setRotation(x,y,z);将变换设置为绕Z轴旋转z度,Y轴旋转y度,然后绕X轴旋转x度。
-基本上,给定您的中心点(纬度,经度),并且地图上的正北是笔直的,则需要以下内容:setRotation(0,lat,-long);
-旋转顺序在这里非常重要,并且基于Z轴与地球旋转​​一致,Y轴旋转最近的曲面的坐标系(回过头来,您拿着的地球)。向上/向下的地球,而X轴是您一直沿视的轴-希望这有意义,这是一个难以描述的概念-请参见旋转矩阵

The MapTransform class requires you to setup a few things. setRadius(1000); sets up the transform to work with a sphere of radius 1000 (units) setBody("EARTH"); sets up the transform with the mean radius of the earth (in metres) setRotation(x, y, z); sets up the transform to rotate about Z axis by z degrees, Y axis by y degrees then X axis by x degrees. - basically, given your centre point (lat, long) and given that true north on the map is straight up, you would need the following: setRotation(0, lat, -long); - the order of rotation is very important here and based on a coordinate system (looking back at the globe you're holding) where the Z axis coincides with the rotation of the earth, the Y axis rotates the closest surface of the globe up/down and the X axis is the axis you are looking along - hope this makes sense, it's a difficult concept to describe - see Rotation Matrix

鉴于您需要从特定点将纬度/经度映射到米,以上就是您所需要的。

Given your requirement to map from lat/long to metres from a specific point, the above should be all you need.

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

The function getMapPosition(lat, long) will return a double[] containing x, y in map units (metres if radius was specified in metres) from your origin

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

My class goes a bit further in terms of applying the coordinates to a specific map tile...

setMapOrigin( x,y);设置地图的旋转原点(旋转后观察者正下方的点)相对于地图左下角的位置。标称值应该以米为单位(如果使用setBody( EARTH);则肯定是),但必须与指定半径的单位相同。
setMapSize(w,h);设置地图的大小(以米或您决定使用的单位为单位)。

setMapOrigin(x, y); sets up where the rotational origin of the map (the point directly below the observer after rotation) is in relation to the bottom left corner of your map. Nominally this should be in metres (certainly if you used setBody("EARTH");) but needs to be in the same units as the specified radius. setMapSize(w, h); sets up the size of the map in metres or what ever units you decided to use.

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

Finally, setBitmapSize(w, h) allows you to describe the size of the bitmap (in pixels) onto which you are projecting your map. In my application I have a bitmap representation of the map area and use the transform to supply the exact coorinates of the pixel on my bitmap where a point should be plotted. However, this isn't part of the question you asked so you may not need it.

真的希望这会有所帮助-似乎和所有示例一样漫长而复杂我现在正在看一个月前。

Really hope this helps - seems just as long winded and complicated as all the examples I was looking at a month ago now.

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?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆