Implementation of geodesic routines in modern Fortran
geographiclib
subroutines are documented at:
sourceforge
These are Fortran implementation of the geodesic algorithms described in:
C. F. F. Karney, Algorithms for geodesics,
J. Geodesy 87, 43--55 (2013). [Apr 23, 2022 version]geodesy_module.f90
). [Nov 6, 2022 version]The principal advantages of these algorithms over previous ones (e.g., Vincenty, 1975) are:
|f| < 1/50
The shortest path between two points on the ellipsoid at (lat1
,
lon1
) and (lat2
, lon2
) is called the geodesic. Its length is
s12
and the geodesic from point 1 to point 2 has forward azimuths
azi1
and azi2
at the two end points.
Traditionally two geodesic problems are considered:
lat1
, lon1
, s12
, and azi1
,
determine lat2
, lon2
, and azi2
. This is solved by the
subroutine direct.lat1
, lon1
, lat2
, lon2
,
determine s12
, azi1
, and azi2
. This is solved by the
subroutine inverse.The ellipsoid is specified by its equatorial radius a (typically
in meters) and flattening f
. The routines are accurate to round
off with real(wp) arithmetic provided that |f| < 1/50
for the WGS84 ellipsoid, the errors are less than 15
nanometers. (Reasonably accurate results are obtained for |f| < 1/5
.)
For a prolate ellipsoid, specify f < 0.
The routines also calculate several other quantities of interest:
SS12
is the area between the geodesic from point 1 to point 2
and the equator; i.e., it is the area, measured counter-clockwise,
of the geodesic quadrilateral with corners (lat1,lon1)
, (0,lon1)
,
(0,lon2)
, and (lat2,lon2)
.m12
, the reduced length of the geodesic is defined such that if
the initial azimuth is perturbed by dazi1
(radians) then the
second point is displaced by m12
dazi1
in the direction
perpendicular to the geodesic. On a curved surface the reduced
length obeys a symmetry relation, m12 + m21 = 0
. On a flat
surface, we have m12 = s12
.MM12
and MM21
are geodesic scales. If two geodesics are
parallel at point 1 and separated by a small distance dt
, then
they are separated by a distance MM12
dt
at point 2. MM21
is defined similarly (with the geodesics being parallel to one
another at point 2). On a flat surface, we have MM12 = MM21 = 1
.a12
is the arc length on the auxiliary sphere. This is a
construct for converting the problem to one in spherical
trigonometry. a12
is measured in degrees. The spherical arc
length from one equator crossing to the next is always 180 deg.If points 1, 2, and 3 lie on a single geodesic, then the following addition rules hold:
s13 = s12 + s23
a13 = a12 + a23
SS13 = SS12 + SS23
m13 = m12 MM23 + m23 MM21
MM13 = MM12 MM23 - (1 - MM12 MM21) m23 / m12
MM31 = MM32 MM21 - (1 - MM23 MM32) m12 / m23
The shortest distance returned by the solution of the inverse problem is (obviously) uniquely defined. However, in a few special cases there are multiple azimuths which yield the same shortest distance. Here is a catalog of those cases:
lat1 = -lat2
(with neither point at a pole). If
azi1 = azi2
, the geodesic is unique. Otherwise there are two
geodesics and the second one is obtained by setting
[azi1, azi2] --> [azi2, azi1], [MM12, MM21] --> [MM21, MM12], SS12 --> -SS12.
(This occurs when the longitude difference is near +/- 180 deg for oblate
ellipsoids.)These routines are a simple transcription of the corresponding C++ classes in geographiclib. Because of the limitations of Fortran 77, the classes have been replaced by simple subroutines with no attempt to save "state" across subroutine calls. Most of the internal comments have been retained. However, in the process of transcription some documentation has been lost and the documentation for the C++ classes, geodesic_module::Geodesic, geodesic_module::GeodesicLine, and geodesic_module::PolygonAreaT, should be consulted. The C++ code remains the "reference implementation". Think twice about restructuring the internals of the Fortran code since this may make porting fixes from the C++ code more difficult.
geographiclib-fortran
Copyright (c) Charles Karney (2012-2022) charles@karney.com and
licensed under the MIT/X11 License. For more information, see
https://geographiclib.sourceforge.io/
Type | Visibility | Attributes | Name | Initial | |||
---|---|---|---|---|---|---|---|
integer, | public, | parameter | :: | geodesic_wp | = | wp |
Real working precision |
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | u | |||
real(kind=wp), | intent(in) | :: | v | |||
real(kind=wp), | intent(out) | :: | t |
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | x |
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | x |
Compute y - x. x and y must both lie in [-180, 180]. The result is equivalent to computing the difference exactly, reducing it to (-180, 180] and rounding the result. Note that this prescription allows -180 to be returned (e.g., if x is tiny and negative and y = 180). The error in the difference is returned in e
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | x | |||
real(kind=wp), | intent(in) | :: | y | |||
real(kind=wp), | intent(out) | :: | e |
The makes the smallest gap in x = 1/16 - nextafter(1/16, 0) = 1/2^57 for reals = 0.7 pm on the earth if x is an angle in degrees. (This is about 1000 times more resolution than we get with angles around 90 degrees.) We use this to avoid having to deal with near singular cases when x is non-zero but tiny (e.g., 1.0e-200).
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | x |
Great circle distance on a spherical body, using the Vincenty algorithm.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | r |
radius of the body [km] |
||
real(kind=wp), | intent(in) | :: | long1 |
longitude of first site [rad] |
||
real(kind=wp), | intent(in) | :: | lat1 |
latitude of the first site [rad] |
||
real(kind=wp), | intent(in) | :: | long2 |
longitude of the second site [rad] |
||
real(kind=wp), | intent(in) | :: | lat2 |
latitude of the second site [rad] |
great circle distance from 1 to 2 [km]
The distance from the center of a celestial body (e.g., the Earth) to a point on the spheroid surface at a specified geodetic latitude.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
equatorial radius (km) |
||
real(kind=wp), | intent(in) | :: | b |
polar radius of point (km) |
||
real(kind=wp), | intent(in) | :: | lat |
geodetic latitude of point (rad) |
distance from center of body to point (km)
Solve the direct geodesic problem.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
the equatorial radius (meters). |
||
real(kind=wp), | intent(in) | :: | f |
the flattening of the ellipsoid. Setting |
||
real(kind=wp), | intent(in) | :: | lat1 |
lat1 latitude of point 1 (degrees). |
||
real(kind=wp), | intent(in) | :: | lon1 |
lon1 longitude of point 1 (degrees). |
||
real(kind=wp), | intent(in) | :: | azi1 |
azi1 azimuth at point 1 (degrees). |
||
real(kind=wp), | intent(in) | :: | s12a12 |
if |
||
integer, | intent(in) | :: | flags |
a bitor'ed combination of the |
||
real(kind=wp), | intent(out) | :: | lat2 |
latitude of point 2 (degrees). |
||
real(kind=wp), | intent(out) | :: | lon2 |
longitude of point 2 (degrees). |
||
real(kind=wp), | intent(out) | :: | azi2 |
(forward) azimuth at point 2 (degrees). The value |
||
integer, | intent(in) | :: | outmask |
a bitor'ed combination of mask values specifying which of the following parameters should be set. |
||
real(kind=wp), | intent(out) | :: | a12s12 |
if |
||
real(kind=wp), | intent(out) | :: | m12 |
reduced length of geodesic (meters). |
||
real(kind=wp), | intent(out) | :: | MM12 |
geodesic scale of point 2 relative to point 1 (dimensionless). |
||
real(kind=wp), | intent(out) | :: | MM21 |
geodesic scale of point 1 relative to point 2 (dimensionless). |
||
real(kind=wp), | intent(out) | :: | SS12 |
area under the geodesic (). |
Solve the inverse geodesic problem.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
the equatorial radius (meters). |
||
real(kind=wp), | intent(in) | :: | f |
the flattening of the ellipsoid. Setting |
||
real(kind=wp), | intent(in) | :: | lat1 |
latitude of point 1 (degrees). |
||
real(kind=wp), | intent(in) | :: | lon1 |
longitude of point 1 (degrees). |
||
real(kind=wp), | intent(in) | :: | lat2 |
latitude of point 2 (degrees). |
||
real(kind=wp), | intent(in) | :: | lon2 |
longitude of point 2 (degrees). |
||
real(kind=wp), | intent(out) | :: | s12 |
distance from point 1 to point 2 (meters). |
||
real(kind=wp), | intent(out) | :: | azi1 |
azimuth at point 1 (degrees). |
||
real(kind=wp), | intent(out) | :: | azi2 |
(forward) azimuth at point 2 (degrees). |
||
integer, | intent(in) | :: | outmask |
a bitor'ed combination of mask values
specifying which of the following parameters should be set.
|
||
real(kind=wp), | intent(out) | :: | a12 |
arc length from point 1 to point 2 (degrees). |
||
real(kind=wp), | intent(out) | :: | m12 |
reduced length of geodesic (meters). |
||
real(kind=wp), | intent(out) | :: | MM12 |
MM12 geodesic scale of point 2 relative to point 1 (dimensionless). |
||
real(kind=wp), | intent(out) | :: | MM21 |
MM21 geodesic scale of point 1 relative to point 2 (dimensionless). |
||
real(kind=wp), | intent(out) | :: | SS12 |
SS12 area under the geodesic (). |
Determine the area of a geodesic polygon
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
the equatorial radius (meters). |
||
real(kind=wp), | intent(in) | :: | f |
the flattening of the ellipsoid. Setting |
||
real(kind=wp), | intent(in) | :: | lats(n) |
an array of the latitudes of the vertices (degrees). lats should be in the range [-90 deg, 90 deg]. |
||
real(kind=wp), | intent(in) | :: | lons(n) |
an array of the longitudes of the vertices (degrees). |
||
integer, | intent(in) | :: | n |
number of points |
||
real(kind=wp), | intent(out) | :: | AA |
the (signed) area of the polygon (). |
||
real(kind=wp), | intent(out) | :: | PP |
the perimeter of the polygon. |
Compute sin(x)
and cos(x)
with x
in degrees
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | x | |||
real(kind=wp), | intent(out) | :: | sinx | |||
real(kind=wp), | intent(out) | :: | cosx |
Heikkinen routine for cartesian to geodetic transformation
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in), | dimension(3) | :: | rvec |
position vector [km] |
|
real(kind=wp), | intent(in) | :: | a |
geoid semimajor axis [km] |
||
real(kind=wp), | intent(in) | :: | b |
geoid semiminor axis [km] |
||
real(kind=wp), | intent(out) | :: | h |
geodetic altitude [km] |
||
real(kind=wp), | intent(out) | :: | lon |
longitude [rad] |
||
real(kind=wp), | intent(out) | :: | lat |
geodetic latitude [rad] |
Olson routine for cartesian to geodetic transformation.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in), | dimension(3) | :: | rvec |
position vector [km] |
|
real(kind=wp), | intent(in) | :: | a |
geoid semimajor axis [km] |
||
real(kind=wp), | intent(in) | :: | b |
geoid semiminor axis [km] |
||
real(kind=wp), | intent(out) | :: | h |
geodetic altitude [km] |
||
real(kind=wp), | intent(out) | :: | long |
longitude [rad] |
||
real(kind=wp), | intent(out) | :: | lat |
geodetic latitude [rad] |
Solve the "direct" geodetic problem: given the latitude and longitude of one point and the azimuth and distance to a second point, determine the latitude and longitude of that second point. The solution is obtained using the algorithm by Vincenty.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
semimajor axis of ellipsoid [m] |
||
real(kind=wp), | intent(in) | :: | f |
flattening of ellipsoid [-] |
||
real(kind=wp), | intent(in) | :: | glat1 |
latitude of 1 [rad] |
||
real(kind=wp), | intent(in) | :: | glon1 |
longitude of 1 [rad] |
||
real(kind=wp), | intent(in) | :: | faz |
forward azimuth 1->2 [rad] |
||
real(kind=wp), | intent(in) | :: | s |
distance from 1->2 [m] |
||
real(kind=wp), | intent(out) | :: | glat2 |
latitude of 2 [rad] |
||
real(kind=wp), | intent(out) | :: | glon2 |
longitude of 2 [rad] |
||
real(kind=wp), | intent(out) | :: | baz |
back azimuth 2->1 [rad] |
Geodetic latitude, longitude, and height to Cartesian position vector.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
geoid semimajor axis [km] |
||
real(kind=wp), | intent(in) | :: | b |
geoid semiminor axis [km] |
||
real(kind=wp), | intent(in) | :: | glat |
geodetic latitude [rad] |
||
real(kind=wp), | intent(in) | :: | lon |
longitude [rad] |
||
real(kind=wp), | intent(in) | :: | h |
geodetic altitude [km] |
||
real(kind=wp), | intent(out), | dimension(3) | :: | r |
Cartesian position vector [x,y,z] |
INVERSE computes the geodetic azimuth and distance between two points, given their geographic positions.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
Equatorial semimajor axis |
||
real(kind=wp), | intent(in) | :: | rf |
reciprocal flattening (1/f) |
||
real(kind=wp), | intent(in) | :: | b1 |
latitude of point 1 (rad, positive north) |
||
real(kind=wp), | intent(in) | :: | l1 |
longitude of point 1 (rad, positive east) |
||
real(kind=wp), | intent(in) | :: | b2 |
latitude of point 2 (rad, positive north) |
||
real(kind=wp), | intent(in) | :: | l2 |
longitude of point 2 (rad, positive east) |
||
real(kind=wp), | intent(out) | :: | faz |
Forward azimuth (rad, clockwise from north) |
||
real(kind=wp), | intent(out) | :: | baz |
Back azimuth (rad, clockwise from north) |
||
real(kind=wp), | intent(out) | :: | s |
Ellipsoidal distance |
||
integer, | intent(out) | :: | it |
iteration count |
||
real(kind=wp), | intent(out) | :: | sig |
spherical distance on auxiliary sphere |
||
real(kind=wp), | intent(out) | :: | lam |
longitude difference on auxiliary sphere |
||
integer, | intent(out) | :: | kind |
solution flag: kind=1, long-line; kind=2, antipodal |
Function computes the Cartesian coordinates given the geodetic latitude (phi), longitude (lambda) and height (h) of a point related to an ellipsoid defined by its three semiaxes ax, ay and b
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | ax |
semiaxes (0 < b <= ay <= ax) |
||
real(kind=wp), | intent(in) | :: | ay |
semiaxes (0 < b <= ay <= ax) |
||
real(kind=wp), | intent(in) | :: | b |
semiaxes (0 < b <= ay <= ax) |
||
real(kind=wp), | intent(in) | :: | phi |
geodetic latitude (radians) |
||
real(kind=wp), | intent(in) | :: | lambda |
geodetic longitude (radians) |
||
real(kind=wp), | intent(in) | :: | h |
geodetic height |
||
real(kind=wp), | intent(out), | dimension(3) | :: | r |
Cartesian position vector [x,y,z] |
Geodetic to Cartesian for Triaxial Ellipsoid.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
ellipsoid radii |
||
real(kind=wp), | intent(in) | :: | b |
ellipsoid radii |
||
real(kind=wp), | intent(in) | :: | c |
ellipsoid radii |
||
real(kind=wp), | intent(in) | :: | lat |
latitude (rad) |
||
real(kind=wp), | intent(in) | :: | long |
longitude (rad) |
||
real(kind=wp), | intent(in) | :: | h |
altitude |
||
real(kind=wp), | intent(out), | dimension(3) | :: | r |
Cartesian coordinates (x,y,z) |
Function computes the geodetic latitude (phi), longitude (lambda) and height (h) of a point related to an ellipsoid defined by its three semiaxes ax, ay and b (0 < b <= ay <= ax) given Cartesian coordinates Xi, Yi, Zi and tolerance (tol). Latitude and longitude are returned in radians.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | ax |
semiaxes (0 < b <= ay <= ax) |
||
real(kind=wp), | intent(in) | :: | ay |
semiaxes (0 < b <= ay <= ax) |
||
real(kind=wp), | intent(in) | :: | b |
semiaxes (0 < b <= ay <= ax) |
||
real(kind=wp), | intent(in), | dimension(3) | :: | r |
Cartesian coordinates (x,y,z) |
|
real(kind=wp), | intent(in) | :: | tol |
tolerance (may be set to zero) |
||
real(kind=wp), | intent(out) | :: | phi |
geodetic latitude (radians) |
||
real(kind=wp), | intent(out) | :: | lambda |
geodetic longitude (radians) |
||
real(kind=wp), | intent(out) | :: | h |
geodetic height |
Cartesian to Geodetic I
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | ax |
semiaxes of the celestial body: ax>ay>az |
||
real(kind=wp), | intent(in) | :: | ay |
semiaxes of the celestial body: ax>ay>az |
||
real(kind=wp), | intent(in) | :: | az |
semiaxes of the celestial body: ax>ay>az |
||
real(kind=wp), | intent(in), | dimension(3) | :: | r |
cartesian coordinates of the considered point in the first octant: xG, yG, zG with (xG,yG,zG)<>(0,0,0) |
|
real(kind=wp), | intent(out) | :: | latitude |
geodetic coordinates of the considered point |
||
real(kind=wp), | intent(out) | :: | longitude |
geodetic coordinates of the considered point |
||
real(kind=wp), | intent(out) | :: | altitude |
geodetic coordinates of the considered point |
||
real(kind=wp), | intent(in) | :: | error |
Values smaller than error treated as 0.0 |
Cartesian into Geodetic II
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | ax |
semiaxes of the celestial body: ax>ay>az |
||
real(kind=wp), | intent(in) | :: | ay |
semiaxes of the celestial body: ax>ay>az |
||
real(kind=wp), | intent(in) | :: | az |
semiaxes of the celestial body: ax>ay>az |
||
real(kind=wp), | intent(in), | dimension(3) | :: | r |
cartesian coordinates of the considered point in the first octant: xG, yG, zG with (xG,yG,zG)<>(0,0,0) |
|
real(kind=wp), | intent(out) | :: | latitude |
geodetic coordinates of the considered point |
||
real(kind=wp), | intent(out) | :: | longitude |
geodetic coordinates of the considered point |
||
real(kind=wp), | intent(out) | :: | altitude |
geodetic coordinates of the considered point |
||
real(kind=wp), | intent(in) | :: | error |
Values smaller than error treated as 0.0 |
Cartesian to geodetic for Triaxial Ellipsoid.
Type | Intent | Optional | Attributes | Name | ||
---|---|---|---|---|---|---|
real(kind=wp), | intent(in) | :: | a |
ellipsoid radii |
||
real(kind=wp), | intent(in) | :: | b |
ellipsoid radii |
||
real(kind=wp), | intent(in) | :: | c |
ellipsoid radii |
||
real(kind=wp), | intent(in), | dimension(3) | :: | r |
Cartesian coordinates (x,y,z) |
|
real(kind=wp), | intent(in) | :: | eps |
convergence tolerance |
||
real(kind=wp), | intent(out) | :: | phi |
latitude (rad) |
||
real(kind=wp), | intent(out) | :: | lambda |
longitude (rad) |
||
real(kind=wp), | intent(out) | :: | h |
altitude |