示例#1
0
文件: trf.py 项目: rbpdqdat/PyGeodesy
def _reframeTransforms(rf2, rf, epoch):
    '''(INTERNAL) Get 0, 1 or 2 Helmert C{Transforms} to convert
       reference frame C{rf} observed at C{epoch} into C{rf2}.
    '''
    n2 = rf2.name  # .upper()
    n1 = rf.name  # .upper()
    if n1 == n2 or (n1.startswith(_S.ITRF) and n2.startswith(_S.WGS84)) \
                or (n2.startswith(_S.ITRF) and n1.startswith(_S.WGS84)):
        return ()  # PYCHOK returns

    if (n1, n2) in _trfXs:
        return (_2Transform((n1, n2), epoch, _Forward), )  # PYCHOK returns

    if (n2, n1) in _trfXs:
        return (_2Transform((n2, n1), epoch, _Reverse), )  # PYCHOK returns

    n = _intermediate(n1, n2)
    if n:
        return (
            _2Transform((n1, n), epoch, _Forward),  # PYCHOK returns
            _2Transform((n, n2), epoch, _Forward))

    n = _intermediate(n2, n1)
    if n:
        return (
            _2Transform((n, n1), epoch, _Reverse),  # PYCHOK returns
            _2Transform((n2, n), epoch, _Reverse))

    t = _SPACE_(RefFrame.__name__, repr(n1), _to_, repr(n2))
    raise TRFError(_no_(_conversion_), txt=t)
示例#2
0
文件: etm.py 项目: rbpdqdat/PyGeodesy
    def _zetaInv(self, taup, lam):
        '''(INTERNAL) Invert C{zeta} using Newton's method.

           @return: 2-Tuple C{(u, v)}.

           @see: C{void TMExact::zetainv(real taup, real lam,
                                         real &u, real &v)}.

           @raise EllipticError: No convergence.
        '''
        psi = asinh(taup)
        sca = _1_0 / hypot1(taup)
        u, v, trip = self._zetaInv0(psi, lam)
        if trip:
            self._iteration = 0
        else:
            stol2 = _TOL_10 / max(psi**2, _1_0)
            U, V = Fsum(u), Fsum(v)
            # min iterations = 2, max = 6, mean = 4.0
            for self._iteration in range(1, _TRIPS):  # GEOGRAPHICLIB_PANIC
                sncndn6 = self._sncndn6(u, v)
                T, L, _ = self._zeta3(*sncndn6)
                dw, dv = self._zetaDwd(*sncndn6)
                T = (taup - T) * sca
                L -= lam
                u, du = U.fsum2_(T * dw, L * dv)
                v, dv = V.fsum2_(T * dv, -L * dw)
                if trip:
                    break
                trip = hypot2(du, dv) < stol2
            else:
                t = unstr(self._zetaInv.__name__, taup, lam)
                raise EllipticError(_no_(_convergence_), txt=t)
        return u, v
示例#3
0
文件: etm.py 项目: rbpdqdat/PyGeodesy
    def _sigmaInv(self, xi, eta):
        '''(INTERNAL) Invert C{sigma} using Newton's method.

           @return: 2-Tuple C{(u, v)}.

           @see: C{void TMExact::sigmainv(real xi, real eta,
                                          real &u, real &v)}.

           @raise EllipticError: No convergence.
        '''
        u, v, trip = self._sigmaInv0(xi, eta)
        if trip:
            self._iteration = 0
        else:
            U, V = Fsum(u), Fsum(v)
            # min iterations = 2, max = 7, mean = 3.9
            for self._iteration in range(1, _TRIPS):  # GEOGRAPHICLIB_PANIC
                sncndn6 = self._sncndn6(u, v)
                X, E, _ = self._sigma3(v, *sncndn6)
                dw, dv = self._sigmaDwd(*sncndn6)
                X = xi - X
                E -= eta
                u, du = U.fsum2_(X * dw, E * dv)
                v, dv = V.fsum2_(X * dv, -E * dw)
                if trip:
                    break
                trip = hypot2(du, dv) < _TOL_10
            else:
                t = unstr(self._sigmaInv.__name__, xi, eta)
                raise EllipticError(_no_(_convergence_), txt=t)
        return u, v
示例#4
0
def _trilaterate5(p1, d1, p2, d2, p3, d3, area=True, eps=EPS1,
                                          radius=R_M, wrap=False):
    # (INTERNAL) Trilaterate three points by area overlap or
    # by perimeter intersection of three circles (radius is
    # only needed for spherical LatLon.distanceTo)

    r1 = Distance_(distance1=d1)
    r2 = Distance_(distance2=d2)
    r3 = Distance_(distance3=d3)

    m  = 0 if area else (r1 + r2 + r3)
    pc = 0
    t  = []
    for _ in range(3):
        try:
            c1, c2 = p1.intersections2(r1, p2, r2, wrap=wrap)

            if area:  # check overlap
                if c1 is c2:  # abutting
                    c = c1
                else:  # nearest point on radical
                    c = p3.nearestOn(c1, c2, within=True, wrap=wrap)
                d = r3 - p3.distanceTo(c, radius=radius, wrap=wrap)
                if d > eps:  # sufficient overlap
                    t.append((d, c))
                m = max(m, d)

            else:  # check intersection
                for c in ((c1,) if c1 is c2 else (c1, c2)):
                    d = abs(r3 - p3.distanceTo(c, radius=radius, wrap=wrap))
                    if d < eps:  # below margin
                        t.append((d, c))
                    m = min(m, d)

        except IntersectionError as x:
            if _near_concentric_ in str(x):  # XXX ConcentricError?
                pc += 1

        p1, r1, p2, r2, p3, r3 = p2, r2, p3, r3, p1, r1  # rotate

    if t:  # get min, max, points and count ...
        t = tuple(sorted(t))
        n = len(t),  # as tuple
        # ... or for a single trilaterated result,
        # min *is* max, min- *is* maxPoint and n=1
        return Trilaterate5Tuple(*(t[0] + t[-1] + n))

    if area and pc == 3:  # all pairwise concentric ...
        r, p = min((r1, p1), (r2, p2), (r3, p3))
        # ... return smallest point twice, the smallest
        # and largest distance and n=0 for concentric
        return Trilaterate5Tuple(float(r), p, float(max(r1, r2, r3)), p, 0)

    f =  max if area else min
    t = _no_(_overlap_ if area else _intersection_)
    t = '%s (%s %.3f)' % (t, f.__name__, m)
    raise IntersectionError(area=area, eps=eps, wrap=wrap, txt=t)
