Coverage for pygeodesy/ellipsoidalBase.py : 95%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
# -*- coding: utf-8 -*-
C{LatLonEllipsoidalBase}.
Pure Python implementation of geodesy tools for ellipsoidal earth models, transcribed in part from JavaScript originals by I{(C) Chris Veness 2005-2016} and published under the same MIT Licence**, see for example U{latlon-ellipsoidal <https://www.Movable-Type.co.UK/scripts/geodesy/docs/latlon-ellipsoidal.js.html>}.
@newfield example: Example, Examples '''
_xinstanceof, _xkwds _IsnotError, _ValueError, _xellipsoidal _exceed_PI_radians_, _Missing, _N_, _near_concentric_, \ NN, _no_convergence_fmt_, _no_conversion_, _radius1_, \ _radius2_, _too_distant_fmt_ # PYCHOK used!
'''(INTERNAL) Base class for ellipsoidal C{Cartesian}s. '''
'''Convert this cartesian point from one to an other reference frame.
@arg reframe2: Reference frame to convert I{to} (L{RefFrame}). @arg reframe: Reference frame to convert I{from} (L{RefFrame}). @kwarg epoch: Optional epoch to observe for B{C{reframe}}, a fractional calendar year (C{scalar}).
@return: The converted point (C{Cartesian}) or this point if conversion is C{nil}.
@raise TRFError: No conversion available from B{C{reframe}} to B{C{reframe2}}.
@raise TypeError: B{C{reframe2}} or B{C{reframe}} not a L{RefFrame} or B{C{epoch}} not C{scalar}. '''
epoch is None else _2epoch(epoch)):
'''(INTERNAL) Base class for ellipsoidal C{LatLon}s. '''
epoch=None, name=NN): '''Create an ellipsoidal C{LatLon} point frome the given lat-, longitude and height on the given datum and with the given reference frame and epoch.
@arg lat: Latitude (C{degrees} or DMS C{[N|S]}). @arg lon: Longitude (C{degrees} or DMS C{str[E|W]}). @kwarg height: Optional elevation (C{meter}, the same units as the datum's half-axes). @kwarg datum: Optional, ellipsoidal datum to use (L{Datum}, L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}). @kwarg reframe: Optional reference frame (L{RefFrame}). @kwarg epoch: Optional epoch to observe for B{C{reframe}} (C{scalar}), a non-zero, fractional calendar year. @kwarg name: Optional name (string).
@raise TypeError: B{C{datum}} is not a L{datum}, B{C{reframe}} is not a L{RefFrame} or B{C{epoch}} is not C{scalar} non-zero.
@example:
>>> p = LatLon(51.4778, -0.0016) # height=0, datum=Datums.WGS84 '''
'''(INTERNAL) Adjust elevation or geoidHeight with difference in Gaussian radii of curvature of given datum and NAD83.
@note: This is an arbitrary, possibly incorrect adjustment. ''' # use ratio, datum and NAD83 units may differ
'''(INTERNAL) Zap cached attributes if updated. ''' '_ups', '_utm', '_wm', *attrs)
'''Return the antipode, the point diametrically opposite to this point.
@kwarg height: Optional height of the antipode, height of this point otherwise (C{meter}).
@return: The antipodal point (C{LatLon}). ''' lla.datum = self.datum
def convergence(self): '''Get this point's UTM or UPS meridian convergence (C{degrees}) or C{None} if not converted from L{Utm} or L{Ups}. '''
'''Convert this point to an other datum.
@arg datum2: Datum to convert I{to} (L{Datum}).
@return: The converted point (ellipsoidal C{LatLon}).
@raise TypeError: The B{C{datum2}} invalid.
@example:
>>> p = LatLon(51.4778, -0.0016) # default Datums.WGS84 >>> p.convertDatum(Datums.OSGB36) # 51.477284°N, 000.00002°E '''
'''Convert this point to an other reference frame.
@arg reframe2: Reference frame to convert I{to} (L{RefFrame}).
@return: The converted point (ellipsoidal C{LatLon}) or this point if conversion is C{nil}.
@raise TRFError: No B{C{.reframe}} or no conversion available from B{C{.reframe}} to B{C{reframe2}}.
@raise TypeError: The B{C{reframe2}} is not a L{RefFrame}.
@example:
>>> p = LatLon(51.4778, -0.0016, reframe=RefFrames.ETRF2000) # default Datums.WGS84 >>> p.convertRefFrame(RefFrames.ITRF2014) # 51.477803°N, 000.001597°W, +0.01m '''
raise TRFError(_no_conversion_, txt='%r.reframe %s' % (self, _Missing))
epoch=self.epoch, reframe=reframe2) # ll.reframe, ll.epoch = reframe2, self.epoch else:
def datum(self): '''Get this point's datum (L{Datum}). '''
def datum(self, datum): '''Set this point's datum I{without conversion}.
@arg datum: New datum (L{Datum}).
@raise TypeError: The B{C{datum}} is not a L{Datum} or not ellipsoidal. ''' raise _IsnotError(_ellipsoidal_, datum=datum)
'''Approximate the distance and (initial) bearing between this and an other (ellipsoidal) point based on the radii of curvature.
Suitable only for short distances up to a few hundred Km or Miles and only between non-near-polar points.
@arg other: The other point (C{LatLon}).
@return: An L{Distance2Tuple}C{(distance, initial)}.
@raise TypeError: The B{C{other}} point is not C{LatLon}.
@raise ValueError: Incompatible datum ellipsoids.
@see: Method L{Ellipsoid.distance2} and U{Local, flat earth approximation<https://www.EdWilliams.org/avform.htm#flat>}. ''' other.lat, other.lon)
'''Return elevation of this point for its or the given datum.
@kwarg adjust: Adjust the elevation for a B{C{datum}} other than C{NAD83} (C{bool}). @kwarg datum: Optional datum (L{Datum}). @kwarg timeout: Optional query timeout (C{seconds}).
@return: An L{Elevation2Tuple}C{(elevation, data_source)} or C{(None, error)} in case of errors.
@note: The adjustment applied is the difference in geocentric earth radius between the B{C{datum}} and C{NAV83} upon which the L{elevations.elevation2} is based.
@note: NED elevation is only available for locations within the U{Conterminous US (CONUS) <https://WikiPedia.org/wiki/Contiguous_United_States>}.
@see: Function L{elevations.elevation2} and method L{Ellipsoid.Rgeocentric} for further details and possible C{error}s. ''' timeout=timeout)
'''Return the ellipsoid of this point's datum or the given datum.
@kwarg datum: Default datum (L{Datum}).
@return: The ellipsoid (L{Ellipsoid} or L{Ellipsoid2}). '''
'''Check the type and ellipsoid of this and an other point's datum.
@arg other: The other point (C{LatLon}).
@return: This point's datum ellipsoid (L{Ellipsoid} or L{Ellipsoid2}).
@raise TypeError: The B{C{other}} point is not C{LatLon}.
@raise ValueError: Incompatible datum ellipsoids. '''
except AttributeError: # PYCHOK no cover try: # no ellipsoid method, try datum e = other.datum.ellipsoid except AttributeError: e = E # no datum, XXX assume equivalent? raise _ValueError(e.named2, txt=_incompatible(E.named2))
def epoch(self): '''Get this point's observed epoch (C{float}) or C{None}. '''
def epoch(self, epoch): '''Set or clear this point's observed epoch.
@arg epoch: Observed epoch, a fractional calendar year (C{scalar}) or C{None}.
@raise TypeError: The B{C{epoch}} is not C{scalar}. '''
'''Return geoid height of this point for its or the given datum.
@kwarg adjust: Adjust the geoid height for a B{C{datum}} other than C{NAD83/NADV88} (C{bool}). @kwarg datum: Optional datum (L{Datum}). @kwarg timeout: Optional query timeout (C{seconds}).
@return: An L{GeoidHeight2Tuple}C{(height, model_name)} or C{(None, error)} in case of errors.
@note: The adjustment applied is the difference in geocentric earth radius between the B{C{datum}} and C{NAV83/NADV88} upon which the L{elevations.geoidHeight2} is based.
@note: The geoid height is only available for locations within the U{Conterminous US (CONUS) <https://WikiPedia.org/wiki/Contiguous_United_States>}.
@see: Function L{elevations.geoidHeight2} and method L{Ellipsoid.Rgeocentric} for further details and possible C{error}s. ''' model=0, timeout=timeout)
equidistant=None, tol=_TOL_M): '''Compute the intersection points of two circles each defined by a center point and a radius.
@arg radius1: Radius of the this circle (C{meter}). @arg other: Center of the other circle (C{LatLon}). @arg radius2: Radius of the other circle (C{meter}). @kwarg height: Optional height for the intersection points, overriding the "radical height" at the "radical line" between both centers (C{meter}) or C{None}. @kwarg wrap: Wrap and unroll longitudes (C{bool}). @kwarg equidistant: An azimuthal equidistant projection class (L{Equidistant} or L{EquidistantKarney}), function L{azimuthal.equidistant} will be invoked if left unspecified. @kwarg tol: Convergence tolerance (C{meter}).
@return: 2-Tuple of the intersection points, each a C{LatLon} instance. For abutting circles, the intersection points are the same instance.
@raise IntersectionError: Concentric, antipodal, invalid or non-intersecting circles or no convergence for B{C{tol}}.
@raise TypeError: If B{C{other}} is not C{LatLon}.
@raise ValueError: Invalid B{C{radius1}}, B{C{radius2}} or B{C{height}}. ''' equidistant=equidistant, tol=tol, LatLon=self.classof)
def iteration(self): '''Get the iteration number (C{int} or C{None} if not available/applicable). '''
'''Parse a string representing a similar, ellipsoidal C{LatLon} point, consisting of C{"lat, lon[, height]"}.
@arg strllh: Lat, lon and optional height (C{str}), see function L{parse3llh}. @kwarg height: Optional, default height (C{meter} or C{None}). @kwarg datum: Optional datum (L{Datum}), overriding this datum I{without conversion}. @kwarg sep: Optional separator (C{str}). @kwarg name: Optional instance name (C{str}), overriding this name.
@return: The similar point (ellipsoidal C{LatLon}).
@raise ParseError: Invalid B{C{strllh}}. ''' r.datum = datum
def reframe(self): '''Get this point's reference frame (L{RefFrame}) or C{None}. '''
def reframe(self, reframe): '''Set or clear this point's reference frame.
@arg reframe: Reference frame (L{RefFrame}) or C{None}.
@raise TypeError: The B{C{reframe}} is not a L{RefFrame}. ''' self._reframe = None
def scale(self): '''Get this point's UTM grid or UPS point scale factor (C{float}) or C{None} if not converted from L{Utm} or L{Ups}. '''
def to3xyz(self): # PYCHOK no cover '''DEPRECATED, use method C{toEcef}.
@return: A L{Vector3Tuple}C{(x, y, z)}.
@note: Overloads C{LatLonBase.to3xyz} ''' r = self.toEcef() return self._xnamed(Vector3Tuple(r.x, r.y, r.z))
'''Convert this C{LatLon} point to an ETM coordinate.
@return: The ETM coordinate (L{Etm}).
@see: Function L{toEtm8}. '''
'''Convert this C{LatLon} point to a Lambert location.
@see: Function L{toLcc} in module L{lcc}.
@return: The Lambert location (L{Lcc}). ''' name=self.name)
'''Convert this C{LatLon} point to an OSGR coordinate.
@see: Function L{toOsgr} in module L{osgr}.
@return: The OSGR coordinate (L{Osgr}). ''' name=self.name)
'''Convert this C{LatLon} point to a UPS coordinate.
@kwarg pole: Optional top/center of (stereographic) projection (C{str}, 'N[orth]' or 'S[outh]'). @kwarg falsed: False easting and northing (C{bool}).
@return: The UPS coordinate (L{Ups}).
@see: Function L{toUps8}. ''' pole=pole, falsed=falsed)
'''Convert this C{LatLon} point to a UTM coordinate.
@return: The UTM coordinate (L{Utm}).
@see: Function L{toUtm8}. '''
'''Convert this C{LatLon} point to a UTM or UPS coordinate.
@kwarg pole: Optional top/center of UPS (stereographic) projection (C{str}, 'N[orth]' or 'S[outh]').
@return: The UTM or UPS coordinate (L{Utm} or L{Ups}).
@raise TypeError: Result in L{Utm} or L{Ups}.
@see: Function L{toUtmUps}. ''' else: else: _xinstanceof(Utm, Ups, toUtmUps8=u)
'''Convert this C{LatLon} point to a WM coordinate.
@see: Function L{toWm} in module L{webmercator}.
@return: The WM coordinate (L{Wm}). '''
# (INTERNAL) C{_intersections2} imported by .ellipsoidalKarney and -Vincenty equidistant=None, tol=_TOL_M, LatLon=None, **LatLon_kwds): '''Iteratively compute the intersection points of two circles each defined by an (ellipsoidal) center point and a radius.
@arg center1: Center of the first circle (ellipsoidal C{LatLon}). @arg radius1: Radius of the first circle (C{meter}). @arg center2: Center of the second circle (ellipsoidal C{LatLon}). @arg radius2: Radius of the second circle (C{meter}). @kwarg height: Optional height for the intersection points, overriding the "radical height" at the "radical line" between both centers (C{meter}). @kwarg wrap: Wrap and unroll longitudes (C{bool}). @kwarg equidistant: An azimuthal equidistant projection class (L{Equidistant} or L{EquidistantKarney}) or C{None} for function L{azimuthal.equidistant}. @kwarg tol: Convergence tolerance (C{meter}). @kwarg LatLon: Optional class to return the intersection points (ellipsoidal C{LatLon}) or C{None}. @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword arguments, ignored if B{C{LatLon=None}}.
@return: 2-Tuple of the intersection points, each a B{C{LatLon}} instance or L{LatLon4Tuple}C{(lat, lon, height, datum)} if B{C{LatLon}} is C{None}. For abutting circles, the intersection points are the same instance.
@raise ImportError: If B{C{equidistant}} is L{EquidistantKarney}) and package U{geographiclib <https://PyPI.org/project/geographiclib>} not installed or not found.
@raise IntersectionError: Concentric, antipodal, invalid or non-intersecting circles or no convergence for B{C{tol}}.
@raise TypeError: If B{C{center1}} or B{C{center2}} not ellipsoidal.
@raise UnitError: Invalid B{C{radius1}}, B{C{radius2}} or B{C{height}}.
@see: U{The B{ellipsoidal} case<https://GIS.StackExchange.com/questions/48937/ calculating-intersection-of-two-circles>}, U{Karney's paper <https://ArXiv.org/pdf/1102.1215.pdf>}, pp 20-21, section 14 I{Maritime Boundaries}, U{circle-circle<https://MathWorld.Wolfram.com/Circle-CircleIntersection.html>} and U{sphere-sphere<https://MathWorld.Wolfram.com/Sphere-SphereIntersection.html>} intersections. '''
equidistant=equidistant, tol=tol, LatLon=LatLon, **LatLon_kwds) raise IntersectionError(center1=center1, radius1=radius1, center2=center2, radius2=radius2, txt=str(x))
equidistant=None, tol=_TOL_M, LatLon=None, **LatLon_kwds): # (INTERNAL) Intersect two spherical circles, see L{_intersections2} # above, separated to allow callers to embellish any exceptions
r = LatLon4Tuple(t.lat, t.lon, h, t.datum) else:
raise ValueError(_exceed_PI_radians_)
c2 = c2.classof(c2.lat, lon2, c2.height, datum=c2.datum)
# distance between centers and radii are # measured along the ellipsoid's surface raise ValueError(_near_concentric_) raise ValueError(_too_distant_fmt_ % (m,))
# get the azimuthal equidistant projection else:
# gu-/estimate initial intersections, spherically ... _LLS(c2.lat, c2.lon, height=c2.height), r2, radius=r, height=height, wrap=False, too_d=m)
# ... and then iterate like Karney suggests to find # tri-points of median lines, @see: references above # convert centers to projection space # compute intersections in projection space t2, r2, # XXX * t2.scale?, sphere=False, too_d=m) # convert intersections back to geodetic t, d = t1, d1 else: # consider only the closer intersection # break if below tolerance or if unchanged ta = t else: raise ValueError(_no_convergence_fmt_ % (tol,))
r = _latlon4(ta, h, n) elif len(ts) == 1: # XXX assume abutting r = _latlon4(ts[0], h, n) else: raise _AssertionError(ts=ts) return r, r
# **) MIT License # # Copyright (C) 2016-2020 -- mrJean1 at Gmail -- All Rights Reserved. # # Permission is hereby granted, free of charge, to any person obtaining a # copy of this software and associated documentation files (the "Software"), # to deal in the Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, distribute, sublicense, # and/or sell copies of the Software, and to permit persons to whom the # Software is furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included # in all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR # OTHER DEALINGS IN THE SOFTWARE. |