summaryrefslogtreecommitdiff
path: root/lib/great_circle_arc.py
diff options
context:
space:
mode:
authorsienkiew <sienkiew@stsci.edu>2014-03-25 10:53:08 -0400
committersienkiew <sienkiew@stsci.edu>2014-03-25 10:53:08 -0400
commit5d00dcb1a0123389d9759698a5c42828e17af9bc (patch)
treeecef07881ea7b6b661ee5f59847b00dafecf3409 /lib/great_circle_arc.py
parent64be292044c4428b5908acf5cc2dda6693ed1afb (diff)
downloadstsci.sphere-5d00dcb1a0123389d9759698a5c42828e17af9bc.tar.gz
mod for d2to1 install
git-svn-id: http://svn.stsci.edu/svn/ssb/stsci_python/stsci.sphere/trunk@30670 fe389314-cf27-0410-b35b-8c050e845b92 Former-commit-id: 91667828b7e01b5aae55679093564473a976e8a9
Diffstat (limited to 'lib/great_circle_arc.py')
-rw-r--r--lib/great_circle_arc.py368
1 files changed, 0 insertions, 368 deletions
diff --git a/lib/great_circle_arc.py b/lib/great_circle_arc.py
deleted file mode 100644
index d5e78f2..0000000
--- a/lib/great_circle_arc.py
+++ /dev/null
@@ -1,368 +0,0 @@
-# -*- coding: utf-8 -*-
-
-# Copyright (C) 2011 Association of Universities for Research in
-# Astronomy (AURA)
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions
-# are met:
-#
-# 1. Redistributions of source code must retain the above
-# copyright notice, this list of conditions and the following
-# disclaimer.
-#
-# 2. Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following
-# disclaimer in the documentation and/or other materials
-# provided with the distribution.
-#
-# 3. The name of AURA and its representatives may not be used to
-# endorse or promote products derived from this software without
-# specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY AURA ``AS IS'' AND ANY EXPRESS OR
-# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
-# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
-# ARE DISCLAIMED. IN NO EVENT SHALL AURA BE LIABLE FOR ANY DIRECT,
-# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
-# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
-# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
-# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
-# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
-# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
-# OF THE POSSIBILITY OF SUCH DAMAGE.
-
-"""
-The `sphere.great_circle_arc` module contains functions for computing
-the length, intersection, angle and midpoint of great circle arcs.
-
-Great circles are circles on the unit sphere whose center is
-coincident with the center of the sphere. Great circle arcs are the
-section of those circles between two points on the unit sphere.
-"""
-
-from __future__ import with_statement, division, absolute_import, unicode_literals
-
-# THIRD-PARTY
-import numpy as np
-
-
-try:
- from . import math_util
- HAS_C_UFUNCS = True
-except ImportError:
- HAS_C_UFUNCS = False
-
-if HAS_C_UFUNCS:
- inner1d = math_util.inner1d
-else:
- from numpy.core.umath_tests import inner1d
-
-
-__all__ = ['angle', 'intersection', 'intersects', 'length', 'midpoint',
- 'interpolate']
-
-
-def _fast_cross(a, b):
- """
- This is a reimplementation of `numpy.cross` that only does 3D x
- 3D, and is therefore faster since it doesn't need any
- conditionals.
- """
- if HAS_C_UFUNCS:
- return math_util.cross(a, b)
-
- cp = np.empty(np.broadcast(a, b).shape)
- aT = a.T
- bT = b.T
- cpT = cp.T
-
- cpT[0] = aT[1]*bT[2] - aT[2]*bT[1]
- cpT[1] = aT[2]*bT[0] - aT[0]*bT[2]
- cpT[2] = aT[0]*bT[1] - aT[1]*bT[0]
-
- return cp
-
-def _cross_and_normalize(A, B):
- if HAS_C_UFUNCS:
- return math_util.cross_and_norm(A, B)
-
- T = _fast_cross(A, B)
- # Normalization
- l = np.sqrt(np.sum(T ** 2, axis=-1))
- l = np.expand_dims(l, 2)
- # Might get some divide-by-zeros, but we don't care
- with np.errstate(invalid='ignore'):
- TN = T / l
- return TN
-
-
-def intersection(A, B, C, D):
- r"""
- Returns the point of intersection between two great circle arcs.
- The arcs are defined between the points *AB* and *CD*. Either *A*
- and *B* or *C* and *D* may be arrays of points, but not both.
-
- Parameters
- ----------
- A, B : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- Endpoints of the first great circle arc.
-
- C, D : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- Endpoints of the second great circle arc.
-
- Returns
- -------
- T : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- If the given arcs intersect, the intersection is returned. If
- the arcs do not intersect, the triple is set to all NaNs.
-
- Notes
- -----
- The basic intersection is computed using linear algebra as follows
- [1]_:
-
- .. math::
-
- T = \lVert(A × B) × (C × D)\rVert
-
- To determine the correct sign (i.e. hemisphere) of the
- intersection, the following four values are computed:
-
- .. math::
-
- s_1 = ((A × B) × A) · T
-
- s_2 = (B × (A × B)) · T
-
- s_3 = ((C × D) × C) · T
-
- s_4 = (D × (C × D)) · T
-
- For :math:`s_n`, if all positive :math:`T` is returned as-is. If
- all negative, :math:`T` is multiplied by :math:`-1`. Otherwise
- the intersection does not exist and is undefined.
-
- References
- ----------
-
- .. [1] Method explained in an `e-mail
- <http://www.mathworks.com/matlabcentral/newsreader/view_thread/276271>`_
- by Roger Stafford.
-
- http://www.mathworks.com/matlabcentral/newsreader/view_thread/276271
- """
- if HAS_C_UFUNCS:
- return math_util.intersection(A, B, C, D)
-
- A = np.asanyarray(A)
- B = np.asanyarray(B)
- C = np.asanyarray(C)
- D = np.asanyarray(D)
-
- A, B = np.broadcast_arrays(A, B)
- C, D = np.broadcast_arrays(C, D)
-
- ABX = _fast_cross(A, B)
- CDX = _fast_cross(C, D)
- T = _cross_and_normalize(ABX, CDX)
- T_ndim = len(T.shape)
-
- if T_ndim > 1:
- s = np.zeros(T.shape[0])
- else:
- s = np.zeros(1)
- s += np.sign(inner1d(_fast_cross(ABX, A), T))
- s += np.sign(inner1d(_fast_cross(B, ABX), T))
- s += np.sign(inner1d(_fast_cross(CDX, C), T))
- s += np.sign(inner1d(_fast_cross(D, CDX), T))
- if T_ndim > 1:
- s = np.expand_dims(s, 2)
-
- cross = np.where(s == -4, -T, np.where(s == 4, T, np.nan))
-
- # If they share a common point, it's not an intersection. This
- # gets around some rounding-error/numerical problems with the
- # above.
- equals = (np.all(A == C, axis = -1) |
- np.all(A == D, axis = -1) |
- np.all(B == C, axis = -1) |
- np.all(B == D, axis = -1))
-
- equals = np.expand_dims(equals, 2)
-
- return np.where(equals, np.nan, cross)
-
-
-def length(A, B, degrees=True):
- r"""
- Returns the angular distance between two points (in vector space)
- on the unit sphere.
-
- Parameters
- ----------
- A, B : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- The endpoints of the great circle arc, in vector space.
-
- degrees : bool, optional
- If `True` (default) the result is returned in decimal degrees,
- otherwise radians.
-
- Returns
- -------
- length : scalar or array of scalars
- The angular length of the great circle arc.
-
- Notes
- -----
- The length is computed using the following:
-
- .. math::
-
- \Delta = \arccos(A \dot B)
- """
- A = np.asanyarray(A)
- B = np.asanyarray(B)
-
- dot = inner1d(A, B)
- dot = np.clip(dot, -1.0, 1.0)
- with np.errstate(invalid='ignore'):
- result = np.arccos(dot)
-
- if degrees:
- return np.rad2deg(result)
- else:
- return result
-
-
-def intersects(A, B, C, D):
- """
- Returns `True` if the great circle arcs between *AB* and *CD*
- intersect. Either *A* and *B* or *C* and *D* may be arrays of
- points, but not both.
-
- Parameters
- ----------
- A, B : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- Endpoints of the first great circle arc.
-
- C, D : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- Endpoints of the second great circle arc.
-
- Returns
- -------
- intersects : bool or array of bool
- If the given arcs intersect, the intersection is returned as
- `True`.
- """
- with np.errstate(invalid='ignore'):
- intersections = intersection(A, B, C, D)
- return np.isfinite(intersections[..., 0])
-
-
-def angle(A, B, C, degrees=True):
- """
- Returns the angle at *B* between *AB* and *BC*.
-
- This always returns the shortest angle < π.
-
- Parameters
- ----------
- A, B, C : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- Points on sphere.
-
- degrees : bool, optional
- If `True` (default) the result is returned in decimal degrees,
- otherwise radians.
-
- Returns
- -------
- angle : float or array of floats
- The angle at *B* between *AB* and *BC*.
-
- References
- ----------
-
- .. [1] Miller, Robert D. Computing the area of a spherical
- polygon. Graphics Gems IV. 1994. Academic Press.
- """
- A = np.asanyarray(A)
- B = np.asanyarray(B)
- C = np.asanyarray(C)
-
- A, B, C = np.broadcast_arrays(A, B, C)
-
- ABX = _fast_cross(A, B)
- ABX = _cross_and_normalize(B, ABX)
- BCX = _fast_cross(C, B)
- BCX = _cross_and_normalize(B, BCX)
- with np.errstate(invalid='ignore'):
- angle = np.arccos(inner1d(ABX, BCX))
-
- if degrees:
- angle = np.rad2deg(angle)
- return angle
-
-
-def midpoint(A, B):
- """
- Returns the midpoint on the great circle arc between *A* and *B*.
-
- Parameters
- ----------
- A, B : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- The endpoints of the great circle arc. It is assumed that
- these points are already normalized.
-
- Returns
- -------
- midpoint : (*x*, *y*, *z*) triple or Nx3 arrays of triples
- The midpoint between *A* and *B*, normalized on the unit
- sphere.
- """
- P = (A + B) / 2.0
- # Now normalize...
- l = np.sqrt(np.sum(P * P, axis=-1))
- l = np.expand_dims(l, 2)
- return P / l
-
-
-def interpolate(A, B, steps=50):
- r"""
- Interpolate along the great circle arc.
-
- Parameters
- ----------
- A, B : (*x*, *y*, *z*) triples or Nx3 arrays of triples
- The endpoints of the great circle arc. It is assumed thats
- these points are already normalized.
-
- steps : int
- The number of interpolation steps
-
- Returns
- -------
- array : (*x*, *y*, *z*) triples
- The points interpolated along the great circle arc
-
- Notes
- -----
-
- This uses Slerp interpolation where *Ω* is the angle subtended by
- the arc, and *t* is the parameter 0 <= *t* <= 1.
-
- .. math::
-
- \frac{\sin((1 - t)\Omega)}{\sin \Omega}A + \frac{\sin(t \Omega)}{\sin \Omega}B
- """
- steps = max(steps, 2)
- t = np.linspace(0.0, 1.0, steps, endpoint=True).reshape((steps, 1))
-
- omega = length(A, B, degrees=False)
- if omega == 0.0:
- offsets = t
- else:
- sin_omega = np.sin(omega)
- offsets = np.sin(t * omega) / sin_omega
-
- return offsets[::-1] * A + offsets * B