示例#5
0
    def reverse(self, x, y, name=NN, LatLon=None, **LatLon_kwds):
        '''Convert an azimuthal gnomonic location to (ellipsoidal) geodetic lat- and longitude.

           @arg x: Easting of the location (C{meter}).
           @arg y: Northing of the location (C{meter}).
           @kwarg name: Optional name for the location (C{str}).
           @kwarg LatLon: Class to use (C{LatLon}) or C{None}.
           @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
                               arguments, ignored if C{B{LatLon}=None}.

           @return: The geodetic (C{LatLon}) or if B{C{LatLon}} is C{None} an
                    L{Azimuthal7Tuple}C{(x, y, lat, lon, azimuth, scale, datum)}.

           @raise AzimuthalError: No convergence.

           @note: The C{lat} will be in the range C{[-90..90] degrees} and C{lon}
                  in the range C{[-180..180] degrees}.  The C{azimuth} is clockwise
                  from true North.  The scale is C{1 / reciprocal**2} in C{radial}
                  direction and C{1 / reciprocal} in the direction perpendicular
                  to this.
        '''
        x = Scalar(x=x)
        y = Scalar(y=y)

        z = atan2d(x, y)  # (x, y) for azimuth from true North
        q = hypot(x, y)

        d = e = self.equatoradius
        s = e * atan(q / e)
        if q > e:

            def _d(r, q):
                return (r.M12 - q * r.m12) * r.m12  # negated

            q = 1 / q
        else:  # little == True

            def _d(r, q):  # PYCHOK _d
                return (q * r.M12 - r.m12) * r.M12  # negated

        e *= _Karney_eps

        S = Fsum(s)
        g = self.geodesic.Line(self.lat0, self.lon0, z, self._mask)
        for self._iteration in range(1, _TRIPS):
            r = g.Position(s, self._mask)
            if abs(d) < e:
                break
            s, d = S.fsum2_(_d(r, q))
        else:
            raise AzimuthalError(x=x, y=y, txt=_no_(Fmt.convergence(e)))

        t = self._7Tuple(x, y, r, r.M12) if LatLon is None else \
            self._toLatLon(r.lat2, r.lon2, LatLon, LatLon_kwds)

        t._iteration = self._iteration
        return self._xnamed(t, name=name)
示例#6
0
    def _direct(self, distance, bearing, llr, height=None):
        '''(INTERNAL) Direct Vincenty method.

           @raise TypeError: The B{C{other}} point is not L{LatLon}.

           @raise ValueError: If this and the B{C{other}} point's L{Datum}
                              ellipsoids are not compatible.

           @raise VincentyError: Vincenty fails to converge for the current
                                 L{LatLon.epsilon} and L{LatLon.iterations}
                                 limit.
        '''
        E = self.ellipsoid()

        c1, s1, t1 = _r3(self.lat, E.f)

        i = radians(bearing)  # initial bearing (forward azimuth)
        si, ci = sincos2(i)
        s12 = atan2(t1, ci) * 2

        sa = c1 * si
        c2a = 1 - sa**2
        if c2a < EPS:
            c2a = 0
            A, B = 1, 0
        else:  # e22 == (a / b)**2 - 1
            A, B = _p2(c2a * E.e22)

        s = d = distance / (E.b * A)
        for self._iteration in range(1, self._iterations + 1):
            ss, cs = sincos2(s)
            c2sm = cos(s12 + s)
            s_, s = s, d + _ds(B, cs, ss, c2sm)
            if abs(s - s_) < self._epsilon:
                break
        else:
            raise VincentyError(_no_(_convergence_),
                                txt=repr(self))  # self.toRepr()

        t = s1 * ss - c1 * cs * ci
        # final bearing (reverse azimuth +/- 180)
        r = atan2b(sa, -t)

        if llr:
            # destination latitude in [-90, 90)
            a = degrees90(
                atan2(s1 * cs + c1 * ss * ci, (1 - E.f) * hypot(sa, t)))
            # destination longitude in [-180, 180)
            b = degrees180(
                atan2(ss * si, c1 * cs - s1 * ss * ci) -
                _dl(E.f, c2a, sa, s, cs, ss, c2sm) + radians(self.lon))
            h = self.height if height is None else height
            d = self.classof(a, b, height=h, datum=self.datum)
        else:
            d = None
        return Destination2Tuple(d, r)
