def __init__(self, points, tolerance, radius, shortest, indices, **options): '''New C{Simplify} state. ''' n, self.pts = len2(points) if n > 0: self.n = n self.r = {0: True, n - 1: True} # dict to avoid duplicates if isNumpy2(points) or isTuple2(points): # NOT self.pts self.subset = points.subset if indices: self.indices = True if radius: self.radius = float(radius) if self.radius < self.eps: raise _ValueError(radius=radius, txt=_too_(_small_)) if options: self.options = options # tolerance converted to degrees squared self.s2 = degrees(tolerance / self.radius)**2 if min(self.s2, tolerance) < self.eps: raise _ValueError(tolerance=tolerance, txt=_too_(_small_)) self.s2e = self.s2 + 1 # sentinel # compute either the shortest or perpendicular distance self.d2i = self.d2iS if shortest else self.d2iP # PYCHOK false
def fidw(xs, ds, beta=2): '''Interpolate using using U{Inverse Distance Weighting <https://WikiPedia.org/wiki/Inverse_distance_weighting>} (IDW). @arg xs: Known values (C{scalar}[]). @arg ds: Non-negative distances (C{scalar}[]). @kwarg beta: Inverse distance power (C{int}, 0, 1, 2, or 3). @return: Interpolated value C{x} (C{float}). @raise ValueError: Invalid B{C{beta}}, negative B{C{ds}} value, weighted B{C{ds}} below L{EPS} or unequal C{len}C{(}B{C{ds}}C{)} and C{len}C{(}B{C{xs}}C{)}. @note: Using B{C{beta}}C{=0} returns the mean of B{C{xs}}. ''' n, xs = len2(xs) d, ds = len2(ds) if n != d or n < 1: raise LenError(fidw, xs=n, ds=d) d, x = min(zip(ds, xs)) if d > EPS and n > 1: b = -Int_(beta, name=_beta_, low=0, high=3) if b < 0: ds = tuple(d**b for d in ds) d = fsum(ds) if d < EPS: raise _ValueError(ds=d) x = fdot(xs, *ds) / d else: x = fmean(xs) elif d < 0: raise _ValueError(_item_sq('ds', ds.index(d)), d) return x
def fpowers(x, n, alts=0): '''Return a series of powers M{[x**i for i=1..n]}. @arg x: Value (C{scalar}). @arg n: Highest exponent (C{int}). @kwarg alts: Only alternating powers, starting with this exponent (C{int}). @return: Powers of B{C{x}} (C{float}[]). @raise TypeError: Non-scalar B{C{x}} or B{C{n}} not C{int}. @raise ValueError: Non-finite B{C{x}} or non-positive B{C{n}}. ''' if not isfinite(x): raise _ValueError(x=x, txt=_not_(_finite_)) if not isint(n): raise _IsnotError(int.__name__, n=n) elif n < 1: raise _ValueError(n=n) xs = [x] for _ in range(1, n): xs.append(xs[-1] * x) if alts > 0: # x**2, x**4, ... # XXX PyChecker chokes on xs[alts-1::2] xs = xs[slice(alts - 1, None, 2)] # XXX PyChecker claims result is None return xs
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
def __init__(self, x, *cs): '''New L{Fhorner} evaluation of the polynomial M{sum(cs[i] * x**i for i=0..len(cs))}. @arg x: Polynomial argument (C{scalar}). @arg cs: Polynomial coeffients (C{scalar}[]). @raise OverflowError: Partial C{2sum} overflow. @raise TypeError: Non-scalar B{C{x}}. @raise ValueError: No B{C{cs}} coefficients or B{C{x}} is not finite. @see: Function L{fhorner} and methods L{Fsum.fadd} and L{Fsum.fmul}. ''' if not isfinite(x): raise _ValueError(x=x, txt=_not_finite_) if not cs: raise _ValueError(cs=cs, txt=_Missing) x, cs = float(x), list(cs) Fsum.__init__(self, cs.pop()) while cs: self.fmul(x) self.fadd_(cs.pop())
def __init__(self, x, *cs): '''New L{Fpolynomial} evaluation of the polynomial M{sum(cs[i] * x**i for i=0..len(cs))}. @arg x: Polynomial argument (C{scalar}). @arg cs: Polynomial coeffients (C{scalar}[]). @raise OverflowError: Partial C{2sum} overflow. @raise TypeError: Non-scalar B{C{x}}. @raise ValueError: No B{C{cs}} coefficients or B{C{x}} is not finite. @see: Function L{fpolynomial} and method L{Fsum.fadd}. ''' if not isfinite(x): raise _ValueError(x=x, txt=_not_(_finite_)) if not cs: raise _ValueError(cs=cs, txt=MISSING) x, cs, xp = float(x), list(cs), _1_0 Fsum.__init__(self, cs.pop(0)) while cs: xp *= x self.fadd_(xp * cs.pop(0))
def latlon(self, latlonh): '''Set the lat- and longitude and optionally the height. @arg latlonh: New lat-, longitude and height (2- or 3-tuple of C{degrees} and C{meter}). @raise TypeError: Height of B{C{latlonh}} not C{scalar} or B{C{latlonh}} not C{list} or C{tuple}. @raise ValueError: Invalid B{C{latlonh}} or M{len(latlonh)}. @see: Function L{parse3llh} to parse a B{C{latlonh}} string into a 3-tuple (lat, lon, h). ''' _xinstanceof(list, tuple, latlonh=latlonh) if len(latlonh) == 3: h = Height(latlonh[2], name=Fmt.SQUARE(latlonh=2)) elif len(latlonh) != 2: raise _ValueError(latlonh=latlonh) else: h = self._height lat = Lat(latlonh[0]) # parseDMS2(latlonh[0], latlonh[1]) lon = Lon(latlonh[1]) self._update(lat != self._lat or lon != self._lon or h != self._height) self._lat, self._lon, self._height = lat, lon, h
def heightOf(angle, distance, radius=R_M): '''Determine the height above the (spherical) earth after traveling along a straight line at a given tilt. @arg angle: Tilt angle above horizontal (C{degrees}). @arg distance: Distance along the line (C{meter} or same units as B{C{radius}}). @kwarg radius: Optional mean earth radius (C{meter}). @return: Height (C{meter}, same units as B{C{distance}} and B{C{radius}}). @raise ValueError: Invalid B{C{angle}}, B{C{distance}} or B{C{radius}}. @see: U{MultiDop geog_lib.GeogBeamHt<https://GitHub.com/NASA/MultiDop>} (U{Shapiro et al. 2009, JTECH <https://Journals.AMetSoc.org/doi/abs/10.1175/2009JTECHA1256.1>} and U{Potvin et al. 2012, JTECH <https://Journals.AMetSoc.org/doi/abs/10.1175/JTECH-D-11-00019.1>}). ''' r = h = Radius(radius) d = abs(Distance(distance)) if d > h: d, h = h, d if d > EPS: d = d / h # PyChecker chokes on ... /= ... s = sin(Phi_(angle, name=_angle_, clip=180)) s = fsum_(1, 2 * s * d, d**2) if s > 0: return h * sqrt(s) - r raise _ValueError(angle=angle, distance=distance, radius=radius)
def hypot_(*xs): '''Compute the norm M{sqrt(sum(xs[i]**2)) for i=0..len(xs)}. @arg xs: X arguments, positional (C{scalar}[]). @return: Norm (C{float}). @raise OverflowError: Partial C{2sum} overflow. @raise ValueError: Invalid or no B{C{xs}} value. @see: Similar to Python 3.8+ U{math.hypot <https://docs.Python.org/3.8/library/math.html#math.hypot>}, but handling of exceptions, C{nan} and C{infinite} values is different. @note: The Python 3.8+ U{math.dist <https://docs.Python.org/3.8/library/math.html#math.dist>} Euclidian distance between 2 I{n}-dimensional points I{p1} and I{p2} can be computed as M{hypot_(*((c1 - c2) for c1, c2 in zip(p1, p2)))}, provided I{p1} and I{p2} have the same, non-zero length I{n}. ''' if xs: n, xs = len2(xs) if n > 0: h = float(max(abs(x) for x in xs)) if h > 0 and n > 1: X = Fsum(1.0) X.fadd((x / h)**2 for x in xs) h *= sqrt(X.fsum_(-1.0)) return h raise _ValueError(xs=xs, txt=_too_few_)
def __mod__(self, arg, **unused): '''Regular C{%} operator. @arg arg: A C{scalar} value to be formatted (either the C{scalar}, or a 1-tuple C{(scalar,)}, or 2-tuple C{(prec, scalar)}. @raise TypeError: Non-scalar B{C{arg}} value. @raise ValueError: Invalid B{C{arg}}. ''' def _error(arg): n = _DOT_(Fstr.__name__, self.name or self) return _SPACE_(n, _PERCENT_, repr(arg)) prec = 6 # default std %f and %F if isinstance(arg, (tuple, list)): n = len(arg) if n == 1: arg = arg[0] elif n == 2: prec, arg = arg else: raise _ValueError(_error(arg)) if not isscalar(arg): raise _TypeError(_error(arg)) return self(arg, prec=prec)
def fadd(self, iterable): '''Accumulate more values from an iterable. @arg iterable: Sequence, list, tuple, etc. (C{scalar}s). @raise OverflowError: Partial C{2sum} overflow. @raise TypeError: Non-scalar B{C{iterable}} value. @raise ValueError: Invalid or non-finite B{C{iterable}} value. ''' if isscalar(iterable): # for backward compatibility iterable = tuple(iterable) ps = self._ps for a in map(float, iterable): # _iter() if not isfinite(a): raise _ValueError(iterable=a, txt=_not_(_finite_)) i = 0 for p in ps: a, p = _2sum(a, p) if p: ps[i] = p i += 1 ps[i:] = [a] self._n += 1 # assert self._ps is ps self._fsum2_ = None
def _streprs(prec, objs, fmt, ints, force, strepr): '''(INTERNAL) Helper for C{fstr}, C{pairs}, C{reprs} and C{strs} ''' if fmt in _EeFfGg: fGg = fmt in _Gg fmt = '%.' + str(abs(prec)) + fmt elif fmt.startswith(_PERCENT_): fGg = False fmt = fmt.replace(_STAR_, str(abs(prec))) else: t = '[%s]%s' % ('%.*', '|'.join(_EeFfGg)) raise _ValueError(fmt=fmt, txt='not %r' % (t,)) 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
def _triangulate(point1, bearing1, point2, bearing2, height=None, **LatLon_LatLon_kwds): # (INTERNAL)Locate a point given two known points and initial # bearings from those points, see LatLon.triangulate above def _gc(p, b, _i_): n = p.toNvector() de = NorthPole.cross(n, raiser=_pole_).unit() # east vector @ n dn = n.cross(de) # north vector @ n s, c = sincos2d(Bearing(b, name=_bearing_ + _i_)) dest = de.times(s) dnct = dn.times(c) d = dnct.plus(dest) # direction vector @ n return n.cross(d) # great circle point + bearing if point1.isequalTo(point2, EPS): raise _ValueError(points=point2, txt=_coincident_) gc1 = _gc(point1, bearing1, _1_) # great circle p1 + b1 gc2 = _gc(point2, bearing2, _2_) # great circle p2 + b2 n = gc1.cross(gc2, raiser=_points_) # n-vector of intersection point h = point1._havg(point2) if height is None else Height(height) kwds = _xkwds(LatLon_LatLon_kwds, height=h) return n.toLatLon(**kwds) # Nvector(n.x, n.y, n.z).toLatLon(...)
def precision(form, prec=None): '''Set the default precison for a given F_ form. @arg form: L{F_D}, L{F_DM}, L{F_DMS}, L{F_DEG}, L{F_MIN}, L{F_SEC}, L{F__E}, L{F__F}, L{F__G} or L{F_RAD} (C{str}). @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}}. @return: Previous precision (C{int}). @raise ValueError: Invalid B{C{form}} or B{C{prec}} or B{C{prec}} outside valid range. ''' try: p = _F_prec[form] except KeyError: raise _ValueError(form=form) if prec is not None: from pygeodesy.units import Precision_ _F_prec[form] = Precision_(prec=prec, low=-9, high=9) return p
def __init__(self, *args, **kwds): if args: # args override kwds if len(args) != 1: t = unstr(self.classname, *args, **kwds) raise _ValueError(args=len(args), txt=t) kwds = _xkwds(dict(args[0]), **kwds) if _name_ in kwds: _Named.name.fset(self, kwds.pop(_name_)) # see _Named.name dict.__init__(self, kwds)
def splice(iterable, n=2, **fill): '''Split an iterable into C{n} slices. @arg iterable: Items to be spliced (C{list}, C{tuple}, ...). @kwarg n: Number of slices to generate (C{int}). @kwarg fill: Optional fill value for missing items. @return: A generator for each of B{C{n}} slices, M{iterable[i::n] for i=0..n}. @raise ValueError: Invalid B{C{n}}. @note: Each generated slice is a C{tuple} or a C{list}, the latter only if the B{C{iterable}} is a C{list}. @example: >>> from pygeodesy import splice >>> a, b = splice(range(10)) >>> a, b ((0, 2, 4, 6, 8), (1, 3, 5, 7, 9)) >>> a, b, c = splice(range(10), n=3) >>> a, b, c ((0, 3, 6, 9), (1, 4, 7), (2, 5, 8)) >>> a, b, c = splice(range(10), n=3, fill=-1) >>> a, b, c ((0, 3, 6, 9), (1, 4, 7, -1), (2, 5, 8, -1)) >>> tuple(splice(list(range(9)), n=5)) ([0, 5], [1, 6], [2, 7], [3, 8], [4]) >>> splice(range(9), n=1) <generator object splice at 0x0...> ''' if not (isint(n) and n > 0): raise _ValueError(n=n) t = iterable if not isinstance(t, (list, tuple)): t = tuple(t) # force tuple, also for PyPy3 if n > 1: fill = _xkwds_get(fill, fill=_Missing) if fill is not _Missing: m = len(t) % n if m > 0: # fill with same type t += type(t)((fill, )) * (n - m) for i in range(n): yield t[i::n] # slice [i:None:n] pychok -Tb ... else: yield t
def halfs2(str2): '''Split a string in 2 halfs. @arg str2: String to split (C{str}). @return: 2-Tuple (_1st, _2nd) half (C{str}). @raise ValueError: Zero or odd C{len}(B{C{str2}}). ''' h, r = divmod(len(str2), 2) if r or not h: raise _ValueError(str2=str2, txt='odd') return str2[:h], str2[h:]
def sqrt3(x): '''Compute the square root, cubed M{sqrt(x)**3} or M{sqrt(x**3)}. @arg x: Value (C{scalar}). @return: Cubed square root (C{float}). @raise ValueError: Negative B{C{x}}. @see: Functions L{cbrt} and L{cbrt2}. ''' if x < 0: raise _ValueError(x=x) return pow(x, _3_2nd) if x else _0_0
def fmean(xs): '''Compute the accurate mean M{sum(xs[i] for i=0..len(xs)) / len(xs)}. @arg xs: Values (C{scalar}s). @return: Mean value (C{float}). @raise OverflowError: Partial C{2sum} overflow. @raise ValueError: No B{C{xs}} values. ''' n, xs = len2(xs) if n > 0: return fsum(xs) / n raise _ValueError(xs=xs)
def enstr2(easting, northing, prec, *extras): '''Return easting, northing string representations. @arg easting: Easting from false easting (C{meter}). @arg northing: Northing from from false northing (C{meter}). @arg prec: Precision in number of digits (C{int}, [1..5]). @arg extras: Optional leading items (C{str}s). @return: B{C{extras}} + 2-Tuple C{(eastingStr, northingStr)}. @raise ValueError: Invalid B{C{prec}}. ''' w = int(prec) // 2 if not 0 < w < 6: raise _ValueError(prec=prec) p = (1e-4, 1e-3, 1e-2, 1e-1, 1)[w - 1] # 10**(5 - w) return extras + (_0wd(w, int(easting * p)), _0wd(w, int(northing * p)))
def _h_x2(xs): '''(INTERNAL) Helper for L{hypot_} and L{hypot2_}. ''' if xs: n, xs = len2(xs) if n > 0: h = float(max(abs(x) for x in xs)) if h > 0: if n > 1: X = Fsum(_1_0) X.fadd((x / h)**2 for x in xs) x2 = X.fsum_(-_1_0) else: x2 = _1_0 else: h = x2 = _0_0 return h, x2 raise _ValueError(xs=xs, txt=_too_(_few_))
def fmul(self, factor): '''Multiple the current, partial sum by a factor. @arg factor: The multiplier (C{scalar}). @raise TypeError: Non-scalar B{C{factor}}. @raise ValueError: Invalid or non-finite B{C{factor}}. @see: Method L{Fsum.fadd}. ''' if not isfinite(factor): raise _ValueError(factor=factor, txt=_not_(_finite_)) f, ps = float(factor), self._ps if ps: # multiply and adjust partial sums ps[:] = [p * f for p in ps] self.fadd_(ps.pop()) self._n -= 1
def enstr2(easting, northing, prec, *extras): '''Return easting, northing string representations. @arg easting: Easting from false easting (C{meter}). @arg northing: Northing from from false northing (C{meter}). @arg prec: Precision in number of digits (C{int}). @arg extras: Optional leading items (C{str}s). @return: B{C{extras}} + 2-Tuple C{(eastingStr, northingStr)}. @raise ValueError: Invalid B{C{prec}}. ''' w = prec // 2 try: p10 = (1e-4, 1e-3, 1e-2, 1e-1, 1)[w - 1] # 10**(5 - w) except IndexError: raise _ValueError(prec=prec) return extras + ('%0*d' % (w, int(easting * p10)), '%0*d' % (w, int(northing * p10)))
def iterNumpy2over(n=None): '''Get or set the L{iterNumpy2} threshold. @kwarg n: Optional, new threshold (C{int}). @return: Previous threshold (C{int}). @raise ValueError: Invalid B{C{n}}. ''' global _iterNumpy2len p = _iterNumpy2len if n is not None: try: i = int(n) if i > 0: _iterNumpy2len = i else: raise ValueError except (TypeError, ValueError): raise _ValueError(n=n) return p
def compassPoint(bearing, prec=3): '''Convert bearing to a compass point. @arg bearing: Bearing from North (compass C{degrees360}). @kwarg prec: Optional precision (1 for cardinal or basic winds, 2 for intercardinal or ordinal or principal winds, 3 for secondary-intercardinal or half-winds or 4 for quarter-winds). @return: Compass point (1-, 2-, 3- or 4-letter C{str}). @raise ValueError: Invalid B{C{prec}}. @see: U{Dms.compassPoint <https://GitHub.com/chrisveness/geodesy/blob/master/dms.js>} and U{Compass rose<https://WikiPedia.org/wiki/Compass_rose>}. @example: >>> p = compassPoint(24, 1) # 'N' >>> p = compassPoint(24, 2) # 'NE' >>> p = compassPoint(24, 3) # 'NNE' >>> p = compassPoint(24) # 'NNE' >>> p = compassPoint(11, 4) # 'NbE' >>> p = compassPoint(30, 4) # 'NEbN' >>> p = compassPoint(11.249) # 'N' >>> p = compassPoint(11.25) # 'NNE' >>> p = compassPoint(-11.25) # 'N' >>> p = compassPoint(348.749) # 'NNW' ''' try: # m = 2 << prec; x = 32 // m m, x = _MOD_X[prec] except KeyError: raise _ValueError(prec=prec) # not round(), i.e. half-even rounding in Python 3, # but round-away-from-zero as int(b + 0.5) iff b is # non-negative, otherwise int(b + copysign(_0_5, b)) q = int((bearing % _360_0) * m / _360_0 + _0_5) % m return _WINDS[q * x]
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)
def _geodesic(datum, points, closed, line, wrap): # Compute the area or perimeter of a polygon, # using the geographiclib package, iff installed g = datum.ellipsoid.geodesic if not wrap: # capability LONG_UNROLL can't be off raise _ValueError(wrap=wrap) _, points = points2(points, closed=closed) # base=LatLonEllipsoidalBase(0, 0) g = g.Polygon(line) # note, lon deltas are unrolled, by default for p in points: g.AddPoint(p.lat, p.lon) if closed and line: p = points[0] g.AddPoint(p.lat, p.lon) # g.Compute returns (number_of_points, perimeter, signed area) return g.Compute(False, True)[1 if line else 2]
def unregister(self, name_or_item): '''Remove a registered item. @arg name_or_item: Name (C{str}) of or the item (any C{type}). @return: The unregistered item. @raise NameError: No item with that B{C{name}}. @raise ValueError: No such item. ''' name = self.find(name_or_item) if name is None: if not isstr(name_or_item): raise _ValueError(name_or_item=name_or_item) name = name_or_item try: item = dict.pop(self, name) except KeyError: raise _NameError(item=self._dot_(name), txt=_doesn_t_exist_) item._enum = None return item
def _validate(self, _OK=False): # see .EcefMatrix '''(INTERNAL) One-time check of C{_Names_} and C{_Units_} for each C{_NamedUnit} I{sub-class separately}. ''' ns = self._Names_ if not (isinstance(ns, tuple) and len(ns) > 1): # XXX > 0 raise _TypeError(_DOT_(self.classname, _Names_), ns) for i, n in enumerate(ns): if not _xvalid(n, _OK=_OK): t = Fmt.SQUARE(_Names_, i) raise _ValueError(_DOT_(self.classname, t), n) us = self._Units_ if not isinstance(us, tuple): raise _TypeError(_DOT_(self.classname, _Units_), us) if len(us) != len(ns): raise LenError(self.__class__, _Units_=len(us), _Names_=len(ns)) for i, u in enumerate(us): if not (u is None or callable(u)): t = Fmt.SQUARE(_Units_, i) raise _TypeError(_DOT_(self.classname, t), u) self.__class__._validated = True
def parse3d(str3d, sep=_COMMA_, name=NN, Vector=Vector3d, **Vector_kwds): '''Parse an C{"x, y, z"} string. @arg str3d: X, y and z values (C{str}). @kwarg sep: Optional separator (C{str}). @kwarg name: Optional instance name (C{str}). @kwarg Vector: Optional class (L{Vector3d}). @kwarg Vector_kwds: Optional B{C{Vector}} keyword arguments, ignored if C{B{Vector}=None}. @return: New B{C{Vector}} or if B{C{Vector}} is C{None}, a L{Vector3Tuple}C{(x, y, z)}. @raise VectorError: Invalid B{C{str3d}}. ''' try: v = [float(v.strip()) for v in str3d.split(sep)] n = len(v) if n != 3: raise _ValueError(len=n) except (TypeError, ValueError) as x: raise VectorError(str3d=str3d, txt=str(x)) return _V_n(Vector3Tuple(*v), name, Vector, Vector_kwds)