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 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
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 I{radius}, squared). @raise TypeError: Some I{points} are not L{LatLon}. @raise ValueError: Insufficient number of I{points}. @note: The area is based on Karney's U{'Area of a spherical polygon' <http://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 if iterNumpy2(points): 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 else: a1, b1 = points[n - 1].to2ab() s, 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) s.append(atan2(tdb * (ta1 + ta2), 1 + ta1 * ta2)) ta1, b1 = ta2, b2 s = fsum(s) * 2 if isPoleEnclosedBy(points): s = abs(s) - PI2 return abs(s * radius**2)
def isenclosedBy(self, points): '''Check whether a (convex) polygon encloses this point. @param points: The polygon points (L{LatLon}[]). @return: C{True} if the polygon encloses this point, C{False} otherwise. @raise ValueError: Insufficient number of I{points} or non-convex polygon. @raise TypeError: Some I{points} are not L{LatLon}. @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 ''' n, points = self.points2(points, closed=True) n0 = self.toVector3d() if iterNumpy2(points): v1 = points[n - 1].toVector3d() v2 = points[n - 2].toVector3d() gc1 = v2.cross(v1) t0 = gc1.angleTo(n0) > PI_2 for i in range(n): v2 = points[i].toVector3d() gc = v1.cross(v2) v1 = v2 ti = gc.angleTo(n0) > PI_2 if ti != t0: return False # outside if gc1.angleTo(gc, vSign=n0) < 0: raise ValueError('non-convex: %r...' % (points[:2], )) gc1 = gc else: # get great-circle vector for each edge gc, v1 = [], points[n - 1].toVector3d() for i in range(n): v2 = points[i].toVector3d() gc.append(v1.cross(v2)) v1 = v2 # check whether this point on same side of all # polygon edges (to the left or right depending # on anti-/clockwise polygon direction) t0 = gc[0].angleTo(n0) > PI_2 # True if on the right for i in range(1, n): ti = gc[i].angleTo(n0) > PI_2 if ti != t0: # different sides of edge i return False # outside # check for convex polygon (otherwise # the test above is not reliable) gc1 = gc[n - 1] for gc2 in gc: # angle between gc vectors, signed by direction of n0 if gc1.angleTo(gc2, vSign=n0) < 0: raise ValueError('non-convex: %r...' % (points[:2], )) gc1 = gc2 return True # inside