示例#7
0
    def __getattr__(name):  # __getattr__ only for Python 3.7+
        # only called once for each undefined pygeodesy attribute
        if name in imports:
            # importlib.import_module() implicitly sets sub-modules
            # on this module as appropriate for direct imports (see
            # note in the _lazy_import.__doc__ above).
            mod, _, attr = imports[name].partition(_DOT_)
            if mod not in imports:
                raise LazyImportError(_no_(_module_), txt=_DOT_(parent, mod))
            imported = import_module(_DOT_(_pygeodesy_, mod), parent)
            pkg = getattr(imported, _p_a_c_k_a_g_e_, None)
            if pkg not in packages:  # invalid package
                raise LazyImportError(_DOT_(mod, _p_a_c_k_a_g_e_), repr(pkg))
            # import the module or module attribute
            if attr:
                imported = getattr(imported, attr, MISSING)
            elif name != mod:
                imported = getattr(imported, name, MISSING)
            if imported is MISSING:
                raise LazyImportError(_no_(_attribute_),
                                      txt=_DOT_(mod, attr or name))

        elif name in (_a_l_l_,):  # XXX '_d_i_r_', '_m_e_m_b_e_r_s_'?
            imported = _ALL_INIT + tuple(imports.keys())
            mod = NN
        else:
            raise LazyImportError(_no_(_module_, _or_, _attribute_),
                                  txt=_DOT_(parent, name))

        setattr(package, name, imported)
        if isLazy > 1:
            z = NN
            if mod and mod != name:
                z = ' from .%s' % (mod,)
            if isLazy > 2:
                try:  # see C{_caller3}
                    _, f, s = _caller3(2)
                    z = '%s by %s line %d' % (z, f, s)
                except ValueError:  # PYCHOK no cover
                    pass
            print('# lazily imported %s%s' % (_DOT_(parent, name), z))

        return imported  # __getattr__
示例#8
0
    def trilaterate5(
            self,
            distance1,
            point2,
            distance2,
            point3,
            distance3,  # PYCHOK signature
            area=False,
            eps=EPS1,
            radius=R_M,
            wrap=False):
        '''B{Not implemented} for C{B{area}=True} or C{B{wrap}=True}
           and falls back to method C{trilaterate} otherwise.

           @return: A L{Trilaterate5Tuple}C{(min, minPoint, max, maxPoint, n)}
                    with a single trilaterated intersection C{minPoint I{is}
                    maxPoint}, C{min I{is} max} the nearest intersection
                    margin and count C{n = 1}.

           @raise IntersectionError: No intersection, trilateration failed.

           @raise NotImplementedError: Keyword argument C{B{area}=True} or
                                       B{C{wrap}=True} not (yet) supported.

           @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}}, B{C{distance3}} or B{C{radius}}.
        '''
        if area or wrap:
            from pygeodesy.named import notImplemented
            notImplemented(self, self.trilaterate5, area=area, wrap=wrap)

        t = _trilaterate(self,
                         distance1,
                         self.others(point2=point2),
                         distance2,
                         self.others(point3=point3),
                         distance3,
                         radius=radius,
                         height=None,
                         useZ=True,
                         LatLon=self.classof)
        # ... and handle B{C{eps}} and C{IntersectionError} as
        # method C{.latlonBase.LatLonBase.trilaterate2}
        d = self.distanceTo(t, radius=radius, wrap=wrap)  # PYCHOK distanceTo
        d = abs(distance1 - d), abs(distance2 - d), abs(distance3 - d)
        d = float(min(d))
        if d < eps:  # min is max, minPoint is maxPoint
            return Trilaterate5Tuple(d, t, d, t, 1)  # n = 1
        t = _SPACE_(_no_(_intersection_),
                    Fmt.PAREN(min.__name__, Fmt.f(d, prec=3)))
        raise IntersectionError(area=area, eps=eps, wrap=wrap, txt=t)
示例#9
0
def elevation2(lat, lon, timeout=2.0):
    '''Get the geoid elevation at an C{NAD83} to C{NAVD88} location.

       @arg lat: Latitude (C{degrees}).
       @arg lon: Longitude (C{degrees}).
       @kwarg timeout: Optional, query timeout (seconds).

       @return: An L{Elevation2Tuple}C{(elevation, data_source)}
                or (C{None, "error"}) in case of errors.

       @raise ValueError: Invalid B{C{timeout}}.

       @note: The returned C{elevation} is C{None} if B{C{lat}} or B{C{lon}}
              is invalid or outside the C{Conterminous US (CONUS)},
              if conversion failed or if the query timed out.  The
              C{error} is the C{HTTP-, IO-, SSL-, Type-, URL-} or
              C{ValueError} as a string (C{str}).

       @see: U{USGS National Map<https://NationalMap.gov/epqs>},
             the U{FAQ<https://www.USGS.gov/faqs/what-are-projection-
             horizontal-and-vertical-datum-units-and-resolution-3dep-standard-dems>},
             U{geoid.py<https://Gist.GitHub.com/pyRobShrk>}, module
             L{geoids}, classes L{GeoidG2012B}, L{GeoidKarney} and
             L{GeoidPGM}.
    '''
    try:
        x = _qURL('https://NED.USGS.gov/epqs/pqs.php',  # 'https://NationalMap.gov/epqs/pqs.php'
                         x=Lon(lon).toStr(prec=6),
                         y=Lat(lat).toStr(prec=6),
                         units='Meters',  # 'Feet', capitalized
                         output=_XML_.lower(),  # _JSON_, lowercase only
                         timeout=Scalar(timeout=timeout))
        if x[:6] == '<?xml ':
            e = _xml('Elevation', x)
            try:
                e = float(e)
                if -1000000 < e < 1000000:
                    return Elevation2Tuple(e, _xml('Data_Source', x))
                e = 'non-CONUS %.2F' % (e,)
            except (TypeError, ValueError):
                pass
        else:
            e = _no_(_XML_, Fmt.QUOTE2(clips(x, limit=128, white=_SPACE_)))
    except (HTTPError, IOError, TypeError, ValueError) as x:
        e = repr(x)
    e = _error(elevation2, lat, lon, e)
    return Elevation2Tuple(None, e)
