View Issue Details

IDProjectCategoryView StatusLast Update
0001188SAS.ПланетаХотелка / Feature requestpublic02-10-2012 18:26
Reportervdemidov Assigned Tozed  
PrioritynormalSeverityminorReproducibilityN/A
Status resolvedResolutionfixed 
Product Version110418 
Target Version121010Fixed in Version121010 
Summary0001188: Алгоритм проецирования точки на эллипсоиде
DescriptionНужен алгоритм проецирования точки на эллипсоиде. То есть функция получает координаты исходной точки, азимут и расстояние в метрах, а возвращает координаты новой точки.
Additional Informationhttp://ru.wikipedia.org/wiki/Ортодромия
TagsNo tags attached.
Attached Files
geo84.pas (7,462 bytes)   
///////////////////////////////////////////////////////////////////////////////
//
//          Thaddeus Vincenty's inverse formula for ellipsoids
//           Geodesic (shortest distance) between two points
//
//                Lat1, Lon1, Lat2, Lon2  in radians
//
//  Notes and Java script     ? 2002-2006 Chris Veness
//
//  Delphi Translation         May 2006  Charles Seitz

{ For the benefit of the terminally obsessive (as well as the genuinely needy),
 Thaddeus Vincenty (?TV?) devised formulae for calculating geodesic distances
 between a pair of latitude/longitude points on the earth?s surface, using an
 accurate ellipsoidal model of the earth. When I looked up the references
 (?Direct and Inverse Solutions of Geodesics on the Ellipsoid with application
 of nested equations?), I discovered to my surprise that while the mathematics
 is utterly beyond me, it is actually quite simple to program. Vincenty?s
 formula is accurate to within 0.5mm, or 0.000015? (!), on the ellipsoid being
 used. Calculations based on a spherical model, such as the (much simpler)
 Haversine, are accurate to around 0.3% (which is still good enough for most
 purposes, of course).

 Note: the accuracy quoted by Vincenty applies to the theoretical ellipsoid
 being used, which will differ (to varying degree) from the real earth geoid.
 If you happen to be located in Colorado, 2km above msl, distances will be
 0.03% greater. In the UK, if you measure the distance from Land?s End to
 John O? Groats using WGS-84, it will be 28m ? 0.003% ? greater than using the
 Airy ellipsoid, which provides a better fit for the UK.}


                                UNIT GEO84;

                                 INTERFACE

//        Lat1, Lon1, Lat2, Lon2  in radians;   Result in km
//                    W and S are negative
//              Calculates TC12 (True) in radians


function
  Geodesic(const Lat1, Lon1, Lat2, Lon2: Double; out TC12: Double): Double;
procedure
  GeoRadial(const Lat1, Lon1, Tc, D: Double; out Lat, Lon: Double);



                              IMPLEMENTATION

uses
  SysUtils, Math;

const                                       // WGS-84 (meters)

  a0 = 6378137.0000;                        // Earth radius at equator
  b0 = 6356752.3142;                        // Earth radius at poles
  f0 = 1/298.257223563;                     // Flattening
  r0 = 1-f0;

  aa = a0*a0;   bb = b0*b0;


procedure RaiseException(Msg: string);
begin  raise Exception.Create(Msg)  end;


function
  GeoDesic(const Lat1, Lon1, Lat2, Lon2: Double; out TC12: Double): Double;
var
  IterLimit: Integer;

  L, U1, U2, SinU1, CosU1, SinU2, CosU2, Lambda, LambdaP, SinLambda, CosLambda,
  Sigma, SinSigma, CosSigma, Cos2SigmaM, SinAlpha, CosSqAlpha, uSqr, A, B, C,
  DeltaSigma, TC21: Extended;

