def hausdorff_(model, target, both=False, early=True, seed=None, units='', 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>}. @param model: First set of points (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @param target: Second set of points (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @keyword both: Return the C{directed} (forward only) or the C{symmetric} (combined forward and reverse) C{Hausdorff} distance (C{bool}). @keyword early: Enable or disable U{early breaking<https://Publik.TUWien.ac.AT/ files/PubDat_247739.pdf>} (C{bool}). @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 units: Optional, name of the distance units (C{str}). @keyword distance: Callable returning the distance between a B{C{model}} and B{C{target}} point (signature C{(point1, point2)}). @Keyword 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 TypeError('%s not callable: %r' % ('distance', distance)) if not callable(point): raise TypeError('%s not callable: %r' % ('point', point)) _, ps1 = points2(model, closed=False, Error=HausdorffError) _, ps2 = points2(target, closed=False, Error=HausdorffError) 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 symmetric(self, points, early=True): '''Compute the combined C{forward and reverse Hausdorff} distance. @param points: Second set of points, aka the C{target} (C{LatLon}[], C{Numpy2LatLon}[], C{Tuple2LatLon}[] or C{other}[]). @keyword 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('invalid %s[%s]: %r' % ('corners', n, corners)) self._clipped = self._points = []
def points2(self, points, closed=True): '''Check a polygon represented by points. @param points: The polygon points (C{LatLon}[]) @keyword closed: Optionally, consider the polygon closed, ignoring any duplicate or closing final B{C{points}} (C{bool}). @return: 2-Tuple (number, ...) of points (C{int}, 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, 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 _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 line and closed: 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 polygon(points, closed=True, base=None): '''DEPRECATED, use function L{points2}. ''' from pygeodesy.utily import points2 return points2(points, closed=closed, base=base)