示例#10
0
def geoidHeight2(lat, lon, model=0, timeout=2.0):
    '''Get the C{NAVD88} geoid height at an C{NAD83} location.

       @arg lat: Latitude (C{degrees}).
       @arg lon: Longitude (C{degrees}).
       @kwarg model: Optional, geoid model ID (C{int}).
       @kwarg timeout: Optional, query timeout (seconds).

       @return: An L{GeoidHeight2Tuple}C{(height, model_name)}
                or C{(None, "error"}) in case of errors.

       @raise ValueError: Invalid B{C{timeout}}.

       @note: The returned C{height} is C{None} if B{C{lat}} or B{C{lon}} is
              invalid or outside the C{Conterminous US (CONUS)}, if the
              B{C{model}} was invalid, if conversion failed or if the
              query timed out.  The C{error} is the C{HTTP-, IO-, SSL-,
              Type-, URL-} or C{ValueError} as a string (C{str}).

       @see: U{NOAA National Geodetic Survey
             <https://www.NGS.NOAA.gov/INFO/geodesy.shtml>},
             U{Geoid<https://www.NGS.NOAA.gov/web_services/geoid.shtml>},
             U{USGS10mElev.py<https://Gist.GitHub.com/pyRobShrk>}, module
             L{geoids}, classes L{GeoidG2012B}, L{GeoidKarney} and
             L{GeoidPGM}.
    '''
    try:
        j = _qURL('https://Geodesy.NOAA.gov/api/geoid/ght',
                         lat=Lat(lat).toStr(prec=6),
                         lon=Lon(lon).toStr(prec=6),
                         model=(model if model else NN),
                         timeout=Scalar(timeout=timeout))  # PYCHOK indent
        if j[:1] == '{' and j[-1:] == '}' and j.find('"error":') > 0:
            d, e = _json(j), 'geoidHeight'
            if isinstance(_xkwds_get(d, error=_n_a_), float):
                h = d.get(e, None)
                if h is not None:
                    m = _xkwds_get(d, geoidModel=_n_a_)
                    return GeoidHeight2Tuple(h, m)
        else:
            e = _JSON_
        e = _no_(e, Fmt.QUOTE2(clips(j, limit=256, white=_SPACE_)))
    except (HTTPError, IOError, ParseError, TypeError, ValueError) as x:
        e = repr(x)
    e = _error(geoidHeight2, lat, lon, e)
    return GeoidHeight2Tuple(None, e)
示例#11
0
def _xml(tag, xml):
    '''(INTERNAL) Get a <tag>value</tag> from XML.
    '''
    # b'<?xml version="1.0" encoding="utf-8" ?>
    #   <USGS_Elevation_Point_Query_Service>
    #    <Elevation_Query x="-121.914200" y="37.881600">
    #     <Data_Source>3DEP 1/3 arc-second</Data_Source>
    #     <Elevation>3851.03</Elevation>
    #     <Units>Feet</Units>
    #    </Elevation_Query>
    #   </USGS_Elevation_Point_Query_Service>'
    i = xml.find(Fmt.TAG(tag))
    if i > 0:
        i += len(tag) + 2
        j = xml.find(Fmt.TAGEND(tag), i)
        if j > i:
            return Str(xml[i:j].strip(), name=tag)
    return _no_(_XML_, Fmt.TAG(tag))
示例#12
0
    def _tanf(self, txi):  # called from .Ellipsoid.auxAuthalic
        '''(INTERNAL) Function M{tan-phi from tan-xi}.
        '''
        tol = _tol(_TOL, txi)

        e2 = self.datum.ellipsoid.e2
        qx = self._qx

        ta = txi
        Ta = Fsum(ta)
        for self._iteration in range(1, _NUMIT):  # max 2, mean 1.99
            # dtxi/dta = (scxi / sca)^3 * 2 * (1 - e^2) / (qZ * (1 - e^2 * sa^2)^2)
            ta2 = ta**2
            sca2 = ta2 + _1_0
            txia = self._txif(ta)
            s3qx = sqrt3(sca2 / (_1_0 + txia**2)) * qx
            ta, d = Ta.fsum2_(
                (txi - txia) * s3qx * (_1_0 - e2 * ta2 / sca2)**2)
            if abs(d) < tol:
                return ta
        raise AlbersError(iteration=_NUMIT, txt=_no_(Fmt.convergence(tol)))
示例#13
0
    def convertRefFrame(self, reframe2):
        '''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
        '''
        from pygeodesy.trf import RefFrame, _reframeTransforms
        _xinstanceof(RefFrame, reframe2=reframe2)

        if not self.reframe:
            t = _SPACE_(_DOT_(repr(self), 'reframe'), MISSING)
            raise TRFError(_no_(_conversion_), txt=t)

        ts = _reframeTransforms(reframe2, self.reframe, self.epoch)
        if ts:
            c = self.toCartesian()
            for t in ts:
                c = c._applyHelmert(t, False)
            ll = c.toLatLon(datum=self.datum,
                            LatLon=self.classof,
                            epoch=self.epoch,
                            reframe=reframe2)
            # ll.reframe, ll.epoch = reframe2, self.epoch
        else:
            ll = self
        return ll
