Comparing Loxodromes and Great Circle Routes

Manipulate[
n=If[dateLineXX[pos1,pos2],1,0]; (* number of date-line crossings *)
Δλ=pos1[[2]]-pos2[[2]];
dir=If[n==1,Sign[Δλ],-Sign[Δλ]]; (* direction of travel: +1 is East, -1 is West *)
Column[{
Text@Style["great circle distance: "<>ToString[Floor[GeoDistance[pos1,pos2]/1000]]<>" km",RGBColor[.25,.43,.82],Bold],Text@Style["loxodrome distance: "<>ToString[Floor[loxDistance[pos1,pos2]]]<>" km",RGBColor[.6,.73,.36],Bold],Graphics3D[{globe,world,axis,equator,primeMeridian,
Dynamic@If[g,{EdgeForm[{RGBColor[.25,.43,.82],Thick}],Polygon[greatCircleSegment[pos1,pos2]]},{}],
Dynamic@If[l,{EdgeForm[{RGBColor[.6,.73,.36],Thick}],Polygon[loxodromeSegment[pos1,pos2,n,dir]]},{}],
Dynamic@{RGBColor[.49,0,0],PointSize[.02],Point[geoToXYZ@pos1],Point[geoToXYZ@pos2]}},
Lighting->"Neutral",ViewPoint->Dynamic[geoToXYZ[Mean[{pos1,pos2}]+{0,180dir n}]] (* center viewpoint on routes *),ViewAngle->60°,Boxed->False,ImageSize->{400,400}]}],
Style["travelling",Bold],
{{pos1,{50.83,4.33},"from"},Dynamic[DeleteCases[majorCityPos,pos2->_],SynchronousUpdating->False](* avoid from==to *),PopupMenu},
{{pos2,{37.76,-122.44},"to"},Dynamic[DeleteCases[majorCityPos,pos1->_],SynchronousUpdating->False],PopupMenu},
Delimiter,
Style["great circle route",RGBColor[.25,.43,.82],Bold],
{{g,True,""},{True,False}},
Style["loxodrome route",RGBColor[.6,.73,.36],Bold],
{{l,True,""},{True,False}},
{{n,0},ControlType->None},
{{dir,-1},ControlType->None},
{{Δλ,126.77},ControlType->None},
FrameMargins->0,ControlPlacement->Left,
SynchronousUpdating->False,
SynchronousInitialization->False,
TrackedSymbols->True,
Initialization:>(
ϵ=$MinMachineNumber;
(* avoid undetermined results *)
geoToXYZ[{ϕ_,λ_}]:={Cos[ϕ °] Cos[λ °],Cos[ϕ °] Sin[λ °],Sin[ϕ °]};(* converts the geographic coordinates(latitude,longitude) of a point on the unit sphere into geocentric cartesian coordinates {x,y,z} *)
dateLineXX[pos1:{ϕ1_,λ1_},pos2:{ϕ2_,λ2_}]:=Sign[λ1] Sign[λ2]==-1&&Abs[λ1]+Abs[λ2]>180;(* gives True if the dateline is crossed going from pos1 to pos2 using the shortes route. *)
Σ[ϕ_]:=Log[Sec[ϕ ]+Tan[ϕ ]];(* calculates the total accumulated longitudinal stretch at latiude ϕ in Mercator projection *)ρ[pos1:{ϕ1_,λ1_},pos2:{ϕ2_,λ2_}]:=Module[{P,Q,w},P=geoToXYZ[pos1 ];Q= geoToXYZ[pos2 ];w=Q-P P.Q;Cos[θ]P+Sin[θ] Normalize[w]];
(* generates the 3 parametric equations {x[θ],y[θ],z[θ]} for the points laying on the great circle passing through pos1 and pos2. θ is the angle between the two points and the center of the sphere *)greatCircle[pos1:{ϕ1_,λ1_},pos2:{ϕ2_,λ2_},n_:24]:=Table[ρ[pos1,pos2],{θ,0,2π,π/n}];greatCircleSegment[pos1:{ϕ1_,λ1_},pos2:{ϕ2_,λ2_},n_:24]:=Module[{θEnd=VectorAngle[geoToXYZ[pos1],geoToXYZ[pos2]]},Table[ρ[pos1,pos2],{θ,0,θEnd,θEnd/n}]];ξ[pos1:{ϕ1_,λ1_},pos2:{ϕ2_,λ2_},n_:0,dir_:+1]:=Module[{β,λ0},β=ArcTan[(λ2  °-λ1 °+2dir n π)/(Σ[ϕ2 °]-Σ[ϕ1 °])];λ0=λ1 °-Σ[ϕ1 °]Tan[β ];{Cos[λ] Sech[(λ-λ0) Cot[β]],Sech[(λ-λ0) Cot[β]] Sin[λ],Tanh[(λ-λ0) Cot[β]]}];(* generates the 3 parametric equations {x[λ],y[λ],z[λ]} laying at latitude λ on a loxodrome passing through the points with pos1 and pos2. n is the number of times the loxodrome crosses the date line. dir is the direction: -1 means going West (left) and +1 means going East (right) *)
loxodromeSegment[pos1:{ϕ1_,λ1_},pos2:{ϕ2_,λ2_},n_:0,dir_:+1]:=Table[ξ[pos1,pos2,n,dir],{λ,λ1 ° ,λ2 ° +2 dir n π,dir 2 °}];loxDistance[pos1:{ϕ1_,λ1_},pos2:{ϕ2_,λ2_}]:=Module[{ΔΣ=Σ[ϕ2 °]-Σ[ϕ1 °]+ϵ},6378. (Sqrt[((ΔΣ)^2)+(((If[Abs[λ2-λ1]>180,-2 π Sign[λ2-λ1],0]+λ2 °-λ1 °))^2)])/Abs[ΔΣ/(ϕ2 °-ϕ1 °+ϵ)]];
globe={GrayLevel[.85],Sphere[]};
equator={EdgeForm[RGBColor[.49,0,0]],FaceForm[],Polygon[greatCircle[{ϵ,ϵ},{ϵ,180°}]]};
primeMeridian={EdgeForm[RGBColor[.49,0,0]],FaceForm[],Polygon[greatCircle[{ϵ,ϵ},{90 °,ϵ}]]};
axis={RGBColor[.49,0,0],AbsoluteThickness[4],Line[{{0,0,-1.05},{0,0,-1}}],Line[{{0,0,1.05},{0,0,1}}]};
world={FaceForm[],EdgeForm[Thickness[.0035]],Polygon/@Map[geoToXYZ,CountryData[#,"SchematicCoordinates"]&/@Prepend[CountryData[],"Antarctica"],{3}]};
majorCityPos={{61.19`,-149.76`}->"Anchorage",{-36.85`,174.76`}->"Auckland",{13.73`,100.5`}->"Bangkok",{39.93`,116.4`}->"Beijing",{52.52`,13.38`}->"Berlin",{4.63`,-74.09`}->"Bogota",{18.96`,72.82`}->"Bombay",{50.83`,4.33`}->"Brussels",{-34.61`,-58.37`}->"BuenosAires",{30.06`,31.25`}->"Cairo",{-33.93`,18.46`}->"CapeTown",{33.6`,-7.62`}->"Casablanca",{41.84`,-87.68`}->"Chicago",{22.284`,114.15`}->"HongKong",{41.1`,29.`}->"Istanbul",{-6.18`,106.83`}->"Jakarta",{-4.31`,15.32`}->"Kinshasa",{-12.07`,-77.05`}->"Lima",{51.5`,-0.17`}->"London",{34.09`,-118.38`}->"LosAngeles",{40.42`,-3.71`}->"Madrid",{14.6`,120.98`}->"Manila",{19.43`,-99.14`}->"MexicoCity",{25.79`,-80.22`}->"Miami",{45.52`,-73.57`}->"Montreal",{55.75`,37.62`}->"Moscow",{-1.29`,36.82`}->"Nairobi",{40.7`,-73.92`}->"NewYork",{48.86`,2.34`}->"Paris",{41.89`,12.5`}->"Rome",{59.93`,30.32`}->"SaintPetersburg",{37.76`,-122.44`}->"San Francisco",{-33.46`,-70.64`}->"Santiago",{-23.53`,-46.63`}->"SaoPaulo",{31.23`,121.47`}->"Shanghai",{1.3`,103.85`}->"Singapore",{-33.87`,151.21`}->"Sydney",{35.67`,139.77`}->"Tokyo",{49.28`,-123.13`}->"Vancouver"};
)]

This Demonstration plots both the great circle route and the shortest loxodrome route between selected cities on the globe. A great circle on a sphere cuts the sphere into two congruent halves. A loxodrome, also known as a rhumb line, is a path on a sphere that cuts all meridians at the same angle (not 90°).

Although historically loxodromes were widely used for navigation, the loxodrome route is not the shortest one. The shortest route is the great circle route, as used in modern air traffic and navigation.

The loxodrome equations used were derived from the article by J. Alexander, "Loxodromes: A Rhumb Way to Go," College Mathematics Journal, 77(5), 2004 pp. 349–356.

From the snapshots you can see that the difference between the routes is greatest on East-West routes or routes close to the poles. The difference gets smaller on North-South routes or routes closer to the equator.

Contributed by: Erik Mahieu