Repository: GenericMappingTools/pygmt

Issue:
Segmentation fault if a grid has zero increments
`grdimage` throwing a segmentation fault insted of some proper error when mistakenly swapping `xarray` y and x coordination.
 


**Full code that generated the error**

```python
X, Y = np.meshgrid(np.arange(10), np.arange(10))
Z = X ** 2 + Y ** 2
da = xr.DataArray(
    data=Z,
    dims=['y', 'x'],
    coords=dict(
        x=(['x'], Y[0, :]),
        y=(['y'], X[:, 0])
    )
)

fig = pygmt.Figure()
fig.basemap(
    frame='a',
    projection=f"X10",
    region=[0, 10, 0, 10]
)
fig.grdimage(da)
fig.show()
```

**Full error message**

```
Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

```

**System information**

Please paste the output of `python -c "import pygmt; pygmt.show_versions()"`:

```
PyGMT information:
  version: v0.4.1
System information:
  python: 3.7.10 | packaged by conda-forge | (default, Feb 19 2021, 15:59:12)  [Clang 11.0.1 ]
  executable: /Users/yohai/anaconda3/envs/faultSlip/bin/python
  machine: Darwin-18.7.0-x86_64-i386-64bit
Dependency information:
  numpy: 1.19.1
  pandas: 1.1.1
  xarray: 0.18.2
  netCDF4: 1.5.7
  packaging: 21.0
  ghostscript: 9.50
  gmt: 6.2.0
GMT library information:
  binary dir: /Users/yohai/anaconda3/envs/faultSlip/bin
  cores: 12
  grid layout: rows
  library path: /Users/yohai/anaconda3/envs/faultSlip/lib/libgmt.dylib
  padding: 2
  plugin dir: /Users/yohai/anaconda3/envs/faultSlip/lib/gmt/plugins
  share dir: /Users/yohai/anaconda3/envs/faultSlip/share/gmt
  version: 6.2.0
```


Discussion:
yohaimagen: /format
seisman: I changed the PR title to "Raise an error for zero increment grid" so that it better describes the changes in this PR. Feel free to improve the PR title yourself.
yohaimagen: I think this one is ready to be merged into main, let me know if there is anything else still to be done over here.

Patch:
diff --git a/pygmt/clib/conversion.py b/pygmt/clib/conversion.py
index 24c6c447..57b2f92c 100644
--- a/pygmt/clib/conversion.py
+++ b/pygmt/clib/conversion.py
@@ -82,7 +82,7 @@ def dataarray_to_matrix(grid):
     """
     if len(grid.dims) != 2:
         raise GMTInvalidInput(
-            "Invalid number of grid dimensions '{}'. Must be 2.".format(len(grid.dims))
+            f"Invalid number of grid dimensions '{len(grid.dims)}'. Must be 2."
         )
     # Extract region and inc from the grid
     region = []
@@ -96,9 +96,11 @@ def dataarray_to_matrix(grid):
         coord_inc = coord_incs[0]
         if not np.allclose(coord_incs, coord_inc):
             raise GMTInvalidInput(
-                "Grid appears to have irregular spacing in the '{}' dimension.".format(
-                    dim
-                )
+                f"Grid appears to have irregular spacing in the '{dim}' dimension."
+            )
+        if coord_inc == 0:
+            raise GMTInvalidInput(
+                f"Grid has a zero increment in the '{dim}' dimension."
             )
         region.extend(
             [


Test patch:
diff --git a/pygmt/tests/test_clib.py b/pygmt/tests/test_clib.py
index 4f2b8f4a..f31a28fc 100644
--- a/pygmt/tests/test_clib.py
+++ b/pygmt/tests/test_clib.py
@@ -743,6 +743,24 @@ def test_dataarray_to_matrix_inc_fails():
         dataarray_to_matrix(grid)
 
 
+def test_dataarray_to_matrix_zero_inc_fails():
+    """
+    Check that dataarray_to_matrix fails for zero increments grid.
+    """
+    data = np.ones((5, 5), dtype="float32")
+    x = np.linspace(0, 1, 5)
+    y = np.zeros_like(x)
+    grid = xr.DataArray(data, coords=[("y", y), ("x", x)])
+    with pytest.raises(GMTInvalidInput):
+        dataarray_to_matrix(grid)
+
+    y = np.linspace(0, 1, 5)
+    x = np.zeros_like(x)
+    grid = xr.DataArray(data, coords=[("y", y), ("x", x)])
+    with pytest.raises(GMTInvalidInput):
+        dataarray_to_matrix(grid)
+
+
 def test_get_default():
     """
     Make sure get_default works without crashing and gives reasonable results.