示例#14
0
    def __init__(self, sa1, ca1, sa2, ca2, k, datum, name):
        '''(INTERNAL) New C{AlbersEqualArea...} instance.
        '''
        if datum not in (None, self._datum):
            self._datum = _ellipsoidal_datum(datum, name=name)
        if name:
            self.name = name

        E = self.datum.ellipsoid
        b_a = E.b_a  # fm  = 1 - E.f
        e2 = E.e2
        e12 = E.e12  # e2m = 1 - E.e2

        self._qZ = qZ = _1_0 + e12 * self._atanhee(1)
        self._qZa2 = qZ * E.a2
        self._qx = qZ / (_2_0 * e12)

        c = min(ca1, ca2)
        if c < 0:
            raise AlbersError(clat1=ca1, clat2=ca2)
        polar = c < _EPS__2  # == 0
        # determine hemisphere of tangent latitude
        if sa1 < 0:  # and sa2 < 0:
            self._sign = -1
            # internally, tangent latitude positive
            sa1, sa2 = -sa1, neg(sa2)
        if sa1 > sa2:  # make phi1 < phi2
            sa1, sa2 = sa2, sa1
            ca1, ca2 = ca2, ca1
        if sa1 < 0:  # or sa2 < 0:
            raise AlbersError(slat1=sa1, slat2=sa2)
        # avoid singularities at poles
        ca1, ca2 = max(_EPS__2, ca1), max(_EPS__2, ca2)
        ta1, ta2 = sa1 / ca1, sa2 / ca2

        par1 = abs(ta1 - ta2) < _EPS__4  # ta1 == ta2
        if par1 or polar:
            C, ta0 = _1_0, ta2
        else:
            s1_qZ, C = self._s1_qZ_C2(ca1, sa1, ta1, ca2, sa2, ta2)

            ta0 = (ta2 + ta1) * _0_5
            Ta0 = Fsum(ta0)
            tol = _tol(_TOL0, ta0)
            for self._iteration in range(1, _NUMIT0):
                ta02 = ta0**2
                sca02 = ta02 + _1_0
                sca0 = sqrt(sca02)
                sa0 = ta0 / sca0
                sa01 = sa0 + _1_0
                sa02 = sa0**2
                # sa0m = 1 - sa0 = 1 / (sec(a0) * (tan(a0) + sec(a0)))
                sa0m = _1_0 / (sca0 * (ta0 + sca0))  # scb0^2 * sa0
                g = (_1_0 + (b_a * ta0)**2) * sa0
                dg = e12 * sca02 * (_1_0 + 2 * ta02) + e2
                D = sa0m * (_1_0 - e2 *
                            (_1_0 + sa01 * 2 * sa0)) / (e12 * sa01)  # dD/dsa0
                dD = -2 * (_1_0 - e2 * sa02 *
                           (_3_0 + 2 * sa0)) / (e12 * sa01**2)
                sa02_ = _1_0 - e2 * sa02
                sa0m_ = sa0m / (_1_0 - e2 * sa0)
                BA = sa0m_ * (self._atanhx1(e2 * sa0m_**2) * e12 - e2 * sa0m) \
                   - sa0m**2 * e2 * (2 + (_1_0 + e2) * sa0) / (e12 * sa02_)  # == B + A
                dAB = 2 * e2 * (2 - e2 *
                                (_1_0 + sa02)) / (e12 * sa02_**2 * sca02)
                u_du = fsum_(s1_qZ *  g,  -D,  g * BA) \
                     / fsum_(s1_qZ * dg, -dD, dg * BA, g * dAB)  # == u/du
                ta0, d = Ta0.fsum2_(-u_du * (sca0 * sca02))
                if abs(d) < tol:
                    break
            else:
                raise AlbersError(iteration=_NUMIT0,
                                  txt=_no_(Fmt.convergence(tol)))

        self._txi0 = txi0 = self._txif(ta0)
        self._scxi0 = hypot1(txi0)
        self._sxi0 = sxi0 = txi0 / self._scxi0
        self._m02 = m02 = _1_0 / (_1_0 + (b_a * ta0)**2)
        self._n0 = n0 = ta0 / hypot1(ta0)
        if polar:
            self._polar = True
            self._nrho0 = self._m0 = _0_0
        else:
            self._m0 = sqrt(m02)  # == nrho0 / E.a
            self._nrho0 = E.a * self._m0  # == E.a * sqrt(m02)
        self._k0_(_1_0 if par1 else (k * sqrt(C / (m02 + n0 * qZ * sxi0))))
        self._lat0 = _Lat(lat0=self._sign * atand(ta0))
示例#15
0
def _convergenceError(where, *args):  # PYCHOK no cover
    '''(INTERNAL) Return an L{EllipticError}.
    '''
    t = _SPACE_(where.__name__, repr(args))
    return EllipticError(_no_(_convergence_), txt=t)  # unstr
