@@ -459,9 +459,14 @@ def print_statistics(self):
459
459
def _get_kriging_matrix (self , n ):
460
460
"""Assembles the kriging matrix."""
461
461
462
- xy = np .concatenate ((self .X_ADJUSTED [:, np .newaxis ],
463
- self .Y_ADJUSTED [:, np .newaxis ]), axis = 1 )
464
- d = cdist (xy , xy , 'euclidean' )
462
+ if self .coordinates_type == 'euclidean' :
463
+ xy = np .concatenate ((self .X_ADJUSTED [:, np .newaxis ],
464
+ self .Y_ADJUSTED [:, np .newaxis ]), axis = 1 )
465
+ d = cdist (xy , xy , 'euclidean' )
466
+ elif self .coordinates_type == 'geographic' :
467
+ d = core .great_circle_distance (self .X_ADJUSTED [:,np .newaxis ],
468
+ self .Y_ADJUSTED [:,np .newaxis ],
469
+ self .X_ADJUSTED , self .Y_ADJUSTED )
465
470
a = np .zeros ((n + 1 , n + 1 ))
466
471
a [:n , :n ] = - self .variogram_function (self .variogram_model_parameters ,
467
472
d )
@@ -660,6 +665,11 @@ def execute(self, style, xpoints, ypoints, mask=None, backend='vectorized',
660
665
raise ValueError ("style argument must be 'grid', "
661
666
"'points', or 'masked'" )
662
667
668
+ if n_closest_points is not None and n_closest_points <= 1 :
669
+ # If this is not checked, nondescriptive errors emerge
670
+ # later in the code.
671
+ raise ValueError ("n_closest_points has to be at least two!" )
672
+
663
673
xpts = np .atleast_1d (np .squeeze (np .array (xpoints , copy = True )))
664
674
ypts = np .atleast_1d (np .squeeze (np .array (ypoints , copy = True )))
665
675
n = self .X_ADJUSTED .shape [0 ]
@@ -699,28 +709,16 @@ def execute(self, style, xpoints, ypoints, mask=None, backend='vectorized',
699
709
[self .XCENTER , self .YCENTER ],
700
710
[self .anisotropy_scaling ],
701
711
[self .anisotropy_angle ]).T
712
+ # Prepare for cdist:
702
713
xy_data = np .concatenate ((self .X_ADJUSTED [:, np .newaxis ],
703
714
self .Y_ADJUSTED [:, np .newaxis ]), axis = 1 )
704
715
xy_points = np .concatenate ((xpts [:, np .newaxis ],
705
716
ypts [:, np .newaxis ]), axis = 1 )
706
717
elif self .coordinates_type == 'geographic' :
707
- # Quick version: Only difference between euclidean and spherical
708
- # space regarding kriging is the distance metric.
709
- # Since the relationship between three dimensional euclidean
710
- # distance and great circle distance on the sphere is monotonous,
711
- # use the existing (euclidean) infrastructure for nearest neighbour
712
- # search and distance calculation in euclidean three space and
713
- # convert distances to great circle distances afterwards.
714
- lon_d = self .X_ADJUSTED [:, np .newaxis ] * np .pi / 180.0
715
- lat_d = self .Y_ADJUSTED [:, np .newaxis ] * np .pi / 180.0
716
- xy_data = np .concatenate ((np .cos (lon_d ) * np .cos (lat_d ),
717
- np .sin (lon_d ) * np .cos (lat_d ),
718
- np .sin (lat_d )), axis = 1 )
719
- lon_p = xpts [:, np .newaxis ] * np .pi / 180.0
720
- lat_p = ypts [:, np .newaxis ] * np .pi / 180.0
721
- xy_points = np .concatenate ((np .cos (lon_p ) * np .cos (lat_p ),
722
- np .sin (lon_p ) * np .cos (lat_p ),
723
- np .sin (lat_p )), axis = 1 )
718
+ # In spherical coordinates, we do not correct for anisotropy.
719
+ # Also, we don't use scipy.spatial.cdist, so we do not have to
720
+ # format the input data accordingly.
721
+ pass
724
722
725
723
if style != 'masked' :
726
724
mask = np .zeros (npt , dtype = 'bool' )
@@ -741,13 +739,36 @@ def execute(self, style, xpoints, ypoints, mask=None, backend='vectorized',
741
739
c_pars = {key : getattr (self , key ) for key in ['Z' , 'eps' , 'variogram_model_parameters' , 'variogram_function' ]}
742
740
743
741
if n_closest_points is not None :
742
+ if self .coordinates_type == 'geographic' :
743
+ # To make use of the KDTree, we have to convert the
744
+ # spherical coordinates into three dimensional Euclidean
745
+ # coordinates, since the standard KDTree cannot handle
746
+ # the periodicity.
747
+ # Do the conversion just for the step involving the KDTree:
748
+ lon_d = self .X_ADJUSTED [:, np .newaxis ] * np .pi / 180.0
749
+ lat_d = self .Y_ADJUSTED [:, np .newaxis ] * np .pi / 180.0
750
+ xy_data = np .concatenate ((np .cos (lon_d ) * np .cos (lat_d ),
751
+ np .sin (lon_d ) * np .cos (lat_d ),
752
+ np .sin (lat_d )), axis = 1 )
753
+ lon_p = xpts [:, np .newaxis ] * np .pi / 180.0
754
+ lat_p = ypts [:, np .newaxis ] * np .pi / 180.0
755
+ xy_points = np .concatenate ((np .cos (lon_p ) * np .cos (lat_p ),
756
+ np .sin (lon_p ) * np .cos (lat_p ),
757
+ np .sin (lat_p )), axis = 1 )
758
+
744
759
from scipy .spatial import cKDTree
745
760
tree = cKDTree (xy_data )
746
761
bd , bd_idx = tree .query (xy_points , k = n_closest_points , eps = 0.0 )
762
+
747
763
if self .coordinates_type == 'geographic' :
748
- # Convert euclidean distances to great circle distances:
749
- bd = core .euclid3_to_great_circle (bd )
750
-
764
+ # Between the nearest neighbours from Euclidean search,
765
+ # calculate the great circle distance using the standard method:
766
+ x_points = np .tile (xpts [:,np .newaxis ],(1 ,n_closest_points ))
767
+ y_points = np .tile (ypts [:,np .newaxis ],(1 ,n_closest_points ))
768
+ bd = core .great_circle_distance (x_points , y_points ,
769
+ self .X_ADJUSTED [bd_idx ],
770
+ self .Y_ADJUSTED [bd_idx ])
771
+
751
772
if backend == 'loop' :
752
773
zvalues , sigmasq = \
753
774
self ._exec_loop_moving_window (a , bd , mask , bd_idx )
@@ -760,11 +781,13 @@ def execute(self, style, xpoints, ypoints, mask=None, backend='vectorized',
760
781
raise ValueError ('Specified backend {} for a moving window '
761
782
'is not supported.' .format (backend ))
762
783
else :
763
- bd = cdist (xy_points , xy_data , 'euclidean' )
764
- if self .coordinates_type == 'geographic' :
765
- # Convert euclidean distances to great circle distances:
766
- bd = core .euclid3_to_great_circle (bd )
767
-
784
+ if self .coordinates_type == 'euclidean' :
785
+ bd = cdist (xy_points , xy_data , 'euclidean' )
786
+ elif self .coordinates_type == 'geographic' :
787
+ bd = core .great_circle_distance (xpts [:,np .newaxis ],
788
+ ypts [:,np .newaxis ],
789
+ self .X_ADJUSTED , self .Y_ADJUSTED )
790
+
768
791
if backend == 'vectorized' :
769
792
zvalues , sigmasq = self ._exec_vector (a , bd , mask )
770
793
elif backend == 'loop' :
0 commit comments