12
12
import math
13
13
import re
14
14
from collections .abc import Mapping
15
+ from contextlib import suppress
15
16
from decimal import Decimal
16
17
from typing import TYPE_CHECKING , Any
17
18
18
19
import pywikibot
19
20
from pywikibot import exceptions
20
21
from pywikibot .backports import Iterator
21
22
from pywikibot .time import Timestamp
22
- from pywikibot .tools import issue_deprecation_warning , remove_last_args
23
+ from pywikibot .tools import (
24
+ deprecate_positionals ,
25
+ issue_deprecation_warning ,
26
+ remove_last_args ,
27
+ )
23
28
24
29
25
30
if TYPE_CHECKING :
@@ -95,28 +100,40 @@ class Coordinate(WbRepresentation):
95
100
96
101
_items = ('lat' , 'lon' , 'entity' )
97
102
98
- def __init__ (self , lat : float , lon : float , alt : float | None = None ,
99
- precision : float | None = None ,
100
- globe : str | None = None , typ : str = '' ,
101
- name : str = '' , dim : int | None = None ,
102
- site : DataSite | None = None ,
103
- globe_item : ItemPageStrNoneType = None ,
104
- primary : bool = False ) -> None :
103
+ @deprecate_positionals (since = '10.4.0' )
104
+ def __init__ (
105
+ self ,
106
+ lat : float ,
107
+ lon : float ,
108
+ * ,
109
+ alt : float | None = None ,
110
+ precision : float | None = None ,
111
+ globe : str | None = None ,
112
+ typ : str = '' ,
113
+ name : str = '' ,
114
+ dim : int | None = None ,
115
+ site : DataSite | None = None ,
116
+ globe_item : ItemPageStrNoneType = None ,
117
+ primary : bool = False
118
+ ) -> None :
105
119
"""Represent a geo coordinate.
106
120
107
- :param lat: Latitude
108
- :param lon: Longitude
109
- :param alt: Altitude
110
- :param precision: precision
111
- :param globe: Which globe the point is on
112
- :param typ: The type of coordinate point
113
- :param name: The name
114
- :param dim: Dimension (in meters)
115
- :param site: The Wikibase site
116
- :param globe_item: The Wikibase item for the globe, or the
117
- entity URI of this Wikibase item. Takes precedence over
118
- 'globe' if present.
119
- :param primary: True for a primary set of coordinates
121
+ .. versionchanged:: 10.4
122
+ The parameters after `lat` and `lon` are now keyword-only.
123
+
124
+ :param lat: Latitude coordinate
125
+ :param lon: Longitude coordinate
126
+ :param alt: Altitude in meters
127
+ :param precision: Precision of the coordinate
128
+ :param globe: The globe the coordinate is on (e.g. 'earth')
129
+ :param typ: Type of coordinate point
130
+ :param name: Name associated with the coordinate
131
+ :param dim: Dimension in meters used for precision calculation
132
+ :param site: The Wikibase site instance
133
+ :param globe_item: Wikibase item or entity URI for the globe;
134
+ takes precedence over *globe*
135
+ :param primary: Indicates if this is a primary coordinate set
136
+ (default: False)
120
137
"""
121
138
self .lat = lat
122
139
self .lon = lon
@@ -137,11 +154,16 @@ def __init__(self, lat: float, lon: float, alt: float | None = None,
137
154
138
155
@property
139
156
def entity (self ) -> str :
140
- """Return the entity uri of the globe."""
157
+ """Return the entity URI of the globe.
158
+
159
+ :raises CoordinateGlobeUnknownError: the globe is not supported
160
+ by Wikibase
161
+ """
141
162
if not self ._entity :
142
163
if self .globe not in self .site .globes ():
143
164
raise exceptions .CoordinateGlobeUnknownError (
144
165
f'{ self .globe } is not supported in Wikibase yet.' )
166
+
145
167
return self .site .globes ()[self .globe ]
146
168
147
169
if isinstance (self ._entity , pywikibot .ItemPage ):
@@ -152,37 +174,41 @@ def entity(self) -> str:
152
174
def toWikibase (self ) -> dict [str , Any ]:
153
175
"""Export the data to a JSON object for the Wikibase API.
154
176
155
- FIXME: Should this be in the DataSite object?
156
-
157
- :return: Wikibase JSON
177
+ :return: Wikibase JSON representation of the coordinate
158
178
"""
159
- return {'latitude' : self .lat ,
160
- 'longitude' : self .lon ,
161
- 'altitude' : self .alt ,
162
- 'globe' : self .entity ,
163
- 'precision' : self .precision ,
164
- }
179
+ return {
180
+ 'latitude' : self .lat ,
181
+ 'longitude' : self .lon ,
182
+ 'altitude' : self .alt ,
183
+ 'globe' : self .entity ,
184
+ 'precision' : self .precision ,
185
+ }
165
186
166
187
@classmethod
167
188
def fromWikibase (cls , data : dict [str , Any ],
168
189
site : DataSite | None = None ) -> Coordinate :
169
- """Constructor to create an object from Wikibase's JSON output.
190
+ """Create an object from Wikibase's JSON output.
170
191
171
- :param data: Wikibase JSON
172
- :param site: The Wikibase site
192
+ :param data: Wikibase JSON data
193
+ :param site: The Wikibase site instance
194
+ :return: Coordinate instance
173
195
"""
174
- if site is None :
175
- site = pywikibot .Site ().data_repository ()
176
-
196
+ site = site or pywikibot .Site ().data_repository ()
177
197
globe = None
178
198
179
- if data [ 'globe' ] :
199
+ if data . get ( 'globe' ) :
180
200
globes = {entity : name for name , entity in site .globes ().items ()}
181
201
globe = globes .get (data ['globe' ])
182
202
183
- return cls (data ['latitude' ], data ['longitude' ],
184
- data ['altitude' ], data ['precision' ],
185
- globe , site = site , globe_item = data ['globe' ])
203
+ return cls (
204
+ data ['latitude' ],
205
+ data ['longitude' ],
206
+ alt = data .get ('altitude' ),
207
+ precision = data .get ('precision' ),
208
+ globe = globe ,
209
+ site = site ,
210
+ globe_item = data .get ('globe' )
211
+ )
186
212
187
213
@property
188
214
def precision (self ) -> float | None :
@@ -214,17 +240,28 @@ def precision(self) -> float | None:
214
240
215
241
precision = math.degrees(
216
242
self._dim / (radius * math.cos(math.radians(self.lat))))
243
+
244
+ :return: precision in degrees or None
217
245
"""
218
- if self ._dim is None and self ._precision is None :
246
+ if self ._precision is not None :
247
+ return self ._precision
248
+
249
+ if self ._dim is None :
219
250
return None
220
- if self ._precision is None and self ._dim is not None :
221
- radius = 6378137 # TODO: Support other globes
251
+
252
+ radius = 6378137 # Earth radius in meters (TODO: support other globes)
253
+ with suppress (ZeroDivisionError ):
222
254
self ._precision = math .degrees (
223
255
self ._dim / (radius * math .cos (math .radians (self .lat ))))
256
+
224
257
return self ._precision
225
258
226
259
@precision .setter
227
260
def precision (self , value : float ) -> None :
261
+ """Set the precision value.
262
+
263
+ :param value: precision in degrees
264
+ """
228
265
self ._precision = value
229
266
230
267
def precisionToDim (self ) -> int | None :
@@ -251,38 +288,50 @@ def precisionToDim(self) -> int | None:
251
288
But this is not valid, since it returns a float value for dim which is
252
289
an integer. We must round it off to the nearest integer.
253
290
254
- Therefore::
291
+ Therefore:
292
+
293
+ .. code-block:: python
294
+
295
+ dim = int(round(math.radians(
296
+ precision)*radius*math.cos(math.radians(self.lat))))
255
297
256
- dim = int(round(math.radians(
257
- precision)*radius*math.cos(math.radians(self.lat))))
298
+ :return: dimension in meters
299
+ :raises ValueError: if neither dim nor precision is set
258
300
"""
259
- if self ._dim is None and self ._precision is None :
301
+ if self ._dim is not None :
302
+ return self ._dim
303
+
304
+ if self ._precision is None :
260
305
raise ValueError ('No values set for dim or precision' )
261
- if self ._dim is None and self ._precision is not None :
262
- radius = 6378137
263
- self ._dim = int (
264
- round (
265
- math .radians (self ._precision ) * radius * math .cos (
266
- math .radians (self .lat ))
267
- )
306
+
307
+ radius = 6378137
308
+ self ._dim = int (
309
+ round (
310
+ math .radians (self ._precision ) * radius * math .cos (
311
+ math .radians (self .lat ))
268
312
)
313
+ )
269
314
return self ._dim
270
315
271
- def get_globe_item (self , repo : DataSite | None = None ,
316
+ @deprecate_positionals (since = '10.4.0' )
317
+ def get_globe_item (self , repo : DataSite | None = None , * ,
272
318
lazy_load : bool = False ) -> pywikibot .ItemPage :
273
319
"""Return the ItemPage corresponding to the globe.
274
320
275
- Note that the globe need not be in the same data repository as
276
- the Coordinate itself.
321
+ .. note:: The globe need not be in the same data repository as
322
+ the Coordinate itself.
277
323
278
324
A successful lookup is stored as an internal value to avoid the
279
325
need for repeated lookups.
280
326
327
+ .. versionchanged:: 10.4
328
+ The *lazy_load* parameter is now keyword-only.
329
+
281
330
:param repo: the Wikibase site for the globe, if different from
282
- that provided with the Coordinate.
283
- :param lazy_load: Do not raise NoPage if ItemPage does not
284
- exist.
285
- :return: pywikibot.ItemPage
331
+ that provided with the Coordinate
332
+ :param lazy_load: Do not raise :exc:`exceptions.NoPageError` if
333
+ ItemPage does not exist
334
+ :return: pywikibot.ItemPage of the globe
286
335
"""
287
336
if isinstance (self ._entity , pywikibot .ItemPage ):
288
337
return self ._entity
0 commit comments