pygeodesy 24.3.24__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (115) hide show
  1. PyGeodesy-24.3.24.dist-info/METADATA +272 -0
  2. PyGeodesy-24.3.24.dist-info/RECORD +115 -0
  3. PyGeodesy-24.3.24.dist-info/WHEEL +6 -0
  4. PyGeodesy-24.3.24.dist-info/top_level.txt +1 -0
  5. pygeodesy/LICENSE +21 -0
  6. pygeodesy/__init__.py +615 -0
  7. pygeodesy/__main__.py +103 -0
  8. pygeodesy/albers.py +867 -0
  9. pygeodesy/auxilats/_CX_4.py +218 -0
  10. pygeodesy/auxilats/_CX_6.py +314 -0
  11. pygeodesy/auxilats/_CX_8.py +475 -0
  12. pygeodesy/auxilats/__init__.py +54 -0
  13. pygeodesy/auxilats/__main__.py +86 -0
  14. pygeodesy/auxilats/auxAngle.py +548 -0
  15. pygeodesy/auxilats/auxDLat.py +302 -0
  16. pygeodesy/auxilats/auxDST.py +296 -0
  17. pygeodesy/auxilats/auxLat.py +848 -0
  18. pygeodesy/auxilats/auxily.py +272 -0
  19. pygeodesy/azimuthal.py +1150 -0
  20. pygeodesy/basics.py +892 -0
  21. pygeodesy/booleans.py +2031 -0
  22. pygeodesy/cartesianBase.py +1062 -0
  23. pygeodesy/clipy.py +704 -0
  24. pygeodesy/constants.py +516 -0
  25. pygeodesy/css.py +660 -0
  26. pygeodesy/datums.py +752 -0
  27. pygeodesy/deprecated/__init__.py +61 -0
  28. pygeodesy/deprecated/bases.py +40 -0
  29. pygeodesy/deprecated/classes.py +262 -0
  30. pygeodesy/deprecated/consterns.py +54 -0
  31. pygeodesy/deprecated/datum.py +40 -0
  32. pygeodesy/deprecated/functions.py +375 -0
  33. pygeodesy/deprecated/nvector.py +48 -0
  34. pygeodesy/deprecated/rhumbBase.py +32 -0
  35. pygeodesy/deprecated/rhumbaux.py +33 -0
  36. pygeodesy/deprecated/rhumbsolve.py +33 -0
  37. pygeodesy/deprecated/rhumbx.py +33 -0
  38. pygeodesy/dms.py +986 -0
  39. pygeodesy/ecef.py +1348 -0
  40. pygeodesy/elevations.py +279 -0
  41. pygeodesy/ellipsoidalBase.py +1224 -0
  42. pygeodesy/ellipsoidalBaseDI.py +913 -0
  43. pygeodesy/ellipsoidalExact.py +343 -0
  44. pygeodesy/ellipsoidalGeodSolve.py +343 -0
  45. pygeodesy/ellipsoidalKarney.py +403 -0
  46. pygeodesy/ellipsoidalNvector.py +685 -0
  47. pygeodesy/ellipsoidalVincenty.py +590 -0
  48. pygeodesy/ellipsoids.py +2476 -0
  49. pygeodesy/elliptic.py +1198 -0
  50. pygeodesy/epsg.py +243 -0
  51. pygeodesy/errors.py +804 -0
  52. pygeodesy/etm.py +1190 -0
  53. pygeodesy/fmath.py +1013 -0
  54. pygeodesy/formy.py +1818 -0
  55. pygeodesy/frechet.py +865 -0
  56. pygeodesy/fstats.py +760 -0
  57. pygeodesy/fsums.py +1898 -0
  58. pygeodesy/gars.py +358 -0
  59. pygeodesy/geodesicw.py +581 -0
  60. pygeodesy/geodesicx/_C4_24.py +1699 -0
  61. pygeodesy/geodesicx/_C4_27.py +2395 -0
  62. pygeodesy/geodesicx/_C4_30.py +3301 -0
  63. pygeodesy/geodesicx/__init__.py +48 -0
  64. pygeodesy/geodesicx/__main__.py +91 -0
  65. pygeodesy/geodesicx/gx.py +1382 -0
  66. pygeodesy/geodesicx/gxarea.py +535 -0
  67. pygeodesy/geodesicx/gxbases.py +154 -0
  68. pygeodesy/geodesicx/gxline.py +669 -0
  69. pygeodesy/geodsolve.py +426 -0
  70. pygeodesy/geohash.py +914 -0
  71. pygeodesy/geoids.py +1884 -0
  72. pygeodesy/hausdorff.py +892 -0
  73. pygeodesy/heights.py +1155 -0
  74. pygeodesy/interns.py +687 -0
  75. pygeodesy/iters.py +545 -0
  76. pygeodesy/karney.py +919 -0
  77. pygeodesy/ktm.py +633 -0
  78. pygeodesy/latlonBase.py +1766 -0
  79. pygeodesy/lazily.py +960 -0
  80. pygeodesy/lcc.py +684 -0
  81. pygeodesy/ltp.py +1107 -0
  82. pygeodesy/ltpTuples.py +1563 -0
  83. pygeodesy/mgrs.py +721 -0
  84. pygeodesy/named.py +1324 -0
  85. pygeodesy/namedTuples.py +683 -0
  86. pygeodesy/nvectorBase.py +695 -0
  87. pygeodesy/osgr.py +781 -0
  88. pygeodesy/points.py +1686 -0
  89. pygeodesy/props.py +628 -0
  90. pygeodesy/resections.py +1048 -0
  91. pygeodesy/rhumb/__init__.py +46 -0
  92. pygeodesy/rhumb/aux_.py +397 -0
  93. pygeodesy/rhumb/bases.py +1148 -0
  94. pygeodesy/rhumb/ekx.py +563 -0
  95. pygeodesy/rhumb/solve.py +572 -0
  96. pygeodesy/simplify.py +647 -0
  97. pygeodesy/solveBase.py +472 -0
  98. pygeodesy/sphericalBase.py +724 -0
  99. pygeodesy/sphericalNvector.py +1264 -0
  100. pygeodesy/sphericalTrigonometry.py +1447 -0
  101. pygeodesy/streprs.py +627 -0
  102. pygeodesy/trf.py +2079 -0
  103. pygeodesy/triaxials.py +1484 -0
  104. pygeodesy/units.py +969 -0
  105. pygeodesy/unitsBase.py +349 -0
  106. pygeodesy/ups.py +538 -0
  107. pygeodesy/utily.py +1231 -0
  108. pygeodesy/utm.py +762 -0
  109. pygeodesy/utmups.py +318 -0
  110. pygeodesy/utmupsBase.py +517 -0
  111. pygeodesy/vector2d.py +785 -0
  112. pygeodesy/vector3d.py +968 -0
  113. pygeodesy/vector3dBase.py +1049 -0
  114. pygeodesy/webmercator.py +383 -0
  115. pygeodesy/wgrs.py +439 -0
