Post Reply 
Geodesy API
10-23-2016, 06:12 PM (This post was last modified: 11-22-2016 06:37 PM by StephenG1CMZ.)
Post: #3
RE: Geodesy API
Version 0.3 implements LatLong-to-distance calculations.
Spherical calculations use Haversine.
Ellipsoidal calculations use Vincenty.
These routines have not been tested apart from a handful of values.

Code:

 // GEODESY © 2016 StephenG1CMZ
 
 LOCAL VER:="0.3";
 LOCAL NL:=CHAR(10); 
 LOCAL SP:=" ";

 G_Use(); //FORWARD
 G_RadiusB(); //FORWARD
 G_MeanRadius(); //FORWARD

 //We assume RA≥RB also Trig in Radians

 //Geoid Data
 EXPORT WGS84a_m:=6378137.0;
 EXPORT WGS84a_km:=WGS84a_m/1000;

 LOCAL WGS84fINV:=298.257223563;
 EXPORT WGS84f:=1/WGS84fINV; 
 LOCAL WGS84b_m:=G_RadiusB(WGS84a_m,WGS84f);
 LOCAL WGS84b_km:=WGS84b_m/1000;
 
 LOCAL RA:=WGS84a_m; //RADIUS EQ
 LOCAL FLATTEN:=WGS84f;
 LOCAL RB:=G_RadiusB(RA,FLATTEN);
 LOCAL RR:=G_MeanRadius(RA,RB);
 LOCAL EPSILON_V:=3ᴇ−11;
 LOCAL LoopSafe:=140;
 //FORMAT:
 //RA RB F
 EXPORT G_XGNAME:=1;
 EXPORT G_XRA:=2;
 EXPORT G_XF:=4;
 EXPORT G_EarthABF:={
  {"WGS84_m",        WGS84a_m,0,WGS84f},
  {"WGS84_km",       WGS84a_m/1000,0,WGS84f},
  {"Bessel_m",       6377397.155,0,1/299.1528128},
  {"International_m",6378388.000,0,1/297},
  {"Sphere",         1,0,0}};

 EXPORT G_ThisEll:=G_EarthABF(1);
 EXPORT G_XLAT:=1;
 EXPORT G_XLON:=2;
 LOCAL ESS_UNITS:="m";

 EXPORT G_About()
 BEGIN
  PRINT();
  PRINT("Geodesy API V 0.3 © 2016 StephenG1CMZ");
  PRINT("NOT FOR NAVIGATIONAL USE");
  PRINT("No liability accepted");
  PRINT("Expect the ordering of Vincenty results to change in later versions");
 END;

 // STD MATHS
 D2R(DEGS)
 BEGIN
  RETURN DEGS*(π/180); 
 END;
 R2D(RADS)
 BEGIN
  RETURN RADS*(180/π);
 END;

 ATAN2YX(YY,XX) 
 BEGIN //MY ATAN2
  IF XX==0 AND YY==0 THEN
   RETURN 0; //CONVENIENT;
  END;
  //-π..π THE SPECIAL CHAR BELOW IS i=sqrt(−1), NOT CHINESE 
  RETURN ARG(XX+*YY);//BUILT-IN HANDLES MOST. END;
 END;

 HAV(THETA)
 //TRIG: HAVERSINE=SIN^2(THETA/2)
 BEGIN
  LOCAL SN:=SIN(THETA/2);
  RETURN SN^2;
 END;
 // END STD MATHS

 
 EXPORT G_RadiusB(RadiusA,Flatten)
 //RETURN POLAR RADIUS
 BEGIN
  RETURN (1-Flatten)*RadiusA;//RADIUS POLAR 
 END;

 EXPORT G_Use(ST)
 //A RADIUS EQU
 //B RADIUS POL
 //AT PRESENT RB IS ALWAYS DERIVED FROM F
 //LATER ALLOW F TO BE DERIVED
 BEGIN
  LOCAL II,POS;
  FOR II FROM  1 TO  SIZE(G_EarthABF) DO
   POS:=INSTRING(G_EarthABF(G_XGNAME),ST);
   IF POS THEN
    G_ThisEll:=G_EarthABF;
   
    RA:=G_ThisEll(G_XRA);
    FLATTEN:=G_ThisEll(G_XF);
    RB:=G_RadiusB(RA,FLATTEN);
    RETURN POS;
   END;
  END;
  RETURN POS;//0=>UNCHANGED   
 END;

 EXPORT G_DxVincentyDo(RA,RB,FLATTEN,Lat1,Lon1,Lat2,Lon2)
 // GEODESY INVERSE PROBLEM ELLIPSOIDAL
 // USE: RA AND FLATTEN FROM ThisEll
 // RETURNS DIST IN SAME UNITS AS R
 // LAT AND LON IN DEGREES 
 BEGIN
  LOCAL NUM,DEN;
  LOCAL LAT1:=HMS→(Lat1);
  LOCAL LAT2:=HMS→(Lat2);
  LOCAL LON1:=HMS→(Lon1);
  LOCAL LON2:=HMS→(Lon2);

  LOCAL UU1,SINUU1,COSUU1; //U1 AUX LATITUDE
  LOCAL UU2,SINUU2,COSUU2; //U2 AUX LATITUDE
  LOCAL USQ;
  LOCAL AAA,BBB;
  LOCAL LONDIFFradians;    //L=DIFF IN LON
  LOCAL PHI1,PHI2;
  LOCAL LAMBDA,SINLAMBDA,COSLAMBDA,LAMBDAOLD; 
  LOCAL SIGMA,SIN_SIGMA,COS_SIGMA,DELTA_SIGMA,SINSQ_SIGMA; //AUX ARC LENGTH
  LOCAL SIN_ALPHA,COSSQ_ALPHA;
  LOCAL COS_2SIGMAM,COSSQ_2SIGMAM;
  LOCAL CC;
  LOCAL BRACKET_TERM;
  LOCAL LOOPCOUNT;
  LOCAL DISTS;
  LOCAL AZ_FWD,AZ_FNL,AZ_REV;
 
  // CALC L
  LONDIFFradians:=D2R(LON2-LON1); //DIFF IN LON 
  
  // CALC U1 U2
  PHI1:=D2R(LAT1); 
  PHI2:=D2R(LAT2);

  UU1:=ATAN((1-FLATTEN)*TAN(PHI1));//CORRECT
  UU2:=ATAN((1-FLATTEN)*TAN(PHI2));//CORRECT

  //TO CALC LAMBDA:
  // PRE-CALC U1 TRIG ONCEONLY
  SINUU1:=SIN(UU1); COSUU1:=COS(UU1);
  // PRE-CALC U2 TRIG EACH POSITION
  SINUU2:=SIN(UU2); COSUU2:=COS(UU2);
  
  //ITERATE CALCULATING LAMBDA
  LOOPCOUNT:=0;
  LAMBDA:=LONDIFFradians; //V13: DIFF IN LON ON AUX. FIRST APPROX 

  REPEAT
   LOOPCOUNT:=LOOPCOUNT+1;

   SINLAMBDA:=SIN(LAMBDA); COSLAMBDA:=COS(LAMBDA); //PRECALC
   //V14
   BRACKET_TERM:=(COSUU1*SINUU2 - SINUU1*COSUU2*COSLAMBDA);   
   SINSQ_SIGMA:=(COSUU2*SINLAMBDA)^2 + (BRACKET_TERM)^2;  
   IF SINSQ_SIGMA==0 THEN 
    //PRINT("SINσ 0: NO DISTANCE");
    //0 DISTANCE MIGHT SUGGEST UNDERFLOW
    RETURN {1,0,0,0,0,0};//VALID ZERO
   END;
   SIN_SIGMA:=√(SINSQ_SIGMA);

   //V15
   COS_SIGMA:=SINUU1*SINUU2 + COSUU1*COSUU2*COSLAMBDA;
  
   //V16
   SIGMA:=ATAN2YX(SIN_SIGMA,COS_SIGMA);
   
   //V17
    SIN_ALPHA:=(COSUU1*COSUU2*SINLAMBDA)/SIN_SIGMA;
    COSSQ_ALPHA:=1-(SIN_ALPHA*SIN_ALPHA);
    
   //V18
    IF COSSQ_ALPHA==0 THEN 
     //PRINT("AVOID COSSQα 0");
     COS_2SIGMAM:=0;
    ELSE
     COS_2SIGMAM:=COS_SIGMA - (2*SINUU1*SINUU2/COSSQ_ALPHA);
    END;
    CC:=(FLATTEN/16)*COSSQ_ALPHA*(4 + FLATTEN*(4 - 3*COSSQ_ALPHA));
    LAMBDAOLD:=LAMBDA;
    LAMBDA:=LONDIFFradians + (1-CC)*FLATTEN*SIN_ALPHA*(SIGMA + CC*SIN_SIGMA*(COS_2SIGMAM + CC*COS_SIGMA*(−1+2*(COS_2SIGMAM*COS_2SIGMAM))));

    //PRINT(LAMBDA);
  UNTIL (ABS(LAMBDA-LAMBDAOLD)≤EPSILON_V) OR (LOOPCOUNT≥LoopSafe);

  //V19
  USQ:=COSSQ_ALPHA*((RA^2 - RB^2)/(RB^2));
   //(V3)
  AAA:=1 + (USQ/16384)*(4096 + USQ*(−768 + USQ*(320 - 175*USQ)));
   //(V4)
  BBB:=    (USQ/ 1024)*( 256 + USQ*(−128 + USQ*( 74 -  47*USQ))); 
   //(V6)
  COSSQ_2SIGMAM:=(COS_2SIGMAM)^2;

  DELTA_SIGMA:=(1/6)*BBB*COS_2SIGMAM*(−3+4*(SIN_ALPHA)^2)*(−3+4*COSSQ_2SIGMAM) ;
  DELTA_SIGMA:=(1/4)*BBB*(COS_SIGMA*(−1+2*COSSQ_2SIGMAM)) - DELTA_SIGMA ;
  DELTA_SIGMA:=BBB*SIN_SIGMA*(COS_2SIGMAM + DELTA_SIGMA);
  //PRINT("Δσ "+DELTA_SIGMA);
  DISTS:=AAA*(SIGMA - DELTA_SIGMA);//ANGULAR ≤π Radians
  DISTS:=RB*DISTS; //same units as radius
  
  //V20:FORWARD AZIMUTH
     //CHECKED SINLAMBDA:ORIGINAL (LON2-LON1) OR LOOPED VALUE? USE LOOPED

  AZ_FWD:=ATAN2YX((COSUU2*SINLAMBDA),BRACKET_TERM);
 
  //V21:FINAL AND REVERSE AZIMUTH
  NUM:=(COSUU1*SINLAMBDA);
  DEN:=(−SINUU1*COSUU2 + COSUU1*SINUU2*COSLAMBDA);
  
  AZ_FNL:=ATAN2YX(NUM,DEN);
  AZ_REV:=ATAN2YX(−NUM,−DEN);
 
  //RETURN RESULTS
  DISTS:=ROUND(DISTS,3);//VINCENTY CLAIMS mm, 
 
  AZ_FWD:=R2D(AZ_FWD);
  AZ_FWD:=IFTE(AZ_FWD<0,((AZ_FWD+360) MOD 360),AZ_FWD);//NO NEGATIVE BEARINGS
  AZ_FNL:=R2D(AZ_FNL);
  AZ_FNL:=IFTE(AZ_FNL<0,((AZ_FNL+360) MOD 360),AZ_FNL);//NO NEGATIVE BEARINGS
  AZ_REV:=R2D(AZ_REV);
  AZ_REV:=IFTE(AZ_REV<0,((AZ_REV+360) MOD 360),AZ_REV);
  // VALIDITY: CONVERGENCE FAILURE
  // DISTS: SAME UNITS AS RADIUS
  // AZIMUTHS: POSITIVE 0..360
  // LOOPCOUNT: CONVERGENCE INFO (LOWER BETTER):DIAGNOSTIC ONLY 
  // EXPECT SEQUENCE ORDER TO CHANGE
  // ESPECIALLY AZIMUTHS
  RETURN {(LOOPCOUNT≠LoopSafe),DISTS,(AZ_FWD),(AZ_FNL),(AZ_REV),LOOPCOUNT};
 END;

 EXPORT G_DxVincenty(Elevation,Point1,Point2)
 // GEODESY INVERSE PROBLEM ELLIPSOIDAL
 // USE: RA AND FLATTEN FROM ThisEll TO DERIVE RB
 // RETURNS DIST IN SAME UNITS AS R
 // LAT AND LON IN DEGREES 
 BEGIN
  LOCAL LAT1:=Point1(G_XLAT);
  LOCAL LON1:=Point1(G_XLON);
  LOCAL LAT2:=Point2(G_XLAT);
  LOCAL LON2:=Point2(G_XLON);
 
  LOCAL RA:=G_ThisEll(G_XRA)+Elevation;
  LOCAL FLATTEN:=G_ThisEll(G_XF);
  LOCAL RB:=G_RadiusB(RA,FLATTEN);
 
  RETURN G_DxVincentyDo(RA,RB,FLATTEN,LAT1,LON1,LAT2,LON2);

 END;

 // Main Spherical Geodesy routines-
 EXPORT G_MeanRadius (RA,RB)
 //In Geodesy, this is usually used as a radius for spherical
 BEGIN
  RETURN (2*RA+RB)/3;
 END;

