You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pyrocko/src/orthodrome.py

1400 lines
44 KiB
Python

# http://pyrocko.org - GPLv3
#
# The Pyrocko Developers, 21st Century
# ---|P------/S----------~Lg----------
from __future__ import division, absolute_import
14 years ago
import math
import numpy as num
from .moment_tensor import euler_to_matrix
from .config import config
from .plot.beachball import spoly_cut
14 years ago
from matplotlib.path import Path
14 years ago
d2r = math.pi/180.
r2d = 1./d2r
earth_oblateness = 1./298.257223563
earthradius_equator = 6378.14 * 1000.
earthradius = config().earthradius
12 years ago
d2m = earthradius_equator*math.pi/180.
m2d = 1./d2m
14 years ago
_testpath = Path([(0, 0), (0, 1), (1, 1), (1, 0), (0, 0)], closed=True)
raise_if_slow_path_contains_points = False
class Slow(Exception):
pass
if hasattr(_testpath, 'contains_points') and num.all(
_testpath.contains_points([(0.5, 0.5), (1.5, 0.5)]) == [True, False]):
def path_contains_points(verts, points):
p = Path(verts, closed=True)
return p.contains_points(points).astype(num.bool)
else:
# work around missing contains_points and bug in matplotlib ~ v1.2.0
def path_contains_points(verts, points):
if raise_if_slow_path_contains_points:
# used by unit test to skip slow gshhg_example.py
raise Slow()
p = Path(verts, closed=True)
result = num.zeros(points.shape[0], dtype=num.bool)
for i in range(result.size):
result[i] = p.contains_point(points[i, :])
return result
try:
cbrt = num.cbrt
except AttributeError:
def cbrt(x):
return x**(1./3.)
def float_array_broadcast(*args):
return num.broadcast_arrays(*[
num.asarray(x, dtype=float) for x in args])
class Loc(object):
'''
Simple location representation.
:attrib lat: Latitude in [deg].
:attrib lon: Longitude in [deg].
'''
def __init__(self, lat, lon):
self.lat = lat
self.lon = lon
14 years ago
def clip(x, mi, ma):
'''
Clip values of an array.
:param x: Continunous data to be clipped.
:param mi: Clip minimum.
:param ma: Clip maximum.
:type x: :py:class:`numpy.ndarray`
:type mi: float
:type ma: float
:return: Clipped data.
:rtype: :py:class:`numpy.ndarray`
'''
return num.minimum(num.maximum(mi, x), ma)
14 years ago
def wrap(x, mi, ma):
'''
Wrapping continuous data to fundamental phase values.
.. math::
4 years ago
x_{\\mathrm{wrapped}} = x_{\\mathrm{cont},i} -
\\frac{ x_{\\mathrm{cont},i} - r_{\\mathrm{min}} }
{ r_{\\mathrm{max}} - r_{\\mathrm{min}}}
\\cdot ( r_{\\mathrm{max}} - r_{\\mathrm{min}}),\\quad
x_{\\mathrm{wrapped}}\\; \\in
\\;[ r_{\\mathrm{min}},\\, r_{\\mathrm{max}}].
:param x: Continunous data to be wrapped.
:param mi: Minimum value of wrapped data.
:param ma: Maximum value of wrapped data.
:type x: :py:class:`numpy.ndarray`
:type mi: float
:type ma: float
:return: Wrapped data.
:rtype: :py:class:`numpy.ndarray`
'''
14 years ago
return x - num.floor((x-mi)/(ma-mi)) * (ma-mi)
def _latlon_pair(args):
if len(args) == 2:
a, b = args
return a.lat, a.lon, b.lat, b.lon
elif len(args) == 4:
return args
def cosdelta(*args):
'''
Cosine of the angular distance between two points ``a`` and ``b`` on a
sphere.
This function (find implementation below) returns the cosine of the
distance angle 'delta' between two points ``a`` and ``b``, coordinates of
which are expected to be given in geographical coordinates and in degrees.
For numerical stability a maximum of 1.0 is enforced.
.. math::
4 years ago
A_{\\mathrm{lat'}} = \\frac{ \\pi}{180} \\cdot A_{lat}, \\quad
A_{\\mathrm{lon'}} = \\frac{ \\pi}{180} \\cdot A_{lon}, \\quad
B_{\\mathrm{lat'}} = \\frac{ \\pi}{180} \\cdot B_{lat}, \\quad
B_{\\mathrm{lon'}} = \\frac{ \\pi}{180} \\cdot B_{lon}\\\\[0.5cm]
4 years ago
\\cos(\\Delta) = \\min( 1.0, \\quad \\sin( A_{\\mathrm{lat'}})
\\sin( B_{\\mathrm{lat'}} ) +
\\cos(A_{\\mathrm{lat'}}) \\cos( B_{\\mathrm{lat'}} )
\\cos( B_{\\mathrm{lon'}} - A_{\\mathrm{lon'}} )
:param a: Location point A.
:type a: :py:class:`pyrocko.orthodrome.Loc`
:param b: Location point B.
:type b: :py:class:`pyrocko.orthodrome.Loc`
:return: Cosdelta.
:rtype: float
'''
alat, alon, blat, blon = _latlon_pair(args)
return min(
1.0,
math.sin(alat*d2r) * math.sin(blat*d2r) +
math.cos(alat*d2r) * math.cos(blat*d2r) *
math.cos(d2r*(blon-alon)))
def cosdelta_numpy(a_lats, a_lons, b_lats, b_lons):
'''
Cosine of the angular distance between two points ``a`` and ``b`` on a
sphere.
This function returns the cosines of the distance
angles *delta* between two points ``a`` and ``b`` given as
:py:class:`numpy.ndarray`.
The coordinates are expected to be given in geographical coordinates
and in degrees. For numerical stability a maximum of ``1.0`` is enforced.
Please find the details of the implementation in the documentation of
the function :py:func:`pyrocko.orthodrome.cosdelta` above.
:param a_lats: Latitudes in [deg] point A.
:param a_lons: Longitudes in [deg] point A.
:param b_lats: Latitudes in [deg] point B.
:param b_lons: Longitudes in [deg] point B.
:type a_lats: :py:class:`numpy.ndarray`
:type a_lons: :py:class:`numpy.ndarray`
:type b_lats: :py:class:`numpy.ndarray`
:type b_lons: :py:class:`numpy.ndarray`
:return: Cosdelta.
:type b_lons: :py:class:`numpy.ndarray`, ``(N)``
'''
return num.minimum(
1.0,
num.sin(a_lats*d2r) * num.sin(b_lats*d2r) +
num.cos(a_lats*d2r) * num.cos(b_lats*d2r) *
num.cos(d2r*(b_lons-a_lons)))
14 years ago
def azimuth(*args):
'''
Azimuth calculation.
This function (find implementation below) returns azimuth ...
between points ``a`` and ``b``, coordinates of
which are expected to be given in geographical coordinates and in degrees.
.. math::
4 years ago
A_{\\mathrm{lat'}} = \\frac{ \\pi}{180} \\cdot A_{lat}, \\quad
A_{\\mathrm{lon'}} = \\frac{ \\pi}{180} \\cdot A_{lon}, \\quad
B_{\\mathrm{lat'}} = \\frac{ \\pi}{180} \\cdot B_{lat}, \\quad
B_{\\mathrm{lon'}} = \\frac{ \\pi}{180} \\cdot B_{lon}\\\\
4 years ago
\\varphi_{\\mathrm{azi},AB} = \\frac{180}{\\pi} \\arctan \\left[
\\frac{
\\cos( A_{\\mathrm{lat'}}) \\cos( B_{\\mathrm{lat'}} )
\\sin(B_{\\mathrm{lon'}} - A_{\\mathrm{lon'}} )}
{\\sin ( B_{\\mathrm{lat'}} ) - \\sin( A_{\\mathrm{lat'}}
cosdelta) } \\right]
:param a: Location point A.
:type a: :py:class:`pyrocko.orthodrome.Loc`
:param b: Location point B.
:type b: :py:class:`pyrocko.orthodrome.Loc`
:return: Azimuth in degree
'''
alat, alon, blat, blon = _latlon_pair(args)
return r2d*math.atan2(
math.cos(alat*d2r) * math.cos(blat*d2r) *
math.sin(d2r*(blon-alon)),
math.sin(d2r*blat) - math.sin(d2r*alat) * cosdelta(
alat, alon, blat, blon))
def azimuth_numpy(a_lats, a_lons, b_lats, b_lons, _cosdelta=None):
'''
Calculation of the azimuth (*track angle*) from a location A towards B.
This function returns azimuths (*track angles*) from locations A towards B
given in :py:class:`numpy.ndarray`. Coordinates are expected to be given in
geographical coordinates and in degrees.
Please find the details of the implementation in the documentation of the
function :py:func:`pyrocko.orthodrome.azimuth`.
:param a_lats: Latitudes in [deg] point A.
:param a_lons: Longitudes in [deg] point A.
:param b_lats: Latitudes in [deg] point B.
:param b_lons: Longitudes in [deg] point B.
:type a_lats: :py:class:`numpy.ndarray`, ``(N)``
:type a_lons: :py:class:`numpy.ndarray`, ``(N)``
:type b_lats: :py:class:`numpy.ndarray`, ``(N)``
:type b_lons: :py:class:`numpy.ndarray`, ``(N)``
:return: Azimuths in [deg].
:rtype: :py:class:`numpy.ndarray`, ``(N)``
'''
if _cosdelta is None:
_cosdelta = cosdelta_numpy(a_lats, a_lons, b_lats, b_lons)
return r2d*num.arctan2(
num.cos(a_lats*d2r) * num.cos(b_lats*d2r) *
num.sin(d2r*(b_lons-a_lons)),
num.sin(d2r*b_lats) - num.sin(d2r*a_lats) * _cosdelta)
def azibazi(*args, **kwargs):
'''
Azimuth and backazimuth from location A towards B and back.
:returns: Azimuth in [deg] from A to B, back azimuth in [deg] from B to A.
:rtype: tuple[float, float]
'''
alat, alon, blat, blon = _latlon_pair(args)
if alat == blat and alon == blon:
return 0., 180.
implementation = kwargs.get('implementation', 'c')
assert implementation in ('c', 'python')
if implementation == 'c':
from pyrocko import orthodrome_ext
return orthodrome_ext.azibazi(alat, alon, blat, blon)
cd = cosdelta(alat, alon, blat, blon)
azi = r2d*math.atan2(
math.cos(alat*d2r) * math.cos(blat*d2r) *
math.sin(d2r*(blon-alon)),
math.sin(d2r*blat) - math.sin(d2r*alat) * cd)
bazi = r2d*math.atan2(
math.cos(blat*d2r) * math.cos(alat*d2r) *
math.sin(d2r*(alon-blon)),
math.sin(d2r*alat) - math.sin(d2r*blat) * cd)
return azi, bazi
def azibazi_numpy(a_lats, a_lons, b_lats, b_lons, implementation='c'):
'''
Azimuth and backazimuth from location A towards B and back.
Arguments are given as :py:class:`numpy.ndarray`.
:param a_lats: Latitude(s) in [deg] of point A.
:type a_lats: :py:class:`numpy.ndarray`
:param a_lons: Longitude(s) in [deg] of point A.
:type a_lons: :py:class:`numpy.ndarray`
:param b_lats: Latitude(s) in [deg] of point B.
:type b_lats: :py:class:`numpy.ndarray`
:param b_lons: Longitude(s) in [deg] of point B.
:type b_lons: :py:class:`numpy.ndarray`
:returns: Azimuth(s) in [deg] from A to B,
back azimuth(s) in [deg] from B to A.
:rtype: :py:class:`numpy.ndarray`, :py:class:`numpy.ndarray`
'''
a_lats, a_lons, b_lats, b_lons = float_array_broadcast(
a_lats, a_lons, b_lats, b_lons)
assert implementation in ('c', 'python')
if implementation == 'c':
from pyrocko import orthodrome_ext
return orthodrome_ext.azibazi_numpy(a_lats, a_lons, b_lats, b_lons)
_cosdelta = cosdelta_numpy(a_lats, a_lons, b_lats, b_lons)
azis = azimuth_numpy(a_lats, a_lons, b_lats, b_lons, _cosdelta)
bazis = azimuth_numpy(b_lats, b_lons, a_lats, a_lons, _cosdelta)
eq = num.logical_and(a_lats == b_lats, a_lons == b_lons)
ii_eq = num.where(eq)[0]
azis[ii_eq] = 0.0
bazis[ii_eq] = 180.0
return azis, bazis
def azidist_numpy(*args):
'''
Calculation of the azimuth (*track angle*) and the distance from locations
A towards B on a sphere.
The assisting functions used are :py:func:`pyrocko.orthodrome.cosdelta` and
:py:func:`pyrocko.orthodrome.azimuth`
:param a_lats: Latitudes in [deg] point A.
:param a_lons: Longitudes in [deg] point A.
:param b_lats: Latitudes in [deg] point B.
:param b_lons: Longitudes in [deg] point B.
:type a_lats: :py:class:`numpy.ndarray`, ``(N)``
:type a_lons: :py:class:`numpy.ndarray`, ``(N)``
:type b_lats: :py:class:`numpy.ndarray`, ``(N)``
:type b_lons: :py:class:`numpy.ndarray`, ``(N)``
:return: Azimuths in [deg], distances in [deg].
:rtype: :py:class:`numpy.ndarray`, ``(2xN)``
'''
_cosdelta = cosdelta_numpy(*args)
_azimuths = azimuth_numpy(_cosdelta=_cosdelta, *args)
return _azimuths, r2d*num.arccos(_cosdelta)
14 years ago
def distance_accurate50m(*args, **kwargs):
'''
Accurate distance calculation based on a spheroid of rotation.
14 years ago
Function returns distance in meter between points A and B, coordinates of
which must be given in geographical coordinates and in degrees.
4 years ago
The returned distance should be accurate to 50 m using WGS84.
Values for the Earth's equator radius and the Earth's oblateness
(``f_oblate``) are defined in the pyrocko configuration file
:py:class:`pyrocko.config`.
From wikipedia (http://de.wikipedia.org/wiki/Orthodrome), based on:
``Meeus, J.: Astronomical Algorithms, S 85, Willmann-Bell,
Richmond 2000 (2nd ed., 2nd printing), ISBN 0-943396-61-1``
14 years ago
.. math::
14 years ago
4 years ago
F = \\frac{\\pi}{180}
\\frac{(A_{lat} + B_{lat})}{2}, \\quad
G = \\frac{\\pi}{180}
\\frac{(A_{lat} - B_{lat})}{2}, \\quad
l = \\frac{\\pi}{180}
\\frac{(A_{lon} - B_{lon})}{2} \\quad
\\\\[0.5cm]
4 years ago
S = \\sin^2(G) \\cdot \\cos^2(l) +
\\cos^2(F) \\cdot \\sin^2(l), \\quad \\quad
C = \\cos^2(G) \\cdot \\cos^2(l) +
\\sin^2(F) \\cdot \\sin^2(l)
.. math::
4 years ago
w = \\arctan \\left( \\sqrt{ \\frac{S}{C}} \\right) , \\quad
r = \\sqrt{\\frac{S}{C} }
The spherical-earth distance D between A and B, can be given with:
.. math::
4 years ago
D_{sphere} = 2w \\cdot R_{equator}
The oblateness of the Earth requires some correction with
correction factors h1 and h2:
.. math::
4 years ago
h_1 = \\frac{3r - 1}{2C}, \\quad
h_2 = \\frac{3r +1 }{2S}\\\\[0.5cm]
4 years ago
D = D_{\\mathrm{sphere}} \\cdot [ 1 + h_1 \\,f_{\\mathrm{oblate}}
\\cdot \\sin^2(F)
\\cos^2(G) - h_2\\, f_{\\mathrm{oblate}}
\\cdot \\cos^2(F) \\sin^2(G)]
:param a: Location point A.
:type a: :py:class:`pyrocko.orthodrome.Loc`
:param b: Location point B.
:type b: :py:class:`pyrocko.orthodrome.Loc`
:return: Distance in [m].
:rtype: float
'''
alat, alon, blat, blon = _latlon_pair(args)
implementation = kwargs.get('implementation', 'c')
assert implementation in ('c', 'python')
if implementation == 'c':
from pyrocko import orthodrome_ext
return orthodrome_ext.distance_accurate50m(alat, alon, blat, blon)
f = (alat + blat)*d2r / 2.
g = (alat - blat)*d2r / 2.
h = (alon - blon)*d2r / 2.
14 years ago
s = math.sin(g)**2 * math.cos(h)**2 + math.cos(f)**2 * math.sin(h)**2
c = math.cos(g)**2 * math.cos(h)**2 + math.sin(f)**2 * math.sin(h)**2
14 years ago
w = math.atan(math.sqrt(s/c))
if w == 0.0:
return 0.0
14 years ago
r = math.sqrt(s*c)/w
d = 2.*w*earthradius_equator
h1 = (3.*r-1.)/(2.*c)
h2 = (3.*r+1.)/(2.*s)
return d * (1. +
earth_oblateness * h1 * math.sin(f)**2 * math.cos(g)**2 -
earth_oblateness * h2 * math.cos(f)**2 * math.sin(g)**2)
def distance_accurate50m_numpy(
a_lats, a_lons, b_lats, b_lons, implementation='c'):
'''
Accurate distance calculation based on a spheroid of rotation.
Function returns distance in meter between points ``a`` and ``b``,
coordinates of which must be given in geographical coordinates and in
degrees.
4 years ago
The returned distance should be accurate to 50 m using WGS84.
Values for the Earth's equator radius and the Earth's oblateness
(``f_oblate``) are defined in the pyrocko configuration file
:py:class:`pyrocko.config`.
From wikipedia (http://de.wikipedia.org/wiki/Orthodrome), based on:
``Meeus, J.: Astronomical Algorithms, S 85, Willmann-Bell,
Richmond 2000 (2nd ed., 2nd printing), ISBN 0-943396-61-1``
.. math::
4 years ago
F_i = \\frac{\\pi}{180}
\\frac{(a_{lat,i} + a_{lat,i})}{2}, \\quad
G_i = \\frac{\\pi}{180}
\\frac{(a_{lat,i} - b_{lat,i})}{2}, \\quad
l_i= \\frac{\\pi}{180}
\\frac{(a_{lon,i} - b_{lon,i})}{2} \\\\[0.5cm]
4 years ago
S_i = \\sin^2(G_i) \\cdot \\cos^2(l_i) +
\\cos^2(F_i) \\cdot \\sin^2(l_i), \\quad \\quad
C_i = \\cos^2(G_i) \\cdot \\cos^2(l_i) +
\\sin^2(F_i) \\cdot \\sin^2(l_i)
.. math::
4 years ago
w_i = \\arctan \\left( \\sqrt{\\frac{S_i}{C_i}} \\right), \\quad
r_i = \\sqrt{\\frac{S_i}{C_i} }
The spherical-earth distance ``D`` between ``a`` and ``b``,
can be given with:
.. math::
4 years ago
D_{\\mathrm{sphere},i} = 2w_i \\cdot R_{\\mathrm{equator}}
The oblateness of the Earth requires some correction with
correction factors ``h1`` and ``h2``:
.. math::
4 years ago
h_{1.i} = \\frac{3r - 1}{2C_i}, \\quad
h_{2,i} = \\frac{3r +1 }{2S_i}\\\\[0.5cm]
4 years ago
D_{AB,i} = D_{\\mathrm{sphere},i} \\cdot [1 + h_{1,i}
\\,f_{\\mathrm{oblate}}
\\cdot \\sin^2(F_i)
\\cos^2(G_i) - h_{2,i}\\, f_{\\mathrm{oblate}}
\\cdot \\cos^2(F_i) \\sin^2(G_i)]
:param a_lats: Latitudes in [deg] point A.
:param a_lons: Longitudes in [deg] point A.
:param b_lats: Latitudes in [deg] point B.
:param b_lons: Longitudes in [deg] point B.
:type a_lats: :py:class:`numpy.ndarray`, ``(N)``
:type a_lons: :py:class:`numpy.ndarray`, ``(N)``
:type b_lats: :py:class:`numpy.ndarray`, ``(N)``
:type b_lons: :py:class:`numpy.ndarray`, ``(N)``
:return: Distances in [m].
:rtype: :py:class:`numpy.ndarray`, ``(N)``
'''
a_lats, a_lons, b_lats, b_lons = float_array_broadcast(
a_lats, a_lons, b_lats, b_lons)
assert implementation in ('c', 'python')
if implementation == 'c':
from pyrocko import orthodrome_ext
return orthodrome_ext.distance_accurate50m_numpy(
a_lats, a_lons, b_lats, b_lons)
eq = num.logical_and(a_lats == b_lats, a_lons == b_lons)
ii_neq = num.where(num.logical_not(eq))[0]
if num.all(eq):
return num.zeros_like(eq, dtype=float)
def extr(x):
if isinstance(x, num.ndarray) and x.size > 1:
return x[ii_neq]
else:
return x
a_lats = extr(a_lats)
a_lons = extr(a_lons)
b_lats = extr(b_lats)
b_lons = extr(b_lons)
f = (a_lats + b_lats)*d2r / 2.
g = (a_lats - b_lats)*d2r / 2.
h = (a_lons - b_lons)*d2r / 2.
s = num.sin(g)**2 * num.cos(h)**2 + num.cos(f)**2 * num.sin(h)**2
c = num.cos(g)**2 * num.cos(h)**2 + num.sin(f)**2 * num.sin(h)**2
w = num.arctan(num.sqrt(s/c))
r = num.sqrt(s*c)/w
d = 2.*w*earthradius_equator
h1 = (3.*r-1.)/(2.*c)
h2 = (3.*r+1.)/(2.*s)
dists = num.zeros(eq.size, dtype=float)
dists[ii_neq] = d * (
1. +
earth_oblateness * h1 * num.sin(f)**2 * num.cos(g)**2 -
earth_oblateness * h2 * num.cos(f)**2 * num.sin(g)**2)
return dists
14 years ago
def ne_to_latlon(lat0, lon0, north_m, east_m):
'''
Transform local cartesian coordinates to latitude and longitude.
From east and north coordinates (``x`` and ``y`` coordinate
:py:class:`numpy.ndarray`) relative to a reference differences in
longitude and latitude are calculated, which are effectively changes in
azimuth and distance, respectively:
.. math::
4 years ago
\\text{distance change:}\\; \\Delta {\\bf{a}} &= \\sqrt{{\\bf{y}}^2 +
{\\bf{x}}^2 }/ \\mathrm{R_E},
4 years ago
\\text{azimuth change:}\\; \\Delta \\bf{\\gamma} &= \\arctan( \\bf{x}
/ \\bf{y}).
The projection used preserves the azimuths of the input points.
:param lat0: Latitude origin of the cartesian coordinate system in [deg].
:param lon0: Longitude origin of the cartesian coordinate system in [deg].
:param north_m: Northing distances from origin in [m].
:param east_m: Easting distances from origin in [m].
:type north_m: :py:class:`numpy.ndarray`, ``(N)``
:type east_m: :py:class:`numpy.ndarray`, ``(N)``
:type lat0: float
:type lon0: float
:return: Array with latitudes and longitudes in [deg].
:rtype: :py:class:`numpy.ndarray`, ``(2xN)``
14 years ago
'''
a = num.sqrt(north_m**2+east_m**2)/earthradius
gamma = num.arctan2(east_m, north_m)
return azidist_to_latlon_rad(lat0, lon0, gamma, a)
def azidist_to_latlon(lat0, lon0, azimuth_deg, distance_deg):
'''
Absolute latitudes and longitudes are calculated from relative changes.
Convenience wrapper to :py:func:`azidist_to_latlon_rad` with azimuth and
distance given in degrees.
:param lat0: Latitude origin of the cartesian coordinate system in [deg].
:type lat0: float
:param lon0: Longitude origin of the cartesian coordinate system in [deg].
:type lon0: float
:param azimuth_deg: Azimuth from origin in [deg].
:type azimuth_deg: :py:class:`numpy.ndarray`, ``(N)``
:param distance_deg: Distances from origin in [deg].
:type distance_deg: :py:class:`numpy.ndarray`, ``(N)``
:return: Array with latitudes and longitudes in [deg].
:rtype: :py:class:`numpy.ndarray`, ``(2xN)``
'''
return azidist_to_latlon_rad(
lat0, lon0, azimuth_deg/180.*num.pi, distance_deg/180.*num.pi)
def azidist_to_latlon_rad(lat0, lon0, azimuth_rad, distance_rad):
'''
Absolute latitudes and longitudes are calculated from relative changes.
For numerical stability a range between of ``-1.0`` and ``1.0`` is
enforced for ``c`` and ``alpha``.
.. math::
4 years ago
\\Delta {\\bf a}_i \\; \\text{and} \\; \\Delta \\gamma_i \\;
\\text{are relative distances and azimuths from lat0 and lon0 for
\\textit{i} source points of a finite source.}
.. math::
4 years ago
\\mathrm{b} &= \\frac{\\pi}{2} -\\frac{\\pi}{180}\\;\\mathrm{lat_0}\\\\
{\\bf c}_i &=\\arccos[\\; \\cos(\\Delta {\\bf{a}}_i)
\\cos(\\mathrm{b}) + |\\Delta \\gamma_i| \\,
\\sin(\\Delta {\\bf a}_i)
\\sin(\\mathrm{b})\\; ] \\\\
\\mathrm{lat}_i &= \\frac{180}{\\pi}
\\left(\\frac{\\pi}{2} - {\\bf c}_i \\right)
.. math::
4 years ago
\\alpha_i &= \\arcsin \\left[ \\; \\frac{ \\sin(\\Delta {\\bf a}_i )
\\sin(|\\Delta \\gamma_i|)}{\\sin({\\bf c}_i)}\\;
\\right] \\\\
\\alpha_i &= \\begin{cases}
4 years ago
\\alpha_i, &\\text{if} \\; \\cos(\\Delta {\\bf a}_i) -
\\cos(\\mathrm{b}) \\cos({\\bf{c}}_i) > 0, \\;
\\text{else} \\\\
4 years ago
\\pi - \\alpha_i, & \\text{if} \\; \\alpha_i > 0,\\;
\\text{else}\\\\
4 years ago
-\\pi - \\alpha_i, & \\text{if} \\; \\alpha_i < 0.
\\end{cases} \\\\
4 years ago
\\mathrm{lon}_i &= \\mathrm<