def __init__(self, points, seed=None, name='', units='', **wrap_adjust): '''New C{Hausdorff...} calculator. @arg points: Initial set of points, aka the C{model} or C{template} (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @kwarg seed: Random sampling seed (C{any}) or C{None}, C{0} or C{False} for no U{random sampling<https:// Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}. @kwarg name: Optional name for this interpolator (C{str}). @kwarg units: Optional, distance units (C{str}). @kwarg wrap_adjust: Optionally, C{wrap} and unroll longitudes, iff applicable (C{bool}) and C{adjust} wrapped, unrolled longitudinal delta by the cosine of the mean latitude, iff applicable. @raise HausdorffError: Insufficient number of B{C{points}} or invalid B{C{seed}} or B{{wrap}} or B{C{ajust}} not applicable. ''' _, self._model = points2(points, closed=False, Error=HausdorffError) if seed: self.seed = seed if name: self.name = name if units and not self.units: self.units = units if wrap_adjust: _bkwds(self, Error=HausdorffError, **wrap_adjust)
def hausdorff_(model, target, both=False, early=True, seed=None, units=NN, distance=None, point=_point): '''Compute the C{directed} or C{symmetric} U{Hausdorff distance<https:// WikiPedia.org/wiki/Hausdorff_distance>} between 2 sets of points with or without U{early breaking<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>} and U{random sampling<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}. @arg model: First set of points (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @arg target: Second set of points (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @kwarg both: Return the C{directed} (forward only) or the C{symmetric} (combined forward and reverse) C{Hausdorff} distance (C{bool}). @kwarg early: Enable or disable U{early breaking<https://Publik.TUWien.ac.AT/ files/PubDat_247739.pdf>} (C{bool}). @kwarg seed: Random sampling seed (C{any}) or C{None}, C{0} or C{False} for no U{random sampling<https://Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}. @kwarg units: Optional, the distance units (C{Unit} or C{str}). @kwarg distance: Callable returning the distance between a B{C{model}} and B{C{target}} point (signature C{(point1, point2)}). @kwarg point: Callable returning the B{C{model}} or B{C{target}} point suitable for B{C{distance}} (signature C{(point)}). @return: A L{Hausdorff6Tuple}C{(hd, i, j, mn, md, units)}. @raise HausdorffError: Insufficient number of B{C{model}} or B{C{target}} points. @raise TypeError: If B{C{distance}} or B{C{point}} is not callable. ''' if not callable(distance): raise _IsnotError(callable.__name__, distance=distance) if not callable(point): raise _IsnotError(callable.__name__, point=point) _, ps1 = points2(model, closed=False, Error=HausdorffError) # PYCHOK non-sequence _, ps2 = points2(target, closed=False, Error=HausdorffError) # PYCHOK non-sequence return _hausdorff_(ps1, ps2, both, early, seed, units, distance, point)
def clipCS3(points, lowerleft, upperright, closed=False, inull=False): # MCCABE 25 '''Clip a path against a rectangular clip box using the U{Cohen-Sutherland <https://WikiPedia.org/wiki/Cohen-Sutherland_algorithm>} algorithm. @param points: The points (C{LatLon}[]). @param lowerleft: Bottom-left corner of the clip box (C{LatLon}). @param upperright: Top-right corner of the clip box (C{LatLon}). @keyword closed: Optionally, close the path (C{bool}). @keyword inull: Optionally, include null edges if inside (C{bool}). @return: Yield a L{ClipCS3Tuple}C{(start, end, index)} for each edge of the clipped path. @raise ValueError: The B{C{lowerleft}} corner is not below and/or not to the left of the B{C{upperright}} corner. ''' cs = _CS(lowerleft, upperright) n, points = points2(points, closed=closed) i, m = _imdex2(closed, n) cmbp = cs.code4(points[i]) for i in range(m, n): c1, m1, b1, p1 = cmbp c2, m2, b2, p2 = cmbp = cs.code4(points[i]) if c1 & c2: # edge outside continue if not cs.edge(p1, p2): if inull: # null edge if not c1: yield ClipCS3Tuple(p1, p1, i) elif not c2: yield ClipCS3Tuple(p2, p2, i) continue for _ in range(5): if c1: # clip p1 c1, m1, b1, p1 = m1(b1, p1) elif c2: # clip p2 c2, m2, b2, p2 = m2(b2, p2) else: # inside if inull or _neq(p1, p2): yield ClipCS3Tuple(p1, p2, i) break if c1 & c2: # edge outside break else: # should never get here raise AssertionError('clipCS3.for _')
def _points2(points, closed, inull): '''(INTERNAL) Get the points to clip. ''' if closed and inull: n, pts = len2(points) # only remove the final, closing point if n > 1 and _eq(pts[n - 1], pts[0]): n -= 1 pts = pts[:n] if n < 3: raise PointsError('too few %s: %s' % ('points', n)) else: n, pts = points2(points, closed=closed) return n, list(pts)
def _pts2(points, closed, inull): '''(INTERNAL) Get the points to clip. ''' if closed and inull: n, pts = len2(points) # only remove the final, closing point if n > 1 and _eq(pts[n - 1], pts[0]): n -= 1 pts = pts[:n] if n < 2: raise PointsError(points=n, txt=_too_(_few_)) else: n, pts = points2(points, closed=closed) return n, list(pts)
def symmetric(self, points, early=True): '''Compute the combined C{forward and reverse Hausdorff} distance. @arg points: Second set of points, aka the C{target} (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @kwarg early: Enable or disable U{early breaking<https:// Publik.TUWien.ac.AT/files/PubDat_247739.pdf>} (C{bool}). @return: A L{Hausdorff6Tuple}C{(hd, i, j, mn, md, units)}. @raise HausdorffError: Insufficient number of B{C{points}}. ''' _, ps2 = points2(points, closed=False, Error=HausdorffError) return _hausdorff_(self._model, ps2, True, early, self.seed, self.units, self.distance, self.point)
def __init__(self, corners): n = '' try: n, cs = len2(corners) if n == 2: # make a box b, l, t, r = boundsOf(cs, wrap=False) cs = LL_(b, l), LL_(t, l), LL_(t, r), LL_(b, r) n, cs = points2(cs, closed=True) self._corners = cs = cs[:n] self._nc = n self._cw = 1 if isclockwise(cs, adjust=False, wrap=False) else -1 if self._cw != isconvex_(cs, adjust=False, wrap=False): raise ValueError except ValueError: raise ValueError('%s[%s] invalid: %r' % ('corners', n, corners)) self._clipped = self._points = []
def points2(self, points, closed=True): '''Check a path or polygon represented by points. @param points: The path or polygon points (C{LatLon}[]) @keyword closed: Optionally, consider the polygon closed, ignoring any duplicate or closing final B{C{points}} (C{bool}). @return: A L{Points2Tuple}C{(number, points)}, C{int} and C{list} or C{tuple}. @raise TypeError: Some B{C{points}} are not C{LatLon}. @raise ValueError: Insufficient number of B{C{points}}. ''' return points2(points, closed=closed, base=self)
def __init__(self, corners, name): n, cs = 0, corners try: # check the clip box/region n, cs = len2(cs) if n == 2: # make a box b, l, t, r = boundsOf(cs, wrap=False) cs = LL_(b, l), LL_(t, l), LL_(t, r), LL_(b, r) n, cs = points2(cs, closed=True) self._cs = cs = cs[:n] self._nc = n self._cw = 1 if isclockwise(cs, adjust=False, wrap=False) else -1 if self._cw != isconvex_(cs, adjust=False, wrap=False): raise ClipError except (ClipError, PointsError, TypeError, ValueError): raise ClipError(n, cs, name) self._name = name
def __init__(self, corners, name=__name__): n, cs = 0, corners try: # check the clip box/region n, cs = len2(cs) if n == 2: # make a box b, l, t, r = boundsOf(cs, wrap=False) cs = LL_(b, l), LL_(t, l), LL_(t, r), LL_(b, r) n, cs = points2(cs, closed=True) self._cs = cs = cs[:n] self._nc = n self._cw = isconvex_(cs, adjust=False, wrap=False) if not self._cw: raise ValueError(_not_convex_) if areaOf(cs, adjust=True, radius=1, wrap=True) < EPS: raise ValueError('near-zero area') except (PointsError, TypeError, ValueError) as x: raise ClipError(name, n, cs, txt=str(x)) self.name = name
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('%s invalid: %s' % ('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 __init__(self, points, seed=None, name='', units=''): '''New L{Hausdorff} calculator. @param points: Initial set of points, aka the C{model} or C{template} (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @keyword seed: Random sampling seed (C{any}) or C{None}, C{0} or C{False} for no U{random sampling<https:// Publik.TUWien.ac.AT/files/PubDat_247739.pdf>}. @keyword name: Optional calculator name (C{str}). @keyword units: Optional, distance units (C{str}). @raise HausdorffError: Insufficient number of B{C{points}} or invalid B{C{seed}}. ''' _, self._model = points2(points, closed=False, Error=HausdorffError) if seed: self.seed = seed if name: self.name = name if units: self.units = units
def _points2(self, points): '''(INTERNAL) Check a set of points. ''' return points2(points, closed=False, Error=HausdorffError)