示例#16
0
    def _inverse(self, other, azis, wrap):
        '''(INTERNAL) Inverse Vincenty method.

           @raise TypeError: The B{C{other}} point is not L{LatLon}.

           @raise ValueError: If this and the B{C{other}} point's L{Datum}
                              ellipsoids are not compatible.

           @raise VincentyError: Vincenty fails to converge for the current
                                 L{LatLon.epsilon} and L{LatLon.iterations}
                                 limit and/or if this and the B{C{other}}
                                 point are coincident or near-antipodal.
        '''
        E = self.ellipsoids(other)

        c1, s1, _ = _r3(self.lat, E.f)
        c2, s2, _ = _r3(other.lat, E.f)

        c1c2, s1c2 = c1 * c2, s1 * c2
        c1s2, s1s2 = c1 * s2, s1 * s2

        dl, _ = unroll180(self.lon, other.lon, wrap=wrap)
        ll = dl = radians(dl)
        for self._iteration in range(1, self._iterations + 1):
            ll_ = ll
            sll, cll = sincos2(ll)

            ss = hypot(c2 * sll, c1s2 - s1c2 * cll)
            if ss < EPS:  # coincident or antipodal, ...
                if self.isantipodeTo(other, eps=self._epsilon):
                    t = '%r %sto %r' % (self, _antipodal_, other)
                    raise VincentyError(_ambiguous_, txt=t)
                # return zeros like Karney, but unlike Veness
                return Distance3Tuple(0.0, 0, 0)

            cs = s1s2 + c1c2 * cll
            s = atan2(ss, cs)

            sa = c1c2 * sll / ss
            c2a = 1 - sa**2
            if abs(c2a) < EPS:
                c2a = 0  # equatorial line
                ll = dl + E.f * sa * s
            else:
                c2sm = cs - 2 * s1s2 / c2a
                ll = dl + _dl(E.f, c2a, sa, s, cs, ss, c2sm)

            if abs(ll - ll_) < self._epsilon:
                break


#           # omitted and applied only after failure to converge below, see footnote
#           # under Inverse at <https://WikiPedia.org/wiki/Vincenty's_formulae>
#           # <https://GitHub.com/ChrisVeness/geodesy/blob/master/latlon-vincenty.js>
#           elif abs(ll) > PI and self.isantipodeTo(other, eps=self._epsilon):
#              raise VincentyError('%s, %r %sto %r' % ('ambiguous', self,
#                                  _antipodal_, other))
        else:
            t = _antipodal_ if self.isantipodeTo(other,
                                                 eps=self._epsilon) else NN
            t = _SPACE_(repr(self), NN(t, _to_), repr(other))
            raise VincentyError(_no_(_convergence_), txt=t)

        if c2a:  # e22 == (a / b)**2 - 1
            A, B = _p2(c2a * E.e22)
            s = A * (s - _ds(B, cs, ss, c2sm))

        b = E.b
        #       if self.height or other.height:
        #           b += self._havg(other)
        d = b * s

        if azis:  # forward and reverse azimuth
            sll, cll = sincos2(ll)
            f = atan2b(c2 * sll, c1s2 - s1c2 * cll)
            r = atan2b(c1 * sll, -s1c2 + c1s2 * cll)
        else:
            f = r = 0
        return Distance3Tuple(d, f, r)
