Package: nvector
nvectorThe nvector library is a suite of tools written in Python to solve geographical position
calculations. Currently the following operations are implemented:Calculate the surface distance between two geographical positions.Convert positions given in one reference frame into another reference frame.Find the destination point given start point, azimuth/bearing and distance.Find the mean position (center/midpoint) of several geographical positions.Find the intersection between two paths.Find the cross track distance between a path and a position.Using n-vector, the calculations become simple and non-singular.
Full accuracy is achieved for any global position (and for any distance).DescriptionIn this library, we represent position with an “n-vector”,  which
is the normal vector to the Earth model (the same reference ellipsoid that is
used for latitude and longitude). When using n-vector, all Earth-positions are
treated equally, and there is no need to worry about singularities or
discontinuities. An additional benefit with using n-vector is that many
position calculations can be solved with simple vector algebra
(e.g. dot product and cross product).Converting between n-vector and latitude/longitude is unambiguous and easy
using the provided functions.n_E is n-vector in the program code, while in documents we use nE. E denotes
an Earth-fixed coordinate frame, and it indicates that the three components of
n-vector are along the three axes of E. More details about the notation and
reference frames can be found in thedocumentation.Documentation and codeOfficial documentation:http://www.navlab.net/nvector/http://nvector.readthedocs.io/en/latest/Kenneth Gade (2010):A Nonsingular Horizontal Position Representation,
The Journal of Navigation, Volume 63, Issue 03, pp 395-417, July 2010.Bleeding edge:https://github.com/pbrod/nvector.Official releases available at:http://pypi.python.org/pypi/nvector.Install nvectorIf you have pip installed and are online, then simply type:$ pip install nvectorto get the lastest stable version. Using pip also has the advantage that all
requirements are automatically installed.You can download nvector and all dependencies to a folder “pkg”, by the following:$ pip install –download=pkg nvectorTo install the downloaded nvector, just type:$ pip install –no-index –find-links=pkg nvectorVerifying installationTo verify that nvector can be seen by Python, typepythonfrom your shell.
Then at the Python prompt, try to import nvector:>>> import nvector as nv
>>> print(nv.__version__)
0.7.7dev0To test if the toolbox is working correctly paste the following in an interactive
python session:import nvector as nv
nv.test('--doctest-modules')or$ py.test –pyargs nvector –doctest-modulesat the command prompt.Getting StartedBelow the object-oriented solution to some common geodesic problems are given.
In the first example the functional solution is also given.
The functional solutions to the remaining problems can be found in
the functional examples section
of the tutorial.Example 1: “A and B to delta”Given two positions, A and B as latitudes, longitudes and depths relative to
Earth, E.Find the exact vector between the two positions, given in meters north, east,
and down, and find the direction (azimuth) to B, relative to north.
Assume WGS-84 ellipsoid. The given depths are from the ellipsoid surface.
Use position A to define north, east, and down directions.
(Due to the curvature of Earth and different directions to the North Pole,
the north, east, and down directions will change (relative to Earth) for
different places. Position A must be outside the poles for the north and east
directions to be defined.)Solution:>>> import numpy as np
>>> import nvector as nv
>>> wgs84 = nv.FrameE(name='WGS84')
>>> pointA = wgs84.GeoPoint(latitude=1, longitude=2, z=3, degrees=True)
>>> pointB = wgs84.GeoPoint(latitude=4, longitude=5, z=6, degrees=True)Step1:  Find p_AB_N (delta decomposed in N).>>> p_AB_N = pointA.delta_to(pointB)
>>> x, y, z = p_AB_N.pvector.ravel()
>>> 'Ex1: delta north, east, down = {0:8.2f}, {1:8.2f}, {2:8.2f}'.format(x, y, z)
'Ex1: delta north, east, down = 331730.23, 332997.87, 17404.27'Step2: Also find the direction (azimuth) to B, relative to north:>>> 'azimuth = {0:4.2f} deg'.format(p_AB_N.azimuth_deg)
'azimuth = 45.11 deg'
>>> 'elevation = {0:4.2f} deg'.format(p_AB_N.elevation_deg)
'elevation = 2.12 deg'
>>> 'distance = {0:4.2f} m'.format(p_AB_N.length)
'distance = 470356.72 m'Functional Solution:>>> import numpy as np
>>> import nvector as nv
>>> from nvector import rad, deg>>> lat_EA, lon_EA, z_EA = rad(1), rad(2), 3
>>> lat_EB, lon_EB, z_EB = rad(4), rad(5), 6Step1: Convert to n-vectors:>>> n_EA_E = nv.lat_lon2n_E(lat_EA, lon_EA)
>>> n_EB_E = nv.lat_lon2n_E(lat_EB, lon_EB)Step2: Find p_AB_E (delta decomposed in E).WGS-84 ellipsoid is default:>>> p_AB_E = nv.n_EA_E_and_n_EB_E2p_AB_E(n_EA_E, n_EB_E, z_EA, z_EB)Step3: Find R_EN for position A:>>> R_EN = nv.n_E2R_EN(n_EA_E)Step4: Find p_AB_N (delta decomposed in N).>>> p_AB_N = np.dot(R_EN.T, p_AB_E).ravel()
>>> x, y, z = p_AB_N
>>> 'Ex1: delta north, east, down = {0:8.2f}, {1:8.2f}, {2:8.2f}'.format(x, y, z)
'Ex1: delta north, east, down = 331730.23, 332997.87, 17404.27'Step5: Also find the direction (azimuth) to B, relative to north:>>> azimuth = np.arctan2(y, x)
>>> 'azimuth = {0:4.2f} deg'.format(deg(azimuth))
'azimuth = 45.11 deg'>>> distance = np.linalg.norm(p_AB_N)
>>> elevation = np.arcsin(z / distance)
>>> 'elevation = {0:4.2f} deg'.format(deg(elevation))
'elevation = 2.12 deg'>>> 'distance = {0:4.2f} m'.format(distance)
'distance = 470356.72 m'See alsoExample 1 at www.navlab.netExample 2: “B and delta to C”A radar or sonar attached to a vehicle B (Body coordinate frame) measures the
distance and direction to an object C. We assume that the distance and two
angles (typically bearing and elevation relative to B) are already combined to
the vector p_BC_B (i.e. the vector from B to C, decomposed in B). The position
of B is given as n_EB_E and z_EB, and the orientation (attitude) of B is given
as R_NB (this rotation matrix can be found from roll/pitch/yaw by using zyx2R).Find the exact position of object C as n-vector and depth ( n_EC_E and z_EC ),
assuming Earth ellipsoid with semi-major axis a and flattening f. For WGS-72,
use a = 6 378 135 m and f = 1/298.26.Solution:>>> import numpy as np
>>> import nvector as nv
>>> wgs72 = nv.FrameE(name='WGS72')
>>> wgs72 = nv.FrameE(a=6378135, f=1.0/298.26)Step 1: Position and orientation of B is given 400m above E:>>> n_EB_E = wgs72.Nvector(nv.unit([[1], [2], [3]]), z=-400)
>>> frame_B = nv.FrameB(n_EB_E, yaw=10, pitch=20, roll=30, degrees=True)Step 2: Delta BC decomposed in B>>> p_BC_B = frame_B.Pvector(np.r_[3000, 2000, 100].reshape((-1, 1)))Step 3: Decompose delta BC in E>>> p_BC_E = p_BC_B.to_ecef_vector()Step 4: Find point C by adding delta BC to EB>>> p_EB_E = n_EB_E.to_ecef_vector()
>>> p_EC_E = p_EB_E + p_BC_E
>>> pointC = p_EC_E.to_geo_point()>>> lat, lon, z = pointC.latlon_deg
>>> msg = 'Ex2: PosC: lat, lon = {:4.4f}, {:4.4f} deg,  height = {:4.2f} m'
>>> msg.format(lat, lon, -z)
'Ex2: PosC: lat, lon = 53.3264, 63.4681 deg,  height = 406.01 m'See alsoExample 2 at www.navlab.netExample 3: “ECEF-vector to geodetic latitude”Position B is given as an “ECEF-vector” p_EB_E (i.e. a vector from E, the
center of the Earth, to B, decomposed in E).
Find the geodetic latitude, longitude and height (latEB, lonEB and hEB),
assuming WGS-84 ellipsoid.Solution:>>> import numpy as np
>>> import nvector as nv
>>> wgs84 = nv.FrameE(name='WGS84')
>>> position_B = 6371e3 * np.vstack((0.9, -1, 1.1))  # m
>>> p_EB_E = wgs84.ECEFvector(position_B)
>>> pointB = p_EB_E.to_geo_point()>>> lat, lon, z = pointB.latlon_deg
>>> 'Ex3: Pos B: lat, lon = {:4.4f}, {:4.4f} deg, height = {:9.3f} m'.format(lat, lon, -z)
'Ex3: Pos B: lat, lon = 39.3787, -48.0128 deg, height = 4702059.834 m'See alsoExample 3 at www.navlab.netExample 4: “Geodetic latitude to ECEF-vector”Geodetic latitude, longitude and height are given for position B as latEB,
lonEB and hEB, find the ECEF-vector for this position, p_EB_E.Solution:>>> import nvector as nv
>>> wgs84 = nv.FrameE(name='WGS84')
>>> pointB = wgs84.GeoPoint(latitude=1, longitude=2, z=-3, degrees=True)
>>> p_EB_E = pointB.to_ecef_vector()>>> 'Ex4: p_EB_E = {} m'.format(p_EB_E.pvector.ravel().tolist())
'Ex4: p_EB_E = [6373290.277218279, 222560.20067473652, 110568.82718178593] m'See alsoExample 4 at www.navlab.netExample 5: “Surface distance”Find the surface distance sAB (i.e. great circle distance) between two
positions A and B. The heights of A and B are ignored, i.e. if they don’t have
zero height, we seek the distance between the points that are at the surface of
the Earth, directly above/below A and B. The Euclidean distance (chord length)
dAB should also be found. Use Earth radius 6371e3 m.
Compare the results with exact calculations for the WGS-84 ellipsoid.Solution for a sphere:>>> import numpy as np
>>> import nvector as nv
>>> frame_E = nv.FrameE(a=6371e3, f=0)
>>> positionA = frame_E.GeoPoint(latitude=88, longitude=0, degrees=True)
>>> positionB = frame_E.GeoPoint(latitude=89, longitude=-170, degrees=True)>>> s_AB, azia, azib = positionA.distance_and_azimuth(positionB)
>>> p_AB_E = positionB.to_ecef_vector() - positionA.to_ecef_vector()
>>> d_AB = p_AB_E.length>>> msg = 'Ex5: Great circle and Euclidean distance = {}'
>>> msg = msg.format('{:5.2f} km, {:5.2f} km')
>>> msg.format(s_AB / 1000, d_AB / 1000)
'Ex5: Great circle and Euclidean distance = 332.46 km, 332.42 km'Alternative sphere solution:>>> path = nv.GeoPath(positionA, positionB)
>>> s_AB2 = path.track_distance(method='greatcircle')
>>> d_AB2 = path.track_distance(method='euclidean')
>>> msg.format(s_AB2 / 1000, d_AB2 / 1000)
'Ex5: Great circle and Euclidean distance = 332.46 km, 332.42 km'Exact solution for the WGS84 ellipsoid:>>> wgs84 = nv.FrameE(name='WGS84')
>>> point1 = wgs84.GeoPoint(latitude=88, longitude=0, degrees=True)
>>> point2 = wgs84.GeoPoint(latitude=89, longitude=-170, degrees=True)
>>> s_12, azi1, azi2 = point1.distance_and_azimuth(point2)>>> p_12_E = point2.to_ecef_vector() - point1.to_ecef_vector()
>>> d_12 = p_12_E.length
>>> msg = 'Ellipsoidal and Euclidean distance = {:5.2f} km, {:5.2f} km'
>>> msg.format(s_12 / 1000, d_12 / 1000)
'Ellipsoidal and Euclidean distance = 333.95 km, 333.91 km'See alsoExample 5 at www.navlab.netExample 6 “Interpolated position”Given the position of B at time t0 and t1, n_EB_E(t0) and n_EB_E(t1).Find an interpolated position at time ti, n_EB_E(ti). All positions are given
as n-vectors.Solution:>>> import nvector as nv
>>> wgs84 = nv.FrameE(name='WGS84')
>>> n_EB_E_t0 = wgs84.GeoPoint(89, 0, degrees=True).to_nvector()
>>> n_EB_E_t1 = wgs84.GeoPoint(89, 180, degrees=True).to_nvector()
>>> path = nv.GeoPath(n_EB_E_t0, n_EB_E_t1)>>> t0 = 10.
>>> t1 = 20.
>>> ti = 16.  # time of interpolation
>>> ti_n = (ti - t0) / (t1 - t0) # normalized time of interpolation>>> g_EB_E_ti = path.interpolate(ti_n).to_geo_point()>>> lat_ti, lon_ti, z_ti = g_EB_E_ti.latlon_deg
>>> msg = 'Ex6, Interpolated position: lat, lon = {:2.1f} deg, {:2.1f} deg'
>>> msg.format(lat_ti, lon_ti)
'Ex6, Interpolated position: lat, lon = 89.8 deg, 180.0 deg'Vectorized solution:>>> t = np.array([10, 20])
>>> nvectors = wgs84.GeoPoint([89, 89], [0, 180], degrees=True).to_nvector()
>>> nvectors_i = nvectors.interpolate(ti, t, kind='linear')
>>> lati, loni, zi = nvectors_i.to_geo_point().latlon_deg
>>> msg.format(lat_ti, lon_ti)
'Ex6, Interpolated position: lat, lon = 89.8 deg, 180.0 deg'See alsoExample 6 at www.navlab.netExample 7: “Mean position”Three positions A, B, and C are given as n-vectors n_EA_E, n_EB_E, and n_EC_E.
Find the mean position, M, given as n_EM_E.
Note that the calculation is independent of the depths of the positions.Solution:>>> import nvector as nv
>>> points = nv.GeoPoint(latitude=[90, 60, 50],
...                      longitude=[0, 10, -20], degrees=True)
>>> nvectors = points.to_nvector()
>>> n_EM_E = nvectors.mean()
>>> g_EM_E = n_EM_E.to_geo_point()
>>> lat, lon = g_EM_E.latitude_deg, g_EM_E.longitude_deg
>>> msg = 'Ex7: Pos M: lat, lon = {:4.4f}, {:4.4f} deg'
>>> msg.format(lat, lon)
'Ex7: Pos M: lat, lon = 67.2362, -6.9175 deg'See alsoExample 7 at www.navlab.netExample 8: “A and azimuth/distance to B”We have an initial position A, direction of travel given as an azimuth
(bearing) relative to north (clockwise), and finally the
distance to travel along a great circle given as sAB.
Use Earth radius 6371e3 m to find the destination point B.In geodesy this is known as “The first geodetic problem” or
“The direct geodetic problem” for a sphere, and we see that this is similar toExample 2, but now the delta is
given as an azimuth and a great circle distance. (“The second/inverse geodetic
problem” for a sphere is already solved in Examples1and5.)Exact solution:>>> import numpy as np
>>> import nvector as nv
>>> frame = nv.FrameE(a=6371e3, f=0)
>>> pointA = frame.GeoPoint(latitude=80, longitude=-90, degrees=True)
>>> pointB, azimuthb = pointA.displace(distance=1000, azimuth=200, degrees=True)
>>> lat, lon = pointB.latitude_deg, pointB.longitude_deg>>> msg = 'Ex8, Destination: lat, lon = {:4.4f} deg, {:4.4f} deg'
>>> msg.format(lat, lon)
'Ex8, Destination: lat, lon = 79.9915 deg, -90.0177 deg'>>> np.allclose(azimuthb, -160.01742926820506)
TrueGreatcircle solution:>>> pointB2, azimuthb = pointA.displace(distance=1000,
...                                     azimuth=200,
...                                     degrees=True,
...                                     method='greatcircle')
>>> lat2, lon2 = pointB2.latitude_deg, pointB.longitude_deg
>>> msg.format(lat2, lon2)
'Ex8, Destination: lat, lon = 79.9915 deg, -90.0177 deg'>>> np.allclose(azimuthb, -160.0174292682187)
TrueSee alsoExample 8 at www.navlab.netExample 9: “Intersection of two paths”Define a path from two given positions (at the surface of a spherical Earth),
as the great circle that goes through the two points.Path A is given by A1 and A2, while path B is given by B1 and B2.Find the position C where the two great circles intersect.Solution:>>> import nvector as nv
>>> pointA1 = nv.GeoPoint(10, 20, degrees=True)
>>> pointA2 = nv.GeoPoint(30, 40, degrees=True)
>>> pointB1 = nv.GeoPoint(50, 60, degrees=True)
>>> pointB2 = nv.GeoPoint(70, 80, degrees=True)
>>> pathA = nv.GeoPath(pointA1, pointA2)
>>> pathB = nv.GeoPath(pointB1, pointB2)>>> pointC = pathA.intersect(pathB)
>>> pointC = pointC.to_geo_point()
>>> lat, lon = pointC.latitude_deg, pointC.longitude_deg
>>> msg = 'Ex9, Intersection: lat, lon = {:4.4f}, {:4.4f} deg'
>>> msg.format(lat, lon)
'Ex9, Intersection: lat, lon = 40.3186, 55.9019 deg'Check that PointC is not between A1 and A2 or B1 and B2:>>> pathA.on_path(pointC)
False
>>> pathB.on_path(pointC)
FalseCheck that PointC is on the great circle going through path A and path B:>>> pathA.on_great_circle(pointC)
True
>>> pathB.on_great_circle(pointC)
TrueSee alsoExample 9 at www.navlab.netExample 10: “Cross track distance”Path A is given by the two positions A1 and A2 (similar to the previous
example).Find the cross track distance sxt between the path A (i.e. the great circle
through A1 and A2) and the position B (i.e. the shortest distance at the
surface, between the great circle and B).Also find the Euclidean distance dxt between B and the plane defined by the
great circle. Use Earth radius 6371e3.Finally, find the intersection point on the great circle and determine if it is
between position A1 and A2.Solution:>>> import numpy as np
>>> import nvector as nv
>>> frame = nv.FrameE(a=6371e3, f=0)
>>> pointA1 = frame.GeoPoint(0, 0, degrees=True)
>>> pointA2 = frame.GeoPoint(10, 0, degrees=True)
>>> pointB = frame.GeoPoint(1, 0.1, degrees=True)
>>> pathA = nv.GeoPath(pointA1, pointA2)>>> s_xt = pathA.cross_track_distance(pointB, method='greatcircle')
>>> d_xt = pathA.cross_track_distance(pointB, method='euclidean')>>> val_txt = '{:4.2f} km, {:4.2f} km'.format(s_xt/1000, d_xt/1000)
>>> 'Ex10: Cross track distance: s_xt, d_xt = {}'.format(val_txt)
'Ex10: Cross track distance: s_xt, d_xt = 11.12 km, 11.12 km'>>> pointC = pathA.closest_point_on_great_circle(pointB)
>>> np.allclose(pathA.on_path(pointC), True)
TrueSee alsoExample 10 at www.navlab.netAcknowledgementsThenvector packageforPythonwas written by Per A. Brodtkorb atFFI (The Norwegian Defence Research Establishment)based on thenvector toolboxforMatlabwritten by the navigation group atFFI. The nvector.core and nvector.rotation module is a
vectorized reimplementation of the matlab nvector toolbox while the nvector.objects
module is a new easy to use object oriented user interface to the nvector core
functionality documented in[GB20].Most of the content is based on the article by K. Gade[Gad10].Thus this article should be cited in publications using this page or
downloaded program code.However, if you use any of the FrameE.direct, FrameE.inverse,
GeoPoint.distance_and_azimuth or GeoPoint.displace methods you should also cite
the article by Karney[Kar13]because these methods call
Karney’sgeographicliblibrary
to do the calculations.References[Gad10]K. Gade,A Nonsingular Horizontal Position Representation, J. Navigation, 63(3):395-417, 2010.[Kar13]C.F.F. Karney.Algorithms for geodesics. J. Geodesy, 87(1):43-55, 2013.[GB20]K. Gade and P.A. Brodtkorb,Nvector Documentation for Python, 2020.ChangelogVersion 0.7.7, June 3, 2021Per A Brodtkorb (27):Added cartopy and matplotlib to requirements.txtUpdated appveyor.yml, setup.cfg and setup.pyUpdated .gitignore to ignore .pytest_cacheCorrected failing doctests in objects.pyUpdated version in _installation.pyUpdated failing docstrings for python 2.7 in objects.py.Added ‘# doctest: SKIP’ to all plt.show() in order to avoid the doctests hangs on the testserver.Fixed a bug  in _info_functional.pyUpdated pycodestyle exlude section in setup.cfg Prettified _examples.py, _examples_object_oriented.py and core.pyUpdated pycodestyle ignore section in setup.cfgAdded doctest option to setup.cfgRemoved print statements in test_objects.pyReturn “NotImplemented” instead of raising “NotImplementedError” in Nvector._mul__ and Nvector.__div__ in objects.pyFixed .travis.yml so that he file paths in coverage.xml is discoverableunder the sonar.sources folder. The problem is that SonarQube is
analysing the checked-out source code (in src/nvector) but the actual
unit tests and coverage.py is run against the installed code (in
build/lib/nvector). Thus the absolute files paths to the installed code
in the generated coverage.xml were causing Sonar to show no coverage.
The workaround was to use sed in the pipeline to replace every path to
build/lib/nvector with src/nvector in coverage.xml.Fixed a bug: Identical expressions should not be used on both sides of a binary operator in test:objects.py.Updated solutions to example 9Added greatcircle method to GeoPoint.distance_and_azimuth in objects.pyAdded _base_angle function that makes sure an angle is between -pi and pi.Added test_direct_and_inverse in test_objects.pyAdded interp_nvectors to docs/reference/nvector_summary.rstAdded vectorized interpolation routines: interp_nvectors function to core.py and Nvector.interpolate to objects.py.Put try except around code in use_docstring to avoid attribute ‘__doc__’of ‘type’ objects is not writable errors for  python2.Added interp_nvectorsReorganized _displace_great_circleAdded check that depths also are equal on in _on_ellipsoid_path and in _on_great_circle_pathRefactored code from use_docstring_from function into the use_docstringfunction in _common.pySimplified the adding of examples to the docstrings of functions and classes in core.py and objects.py.Version 0.7.6, December 18, 2020Per A Brodtkorb (30):Renamed _core.py to core.pyRemoved the module index from the appendix because it was incomplete.Removed nvector.tests package from the reference chapter.Added indent function to _common.py to avoid failure on python 2.7.Moved isclose, allclose and array_to_list_dict from objects.py to util.pyMoved the following function from test_nvector.py to test_rotation.py:test_n_E_and_wa2R_EL, test_R2zxy, test_R2zxy_x90, test_R2zxy_y90test_R2zxy_z90, test_R2zxy_0, test_R2xyz test_R2xyz_with_vectorsReplaced assert_array_almost_equal with assert_allclose in test_objects.pyRenamed test_frames.py to test_objects.pyAdded missing functions great_circle_normal and interpolate to the nvector_summary.rstMoved the following functions related to rotation matrices from _core to rotation module:E_rotation, n_E_and_wa2R_EL, n_E2R_EN, R_EL2n_E, R_EN2n_E, R2xyz, R2zyx, xyz2R, zyx2RRenamed select_ellipsoid to get_ellipsoidMoved the following utility functions from _core to util module:deg, rad, mdot, nthroot, get_ellipsoid, unit, _check_length_deviationAdded _get_h1line and _make_summary to _common.pyReplaced numpy.rollaxis with numpy.swapaxes to make the code clearer._atleast_3d now broadcast the input against each other.Added examples to zyx2RAdded the following references to zyx2R, xyz2R, R2xyz, R2zyx:https://en.wikipedia.org/wiki/Aircraft_principal_axeshttps://en.wikipedia.org/wiki/Euler_angleshttps://en.wikipedia.org/wiki/Axes_conventionsRemoved tabs from CHANGELOG.rstUpdated CHANGELOG.rst and prepared for release v0.7.6Fixed the documentation so that it shows correctly in the reference manual.Added logo.png and docs/reference/nvector.rstUpdated build_package.py so it generates a valid README.rst file.Updated THANKS.rstUpdated CHANGELOG.rst and prepare for release 0.7.6Added Nvector documentation refhttps://nvector.readthedocs.io/en/v0.7.5to refs1.bib and _acknowledgements.pyUpdated README.rstRenamed requirements.readthedocs.txt to docs/requirements.txtAdded .readthedocs.ymlAdded sphinxcontrib-bibtex to requirements.readthedocs.txtAdded missing docs/tutorials/images/ex3img.pngDeleted obsolete ex10img.pngUpdated acknowledgement with reference to Karney’s article.Updated README.rst by moving acknowledgement to the end with references.Renamed position input argument to point in the FrameN, FrameB and FrameL classes.Deleted _example_images.pyRenamed nvector.rst to nvector_summary.rst in docs/referenceAdded example images to tutorials/images/ folderAdded Nvector logo, install.rst to docsAdded src/nvector/_example_images.pyAdded docs/tutorials/whatsnext.rstReorganized the documentation in docs by splitting _info.py into:_intro.py,_documentation.py_examples_object_oriented.py_images.py_installation.py and _acknowledgements.pyAdded docs/tutorials/index.rst, docs/intro/index.rst, docs/how-to/index.rst docs/appendix/index.rst and docs/make.batupdated references.Version 0.7.5, December 12, 2020Per A Brodtkorb (32):Updated CHANGELOG.rst and prepare for release 0.7.5Changed so that GeoPath.on_great_circle and GeoPath.on_great_circlereturns scalar result if the two points defining the path are scalars. See issue #10.Fixed failing doctests.Added doctest configuration to docs/conf.pyAdded allclose to nvector/objects.pyAdded array_to_list_dict and isclose functions in nvector.objects.pyReplaced f-string in the __repr__ method of the _Common class in
nvector.objects.py with format in order to work on python version 3.5
and below.Made nvector.plot.py more robust.Removed rtol parameter from the on_greatcircle function. See issue #12 for a discussion.Added nvector solution to the GeoPoint.displace method.Updated docs/conf.pyUpdated README.rst and LICENSE.txtReplaced import unittest with import pytest in test_frames.pyFixed issue #10: Inconsistent return types in GeoPath.track_distance:GeoPath, GeoPoint, Nvector and ECEFvector and Pvector now return
scalars for the case where the input is not actually arrays of points
but just single objects.Added extra tests for issue #10 and updated old tests and the examples in the help headers.Vectorized FrameE.inverse and FrameE.direct methods.Extended deg and rad functions in _core.py.Vectorized GeoPoint.distance_and_azimuthMade import of cartopy in nvector.plot more robust.Updated test_Ex10_cross_track_distanceUpdated sonar-project.propertiesReplaced deprecated sonar.XXXX.reportPath with sonar.XXXX.reportPathsSimplified nvector/_core.__doc__Updated .travis.ymlChanged the definition of sonar addonAdded CC_TEST_REPORTER_ID to .travis.ymlAdded python 3.8 to the CI testing.Changed so that setup.py is python 2.7 compatible again.Updated build_package.pyRenamed CHANGES.rst to CHANGELOG.rstUpdated setup.cfg and setup.pyAdded license.pyUpdated build_package.pyRemoved conda-build from .travis.ymlAttempt to get travis to run the tests again….API change: replaced “python setup.py doctests” with “python setup.py doctest”Added doctest example to nvector._core._atleast_3d Made xyz2R and zyx2R code simpler.Replaced deprecated Nvector.mean_horizontal_position with  Nvector.mean in test_frames.pyAdded mdot to __all__ in nvector/_core.py and in documentation summary.Sorted the the documentation summary by function name in nvector.rstRemoved –pyargs nvector –doctest-modules –pep8 from addopts section in setup.cfgUpdated documentation and added missing documentation.Version 0.7.4, June 4, 2019Per A Brodtkorb (2):Fixed PyPi badge and added downloads badge in nvector/_info.py and README.rstRemoved obsolete and wrong badges from docs/index.rstVersion 0.7.3, June 4, 2019Per A Brodtkorb (6):Renamed LICENSE.txt and THANKS.txt to LICENSE.rst and THANKS.rstUpdated README.rst and nvector/_info.pyFixed issue 7# incorrect test for test_n_E_and_wa2R_EL.Removed coveralls test coverage report.Replaced coverage badge from coveralls to codecov.Updated code-climate reporter.Simplified duplicated code in nvector._core.Added tests/__init__.pyAdded “–pyargs nvector” to pytest options in setup.cfgExclude build_package.py from distribution in MANIFEST.inReplaced health_img from landscape to codeclimate.Updated travis to explicitly install pytest-cov and pytest-pep8Removed dependence on pyscaffoldAdded MANIFEST.inRenamed set_package_version.py to build_package.pyVersion 0.7.0, June 2, 2019Gary van der Merwe (1):Add interpolate to __all__ so that it can be importedPer A Brodtkorb (26):Updated long_description in setup.cfgReplaced deprecated sphinx.ext.pngmath with sphinx.ext.imgmathAdded imgmath to requirements for building the docs.Fixing shallow clone warning.Replaced property ‘sonar.python.coverage.itReportPath’ with‘sonar.python.coverage.reportPaths’ instead, because it is has been removed.Drop python 3.4 supportAdded python 3.7 supportFixed a bug: Mixed scalars and np.array([1]) values don’t work with np.rad2deg function.Added ETRS ELLIPSOID in _core.py Added ED50 as alias for International(Hayford)/European Datum in _core.py Added sad69 as alias for South American 1969 in _core.pySimplified docstring for nv.testGeneralized the setup.py.Replaced aliases with the correct names in setup.cfg.Version 0.6.0, December 9, 2018Per A Brodtkorb (79):Updated requirements in setup.pyRemoved tox.iniUpdated documentation on how to set package versionMade a separate script to set package version in nvector/__init__.pyUpdated docstring for select_ellipsoidReplace GeoPoint.geo_point with GeoPoint.displace and removed deprecated GeoPoint.geo_pointUpdate .travis.ymlFix so that codeclimate is able to parse .travis.ymlOnly run sonar and codeclimate reporter for python v3.6Added sonar-project.propertiesPinned coverage to v4.3.4 due to fact that codeclimate reporter is onlycompatible with Coverage.py versions >=4.0,<4.4.Updated with sonar scanner.Added .pylintrcSet up codeclimate reporterUpdated docstring for unit function.Avoid division by zero in unit function.Reenabled the doctest of plot_mean_positionReset “pyscaffold==2.5.11”Replaced deprecated basemap with cartopy.Replaced doctest of plot_mean_position with test_plot_mean_position intest_plot.pyFixed failing doctests for python v3.4 and v3.5 and made them morerobust.Fixed failing doctests and made them more robust.Increased pycoverage version to use.moved nvector to src/nvector/Reset the setup.py to require ‘pyscaffold==2.5.11’ which works onpython version 3.4, 3.5 and 3.6. as well as 2.7Updated unittests.Updated tests.Removed obsolete codeAdded test for delta_LAdded corner testcase forpointA.displace(distance=1000,azimuth=np.deg2rad(200))Added test for path.track_distance(method=’exact’)Added delta_L a function thet teturn cartesian delta vector frompositions A to B decomposed in L.Simplified OO-solution in example 1 by using delta_N functionRefactored duplicated codeVectorized code so that the frames can take more than one position atthe time.Keeping only the html docs in the distribution.replaced link from latest to stable docs on readthedocs and updatedcrosstrack distance test.updated documentation in setup.pyVersion 0.5.2, March 7, 2017Per A Brodtkorb (10):Fixed tests in tests/test_frames.pyUpdated to setup.cfg and tox.ini + pep8updated .travis.ymlUpdated Readme.rst with new example 10 picture and link to nvector docs at readthedocs.updated official documentation linksUpdated crosstrack distance tests.Version 0.5.1, March 5, 2017Cody (4):Explicitely numbered replacement fieldsMigrated%string formatingPer A Brodtkorb (29):pep8Updated failing examplesUpdated README.rstRemoved obsolete pass statementDocumented functionsadded .checkignore for quantifycodemoved test_docstrings and use_docstring_from into _common.pyAdded .codeclimate.ymlUpdated installation information in _info.pyAdded GeoPath.on_path method. Clearified intersection exampleAdded great_circle_normal, cross_track_distanceRenamed intersection to intersect (Intersection is deprecated.)Simplified R2zyx with a call to R2xyz Improved accuracy for great circle cross track distance for small distances.Added on_great_circle, _on_great_circle_path, _on_ellipsoid_path, closest_point_on_great_circle and closest_point_on_path to GeoPathmade __eq__ more robust for framesRemoved duplicated codeUpdated testsRemoved fishy testreplaced zero n-vector with nanCommented out failing test.Added example 10 imageAdded ‘closest_point_on_great_circle’, ‘on_great_circle’,’on_great_circle_path’.Updated examples + documentationUpdated index depthUpdated README.rst and classifier in setup.cfgVersion 0.4.1, January 19, 2016pbrod (46):Cosmetic updatesUpdated README.rstupdated docs and removed unused codeupdated README.rst and .coveragercRefactored out _check_framesRefactored out _default_frameUpdated .coveragercAdded link to geographiclibUpdated external linkUpdated documentationAdded figures to examplesAdded GeoPath.interpolate + interpolation example 6Added links to FFI homepage.Updated documentation:Added link to nvector toolbox for matlabFor each example added links to the more detailed explanation on the homepageUpdated link to nvector toolbox for matlabAdded link to nvector on  pypiUpdated documentation fro FrameB, FrameE, FrameL and FrameN.updated __all__ variableAdded missing R_Ee to function n_EA_E_and_n_EB_E2azimuth + updated documentationUpdated CHANGES.rstUpdated conf.pyRenamed info.py to _info.pyAll examples are now generated from _examples.py.Version 0.1.3, January 1, 2016pbrod (31):RefactoredUpdated testsUpdated docsMoved tests to nvector/testsUpdated .coverage     Added travis.yml, .landscape.ymlDeleted obsolete LICENSEUpdated README.rstRemoved ngs versionFixed bug in .travis.ymlUpdated .travis.ymlRemoved dependence on navigator.pyUpdated README.rstUpdated examplesDeleted skeleton.py and added tox.iniRenamed distance_rad_bearing_rad2point to n_EA_E_distance_and_azimuth2n_EB_ERenamed azimuth to n_EA_E_and_n_EB_E2azimuthAdded tests for R2xyz as well as R2zyxRemoved backward compatibilityAdded test_n_E_and_wa2R_ELRefactored testsCommented out failing tests on python 3+updated CHANGES.rstRemoved bug in setup.pyVersion 0.1.1, January 1, 2016pbrod (31):Initial commit: Translated code from Matlab to Python.Added object oriented interface to nvector libraryAdded tests for object oriented interfaceAdded geodesic tests.The content of this library is based on the following publication:Gade, K. (2010). A Nonsingular Horizontal Position Representation, The Journal
of Navigation, Volume 63, Issue 03, pp 395-417, July 2010.
(www.navlab.net/Publications/A_Nonsingular_Horizontal_Position_Representation.pdf)This paper should be cited in publications using this library.Copyright (c) 2015-2021, Norwegian Defence Research Establishment (FFI)
All rights reserved.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 publication
information, copyright notice, this list of conditions and the following
disclaimer.2. Redistributions in binary form must reproduce the above publication
information, copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided with the
distribution.THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
“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 THE COPYRIGHT HOLDER OR CONTRIBUTORS
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.ContributersKenneth Gade, FFI: Main author of Matlab toolbox nvector.Kristian Svartveit, FFI: Contributions to matlab code: R_Ee.m and unit.m.Brita Hafskjold Gade, FFI: Contributions to matlab code: n_EB_E2p_EB_E.m, p_EB_E2n_EB_E.mPer A Brodtkorb, FFI: Translation of nvector from matlab to Python and maintainer of the python version.