begin
  if (Lat1 = Lat2) and (Lon1 = Lon2) then            // Coincident points
    begin
      Result := 0.0;
      Exit;
    end;

  L := Lon2-Lon1;

  if (Lat1 = 0) and (Lat2 = 0) then                  // Along equator
    begin
      Result := Abs(a0*L);
      if (L > 0) then
        TC12 := PI/2
      else
        TC12 := 3*PI/2;
      Exit;
    end;

  U1 := ArcTan(r0 * Tan(Lat1));
  U2 := ArcTan(r0 * Tan(Lat2));
  SinCos(U1, SinU1, CosU1);
  SinCos(U2, SinU2, CosU2);
  Lambda := L;
  LambdaP := 2*PI;
  IterLimit := 20;
  while (Abs(Lambda-LambdaP) > 1E-12) do
    begin
      SinCos(Lambda, SinLambda, CosLambda);
      SinSigma :=
        Sqrt((Sqr(CosU2*SinLambda)) + Sqr(CosU1*SinU2-SinU1*CosU2*CosLambda));
      CosSigma := SinU1*SinU2 + CosU1*CosU2*CosLambda;
      Sigma := ArcTan2(SinSigma, CosSigma);
      SinAlpha := CosU1 * CosU2 * SinLambda / SinSigma;
      CosSqAlpha := 1 - Sqr(SinAlpha);
      Cos2SigmaM := CosSigma - 2*SinU1*SinU2/CosSqAlpha;
      C := f0/16*CosSqAlpha*(4+f0*(4-3*CosSqAlpha));
      LambdaP := Lambda;
      Lambda := L + (1-C) * f0 * SinAlpha *
        (Sigma + C*SinSigma*(Cos2SigmaM+C*CosSigma*(-1+2*Sqr(Cos2SigmaM))));
      Dec(IterLimit, 1);
      if (iterLimit<=0) then
        RaiseException('GeoDis failed to converge');
    end;
  uSqr := CosSqAlpha * (aa-bb)/bb;
  A := 1 + uSqr/16384*(4096+uSqr*(-768+uSqr*(320-175*uSqr)));
  B := uSqr/1024 * (256+uSqr*(-128+uSqr*(74-47*uSqr)));
  DeltaSigma :=
    B*SinSigma*(Cos2SigmaM + B/4*(SinSigma*(-1+2*Sqr(Cos2SigmaM))-
    B/6*Cos2SigmaM*(-3+4*SinSigma*SinSigma)*(-3+4*Sqr(Cos2SigmaM))));
  Result := b0*A*(Sigma-DeltaSigma)/1000;

  TC12 := ArcTan2(CosU2*SinLambda, CosU1*SinU2 - SinU1*CosU2*CosLambda);
  if (TC12 < 0) then TC12 := 2*Pi + TC12;

  TC21 := ArcTan2(cosU1*SinLambda, (-sinU1*cosU2 + cosU1*sinU2*cosLambda))-pi;
  if (TC21 < 0) then TC21 := 2*Pi + TC21;
 
end;  // Vincinty's inverse algorithm


///////////////////////////////////////////////////////////////////////////////
//
//          Thaddeus Vincenty's direct formula for ellipsoids
//
//                Lat1, Lon1, Lat, Lon, Tc in radians
//                D in km


