Repository: fatiando/verde

Issue:
Generalize BaseGridder docstrings
**Description of the desired feature**

Class `verde.BaseGridder` is used to create a broad type of data interpolators.
When subclassing it, we just need to define only certain methods for each specific type of interpolation, while many other methods are used directly from the originals ones defined on `verde.BaseClass`. Most of them are compatible with any coordinate system (Cartesian, spherical or geodetic), except for `profile` (which involves computing distances) (see #231 for more dicussion about this). Nevertheless, the docstrings of some of these methods aren't so general, and some of them specifically asks variables defined in Cartesian coordinates. This may create inconsistent docstrings for subclasses that are intended to work with a specific coordinate system (see fatiando/harmonica#157).

A possible solution is to make the `verde.BaseGridder` docstrings more general, so the methods are not asking for a specific coordinate system. Also, we could add the `dims` argument of `grid` method as a variable class, so when we subclass it we can override it.

**Are you willing to help implement and maintain this feature?** Yes

Discussion:
santisoler: @leouieda Would you like to take a look at this? Is this what you had in mind?
santisoler: I'm quite liking to create examples that somehow test the behavior of the method/function. In fact it's a nice way to create quick tests when drafting a new function. Nevertheless, for this case I decided to remove the examples from `_get_dims` just because it's a little private method and also very easy to read.
santisoler: @leouieda feel free to merge if CIs pass.

Patch:
diff --git a/verde/base/base_classes.py b/verde/base/base_classes.py
index 503f9ab..bc581c8 100644
--- a/verde/base/base_classes.py
+++ b/verde/base/base_classes.py
@@ -83,6 +83,10 @@ class BaseGridder(BaseEstimator):
 
     """
 
+    # The default dimension names for generated outputs
+    # (pd.DataFrame, xr.Dataset, etc)
+    dims = ("northing", "easting")
+
     def predict(self, coordinates):
         """
         Predict data on the given coordinate values. NOT IMPLEMENTED.
@@ -148,6 +152,8 @@ class BaseGridder(BaseEstimator):
         coordinates : tuple of arrays
             Arrays with the coordinates of each data point. Should be in the
             following order: (easting, northing, vertical, ...).
+            For the specific definition of coordinate systems and what these
+            names mean, see the class docstring.
         data : array or tuple of arrays
             The data values of each data point. If the data has more than one
             component, *data* must be a tuple of arrays (one for each
@@ -190,6 +196,8 @@ class BaseGridder(BaseEstimator):
         coordinates : tuple of arrays
             Arrays with the coordinates of each data point. Should be in the
             following order: (easting, northing, vertical, ...).
+            For the specific definition of coordinate systems and what these
+            names mean, see the class docstring.
         data : array or tuple of arrays
             The data values of each data point. If the data has more than one
             component, *data* must be a tuple of arrays (one for each
@@ -246,8 +254,7 @@ class BaseGridder(BaseEstimator):
         Parameters
         ----------
         region : list = [W, E, S, N]
-            The boundaries of a given region in Cartesian or geographic
-            coordinates.
+            The west, east, south, and north boundaries of a given region.
         shape : tuple = (n_north, n_east) or None
             The number of points in the South-North and West-East directions,
             respectively.
@@ -256,8 +263,10 @@ class BaseGridder(BaseEstimator):
             respectively.
         dims : list or None
             The names of the northing and easting data dimensions,
-            respectively, in the output grid. Defaults to ``['northing',
-            'easting']``. **NOTE: This is an exception to the "easting" then
+            respectively, in the output grid. Default is determined from the
+            ``dims`` attribute of the class. Must be defined in the following
+            order: northing dimension, easting dimension.
+            **NOTE: This is an exception to the "easting" then
             "northing" pattern but is required for compatibility with xarray.**
         data_names : list of None
             The name(s) of the data variables in the output grid. Defaults to
@@ -285,7 +294,7 @@ class BaseGridder(BaseEstimator):
         verde.grid_coordinates : Generate the coordinate values for the grid.
 
         """
-        dims = get_dims(dims)
+        dims = self._get_dims(dims)
         region = get_instance_region(self, region)
         coordinates = grid_coordinates(region, shape=shape, spacing=spacing, **kwargs)
         if projection is None:
@@ -327,8 +336,7 @@ class BaseGridder(BaseEstimator):
         Parameters
         ----------
         region : list = [W, E, S, N]
-            The boundaries of a given region in Cartesian or geographic
-            coordinates.
+            The west, east, south, and north boundaries of a given region.
         size : int
             The number of points to generate.
         random_state : numpy.random.RandomState or an int seed
@@ -338,8 +346,10 @@ class BaseGridder(BaseEstimator):
             (resulting in different numbers with each run).
         dims : list or None
             The names of the northing and easting data dimensions,
-            respectively, in the output dataframe. Defaults to ``['northing',
-            'easting']``. **NOTE: This is an exception to the "easting" then
+            respectively, in the output dataframe. Default is determined from
+            the ``dims`` attribute of the class. Must be defined in the
+            following order: northing dimension, easting dimension.
+            **NOTE: This is an exception to the "easting" then
             "northing" pattern but is required for compatibility with xarray.**
         data_names : list of None
             The name(s) of the data variables in the output dataframe. Defaults
@@ -362,7 +372,7 @@ class BaseGridder(BaseEstimator):
             The interpolated values on a random set of points.
 
         """
-        dims = get_dims(dims)
+        dims = self._get_dims(dims)
         region = get_instance_region(self, region)
         coordinates = scatter_points(region, size, random_state=random_state, **kwargs)
         if projection is None:
@@ -411,8 +421,10 @@ class BaseGridder(BaseEstimator):
             The number of points to generate.
         dims : list or None
             The names of the northing and easting data dimensions,
-            respectively, in the output dataframe. Defaults to ``['northing',
-            'easting']``. **NOTE: This is an exception to the "easting" then
+            respectively, in the output dataframe. Default is determined from
+            the ``dims`` attribute of the class. Must be defined in the
+            following order: northing dimension, easting dimension.
+            **NOTE: This is an exception to the "easting" then
             "northing" pattern but is required for compatibility with xarray.**
         data_names : list of None
             The name(s) of the data variables in the output dataframe. Defaults
@@ -435,7 +447,7 @@ class BaseGridder(BaseEstimator):
             The interpolated values along the profile.
 
         """
-        dims = get_dims(dims)
+        dims = self._get_dims(dims)
         coordinates, distances = profile_coordinates(point1, point2, size, **kwargs)
         if projection is None:
             data = check_data(self.predict(coordinates))
@@ -450,23 +462,13 @@ class BaseGridder(BaseEstimator):
         columns.extend(zip(data_names, data))
         return pd.DataFrame(dict(columns), columns=[c[0] for c in columns])
 
-
-def get_dims(dims):
-    """
-    Get default dimension names.
-
-    Examples
-    --------
-
-    >>> get_dims(dims=None)
-    ('northing', 'easting')
-    >>> get_dims(dims=('john', 'paul'))
-    ('john', 'paul')
-
-    """
-    if dims is not None:
-        return dims
-    return ("northing", "easting")
+    def _get_dims(self, dims):
+        """
+        Get default dimension names.
+        """
+        if dims is not None:
+            return dims
+        return self.dims
 
 
 def get_data_names(data, data_names):


Test patch:
diff --git a/verde/tests/test_base.py b/verde/tests/test_base.py
index 184f655..c95eef9 100644
--- a/verde/tests/test_base.py
+++ b/verde/tests/test_base.py
@@ -1,4 +1,4 @@
-# pylint: disable=unused-argument,too-many-locals
+# pylint: disable=unused-argument,too-many-locals,protected-access
 """
 Test the base classes and their utility functions.
 """
@@ -9,7 +9,6 @@ import pytest
 from ..base.utils import check_fit_input, check_coordinates
 from ..base.base_classes import (
     BaseGridder,
-    get_dims,
     get_data_names,
     get_instance_region,
 )
@@ -32,8 +31,11 @@ def test_check_coordinates():
 
 def test_get_dims():
     "Tests that get_dims returns the expected results"
-    assert get_dims(dims=None) == ("northing", "easting")
-    assert get_dims(dims=("john", "paul")) == ("john", "paul")
+    gridder = BaseGridder()
+    assert gridder._get_dims(dims=None) == ("northing", "easting")
+    assert gridder._get_dims(dims=("john", "paul")) == ("john", "paul")
+    gridder.dims = ("latitude", "longitude")
+    assert gridder._get_dims(dims=None) == ("latitude", "longitude")
 
 
 def test_get_data_names():