Преобразование из географических координат в экранные

Добрый день!

Пытаюсь реализовать трёхмерную программу навигации на Java

Возникла сложность с преобразованием географических координат (широта и долгота) в экранные.

Сейчас реализовал алгоритм этого преобразования, как описано в книге “Имитационное моделирование экономических процессов” Емельянов А.А, Там есть глава “Отображение геоинформации в функциональном окне имитационной модели”

В итоге написал следующий код


public  class GeoTransform {
    
    double lonmax,  lonmin,  latmax,  latmin;
    double l1, l2, xmax, ymax,theta;

//В конструктор передается максимум по x и у для экрана минимальная и максимальная широта и долгота
    public GeoTransform(double xmax, double ymax, double lonmax, double lonmin, double latmax, double latmin)
    {
        this.latmax=latmax;
        this.latmin=latmin;
        this.lonmax=lonmax;
        this.lonmin=lonmin;
        this.xmax=xmax;
        this.ymax=ymax;
        l1=xmax/Math.sin(0.5*Math.abs(lonmax-lonmin));
        l2=(l1-2*ymax)/Math.cos(0.5*Math.abs(lonmax-lonmin));
         theta=Math.abs(lonmax-lonmin);
        
    }
  
//Получить радиус земли на заданной широте  
 static   public  double GetGeoRadiusMeters(double Lat)
    {
        double z1=6378245.0;
        double z2=6356863.019;
        double R=(z1*z2)/Math.sqrt(Math.pow(z1, 2)*Math.pow(Math.cos(Lat), 2)+Math.pow(z2, 2)*Math.pow(Math.sin(Lat), 2));                                                                               
        return R;
        
    }
 
  //Вычисляет x на экране   
    public double FindScreenX(double lat, double lon)
    {
        double l=CalculateL(lat);
        //My code is here
        double x=l*(Math.sin(-0.5*theta+(lon-lonmin)));
        
        return x;
        
        
    }
 //Вычисляет у на экране 
    public double FindScreenY(double lat, double lon)
    {
        double l=CalculateL(lat);
        
        double y=ymax-(l1-l*Math.cos(-0.5*theta+(lon-lonmin)));
        
        return y;
    }
    
    private double CalculateL(double lat)
    {
        double l = l1-(l1-l2)*(lat-latmin)/(latmax-latmin);
        return l;
    }
    public double GetDirection(double lat1, double lon1, double lat2,double lon2){
            
             double x=(Math.cos(lat1)*Math.sin(lat2)-Math.sin(lat1)*Math.cos(lat2)*Math.cos(lon2-lon1));
             double y=Math.sin(lon2-lon1)*Math.cos(lat2);
             double br=360-Math.toDegrees(Math.atan2(y, x));
             if(br>360)
             {
             br=br-360;

             }
             return br;

    }
    
    
  static void GetRect(double lat1, double lon1, double radius, NODE vl, NODE nr)
    {
         
       double r=GetGeoRadiusMeters(lat1);
      double gamma=radius*360/(2*Math.PI*r);
          
          
        vl.setLatitude(gamma+lat1);
        vl.setLongitude(lon1-gamma);
        
        nr.setLatitude(lat1-gamma);
        nr.setLongitude(gamma+lon1);
        
        
    }
    
    int IsInRect( NODE point,  NODE vl, NODE nr)
    {
        
        if(point.getLatitude()<vl.getLatitude()&&point.getLatitude()>nr.getLatitude()&&point.getLongitude()>vl.getLongitude() &&point.getLongitude()<nr.getLongitude())
        {
            return 1;
        }
        else
        {
            return 0;
        }
        
    }
    
    
    
    
}



В результате перпендикулярные линии не перпендикулярны при отображении:

http://fotki.yandex.ru/users/atour07/view/530477/?page=0

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

Или киньте ссылку может кто сталкивался с подобной задачей.

Никак не могу найти.

Спасибо.

Если для Вас важно сохранение в проекции углов, читать следует про проекцию Меркатора.

А в JOSME как это реализовано? Похоже, что там углы сохраняются

Не могу найти в каком модуле производится это преобразование.

