Computer Code

The spherical and plane trigonometric laws pertaining to a triangle:  cosine, haversine, sine (long and short forms), tangent, half-tangent, Napier (one and two), and area are provided as functions, which may be cut and pasted into an application.

The following code is intended for Maxima®

It works in either Maxima, xMaxima, or wxMaxima, as is.  However, be aware that they may be overwhelmed by the size of the code.  Be selective in what you copy.  For Derive®, employ a text editor to do a global replacement from “:=” to “=”, prior to pasting it into Derive.  For Maple®, employ a text editor to do a global replacement from “:=” to “->”, followed by a manual replacement of the first occurrence of “(” on each line by “:=”.  I believe that Maple 13 also accepts the same syntax as Maxima, as well.  If that insooth is the case; then no modification is required.  The C-language will accept these formulae in an *.h file.  Employ a text editor to do a global replacement from “:=” to “ ” and “<cr>” to “<cr#, followed by a manual insertion of “# “ in front of the first line and a deletion of a trailing “# ” after the end of the last line.

While Maxima is open-source and free, the other symbolic-mathematics programs are prohibitively expensive for personal use.  There are numerous C-language compilers --some free, others expensive.

Each formula returns the principal-value.  It is up to you to compute from it any other value that you may desire.  For each formula, its cyclical permutation -- as well as a renaming of the parts of a triangle -- will yield another valid formula.  Just call the formula with the parts of the triangle replaced as desired.

Since these are live functions, they may be used to construct any composite function that you want, subject to the limitation of the depth to which the particular symbolic mathematics program supports.

Naming convention:  The plane functions have an explicit designation to prevent a naming-confect with the corresponding spherical function.

To reduce the clutter and forestall a possible overwhelming of the program in which you intend to employ the formulae, you should copy only those formulae – and their dependants – that you require.


# COMMON functions

The sides of the triangle are {sa, sb, sc} and the opposite angles are {aA, aB, aC}.

For brevity, a few functions that are employed by both the spherical and the plane sections are presented here.

The definition of the haversine function and its inverse, the archaversine, are provided; to make them available – the symbolic mathematics programs do not have the haversine built-in.  The ahav returns the principal-value; the other value is its negative.  In passing, we mention that the inverse of each trigonometric function returns its principal-value.  The other value of the asin is its supplement.  The other value of the acos is its negative.  The other value of the atan is Pi/2 + its principal-value; likewise for the acot.  These remarks apply to each of the laws that returns a value from an inverse trigonometric function. 

# SPHERICAL laws

This is the spherical section.

The spherical law of cosine (as well as the plane law of cosine) is conspicuous by its absence; because of its inherent excessive computational round-off error.  Employ the law of haversine instead.  It is logically equivalent, has the same calling sequence, and returns the same variable; but with negligible round-off error.

We have combined the haversine and sine (short form) laws, to obtain a consistent additional angle for the solution of the SAS triangle.  Likewise, the dual solves the ASA triangle.

We have cleared the spherical law of tangents of fractions, to forestall a possible attempt at a division by zero.. Use it as a check. The function should calculate to zero.  It is self-dual.

Next, we provide the spherical law of half-tangent and its dual.  We provide the 36 Mollweide formulae for a right-triangle with A = Pi/2 (a = Pi/2, in the case of the dual).  Copy these only if your triangle is known to be a right-triangle.  Then come the spherical Napier's analogies and their duals.

The spherical area is equal to the spherical-excess E multiplied by the square of the radius of the sphere.

Finally, we solve the navigation problem:  Given the latitude and longitude of the origination point A and the destination point B, find the initial bearing angle and the directed-distance from A to B.  Observe that the domain of the longitude is the semi-open interval (-Pi, Pi]; hence, the range of the distance is the same interval.  The range of the bearing likewise is the semi-open interval (-Pi, Pi].  In navigation, the latitude is in the closed interval [-Pi/2, Pi/2], measured northward from the equator.  The longitude is in the semi-open interval (-Pi, Pi], measured eastward from the prime-meridian through Greenwich.  The radius is in the semi-closed interval [0, oo), measured outward from the origin at the center of the Earth.  Thus, we obtain a left-handed coordinate system.  In mathematics, it is usual to have the latitude in the semi-closed interval [0, pi), measured southward from the north-pole.  Thus, the coordinate system is right-handed.  The mathematical and the navigational latitude are the co-functions of each other.  Confusing?

# PLANE laws

This is the plane section.

All of the analogous laws are presented; but, with the redundancies removed.


Strategy

This strategy is specifically for the solution of spherical triangles (but, may be employed for plane triangles, as well), employing the foregoing functions.  We adapt the reasonable, but purely arbitrary, convention that we will view a sphere from the outside and that we will traverse the boundary of a region on the sphere so that the region is on our left-side.  There are two difficulties:

With the foregoing convention, we distinguish the triangles ABC and ACB.

The ultimate goal is the solution of the triangle. That is, to find the three sides {sa,sb,sc} and the opposite three angles {aA,aB,aC}. We partition this problem according as the emphasis is on the sides or the angles, with the latter being the dual of the former.

sides.

We take as a sub-goal that of obtaining the three sides and only the one angle.

angles.

The angles are the dual of the sides, above.

We take as a sub-goal that of obtaining the three angles and only the one side.

plane angles.

This is easier than the foregoing "angles", which is applicable to either the spherical or plane cases.

alternatives.

Instead of the law of haversines, one may employ the corresponding law of half-tangents. Except for the resulting large computational errors, the corresponding law of cosines could be employed, as well. 


Any comments or suggestions?  If you discover any errors; please, report them to the Webmaster, below.

 Copyright (c) 2003,9 by R.I. 'Scibor-Marchocki  last modified Sunday 05-th July 2009  Uploaded on Wednesday 08th July 2009.  mailto:Webmaster@rism.com  Last updated Friday 07-VIII-2009.

 



Pi():=4*atan(1.0);

hav(x):=(sin(x/2))^2;

ahav(x):=2*asin(sqrt(x));

ss(sa,sb,sc):=(sa+sb+sc)/2;

 

aS(aA,aB,aC):=(aA+aB+aC)/2;

sr(sa,sb,sc):=sqrt(sin(ss(sa,sb,sc)-sa)*sin(ss(sa,sb,sc)-sb)*sin(ss(sa,sb,sc)-sc)/sin(ss(sa,sb,sc)));

aR(aA,aB,aC):=sqrt(cos(aS(aA,aB,aC)-aA)*cos(aS(aA,aB,aC)-aB)*cos(aS(aA,aB,aC)-aC)/cos(Pi()-aS(aA,aB,aC)));

law_hav_sa(sb,sc,aA):= ahav(hav(sb-sc)+sin(sb)*sin(sc)*hav(aA));

law_hav_aA(sa,sb,sc):=ahav((hav(sa)-hav(sb-sc))/(sin(sb)*sin(sc)));

law_havdual_aA(aB,aC,sa):=Pi()-hav(hav(aB-aC)+sin(aB)*sin(aC)*hav(Pi()-sa));

law_havdual_sa(aA,aB,aC):=Pi()-ahav((hav(Pi()-aA)-hav(aB-aC))/(sin(aB)*sin(aC)));

law_isosceles_hav_sa(sb,aA):=2*asin(sin(sb)*sin(aA/2));

las_isosceles_hav_aA(sa,sb):=2*asin(sin(sa/2)/sin(sb));

law_isosceles_havdual_aA(aB,sa):=2*acos(sin(aB)*cos(sa/2));

law_isosceles_havdual_sa(aA,aB):=2*acos(cos(aA/2)/sin(aB));

law_sin_expr(sa,sb,sc):=sqrt((2* cos(sa)*cos(sb)* cos(sc)+((sin(sa))^2+(sin(sb))^2+(sin(sc))^2)^2-2))/(sin(sa)*sin(sb)*sin(sc));

law_sin_short_aA(sa,aB,sb):=asin(sin(sa)*sin(aB)/sin(sb));

law_sin_long_aA(sa,sb,sc):=asin(sin(sa)*law_sin_expr(sa,sb,sc));

law_sin_short_sa(aA,aB,sb):=asin(sin(aA)*sin(sb)/sin(aB));

law_sin_long_sa(aA,aB, aC):=arcsin(sin(aA)*law_sin_expr(Pi()-aA,Pi()-aB,Pi()-aC));

law_hav_and_sin_aB(sa,sb,sc,aA):=law_hav_aA(sb,sc,sa)*signum(law_sin_short_aA(sb,aA,sa));

law_hav_and_sin_sb(aA,aB,aC,sa):=law_havdual_sa(aB,aC,aA)*signum(law_sin_short_sa(aB,aA,sa));

law_hav_hav_and_sin_aB(sb,sc,aA):=law_hav_and_sin_aB(law_hav_sa(sb,sc,aA),sb,sc,aA);

law_hav_hav_and_sin_sb(aB,aC,sa):=law_hav_and_sin_sb(law_havdual_aA(aB,aC,sa),aB,aC,sa);

law_tan_zero(sb,sc,aB,aC):=sin((sb-sc)/2) *sin((aB+aC)/2)*cos((aB-aC)/2)*cos((aB-aC)/2)-sin((aB-aC)/2)*sin((aB-aC)/2)*cos((sb-sc)/2)*cos((aB+aC)/2);   

law_half_tan_aA(sa,sb,sc):=2*atan(sr(sa,sb,sc)/sin(ss(sa,sb,sc)-sa));

law_half_tan_sa(aA,aB,aC):=2* aR(aA,aB,aC)*cos(aS(aA,aB,aC)-aA);

mollweide_one_sb(sa,aB):=asin(sin(sa)*sin(aB));

mollweide_one_sa(sb,aB):=asin(sin(sb)/sin(aB));

mollweide_one_aB(sb,sa):=asin(sin(sb)/sin(sa));

mollweide_two_aB(sb,aC):=acos(cos(sb)*sin(aC));

mollweide_two_sb(aB,aC):=acos(cos(aB)/sin(aC));

mollweide_two_aC(aB,sb):=asin(cos(aB)/cos(sb));

mollweide_three_sa(sb,sc):=acos(cos(sb)*cos(sc));

mollweide_three_sb(sa,sc):=acos(cos(sa)/cos(sc));

mollweide_three_sc(sa,sb):=acos(cos(sa)/cos(sb));

mollweide_four_sb(sc,aC):=asin(tan(sc)*cot(aC));

mollweide_four_sc(sb,aC):=atan(sin(sb)*tan(aC));

mollweide_four_aC(sb,sc):=acot(sin(sb)*cot(sc));

mollweide_five_aB(sc,sa):=acos(tan(sc)*cot(sa));

mollweide_five_sc(sa,aB):=atan(cos(aB)*tan(sa));

mollweide_five_sa(aB,sc):=acot(cos(aB)*cot(sc));

mollweide_six_sa(aB,aC):=acos(cot(aB)*cot(aC));

mollweide_six_aB(sa,aC):=acot(cos(sa)*tan(aC));

mollweide_six_aC(sa,aB):=acot(cos(sa)*tan(aB));

mollweide_onedual_aB(aA,sb):=asin(sin(aA)*sin(sb));

mollweide_onedual_aA(aB,sb):=asin(sin(aB)/sin(sB));

mollweide_onedual_sb(aB,aA):=asin(sin(aB)/sin(aA));

mollweide_twodual_sb(aB,sc):=acos(cos(aB)*sin(sc));

mollweide_twodual_aB(sb,sc):=acos(cos(sb)/sin(sc));

mollweide_twodual_sc(sb,aB):=asin(cos(sb)/cos(aB));

mollweide_threedual_aA(aB,aC):=acos(-cos(aB)*cos(aC));

mollweide_threedual_aB(aA,aC):=acos(-cos(aA)/cos(aC));

mollweide_threedual_aC(aA,aB):=acos(-cos(aA)/cos(aB));

mollweide_fourdual_aB(aC,sc):=asin(tan(aC)*cot(sc));

mollweide_fourdual_aC(aB,sc):=atan(sin(aB)*tan(sc));

mollweide_fourdual_sc(aB,aC):=acot(sin(aB)*cot(aC));

mollweide_fivedual_sb(aC,aA):=acos(-tan(aC)*cot(aA));

mollweide_fivedual_aC(aA,sb):=atan(-cos(sb)*tan(aA));

mollweide_fivedual_aA(sb,aC):=acot(-cos(sb)*cot(aC));

mollweide_sixdual_aA(sb,sc):=acos(-cot(sb)*cot(sc));

mollweide_sixdual_sb(aA,sc):=acot(-cos(aA)*tan(sc));

mollweide_sixdual_sc(aA,sb):=acot(-cos(aA)*tan(sb));

napier_one_sc(aA,aB,sa,sb):=2*atan(sin((aA+aB)/2)*tan((sa-sb)/2)/sin((aA-aB)/2));

napier_one_aC(sa,sb,aA,aB):=2*acot(sin((sa+sb)/2)*tan((aA-aB)/2)/sin((sa-sb)/2));

napier_two_sc(aA,aB,sa,sb):=2*atan(cos((aA+aB)/2)*tan((sa+sb)/2)/cos((aA-aB)/2));

napier_two_aC(sa,sb,aA,aB):=2*acot(cos((sa+sb)/2)*tan((aA+aB)/2)/cos((sa-sb)/2));

spherical_excess_aE(aA,aB,aC):=2*aS(aA,aB,aC)-Pi();

spherical_lHuilier_first_sE(sa,sb,sc):=2*acos((1+cos(sa)+cos(sb)+cos(sc))/(4*cos(sa/2)*cos(sb/2)*cos(sc/2)));

spherical_lHuilier_second_sE(sa,sb,sc):= 4*atan(sqrt(tan(ss(sa,sb,sc)/2)*tan((ss(sa,sb,sc)-sa)/2)*tan((ss(sa,sb,sc)-sb)/2)*tan((ss(sa,sb,sc)-sc)/2)));

srr(sa,sb,sc):=atan(sqrt(((sin(ss(sa,sb,sc)- a)*sin(ss(sa,sb,sc)-ab)*sin(ss(sa,sb,sc)-sc))/sin(ss(sa,sb,sc)))));

aRR(aA,aB,aC):=atan(sqrt(-cos(aS(aA,aB,aC))/((cos(aS(aA,aB,aC)-aA)*cos(aS(aA,aB,aC)-aB)*cos(S(aA,aB,aC)-aC)))));

srr_equilateral(sa):=atan(sqrt((sin(sa/2))^2*abs(sin(sa/2)/sin(3*sa/2))));

aRR_equiangular(aA):=atan(sqrt(abs(-cos(3*aA/2)/cos(aA/2))/(cos(aA/2))^2));

spherical_lHuilier_equilateral_first_sE(sa):=2*acos((1+3*cos(sa))/(4*(cos(sa/2))^3));

spherical_lHuilier_equilaterai_second_sE(sa):= 4*atan(sqrt((tan(sa/4))^2*abs(tan(sa/4)*tan(3*sa/4))));

colat(lat):=Pi()/2-lat;

navigation_dist_s(lat_a,long_a,lat_b,long_b):=law_hav_sa(colat(lat_a),colat(lat_b),(long_b-long_a));

navigation_bearing_a(lat_a,long_a,lat_b,long_b):=law_hav_hav_and_sin_aB(colat(lat_a),colat(lat_b),(long_b-long_a));

 

 

plane_stereo_r(phi,rho):=2*rho*tan(phi/2);

plane_stereo_phi(r,rho):=2*atan(r/(2*rho));

plane_sr(sa,sb,sc):=(ss(sa,sb,sc)-sa)*(ss(sa,sb,sc)-sb)*(ss(sa,sb,sc)-sc)/ss(sa,sb,sc);

plane_aR(sa,sb,sc):=sqrt((sa^2*sb^2*sc^2)/(4*sb^2*sc^2+sc^2*sa^2+sa^2*sb^2)-(sa^2+sb^2+sc^2)^2);

law_hav_plane_sa(sb,sc,aA):=sqrt((sb-sc)^2+4*sb*sc*hav(aA));

law_hav_plane_aA(sa,sb,sc):=ahav((sa-(sb-sc))*(sa+(sb-sc))/(4*sb*sc));

law_isosceles_hav_plane_sa(sb,aA):=2*sb*sin(aA/2);

las_isosceles_hav_plane_aA(sa,sb):=2*asin(sa/(2*sb));

law_sin_short_plane_aA(sa,aB,sb):=asin(sa*sin(aB)/sb);

law_sin_long_plane_aA(sa,sb,sc):=asin(sa/(2*plane_aR(sa,sb,sc)));

law_sin_short_plane_sa(aA,aB,sb):=asin(sin(aA)*sb/sin(aB));

law_plane_tan_zero(sb,sc,aB,aC):=(sb-sc)*sin((aB+aC)/2)*cos((aB-aC)/2)-(sb+sc)*sin((aB-aC)/2)*cos((aB+aC)/2);

law_half_tan_plane_aA(sa,sb,sc):=2*atan(plane_sr(sa,sb,sc)/(ss(sa,sb,sc)-sa));

law_half_tan_plane_sa(aA,aB,aC):=2*sin(aS(aA,aB,aC)-aA)/plane_aR(aA,aB,aC);

mollweide_one_plane_sb(sa,aB):=sa*sin(aB);

mollweide_one_plane_sa(sb,aB):=sb/sin(aB);

mollweide_one_plane_aB(sb,sa):=asin(sb/sa);

mollweide_two_plane_aB(sb,aC):=Pi()/2-aC;

mollweide_four_plane_sb(sc,aC):=sc*cot(aC);

mollweide_four_plane_aC(sb,sc):=acot(sb/sc);

napier_one_plane_sc(aA,aB,sa,sb):=sin((aA+aB)/2)*(sa-sb)/sin((aA-aB)/2);

napier_one_plane_aC(sa,sb,aA,aB):=2*acot((sa+sb)*tan((aA-aB)/2)/(sa-sb));

napier_two_plane_sc(aA,aB,sa,sb):=cos((aA+aB)/2)*(sa+sb)/cos((aA-aB)/2);

napier_two_plane_aC(aA,aB):=Pi()-(aA+aB);

plane_Heron_area(sa,sb,sc):=sqrt(ss(sa,sb,sc)*(ss(sa,sb,sc)-sa)*(ss(sa,sb,sc)-sb)*(ss(sa,sb,sc)-sc));

plane_circum_area(sa,sb,sc):=(sa*sb*sc)/(4*aR(sa,sb,sc));

plane_in_area(sa,sb,sc):=sr(sa,sb,sc)*ss(sa,sb,sc);

plane_area(sa,sb,aC):=(sa*sb/2)*sin(aC);