def initialBearingTo(self, other, wrap=False): '''Compute the initial bearing (aka forward azimuth) from this to an other point. @param other: The other point (L{LatLon}). @keyword wrap: Wrap and unroll longitudes (bool). @return: Initial bearing (compass degrees). @raise TypeError: The I{other} point is not L{LatLon}. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351) >>> b = p1.initialBearingTo(p2) # 156.2 @JSname: I{bearingTo}. ''' self.others(other) a1, b1 = self.to2ab() a2, b2 = other.to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) ca1, ca2, cdb = map1(cos, a1, a2, db) sa1, sa2, sdb = map1(sin, a1, a2, db) # see <http://mathforum.org/library/drmath/view/55417.html> x = ca1 * sa2 - sa1 * ca2 * cdb y = sdb * ca2 return degrees360(atan2(y, x))
def distanceTo(self, other, radius=R_M, wrap=False): '''Compute the distance from this to an other point. @param other: The other point (L{LatLon}). @keyword radius: Optional, mean earth radius (meter). @keyword wrap: Wrap and unroll longitudes (bool). @return: Distance between this and the I{other} point (in the same units as radius). @raise TypeError: The I{other} point is not L{LatLon}. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351); >>> d = p1.distanceTo(p2) # 404300 ''' self.others(other) a1, b1 = self.to2ab() a2, b2 = other.to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) r = haversine_(a2, a1, db) return r * float(radius)
def crossingParallels(self, other, lat, wrap=False): '''Return the pair of meridians at which a great circle defined by this and an other point crosses the given latitude. @param other: The other point defining great circle (L{LatLon}). @param lat: Latitude at the crossing (degrees). @keyword wrap: Wrap and unroll longitudes (bool). @return: 2-Tuple (lon1, lon2) in (degrees180) or None if the great circle doesn't reach the given I{lat}. ''' self.others(other) a1, b1 = self.to2ab() a2, b2 = other.to2ab() a = radians(lat) db, b2 = unrollPI(b1, b2, wrap=wrap) ca, ca1, ca2, cdb = map1(cos, a, a1, a2, db) sa, sa1, sa2, sdb = map1(sin, a, a1, a2, db) x = sa1 * ca2 * ca * sdb y = sa1 * ca2 * ca * cdb - ca1 * sa2 * ca z = ca1 * ca2 * sa * sdb h = hypot(x, y) if h < EPS or abs(z) > h: return None # great circle doesn't reach latitude m = atan2(-y, x) + b1 # longitude at max latitude d = acos(z / h) # delta longitude to intersections return degrees180(m - d), degrees180(m + d)
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
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
def intermediateTo(self, other, fraction, height=None, wrap=False): '''Locate the point at given fraction between this and an other point. @param other: The other point (L{LatLon}). @param fraction: Fraction between both points (float, 0.0 = this point, 1.0 = the other point). @keyword height: Optional height, overriding the fractional height (meter). @keyword wrap: Wrap and unroll longitudes (bool). @return: Intermediate point (L{LatLon}). @raise TypeError: The I{other} point is not L{LatLon}. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351) >>> p = p1.intermediateTo(p2, 0.25) # 51.3721°N, 000.7073°E @JSname: I{intermediatePointTo}. ''' self.others(other) a1, b1 = self.to2ab() a2, b2 = other.to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) r = haversine_(a2, a1, db) if r > EPS: cb1, cb2, ca1, ca2 = map1(cos, b1, b2, a1, a2) sb1, sb2, sa1, sa2, sr = map1(sin, b1, b2, a1, a2, r) A = sin((1 - fraction) * r) / sr B = sin(fraction * r) / sr x = A * ca1 * cb1 + B * ca2 * cb2 y = A * ca1 * sb1 + B * ca2 * sb2 z = A * sa1 + B * sa2 a = atan2(z, hypot(x, y)) b = atan2(y, x) else: # points too close a = favg(a1, a2, f=fraction) b = favg(b1, b2, f=fraction) if height is None: h = self._havg(other, f=fraction) else: h = height return self.classof(degrees90(a), degrees180(b), height=h)
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) # 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
def distance3(self, other, radius=R_M, wrap=False): '''Compute the great-circle distance between this and an other geohash using the U{Haversine <http://www.movable-type.co.uk/scripts/latlong.html>} formula. @param other: The other geohash (L{Geohash}). @keyword radius: Optional, mean earth radius (meter). @keyword wrap: Wrap and unroll longitudes (bool). @return: Great-circle distance (meter, same units as I{radius}). @raise TypeError: The I{other} is not a L{Geohash}, I{LatLon} or str. ''' other = _2Geohash(other) a1, b1 = self.ab a2, b2 = other.ab db, b2 = unrollPI(b1, b2, wrap=wrap) return haversine_(a2, a1, db) * float(radius)
def midpointTo(self, other, height=None, wrap=False): '''Find the midpoint between this and an other point. @param other: The other point (L{LatLon}). @keyword height: Optional height for midpoint, overriding the mean height (meter). @keyword wrap: Wrap and unroll longitudes (bool). @return: Midpoint (L{LatLon}). @raise TypeError: The I{other} point is not L{LatLon}. @example: >>> p1 = LatLon(52.205, 0.119) >>> p2 = LatLon(48.857, 2.351) >>> m = p1.midpointTo(p2) # '50.5363°N, 001.2746°E' ''' self.others(other) # see <http://mathforum.org/library/drmath/view/51822.html> a1, b1 = self.to2ab() a2, b2 = other.to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) ca1, ca2, cdb = map1(cos, a1, a2, db) sa1, sa2, sdb = map1(sin, a1, a2, db) x = ca2 * cdb + ca1 y = ca2 * sdb a = atan2(sa1 + sa2, hypot(x, y)) b = atan2(y, x) + b1 if height is None: h = self._havg(other) else: h = height return self.classof(degrees90(a), degrees180(b), height=h)
def intersection(start1, bearing1, start2, bearing2, height=None, wrap=False, LatLon=LatLon): '''Compute the intersection point of two paths each defined by a start point and an initial bearing. @param start1: Start point of first path (L{LatLon}). @param bearing1: Initial bearing from start1 (compass degrees). @param start2: Start point of second path (L{LatLon}). @param bearing2: Initial bearing from start2 (compass degrees). @keyword height: Optional height for the intersection point, overriding the mean height (meter). @keyword wrap: Wrap and unroll longitudes (bool). @keyword LatLon: Optional LatLon class for the intersection point (L{LatLon}) or None. @return: Intersection point (L{LatLon}) or 3-tuple (lat, lon, height) if I{LatLon} is None. @raise TypeError: Point I{start1} or I{start2} is not L{LatLon}. @raise ValueError: Intersection is ambiguous or infinite or the paths are parallel or coincide. @example: >>> p = LatLon(51.8853, 0.2545) >>> s = LatLon(49.0034, 2.5735) >>> i = intersection(p, 108.547, s, 32.435) # '50.9078°N, 004.5084°E' ''' _Trll.others(start1, name='start1') _Trll.others(start2, name='start2') # see <http://www.edwilliams.org/avform.htm#Intersection> a1, b1 = start1.to2ab() a2, b2 = start2.to2ab() db, b2 = unrollPI(b1, b2, wrap=wrap) r12 = haversine_(a2, a1, db) if abs(r12) < EPS: raise ValueError('intersection %s: %r vs %r' % ('parallel', start1, start2)) ca1, ca2 = map1(cos, a1, a2) sa1, sa2, sr12 = map1(sin, a1, a2, r12) x1, x2 = (sr12 * ca1), (sr12 * ca2) if min(abs(x1), abs(x2)) < EPS: raise ValueError('intersection %s: %r vs %r' % ('parallel', start1, start2)) cr12 = cos(r12) t1, t2 = map1(acos, (sa2 - sa1 * cr12) / x1, (sa1 - sa2 * cr12) / x2) if sin(db) > 0: t12, t21 = t1, PI2 - t2 else: t12, t21 = PI2 - t1, t2 t13, t23 = map1(radians, bearing1, bearing2) x1, x2 = map1( wrapPI, t13 - t12, # angle 2-1-3 t21 - t23) # angle 1-2-3 sx1, sx2 = map1(sin, x1, x2) if sx1 == 0 and sx2 == 0: raise ValueError('intersection %s: %r vs %r' % ('infinite', start1, start2)) sx3 = sx1 * sx2 if sx3 < 0: raise ValueError('intersection %s: %r vs %r' % ('ambiguous', start1, start2)) cx1, cx2 = map1(cos, x1, x2) x3 = acos(cr12 * sx3 - cx2 * cx1) r13 = atan2(sr12 * sx3, cx2 + cx1 * cos(x3)) a, b = _destination2(a1, b1, r13, t13) h = start1._havg(start2) if height is None else height return (a, b, h) if LatLon is None else LatLon(a, b, height=h)
def areaOf(points, radius=R_M, wrap=True): '''Calculate the area of a spherical polygon where the sides of the polygon are great circle arcs joining the points. @param points: The points defining the polygon (L{LatLon}[]). @keyword radius: Optional, mean earth radius (meter). @keyword wrap: Wrap and unroll longitudes (bool). @return: Polygon area (float, same units as 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 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{ellipsoidalVincenty.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.points(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)