示例#17
0
def _intersects2(
        c1,
        r1,
        c2,
        r2,
        height=None,
        wrap=True,  # MCCABE 17
        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

    from pygeodesy.sphericalTrigonometry import _intersects2 as _si2, LatLon as _LLS
    from pygeodesy.vector3d import _intersects2 as _vi2

    def _latlon4(t, h, n):
        r = _LatLon4Tuple(t.lat, t.lon, h, t.datum, LatLon, LatLon_kwds)
        r._iteration = t.iteration  # ._iteration for tests
        return _xnamed(r, n)

    if r1 < r2:
        c1, c2 = c2, c1
        r1, r2 = r2, r1

    E = c1.ellipsoids(c2)
    if r1 > (min(E.b, E.a) * PI):
        raise ValueError(_exceed_PI_radians_)

    if wrap:  # unroll180 == .karney._unroll2
        c2 = _unrollon(c1, c2)

    # distance between centers and radii are
    # measured along the ellipsoid's surface
    m = c1.distanceTo(c2, wrap=False)  # meter
    if m < max(r1 - r2, EPS):
        raise ValueError(_near_concentric_)
    if fsum_(r1, r2, -m) < 0:
        raise ValueError(_too_(Fmt.distant(m)))

    f = _radical2(m, r1, r2).ratio  # "radical fraction"
    r = E.rocMean(favg(c1.lat, c2.lat, f=f))
    e = max(m2degrees(tol, radius=r), EPS)

    # get the azimuthal equidistant projection
    A = _Equidistant2(equidistant, c1.datum)

    # gu-/estimate initial intersections, spherically ...
    t1, t2 = _si2(_LLS(c1.lat, c1.lon, height=c1.height),
                  r1,
                  _LLS(c2.lat, c2.lon, height=c2.height),
                  r2,
                  radius=r,
                  height=height,
                  wrap=False,
                  too_d=m)
    h, n = t1.height, t1.name

    # ... and then iterate like Karney suggests to find
    # tri-points of median lines, @see: references under
    # method LatLonEllipsoidalBase.intersections2 above
    ts, ta = [], None
    for t in ((t1, ) if t1 is t2 else (t1, t2)):
        p = None  # force first d == p to False
        for i in range(1, _TRIPS):
            A.reset(t.lat, t.lon)  # gu-/estimate as origin
            # convert centers to projection space
            t1 = A.forward(c1.lat, c1.lon)
            t2 = A.forward(c2.lat, c2.lon)
            # compute intersections in projection space
            v1, v2 = _vi2(
                t1,
                r1,  # XXX * t1.scale?,
                t2,
                r2,  # XXX * t2.scale?,
                sphere=False,
                too_d=m)
            # convert intersections back to geodetic
            t1 = A.reverse(v1.x, v1.y)
            d1 = euclid(t1.lat - t.lat, t1.lon - t.lon)
            if v1 is v2:  # abutting
                t, d = t1, d1
            else:
                t2 = A.reverse(v2.x, v2.y)
                d2 = euclid(t2.lat - t.lat, t2.lon - t.lon)
                # consider only the closer intersection
                t, d = (t1, d1) if d1 < d2 else (t2, d2)
            # break if below tolerance or if unchanged
            if d < e or d == p:
                t._iteration = i  # _NamedTuple._iteration
                ts.append(t)
                if v1 is v2:  # abutting
                    ta = t
                break
            p = d
        else:
            raise ValueError(_no_(Fmt.convergence(tol)))

    if ta:  # abutting circles
        r = _latlon4(ta, h, n)
    elif len(ts) == 2:
        return _latlon4(ts[0], h, n), _latlon4(ts[1], h, n)
    elif len(ts) == 1:  # XXX assume abutting
        r = _latlon4(ts[0], h, n)
    else:
        raise _AssertionError(ts=ts)
    return r, r
示例#18
0
def _nearestOn(p,
               p1,
               p2,
               within=True,
               height=None,
               wrap=True,
               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

    from pygeodesy.sphericalNvector import LatLon as _LLS
    from pygeodesy.vector3d import _nearestOn as _vnOn, Vector3d

    def _v(t, h):
        return Vector3d(t.x, t.y, h)

    _ = p.ellipsoids(p1)
    E = p.ellipsoids(p2)

    if wrap:
        p1 = _unrollon(p, p1)
        p2 = _unrollon(p, p2)
        p2 = _unrollon(p1, p2)

    r = E.rocMean(fmean_(p.lat, p1.lat, p2.lat))
    e = max(m2degrees(tol, radius=r), EPS)

    # get the azimuthal equidistant projection
    A = _Equidistant2(equidistant, p.datum)

    # gu-/estimate initial nearestOn, spherically ... wrap=False
    t = _LLS(p.lat, p.lon, height=p.height).nearestOn(_LLS(p1.lat,
                                                           p1.lon,
                                                           height=p1.height),
                                                      _LLS(p2.lat,
                                                           p2.lon,
                                                           height=p2.height),
                                                      within=within,
                                                      height=height)
    n = t.name

    h = h1 = h2 = 0
    if height is False:  # use height as Z component
        h = t.height
        h1 = p1.height
        h2 = p2.height

    # ... and then iterate like Karney suggests to find
    # tri-points of median lines, @see: references under
    # method LatLonEllipsoidalBase.intersections2 above
    c = None  # force first d == c to False
    # closest to origin, .z to interpolate height
    p = Vector3d(0, 0, h)
    for i in range(1, _TRIPS):
        A.reset(t.lat, t.lon)  # gu-/estimate as origin
        # convert points to projection space
        t1 = A.forward(p1.lat, p1.lon)
        t2 = A.forward(p2.lat, p2.lon)
        # compute nearestOn in projection space
        v = _vnOn(p, _v(t1, h1), _v(t2, h2), within=within)
        # convert nearestOn back to geodetic
        r = A.reverse(v.x, v.y)
        d = euclid(r.lat - t.lat, r.lon - t.lon)
        # break if below tolerance or if unchanged
        t = r
        if d < e or d == c:
            t._iteration = i  # _NamedTuple._iteration
            if height is False:
                h = v.z  # nearest interpolated
            break
        c = d
    else:
        raise ValueError(_no_(Fmt.convergence(tol)))

    r = _LatLon4Tuple(t.lat, t.lon, h, t.datum, LatLon, LatLon_kwds)
    r._iteration = t.iteration  # ._iteration for tests
    return _xnamed(r, n)
示例#19
0
def _trilaterate3d2(c1, r1, c2, r2, c3, r3, eps=EPS, Vector=None, **Vector_kwds):  # MCCABE 13
    # (INTERNAL) Intersect three spheres or circles, see L{trilaterate3d2}
    # above, separated to allow callers to embellish any exceptions

    def _0f3d(F):
        # map numpy 4-vector to floats and split
        F0, x, y, z = map(float, F)
        return F0, Vector3d(x, y, z)

    def _N3(t01, x, z):
        # compute x, y and z and return as Vector
        v = x.plus(z.times(t01))
        n = trilaterate3d2.__name__
        return _V_n(v, n, Vector, Vector_kwds)

    def _real_roots(numpy, *coeffs):
        # non-complex roots of a polynomial
        rs = numpy.polynomial.polynomial.polyroots(coeffs)
        return tuple(float(r) for r in rs if not numpy.iscomplex(r))

    def _txt(c1, r1, c2, r2):
        # check for concentric or too distant spheres
        d = c1.minus(c2).length
        if d < abs(r1 - r2):
            t = _near_concentric_
        elif d > (r1 + r2):
            t = _too_(Fmt.distant(d))
        else:
            return NN
        return _SPACE_(c1.name, 'and', c2.name, t)

    np = Vector3d._numpy
    if np is None:  # get numpy, once or ImportError
        Vector3d._numpy = np = _xnumpy(trilaterate3d2, 1, 10)  # macOS' Python 2.7 numpy 1.8 OK

    c2 = _otherV3d(center2=c2)
    c3 = _otherV3d(center3=c3)

    A = []  # 3 x 4
    b = []  # 1 x 3 or 3 x 1
    for c, d in ((c1, r1),
                 (c2, Radius_(radius2=r2, low=eps)),
                 (c3, Radius_(radius3=r3, low=eps))):
        A.append((_1_0, -2 * c.x, -2 * c.y, -2 * c.z))
        b.append(d**2 - c.length2)

    try:  # <https://NumPy.org/doc/stable/reference/generated/numpy.seterr.html>
        e = np.seterr(all='raise')  # throw FloatingPointError for numpy errors

        X = np.dot(np.linalg.pinv(A), b)  # Moore-Penrose pseudo-inverse
        Z, _ = _null_space2(np, A, eps)
        if Z is None:
            t = ()  # coincident, colinear, concentric, etc.
        else:
            X0, x = _0f3d(X)
            Z0, z = _0f3d(Z)
            # quadratic polynomial coefficients, ordered (^0, ^1, ^2)
            t = _real_roots(np, x.length2    - X0,  # fdot(X, -_1_0, *x.xyz)
                                z.dot(x) * 2 - Z0,  # fdot(Z, -_0_5, *x.xyz) * 2
                                z.length2)          # fdot(Z,  _0_0, *z.xyz)

    finally:  # restore numpy error handling
        np.seterr(**e)

    if not t:  # coincident, colinear, too distant, no intersection, etc.
        raise FloatingPointError(_txt(c1, r1, c2, r2) or
                                 _txt(c1, r1, c3, r3) or
                                 _txt(c2, r2, c3, r3) or (_colinear_ if
                                 _iscolinearWith(c1, c2, c3, eps=eps) else
                                 _no_(_intersection_)))
    elif len(t) < 2:  # one intersection
        t *= 2

    v = _N3(t[0], x, z)
    if abs(t[0] - t[1]) < eps:  # abutting
        t = v, v
    else:  # "lowest" intersection first (to avoid test failures)
        u = _N3(t[1], x, z)
        t = (u, v) if u < v else (v, u)
    return t
示例#20
0
def _lazy_import2(_pygeodesy_):  # MCCABE 15
    '''Check for and set up C{lazy import}.

       @arg _pygeodesy_: The name of the package (C{str}) performing
                         the imports, to help facilitate resolving
                         relative imports, usually C{__package__}.

       @return: 2-Tuple C{(package, getattr)} of the importing package
                for easy reference within itself and the callable to
                be set to `__getattr__`.

       @raise LazyImportError: Lazy import not supported or not enabled,
                               an import failed or the package name or
                               module name or attribute name is invalid
                               or does not exist.

       @note: This is the original function U{modutil.lazy_import
              <https://GitHub.com/brettcannon/modutil/blob/master/modutil.py>}
              modified to handle the C{__all__} and C{__dir__} attributes
              and call C{importlib.import_module(<module>.<name>, ...)}
              without causing a C{ModuleNotFoundError}.

       @see: The original U{modutil<https://PyPi.org/project/modutil>} and
             U{PEP 562<https://www.Python.org/dev/peps/pep-0562>}.
    '''
    if _sys.version_info[:2] < (3, 7):  # not supported before 3.7
        t = _no_(_DOT_(_pygeodesy_, _lazy_import2.__name__))
        raise LazyImportError(t, txt=_Python_(_sys))

    import_module, package, parent = _lazy_init3(_pygeodesy_)

    packages = (parent, '__main__', NN, _DOT_(parent, _deprecated_))
    imports  = _all_imports()

    def __getattr__(name):  # __getattr__ only for Python 3.7+
        # only called once for each undefined pygeodesy attribute
        if name in imports:
            # importlib.import_module() implicitly sets sub-modules
            # on this module as appropriate for direct imports (see
            # note in the _lazy_import.__doc__ above).
            mod, _, attr = imports[name].partition(_DOT_)
            if mod not in imports:
                raise LazyImportError(_no_(_module_), txt=_DOT_(parent, mod))
            imported = import_module(_DOT_(_pygeodesy_, mod), parent)
            pkg = getattr(imported, _p_a_c_k_a_g_e_, None)
            if pkg not in packages:  # invalid package
                raise LazyImportError(_DOT_(mod, _p_a_c_k_a_g_e_), repr(pkg))
            # import the module or module attribute
            if attr:
                imported = getattr(imported, attr, MISSING)
            elif name != mod:
                imported = getattr(imported, name, MISSING)
            if imported is MISSING:
                raise LazyImportError(_no_(_attribute_),
                                      txt=_DOT_(mod, attr or name))

        elif name in (_a_l_l_,):  # XXX '_d_i_r_', '_m_e_m_b_e_r_s_'?
            imported = _ALL_INIT + tuple(imports.keys())
            mod = NN
        else:
            raise LazyImportError(_no_(_module_, _or_, _attribute_),
                                  txt=_DOT_(parent, name))

        setattr(package, name, imported)
        if isLazy > 1:
            z = NN
            if mod and mod != name:
                z = ' from .%s' % (mod,)
            if isLazy > 2:
                try:  # see C{_caller3}
                    _, f, s = _caller3(2)
                    z = '%s by %s line %d' % (z, f, s)
                except ValueError:  # PYCHOK no cover
                    pass
            print('# lazily imported %s%s' % (_DOT_(parent, name), z))

        return imported  # __getattr__

    return package, __getattr__  # _lazy_import2