procedure GeoRadial(const Lat1, Lon1, Tc, D: Double; out Lat, Lon: Double);
var
  SinTc, CosTc, U1, TanU1, SinU1, CosU1, SinAlpha, CosAlpha, Usqr, S, A, B, C,
  Term1, Term2, Term3, Sigma, SinSigma, CosSigma, TanSigma1, DeltaSigma,
  Sigma1, TwoSigmaM, c2sm, LastSigma, Lambda, Omega: Extended;

  begin
    if D = 0.0 then                                  // Coincident points
      begin
        Lat := Lat1;                                 // TC21 := 0.0;
        Lon := Lon1;
        Exit;
      end;

    S := D*1000;
    TanU1 := r0 * Tan(Lat1);
    TanSigma1 := TanU1/Cos(Tc);                      //eq 1
    U1 := ArcTan(TanU1);
    SinCos(U1, SinU1, CosU1);
    SinAlpha := CosU1 * Sin(Tc);                     //eq 2
    CosAlpha := Sqrt(1 - Sqr(SinAlpha));
    Usqr := Sqr(CosAlpha) * (aa-bb)/bb;

    Term1 := Usqr/16384;
    Term2 := 4096 + Usqr*(-768 + Usqr*(320 - 175*Usqr));

    A := 1 + Term1 * Term2;
    B := Usqr / 1024*(256 + Usqr*(-128 + Usqr*(74 - 47*Usqr)));   //eq 4
                                                     
    Sigma := S / (b0*a);
    Sigma1 := ArcTan(TanSigma1);

    repeat
      Lastsigma := Sigma;
      TwoSigmaM := 2*Sigma1 + Sigma;                 //eq 5
      SinCos(Sigma, SinSigma, CosSigma);
      SinCos(Tc, SinTc, CosTc);
      c2sm := Cos(TwoSigmam);

      DeltaSigma :=                                 
        B*SinSigma*(c2sm + b/4 * (CosSigma*(-1 + 2*Sqr(c2sm)) -
        B / 6*c2sm * (-3 + 4*Sqr(SinSigma)) * (-3 + 4*Sqr(c2sm))));    //eq 6

      Sigma := S / (b0*a) + DeltaSigma;              //eq 7
    until (Abs(Sigma - LastSigma) <= 1E-12);

    TwoSigmam := 2*Sigma1 + Sigma;                   //eq 5
    c2sm := Cos(TwoSigmaM);
    SinCos(Sigma, SinSigma, CosSigma);
    Term1 := SinU1*CosSigma + CosU1*SinSigma*CosTc;
    Term2 := Sqr(SinAlpha) + Sqr(SinU1*SinSigma - CosU1*CosSigma*CosTc);
    Term3 := r0*Sqrt(Term2);
    Lat   := ArcTan2(Term1, Term3);
    Term1 := SinSigma*Sin(Tc);
    Term2 := CosU1*CosSigma - SinU1*SinSigma*CosTc;
    Lambda := ArcTan2(Term1, Term2);

    C := f0/ 16*Sqr(CosAlpha) * (4 + f0*(4 - 3*Sqr(CosAlpha)));

    Omega := Lambda - (1-c) * f0 * SinAlpha *
               (Sigma + C*SinSigma*(c2sm + C*CosSigma*(-1 + 2*Sqr(c2sm))));

    Lon := Lon1+Omega;

  end;  // Vincinty's direct algorithm

end.  //  GEO84 
geo84.pas (7,462 bytes)   

Relationships

related to 0002088 resolvedzed Добавить в IDatum функцию построения N промежуточных точек отрезка 
child of 0000713 resolvedvdemidov Настраиваемые кольца расстояния вокруг метки положения 
child of 0001116 confirmed При измерении расстояний отображать линии в виде дуг 
child of 0000663 resolvedzed Создание круглых областей заданного радиуса 
child of 0000094 resolvedvdemidov Добавить создание точек по азимуту 
child of 0000099 confirmed Добавить инструмент - вычисление азимута между двумя существующими метками 
child of 0000219 resolvedzed Отображение азимута в инструменте измерений расстояний 
child of 0000143 confirmed Навигация на точку(метку) 
child of 0001051 confirmed Построение лучей азимутов 
child of 0001021 resolvedzed Измерение растояния 
child of 0001616 resolvedzed Выделение квадратной области определяемый указанным радиусом вписанной окружности 

Activities

vasketsov

26-02-2012 12:52

manager   ~0005659

есть такое, куда залить или отдельно приложить? только не азимут, а смещение x и y.

vdemidov

26-02-2012 13:23

manager   ~0005661

Нет. Нужен именно азимут.

vasketsov

26-02-2012 13:52

manager   ~0005664

>расстояние в метрах
о каких примерно величинах идёт речь? метры? сотни километров?
о плюсах?

