Как перевести данную координату в ограничивающую рамку «диаметр» х? - PullRequest
2 голосов
/ 10 декабря 2011

Например, у меня есть широта и долгота в десятичном формате (в отличие от градусов-часов-минут, таких как широта = 44,1 ° 9,5 '30' ').Чтобы искать близлежащие объекты, я должен указать поиск «радиус» в виде прямоугольника с четырьмя значениями:

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

Существует ли формула или практическое правило, как преобразовать десятичные значения в широте / долготе в прямоугольные ограничения?поле, в котором указанная широта / долгота находится в центре этого поля?

Должен ли я возиться с алгоритмом эллипсоида WGS84 или есть открытые решения проблемы?

Ответы [ 4 ]

2 голосов
/ 11 декабря 2011

У меня была именно эта проблема, и решение не такое простое, но хорошая новость заключается в том, что после большой работы (и большой помощи со стороны 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, они всегда будут слегка отклонены, поскольку Земля не плоская, и для меня код, представленный ниже, делает свою работу. Если ваша карта находится очень близко к полюсам, я подозреваю, что результаты этого алгоритма могут стать весьма неточными - в основном широта / долгота не очень хорошо работают на полюсах (просто взгляните на Google Earth сверху).

Класс MapTransform требует от вас настройки нескольких вещей. setRadius (1000); настраивает преобразование для работы со сферой радиуса 1000 (единиц) setBody ( "ЗЕМЛЯ"); устанавливает преобразование со средним радиусом Земли (в метрах) setRotation (x, y, z); устанавливает преобразование для поворота вокруг оси Z на z градусов, оси Y на y градусов, а затем оси X на x градусов. - в основном, учитывая вашу центральную точку (широта, долгота) и учитывая, что истинный север на карте расположен прямо вверх, вам потребуется следующее: setRotation (0, широта, -long);- порядок вращения здесь очень важен и основан на системе координат (оглядываясь на глобус, который вы держите), где ось Z совпадает с вращением Земли, ось Y вращает ближайшую поверхность шара вверх /вниз, а ось X - это ось, вдоль которой вы смотрите - надеюсь, это имеет смысл, это сложная концепция для описания - см. Матрица вращения

Учитывая ваше требование отображать широту / долготу вметров от конкретной точки, все, что вам нужно, должно быть выше.

Функция 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);
        }
    }
}
0 голосов
/ 12 декабря 2011

Еще один, гораздо более короткий ответ на мой другой пост, если вам просто нужно расстояние до другого лата, длиннее вашей точки происхождения, которое я нашел на 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; 
}
0 голосов
/ 11 декабря 2011

Почему бы не сделать диапазон / азимут от вашей центральной точки, чтобы определить широту / долготу угла коробки (если это то, что вы спрашиваете)?Используйте четыре подшипника 45 градусов, 135 градусов, 225 градусов, 315 градусов.См. Этот веб-сайт для «Пункт назначения из диапазона / подшипника»: http://www.movable -type.co.uk / scripts / latlong.html

0 голосов
/ 10 декабря 2011

Если вы говорите о координатах на глобусе, неужели нет четкого определения «прямоугольной ограничительной рамки» на коллекторе?

Не могли бы вы просто приблизить центр «рамки»"путем усреднения размеров прямоугольника как в декартовых координатах:

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