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 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 B{C{points}} or non-convex polygon. @raise TypeError: Some B{C{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
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