Пример #1
0
def _streprs(prec, objs, fmt, ints, force, strepr):
    '''(INTERNAL) Helper for C{fstr}, C{pairs}, C{reprs} and C{strs}
    '''
    # <https://docs.Python.org/3/library/stdtypes.html#printf-style-string-formatting>
    if fmt in _FfEeGg:
        fGg = fmt in _Gg
        fmt = NN(_PERCENT_, _DOT_, abs(prec), fmt)

    elif fmt.startswith(_PERCENT_):
        fGg = False
        try:  # to make sure fmt is valid
            f = fmt.replace(_DOTSTAR_, Fmt.DOT(abs(prec)))
            _ = f % (0.0, )
        except (TypeError, ValueError):
            raise _ValueError(fmt=fmt, txt=_not_(repr(_DOTSTAR_)))
        fmt = f

    else:
        raise _ValueError(fmt=fmt, txt=_not_(repr(_Fspec_)))

    for o in objs:
        if force or isinstance(o, float):
            t = fmt % (float(o), )
            if ints and (isint(o, both=True) or  # for ...
                         # corner case testLcc lon0=-96.0
                         t.rstrip(_0_).endswith(_DOT_)):
                t = t.split(_DOT_)[0]
            elif prec > 1:
                t = fstrzs(t, ap1z=fGg)
        elif strepr:
            t = strepr(o)
        else:
            raise _IsnotError(_scalar_, floats=o)
        yield t
Пример #2
0
 def _xtend(self, xTuple, *items):
     '''(INTERNAL) Extend this C{Named-Tuple} with C{items} to an other B{C{xTuple}}.
     '''
     if not (issubclassof(xTuple, _NamedTuple) and
             (len(self._Names_) + len(items)) == len(xTuple._Names_)
             and self._Names_ == xTuple._Names_[:len(self)]):
         c = NN(self.classname, repr(self._Names_))
         x = NN(xTuple.__name__, repr(xTuple._Names_))
         raise TypeError(_SPACE_(c, _vs_, x))
     return self._xnamed(xTuple(*(self + items)))
Пример #3
0
def clips(bstr, limit=50, white=NN):
    '''Clip a string to the given length limit.

       @arg bstr: String (C{bytes} or C{str}).
       @kwarg limit: Length limit (C{int}).
       @kwarg white: Optionally, replace all whitespace (C{str}).

       @return: The clipped or unclipped B{C{bstr}}.
    '''
    if len(bstr) > limit > 8:
        h = limit // 2
        bstr = NN(bstr[:h], type(bstr)('....'), bstr[-h:])
    if white:  # replace whitespace
        bstr = type(bstr)(white).join(bstr.split())
    return bstr
Пример #4
0
def callername(up=1, dflt=NN, source=False):
    '''Get the name of the calling callable.

       @kwarg up: Number of call stack frames up (C{int}).
       @kwarg dflt: Default return value (C{any}).
       @kwarg source: Include source file name and line
                      number (C{bool}).

       @return: Name of the non-internal callable (C{str})
                or B{C{dflt}} if none found.
    '''
    try:
        from sys import _getframe
        for u in range(up, up + 32):
            f = _getframe(u)
            n = f.f_code.co_name
            if n and (n.startswith(_DUNDER_)
                      or not n.startswith(_UNDERSCORE_)):
                if source:
                    from os.path import basename
                    n = NN.join(
                        (n, _AT_, basename(f.f_code.co_filename), _COLON_,
                         str(f.f_lineno)))
                return n
    except (AttributeError, ImportError):
        pass
    return dflt
Пример #5
0
 def utmupsStr(self, B=False):
     '''Get the UTM/UPS zone, band and hemisphere/-pole (C{str}).
     '''
     b = self.band if B else NN
     h = s = self.hemisphere
     if h:
         s = _SPACE_
     return NN(Fmt.zone(self.zone), b, s, h)