EXPORT G_DistHav(Point1,Point2)
 BEGIN
  LOCAL LAT1:=HMS→(Point1(G_XLAT));
  LOCAL LAT2:=HMS→(Point2(G_XLAT));
  LOCAL LON1:=HMS→(Point1(G_XLON));
  LOCAL LON2:=HMS→(Point2(G_XLON));
 
  LOCAL DLON:=D2R(LON2-LON1);
  LOCAL DLAT:=D2R(LAT2-LAT1);
  LOCAL AA:=HAV(DLAT) + (COS(D2R(LAT1))*COS(D2R(LAT2))*HAV(DLON));
  LOCAL CC:=2*ASIN(MIN(1,√(AA)));//MIN 1 AVOIDS ROUNDING OUTRANGE
  LOCAL DD:=RR*CC;
  RETURN ROUND(DD,−6);
 END;


 // End Main Spherical Routines
 
 G_EllStats(PR)
 //PR:PRINT ELSE TEXTOUT
 BEGIN
  LOCAL II;
  LOCAL ST:={};
 
  ST(1):=G_ThisEll(G_XGNAME);
  ST(2):="RA: "+RA;
  ST(3):="RB: "+RB;
  ST(4):="f:  "+(FLATTEN); 
  IF FLATTEN≠0 THEN
   ST(5):="1/f: "+(1/FLATTEN);
  END;
  IF PR THEN
   FOR II FROM 1 TO SIZE(ST) DO
    PRINT(ST(II));
   END;
  ELSE
   FOR II FROM 1 TO SIZE(ST) DO
    TEXTOUT_P(ST(II),0,20*II+100);
   END;
  END;
 END;

 G_EX(LAT1,LON1,LAT2,LON2)
 //An example displaying results
 BEGIN
  LOCAL TM,RESULT1,RESULT2,DISTS;
  PRINT("  ");
  PRINT("From LAT "+HMS→(LAT1)+" "+→HMS(LAT1));  
  PRINT("From LON "+HMS→(LON1)+" "+→HMS(LON1)); 
  PRINT("To   LAT "+HMS→(LAT2)+" "+→HMS(LAT2));  
  PRINT("To   LON "+HMS→(LON2)+" "+→HMS(LON2)); 
 
  TM:=TICKS;
  RESULT1:=G_DistHav({LAT1,LON1},{LAT2,LON2});
  RESULT2:=G_DxVincenty(0,{LAT1,LON1},{LAT2,LON2});
  TM:=TICKS-TM; 
  PRINT("Elapsed "+(TM/1000)+" s"); 
  PRINT("Spherical Haversine: "+(RESULT1)+" m");
  PRINT("Ellipsoidal Vincenty:   "+(RESULT2(2))+" m");
  PRINT(" AZ "+(→HMS(RESULT2(3))));
  PRINT(" AZ "+(→HMS(RESULT2(4)))); 
  PRINT(" AZ "+(→HMS(RESULT2(5))));
  PRINT("Validity: " +(RESULT2(1))+" "+(RESULT2(6)));
  //PRINT(RESULT2);
  //RESULT:=Z_GEODESY(LAT1,LON1,LAT2,LON2);
 
 
 END;

 EXPORT G_NewEll(LST)
 BEGIN
  G_EarthABF(0):=LST;
  RETURN 1;
 END;

 EXPORT ExampleUse()
 //A short example of key procedures
 BEGIN
  LOCAL OKC,DIST,RESULT,EXAMPLEPOINT,XLAT,XLON;
  OKC:=G_NewEll({"Add your own",{RA,0,FLATTEN}});//Insert into List. Use use to use it. 
  OKC:=G_Use("WGS84_m");//DEFAULT
  IF OKC THEN //Found in list
   DIST:=G_DistHav({1,2},{3,4});
   //RESULT:=G_DxVincentyDo(RA,RB,FLATTEN,LAT1,LON1,LAT2,LON2);
   RESULT:=G_DxVincenty(0,{1,2},{3,4});
   //Variables prefixed X index into data structures
   EXAMPLEPOINT:={"From Here",1,2};
   XLAT:=2; XLON:=3;
   XLAT:=1; XLON:=2; //REINSTATE CURRENT DEFAULT
   EXAMPLEPOINT:={1,2};
   //and similarly for geoid structure  
  END; 
 END;

 EXPORT G_DrawEll(Flatten)
 //The stats are omitted because flattening drawn may differ
 //makes illustrate changing f easier
 //for now use myRadiusA of 100 pixels
 BEGIN
   LOCAL XX,YY;
   LOCAL DX,DY;
   LOCAL MyRadiusA:=100;
   LOCAL HEIGHT:=100;
   LOCAL WIDTH:=100;
   //LOCAL SCALE:=RadiusA/WIDTH;
   LOCAL EWIDTH:=MyRadiusA;
   LOCAL EHEIGHT:=((1-Flatten)*MyRadiusA);//RADIUS POLAR 
  
   RECT();
   //G_EllStats(0);
   FOR YY FROM −HEIGHT TO HEIGHT DO
    //PRINT(YY);
    FOR XX FROM −WIDTH  TO WIDTH  DO
     DX:=XX/(EWIDTH);
     DY:=YY/(EHEIGHT);
     IF (DX^2+DY^2)≤1 THEN
      //PRINT({WIDTH+10+XX,HEIGHT+20+YY});
      PIXON_P(WIDTH+100+XX,HEIGHT+20+YY,#009900h);
     END;//IF
    END;//FOR
   END;//FOR
   //G_EllStats(0);
   WAIT;
 END;

 G_TEST()
 //A few tests. More needed.
 BEGIN

  G_EX(50°03′58.76″,−005°42′53.10″,58°38′38.48″,−003°04′12.34″);//50PERCENT
  PRINT("EXPECT 969 954_m");
 
  G_EX(−37°57′3.72030″,144°25′29.52440″,−37°39′10.15610″,143°55′35.38390″);
  PRINT("EXPECTED: 54972.271m");//VINCENTY AUSTRALIA
  PRINT("306d52 05 * 127d10 25 * 307d10 25.07 ");
  G_EX(31.8300167,35.0662833,31.83000000,35.0708167);
  PRINT("PYTHON: CIRCLE 428.4 VIN 429.1677 m");
  //G_EX(0,0,0.5,179.5);
  G_EX(0,0,0.5,179.7);//WILL NOT CONVERGE
  //G_EX(0,0,0,90);
  //G_EX(0,0,0.5,0.5);
  G_EX(2,2,2,2);  
  G_EX(50°03′58.76″,−005°42′53.10″,58°38′38.48″,−003°04′12.34″);//50PERCENT
  PRINT("EXPECT 969 954_m");
  //IF 0 THEN
  G_EX(50°3′59″,−5°42′53″,58°38′38″,−3°4′12″);
  PRINT("RECHECK: EXPECTED 968.9km 009DEG0711");
  IF 0 THEN
  G_EX(−37°57′3.72030″,144°25′29.52440″,−37°39′10.15610″,143°55′35.38390″);//EXPECTS 54972m
  PRINT("EXPECTED: 54972.271m ");
  PRINT("306d52:5.37 / 127d10:25.07");//VINCENTY AUSTRALIA
  //IF 1 THEN
  G_EX(31.8300167,35.0662833,31.83000000,35.0708167);
  PRINT("PYTHON: CIRCLE 428.4 VIN 429.1677 m");
  //IF 0 THEN
  G_EX(0,0,0.5,179.5);
  G_EX(0,0,0.5,179.7);//WILL NOT CONVERGE
  //IF 1 THEN
  G_EX(0,0,0,90);
  G_EX(0,0,0.5,0.5);
  G_EX(2,2,2,2);
  END;
 
  //PRINT("SEMI: "+π*ESS(1));//MAX
 END;
 
 EXPORT GEODESY()
 BEGIN
  G_About(); 
  G_EllStats(1);
  PRINT("Esc");WAIT;
  G_TEST();
 END;

Update: Version 0.3 Vincenty is deprecated as it has bugs detailed below.

Stephen Lewkowicz (G1CMZ)
https://my.numworks.com/python/steveg1cmz
Visit this user's website Find all posts by this user
Quote this message in a reply
Post Reply 


Messages In This Thread
Geodesy API - StephenG1CMZ - 10-17-2016, 09:49 PM
RE: Geodesy API - StephenG1CMZ - 10-17-2016, 10:11 PM
RE: Geodesy API - StephenG1CMZ - 10-23-2016 06:12 PM
RE: Geodesy API - StephenG1CMZ - 11-22-2016, 06:34 PM
RE: Geodesy API - StephenG1CMZ - 11-23-2016, 02:09 PM
RE: Geodesy API - StephenG1CMZ - 11-23-2016, 03:03 PM
RE: Geodesy API - StephenG1CMZ - 12-13-2016, 08:57 PM



User(s) browsing this thread: 1 Guest(s)