1
+ import math
1
2
import numpy as np
2
3
3
4
from matplotlib import _api , rcParams
@@ -52,8 +53,8 @@ def cla(self):
52
53
53
54
self .grid (rcParams ['axes.grid' ])
54
55
55
- Axes .set_xlim (self , - np .pi , np .pi )
56
- Axes .set_ylim (self , - np .pi / 2.0 , np .pi / 2.0 )
56
+ Axes .set_xlim (self , - math .pi , math .pi )
57
+ Axes .set_ylim (self , - math .pi / 2.0 , math .pi / 2.0 )
57
58
58
59
def _set_lim_and_transforms (self ):
59
60
# A (possibly non-linear) projection on the (already scaled) data
@@ -88,7 +89,7 @@ def _set_lim_and_transforms(self):
88
89
Affine2D ().translate (0 , - 4 )
89
90
90
91
# This is the transform for latitude ticks.
91
- yaxis_stretch = Affine2D ().scale (np .pi * 2 , 1 ).translate (- np .pi , 0 )
92
+ yaxis_stretch = Affine2D ().scale (math .pi * 2 , 1 ).translate (- math .pi , 0 )
92
93
yaxis_space = Affine2D ().scale (1 , 1.1 )
93
94
self ._yaxis_transform = \
94
95
yaxis_stretch + \
@@ -108,8 +109,8 @@ def _set_lim_and_transforms(self):
108
109
109
110
def _get_affine_transform (self ):
110
111
transform = self ._get_core_transform (1 )
111
- xscale , _ = transform .transform ((np .pi , 0 ))
112
- _ , yscale = transform .transform ((0 , np .pi / 2 ))
112
+ xscale , _ = transform .transform ((math .pi , 0 ))
113
+ _ , yscale = transform .transform ((0 , math .pi / 2 ))
113
114
return Affine2D () \
114
115
.scale (0.5 / xscale , 0.5 / yscale ) \
115
116
.translate (0.5 , 0.5 )
@@ -261,14 +262,17 @@ def transform_non_affine(self, ll):
261
262
longitude , latitude = ll .T
262
263
263
264
# Pre-compute some values
264
- half_long = longitude / 2.0
265
- cos_latitude = np .cos (latitude )
265
+ half_long = longitude / 2
266
+ cos_half = np .cos (half_long )
267
+ sin_half = np .cos (half_long )
268
+ cos_latitude = cos_half * cos_half - sin_half * sin_half
266
269
267
- alpha = np .arccos (cos_latitude * np .cos (half_long ))
268
- sinc_alpha = np .sinc (alpha / np .pi ) # np.sinc is sin(pi*x)/(pi*x).
270
+ alpha = np .arccos (cos_latitude * cos_half )
271
+ # np.sinc is sin(pi*x)/(pi*x).
272
+ sinc_alpha = np .sinc (alpha / math .pi )
269
273
270
- x = (cos_latitude * np . sin ( half_long ) ) / sinc_alpha
271
- y = np . sin (latitude ) / sinc_alpha
274
+ x = (cos_latitude * sin_half ) / sinc_alpha
275
+ y = 2.0 * sin_half * cos_half / sinc_alpha # sin(lat ) / sinc_alpha
272
276
return np .column_stack ([x , y ])
273
277
274
278
def inverted (self ):
@@ -287,7 +291,7 @@ def inverted(self):
287
291
return AitoffAxes .AitoffTransform (self ._resolution )
288
292
289
293
def __init__ (self , * args , ** kwargs ):
290
- self ._longitude_cap = np .pi / 2.0
294
+ self ._longitude_cap = math .pi / 2
291
295
super ().__init__ (* args , ** kwargs )
292
296
self .set_aspect (0.5 , adjustable = 'box' , anchor = 'C' )
293
297
self .cla ()
@@ -305,12 +309,14 @@ class HammerTransform(_GeoTransform):
305
309
def transform_non_affine (self , ll ):
306
310
# docstring inherited
307
311
longitude , latitude = ll .T
308
- half_long = longitude / 2.0
309
- cos_latitude = np .cos (latitude )
310
- sqrt2 = np .sqrt (2.0 )
311
- alpha = np .sqrt (1.0 + cos_latitude * np .cos (half_long ))
312
- x = (2.0 * sqrt2 ) * (cos_latitude * np .sin (half_long )) / alpha
313
- y = (sqrt2 * np .sin (latitude )) / alpha
312
+ half_long = longitude / 2
313
+ cos_half = np .cos (half_long )
314
+ sin_half = np .cos (half_long )
315
+ cos_latitude = cos_half * cos_half - sin_half * sin_half
316
+ sqrt8 = 8 ** (1 / 2 )
317
+ alpha = np .sqrt (1.0 + cos_latitude * cos_half )
318
+ x = (sqrt8 * cos_latitude * sin_half ) / alpha
319
+ y = (sqrt8 * cos_half * sin_half ) / alpha # sin(lat)
314
320
return np .column_stack ([x , y ])
315
321
316
322
def inverted (self ):
@@ -324,15 +330,15 @@ def transform_non_affine(self, xy):
324
330
x , y = xy .T
325
331
z = np .sqrt (1 - (x / 4 ) ** 2 - (y / 2 ) ** 2 )
326
332
longitude = 2 * np .arctan ((z * x ) / (2 * (2 * z ** 2 - 1 )))
327
- latitude = np .arcsin (y * z )
333
+ latitude = np .arcsin (y * z )
328
334
return np .column_stack ([longitude , latitude ])
329
335
330
336
def inverted (self ):
331
337
# docstring inherited
332
338
return HammerAxes .HammerTransform (self ._resolution )
333
339
334
340
def __init__ (self , * args , ** kwargs ):
335
- self ._longitude_cap = np .pi / 2.0
341
+ self ._longitude_cap = math .pi / 2
336
342
super ().__init__ (* args , ** kwargs )
337
343
self .set_aspect (0.5 , adjustable = 'box' , anchor = 'C' )
338
344
self .cla ()
@@ -351,18 +357,18 @@ def transform_non_affine(self, ll):
351
357
# docstring inherited
352
358
def d (theta ):
353
359
delta = (- (theta + np .sin (theta ) - pi_sin_l )
354
- / (1 + np .cos (theta )))
360
+ / (1.0 + np .cos (theta )))
355
361
return delta , np .abs (delta ) > 0.001
356
362
357
363
longitude , latitude = ll .T
358
-
359
- clat = np . pi / 2 - np .abs (latitude )
364
+ pi_half = math . pi / 2
365
+ clat = pi_half - np .abs (latitude )
360
366
ihigh = clat < 0.087 # within 5 degrees of the poles
361
367
ilow = ~ ihigh
362
368
aux = np .empty (latitude .shape , dtype = float )
363
369
364
370
if ilow .any (): # Newton-Raphson iteration
365
- pi_sin_l = np .pi * np .sin (latitude [ilow ])
371
+ pi_sin_l = math .pi * np .sin (latitude [ilow ])
366
372
theta = 2.0 * latitude [ilow ]
367
373
delta , large_delta = d (theta )
368
374
while np .any (large_delta ):
@@ -372,12 +378,13 @@ def d(theta):
372
378
373
379
if ihigh .any (): # Taylor series-based approx. solution
374
380
e = clat [ihigh ]
375
- d = 0.5 * ( 3 * np .pi * e ** 2 ) ** ( 1.0 / 3 )
376
- aux [ihigh ] = (np . pi / 2 - d ) * np .sign (latitude [ihigh ])
381
+ d = np . cbrt ( 3.0 * math .pi * e ** 2 ) / 2
382
+ aux [ihigh ] = (pi_half - d ) * np .sign (latitude [ihigh ])
377
383
378
384
xy = np .empty (ll .shape , dtype = float )
379
- xy [:, 0 ] = (2.0 * np .sqrt (2.0 ) / np .pi ) * longitude * np .cos (aux )
380
- xy [:, 1 ] = np .sqrt (2.0 ) * np .sin (aux )
385
+ sqrt2 = 2 ** (1 / 2 )
386
+ xy [:, 0 ] = (sqrt2 / pi_half ) * longitude * np .cos (aux )
387
+ xy [:, 1 ] = sqrt2 * np .sin (aux )
381
388
382
389
return xy
383
390
@@ -392,17 +399,18 @@ def transform_non_affine(self, xy):
392
399
x , y = xy .T
393
400
# from Equations (7, 8) of
394
401
# https://mathworld.wolfram.com/MollweideProjection.html
395
- theta = np .arcsin (y / np .sqrt (2 ))
396
- longitude = (np .pi / (2 * np .sqrt (2 ))) * x / np .cos (theta )
397
- latitude = np .arcsin ((2 * theta + np .sin (2 * theta )) / np .pi )
402
+ sqrt2 = 2 ** (1 / 2 )
403
+ theta = np .arcsin (y / sqrt2 )
404
+ longitude = (math .pi / (2.0 * sqrt2 )) * x / np .cos (theta )
405
+ latitude = np .arcsin ((2.0 * theta + np .sin (2.0 * theta )) / math .pi )
398
406
return np .column_stack ([longitude , latitude ])
399
407
400
408
def inverted (self ):
401
409
# docstring inherited
402
410
return MollweideAxes .MollweideTransform (self ._resolution )
403
411
404
412
def __init__ (self , * args , ** kwargs ):
405
- self ._longitude_cap = np .pi / 2.0
413
+ self ._longitude_cap = math .pi / 2.0
406
414
super ().__init__ (* args , ** kwargs )
407
415
self .set_aspect (0.5 , adjustable = 'box' , anchor = 'C' )
408
416
self .cla ()
@@ -434,15 +442,17 @@ def transform_non_affine(self, ll):
434
442
clat = self ._center_latitude
435
443
cos_lat = np .cos (latitude )
436
444
sin_lat = np .sin (latitude )
445
+ cos_clat = np .cos (clat )
446
+ sin_clat = np .sin (clat )
437
447
diff_long = longitude - clong
438
448
cos_diff_long = np .cos (diff_long )
439
449
440
450
inner_k = np .maximum ( # Prevent divide-by-zero problems
441
- 1 + np . sin ( clat ) * sin_lat + np . cos ( clat ) * cos_lat * cos_diff_long ,
451
+ 1.0 + sin_clat * sin_lat + cos_clat * cos_lat * cos_diff_long ,
442
452
1e-15 )
443
- k = np .sqrt (2 / inner_k )
453
+ k = np .sqrt (2.0 / inner_k )
444
454
x = k * cos_lat * np .sin (diff_long )
445
- y = k * (np . cos ( clat ) * sin_lat - np . sin ( clat ) * cos_lat * cos_diff_long )
455
+ y = k * (cos_clat * sin_lat - sin_clat * cos_lat * cos_diff_long )
446
456
447
457
return np .column_stack ([x , y ])
448
458
@@ -466,7 +476,7 @@ def transform_non_affine(self, xy):
466
476
clong = self ._center_longitude
467
477
clat = self ._center_latitude
468
478
p = np .maximum (np .hypot (x , y ), 1e-9 )
469
- c = 2 * np .arcsin (0.5 * p )
479
+ c = 2.0 * np .arcsin (0.5 * p )
470
480
sin_c = np .sin (c )
471
481
cos_c = np .cos (c )
472
482
@@ -485,7 +495,7 @@ def inverted(self):
485
495
self ._resolution )
486
496
487
497
def __init__ (self , * args , center_longitude = 0 , center_latitude = 0 , ** kwargs ):
488
- self ._longitude_cap = np .pi / 2
498
+ self ._longitude_cap = math .pi / 2
489
499
self ._center_longitude = center_longitude
490
500
self ._center_latitude = center_latitude
491
501
super ().__init__ (* args , ** kwargs )
0 commit comments