def isenclosedBy(self, points): '''Check whether this point is enclosed by a (convex) polygon. @param points: The polygon points (L{LatLon}[]). @return: C{True} if the polygon encloses this point, C{False} otherwise. @raise TypeError: Some B{C{points}} are not L{LatLon}. @raise ValueError: Insufficient number of B{C{points}}. @example: >>> b = LatLon(45,1), LatLon(45,2), LatLon(46,2), LatLon(46,1) >>> p = LatLon(45.1, 1.1) >>> inside = p.isEnclosedBy(b) # True @JSname: I{enclosedBy}. ''' n, points = self.points2(points, closed=True) # use normal vector to this point for sign of α n0 = self.toNvector() if iterNumpy2(points): def _subtangles(n, points, n0): # iterate vs = n0.minus(points[n - 1].toNvector()) for i in range(n): vs1 = n0.minus(points[i].toNvector()) yield vs.angleTo(vs1, vSign=n0) # PYCHOK false vs = vs1 # sum subtended angles s = fsum(_subtangles(n, points, n0)) else: # get vectors from this to each point vs = [n0.minus(points[i].toNvector()) for i in range(n)] # close the polygon vs.append(vs[0]) # sum subtended angles of each edge (using n0 to determine sign) s = fsum(vs[i].angleTo(vs[i + 1], vSign=n0) for i in range(n)) # Note, this method uses angle summation test: on a plane, # angles for an enclosed point will sum to 360°, angles for # an exterior point will sum to 0°. On a sphere, enclosed # point angles will sum to less than 360° (due to spherical # excess), exterior point angles will be small but non-zero. # XXX are winding number optimisations equally applicable to # spherical surface? return abs(s) > PI
def sumOf(vectors, Vector=Vector3d, **kwds): '''Compute the vectorial sum of several vectors. @param vectors: Vectors to be added (L{Vector3d}[]). @keyword Vector: Optional class for the vectorial sum (L{Vector3d}). @keyword kwds: Optional, additional I{Vector} keyword arguments. @return: Vectorial sum (I{Vector}). @raise ValueError: No I{vectors}. ''' n, vectors = len2(vectors) if n < 1: raise ValueError('no vectors: %r' & (n, )) return Vector(fsum(v.x for v in vectors), fsum(v.y for v in vectors), fsum(v.z for v in vectors), **kwds)
def perimeterOf(points, closed=False, radius=R_M): '''Compute the perimeter of a (spherical) polygon (with great circle arcs joining consecutive points). @param points: The polygon points (L{LatLon}[]). @keyword closed: Optionally, close the polygon (C{bool}). @keyword radius: Optional, mean earth radius (C{meter}). @return: Polygon perimeter (C{meter}, same units as B{C{radius}}). @raise TypeError: Some B{C{points}} are not L{LatLon}. @raise ValueError: Insufficient number of B{C{points}}. @see: L{pygeodesy.perimeterOf}, L{sphericalTrigonometry.perimeterOf} and L{ellipsoidalKarney.perimeterOf}. ''' n, points = _Nvll.points2(points, closed=closed) def _rads(n, points, closed): # angular edge lengths in radians i, m = _imdex2(closed, n) v1 = points[i].toNvector() for i in range(m, n): v2 = points[i].toNvector() yield v1.angleTo(v2) v1 = v2 r = fsum(_rads(n, points, closed)) return r * float(radius)
def perimeterOf(points, closed=False, radius=R_M, wrap=True): '''Compute the perimeter of a polygon/-line defined by an array, list, sequence, set or tuple of points. @param points: The points defining the polygon (L{LatLon}[]). @keyword closed: Optionally, close the polygon/-line (bool). @keyword radius: Optional, mean earth radius (meter). @keyword wrap: Wrap and unroll longitudes (bool). @return: Polygon perimeter (float, same units as radius). @raise TypeError: Some I{points} are not L{LatLon}. @raise ValueError: Insufficient number of I{points}. @note: This perimeter is based on the L{haversine} formula. @see: L{pygeodesy.perimeterOf} and L{ellipsoidalVincenty.perimeterOf}. ''' n, points = _Trll.points(points, closed=closed) def _rads(n, points, closed): # angular edge lengths in radians if closed: m, i = 0, n - 1 else: m, i = 1, 0 a1, b1 = points[i].to2ab() for i in range(m, n): a2, b2 = points[i].to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) yield haversine_(a2, a1, db) a1, b1 = a2, b2 r = fsum(_rads(n, points, closed)) return r * float(radius)
def perimeterOf(points, closed=False, radius=R_M, wrap=True): '''Compute the perimeter of a polygon. @param points: The polygon points (L{LatLon}[]). @keyword closed: Optionally, close the polygon (C{bool}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword wrap: Wrap and unroll longitudes (C{bool}). @return: Polygon perimeter (C{meter}, same units as I{radius}). @raise TypeError: Some I{points} are not L{LatLon}. @raise ValueError: Insufficient number of I{points}. @note: This perimeter is based on the L{haversine} formula. @see: L{pygeodesy.perimeterOf} and L{ellipsoidalKarney.perimeterOf}. ''' n, points = _Trll.points2(points, closed=closed) def _rads(n, points, closed): # angular edge lengths in radians i, m = _imdex2(closed, n) a1, b1 = points[i].to2ab() for i in range(m, n): a2, b2 = points[i].to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) yield haversine_(a2, a1, db) a1, b1 = a2, b2 r = fsum(_rads(n, points, closed)) return r * float(radius)
def isPoleEnclosedBy(points, wrap=False): '''Test whether a pole is enclosed by a polygon defined by a list, sequence, set or tuple of points. @param points: The points defining the polygon (L{LatLon}[]). @keyword wrap: Wrap and unroll longitudes (bool). @return: True if the polygon encloses a pole (bool). @raise ValueError: Insufficient number of I{points}. @raise TypeError: Some I{points} are not L{LatLon}. ''' n, points = _Trll.points(points) def _cds(n, points): # iterate over course deltas p1 = points[n - 1] b1 = p1.initialBearingTo( points[0], wrap=wrap) # XXX p1.finalBearingTo(points[0])? for i in range(n): p2 = points[i] if not p2.equals(p1, EPS): b = p1.initialBearingTo(p2, wrap=wrap) yield wrap180(b - b1) # (b - b1 + 540) % 360 - 180 b2 = p1.finalBearingTo(p2, wrap=wrap) yield wrap180(b2 - b) # (b2 - b + 540) % 360 - 180 p1, b1 = p2, b2 # sum of course deltas around pole is 0° rather than normally ±360° # <http://blog.element84.com/determining-if-a-spherical-polygon-contains-a-pole.html> s = fsum(_cds(n, points)) # XXX fix (intermittant) edge crossing pole - eg (85,90), (85,0), (85,-90) return abs(s) < 90 # "zero-ish"
def _hIDW(self, x, y): # interpolate height at (x, y) radians ws = tuple(self._distances(x, y)) w, h = min(zip(ws, self._hs)) if w > EPS: ws = tuple(w**self._b for w in ws) h = fdot(ws, *self._hs) / fsum(ws) return h
def areaOf(points, radius=R_M, wrap=True): '''Calculate the area of a (spherical) polygon (with great circle arcs joining the points). @param points: The polygon points (L{LatLon}[]). @keyword radius: Optional, mean earth radius (C{meter}). @keyword wrap: Wrap and unroll longitudes (C{bool}). @return: Polygon area (C{meter}, same units as B{C{radius}}, squared). @raise TypeError: Some B{C{points}} are not L{LatLon}. @raise ValueError: Insufficient number of B{C{points}}. @note: The area is based on Karney's U{'Area of a spherical polygon' <https://OSGeo-org.1560.x6.nabble.com/ Area-of-a-spherical-polygon-td3841625.html>}. @see: L{pygeodesy.areaOf}, L{sphericalNvector.areaOf} and L{ellipsoidalKarney.areaOf}. @example: >>> b = LatLon(45, 1), LatLon(45, 2), LatLon(46, 2), LatLon(46, 1) >>> areaOf(b) # 8666058750.718977 >>> c = LatLon(0, 0), LatLon(1, 0), LatLon(0, 1) >>> areaOf(c) # 6.18e9 ''' n, points = _Trll.points2(points, closed=True) # Area method due to Karney: for each edge of the polygon, # # tan(Δλ/2) · (tan(φ1/2) + tan(φ2/2)) # tan(E/2) = ------------------------------------ # 1 + tan(φ1/2) · tan(φ2/2) # # where E is the spherical excess of the trapezium obtained by # extending the edge to the equator-circle vector for each edge def _exs(n, points): # iterate over spherical edge excess a1, b1 = points[n - 1].to2ab() ta1 = tan_2(a1) for i in range(n): a2, b2 = points[i].to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) ta2, tdb = map1(tan_2, a2, db) yield atan2(tdb * (ta1 + ta2), 1 + ta1 * ta2) ta1, b1 = ta2, b2 s = fsum(_exs(n, points)) * 2 if isPoleEnclosedBy(points): s = abs(s) - PI2 return abs(s * radius**2)
def perimeterOf(points, closed=False, adjust=True, radius=R_M, wrap=True): '''Approximate the perimeter of a polygon/-line defined by an array, list, sequence, set or tuple of points. @param points: The points defining the polygon/-line (I{LatLon}[]). @keyword closed: Optionally, close the polygon/-line (bool). @keyword adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude (bool). @keyword radius: Optional, mean earth radius (meter). @keyword wrap: Wrap lat-, wrap and unroll longitudes (bool). @return: Approximate perimeter (meter, same units as I{radius}). @raise TypeError: Some I{points} are not I{LatLon}. @raise ValueError: Insufficient number of I{points}. @note: This perimeter is based on the L{equirectangular_} distance approximation and is ill-suited for regions exceeding several hundred Km or Miles or with near-polar latitudes. @see: L{sphericalTrigonometry.perimeterOf} and L{ellipsoidalVincenty.perimeterOf}. ''' pts = LatLon2psxy(points, closed=closed, radius=None, wrap=False) def _degs(n, pts, closed): # angular edge lengths in degrees u = 0 # previous x2's unroll/wrap if closed: j, i = 0, n - 1 else: j, i = 1, 0 x1, y1, _ = pts[i] for i in range(j, n): x2, y2, _ = pts[i] # apply previous x2's unroll/wrap to new x1 d2, _, _, u = equirectangular_(y1, x1 + u, y2, x2, adjust=adjust, limit=None, wrap=wrap) yield sqrt(d2) x1, y1 = x2, y2 d = fsum(_degs(len(pts), pts, closed)) return radians(d) * float(radius)
def perimeterOf(points, closed=False, adjust=True, radius=R_M, wrap=True): '''Approximate the perimeter of a path or polygon. @param points: The path or polygon points (C{LatLon}[]). @keyword closed: Optionally, close the path or polygon (C{bool}). @keyword adjust: Adjust the wrapped, unrolled longitudinal delta by the cosine of the mean latitude (C{bool}). @keyword radius: Optional, mean earth radius (C{meter}). @keyword wrap: Wrap lat-, wrap and unroll longitudes (C{bool}). @return: Approximate perimeter (C{meter}, same units as I{radius}). @raise TypeError: Some I{points} are not C{LatLon}. @raise ValueError: Insufficient number of I{points}. @note: This perimeter is based on the L{equirectangular_} distance approximation and is ill-suited for regions exceeding several hundred Km or Miles or with near-polar latitudes. @see: L{sphericalTrigonometry.perimeterOf} and L{ellipsoidalKarney.perimeterOf}. ''' pts = LatLon2psxy(points, closed=closed, radius=None, wrap=False) def _degs(n, pts, closed): # angular edge lengths in degrees i, m = _imdex2(closed, n) x1, y1, _ = pts[i] u = 0 # previous x2's unroll/wrap for i in range(m, n): x2, y2, _ = pts[i] w = wrap if (not closed or i < (n - 1)) else False # apply previous x2's unroll/wrap to new x1 _, dy, dx, u = equirectangular_(y1, x1 + u, y2, x2, adjust=adjust, limit=None, wrap=w) yield hypot(dx, dy) x1, y1 = x2, y2 d = fsum(_degs(len(pts), pts, closed)) return degrees2m(d, radius)
def sumOf(nvectors, Vector=Nvector, h=None, **kwds): '''Return the vectorial sum of two or more n-vectors. @param nvectors: Vectors to be added (L{Nvector}[]). @keyword Vector: Optional class for the vectorial sum (L{Nvector}). @keyword kwds: Optional, additional B{C{Vector}} keyword arguments. @keyword h: Optional height, overriding the mean height (C{meter}). @return: Vectorial sum (B{C{Vector}}). @raise ValueError: No B{C{nvectors}}. ''' n, nvectors = len2(nvectors) if n < 1: raise ValueError('no nvectors: %r' & (n, )) if h is None: h = fsum(v.h for v in nvectors) / float(n) return _sumOf(nvectors, Vector=Vector, h=h, **kwds)
def sumOf(nvectors, Vector=Nvector, h=None, **kwds): '''Return the vectorial sum of any number of n-vectors. @param nvectors: Vectors to be added (L{Nvector}[]). @keyword Vector: Optional class for the vectorial sum (L{Nvector}). @keyword kwds: Optional, additional I{Vector} keyword argments. @keyword h: Optional height, overriding the mean height (meter). @return: Vectorial sum (I{Vector}). @raise ValueError: No I{nvectors}. ''' n, nvectors = len2(nvectors) if n < 1: raise ValueError('no nvectors: %r' & (n, )) if h is None: m = fsum(v.h for v in nvectors) / float(n) else: m = h return _sumOf(nvectors, Vector=Vector, h=m, **kwds)
def _area2(points, adjust, wrap): # return the signed area in radians squared # setting radius=1 converts degrees to radians pts = LatLon2psxy(points, closed=True, radius=1, wrap=wrap) def _rads2(n, pts): # trapezoidal areas in rads**2 x1, y1, _ = pts[n - 1] for i in range(n): x2, y2, _ = pts[i] w, x2 = unrollPI(x1, x2, wrap=wrap if i < (n - 1) else False) # approximate trapezoid by a rectangle, adjusting # the top width by the cosine of the latitudinal # average and bottom width by some fudge factor h = (y2 + y1) * 0.5 if adjust: w *= (cos(h) + 1.2876) * 0.5 yield h * w # signed trapezoidal area x1, y1 = x2, y2 return fsum(_rads2(len(pts), pts)), pts
def ispolar(points, wrap=False): '''Check whether a polygon encloses a pole. @param points: The polygon points (C{LatLon}[]). @keyword wrap: Wrap and unroll longitudes (C{bool}). @return: C{True} if a pole is enclosed by the polygon, C{False} otherwise. @raise ValueError: Insufficient number of I{points}. @raise TypeError: Some I{points} are not C{LatLon} or don't have C{bearingTo2}, C{initialBearingTo} and C{finalBearingTo} methods. ''' n, points = points2(points, closed=True) def _cds(n, points): # iterate over course deltas p1 = points[n - 1] try: # LatLon must have initial- and finalBearingTo b1, _ = p1.bearingTo2(points[0], wrap=wrap) except AttributeError: raise TypeError('invalid %s: %r' % ('points', p1)) for i in range(n): p2 = points[i] if not p2.isequalTo(p1, EPS): b, b2 = p1.bearingTo2(p2, wrap=wrap) yield wrap180(b - b1) # (b - b1 + 540) % 360 - 180 yield wrap180(b2 - b) # (b2 - b + 540) % 360 - 180 p1, b1 = p2, b2 # sum of course deltas around pole is 0° rather than normally ±360° # <http://blog.Element84.com/determining-if-a-spherical-polygon-contains-a-pole.html> s = fsum(_cds(n, points)) # XXX fix (intermittant) edge crossing pole - eg (85,90), (85,0), (85,-90) return abs(s) < 90 # "zero-ish"
def areaOf(points, radius=R_M): '''Calculate the area of a (spherical) polygon (with great circle arcs joining consecutive points). @param points: The polygon points (L{LatLon}[]). @keyword radius: Optional, mean earth radius (C{meter}). @return: Polygon area (C{meter}, same units as B{C{radius}}, squared). @raise TypeError: Some B{C{points}} are not L{LatLon}. @raise ValueError: Insufficient number of B{C{points}}. @see: L{pygeodesy.areaOf}, L{sphericalTrigonometry.areaOf} and L{ellipsoidalKarney.areaOf}. @example: >>> b = LatLon(45, 1), LatLon(45, 2), LatLon(46, 2), LatLon(46, 1) >>> areaOf(b) # 8666058750.718977 ''' n, points = _Nvll.points2(points, closed=True) # use vector to 1st point as plane normal for sign of α n0 = points[0].toNvector() if iterNumpy2(points): def _interangles(n, points, n0): # iterate v2 = points[n - 2].toNvector() v1 = points[n - 1].toNvector() gc = v2.cross(v1) for i in range(n): v2 = points[i].toNvector() gc1 = v1.cross(v2) v1 = v2 yield gc.angleTo(gc1, vSign=n0) gc = gc1 # sum interior angles s = fsum(_interangles(n, points, n0)) else: # get great-circle vector for each edge gc, v1 = [], points[n - 1].toNvector() for i in range(n): v2 = points[i].toNvector() gc.append(v1.cross(v2)) # PYCHOK false, does have .cross v1 = v2 gc.append(gc[0]) # XXX needed? # sum interior angles: depending on whether polygon is cw or ccw, # angle between edges is π−α or π+α, where α is angle between # great-circle vectors; so sum α, then take n·π − |Σα| (cannot # use Σ(π−|α|) as concave polygons would fail) s = fsum(gc[i].angleTo(gc[i + 1], vSign=n0) for i in range(n)) # using Girard’s theorem: A = [Σθᵢ − (n−2)·π]·R² # (PI2 - abs(s) == (n*PI - abs(s)) - (n-2)*PI) return abs(PI2 - abs(s)) * radius**2