pygeodesy 24.7.24__py2.py3-none-any.whl → 24.8.4__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.
- {PyGeodesy-24.7.24.dist-info → PyGeodesy-24.8.4.dist-info}/METADATA +6 -6
- {PyGeodesy-24.7.24.dist-info → PyGeodesy-24.8.4.dist-info}/RECORD +26 -26
- pygeodesy/__init__.py +4 -5
- pygeodesy/auxilats/auxAngle.py +2 -2
- pygeodesy/auxilats/auxDST.py +2 -2
- pygeodesy/azimuthal.py +4 -4
- pygeodesy/cartesianBase.py +2 -2
- pygeodesy/css.py +5 -5
- pygeodesy/ellipsoids.py +3 -3
- pygeodesy/formy.py +2 -2
- pygeodesy/gars.py +59 -52
- pygeodesy/geodesici.py +4 -10
- pygeodesy/geohash.py +484 -267
- pygeodesy/geoids.py +6 -2
- pygeodesy/heights.py +30 -40
- pygeodesy/internals.py +19 -6
- pygeodesy/karney.py +2 -2
- pygeodesy/latlonBase.py +2 -2
- pygeodesy/lazily.py +17 -16
- pygeodesy/ltp.py +2 -2
- pygeodesy/ltpTuples.py +6 -6
- pygeodesy/triaxials.py +4 -4
- pygeodesy/units.py +34 -17
- pygeodesy/wgrs.py +93 -83
- {PyGeodesy-24.7.24.dist-info → PyGeodesy-24.8.4.dist-info}/WHEEL +0 -0
- {PyGeodesy-24.7.24.dist-info → PyGeodesy-24.8.4.dist-info}/top_level.txt +0 -0
pygeodesy/geohash.py
CHANGED
|
@@ -1,93 +1,47 @@
|
|
|
1
1
|
|
|
2
2
|
# -*- coding: utf-8 -*-
|
|
3
3
|
|
|
4
|
-
u'''Geohash
|
|
4
|
+
u'''I{Gustavo Niemeyer}’s U{Geohash<https://WikiPedia.org/wiki/Geohash>}.
|
|
5
5
|
|
|
6
|
-
|
|
7
|
-
|
|
6
|
+
Class L{Geohash} and several functions to encode, decode and inspect
|
|
7
|
+
C{geohashes} and optional L{Geohashed} caches.
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
and published under the same MIT Licence**, see
|
|
11
|
-
<https://www.Movable-Type.co.UK/scripts/geohash.html>}.
|
|
9
|
+
Originally transcoded from JavaScript originals by I{(C) Chris Veness
|
|
10
|
+
2011-2015} and published under the same MIT Licence**, see
|
|
11
|
+
U{Geohashes<https://www.Movable-Type.co.UK/scripts/geohash.html>}.
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
<https://
|
|
15
|
-
<https://
|
|
16
|
-
<https://
|
|
13
|
+
@see: U{Geohash<https://WikiPedia.org/wiki/Geohash>}, I{Karney}'s C++
|
|
14
|
+
U{Geohash<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Geohash.html>},
|
|
15
|
+
U{geohash<https://GitHub.com/vinsci/geohash>},
|
|
16
|
+
U{pygeohash<https://PyPI.org/project/pygeohash>} and
|
|
17
|
+
U{geohash-js<https://GitHub.com/DaveTroy/geohash-js>}.
|
|
17
18
|
'''
|
|
18
19
|
|
|
19
|
-
from pygeodesy.basics import
|
|
20
|
-
from pygeodesy.constants import EPS, R_M,
|
|
21
|
-
|
|
22
|
-
from pygeodesy.dms import parse3llh # parseDMS2
|
|
20
|
+
from pygeodesy.basics import isstr, map2
|
|
21
|
+
from pygeodesy.constants import EPS, R_M, _0_0, _0_5, _180_0, _360_0, \
|
|
22
|
+
_90_0, _N_90_0, _N_180_0 # PYCHOK used!
|
|
23
23
|
from pygeodesy.errors import _ValueError, _xkwds, _xStrError
|
|
24
|
-
from pygeodesy.fmath import favg
|
|
25
24
|
# from pygeodesy import formy as _formy # _MODS
|
|
26
|
-
from pygeodesy.interns import NN, _COMMA_, _DOT_, _E_, _N_, _NE_,
|
|
27
|
-
_S_, _SE_, _SW_, _W_
|
|
28
|
-
|
|
25
|
+
from pygeodesy.interns import NN, _COMMA_, _DOT_, _E_, _height_, _N_, _NE_, \
|
|
26
|
+
_NW_, _radius_, _S_, _SE_, _SPACE_, _SW_, _W_, \
|
|
27
|
+
_width_
|
|
28
|
+
from pygeodesy.lazily import _ALL_DOCS, _ALL_LAZY, _ALL_MODS as _MODS
|
|
29
29
|
from pygeodesy.named import _name__, _NamedDict, _NamedTuple, nameof, _xnamed
|
|
30
30
|
from pygeodesy.namedTuples import Bounds2Tuple, Bounds4Tuple, LatLon2Tuple, \
|
|
31
31
|
PhiLam2Tuple
|
|
32
32
|
from pygeodesy.props import deprecated_function, deprecated_method, \
|
|
33
|
-
deprecated_property_RO, Property_RO
|
|
34
|
-
|
|
35
|
-
from pygeodesy.
|
|
33
|
+
deprecated_property_RO, Property_RO, \
|
|
34
|
+
property_RO, property_ROver
|
|
35
|
+
# from pygeodesy.streprs import Fmt, fstr # _MODS
|
|
36
|
+
from pygeodesy.units import Degrees_, Int, Lat_, Lon_, Meter, Precision_, Str
|
|
36
37
|
|
|
37
38
|
from math import fabs, ldexp, log10, radians
|
|
38
39
|
|
|
39
40
|
__all__ = _ALL_LAZY.geohash
|
|
40
|
-
__version__ = '24.
|
|
41
|
+
__version__ = '24.08.02'
|
|
41
42
|
|
|
42
|
-
_formy
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
class _GH(object):
|
|
46
|
-
'''(INTERNAL) Lazily defined constants.
|
|
47
|
-
'''
|
|
48
|
-
def _4d(self, n, e, s, w): # helper
|
|
49
|
-
return dict(N=(n, e), S=(s, w),
|
|
50
|
-
E=(e, n), W=(w, s))
|
|
51
|
-
|
|
52
|
-
@Property_RO
|
|
53
|
-
def Borders(self):
|
|
54
|
-
return self._4d('prxz', 'bcfguvyz', '028b', '0145hjnp')
|
|
55
|
-
|
|
56
|
-
Bounds4 = (_N_90_0, _N_180_0, _90_0, _180_0)
|
|
57
|
-
|
|
58
|
-
@Property_RO
|
|
59
|
-
def DecodedBase32(self): # inverse GeohashBase32 map
|
|
60
|
-
return dict((c, i) for i, c in enumerate(self.GeohashBase32))
|
|
61
|
-
|
|
62
|
-
# Geohash-specific base32 map
|
|
63
|
-
GeohashBase32 = '0123456789bcdefghjkmnpqrstuvwxyz' # no a, i, j and o
|
|
64
|
-
|
|
65
|
-
@Property_RO
|
|
66
|
-
def Neighbors(self):
|
|
67
|
-
return self._4d('p0r21436x8zb9dcf5h7kjnmqesgutwvy',
|
|
68
|
-
'bc01fg45238967deuvhjyznpkmstqrwx',
|
|
69
|
-
'14365h7k9dcfesgujnmqp0r2twvyx8zb',
|
|
70
|
-
'238967debc01fg45kmstqrwxuvhjyznp')
|
|
71
|
-
|
|
72
|
-
@Property_RO
|
|
73
|
-
def Sizes(self): # lat-, lon and radial size (in meter)
|
|
74
|
-
# ... where radial = sqrt(latSize * lonWidth / PI)
|
|
75
|
-
_t = _floatuple
|
|
76
|
-
return (_t(20032e3, 20000e3, 11292815.096), # 0
|
|
77
|
-
_t( 5003e3, 5000e3, 2821794.075), # 1
|
|
78
|
-
_t( 650e3, 1225e3, 503442.397), # 2
|
|
79
|
-
_t( 156e3, 156e3, 88013.575), # 3
|
|
80
|
-
_t( 19500, 39100, 15578.683), # 4
|
|
81
|
-
_t( 4890, 4890, 2758.887), # 5
|
|
82
|
-
_t( 610, 1220, 486.710), # 6
|
|
83
|
-
_t( 153, 153, 86.321), # 7
|
|
84
|
-
_t( 19.1, 38.2, 15.239), # 8
|
|
85
|
-
_t( 4.77, 4.77, 2.691), # 9
|
|
86
|
-
_t( 0.596, 1.19, 0.475), # 10
|
|
87
|
-
_t( 0.149, 0.149, 0.084), # 11
|
|
88
|
-
_t( 0.0186, 0.0372, 0.015)) # 12 _MaxPrec
|
|
89
|
-
|
|
90
|
-
_GH = _GH() # PYCHOK singleton
|
|
43
|
+
_formy = _MODS.into(formy=__name__)
|
|
44
|
+
_MASK5 = 16, 8, 4, 2, 1 # PYCHOK used!
|
|
91
45
|
_MaxPrec = 12
|
|
92
46
|
|
|
93
47
|
|
|
@@ -106,16 +60,22 @@ def _2bounds(LatLon, LatLon_kwds, s, w, n, e, **name):
|
|
|
106
60
|
def _2center(bounds):
|
|
107
61
|
'''(INTERNAL) Return the C{bounds} center.
|
|
108
62
|
'''
|
|
109
|
-
return (
|
|
110
|
-
|
|
63
|
+
return (_2mid(bounds.latN, bounds.latS),
|
|
64
|
+
_2mid(bounds.lonE, bounds.lonW))
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _2dab(d, a, b):
|
|
68
|
+
'''(INTERNAL) Get delta lat or lon from center.
|
|
69
|
+
'''
|
|
70
|
+
return fabs(d - round(*_2mid_ndigits(a, b)))
|
|
111
71
|
|
|
112
72
|
|
|
113
73
|
def _2fll(lat, lon, *unused):
|
|
114
74
|
'''(INTERNAL) Convert lat, lon to 2-tuple of floats.
|
|
115
75
|
'''
|
|
116
76
|
# lat, lon = parseDMS2(lat, lon)
|
|
117
|
-
return (
|
|
118
|
-
|
|
77
|
+
return (Lat_(lat, Error=GeohashError),
|
|
78
|
+
Lon_(lon, Error=GeohashError))
|
|
119
79
|
|
|
120
80
|
|
|
121
81
|
def _2Geohash(geohash):
|
|
@@ -125,61 +85,226 @@ def _2Geohash(geohash):
|
|
|
125
85
|
Geohash(geohash)
|
|
126
86
|
|
|
127
87
|
|
|
128
|
-
def
|
|
129
|
-
'''(INTERNAL)
|
|
88
|
+
def _2latlon(s, w, n, e, fstr=None):
|
|
89
|
+
'''(INTERNAL) Get the center C{lat, lon}, rounded.
|
|
130
90
|
'''
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
91
|
+
lat, a = _2mid_ndigits(n, s)
|
|
92
|
+
lon, b = _2mid_ndigits(e, w)
|
|
93
|
+
return (fstr(lat, prec=a), fstr(lon, prec=b)) if fstr else \
|
|
94
|
+
(round(lat, a), round(lon, b))
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _2mid(a, b):
|
|
98
|
+
'''(INTERNAL) Bisect C{a} to C{b}.
|
|
99
|
+
'''
|
|
100
|
+
return (a + b) * _0_5 # favg
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _2mid_ndigits(a, b): # a > b
|
|
104
|
+
'''(INTERNAL) Return 2-tuple C{(_2mid, ndigits)}.
|
|
105
|
+
'''
|
|
106
|
+
# round to near centre without excessive
|
|
107
|
+
# precision to ⌊2-log10(Δ°)⌋ ndigits
|
|
108
|
+
return _2mid(a, b), int(2 - log10(a - b))
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _2Precision(p):
|
|
112
|
+
'''(INTERNAL) Get a valid C{Precision}.
|
|
113
|
+
'''
|
|
114
|
+
return Precision_(p, low=1, high=_MaxPrec, Error=GeohashError)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _2res(res, **prec):
|
|
118
|
+
'''(INTERNAL) Get the C{res}olution for a C{prec}ision.
|
|
119
|
+
'''
|
|
120
|
+
p = max(min(Int(Error=GeohashError, **prec), _MaxPrec), 0) * 5
|
|
121
|
+
p = (p - p // 2) if res > _180_0 else (p // 2)
|
|
122
|
+
return ldexp(res, -p) if p else res
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
class _GH(object):
|
|
126
|
+
'''(INTERNAL) Lazily defined constants.
|
|
127
|
+
'''
|
|
128
|
+
def _4d(self, s, w, n, e): # helper
|
|
129
|
+
return dict(S=(s, w), W=(w, s),
|
|
130
|
+
N=(n, e), E=(e, n))
|
|
131
|
+
|
|
132
|
+
@property_ROver
|
|
133
|
+
def Borders(self):
|
|
134
|
+
return self._4d('028b', '0145hjnp', 'prxz', 'bcfguvyz')
|
|
135
|
+
|
|
136
|
+
@property_ROver
|
|
137
|
+
def DecodeB32(self): # inverse EncodeB32 map
|
|
138
|
+
return dict((c, i) for i, c in enumerate(self.EncodeB32))
|
|
139
|
+
|
|
140
|
+
def decode2(self, geohash):
|
|
141
|
+
'''Decode C{geohash} to 2-tuple C{(lat, lon)}.
|
|
142
|
+
'''
|
|
143
|
+
swne = self.swne4(geohash)
|
|
144
|
+
return _2latlon(*swne)
|
|
145
|
+
|
|
146
|
+
# Geohash's base32 codes, no a, i, l and o
|
|
147
|
+
EncodeB32 = '0123456789bcdefghjkmnpqrstuvwxyz'
|
|
148
|
+
|
|
149
|
+
def encode(self, *lat_lon_prec_eps):
|
|
150
|
+
'''Encode C{lat, lon} to C{prec}ision or C{eps}.
|
|
151
|
+
'''
|
|
152
|
+
def _encodes(lat, lon, prec, eps=0):
|
|
153
|
+
s, w, n, e = self.SWNE4
|
|
154
|
+
E, d, _mid = self.EncodeB32, True, _2mid
|
|
155
|
+
for _ in range(prec):
|
|
156
|
+
i = 0
|
|
157
|
+
for _ in range(5): # len(_MASK5)
|
|
158
|
+
i += i
|
|
159
|
+
if d: # bisect longitude
|
|
160
|
+
a = _mid(e, w)
|
|
161
|
+
if lon < a:
|
|
162
|
+
e = a
|
|
163
|
+
else:
|
|
164
|
+
w = a
|
|
165
|
+
i += 1
|
|
166
|
+
else: # bisect latitude
|
|
167
|
+
a = _mid(n, s)
|
|
168
|
+
if lat < a:
|
|
169
|
+
n = a
|
|
170
|
+
else:
|
|
171
|
+
s = a
|
|
172
|
+
i += 1
|
|
173
|
+
d = not d
|
|
174
|
+
yield E[i]
|
|
175
|
+
if eps > 0: # infer prec
|
|
176
|
+
if _2dab(lon, e, w) < eps and \
|
|
177
|
+
_2dab(lat, n, s) < eps:
|
|
178
|
+
break
|
|
179
|
+
|
|
180
|
+
return NN.join(_encodes(*lat_lon_prec_eps))
|
|
181
|
+
|
|
182
|
+
def encode2(self, lat, lon, prec, eps):
|
|
183
|
+
'''Return 2-tuple C{geohash, (lat, lon))}.
|
|
184
|
+
'''
|
|
185
|
+
lat, lon = _2fll(lat, lon)
|
|
186
|
+
if prec:
|
|
187
|
+
p, e = _2Precision(prec), 0
|
|
188
|
+
else: # infer precision by refining geohash
|
|
189
|
+
p, e = _MaxPrec, max(eps, EPS)
|
|
190
|
+
return self.encode(lat, lon, p, e), (lat, lon)
|
|
191
|
+
|
|
192
|
+
@property_ROver
|
|
193
|
+
def _LatLon2Tuple(self):
|
|
194
|
+
|
|
195
|
+
class _LatLon2Tuple(_NamedTuple):
|
|
196
|
+
'''DEPRECATED on 2024.07.28, C{(lat, lon)} in B{C{meter}}, use L{Sizes3Tuple}.'''
|
|
197
|
+
_Names_ = LatLon2Tuple._Names_
|
|
198
|
+
_Units_ = Meter, Meter
|
|
199
|
+
|
|
200
|
+
return _LatLon2Tuple
|
|
201
|
+
|
|
202
|
+
@property_ROver
|
|
203
|
+
def Neighbors(self):
|
|
204
|
+
return self._4d('14365h7k9dcfesgujnmqp0r2twvyx8zb',
|
|
205
|
+
'238967debc01fg45kmstqrwxuvhjyznp',
|
|
206
|
+
'p0r21436x8zb9dcf5h7kjnmqesgutwvy',
|
|
207
|
+
'bc01fg45238967deuvhjyznpkmstqrwx')
|
|
208
|
+
|
|
209
|
+
@property_ROver
|
|
210
|
+
def Sizes(self): # height, width and radius (in meter)
|
|
211
|
+
# where radius = sqrt(height * width / PI), the
|
|
212
|
+
# radius of a circle with area (height * width)
|
|
213
|
+
T = Sizes3Tuple
|
|
214
|
+
return (T(20000e3, 20032e3, 11292815.096), # 0
|
|
215
|
+
T( 5000e3, 5003e3, 2821794.075), # 1
|
|
216
|
+
T( 650e3, 1225e3, 503442.397), # 2
|
|
217
|
+
T( 156e3, 156e3, 88013.575), # 3
|
|
218
|
+
T( 19500, 39100, 15578.683), # 4
|
|
219
|
+
T( 4890, 4890, 2758.887), # 5
|
|
220
|
+
T( 610, 1220, 486.710), # 6
|
|
221
|
+
T( 153, 153, 86.321), # 7
|
|
222
|
+
T( 19.1, 38.2, 15.239), # 8
|
|
223
|
+
T( 4.77, 4.77, 2.691), # 9
|
|
224
|
+
T( 0.596, 1.19, 0.475), # 10
|
|
225
|
+
T( 0.149, 0.149, 0.084), # 11
|
|
226
|
+
T( 0.0186, 0.0372, 0.015)) # 12 _MaxPrec
|
|
227
|
+
|
|
228
|
+
SWNE4 = (_N_90_0, _N_180_0, _90_0, _180_0)
|
|
229
|
+
|
|
230
|
+
def swne4(self, geohash, mask5=_MASK5):
|
|
231
|
+
'''Decode C{geohash} into 4-tuple C{(s, w, n, e)}.
|
|
232
|
+
'''
|
|
233
|
+
nc = len(geohash) if isstr(geohash) else 0
|
|
234
|
+
if not (0 < nc <= _MaxPrec):
|
|
235
|
+
raise GeohashError(geohash=geohash, len=nc)
|
|
236
|
+
s, w, n, e = self.SWNE4
|
|
237
|
+
D, d, _mid = self.DecodeB32, True, _2mid
|
|
238
|
+
try:
|
|
239
|
+
for j, c in enumerate(geohash.lower()):
|
|
240
|
+
i = D[c]
|
|
241
|
+
for m in mask5:
|
|
242
|
+
if d: # longitude
|
|
243
|
+
a = _mid(e, w)
|
|
244
|
+
if (i & m):
|
|
245
|
+
w = a
|
|
246
|
+
else:
|
|
247
|
+
e = a
|
|
248
|
+
else: # latitude
|
|
249
|
+
a = _mid(n, s)
|
|
250
|
+
if (i & m):
|
|
251
|
+
s = a
|
|
252
|
+
else:
|
|
253
|
+
n = a
|
|
254
|
+
d = not d
|
|
255
|
+
except KeyError:
|
|
256
|
+
c = _MODS.streprs.Fmt.INDEX(repr(c), j)
|
|
257
|
+
raise GeohashError(geohash=geohash, len=nc, txt=c)
|
|
258
|
+
return s, w, n, e
|
|
259
|
+
|
|
260
|
+
_GH = _GH() # PYCHOK singleton
|
|
141
261
|
|
|
142
262
|
|
|
143
263
|
class Geohash(Str):
|
|
144
264
|
'''Geohash class, a named C{str}.
|
|
145
265
|
'''
|
|
146
266
|
# no str.__init__ in Python 3
|
|
147
|
-
def __new__(cls,
|
|
148
|
-
'''New L{Geohash} from an other L{Geohash} instance or C{str}
|
|
149
|
-
or from a
|
|
150
|
-
|
|
151
|
-
@arg
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
267
|
+
def __new__(cls, lat_ghll, lon=None, precision=None, eps=EPS, **name):
|
|
268
|
+
'''New L{Geohash} from an other L{Geohash} instance or geohash C{str}
|
|
269
|
+
or from a lat- and longitude.
|
|
270
|
+
|
|
271
|
+
@arg lat_ghll: Latitude (C{degrees90}), a geohash (L{Geohash},
|
|
272
|
+
C{str}) or a location (C{LatLon}, C{LatLon*Tuple}).
|
|
273
|
+
@kwarg lon: Logitude (C{degrees180)}, required if B{C{lat_ghll}}
|
|
274
|
+
is C{degrees90}, ignored otherwise.
|
|
275
|
+
@kwarg precision: The desired geohash length (C{int} 1..12) or
|
|
276
|
+
C{None} or C{0}, see L{encode<pygeodesy.geohash.encode>}.
|
|
277
|
+
@kwarg eps: Optional inference tolerance (C{degrees}), see
|
|
278
|
+
L{encode<pygeodesy.geohash.encode>}.
|
|
155
279
|
@kwarg name: Optional C{B{name}=NN} (C{str}).
|
|
156
280
|
|
|
157
281
|
@return: New L{Geohash}.
|
|
158
282
|
|
|
159
|
-
@raise GeohashError:
|
|
283
|
+
@raise GeohashError: Invalid B{C{lat_ghll}}.
|
|
160
284
|
|
|
161
|
-
@raise
|
|
162
|
-
'''
|
|
163
|
-
ll = None
|
|
164
|
-
|
|
165
|
-
if isinstance(cll, Geohash):
|
|
166
|
-
gh = _2geostr(str(cll))
|
|
167
|
-
|
|
168
|
-
elif isstr(cll):
|
|
169
|
-
if _COMMA_ in cll:
|
|
170
|
-
ll = _2fll(*parse3llh(cll))
|
|
171
|
-
gh = encode(*ll, precision=precision)
|
|
172
|
-
else:
|
|
173
|
-
gh = _2geostr(cll)
|
|
174
|
-
|
|
175
|
-
else: # assume LatLon
|
|
176
|
-
try:
|
|
177
|
-
ll = _2fll(cll.lat, cll.lon)
|
|
178
|
-
gh = encode(*ll, precision=precision)
|
|
179
|
-
except AttributeError:
|
|
180
|
-
raise _xStrError(Geohash, cll=cll, Error=GeohashError)
|
|
285
|
+
@raise RangeError: Invalid B{C{lat_gll}} or B{C{lon}}.
|
|
181
286
|
|
|
182
|
-
|
|
287
|
+
@raise TypeError: Invalid B{C{lat_ghll}}.
|
|
288
|
+
'''
|
|
289
|
+
if lon is None:
|
|
290
|
+
if isinstance(lat_ghll, Geohash):
|
|
291
|
+
gh, ll = str(lat_ghll), lat_ghll.latlon
|
|
292
|
+
elif isstr(lat_ghll): # "lat, lon" or "geohash"
|
|
293
|
+
ll = lat_ghll.replace(_COMMA_, _SPACE_).split()
|
|
294
|
+
if len(ll) > 1:
|
|
295
|
+
gh, ll = _GH.encode2(ll[0], ll[1], precision, eps)
|
|
296
|
+
else:
|
|
297
|
+
gh, ll = lat_ghll.lower(), None
|
|
298
|
+
_ = _GH.swne4(gh, mask5=()) # validate
|
|
299
|
+
else: # assume LatLon
|
|
300
|
+
try:
|
|
301
|
+
gh, ll = _GH.encode2(lat_ghll.lat, lat_ghll.lon, precision, eps)
|
|
302
|
+
except AttributeError:
|
|
303
|
+
raise _xStrError(Geohash, ghll=lat_ghll, Error=GeohashError)
|
|
304
|
+
else:
|
|
305
|
+
gh, ll = _GH.encode2(lat_ghll, lon, precision, eps)
|
|
306
|
+
|
|
307
|
+
self = Str.__new__(cls, gh, name=_name__(name, _or_nameof=lat_ghll))
|
|
183
308
|
self._latlon = ll
|
|
184
309
|
return self
|
|
185
310
|
|
|
@@ -205,7 +330,7 @@ class Geohash(Str):
|
|
|
205
330
|
if D not in _GH.Neighbors:
|
|
206
331
|
raise GeohashError(direction=direction)
|
|
207
332
|
|
|
208
|
-
e = 1
|
|
333
|
+
e = len(self) & 1 # int(isodd(len(self)))
|
|
209
334
|
|
|
210
335
|
c = self[-1:] # last hash char
|
|
211
336
|
i = _GH.Neighbors[D][e].find(c)
|
|
@@ -221,7 +346,7 @@ class Geohash(Str):
|
|
|
221
346
|
if n:
|
|
222
347
|
n = _DOT_(n, D)
|
|
223
348
|
# append letter for direction to parent
|
|
224
|
-
return Geohash(p + _GH.
|
|
349
|
+
return Geohash(p + _GH.EncodeB32[i], name=n)
|
|
225
350
|
|
|
226
351
|
@Property_RO
|
|
227
352
|
def _bounds(self):
|
|
@@ -270,7 +395,7 @@ class Geohash(Str):
|
|
|
270
395
|
for n in range(n):
|
|
271
396
|
if self[n] != other[n]:
|
|
272
397
|
break
|
|
273
|
-
return _GH.Sizes[n]
|
|
398
|
+
return _GH.Sizes[n].radius
|
|
274
399
|
|
|
275
400
|
@deprecated_method
|
|
276
401
|
def distance1To(self, other): # PYCHOK no cover
|
|
@@ -382,14 +507,20 @@ class Geohash(Str):
|
|
|
382
507
|
'''
|
|
383
508
|
return len(self)
|
|
384
509
|
|
|
385
|
-
@
|
|
510
|
+
@deprecated_property_RO
|
|
386
511
|
def sizes(self):
|
|
387
|
-
'''
|
|
388
|
-
|
|
512
|
+
'''DEPRECATED on 2024.07.28, use property C{Geohash.sizes3}.'''
|
|
513
|
+
t = self.sizes3
|
|
514
|
+
return _GH._LatLon2Tuple(t.height, t.width, name=t.name)
|
|
515
|
+
|
|
516
|
+
@Property_RO
|
|
517
|
+
def sizes3(self):
|
|
518
|
+
'''Get the lat-, longitudinal and radial size of this cell as
|
|
519
|
+
a L{Sizes3Tuple}C{(height, width, radius)}, all C{meter}.
|
|
389
520
|
'''
|
|
390
521
|
z = _GH.Sizes
|
|
391
522
|
n = min(len(z) - 1, max(self.precision, 1))
|
|
392
|
-
return
|
|
523
|
+
return Sizes3Tuple(z[n], name=self.name)
|
|
393
524
|
|
|
394
525
|
def toLatLon(self, LatLon=None, **LatLon_kwds):
|
|
395
526
|
'''Return (the approximate center of) this geohash cell
|
|
@@ -423,18 +554,6 @@ class Geohash(Str):
|
|
|
423
554
|
'''
|
|
424
555
|
return self._distanceTo(_formy.vincentys, other, **radius_wrap)
|
|
425
556
|
|
|
426
|
-
@Property_RO
|
|
427
|
-
def N(self):
|
|
428
|
-
'''Get the cell North of this (L{Geohash}).
|
|
429
|
-
'''
|
|
430
|
-
return self.adjacent(_N_)
|
|
431
|
-
|
|
432
|
-
@Property_RO
|
|
433
|
-
def S(self):
|
|
434
|
-
'''Get the cell South of this (L{Geohash}).
|
|
435
|
-
'''
|
|
436
|
-
return self.adjacent(_S_)
|
|
437
|
-
|
|
438
557
|
@Property_RO
|
|
439
558
|
def E(self):
|
|
440
559
|
'''Get the cell East of this (L{Geohash}).
|
|
@@ -442,10 +561,10 @@ class Geohash(Str):
|
|
|
442
561
|
return self.adjacent(_E_)
|
|
443
562
|
|
|
444
563
|
@Property_RO
|
|
445
|
-
def
|
|
446
|
-
'''Get the cell
|
|
564
|
+
def N(self):
|
|
565
|
+
'''Get the cell North of this (L{Geohash}).
|
|
447
566
|
'''
|
|
448
|
-
return self.adjacent(
|
|
567
|
+
return self.adjacent(_N_)
|
|
449
568
|
|
|
450
569
|
@Property_RO
|
|
451
570
|
def NE(self):
|
|
@@ -459,6 +578,12 @@ class Geohash(Str):
|
|
|
459
578
|
'''
|
|
460
579
|
return self.N.W
|
|
461
580
|
|
|
581
|
+
@Property_RO
|
|
582
|
+
def S(self):
|
|
583
|
+
'''Get the cell South of this (L{Geohash}).
|
|
584
|
+
'''
|
|
585
|
+
return self.adjacent(_S_)
|
|
586
|
+
|
|
462
587
|
@Property_RO
|
|
463
588
|
def SE(self):
|
|
464
589
|
'''Get the cell SouthEast of this (L{Geohash}).
|
|
@@ -471,6 +596,124 @@ class Geohash(Str):
|
|
|
471
596
|
'''
|
|
472
597
|
return self.S.W
|
|
473
598
|
|
|
599
|
+
@Property_RO
|
|
600
|
+
def W(self):
|
|
601
|
+
'''Get the cell West of this (L{Geohash}).
|
|
602
|
+
'''
|
|
603
|
+
return self.adjacent(_W_)
|
|
604
|
+
|
|
605
|
+
|
|
606
|
+
class Geohashed(object):
|
|
607
|
+
'''A cache of en- and decoded geohashes of one precision.
|
|
608
|
+
'''
|
|
609
|
+
_nn = None, # 1-tuple
|
|
610
|
+
|
|
611
|
+
def __init__(self, precision, ndigits=None):
|
|
612
|
+
'''New L{Geohashed} cache.
|
|
613
|
+
|
|
614
|
+
@arg precision: The geohash encoded length (C{int}, 1..12).
|
|
615
|
+
@kwarg ndigits: Optional number of digits to round C{lat}
|
|
616
|
+
and C{lon} to cache keys (C{int}, typically
|
|
617
|
+
C{B{ndigits}=B{precision}}) or C{None} for
|
|
618
|
+
no rounding.
|
|
619
|
+
'''
|
|
620
|
+
self._p = _2Precision(precision)
|
|
621
|
+
if ndigits is None:
|
|
622
|
+
self._ab2 = self._ab2float
|
|
623
|
+
else:
|
|
624
|
+
self._ab2 = self._ab2round
|
|
625
|
+
n = Int(ndigits=ndigits)
|
|
626
|
+
self._nn = n, n
|
|
627
|
+
self.clear()
|
|
628
|
+
|
|
629
|
+
def __len__(self):
|
|
630
|
+
'''Return the number of I{unigue} geohashes (C{int}).
|
|
631
|
+
'''
|
|
632
|
+
d = self._d
|
|
633
|
+
d = set(d.keys())
|
|
634
|
+
n = len(d)
|
|
635
|
+
for e in self._e.values():
|
|
636
|
+
e = set(e.values())
|
|
637
|
+
n += len(e - d)
|
|
638
|
+
return n
|
|
639
|
+
|
|
640
|
+
def _ab2(self, *ll): # overwritten
|
|
641
|
+
'''(INTERNAL) Make encoded keys C{a, b}.
|
|
642
|
+
'''
|
|
643
|
+
return ll
|
|
644
|
+
|
|
645
|
+
def _ab2float(self, *ll):
|
|
646
|
+
'''(INTERNAL) Make encoded keys C{a, b}.
|
|
647
|
+
'''
|
|
648
|
+
return map(float, ll)
|
|
649
|
+
|
|
650
|
+
def _ab2round(self, *ll):
|
|
651
|
+
'''(INTERNAL) Make encoded keys C{a, b}.
|
|
652
|
+
'''
|
|
653
|
+
return map(round, ll, self._nn)
|
|
654
|
+
|
|
655
|
+
def clear(self):
|
|
656
|
+
'''Clear the C{en-} and C{decoded} cache.
|
|
657
|
+
'''
|
|
658
|
+
self._e = {}
|
|
659
|
+
self._d = {}
|
|
660
|
+
|
|
661
|
+
def decoded(self, geohash, encoded=False):
|
|
662
|
+
'''Get and cache the C{(lat, lon)} for C{geohash}, see L{decode<pygeodesy.geohash.decode>}.
|
|
663
|
+
|
|
664
|
+
@kwarg encoded: If C{True}, cache the result as C{encoded}.
|
|
665
|
+
|
|
666
|
+
@return: The C{(lat, lon}) pair for C{geohash}.
|
|
667
|
+
'''
|
|
668
|
+
try:
|
|
669
|
+
ll = self._d[geohash]
|
|
670
|
+
except KeyError:
|
|
671
|
+
self._d[geohash] = ll = _GH.decode2(geohash)
|
|
672
|
+
if encoded:
|
|
673
|
+
a, b = self._ab2(*ll)
|
|
674
|
+
try:
|
|
675
|
+
_ = self._e[b][a]
|
|
676
|
+
except KeyError:
|
|
677
|
+
self._e.setdefault(b, {})[a] = geohash
|
|
678
|
+
return ll
|
|
679
|
+
|
|
680
|
+
def encoded(self, lat, lon, decoded=False):
|
|
681
|
+
'''Get and cache the C{geohash} for C{(lat, lon)}, see L{encode<pygeodesy.geohash.encode>}.
|
|
682
|
+
|
|
683
|
+
@kwarg decoded: If C{True}, cache the result as C{decoded}.
|
|
684
|
+
|
|
685
|
+
@return: The C{geohash} for pair C{(lat, lon}).
|
|
686
|
+
'''
|
|
687
|
+
lat, lon = ll = _2fll(lat, lon)
|
|
688
|
+
a, b = self._ab2(*ll)
|
|
689
|
+
try:
|
|
690
|
+
gh = self._e[b][a]
|
|
691
|
+
except KeyError:
|
|
692
|
+
gh = _GH.encode(lat, lon, self._p, 0)
|
|
693
|
+
self._e.setdefault(b, {})[a] = gh
|
|
694
|
+
if decoded and gh not in self._d:
|
|
695
|
+
self._d[gh] = ll
|
|
696
|
+
return gh
|
|
697
|
+
|
|
698
|
+
@property_RO
|
|
699
|
+
def len2(self):
|
|
700
|
+
'''Return 2-tuple C{(lencoded, ldecoded)} with the C{len}gths of the
|
|
701
|
+
C{en-} and C{decoded} cache.
|
|
702
|
+
'''
|
|
703
|
+
return sum(len(e) for e in self._e.values()), len(self._d)
|
|
704
|
+
|
|
705
|
+
@Property_RO
|
|
706
|
+
def ndigits(self):
|
|
707
|
+
'''Get the rounding (C{int} or C{None}).
|
|
708
|
+
'''
|
|
709
|
+
return self._nn[0]
|
|
710
|
+
|
|
711
|
+
@Property_RO
|
|
712
|
+
def precision(self):
|
|
713
|
+
'''Get the C{precision} (C{int}).
|
|
714
|
+
'''
|
|
715
|
+
return self._p
|
|
716
|
+
|
|
474
717
|
|
|
475
718
|
class GeohashError(_ValueError):
|
|
476
719
|
'''Geohash encode, decode or other L{Geohash} issue.
|
|
@@ -493,6 +736,34 @@ _Neighbors8Defaults = dict(zip(Neighbors8Dict._Keys_, (None,) *
|
|
|
493
736
|
len(Neighbors8Dict._Keys_))) # XXX frozendict
|
|
494
737
|
|
|
495
738
|
|
|
739
|
+
class Resolutions2Tuple(_NamedTuple):
|
|
740
|
+
'''2-Tuple C{(res1, res2)} with the primary I{(longitudinal)} and
|
|
741
|
+
secondary I{(latitudinal)} resolution, both in C{degrees}.
|
|
742
|
+
'''
|
|
743
|
+
_Names_ = ('res1', 'res2')
|
|
744
|
+
_Units_ = ( Degrees_, Degrees_)
|
|
745
|
+
|
|
746
|
+
@property_RO
|
|
747
|
+
def lat(self):
|
|
748
|
+
'''Get the secondary, latitudinal resolution (C{degrees}).
|
|
749
|
+
'''
|
|
750
|
+
return self[1]
|
|
751
|
+
|
|
752
|
+
@property_RO
|
|
753
|
+
def lon(self):
|
|
754
|
+
'''Get the primary, longitudinal resolution (C{degrees}).
|
|
755
|
+
'''
|
|
756
|
+
return self[0]
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
class Sizes3Tuple(_NamedTuple):
|
|
760
|
+
'''3-Tuple C{(height, width, radius)} with latitudinal C{height},
|
|
761
|
+
longitudinal C{width} and area C{radius}, all in C{meter}.
|
|
762
|
+
'''
|
|
763
|
+
_Names_ = (_height_, _width_, _radius_)
|
|
764
|
+
_Units_ = ( Meter, Meter, Meter)
|
|
765
|
+
|
|
766
|
+
|
|
496
767
|
def bounds(geohash, LatLon=None, **LatLon_kwds):
|
|
497
768
|
'''Returns the lower-left SW and upper-right NE corners of a geohash.
|
|
498
769
|
|
|
@@ -510,43 +781,11 @@ def bounds(geohash, LatLon=None, **LatLon_kwds):
|
|
|
510
781
|
|
|
511
782
|
@raise GeohashError: Invalid or C{null} B{C{geohash}}.
|
|
512
783
|
'''
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
raise GeohashError(geohash=geohash)
|
|
516
|
-
|
|
517
|
-
s, w, n, e = _GH.Bounds4
|
|
518
|
-
try:
|
|
519
|
-
d, _avg = True, favg
|
|
520
|
-
for c in gh.lower():
|
|
521
|
-
i = _GH.DecodedBase32[c]
|
|
522
|
-
for m in (16, 8, 4, 2, 1):
|
|
523
|
-
if d: # longitude
|
|
524
|
-
a = _avg(w, e)
|
|
525
|
-
if (i & m):
|
|
526
|
-
w = a
|
|
527
|
-
else:
|
|
528
|
-
e = a
|
|
529
|
-
else: # latitude
|
|
530
|
-
a = _avg(s, n)
|
|
531
|
-
if (i & m):
|
|
532
|
-
s = a
|
|
533
|
-
else:
|
|
534
|
-
n = a
|
|
535
|
-
d = not d
|
|
536
|
-
except KeyError:
|
|
537
|
-
raise GeohashError(geohash=geohash)
|
|
538
|
-
|
|
539
|
-
return _2bounds(LatLon, LatLon_kwds, s, w, n, e,
|
|
784
|
+
swne = _GH.swne4(geohash)
|
|
785
|
+
return _2bounds(LatLon, LatLon_kwds, *swne,
|
|
540
786
|
name=nameof(geohash)) # _or_nameof=geohash
|
|
541
787
|
|
|
542
788
|
|
|
543
|
-
def _bounds3(geohash):
|
|
544
|
-
'''(INTERNAL) Return 3-tuple C{(bounds, height, width)}.
|
|
545
|
-
'''
|
|
546
|
-
b = bounds(geohash)
|
|
547
|
-
return b, (b.latN - b.latS), (b.lonE - b.lonW)
|
|
548
|
-
|
|
549
|
-
|
|
550
789
|
def decode(geohash):
|
|
551
790
|
'''Decode a geohash to lat-/longitude of the (approximate
|
|
552
791
|
centre of) geohash cell to reasonable precision.
|
|
@@ -560,13 +799,10 @@ def decode(geohash):
|
|
|
560
799
|
|
|
561
800
|
@raise GeohashError: Invalid or null B{C{geohash}}.
|
|
562
801
|
'''
|
|
563
|
-
b, h, w = _bounds3(geohash)
|
|
564
|
-
lat, lon = _2center(b)
|
|
565
|
-
|
|
566
802
|
# round to near centre without excessive precision to
|
|
567
803
|
# ⌊2-log10(Δ°)⌋ decimal places, strip trailing zeros
|
|
568
|
-
|
|
569
|
-
|
|
804
|
+
swne = _GH.swne4(geohash)
|
|
805
|
+
return _2latlon(*swne, fstr=_MODS.streprs.fstr)
|
|
570
806
|
|
|
571
807
|
|
|
572
808
|
def decode2(geohash, LatLon=None, **LatLon_kwds):
|
|
@@ -587,14 +823,20 @@ def decode2(geohash, LatLon=None, **LatLon_kwds):
|
|
|
587
823
|
|
|
588
824
|
@raise GeohashError: Invalid or null B{C{geohash}}.
|
|
589
825
|
'''
|
|
590
|
-
|
|
591
|
-
r
|
|
826
|
+
ll = _GH.decode2(geohash)
|
|
827
|
+
r = LatLon2Tuple(ll) if LatLon is None else \
|
|
828
|
+
LatLon( *ll, **LatLon_kwds)
|
|
592
829
|
return _xnamed(r, name__=decode2)
|
|
593
830
|
|
|
594
831
|
|
|
832
|
+
@deprecated_function
|
|
595
833
|
def decode_error(geohash):
|
|
596
|
-
'''
|
|
597
|
-
|
|
834
|
+
'''DEPRECATED on 2024.07.28, use L{geohash.decode_error2}.'''
|
|
835
|
+
return decode_error2(geohash)
|
|
836
|
+
|
|
837
|
+
|
|
838
|
+
def decode_error2(geohash):
|
|
839
|
+
'''Return the lat- and longitude decoding error for a geohash.
|
|
598
840
|
|
|
599
841
|
@arg geohash: To be decoded (L{Geohash}).
|
|
600
842
|
|
|
@@ -606,9 +848,9 @@ def decode_error(geohash):
|
|
|
606
848
|
|
|
607
849
|
@raise GeohashError: Invalid or null B{C{geohash}}.
|
|
608
850
|
'''
|
|
609
|
-
|
|
610
|
-
return LatLon2Tuple(
|
|
611
|
-
w * _0_5) #
|
|
851
|
+
s, w, n, e = _GH.swne4(geohash)
|
|
852
|
+
return LatLon2Tuple((n - s) * _0_5, # lat error
|
|
853
|
+
(e - w) * _0_5) # lon error
|
|
612
854
|
|
|
613
855
|
|
|
614
856
|
def distance_(geohash1, geohash2):
|
|
@@ -643,67 +885,23 @@ def distance3(geohash1, geohash2):
|
|
|
643
885
|
return haversine_(geohash1, geohash2)
|
|
644
886
|
|
|
645
887
|
|
|
646
|
-
def encode(lat, lon, precision=None):
|
|
888
|
+
def encode(lat, lon, precision=None, eps=EPS):
|
|
647
889
|
'''Encode a lat-/longitude as a C{geohash}, either to the specified
|
|
648
|
-
precision or if not provided, to an
|
|
649
|
-
precision.
|
|
890
|
+
precision or if not provided, to an inferred precision.
|
|
650
891
|
|
|
651
|
-
@arg lat: Latitude (C{
|
|
652
|
-
@arg lon: Longitude (C{
|
|
653
|
-
@kwarg precision:
|
|
654
|
-
|
|
892
|
+
@arg lat: Latitude (C{degrees90}).
|
|
893
|
+
@arg lon: Longitude (C{degrees180}).
|
|
894
|
+
@kwarg precision: The desired geohash length (C{int} 1..12) or
|
|
895
|
+
C{None} or C{0} for inferred.
|
|
896
|
+
@kwarg eps: Optional inference tolerance (C{degrees}), ignored
|
|
897
|
+
if B{C{precision}} is not C{None} or C{0}.
|
|
655
898
|
|
|
656
899
|
@return: The C{geohash} (C{str}).
|
|
657
900
|
|
|
658
901
|
@raise GeohashError: Invalid B{C{lat}}, B{C{lon}} or B{C{precision}}.
|
|
659
902
|
'''
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
if precision is None:
|
|
663
|
-
# Infer precision by refining geohash until
|
|
664
|
-
# it matches precision of supplied lat/lon.
|
|
665
|
-
for p in range(1, _MaxPrec + 1):
|
|
666
|
-
gh = encode(lat, lon, p)
|
|
667
|
-
ll = map2(float, decode(gh))
|
|
668
|
-
if fabs(lat - ll[0]) < EPS and \
|
|
669
|
-
fabs(lon - ll[1]) < EPS:
|
|
670
|
-
return gh
|
|
671
|
-
p = _MaxPrec
|
|
672
|
-
else:
|
|
673
|
-
p = Precision_(precision, Error=GeohashError, low=1, high=_MaxPrec)
|
|
674
|
-
|
|
675
|
-
b = i = 0
|
|
676
|
-
d, gh = True, []
|
|
677
|
-
s, w, n, e = _GH.Bounds4
|
|
678
|
-
|
|
679
|
-
_avg = favg
|
|
680
|
-
while p > 0:
|
|
681
|
-
i += i
|
|
682
|
-
if d: # bisect longitude
|
|
683
|
-
m = _avg(e, w)
|
|
684
|
-
if lon < m:
|
|
685
|
-
e = m
|
|
686
|
-
else:
|
|
687
|
-
w = m
|
|
688
|
-
i += 1
|
|
689
|
-
else: # bisect latitude
|
|
690
|
-
m = _avg(n, s)
|
|
691
|
-
if lat < m:
|
|
692
|
-
n = m
|
|
693
|
-
else:
|
|
694
|
-
s = m
|
|
695
|
-
i += 1
|
|
696
|
-
d = not d
|
|
697
|
-
|
|
698
|
-
b += 1
|
|
699
|
-
if b == 5:
|
|
700
|
-
# 5 bits gives a character:
|
|
701
|
-
# append it and start over
|
|
702
|
-
gh.append(_GH.GeohashBase32[i])
|
|
703
|
-
b = i = 0
|
|
704
|
-
p -= 1
|
|
705
|
-
|
|
706
|
-
return NN.join(gh)
|
|
903
|
+
gh, _ = _GH.encode2(lat, lon, precision, eps)
|
|
904
|
+
return gh
|
|
707
905
|
|
|
708
906
|
|
|
709
907
|
def equirectangular4(geohash1, geohash2, radius=R_M):
|
|
@@ -799,14 +997,6 @@ def precision(res1, res2=None):
|
|
|
799
997
|
return _MaxPrec
|
|
800
998
|
|
|
801
999
|
|
|
802
|
-
class Resolutions2Tuple(_NamedTuple):
|
|
803
|
-
'''2-Tuple C{(res1, res2)} with the primary I{(longitudinal)} and
|
|
804
|
-
secondary I{(latitudinal)} resolution, both in C{degrees}.
|
|
805
|
-
'''
|
|
806
|
-
_Names_ = ('res1', 'res2')
|
|
807
|
-
_Units_ = ( Degrees_, Degrees_)
|
|
808
|
-
|
|
809
|
-
|
|
810
1000
|
def resolution2(prec1, prec2=None):
|
|
811
1001
|
'''Determine the (geographic) resolutions of given L{Geohash}
|
|
812
1002
|
precisions.
|
|
@@ -817,38 +1007,37 @@ def resolution2(prec1, prec2=None):
|
|
|
817
1007
|
(C{int} 1..12).
|
|
818
1008
|
|
|
819
1009
|
@return: L{Resolutions2Tuple}C{(res1, res2)} with the
|
|
820
|
-
(geographic) resolutions C{degrees}, where C{res2
|
|
821
|
-
B{
|
|
1010
|
+
(geographic) resolutions C{degrees}, where C{res2
|
|
1011
|
+
B{is} res1} if no B{C{prec2}} is given.
|
|
822
1012
|
|
|
823
1013
|
@raise GeohashError: Invalid B{C{prec1}} or B{C{prec2}}.
|
|
824
1014
|
|
|
825
1015
|
@see: I{Karney}'s C++ class U{Geohash
|
|
826
1016
|
<https://GeographicLib.SourceForge.io/C++/doc/classGeographicLib_1_1Geohash.html>}.
|
|
827
1017
|
'''
|
|
828
|
-
|
|
1018
|
+
lon = _2res(_360_0, prec1=prec1)
|
|
1019
|
+
lat = lon if prec2 is None else \
|
|
1020
|
+
_2res(_180_0, prec2=prec2)
|
|
1021
|
+
return Resolutions2Tuple(lon, lat)
|
|
829
1022
|
|
|
830
|
-
if prec1:
|
|
831
|
-
p = 5 * max(0, min(Int(prec1=prec1, Error=GeohashError), _MaxPrec))
|
|
832
|
-
res1 = res2 = ldexp(res1, -(p - p // 2))
|
|
833
1023
|
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
1024
|
+
@deprecated_function
|
|
1025
|
+
def sizes(geohash):
|
|
1026
|
+
'''DEPRECATED on 2024.07.28, use function L{pygeodesy.geohash.sizes3}.'''
|
|
1027
|
+
t = sizes3(geohash)
|
|
1028
|
+
return _GH._LatLon2Tuple(t.height, t.width, name=t.name)
|
|
837
1029
|
|
|
838
|
-
return Resolutions2Tuple(res1, res2)
|
|
839
1030
|
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
'''Return the lat- and longitudinal size of this L{Geohash} cell.
|
|
1031
|
+
def sizes3(geohash):
|
|
1032
|
+
'''Return the lat-, longitudinal and radial size of this L{Geohash} cell.
|
|
843
1033
|
|
|
844
1034
|
@arg geohash: Cell for which size are required (L{Geohash} or C{str}).
|
|
845
1035
|
|
|
846
|
-
@return: A L{
|
|
847
|
-
longitudinal width in (C{meter}).
|
|
1036
|
+
@return: A L{Sizes3Tuple}C{(height, width, radius)}, all C{meter}.
|
|
848
1037
|
|
|
849
1038
|
@raise TypeError: The B{C{geohash}} is not a L{Geohash}, C{LatLon} or C{str}.
|
|
850
1039
|
'''
|
|
851
|
-
return _2Geohash(geohash).
|
|
1040
|
+
return _2Geohash(geohash).sizes3
|
|
852
1041
|
|
|
853
1042
|
|
|
854
1043
|
def vincentys_(geohash1, geohash2, **radius_wrap):
|
|
@@ -868,10 +1057,38 @@ def vincentys_(geohash1, geohash2, **radius_wrap):
|
|
|
868
1057
|
return _2Geohash(geohash1).vincentysTo(geohash2, **radius_wrap)
|
|
869
1058
|
|
|
870
1059
|
|
|
871
|
-
__all__ +=
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
1060
|
+
__all__ += _ALL_DOCS(bounds, # functions
|
|
1061
|
+
decode, decode2, decode_error2, distance_,
|
|
1062
|
+
encode, equirectangular4, euclidean_, haversine_,
|
|
1063
|
+
neighbors, precision, resolution2, sizes3, vincentys_,
|
|
1064
|
+
decode_error, sizes) # DEPRECATED
|
|
1065
|
+
|
|
1066
|
+
if __name__ == '__main__':
|
|
1067
|
+
|
|
1068
|
+
from pygeodesy.internals import printf, _versions
|
|
1069
|
+
from timeit import timeit
|
|
1070
|
+
|
|
1071
|
+
for f, p in (('encode', _MaxPrec), ('infer', None)):
|
|
1072
|
+
|
|
1073
|
+
def _t(prec=p):
|
|
1074
|
+
i = 0
|
|
1075
|
+
for lat in range(-90, 90, 3):
|
|
1076
|
+
for lon in range(-180, 180, 7):
|
|
1077
|
+
_ = encode(lat, lon, prec)
|
|
1078
|
+
i += 1
|
|
1079
|
+
return i
|
|
1080
|
+
|
|
1081
|
+
i = _t() # prime
|
|
1082
|
+
n = 10
|
|
1083
|
+
t = timeit(_t, number=n) / (i * n)
|
|
1084
|
+
printf('%s %.3f usec, %s', f, t * 1e6, _versions())
|
|
1085
|
+
|
|
1086
|
+
# % python3.12 -m pygeodesy.geohash
|
|
1087
|
+
# encode 10.145 usec, pygeodesy 24.8.4 Python 3.12.4 64bit arm64 macOS 14.5
|
|
1088
|
+
# infer 14.780 usec, pygeodesy 24.8.4 Python 3.12.4 64bit arm64 macOS 14.5
|
|
1089
|
+
# or about 6.56 and 74.12 times faster than pygeodesy 24.7.24 and older:
|
|
1090
|
+
# encode 66.524 usec, pygeodesy 24.7.24 Python 3.12.4 64bit arm64 macOS 14.5
|
|
1091
|
+
# infer 1095.386 usec, pygeodesy 24.7.24 Python 3.12.4 64bit arm64 macOS 14.5
|
|
875
1092
|
|
|
876
1093
|
# **) MIT License
|
|
877
1094
|
#
|