1 """Define the :class:`~geographiclib.geodesicline.GeodesicLine` class
3 The constructor defines the starting point of the line. Points on the
6 * :meth:`~geographiclib.geodesicline.GeodesicLine.Position` position
7 given in terms of distance
8 * :meth:`~geographiclib.geodesicline.GeodesicLine.ArcPosition` position
9 given in terms of spherical arc length
11 A reference point 3 can be defined with
13 * :meth:`~geographiclib.geodesicline.GeodesicLine.SetDistance` set
14 position of 3 in terms of the distance from the starting point
15 * :meth:`~geographiclib.geodesicline.GeodesicLine.SetArc` set
16 position of 3 in terms of the spherical arc length from the starting point
18 The object can also be constructed by
20 * :meth:`Geodesic.Line <geographiclib.geodesic.Geodesic.Line>`
21 * :meth:`Geodesic.DirectLine <geographiclib.geodesic.Geodesic.DirectLine>`
22 * :meth:`Geodesic.ArcDirectLine
23 <geographiclib.geodesic.Geodesic.ArcDirectLine>`
24 * :meth:`Geodesic.InverseLine <geographiclib.geodesic.Geodesic.InverseLine>`
26 The public attributes for this class are
28 * :attr:`~geographiclib.geodesicline.GeodesicLine.a`
29 :attr:`~geographiclib.geodesicline.GeodesicLine.f`
30 :attr:`~geographiclib.geodesicline.GeodesicLine.caps`
31 :attr:`~geographiclib.geodesicline.GeodesicLine.lat1`
32 :attr:`~geographiclib.geodesicline.GeodesicLine.lon1`
33 :attr:`~geographiclib.geodesicline.GeodesicLine.azi1`
34 :attr:`~geographiclib.geodesicline.GeodesicLine.salp1`
35 :attr:`~geographiclib.geodesicline.GeodesicLine.calp1`
36 :attr:`~geographiclib.geodesicline.GeodesicLine.s13`
37 :attr:`~geographiclib.geodesicline.GeodesicLine.a13`
64 """Points on a geodesic path"""
67 caps = GeodesicCapability.STANDARD |
68 GeodesicCapability.DISTANCE_IN,
69 salp1 = Math.nan, calp1 = Math.nan):
70 """Construct a GeodesicLine object
72 :param geod: a :class:`~geographiclib.geodesic.Geodesic` object
73 :param lat1: latitude of the first point in degrees
74 :param lon1: longitude of the first point in degrees
75 :param azi1: azimuth at the first point in degrees
76 :param caps: the :ref:`capabilities <outmask>`
78 This creates an object allowing points along a geodesic starting at
79 (*lat1*, *lon1*), with azimuth *azi1* to be found. The default
80 value of *caps* is STANDARD | DISTANCE_IN. The optional parameters
81 *salp1* and *calp1* should not be supplied; they are part of the
88 """The equatorial radius in meters (readonly)"""
90 """The flattening (readonly)"""
94 self.
caps = (caps | Geodesic.LATITUDE | Geodesic.AZIMUTH |
96 """the capabilities (readonly)"""
99 self.
lat1 = Math.LatFix(lat1)
100 """the latitude of the first point in degrees (readonly)"""
102 """the longitude of the first point in degrees (readonly)"""
103 if Math.isnan(salp1)
or Math.isnan(calp1):
104 self.
azi1 = Math.AngNormalize(azi1)
105 self.
salp1, self.
calp1 = Math.sincosd(Math.AngRound(azi1))
108 """the azimuth at the first point in degrees (readonly)"""
110 """the sine of the azimuth at the first point (readonly)"""
112 """the cosine of the azimuth at the first point (readonly)"""
115 sbet1, cbet1 = Math.sincosd(Math.AngRound(lat1)); sbet1 *= self.
_f1
117 sbet1, cbet1 = Math.norm(sbet1, cbet1); cbet1 =
max(Geodesic.tiny_, cbet1)
118 self.
_dn1 = math.sqrt(1 + geod._ep2 * Math.sq(sbet1))
136 if sbet1 != 0
or self.
calp1 != 0
else 1)
142 self.
_k2 = Math.sq(self.
_calp0) * geod._ep2
143 eps = self.
_k2 / (2 * (1 + math.sqrt(1 + self.
_k2)) + self.
_k2)
145 if self.
caps & Geodesic.CAP_C1:
148 Geodesic._C1f(eps, self.
_C1a)
149 self.
_B11 = Geodesic._SinCosSeries(
158 if self.
caps & Geodesic.CAP_C1p:
162 if self.
caps & Geodesic.CAP_C2:
165 Geodesic._C2f(eps, self.
_C2a)
166 self.
_B21 = Geodesic._SinCosSeries(
169 if self.
caps & Geodesic.CAP_C3:
171 geod._C3f(eps, self.
_C3a)
173 self.
_B31 = Geodesic._SinCosSeries(
176 if self.
caps & Geodesic.CAP_C4:
181 self.
_B41 = Geodesic._SinCosSeries(
184 """the distance between point 1 and point 3 in meters (readonly)"""
186 """the arc length between point 1 and point 3 in degrees (readonly)"""
190 """Private: General solution of position along geodesic"""
192 a12 = lat2 = lon2 = azi2 = s12 = m12 = M12 = M21 = S12 = Math.nan
193 outmask &= self.
caps & Geodesic.OUT_MASK
195 (self.
caps & (Geodesic.OUT_MASK & Geodesic.DISTANCE_IN))):
197 return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12
203 sig12 = math.radians(s12_a12)
204 ssig12, csig12 = Math.sincosd(s12_a12)
207 tau12 = s12_a12 / (self.
_b * (1 + self.
_A1m1))
208 s = math.sin(tau12); c = math.cos(tau12)
210 B12 = - Geodesic._SinCosSeries(
True,
214 sig12 = tau12 - (B12 - self.
_B11)
215 ssig12 = math.sin(sig12); csig12 = math.cos(sig12)
216 if abs(self.
f) > 0.01:
240 B12 = Geodesic._SinCosSeries(
True, ssig2, csig2, self.
_C1a)
241 serr = ((1 + self.
_A1m1) * (sig12 + (B12 - self.
_B11)) -
243 sig12 = sig12 - serr / math.sqrt(1 + self.
_k2 * Math.sq(ssig2))
244 ssig12 = math.sin(sig12); csig12 = math.cos(sig12)
252 dn2 = math.sqrt(1 + self.
_k2 * Math.sq(ssig2))
254 Geodesic.DISTANCE | Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE):
255 if arcmode
or abs(self.
f) > 0.01:
256 B12 = Geodesic._SinCosSeries(
True, ssig2, csig2, self.
_C1a)
257 AB1 = (1 + self.
_A1m1) * (B12 - self.
_B11)
259 sbet2 = self.
_calp0 * ssig2
264 cbet2 = csig2 = Geodesic.tiny_
268 if outmask & Geodesic.DISTANCE:
269 s12 = self.
_b * ((1 + self.
_A1m1) * sig12 + AB1)
if arcmode
else s12_a12
271 if outmask & Geodesic.LONGITUDE:
273 somg2 = self.
_salp0 * ssig2; comg2 = csig2
274 E = Math.copysign(1, self.
_salp0)
277 - (math.atan2( ssig2, csig2) -
279 + (math.atan2(E * somg2, comg2) -
281 if outmask & Geodesic.LONG_UNROLL
282 else math.atan2(somg2 * self.
_comg1 - comg2 * self.
_somg1,
284 lam12 = omg12 + self.
_A3c * (
285 sig12 + (Geodesic._SinCosSeries(
True, ssig2, csig2, self.
_C3a)
287 lon12 = math.degrees(lam12)
288 lon2 = (self.
lon1 + lon12
if outmask & Geodesic.LONG_UNROLL
else
289 Math.AngNormalize(Math.AngNormalize(self.
lon1) +
290 Math.AngNormalize(lon12)))
292 if outmask & Geodesic.LATITUDE:
293 lat2 = Math.atan2d(sbet2, self.
_f1 * cbet2)
295 if outmask & Geodesic.AZIMUTH:
296 azi2 = Math.atan2d(salp2, calp2)
298 if outmask & (Geodesic.REDUCEDLENGTH | Geodesic.GEODESICSCALE):
299 B22 = Geodesic._SinCosSeries(
True, ssig2, csig2, self.
_C2a)
300 AB2 = (1 + self.
_A2m1) * (B22 - self.
_B21)
301 J12 = (self.
_A1m1 - self.
_A2m1) * sig12 + (AB1 - AB2)
302 if outmask & Geodesic.REDUCEDLENGTH:
305 m12 = self.
_b * (( dn2 * (self.
_csig1 * ssig2) -
307 - self.
_csig1 * csig2 * J12)
308 if outmask & Geodesic.GEODESICSCALE:
311 M12 = csig12 + (t * ssig2 - csig2 * J12) * self.
_ssig1 / self.
_dn1
312 M21 = csig12 - (t * self.
_ssig1 - self.
_csig1 * J12) * ssig2 / dn2
314 if outmask & Geodesic.AREA:
315 B42 = Geodesic._SinCosSeries(
False, ssig2, csig2, self.
_C4a)
319 salp12 = salp2 * self.
calp1 - calp2 * self.
salp1
320 calp12 = calp2 * self.
calp1 + salp2 * self.
salp1
331 self.
_csig1 * (1 - csig12) + ssig12 * self.
_ssig1 if csig12 <= 0
332 else ssig12 * (self.
_csig1 * ssig12 / (1 + csig12) + self.
_ssig1))
333 calp12 = (Math.sq(self.
_salp0) +
335 S12 = (self.
_c2 * math.atan2(salp12, calp12) +
338 a12 = s12_a12
if arcmode
else math.degrees(sig12)
339 return a12, lat2, lon2, azi2, s12, m12, M12, M21, S12
341 def Position(self, s12, outmask = GeodesicCapability.STANDARD):
342 """Find the position on the line given *s12*
344 :param s12: the distance from the first point to the second in
346 :param outmask: the :ref:`output mask <outmask>`
347 :return: a :ref:`dict`
349 The default value of *outmask* is STANDARD, i.e., the *lat1*,
350 *lon1*, *azi1*, *lat2*, *lon2*, *azi2*, *s12*, *a12* entries are
351 returned. The :class:`~geographiclib.geodesicline.GeodesicLine`
352 object must have been constructed with the DISTANCE_IN capability.
357 result = {
'lat1': self.
lat1,
358 'lon1': self.
lon1 if outmask & Geodesic.LONG_UNROLL
else
359 Math.AngNormalize(self.
lon1),
360 'azi1': self.
azi1,
's12': s12}
361 a12, lat2, lon2, azi2, s12, m12, M12, M21, S12 = self.
_GenPosition(
363 outmask &= Geodesic.OUT_MASK
365 if outmask & Geodesic.LATITUDE: result[
'lat2'] = lat2
366 if outmask & Geodesic.LONGITUDE: result[
'lon2'] = lon2
367 if outmask & Geodesic.AZIMUTH: result[
'azi2'] = azi2
368 if outmask & Geodesic.REDUCEDLENGTH: result[
'm12'] = m12
369 if outmask & Geodesic.GEODESICSCALE:
370 result[
'M12'] = M12; result[
'M21'] = M21
371 if outmask & Geodesic.AREA: result[
'S12'] = S12
374 def ArcPosition(self, a12, outmask = GeodesicCapability.STANDARD):
375 """Find the position on the line given *a12*
377 :param a12: spherical arc length from the first point to the second
379 :param outmask: the :ref:`output mask <outmask>`
380 :return: a :ref:`dict`
382 The default value of *outmask* is STANDARD, i.e., the *lat1*,
383 *lon1*, *azi1*, *lat2*, *lon2*, *azi2*, *s12*, *a12* entries are
389 result = {
'lat1': self.
lat1,
390 'lon1': self.
lon1 if outmask & Geodesic.LONG_UNROLL
else
391 Math.AngNormalize(self.
lon1),
392 'azi1': self.
azi1,
'a12': a12}
393 a12, lat2, lon2, azi2, s12, m12, M12, M21, S12 = self.
_GenPosition(
395 outmask &= Geodesic.OUT_MASK
396 if outmask & Geodesic.DISTANCE: result[
's12'] = s12
397 if outmask & Geodesic.LATITUDE: result[
'lat2'] = lat2
398 if outmask & Geodesic.LONGITUDE: result[
'lon2'] = lon2
399 if outmask & Geodesic.AZIMUTH: result[
'azi2'] = azi2
400 if outmask & Geodesic.REDUCEDLENGTH: result[
'm12'] = m12
401 if outmask & Geodesic.GEODESICSCALE:
402 result[
'M12'] = M12; result[
'M21'] = M21
403 if outmask & Geodesic.AREA: result[
'S12'] = S12
407 """Specify the position of point 3 in terms of distance
409 :param s13: distance from point 1 to point 3 in meters
414 self.
a13, _, _, _, _, _, _, _, _ = self.
_GenPosition(
False, self.
s13, 0)
417 """Specify the position of point 3 in terms of arc length
419 :param a13: spherical arc length from point 1 to point 3 in degrees
425 _, _, _, _, self.
s13, _, _, _, _ = self.
_GenPosition(
True, self.
a13,