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 _IsnotError, _ValueError, _xellipsoidal _Missing, _N_, _near_concentric_, _no_convergence_fmt_, \ _no_conversion_, _too_distant_fmt_, _3_0
'''(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 Epoch(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
'''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:
'''Get this point's datum (L{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)
'''I{Approximate} the distance and (initial) bearing between this and an other (ellipsoidal) point based on the radii of curvature.
I{Suitable only for short distances up to a few hundred Km or Miles and only between points not near-polar}.
@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>} aka U{Hubeny<https://www.OVG.AT/de/vgi/files/pdf/3781/>} formula. ''' 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))
'''Get this point's observed or C{reframe} epoch (C{float}) or C{None}. '''
'''Set or clear this point's observed epoch.
@arg epoch: Observed epoch, a fractional calendar year (L{Epoch}, C{scalar}) or C{None}.
@raise TRFError: Invalid B{C{epoch}}. '''
'''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, both intersection points are the same instance.
@raise IntersectionError: Concentric, antipodal, invalid or non-intersecting circles or no convergence for B{C{tol}}.
@raise ImportError: Package U{geographiclib <https://PyPI.org/project/geographiclib>} not installed or not found.
@raise TypeError: Invalid B{C{other}} or B{C{equidistant}}.
@raise ValueError: Invalid B{C{radius1}}, B{C{radius2}} or B{C{height}}. ''' equidistant=equidistant, tol=tol, LatLon=self.classof, datum=self.datum)
equidistant=None, tol=_TOL_M): '''Locate the closest point between two other points.
@arg point1: Start point (C{LatLon}). @arg point2: End point (C{LatLon}). @kwarg within: If C{True} return the closest point I{between} B{C{point1}} and B{C{point2}}, otherwise the closest point elsewhere on the arc (C{bool}). @kwarg height: Optional height for the closest point (C{meter}) or C{None} to interpolate the height. @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: Closest point (C{LatLon}).
@raise ImportError: Package U{geographiclib <https://PyPI.org/project/geographiclib>} not installed or not found.
@raise TypeError: Invalid B{C{point1}}, B{C{point2}} or B{C{equidistant}}. ''' equidistant=equidistant, tol=tol, LatLon=self.classof, datum=self.datum)
'''Get the iteration number (C{int} or C{None} if not available/applicable) of the most recent C{intersections2} or C{nearestOn} invokation. '''
'''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
'''Get this point's reference frame (L{RefFrame}) or C{None}. '''
'''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
'''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}). '''
area=True, eps=EPS1, wrap=False): '''Trilaterate three points by area overlap or perimeter intersection three corresponding circles.
@arg distance1: Distance to this point (C{meter}), same units as B{C{eps}}). @arg point2: Second center point (C{LatLon}). @arg distance2: Distance to point2 (C{meter}, same units as B{C{eps}}). @arg point3: Third center point (C{LatLon}). @arg distance3: Distance to point3 (C{meter}, same units as B{C{eps}}). @kwarg area: If C{True} compute the area overlap, otherwise the perimeter intersection of the circles (C{bool}). @kwarg eps: The required I{minimal overlap} for C{B{area}=True} or the I{intersection margin} for C{B{area}=False} (C{meter}, conventionally). @kwarg wrap: Wrap/unroll angular distances (C{bool}).
@return: A L{Trilaterate5Tuple}C{(min, minPoint, max, maxPoint, n)} with C{min} and C{max} in C{meter}, same units as B{C{eps}}, the corresponding trilaterated points C{minPoint} and C{maxPoint} as I{ellipsoidal} C{LatLon} and C{n}, the number of trilatered points found for the given B{C{eps}}.
If only a single trilaterated point is found, C{min I{is} max}, C{minPoint I{is} maxPoint} and C{n = 1}.
For C{B{area}=True}, C{min} and C{max} are the smallest respectively largest I{radial} overlap found.
For C{B{area}=False}, C{min} and C{max} represent the nearest respectively farthest intersection margin.
If C{B{area}=True} and all 3 circles are concentric, C{n = 0} and C{minPoint} and C{maxPoint} are both the B{C{point#}} with the smallest B{C{distance#}} C{min} and C{max} the largest B{C{distance#}}.
@raise IntersectionError: Trilateration failed for the given B{C{eps}}, insufficient overlap for C{B{area}=True} or no intersection or all (near-)concentric for C{B{area}=False}.
@raise TypeError: Invalid B{C{point2}} or B{C{point3}}.
@raise ValueError: Some B{C{points}} coincide or invalid B{C{distance1}}, B{C{distance2}} or B{C{distance3}}.
@note: Ellipsoidal trilateration invokes methods C{LatLon.intersections2} and C{LatLon.nearestOn}. Install Karney's Python package U{geographiclib<https://PyPI.org/project/geographiclib>} to obtain the most accurate results for both C{ellipsoidalVincenty.-} and C{ellipsoidalKarney.LatLon} points. ''' self.others(point2=point2), distance2, self.others(point3=point3), distance3, area=area, eps=eps, wrap=wrap)
equidistant=None, tol=_TOL_M, LatLon=None, **LatLon_kwds): # (INTERNAL) Iteratively compute the intersection points of two circles # each defined by an (ellipsoidal) center point and a radius, imported # by .ellipsoidalKarney and -Vincenty
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
raise ValueError(_exceed_PI_radians_)
# 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
# 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
# get an C{azimuthal.Equidistant} or {-.Karney} instance
issubclassof(equidistant, _az.EquidistantKarney)): raise _IsnotError(_az.Equidistant.__name__, _az.EquidistantKarney.__name__, equidistant=equidistant)
# wrap, unroll and replace longitude if different p2 = p2.classof(p2.lat, lon, p2.height, datum=p2.datum)
equidistant=None, tol=_TOL_M, LatLon=None, **LatLon_kwds): # (INTERNAL) Get closet point, like L{_intersects2} above, # separated to allow callers to embellish any exceptions
# get the azimuthal equidistant projection
# gu-/estimate initial nearestOn, spherically ... wrap=False _LLS(p1.lat, p1.lon, height=p1.height), _LLS(p2.lat, p2.lon, height=p2.height), within=within, height=height)
h = t.height h1 = p1.height h2 = p2.height
# ... and then iterate like Karney suggests to find # tri-points of median lines, @see: references above # closest to origin, .z to interpolate height # convert points to projection space # compute nearestOn in projection space # convert nearestOn back to geodetic # break if below tolerance or if unchanged h = v.z # nearest interpolated else: raise ValueError(_no_convergence_fmt_ % (tol,))
# **) 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. |