Пример #6
0
def fstrzs(efstr, ap1z=False):
    '''Strip trailing zero decimals from a C{float} string.

       @arg efstr: Float with or without exponent (C{str}).
       @kwarg ap1z: Append the decimal point and one zero decimal
                    if the B{C{efstr}} is all digits (C{bool}).

       @return: Float (C{str}).
    '''
    s = efstr.find(_DOT_)
    if s >= 0:
        e = efstr.rfind(Fmt.e)
        if e < 0:
            e = efstr.rfind(Fmt.E)
            if e < 0:
                e = len(efstr)
        s += 2  # keep 1st _DOT_ + _0_
        if s < e and efstr[e - 1] == _0_:
            efstr = NN(efstr[:s], efstr[s:e].rstrip(_0_), efstr[e:])

    elif ap1z:
        # %.G and %.g formats may drop the decimal
        # point and all trailing zeros, ...
        if efstr.isdigit():
            efstr += _DOT_ + _0_  # ... append or ...
        else:  # ... insert one dot and zero
            e = efstr.rfind(Fmt.e)
            if e < 0:
                e = efstr.rfind(Fmt.E)
            if e > 0:
                efstr = NN(efstr[:e], _DOT_, _0_, efstr[e:])

    return efstr
Пример #7
0
    def hStr(self, prec=-2, m=NN):
        '''Return a string for the height B{C{h}}.

           @kwarg prec: Optional number of decimals, unstripped (C{int}).
           @kwarg m: Optional unit of the height (C{str}).

           @see: Function L{hstr}.
        '''
        return NN(self.H, hstr(self.h, prec=prec, m=m))
Пример #8
0
def _clipped_(angle, limit, units):
    '''(INTERNAL) Helper for C{clipDegrees} and C{clipRadians}.
    '''
    c = min(limit, max(-limit, angle))
    if c != angle and _rangerrors:
        t = NN(fstr(angle, prec=6, ints=True), 'beyond',
               copysign(limit, angle), units)
        raise RangeError(t, txt=None)
    return c