В коде Josm’а не разбирался.
Но в нем используется именно проекция Меркатора.
С моей точки зрения, гораздо быстрее прочитать 2-3 экрана о проекции Меркатора и запрограммировать ее самому, чем искать нужный фрагмент в мегабайтах исходников, а потом еще чесать репу, как этот код адаптировать для своего проекта.

Большое спасибо!

Есть еще одна проекция, которая используется в навигационных программах - это вот эта http://en.wikipedia.org/wiki/Equirectangular_projection
Опорная параллель при этом выбирается та, что проходит через центр экрана.

BushmanK
Это личные знания или гугл ? Потому что из данных там формул (а скриншот там подтверждает формулы) ну никак эта проекция не годится для нав.применения. Кроме того если вчитаться в текст, то сказано что эта проекция как раз бесполезна для навигации

Это личные знания, которые основаны на тщательном анализе скриншотов с навигаторов Delorme и Garmin. Проекция на цилиндр, пересекающий поверхность Земли по параллели, всегда находящейся в центре экрана. Опорная параллель динамически меняется. По крайней мере, для сильного увеличения (когда в экран влезают единицы сотен километров и много меньше). Для масштабов, когда в экран влезает континент, не изучал.
Для эксперимента, достаточно сгенерировать простейшую карту (можно растровую - это быстрее) для Garmin с кольцами радиусом километров в 100 и посмотреть, что происходит с ними при скроллинге с юга на север. Эта же проекция используется в Mapsource (там опорную параллель еще можно менять какой-то хитрой комбинацией кнопок, которую я не помню).
http://www.gps-forum.ru/cgi-bin/forum/showpost.pl?Board=gpsgeneral&Number=123475&Search=true&Forum=gpsgeneral&Words=quirect&Match=Entire%20Phrase&Searchpage=0&Limit=100&Old=allposts тут вот пара примеров есть, для опорной 0 и 40 градусов. Вот точно также навигаторы ее и меняют - смотрим на нулевую параллель, будет нулевая опорная, смотрим на сороковую - сороковая, и так далее.

BushmanK
По указанной ссылке и картинкам - скажите какая проекция используется в Гармине. Неужели круг в Гармине превращается в эллипс. У меня нет Гармина, но подозреваю что это не так.

В приборах Гармин не получается эллипс потому что опорная параллель все время меняется. Используется следующая:
x=Lon*cos(LatScreen)
y=Lat
где
x,y - координаты на экране
Lat, Lon - широта и долгота в градусах
LatScreen - широта текущего центра экрана.
По крайней мере, при при достаточно сильном увеличении. Если отдалить карту, на юге и севере некоторая эллипсовидность кругов видна, но при вертикальной прокрутке сдвинуть ее в центр не удастся, так что проекция именно такая.

Мне понятен скепсис, но обвинять меня во вранье - как-то слишком.

BushmanK
Видимо речь идет о поперечном Меркаторе на сфере (Transverse Mercator) с изменяемым нулевым (центральным) меридианом и параллелью. Формула превращается

dLat=Lat-LatScreen
dLon=Lon-LonScreen
x=dLon*cos(LatScreen)
y=dLat

где
LatScreen, LonScreen - центр экрана (соответственно xy=[0,0])
Lat, Lon - исходные широта и долгота отображаемой точки в градусах
x,y - координаты точки на экране, которые можно привести к нужному масштабу.

Эта проекция не сохраняет углы, а, судя по первому посту, это важно.

Кажется я понял… Наверное BushmanK прав про гармин. Странно что Гармин выбрал такой путь - карта должна быть картой, углы и масштаб нужно сохранять.
У поперечного Меркатора ™ с этим гораздо лучше, а формула лишь ненамного сложнее.

Для мапсоурса согласен - там надо весь мир отображать, можно и схематически. TM для этого не подходит.

Если бы в приборах это была поперечная Меркатора, то даже при сравнительно небольшом (3-4 градуса) охвате с запада на восток было бы видно, что параллели не выглядят параллельными (аналогично тому, как это видно на бумажных листах уже полумиллионного масштаба).

В TomTom и Sygic так и есть. Ещё и меридианы сходятся.

Я думаю что у Гармин это все наследие первых черно-белых приемников с поддержкой карт, где процессор и так еле ворочал картой.