Repository: geopandas/geopandas

Issue:
ENH: Check CRS mismatch for binary predicates and operations
Check if the CRS of both GeometryArrays is the same when doing binary predicates (`within`, 'intersects`) and binary operations (`intersection`...) between two arrays.

Requires #1339 to be merged to have CRS on array level.


Discussion:

Resolving patch:
diff --git a/geopandas/array.py b/geopandas/array.py
--- a/geopandas/array.py
+++ b/geopandas/array.py
@@ -1,6 +1,7 @@
 from collections.abc import Iterable
 import numbers
 import operator
+import warnings
 
 import numpy as np
 import pandas as pd
@@ -67,6 +68,20 @@ def _isna(value):
         return False
 
 
+def _check_crs(left, right, allow_none=False):
+    """
+    Check if the projection of both arrays is the same.
+
+    If allow_none is True, empty CRS is treated as the same.
+    """
+    if allow_none:
+        if not left.crs or not right.crs:
+            return True
+    if not left.crs == right.crs:
+        return False
+    return True
+
+
 # -----------------------------------------------------------------------------
 # Constructors / converters to other formats
 # -----------------------------------------------------------------------------
@@ -215,6 +230,8 @@ class GeometryArray(ExtensionArray):
 
     def __init__(self, data, crs=None):
         if isinstance(data, self.__class__):
+            if not crs:
+                crs = data.crs
             data = data.data
         elif not isinstance(data, np.ndarray):
             raise TypeError(
@@ -405,6 +422,19 @@ def _binary_method(op, left, right, **kwargs):
                     len(left), len(right)
                 )
                 raise ValueError(msg)
+            if not _check_crs(left, right):
+                warnings.warn(
+                    "CRS mismatch between the CRS of left geometries "
+                    "and the CRS of right geoemtries. "
+                    "Use `GeoSeries.to_crs()` to reproject one of "
+                    "the passed geometry arrays.\n"
+                    "Left CRS:\n {0}\n"
+                    "Right CRS:\n {1}\n".format(
+                        left.crs.__repr__(), right.crs.__repr__()
+                    ),
+                    UserWarning,
+                    stacklevel=6,
+                )
             right = right.data
 
         return getattr(vectorized, op)(left.data, right, **kwargs)
diff --git a/geopandas/base.py b/geopandas/base.py
--- a/geopandas/base.py
+++ b/geopandas/base.py
@@ -43,8 +43,6 @@ def _delegate_binary_method(op, this, other, *args, **kwargs):
     if isinstance(other, GeoPandasBase):
         this, other = this.align(other.geometry)
 
-        if this.crs != other.crs:
-            warn("GeoSeries crs mismatch: {0} and {1}".format(this.crs, other.crs))
         a_this = GeometryArray(this.values)
         other = GeometryArray(other.values)
     elif isinstance(other, BaseGeometry):


Test patch:
diff --git a/geopandas/testing.py b/geopandas/testing.py
--- a/geopandas/testing.py
+++ b/geopandas/testing.py
@@ -122,7 +122,7 @@ def assert_geoseries_equal(
 
     if not check_crs:
         with warnings.catch_warnings():
-            warnings.filterwarnings("ignore", "GeoSeries crs mismatch", UserWarning)
+            warnings.filterwarnings("ignore", "CRS mismatch", UserWarning)
             if check_less_precise:
                 assert geom_almost_equals(left, right)
             else:
diff --git a/geopandas/tests/test_array.py b/geopandas/tests/test_array.py
--- a/geopandas/tests/test_array.py
+++ b/geopandas/tests/test_array.py
@@ -20,6 +20,7 @@
     points_from_xy,
     to_wkb,
     to_wkt,
+    _check_crs,
 )
 import geopandas._compat as compat
 
@@ -791,3 +792,11 @@ def test_astype_multipolygon():
     result = arr.astype(np.dtype("U10"))
     assert result.dtype == np.dtype("U10")
     assert result[0] == multi_poly.wkt[:10]
+
+
+def test_check_crs():
+    t1 = T.copy()
+    t1.crs = 4326
+    assert _check_crs(t1, T) is False
+    assert _check_crs(t1, t1) is True
+    assert _check_crs(t1, T, allow_none=True) is True
diff --git a/geopandas/tests/test_crs.py b/geopandas/tests/test_crs.py
--- a/geopandas/tests/test_crs.py
+++ b/geopandas/tests/test_crs.py
@@ -10,7 +10,7 @@
 import pyproj
 
 from geopandas import GeoSeries, GeoDataFrame, points_from_xy, datasets, read_file
-from geopandas.array import from_shapely, from_wkb, from_wkt
+from geopandas.array import from_shapely, from_wkb, from_wkt, GeometryArray
 
 from geopandas.testing import assert_geodataframe_equal
 import pytest
@@ -160,6 +160,12 @@ def test_array(self):
         arr = from_shapely(self.geoms, crs=27700)
         assert arr.crs == self.osgb
 
+        arr = GeometryArray(arr)
+        assert arr.crs == self.osgb
+
+        arr = GeometryArray(arr, crs=4326)
+        assert arr.crs == self.wgs
+
     def test_series(self):
         s = GeoSeries(crs=27700)
         assert s.crs == self.osgb
diff --git a/geopandas/tests/test_testing.py b/geopandas/tests/test_testing.py
--- a/geopandas/tests/test_testing.py
+++ b/geopandas/tests/test_testing.py
@@ -56,6 +56,7 @@
 )
 
 
+@pytest.mark.filterwarnings("ignore::UserWarning")
 def test_geoseries():
     assert_geoseries_equal(s1, s2)
     assert_geoseries_equal(s1, s3, check_series_type=False, check_dtype=False)