в вики про локсодромию только на сфере написано (((
http://ru.wikipedia.org/wiki/%CB%EE%EA%F1%EE%E4%F0%EE%EC%E0

vdemidov

26-02-2012 14:20

manager   ~0005665

А я не про локсодромию, а про навигацию на большом круге. Соответственно расстояния могут быть любыми до половины длинны экватора.

vdemidov

26-02-2012 14:28

manager   ~0005666

http://ru.wikipedia.org/wiki/Ортодромия
Только все эти формулы на сфере, а нужно на эллипсоиде.

vasketsov

26-02-2012 14:35

manager   ~0005667

>я не про локсодромию
Вообще-то "координаты исходной точки, азимут и расстояние в метрах, а возвращает координаты новой точки" - именно движение по локсодромии. Или имелось ввиду что начальный азимут задаёт направление, а дальше по нему идём прямо и азимут меняется, вплоть за полюс?

vdemidov

26-02-2012 21:07

manager   ~0005670

Last edited: 26-02-2012 21:14

>начальный азимут задаёт направление, а дальше по нему идём прямо и азимут меняется, вплоть за полюс?
Именно.
Локсодромия интересна только при навигации по обычным бумажным картам.

vdemidov

26-02-2012 21:34

manager   ~0005671

Нужны алгоритмы аналогичные используемым вот в этом онлайн калькуляторе:
http://planetcalc.ru/722/

Tolik

27-02-2012 04:14

manager   ~0005672

Наверно, и 0001116 можно сюда привязать?

Dima2000

19-05-2012 07:48

developer   ~0007118

Last edited: 19-05-2012 22:09

Вики про длину дуги даже эллипса говорит "Получившийся интеграл принадлежит семейству эллиптических интегралов, которые в элементарных функциях не выражаются, и сводится к эллиптическому интегралу второго рода E(t,e)." - т.е. или апроксимировать какой-то сферой (для малых расстояний почему бы нет? Или нужна офигительная точность?) или считать итерационно (разбивать отрезок на кусочки и каждый считать на своей приближенной сфере). Т.е. вопрос лишь в применении, какая точность необходима. Для всех связанных тикетов мне кажется достаточно будет и навигации по сфере, для которой есть точные формулы.

Проверил по тому "идеальному" калькулятору, Москва - Владивосток, расстояние 6445км/6456км, погрешность менее 0.2%, азимуты отличаются менее 0.05° (WGS84 vs сфера). Неужели для чего-то нужна ещё бОльшая точность?

vdemidov

21-05-2012 05:17

manager   ~0007141

Я ж не против. Реализуйте хоть на сфере.

vdemidov

12-07-2012 05:29

manager   ~0007799

http://www.spywatcher.com.ua/forum/index.php?PHPSESSID=a35d32a4bfd512c148109fd622141327&topic=45.0

vdemidov

02-08-2012 16:52

manager   ~0008050

Нужно создать что-то похожее на то что появилось в API Яндекс-Карт
http://api.yandex.ru/maps/doc/jsapi/2.x/ref/reference/coordSystem.geo.xml

zed

02-08-2012 19:49

manager   ~0008053

http://habrahabr.ru/post/143898/

Со своей стороны мы написали две стандартных реализации — для обычной декартовой плоскости и для референсного эллипсоида WGS 84. Для второй реализации мы использовали формулы Винсенти (http://en.wikipedia.org/wiki/Vincenty%27s_formulae). Кстати, непосредственно реализовывал эту логику runawayed (http://habrahabr.ru/users/runawayed/), передаём ему привет :).

Постучись в гости к этому "runawayed", может он и для САСа такое сделает. Или хотя бы кинет исходники (если они не засекречены).

vdemidov

03-08-2012 06:34

manager   ~0008060

Ну у меня на хабре аккаунта нету. Так что не смогу. Если кто постучится к runawayed буду благодарен.

zed

02-10-2012 07:19

manager   ~0009104

Last edited: 02-10-2012 07:23

vdemidov
>Нужны алгоритмы аналогичные используемым вот в этом онлайн калькуляторе:
Немного покопался в теме: все расчёты базируются на алгоритме некого поляка Vincenty и непродолжительное гугление по имени вывело на исходники, реализующие этот алгоритм: http://www.programmersheaven.com/download/48584/19/ZipView.aspx

Исходники на всякий случай приаттачил (geo84.pas). Необходимая нам реализация в процедуре: procedure GeoRadial(const Lat1, Lon1, Tc, D: Double; out Lat, Lon: Double);

Оно?

Только сейчас обратил внимание, что и на хабре оказывается про этого же поляка говорили, так что скорее всего оно.

vdemidov

02-10-2012 07:33

manager   ~0009105

Стопудово оно. Только нужно убрать жесткую привязку к константам и загнать в класс TDatum. Еще хорошо бы сравнить результаты функции Geodesic с тем что сейчас реализовано.

zed

02-10-2012 12:20

manager   ~0009111

Сейчас попробую заняться.

На сколько я понял, вот эта константа:
> f0 = 1/298.257223563; // Flattening

это тоже самое, что и FExct: Double; в TDatum?

vdemidov

02-10-2012 12:25

manager   ~0009112

Тоже самое с точность может быть до обратного значения. Я точно не помню. Просто сравни значение для стандартного эллипсоида WGS84

zed

02-10-2012 15:50

manager   ~0009124

Оказалось не тоже самое.

zed

02-10-2012 15:59

manager   ~0009125

На всякий случай, оставлю тут ссылку на калькулятор, которым я проверял правильность работы алгоритма: http://www.geomidpoint.com/destination/

zed

02-10-2012 18:26

manager   ~0009126

>Еще хорошо бы сравнить результаты функции Geodesic с тем что сейчас реализовано.
Сравнил. САС-овский алгоритм оказался немножко менее точный, поэтому переделал и его до кучи.

Issue History

Date Modified Username Field Change
26-02-2012 10:05 vdemidov New Issue
26-02-2012 10:16 vdemidov Relationship added child of 0000713
26-02-2012 12:52 vasketsov Note Added: 0005659
26-02-2012 13:23 vdemidov Note Added: 0005661
26-02-2012 13:52 vasketsov Note Added: 0005664
26-02-2012 14:20 vdemidov Note Added: 0005665
26-02-2012 14:28 vdemidov Note Added: 0005666
26-02-2012 14:35 vasketsov Note Added: 0005667
26-02-2012 21:07 vdemidov Note Added: 0005670
26-02-2012 21:14 vdemidov Note Edited: 0005670
26-02-2012 21:26 vdemidov Additional Information Updated
26-02-2012 21:34 vdemidov Note Added: 0005671
27-02-2012 04:14 Tolik Note Added: 0005672
27-02-2012 08:56 vdemidov Relationship added child of 0001116
27-02-2012 09:03 vdemidov Relationship added child of 0000663
27-02-2012 16:13 vdemidov Status new => confirmed
27-02-2012 16:13 vdemidov Target Version => 41xxxx
05-03-2012 06:15 vdemidov Relationship added child of 0000094
05-03-2012 06:15 vdemidov Relationship added child of 0000099
05-03-2012 08:22 vdemidov Relationship added child of 0000219
05-03-2012 10:13 vdemidov Relationship added child of 0000143
05-03-2012 10:57 vdemidov Relationship added child of 0001051
19-05-2012 07:48 Dima2000 Note Added: 0007118
19-05-2012 22:09 Dima2000 Note Edited: 0007118
21-05-2012 05:17 vdemidov Note Added: 0007141
12-07-2012 05:29 vdemidov Note Added: 0007799
02-08-2012 16:52 vdemidov Note Added: 0008050
02-08-2012 19:49 zed Note Added: 0008053
03-08-2012 06:34 vdemidov Note Added: 0008060
02-10-2012 07:19 zed Note Added: 0009104
02-10-2012 07:19 zed File Added: geo84.pas
02-10-2012 07:23 zed Note Edited: 0009104
02-10-2012 07:33 vdemidov Note Added: 0009105
02-10-2012 12:20 zed Note Added: 0009111
02-10-2012 12:25 vdemidov Note Added: 0009112
02-10-2012 15:50 zed Note Added: 0009124
02-10-2012 15:50 zed Status confirmed => resolved
02-10-2012 15:50 zed Fixed in Version => 121010
02-10-2012 15:50 zed Resolution open => fixed
02-10-2012 15:50 zed Assigned To => zed
02-10-2012 15:50 zed Target Version 41xxxx => 121010
02-10-2012 15:59 zed Note Added: 0009125
02-10-2012 18:26 zed Note Added: 0009126
08-10-2012 14:09 vdemidov Relationship added child of 0001021
09-10-2012 06:49 vdemidov Relationship added child of 0001616
13-08-2013 12:46 vdemidov Relationship added related to 0002088
08-08-2025 13:24 zed Category Хотелка => Хотелка / Feature request