pygeodesy/css.py ADDED
@@ -0,0 +1,660 @@
1
+
2
+ # -*- coding: utf-8 -*-
3
+
4
+ u'''Cassini-Soldner (CSS) projection.
5
+
6
+ Classes L{CassiniSoldner}, L{Css} and L{CSSError} requiring I{Charles Karney}'s
7
+ U{geographiclib <https://PyPI.org/project/geographiclib>} Python package to be
8
+ installed.
9
+ '''
10
+
11
+ from pygeodesy.basics import islistuple, neg, _xinstanceof, _xsubclassof
12
+ from pygeodesy.constants import _umod_360, _0_0, _0_5, _90_0
13
+ from pygeodesy.datums import _ellipsoidal_datum, _WGS84
14
+ from pygeodesy.ellipsoidalBase import LatLonEllipsoidalBase as _LLEB
15
+ from pygeodesy.errors import _ValueError, _xdatum, _xellipsoidal, \
16
+ _xattr, _xkwds
17
+ from pygeodesy.interns import NN, _azimuth_, _COMMASPACE_, _easting_, \
18
+ _lat_, _lon_, _m_, _name_, _northing_, \
19
+ _reciprocal_, _SPACE_
20
+ from pygeodesy.interns import _C_ # PYCHOK used!
21
+ from pygeodesy.karney import _atan2d, _copysign, _diff182, _norm2, \
22
+ _norm180, _signBit, _sincos2d, fabs
23
+ from pygeodesy.lazily import _ALL_LAZY, _ALL_MODS as _MODS
24
+ from pygeodesy.named import _NamedBase, _NamedTuple, nameof
25
+ from pygeodesy.namedTuples import EasNor2Tuple, EasNor3Tuple, \
26
+ LatLon2Tuple, LatLon4Tuple, _LL4Tuple
27
+ from pygeodesy.props import deprecated_Property_RO, Property, \
28
+ Property_RO, _update_all
29
+ from pygeodesy.streprs import Fmt, _fstrENH2, _fstrLL0, _xzipairs
30
+ from pygeodesy.units import Bearing, Degrees, Easting, Height, _heigHt, \
31
+ Lat_, Lon_, Northing, Scalar
32
+
33
+ # from math import fabs # from .karney
34
+
35
+ __all__ = _ALL_LAZY.css
36
+ __version__ = '24.02.21'
37
+
38
+
39
+ def _CS0(cs0):
40
+ '''(INTERNAL) Get/set default projection.
41
+ '''
42
+ if cs0 is None:
43
+ cs0 = Css._CS0
44
+ if cs0 is None:
45
+ Css._CS0 = cs0 = CassiniSoldner(_0_0, _0_0, name='Default')
46
+ else:
47
+ _xinstanceof(CassiniSoldner, cs0=cs0)
48
+ return cs0
49
+
50
+
51
+ class CSSError(_ValueError):
52
+ '''Cassini-Soldner (CSS) conversion or other L{Css} issue.
53
+ '''
54
+ pass
55
+
56
+
57
+ class CassiniSoldner(_NamedBase):
58
+ '''Cassini-Soldner projection, a Python version of I{Karney}'s C++ class U{CassiniSoldner
59
+ <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1CassiniSoldner.html>}.
60
+ '''
61
+ _cb0 = _0_0
62
+ _datum = _WGS84 # L{Datum}
63
+ _geodesic = None
64
+ _latlon0 = ()
65
+ _meridian = None
66
+ _sb0 = _0_0
67
+
68
+ def __init__(self, lat0, lon0, datum=_WGS84, name=NN):
69
+ '''New L{CassiniSoldner} projection.
70
+
71
+ @arg lat0: Latitude of center point (C{degrees90}).
72
+ @arg lon0: Longitude of center point (C{degrees180}).
73
+ @kwarg datum: Optional datum or ellipsoid (L{Datum},
74
+ L{Ellipsoid}, L{Ellipsoid2} or L{a_f2Tuple}).
75
+ @kwarg name: Optional name (C{str}).
76
+
77
+ @raise CSSError: Invalid B{C{lat}} or B{C{lon}}.
78
+ '''
79
+ if datum not in (None, self._datum):
80
+ self._datum = _xellipsoidal(datum=_ellipsoidal_datum(datum, name=name))
81
+ if name:
82
+ self.name = name
83
+
84
+ self.reset(lat0, lon0)
85
+
86
+ @Property
87
+ def datum(self):
88
+ '''Get the datum (L{Datum}).
89
+ '''
90
+ return self._datum
91
+
92
+ @datum.setter # PYCHOK setter!
93
+ def datum(self, datum):
94
+ '''Set the datum or ellipsoid (L{Datum}, L{Ellipsoid}, L{Ellipsoid2}
95
+ or L{a_f2Tuple}) or C{None} for the default.
96
+ '''
97
+ d = CassiniSoldner._datum if datum is None else \
98
+ _xellipsoidal(datum=_ellipsoidal_datum(datum, name=self.name))
99
+ if self._datum != d:
100
+ self._datum = d
101
+ self.geodesic = None if self._geodesic is None else self.isExact
102
+
103
+ def _datumatch(self, latlon):
104
+ '''Check for matching datum ellipsoids.
105
+
106
+ @raise CSSError: Ellipsoid mismatch of B{C{latlon}} and this projection.
107
+ '''
108
+ d = _xattr(latlon, datum=None)
109
+ if d:
110
+ _xdatum(self.datum, d, Error=CSSError)
111
+
112
+ @Property_RO
113
+ def equatoradius(self):
114
+ '''Get the ellipsoid's equatorial radius, semi-axis (C{meter}).
115
+ '''
116
+ return self.geodesic.a
117
+
118
+ a = equatoradius
119
+
120
+ @Property_RO
121
+ def flattening(self):
122
+ '''Get the ellipsoid's flattening (C{scalar}).
123
+ '''
124
+ return self.geodesic.f
125
+
126
+ f = flattening
127
+
128
+ def forward(self, lat, lon, name=NN):
129
+ '''Convert an (ellipsoidal) geodetic location to Cassini-Soldner
130
+ easting and northing.
131
+
132
+ @arg lat: Latitude of the location (C{degrees90}).
133
+ @arg lon: Longitude of the location (C{degrees180}).
134
+ @kwarg name: Name inlieu of this projection's name (C{str}).
135
+
136
+ @return: An L{EasNor2Tuple}C{(easting, northing)}.
137
+
138
+ @see: Methods L{CassiniSoldner.forward4}, L{CassiniSoldner.reverse}
139
+ and L{CassiniSoldner.reverse4}.
140
+
141
+ @raise CSSError: Invalid B{C{lat}} or B{C{lon}}.
142
+ '''
143
+ t = self.forward6(lat, lon, name=name)
144
+ return EasNor2Tuple(t.easting, t.northing, name=t.name)
145
+
146
+ def forward4(self, lat, lon, name=NN):
147
+ '''Convert an (ellipsoidal) geodetic location to Cassini-Soldner
148
+ easting and northing.
149
+
150
+ @arg lat: Latitude of the location (C{degrees90}).
151
+ @arg lon: Longitude of the location (C{degrees180}).
152
+ @kwarg name: Name inlieu of this projection's name (C{str}).
153
+
154
+ @return: An L{EasNorAziRk4Tuple}C{(easting, northing,
155
+ azimuth, reciprocal)}.
156
+
157
+ @see: Method L{CassiniSoldner.forward}, L{CassiniSoldner.forward6},
158
+ L{CassiniSoldner.reverse} and L{CassiniSoldner.reverse4}.
159
+
160
+ @raise CSSError: Invalid B{C{lat}} or B{C{lon}}.
161
+ '''
162
+ t = self.forward6(lat, lon, name=name)
163
+ return EasNorAziRk4Tuple(t.easting, t.northing,
164
+ t.azimuth, t.reciprocal, name=t.name)
165
+
166
+ def forward6(self, lat, lon, name=NN):
167
+ '''Convert an (ellipsoidal) geodetic location to Cassini-Soldner
168
+ easting and northing.
169
+
170
+ @arg lat: Latitude of the location (C{degrees90}).
171
+ @arg lon: Longitude of the location (C{degrees180}).
172
+ @kwarg name: Name inlieu of this projection's name (C{str}).
173
+
174
+ @return: An L{EasNorAziRkEqu6Tuple}C{(easting, northing,
175
+ azimuth, reciprocal, equatorarc, equatorazimuth)}.
176
+
177
+ @see: Method L{CassiniSoldner.forward}, L{CassiniSoldner.forward4},
178
+ L{CassiniSoldner.reverse} and L{CassiniSoldner.reverse4}.
179
+
180
+ @raise CSSError: Invalid B{C{lat}} or B{C{lon}}.
181
+ '''
182
+ g = self.geodesic
183
+
184
+ lat = Lat_(lat, Error=CSSError)
185
+ d, _ = _diff182(self.lon0, Lon_(lon, Error=CSSError)) # _2sum
186
+ D = fabs(d)
187
+
188
+ r = g.Inverse(lat, -D, lat, D)
189
+ z1, a = r.azi1, (r.a12 * _0_5)
190
+ z2, e = r.azi2, (r.s12 * _0_5)
191
+ if e == 0: # PYCHOK no cover
192
+ z = _diff182(z1, z2)[0] * _0_5 # _2sum
193
+ c = _copysign(_90_0, 90 - D) # -90 if D > 90 else 90
194
+ z1, z2 = c - z, c + z
195
+ if _signBit(d):
196
+ a, e, z2 = neg(a), neg(e), z1
197
+
198
+ z = _norm180(z2) # azimuth of easting direction
199
+ p = g.Line(lat, d, z, g.DISTANCE | g.GEODESICSCALE | g.LINE_OFF)
200
+ # reciprocal of azimuthal northing scale
201
+ rk = p.ArcPosition(neg(a), g.GEODESICSCALE).M21
202
+ # rk = p._GenPosition(True, -a, g.DISTANCE)[7]
203
+
204
+ s, c = _sincos2d(p.azi0) # aka equatorazimuth
205
+ sb1 = _copysign(c, lat)
206
+ cb1 = _copysign(s, 90 - D) # -abs(s) if D > 90 else abs(s)
207
+ d = _atan2d(sb1 * self._cb0 - cb1 * self._sb0,
208
+ cb1 * self._cb0 + sb1 * self._sb0)
209
+ n = self._meridian.ArcPosition(d, g.DISTANCE).s12
210
+ # n = self._meridian._GenPosition(True, d, g.DISTANCE)[4]
211
+ return EasNorAziRkEqu6Tuple(e, n, z, rk, p.a1, p.azi0,
212
+ name=name or self.name)
213
+
214
+ @Property
215
+ def geodesic(self):
216
+ '''Get this projection's I{wrapped} U{geodesic.Geodesic
217
+ <https://GeographicLib.SourceForge.io/Python/doc/code.html>}, provided
218
+ I{Karney}'s U{geographiclib<https://PyPI.org/project/geographiclib>}
219
+ package is installed, otherwise an I{exact} L{GeodesicExact} instance.
220
+ '''
221
+ g = self._geodesic
222
+ if g is None:
223
+ E = self.datum.ellipsoid
224
+ try:
225
+ g = E.geodesicw
226
+ except ImportError:
227
+ g = E.geodesicx
228
+ self._geodesic = g
229
+ return g
230
+
231
+ @geodesic.setter # PYCHOK setter!
232
+ def geodesic(self, exact):
233
+ '''Set this projection's geodesic (C{bool}) to L{GeodesicExact}
234
+ or I{wrapped Karney}'s or C{None} for the default.
235
+
236
+ @raise ImportError: Package U{geographiclib<https://PyPI.org/
237
+ project/geographiclib>} not installed or
238
+ not found and C{B{exact}=False}.
239
+ '''
240
+ E = self.datum.ellipsoid
241
+ self._geodesic = None if exact is None else (
242
+ E.geodesicx if exact else E.geodesicw)
243
+ self.reset(*self.latlon0)
244
+
245
+ @Property_RO
246
+ def isExact(self):
247
+ '''Return C{True} if this projection's geodesic is L{GeodesicExact}.
248
+ '''
249
+ return isinstance(self.geodesic, _MODS.geodesicx.GeodesicExact)
250
+
251
+ @Property_RO
252
+ def lat0(self):
253
+ '''Get the center latitude (C{degrees90}).
254
+ '''
255
+ return self.latlon0.lat
256
+
257
+ @property
258
+ def latlon0(self):
259
+ '''Get the center lat- and longitude (L{LatLon2Tuple}C{(lat, lon)})
260
+ in (C{degrees90}, (C{degrees180}).
261
+ '''
262
+ return self._latlon0
263
+
264
+ @latlon0.setter # PYCHOK setter!
265
+ def latlon0(self, latlon0):
266
+ '''Set the center lat- and longitude (ellipsoidal C{LatLon},
267
+ L{LatLon2Tuple}, L{LatLon4Tuple} or a C{tuple} or C{list}
268
+ with the C{lat}- and C{lon}gitude in C{degrees}).
269
+
270
+ @raise CSSError: Invalid B{C{latlon0}} or ellipsoid mismatch
271
+ of B{C{latlon0}} and this projection.
272
+ '''
273
+ if islistuple(latlon0, 2):
274
+ lat0, lon0 = latlon0[:2]
275
+ else:
276
+ try:
277
+ lat0, lon0 = latlon0.lat, latlon0.lon
278
+ self._datumatch(latlon0)
279
+ except (AttributeError, TypeError, ValueError) as x:
280
+ raise CSSError(latlon0=latlon0, cause=x)
281
+ self.reset(lat0, lon0)
282
+
283
+ @Property_RO
284
+ def lon0(self):
285
+ '''Get the center longitude (C{degrees180}).
286
+ '''
287
+ return self.latlon0.lon
288
+
289
+ @deprecated_Property_RO
290
+ def majoradius(self): # PYCHOK no cover
291
+ '''DEPRECATED, use property C{equatoradius}.'''
292
+ return self.equatoradius
293
+
294
+ def reset(self, lat0, lon0):
295
+ '''Set or reset the center point of this Cassini-Soldner projection.
296
+
297
+ @arg lat0: Center point latitude (C{degrees90}).
298
+ @arg lon0: Center point longitude (C{degrees180}).
299
+
300
+ @raise CSSError: Invalid B{C{lat0}} or B{C{lon0}}.
301
+ '''
302
+ _update_all(self)
303
+
304
+ g = self.geodesic
305
+ self._meridian = m = g.Line(Lat_(lat0=lat0, Error=CSSError),
306
+ Lon_(lon0=lon0, Error=CSSError), _0_0,
307
+ g.STANDARD | g.DISTANCE_IN | g.LINE_OFF)
308
+ self._latlon0 = LatLon2Tuple(m.lat1, m.lon1)
309
+ s, c = _sincos2d(m.lat1) # == self.lat0 == self.LatitudeOrigin()
310
+ self._sb0, self._cb0 = _norm2(s * g.f1, c)
311
+
312
+ def reverse(self, easting, northing, name=NN, LatLon=None, **LatLon_kwds):
313
+ '''Convert a Cassini-Soldner location to (ellipsoidal) geodetic
314
+ lat- and longitude.
315
+
316
+ @arg easting: Easting of the location (C{meter}).
317
+ @arg northing: Northing of the location (C{meter}).
318
+ @kwarg name: Name inlieu of this projection's name (C{str}).
319
+ @kwarg LatLon: Optional, ellipsoidal class to return the
320
+ geodetic location as (C{LatLon}) or C{None}.
321
+ @kwarg LatLon_kwds: Optional (C{LatLon}) keyword arguments,
322
+ ignored if C{B{LatLon} is None}.
323
+
324
+ @return: Geodetic location B{C{LatLon}} or if B{C{LatLon}}
325
+ is C{None}, a L{LatLon2Tuple}C{(lat, lon)}.
326
+
327
+ @raise CSSError: Ellipsoidal mismatch of B{C{LatLon}} and this projection.
328
+
329
+ @raise TypeError: Invalid B{C{LatLon}} or B{C{LatLon_kwds}}.
330
+
331
+ @see: Method L{CassiniSoldner.reverse4}, L{CassiniSoldner.forward}.
332
+ L{CassiniSoldner.forward4} and L{CassiniSoldner.forward6}.
333
+ '''
334
+ r = self.reverse4(easting, northing, name=name)
335
+ if LatLon is None:
336
+ r = LatLon2Tuple(r.lat, r.lon, name=r.name) # PYCHOK expected
337
+ else:
338
+ _xsubclassof(_LLEB, LatLon=LatLon)
339
+ kwds = _xkwds(LatLon_kwds, datum=self.datum, name=r.name)
340
+ r = LatLon(r.lat, r.lon, **kwds) # PYCHOK expected
341
+ self._datumatch(r)
342
+ return r
343
+
344
+ def reverse4(self, easting, northing, name=NN):
345
+ '''Convert a Cassini-Soldner location to (ellipsoidal) geodetic
346
+ lat- and longitude.
347
+
348
+ @arg easting: Easting of the location (C{meter}).
349
+ @arg northing: Northing of the location (C{meter}).
350
+ @kwarg name: Name inlieu of this projection's name (C{str}).
351
+
352
+ @return: A L{LatLonAziRk4Tuple}C{(lat, lon, azimuth, reciprocal)}.
353
+
354
+ @see: Method L{CassiniSoldner.reverse}, L{CassiniSoldner.forward}
355
+ and L{CassiniSoldner.forward4}.
356
+ '''
357
+ g = self.geodesic
358
+ n = self._meridian.Position(northing)
359
+ r = g.Direct(n.lat2, n.lon2, n.azi2 + _90_0, easting, outmask=g.STANDARD | g.GEODESICSCALE)
360
+ z = _umod_360(r.azi2) # -180 <= r.azi2 < 180 ... 0 <= z < 360
361
+ # include z azimuth of easting direction and rk reciprocal
362
+ # of azimuthal northing scale (see C++ member Direct() 5/6
363
+ # <https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Geodesic.html>)
364
+ return LatLonAziRk4Tuple(r.lat2, r.lon2, z, r.M12, name=name or self.name)
365
+
366
+ toLatLon = reverse # XXX not reverse4
367
+
368
+ def toRepr(self, prec=6, **unused): # PYCHOK expected
369
+ '''Return a string representation of this projection.
370
+
371
+ @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
372
+
373
+ @return: This projection as C{"<classname>(lat0, lon0, ...)"}
374
+ (C{str}).
375
+ '''
376
+ return _fstrLL0(self, prec, True)
377
+
378
+ def toStr(self, prec=6, sep=_SPACE_, **unused): # PYCHOK expected
379
+ '''Return a string representation of this projection.
380
+
381
+ @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
382
+ @kwarg sep: Separator to join (C{str}).
383
+
384
+ @return: This projection as C{"lat0 lon0"} (C{str}).
385
+ '''
386
+ t = _fstrLL0(self, prec, False)
387
+ return t if sep is None else sep.join(t)
388
+
389
+
390
+ class Css(_NamedBase):
391
+ '''Cassini-Soldner East-/Northing location.
392
+ '''
393
+ _CS0 = None # default projection (L{CassiniSoldner})
394
+ _cs0 = None # projection (L{CassiniSoldner})
395
+ _easting = _0_0 # easting (C{float})
396
+ _height = 0 # height (C{meter})
397
+ _northing = _0_0 # northing (C{float})
398
+
399
+ def __init__(self, e, n, h=0, cs0=None, name=NN):
400
+ '''New L{Css} Cassini-Soldner position.
401
+
402
+ @arg e: Easting (C{meter}).
403
+ @arg n: Northing (C{meter}).
404
+ @kwarg h: Optional height (C{meter}).
405
+ @kwarg cs0: Optional, the Cassini-Soldner projection
406
+ (L{CassiniSoldner}).
407
+ @kwarg name: Optional name (C{str}).
408
+
409
+ @return: The Cassini-Soldner location (L{Css}).
410
+
411
+ @raise CSSError: If B{C{e}} or B{C{n}} is invalid.
412
+
413
+ @raise TypeError: If B{C{cs0}} is not L{CassiniSoldner}.
414
+
415
+ @raise ValueError: Invalid B{C{h}}.
416
+ '''
417
+ self._cs0 = _CS0(cs0)
418
+ self._easting = Easting(e, Error=CSSError)
419
+ self._northing = Northing(n, Error=CSSError)
420
+ if h:
421
+ self._height = Height(h=h)
422
+ if name:
423
+ self.name = name
424
+
425
+ @Property_RO
426
+ def azi(self):
427
+ '''Get the azimuth of easting direction (C{degrees}).
428
+ '''
429
+ return self.reverse4.azimuth
430
+
431
+ azimuth = azi
432
+
433
+ @Property
434
+ def cs0(self):
435
+ '''Get the projection (L{CassiniSoldner}).
436
+ '''
437
+ return self._cs0 or Css._CS0
438
+
439
+ @cs0.setter # PYCHOK setter!
440
+ def cs0(self, cs0):
441
+ '''Set the I{Cassini-Soldner} projection (L{CassiniSoldner}).
442
+
443
+ @raise TypeError: Invalid B{C{cs0}}.
444
+ '''
445
+ cs0 = _CS0(cs0)
446
+ if cs0 != self._cs0:
447
+ _update_all(self)
448
+ self._cs0 = cs0
449
+
450
+ # def dup(self, name=NN, **e_n_h_cs0): # PYCHOK signature
451
+ # '''Duplicate this position with some attributes modified.
452
+ #
453
+ # @kwarg e_n_h_cs0: Use keyword argument C{B{e}=...}, C{B{n}=...},
454
+ # C{B{h}=...} and/or C{B{cs0}=...} to override
455
+ # the current C{easting}, C{northing} C{height}
456
+ # or C{cs0} projectio, respectively.
457
+ # '''
458
+ # def _args_kwds(e=None, n=None, **kwds):
459
+ # return (e, n), kwds
460
+ #
461
+ # kwds = _xkwds(e_n_h_cs0, e=self.easting, n=self.northing,
462
+ # h=self.height, cs0=self.cs0,
463
+ # name=name or self.name)
464
+ # args, kwds = _args_kwds(**kwds)
465
+ # return self.__class__(*args, **kwds) # .classof
466
+
467
+ @Property_RO
468
+ def easting(self):
469
+ '''Get the easting (C{meter}).
470
+ '''
471
+ return self._easting
472
+
473
+ @Property_RO
474
+ def height(self):
475
+ '''Get the height (C{meter}).
476
+ '''
477
+ return self._height
478
+
479
+ @Property_RO
480
+ def latlon(self):
481
+ '''Get the lat- and longitude (L{LatLon2Tuple}).
482
+ '''
483
+ r = self.reverse4
484
+ return LatLon2Tuple(r.lat, r.lon, name=self.name)
485
+
486
+ @Property_RO
487
+ def northing(self):
488
+ '''Get the northing (C{meter}).
489
+ '''
490
+ return self._northing
491
+
492
+ @Property_RO
493
+ def reverse4(self):
494
+ '''Get the lat, lon, azimuth and reciprocal (L{LatLonAziRk4Tuple}).
495
+ '''
496
+ return self.cs0.reverse4(self.easting, self.northing, name=self.name)
497
+
498
+ @Property_RO
499
+ def rk(self):
500
+ '''Get the reciprocal of azimuthal northing scale (C{scalar}).
501
+ '''
502
+ return self.reverse4.reciprocal
503
+
504
+ reciprocal = rk
505
+
506
+ def toLatLon(self, LatLon=None, height=None, **LatLon_kwds):
507
+ '''Convert this L{Css} to an (ellipsoidal) geodetic point.
508
+
509
+ @kwarg LatLon: Optional, ellipsoidal class to return the
510
+ geodetic point (C{LatLon}) or C{None}.
511
+ @kwarg height: Optional height for the point, overriding the
512
+ default height (C{meter}).
513
+ @kwarg LatLon_kwds: Optional, additional B{C{LatLon}} keyword
514
+ arguments, ignored if C{B{LatLon} is None}.
515
+
516
+ @return: The geodetic point (B{C{LatLon}}) or if B{C{LatLon}}
517
+ is C{None}, a L{LatLon4Tuple}C{(lat, lon, height,
518
+ datum)}.
519
+
520
+ @raise TypeError: If B{C{LatLon}} or B{C{datum}} is not
521
+ ellipsoidal or invalid B{C{height}} or
522
+ B{C{LatLon_kwds}}.
523
+ '''
524
+ if LatLon:
525
+ _xsubclassof(_LLEB, LatLon=LatLon)
526
+
527
+ lat, lon = self.latlon
528
+ h = _heigHt(self, height)
529
+ return _LL4Tuple(lat, lon, h, self.cs0.datum, LatLon, LatLon_kwds,
530
+ inst=self, name=self.name)
531
+
532
+ def toRepr(self, prec=6, fmt=Fmt.SQUARE, sep=_COMMASPACE_, m=_m_, C=False): # PYCHOK expected
533
+ '''Return a string representation of this L{Css} position.
534
+
535
+ @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
536
+ @kwarg fmt: Enclosing backets format (C{str}).
537
+ @kwarg sep: Optional separator between name:values (C{str}).
538
+ @kwarg m: Optional unit of the height, default meter (C{str}).
539
+ @kwarg C: Optionally, include name of projection (C{bool}).
540
+
541
+ @return: This position as C{"[E:meter, N:meter, H:m, name:'',
542
+ C:Conic.Datum]"} (C{str}).
543
+ '''
544
+ t, T = _fstrENH2(self, prec, m)
545
+ if self.name:
546
+ t += repr(self.name),
547
+ T += _name_,
548
+ if C:
549
+ t += self.cs0.toRepr(prec=prec),
550
+ T += _C_,
551
+ return _xzipairs(T, t, sep=sep, fmt=fmt)
552
+
553
+ def toStr(self, prec=6, sep=_SPACE_, m=_m_): # PYCHOK expected
554
+ '''Return a string representation of this L{Css} position.
555
+
556
+ @kwarg prec: Number of (decimal) digits, unstripped (C{int}).
557
+ @kwarg sep: Optional separator to join (C{str}) or C{None}
558
+ to return an unjoined C{tuple} of C{str}s.
559
+ @kwarg m: Height units, default C{meter} (C{str}).
560
+
561
+ @return: This position as C{"easting nothing"} C{str} in
562
+ C{meter} plus C{" height"} and C{'m'} if height
563
+ is non-zero (C{str}).
564
+ '''
565
+ t, _ = _fstrENH2(self, prec, m)
566
+ return t if sep is None else sep.join(t)
567
+
568
+
569
+ class EasNorAziRk4Tuple(_NamedTuple):
570
+ '''4-Tuple C{(easting, northing, azimuth, reciprocal)} for the
571
+ Cassini-Soldner location with C{easting} and C{northing} in
572
+ C{meters} and the C{azimuth} of easting direction and
573
+ C{reciprocal} of azimuthal northing scale, both in C{degrees}.
574
+ '''
575
+ _Names_ = (_easting_, _northing_, _azimuth_, _reciprocal_)
576
+ _Units_ = ( Easting, Northing, Bearing, Scalar)
577
+
578
+
579
+ class EasNorAziRkEqu6Tuple(_NamedTuple):
580
+ '''6-Tuple C{(easting, northing, azimuth, reciprocal, equatorarc,
581
+ equatorazimuth)} for the Cassini-Soldner location with
582
+ C{easting} and C{northing} in C{meters} and the C{azimuth} of
583
+ easting direction, C{reciprocal} of azimuthal northing scale,
584
+ C{equatorarc} and C{equatorazimuth}, all in C{degrees}.
585
+ '''
586
+ _Names_ = EasNorAziRk4Tuple._Names_ + ('equatorarc', 'equatorazimuth')
587
+ _Units_ = EasNorAziRk4Tuple._Units_ + ( Degrees, Bearing)
588
+
589
+
590
+ class LatLonAziRk4Tuple(_NamedTuple):
591
+ '''4-Tuple C{(lat, lon, azimuth, reciprocal)}, all in C{degrees}
592
+ where C{azimuth} is the azimuth of easting direction and
593
+ C{reciprocal} the reciprocal of azimuthal northing scale.
594
+ '''
595
+ _Names_ = (_lat_, _lon_, _azimuth_, _reciprocal_)
596
+ _Units_ = ( Lat_, Lon_, Bearing, Scalar)
597
+
598
+
599
+ def toCss(latlon, cs0=None, height=None, Css=Css, name=NN):
600
+ '''Convert an (ellipsoidal) geodetic point to a Cassini-Soldner
601
+ location.
602
+
603
+ @arg latlon: Ellipsoidal point (C{LatLon} or L{LatLon4Tuple}).
604
+ @kwarg cs0: Optional, the Cassini-Soldner projection to use
605
+ (L{CassiniSoldner}).
606
+ @kwarg height: Optional height for the point, overriding the
607
+ default height (C{meter}).
608
+ @kwarg Css: Optional class to return the location (L{Css}) or C{None}.
609
+ @kwarg name: Optional B{C{Css}} name (C{str}).
610
+
611
+ @return: The Cassini-Soldner location (B{C{Css}}) or an
612
+ L{EasNor3Tuple}C{(easting, northing, height)}
613
+ if B{C{Css}} is C{None}.
614
+
615
+ @raise CSSError: Ellipsoidal mismatch of B{C{latlon}} and B{C{cs0}}.
616
+
617
+ @raise ImportError: Package U{geographiclib<https://PyPI.org/
618
+ project/geographiclib>} not installed or
619
+ not found.
620
+
621
+ @raise TypeError: If B{C{latlon}} is not ellipsoidal.
622
+ '''
623
+ _xinstanceof(_LLEB, LatLon4Tuple, latlon=latlon)
624
+
625
+ cs = _CS0(cs0)
626
+ cs._datumatch(latlon)
627
+
628
+ c = cs.forward4(latlon.lat, latlon.lon)
629
+ h = _heigHt(latlon, height)
630
+ n = name or nameof(latlon)
631
+
632
+ if Css is None:
633
+ r = EasNor3Tuple(c.easting, c.northing, h, name=n)
634
+ else:
635
+ r = Css(c.easting, c.northing, h=h, cs0=cs, name=n)
636
+ r._latlon = LatLon2Tuple(latlon.lat, latlon.lon, name=n)
637
+ r._azi, r._rk = c.azimuth, c.reciprocal
638
+ return r
639
+
640
+ # **) MIT License
641
+ #
642
+ # Copyright (C) 2016-2024 -- mrJean1 at Gmail -- All Rights Reserved.
643
+ #
644
+ # Permission is hereby granted, free of charge, to any person obtaining a
645
+ # copy of this software and associated documentation files (the "Software"),
646
+ # to deal in the Software without restriction, including without limitation
647
+ # the rights to use, copy, modify, merge, publish, distribute, sublicense,
648
+ # and/or sell copies of the Software, and to permit persons to whom the
649
+ # Software is furnished to do so, subject to the following conditions:
650
+ #
651
+ # The above copyright notice and this permission notice shall be included
652
+ # in all copies or substantial portions of the Software.
653
+ #
654
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
655
+ # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
656
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
657
+ # THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
658
+ # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
659
+ # ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
660
+ # OTHER DEALINGS IN THE SOFTWARE.