Пример #9
0
def encode(lat, lon, precision=3, height=None, radius=None):  # MCCABE 14
    '''Encode a lat-/longitude as a C{georef} of the given precision.

       @arg lat: Latitude (C{degrees}).
       @arg lon: Longitude (C{degrees}).
       @kwarg precision: Optional, the desired C{georef} resolution and length
                         (C{int} 0..11).
       @kwarg height: Optional, height in C{meter}, see U{Designation of area
                      <https://WikiPedia.org/wiki/World_Geographic_Reference_System>}.
       @kwarg radius: Optional, radius in C{meter}, see U{Designation of area
                      <https://WikiPedia.org/wiki/World_Geographic_Reference_System>}.

       @return: The C{georef} (C{str}).

       @raise RangeError: Invalid B{C{lat}} or B{C{lon}}.

       @raise WGRSError: Invalid B{C{precision}}, B{C{height}} or B{C{radius}}.

       @note: The B{C{precision}} value differs from U{Georef<https://
              GeographicLib.SourceForge.io/html/classGeographicLib_1_1Georef.html>}.
              The C{georef} length is M{2 * (precision + 1)} and the
              C{georef} resolution is I{15°} for B{C{precision}} 0, I{1°}
              for 1, I{1′} for 2, I{0.1′} for 3, I{0.01′} for 4, ...
              M{10**(2 - precision)}.
    '''
    def _option(name, m, m2_, K):
        f = Scalar_(m, name=name, Error=WGRSError)
        return '%s%d' % (name[0].upper(), int(m2_(f * K) + 0.5))

    def _pstr(p, x):
        return '%0*d' % (p, x)

    p = _2Precision(precision)

    lat, lon, _ = _2fllh(lat, lon)
    if lat == 90:
        lat *= EPS1_2

    xt, xd, x = _2divmod3(lon, _LonOrig_M_)
    yt, yd, y = _2divmod3(lat, _LatOrig_M_)

    g = _LonTile[xt], _LatTile[yt]
    if p > 0:
        g += _DegChar[xd], _DegChar[yd]
        p -= 1
        if p > 0:
            d = pow(_Base, _MaxPrec - p)
            x = _pstr(p, x // d)
            y = _pstr(p, y // d)
            g += x, y

    if radius is not None:  # R before H
        g += _option(_radius_, radius, m2NM, 1.0),
    if height is not None:  # H is last
        g += _option(_height_, height, m2ft, 1e-3),

    return NN.join(g)  # XXX Georef(''.join(g))
Пример #10
0
    def _DDDMMSS_(strDDDMMSS, suffix, sep, clip):
        S = suffix.upper()
        if isstr(strDDDMMSS):
            t = strDDDMMSS.strip()
            if sep:
                t = t.replace(sep, NN).strip()

            s = t[:1]  # sign or digit
            P = t[-1:]  # compass point, digit or dot

            t = t.lstrip(_PLUSMINUS_).rstrip(S).strip()
            f = t.split(_DOT_)
            d = len(f[0])
            f = NN.join(f)
            if 1 < d < 8 and f.isdigit() and (
                (P in S and s.isdigit()) or
                (P.isdigit() and s in '-0123456789+'  # PYCHOK indent
                 and S in ((_NS_, _EW_) + _WINDS))):
                # check [D]DDMMSS form and compass point
                X = _EW_ if (d & 1) else _NS_
                if not (P in X or (S in X and (P.isdigit() or P == _DOT_))):
                    t = 'DDDMMSS'[d & 1 ^ 1:d | 1], X[:1], X[1:]
                    raise ParseError('form %s applies %s-%s' % t)
                f = 0  # fraction
            else:  # try other forms
                return _DMS2deg(strDDDMMSS, S, sep, clip)

        else:  # float or int to [D]DDMMSS[.fff]
            f = float(strDDDMMSS)
            s = _MINUS_ if f < 0 else NN
            P = _0_  # anything except _SW_
            f, i = modf(abs(f))
            t = Fmt.f(i, prec=0)  # str(i) == 'i.0'
            d = len(t)
            # bump number of digits to match
            # the given, valid compass point
            if S in (_NS_ if (d & 1) else _EW_):
                t = _0_ + t
                d += 1
            #   P = S
            # elif d > 1:
            #   P = (_EW_ if (d & 1) else _NS_)[0]

        if d < 4:  # [D]DD[.ddd]
            if f:
                t = float(t) + f
            t = t, 0, 0
        else:
            f += float(t[d - 2:])
            if d < 6:  # [D]DDMM[.mmm]
                t = t[:d - 2], f, 0
            else:  # [D]DDMMSS[.sss]
                t = t[:d - 4], t[d - 4:d - 2], f
        d = _dms2deg(s, P, *map2(float, t))

        return clipDegrees(d, float(clip)) if clip else d
Пример #11
0
 def _update(self, updated, *attrs):
     '''(INTERNAL) Zap cached instance attributes.
     '''
     if updated and attrs:
         for a in attrs:  # zap attrs to None
             if getattr(self, a, None) is not None:
                 setattr(self, a, None)
             elif not hasattr(self, a):
                 a = NN(_DOT_, a, _SPACE_, _invalid_)
                 raise _AssertionError(a, txt=repr(self))
Пример #12
0
    def __init__(self, Class, *Classes, **name):
        '''New C{_NamedEnum}.

           @arg Class: Initial class or type acceptable as enum
                       values (C{str}).
           @arg Classes: Additional, acceptable classes or types.
        '''
        self._item_Classes = (Class, ) + Classes
        n = name.get(_name_, NN) or NN(Class.__name__, _s_)
        if n and _xvalid(n, _OK=True):
            _Named.name.fset(self, n)  # see _Named.name
Пример #13
0
 def _toStr(self, hemipole, B, cs, prec, sep):
     '''(INTERNAL) Return a string for this ETM/UTM/UPS coordinate.
     '''
     z = NN(Fmt.zone(self.zone), (self.band if B else NN))  # PYCHOK band
     t = (z, hemipole) + _fstrENH2(self, prec, None)[0]
     if cs:
         prec = cs if isint(cs) else 8  # for backward compatibility
         t += (_n_a_ if self.convergence is None else degDMS(
             self.convergence, prec=prec, pos=_PLUS_),
               _n_a_ if self.scale is None else fstr(self.scale, prec=prec))
     return t if sep is None else sep.join(t)
Пример #14
0
def _boolkwds(inst, **name_value_pairs):  # in .frechet, .hausdorff, .heights
    '''(INTERNAL) Set applicable C{bool} properties/attributes.
    '''
    for n, v in name_value_pairs.items():
        b = getattr(inst, n, None)
        if b is None:  # invalid bool attr
            t = _SPACE_(_EQUAL_(n, repr(v)), 'for',
                        inst.__class__.__name__)  # XXX .classname
            raise _AttributeError(t, txt=_not_('applicable'))
        if v in (False, True) and v != b:
            setattr(inst, NN(_UNDER_, n), v)
Пример #15
0
def degDMS(deg, prec=6, s_D=S_DEG, s_M=S_MIN, s_S=S_SEC, neg=_MINUS_, pos=NN):
    '''Convert degrees to a string in degrees, minutes B{I{or}} seconds.

       @arg deg: Value in degrees (C{scalar}).
       @kwarg prec: Optional number of decimal digits (0..9 or
                    C{None} for default).  Trailing zero decimals
                    are stripped for B{C{prec}} values of 1 and
                    above, but kept for negative B{C{prec}}.
       @kwarg s_D: Symbol for degrees (C{str}).
       @kwarg s_M: Symbol for minutes (C{str}) or C{""}.
       @kwarg s_S: Symbol for seconds (C{str}) or C{""}.
       @kwarg neg: Optional sign for negative (C{'-'}).
       @kwarg pos: Optional sign for positive (C{''}).

       @return: I{Either} degrees, minutes B{I{or}} seconds (C{str}).
    '''
    try:
        deg = float(deg)
    except (TypeError, ValueError) as x:
        raise _ValueError(deg=deg, txt=str(x))

    d, s = abs(deg), s_D
    if d < 1:
        if s_M:
            d *= _60_0
            if d < 1 and s_S:
                d *= _60_0
                s = s_S
            else:
                s = s_M
        elif s_S:
            d *= 3600
            s = s_S

    n = neg if deg < 0 else pos
    z = int(prec)
    t = NN(n, Fmt.F(d, prec=abs(z)))
    if z > 1:
        t = fstrzs(t)
    return NN(t, s)
Пример #16
0
def hstr(height, prec=2, fmt=Fmt.h, ints=False, m=NN):
    '''Return a string for the height value.

       @arg height: Height value (C{float}).
       @kwarg prec: The C{float} precision, number of decimal digits (0..9).
                    Trailing zero decimals are stripped if B{C{prec}} is
                    positive, but kept for negative B{C{prec}} values.
       @kwarg fmt: Optional, C{float} format (C{str}).
       @kwarg ints: Optionally, remove the decimal dot for C{int} values (C{bool}).
       @kwarg m: Optional unit of the height (C{str}).
    '''
    h = next(_streprs(prec, (height, ), fmt, ints, True, None))
    return NN(h, str(m)) if m else h
Пример #17
0
 def __call__(self, *name_value_, **name_value):
     '''Format a C{name=value} pair or C{name, value} pair
        or just a single C{value}.
     '''
     for n, v in name_value.items():
         break
     else:
         if len(name_value_) > 1:
             n, v = name_value_[:2]
         elif name_value_:
             n, v = NN, name_value_[0]
         else:
             n, v = NN, MISSING
     t = str.__mod__(self, v)
     return NN(n, t) if n else t
Пример #18
0
def encode(lat, lon, precision=1):  # MCCABE 14
    '''Encode a lat-/longitude as a C{garef} of the given precision.

       @arg lat: Latitude (C{degrees}).
       @arg lon: Longitude (C{degrees}).
       @kwarg precision: Optional, the desired C{garef} resolution
                         and length (C{int} 0..2).

       @return: The C{garef} (C{str}).

       @raise RangeError: Invalid B{C{lat}} or B{C{lon}}.

       @raise GARSError: Invalid B{C{precision}}.

       @note: The C{garef} length is M{precision + 5} and the C{garef}
              resolution is B{30′} for B{C{precision}} 0, B{15′} for 1
              and B{5′} for 2, respectively.
    '''
    def _digit(x, y, m):
        return _Digits[m * (m - y - 1) + x + 1],

    def _str(chars, x, n):
        s, b = [], len(chars)
        for i in range(n):
            x, i = divmod(x, b)
            s.append(chars[i])
        return tuple(reversed(s))

    p = _2Precision(precision)

    lat, lon = _2fll(lat, lon)
    if lat == 90:
        lat *= EPS1_2

    ix, x = _2divmod2(lon, _LonOrig_M_)
    iy, y = _2divmod2(lat, _LatOrig_M_)

    g = _str(_Digits, ix + 1, _LonLen) + _str(_Letters, iy, _LatLen)
    if p > 0:
        ix, x = divmod(x, _M3)
        iy, y = divmod(y, _M3)
        g += _digit(ix, iy, _M2)
        if p > 1:
            g += _digit(x, y, _M3)

    return NN.join(g)
Пример #19
0
def _qURL(url, timeout=2, **params):
    '''(INTERNAL) Build B{C{url}} query, get and verify response.
    '''
    if params:  # build url query, don't map(quote, params)!
        p = '&'.join(Fmt.EQUAL(p, v) for p, v in params.items() if v)
        if p:
            url = NN(url, '?', p)
    u = urlopen(url, timeout=timeout)  # secs

    s = u.getcode()
    if s != 200:  # http.HTTPStatus.OK or http.client.OK
        raise IOError('code %d: %s' % (s, u.geturl()))

    r = u.read()
    u.close()
    # urlcleanup()
    return ub2str(r).strip()
Пример #20
0
def _error_init(Error,
                inst,
                name_value,
                fmt_name_value='%s (%r)',
                txt=_invalid_,
                **name_values):  # by .lazily
    '''(INTERNAL) Format an error text and initialize an C{Error} instance.

       @arg Error: The error super-class (C{Exception}).
       @arg inst: Sub-class instance to be initialized (C{_Exception}).
       @arg name_value: Either just a value or several name, value, ...
                        positional arguments (C{str}, any C{type}), in
                        particular for name conflicts with keyword
                        arguments of C{error_init} or which can't be
                        used as C{name=value} keyword arguments.
       @kwarg name_value_fmt: Format for (name, value) (C{str}).
       @kwarg txt: Optional explanation of the error (C{str}).
       @kwarg name_values: One or more B{C{name=value}} pairs overriding
                           any B{C{name_value}} positional arguments.
    '''
    if name_values:
        t = _or(*(fmt_name_value % t
                  for t in name_values.items()))  # XXX sorted
    elif len(name_value) > 1:
        t = _or(*(fmt_name_value % t
                  for t in zip(name_value[0::2], name_value[1::2])))
    elif name_value:
        t = str(name_value[0])
    else:
        t = '%s %s' % (_Missing, _Missing)

    if txt is None:
        x = NN
    else:
        x = str(txt) or _invalid_
        c = _COMMA_ if _COLON_ in t else _COLON_
        t = NN.join((t, c, _SPACE_, x))
    Error.__init__(inst, t)
    #   inst.__x_txt__ = x  # hold explanation
    _cause_(inst)  # no Python 3+ exception chaining
    _ename_(inst)
Пример #21
0
def callername(up=1, dflt=NN, source=False):
    '''Get the name of the calling callable.

       @kwarg up: Number of call stack frames up (C{int}).
       @kwarg dflt: Default return value (C{any}).
       @kwarg source: Include source file name and line
                      number (C{bool}).

       @return: Name of the non-internal callable (C{str})
                or B{C{dflt}} if none found.
    '''
    try:  # see .lazily._caller3
        for u in range(up, up + 32):
            n, f, s = _caller3(u)
            if n and (n.startswith(_DUNDER_) or not n.startswith(_UNDER_)):
                if source:
                    n = NN(n, _AT_, f, _COLON_, str(s))
                return n
    except (AttributeError, ValueError):  # PYCHOK no cover
        pass
    return dflt
Пример #22
0
    def toStr(self, prec=10, sep=_SPACE_):  # PYCHOK expected
        '''Return a string representation of this MGRS grid reference.

           Note that MGRS grid references are truncated, not rounded
           (unlike UTM coordinates).

           @kwarg prec: Optional number of digits (C{int}), 4:km, 10:m.
           @kwarg sep: Optional separator to join (C{str}) or C{None}
                       to return an unjoined C{tuple} of C{str}s.

           @return: This Mgrs as "00B EN easting northing" (C{str}).

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

           @example:

           >>> m = Mgrs(31, 'DQ', 48251, 11932, band='U')
           >>> m.toStr()  # '31U DQ 48251 11932'
        '''
        t = NN(Fmt.zone(self._zone), self._band)
        t = enstr2(self._easting, self._northing, prec, t, self._en100k)
        return t if sep is None else sep.join(t)
Пример #23
0
    def __init__(self):
        for n, a in self.__class__.__dict__.items():
            if isinstance(a, (Fstr, _Fmt)):
                setattr(a, _name_, n)


Fmt = Fmt()  # PYCHOK singleton
Fmt.__name__ = Fmt.__class__.__name__

_DOTSTAR_ = Fmt.DOT(_STAR_)
# formats %G and %g drop all trailing zeros and the
# decimal point, making the float appear as an int
_Gg = (Fmt.G, Fmt.g)
_FfEeGg = (Fmt.F, Fmt.f, Fmt.E, Fmt.e) + _Gg  # float formats
_Fspec_ = NN('[%[<flags>][<width>]', _DOTSTAR_, ']',
             _BAR_.join(_FfEeGg))  # in testStreprs


def _streprs(prec, objs, fmt, ints, force, strepr):
    '''(INTERNAL) Helper for C{fstr}, C{pairs}, C{reprs} and C{strs}
    '''
    # <https://docs.Python.org/3/library/stdtypes.html#printf-style-string-formatting>
    if fmt in _FfEeGg:
        fGg = fmt in _Gg
        fmt = NN(_PERCENT_, _DOT_, abs(prec), fmt)

    elif fmt.startswith(_PERCENT_):
        fGg = False
        try:  # to make sure fmt is valid
            f = fmt.replace(_DOTSTAR_, Fmt.DOT(abs(prec)))
            _ = f % (0.0, )
Пример #24
0
 def _toRepr(self, value):
     '''(INTERNAL) Representation "<name> (<value>)" or "<classname>(<value>)".
     '''
     t = NN(self.name, _SPACE_) if self.name else self.classname
     return Fmt.PAREN(t, value)
Пример #25
0
def _toDMS(deg, form, prec, sep, ddd, suff):  # MCCABE 15 by .units.py
    '''(INTERNAL) Convert degrees to C{str}, with/-out sign and/or suffix.
    '''
    try:
        deg = float(deg)
    except (TypeError, ValueError) as x:
        raise _ValueError(deg=deg, txt=str(x))

    form = form.lower()
    sign = form[:1]
    if sign in _PLUSMINUS_:
        form = form[1:]
    else:
        sign = S_NUL

    if prec is None:
        z = p = _F_prec.get(form, 6)
    else:
        z = int(prec)
        p = abs(z)
    w = p + (1 if p else 0)
    d = abs(deg)

    if form in _F_symb:
        s_deg = s_min = s_sec = S_NUL  # no symbols
    else:
        s_deg, s_min, s_sec = S_DEG, S_MIN, S_SEC

    F = _F_case.get(form, F_DMS)
    if F is F_DMS:  # 'deg+min+sec'
        d, s = divmod(round(d * _3600_0, p), _3600_0)
        m, s = divmod(s, _60_0)
        t = NN(_0wpF(ddd, 0, d), s_deg, sep, _0wpF(2, 0, m), s_min, sep,
               _0wpF(w + 2, p, s))
        s = s_sec

    elif F is F_DM:  # 'deg+min'
        d, m = divmod(round(d * _60_0, p), _60_0)
        t = NN(_0wpF(ddd, 0, d), s_deg, sep, _0wpF(w + 2, p, m))
        s = s_min

    elif F is F_D:  # 'deg'
        t = _0wpF(ddd + w, p, d)
        s = s_deg

    elif F is F_RAD:
        t = NN(_PERCENTDOTSTAR_, 'F') % (p, radians(d))
        s = S_RAD

    else:  # F in (F__E, F__F, F__G)
        t = NN(_PERCENTDOTSTAR_, F) % (p, d)
        s = S_NUL

    if z > 1:
        t = fstrzs(t, ap1z=F is F__G)

    if sign:
        if deg < 0:
            t = _MINUS_ + t
        elif deg > 0 and sign == _PLUS_:
            t = _PLUS_ + t
    elif suff:  # and deg:  # zero suffix?
        s += sep + suff
    return t + s
Пример #26
0
 def _option(name, m, m2_, K):
     f = Scalar_(m, name=name, Error=WGRSError)
     return NN(name[0].upper(), int(m2_(f * K) + _0_5))
Пример #27
0
def _invokationError(name, *args):  # PYCHOK no cover
    '''(INTERNAL) Return an L{EllipticError}.
    '''
    return EllipticError(_SPACE_('invokation', NN(name, repr(args))))  # unstr
Пример #28
0
def _trilaterate(point1,
                 distance1,
                 point2,
                 distance2,
                 point3,
                 distance3,
                 radius=R_M,
                 height=None,
                 useZ=False,
                 **LatLon_LatLon_kwds):
    # (INTERNAL) Locate a point at given distances from
    # three other points, see LatLon.triangulate above

    def _nd2(p, d, r, _i_, *qs):  # .toNvector and angular distance squared
        for q in qs:
            if p.isequalTo(q, EPS):
                raise _ValueError(points=p, txt=_coincident_)
        return p.toNvector(), (Scalar(d, name=_distance_ + _i_) / r)**2

    r = Radius_(radius)

    n1, r12 = _nd2(point1, distance1, r, _1_)
    n2, r22 = _nd2(point2, distance2, r, _2_, point1)
    n3, r32 = _nd2(point3, distance3, r, _3_, point1, point2)

    # the following uses x,y coordinate system with origin at n1, x axis n1->n2
    y = n3.minus(n1)
    x = n2.minus(n1)
    z = None

    d = x.length  # distance n1->n2
    if d > EPS_2:  # and y.length > EPS_2:
        X = x.unit()  # unit vector in x direction n1->n2
        i = X.dot(y)  # signed magnitude of x component of n1->n3
        Y = y.minus(X.times(i)).unit()  # unit vector in y direction
        j = Y.dot(y)  # signed magnitude of y component of n1->n3
        if abs(j) > EPS_2:
            # courtesy Carlos Freitas <https://GitHub.com/mrJean1/PyGeodesy/issues/33>
            x = fsum_(r12, -r22, d**2) / (2 * d)  # n1->intersection x- and ...
            y = fsum_(r12, -r32, i**2, j**2, -2 * x * i) / (
                2 * j)  # ... y-component
            # courtesy AleixDev <https://GitHub.com/mrJean1/PyGeodesy/issues/43>
            z = fsum_(max(r12, r22, r32), -(x**2),
                      -(y**2))  # XXX not just r12!
            if z > EPS:
                n = n1.plus(X.times(x)).plus(Y.times(y))
                if useZ:  # include Z component
                    Z = X.cross(Y)  # unit vector perpendicular to plane
                    n = n.plus(Z.times(sqrt(z)))
                if height is None:
                    h = fidw((point1.height, point2.height, point3.height),
                             map1(fabs, distance1, distance2, distance3))
                else:
                    h = Height(height)
                kwds = _xkwds(LatLon_LatLon_kwds, height=h)
                return n.toLatLon(**
                                  kwds)  # Nvector(n.x, n.y, n.z).toLatLon(...)

    # no intersection, d < EPS_2 or abs(j) < EPS_2 or z < EPS
    t = NN(_no_, _intersection_, _SPACE_)
    raise IntersectionError(point1=point1,
                            distance1=distance1,
                            point2=point2,
                            distance2=distance2,
                            point3=point3,
                            distance3=distance3,
                            txt=unstr(t, z=z, useZ=useZ))
Пример #29
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)
Пример #30
0
 def _property(method):
     '''(INTERNAL) Return C{method} as documented C{property.getter}.
     '''
     t = 'get and set' if doc.startswith(_SPACE_) else NN
     return property(method, None, None, NN('Property to ', t, doc))