jorisvandenbossche: Thanks for the review!

I still need to further update the tests (and properly skip for older pyarrow versions).
jorisvandenbossche: @brendan-ward this is ready for another look.

I added benchmarks for Feather, but also refactored the IO benchmarks a bit to combine those with Parquet (the setup was basically the same).

Running them now gives:

```
[ 60.00%] ··· io.BenchFrame.time_read                                                                                                                                                                            ok
[ 60.00%] ··· ================ ===========
                file_format               
              ---------------- -----------
               ESRI Shapefile   367±300ms 
                  GeoJSON       619±100ms 
                    GPKG        615±100ms 
                  Parquet        20.8±3ms 
                  Feather        19.5±5ms 
              ================ ===========

[ 70.00%] ··· io.BenchFrame.time_write                                                                                                                                                                           ok
[ 70.00%] ··· ================ ============
                file_format                
              ---------------- ------------
               ESRI Shapefile   1.35±0.08s 
                  GeoJSON       1.33±0.05s 
                    GPKG        1.13±0.2s  
                  Parquet       33.0±10ms  
                  Feather       28.8±20ms  
              ================ ============

[ 80.00%] ··· io.BenchSeries.time_read_series                                                                                                                                                                    ok
[ 80.00%] ··· ================ ==========
                file_format              
              ---------------- ----------
               ESRI Shapefile   463±60ms 
                  GeoJSON       519±40ms 
                    GPKG        512±40ms 
              ================ ==========

[ 90.00%] ··· io.BenchSeries.time_read_series_from_frame                                                                                                                                                         ok
[ 90.00%] ··· ================ ==========
                file_format              
              ---------------- ----------
               ESRI Shapefile   597±80ms 
                  GeoJSON       672±20ms 
                    GPKG        526±30ms 
              ================ ==========

[100.00%] ··· io.BenchSeries.time_write_series                                                                                                                                                                   ok
[100.00%] ··· ================ ==========
                file_format              
              ---------------- ----------
               ESRI Shapefile   837±70ms 
                  GeoJSON       706±60ms 
                    GPKG        754±40ms 
              ================ ==========
```
diff --git a/benchmarks/io.py b/benchmarks/io.py
index f169512a..70cf845c 100644
--- a/benchmarks/io.py
+++ b/benchmarks/io.py
@@ -1,59 +1,109 @@
 import os
 import shutil
 import tempfile
+import warnings
 
-from geopandas import GeoDataFrame, GeoSeries
 import numpy as np
+
 from shapely.geometry import Point
 
+from geopandas import GeoDataFrame, GeoSeries, read_file, read_parquet, read_feather
+
+
+# TEMP: hide warning from to_parquet
+warnings.filterwarnings("ignore", message=".*initial implementation of Parquet.*")
 
-class Bench:
 
-    # extensions for different file types to test
-    params = [".shp", ".json", ".gpkg"]
-    param_names = ["ext"]
+format_dict = {
+    "ESRI Shapefile": (
+        ".shp",
+        lambda gdf, filename: gdf.to_file(filename, driver="ESRI Shapefile"),
+        lambda filename: read_file(filename, driver="ESRI Shapefile"),
+    ),
+    "GeoJSON": (
+        ".json",
+        lambda gdf, filename: gdf.to_file(filename, driver="GeoJSON"),
+        lambda filename: read_file(filename, driver="GeoJSON"),
+    ),
+    "GPKG": (
+        ".gpkg",
+        lambda gdf, filename: gdf.to_file(filename, driver="GeoJSON"),
+        lambda filename: read_file(filename, driver="GeoJSON"),
+    ),
+    "Parquet": (
+        ".parquet",
+        lambda gdf, filename: gdf.to_parquet(filename),
+        lambda filename: read_parquet(filename),
+    ),
+    "Feather": (
+        ".feather",
+        lambda gdf, filename: gdf.to_feather(filename),
+        lambda filename: read_feather(filename),
+    ),
+}
 
-    def setup(self, ext):
 
-        self.driver_dict = {".shp": "ESRI Shapefile",
-                            ".json": "GeoJSON",
-                            ".gpkg": "GPKG"}
-        driver = self.driver_dict[ext]
+class Bench:
+
+    params = ["ESRI Shapefile", "GeoJSON", "GPKG", "Parquet", "Feather"]
+    param_names = ["file_format"]
+
+    def setup(self, file_format):
+        self.ext, self.writer, self.reader = format_dict[file_format]
 
         num_points = 20000
         xs = np.random.rand(num_points)
         ys = np.random.rand(num_points)
 
         self.points = GeoSeries([Point(x, y) for (x, y) in zip(xs, ys)])
-        self.df = GeoDataFrame({"geometry": self.points, "x": xs, "y": ys,
-                                "s": np.zeros(num_points, dtype="object")})
+        self.df = GeoDataFrame(
+            {
+                "geometry": self.points,
+                "x": xs,
+                "y": ys,
+                "s": np.zeros(num_points, dtype="object"),
+            }
+        )
 
         self.tmpdir = tempfile.mkdtemp()
-        self.series_filename = os.path.join(self.tmpdir, "series" + ext)
-        self.frame_filename = os.path.join(self.tmpdir, "frame" + ext)
-        self.points.to_file(self.series_filename, driver=driver)
-        self.df.to_file(self.frame_filename, driver=driver)
+        self.filename = os.path.join(self.tmpdir, "frame" + self.ext)
+        self.writer(self.df, self.filename)
 
-    def teardown(self, ext):
+    def teardown(self, file_format):
         shutil.rmtree(self.tmpdir)
 
-    def time_write_frame(self, ext):
-        driver = self.driver_dict[ext]
-        with tempfile.TemporaryDirectory() as tmpdir:
-            out_filename = os.path.join(tmpdir, "frame" + ext)
-            self.df.to_file(out_filename, driver=driver)
 
-    def time_write_series(self, ext):
-        driver = self.driver_dict[ext]
+class BenchFrame(Bench):
+
+    params = ["ESRI Shapefile", "GeoJSON", "GPKG", "Parquet", "Feather"]
+    param_names = ["file_format"]
+
+    def time_write(self, file_format):
         with tempfile.TemporaryDirectory() as tmpdir:
-            out_filename = os.path.join(tmpdir, "series" + ext)
-            self.points.to_file(out_filename, driver=driver)
+            out_filename = os.path.join(tmpdir, "frame" + self.ext)
+            self.writer(self.df, out_filename)
+
+    def time_read(self, file_format):
+        self.reader(self.filename)
+
 
-    def time_read_frame(self, ext):
-        frame = GeoDataFrame.from_file(self.frame_filename)
+class BenchSeries(Bench):
+
+    params = ["ESRI Shapefile", "GeoJSON", "GPKG"]
+    param_names = ["file_format"]
+
+    def setup(self, file_format):
+        super().setup(file_format)
+        self.filename_series = os.path.join(self.tmpdir, "series" + self.ext)
+        self.writer(self.points, self.filename_series)
+
+    def time_write_series(self, file_format):
+        with tempfile.TemporaryDirectory() as tmpdir:
+            out_filename = os.path.join(tmpdir, "series" + self.ext)
+            self.writer(self.points, out_filename)
 
-    def time_read_series(self, ext):
-        points = GeoSeries.from_file(self.series_filename)
+    def time_read_series(self, file_format):
+        GeoSeries.from_file(self.filename_series)
 
-    def time_read_series_from_frame(self, ext):
-        points = GeoSeries.from_file(self.frame_filename)
+    def time_read_series_from_frame(self, file_format):
+        GeoSeries.from_file(self.filename)
diff --git a/benchmarks/parquet.py b/benchmarks/parquet.py
deleted file mode 100644
index f2a707bc..00000000
--- a/benchmarks/parquet.py
+++ /dev/null
@@ -1,44 +0,0 @@
-import os
-import shutil
-import tempfile
-import warnings
-
-from geopandas import GeoDataFrame, GeoSeries, read_parquet
-import numpy as np
-from shapely.geometry import Point
-
-
-# TEMP: hide warning from to_parquet
-warnings.filterwarnings("ignore", message=".*initial implementation of Parquet.*")
-
-
-class Bench:
-    def setup(self):
-        num_points = 20000
-        xs = np.random.rand(num_points)
-        ys = np.random.rand(num_points)
-
-        self.points = GeoSeries([Point(x, y) for (x, y) in zip(xs, ys)])
-        self.df = GeoDataFrame(
-            {
-                "geometry": self.points,
-                "x": xs,
-                "y": ys,
-                "s": np.zeros(num_points, dtype="object"),
-            }
-        )
-
-        self.tmpdir = tempfile.mkdtemp()
-        self.filename = os.path.join(self.tmpdir, "frame.pq")
-
-        self.df.to_parquet(self.filename)
-
-    def teardown(self):
-        shutil.rmtree(self.tmpdir)
-
-    def time_read_parquet(self):
-        read_parquet(self.filename)
-
-    def time_write_parquet(self):
-        with tempfile.TemporaryDirectory() as tmpdir:
-            self.df.to_parquet(os.path.join(tmpdir, "frame.pq"))
diff --git a/doc/source/reference.rst b/doc/source/reference.rst
index 50753ead..820d9ad0 100644
--- a/doc/source/reference.rst
+++ b/doc/source/reference.rst
@@ -157,6 +157,8 @@ Currently, the following methods/attributes are implemented for a ``GeoDataFrame
 
 .. automethod:: geopandas.GeoDataFrame.to_parquet
 
+.. automethod:: geopandas.GeoDataFrame.to_feather
+
 .. automethod:: geopandas.GeoDataFrame.to_postgis
 
 .. automethod:: geopandas.GeoDataFrame.plot
@@ -202,6 +204,7 @@ Top-level Functions
   GeoSeries
   read_file
   read_parquet
+  read_feather
   read_postgis
   sjoin
   overlay
diff --git a/geopandas/__init__.py b/geopandas/__init__.py
index a0fed937..79bffdd7 100644
--- a/geopandas/__init__.py
+++ b/geopandas/__init__.py
@@ -5,7 +5,8 @@ from geopandas.geodataframe import GeoDataFrame  # noqa
 from geopandas.array import points_from_xy  # noqa
 
 from geopandas.io.file import _read_file as read_file  # noqa
-from geopandas.io.parquet import _read_parquet as read_parquet  # noqa
+from geopandas.io.arrow import _read_parquet as read_parquet  # noqa
+from geopandas.io.arrow import _read_feather as read_feather  # noqa
 from geopandas.io.sql import _read_postgis as read_postgis  # noqa
 from geopandas.tools import sjoin  # noqa
 from geopandas.tools import overlay  # noqa
diff --git a/geopandas/array.py b/geopandas/array.py
index 1e481388..d886a1f9 100644
--- a/geopandas/array.py
+++ b/geopandas/array.py
@@ -55,7 +55,7 @@ if compat.PANDAS_GE_024:
 
 def _isna(value):
     """
-    Check if scalar value is NA-like (None or np.nan).
+    Check if scalar value is NA-like (None, np.nan or pd.NA).
 
     Custom version that only works for scalars (returning True or False),
     as `pd.isna` also works for array-like input returning a boolean array.
@@ -64,6 +64,8 @@ def _isna(value):
         return True
     elif isinstance(value, float) and np.isnan(value):
         return True
+    elif compat.PANDAS_GE_10 and value is pd.NA:
+        return True
     else:
         return False
 
@@ -82,6 +84,36 @@ def _check_crs(left, right, allow_none=False):
     return True
 
 
+def _crs_mismatch_warn(left, right, stacklevel=3):
+    """
+    Raise a CRS mismatch warning with the information on the assigned CRS.
+    """
+    if left.crs:
+        left_srs = left.crs.to_string()
+        left_srs = left_srs if len(left_srs) <= 50 else " ".join([left_srs[:50], "..."])
+    else:
+        left_srs = None
+
+    if right.crs:
+        right_srs = right.crs.to_string()
+        right_srs = (
+            right_srs if len(right_srs) <= 50 else " ".join([right_srs[:50], "..."])
+        )
+    else:
+        right_srs = None
+
+    warnings.warn(
+        "CRS mismatch between the CRS of left geometries "
+        "and the CRS of right geometries.\n"
+        "Use `to_crs()` to reproject one of "
+        "the input geometries to match the CRS of the other.\n\n"
+        "Left CRS: {0}\n"
+        "Right CRS: {1}\n".format(left_srs, right_srs),
+        UserWarning,
+        stacklevel=stacklevel,
+    )
+
+
 # -----------------------------------------------------------------------------
 # Constructors / converters to other formats
 # -----------------------------------------------------------------------------
@@ -423,18 +455,7 @@ class GeometryArray(ExtensionArray):
                 )
                 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,
-                )
+                _crs_mismatch_warn(left, right, stacklevel=7)
             right = right.data
 
         return getattr(vectorized, op)(left.data, right, **kwargs)
diff --git a/geopandas/base.py b/geopandas/base.py
index 3e971b17..ad31a6df 100644
--- a/geopandas/base.py
+++ b/geopandas/base.py
@@ -748,8 +748,8 @@ class GeoPandasBase(object):
         original index and a zero-based integer index that counts the
         number of single geometries within a multi-part geometry.
 
-        Example
-        -------
+        Examples
+        --------
         >>> gdf  # gdf is GeoSeries of MultiPoints
         0         MULTIPOINT (0 0, 1 1)
         1    MULTIPOINT (2 2, 3 3, 4 4)
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
index 238c41df..67e042d1 100644
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -579,7 +579,7 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
 
         return geo
 
-    def to_parquet(self, filename, compression="snappy", index=None, **kwargs):
+    def to_parquet(self, path, index=None, compression="snappy", **kwargs):
         """Write a GeoDataFrame to the Parquet format.
 
         Any geometry columns present are serialized to WKB format in the file.
@@ -600,21 +600,59 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
         Parameters
         ----------
         path : str, path object
+        index : bool, default None
+            If ``True``, always include the dataframe's index(es) as columns
+            in the file output.
+            If ``False``, the index(es) will not be written to the file.
+            If ``None``, the index(ex) will be included as columns in the file
+            output except `RangeIndex` which is stored as metadata only.
         compression : {'snappy', 'gzip', 'brotli', None}, default 'snappy'
             Name of the compression to use. Use ``None`` for no compression.
+        kwargs
+            Additional keyword arguments passed to to pyarrow.parquet.write_table().
+        """
+
+        from geopandas.io.arrow import _to_parquet
+
+        _to_parquet(self, path, compression=compression, index=index, **kwargs)
+
+    def to_feather(self, path, index=None, compression=None, **kwargs):
+        """Write a GeoDataFrame to the Feather format.
+
+        Any geometry columns present are serialized to WKB format in the file.
+
+        Requires 'pyarrow' >= 0.17.
+
+        WARNING: this is an initial implementation of Feather file support and
+        associated metadata.  This is tracking version 0.1.0 of the metadata
+        specification at:
+        https://github.com/geopandas/geo-arrow-spec
+
+        This metadata specification does not yet make stability promises.  As such,
+        we do not yet recommend using this in a production setting unless you are
+        able to rewrite your Feather files.
+
+        .. versionadded:: 0.8
+
+        Parameters
+        ----------
+        path : str, path object
         index : bool, default None
             If ``True``, always include the dataframe's index(es) as columns
             in the file output.
             If ``False``, the index(es) will not be written to the file.
             If ``None``, the index(ex) will be included as columns in the file
             output except `RangeIndex` which is stored as metadata only.
+        compression : {'zstd', 'lz4', 'uncompressed'}, optional
+            Name of the compression to use. Use ``"uncompressed"`` for no
+            compression. By default uses LZ4 if available, otherwise uncompressed.
         kwargs
-            Additional keyword arguments passed to to pyarrow.parquet.write_table().
+            Additional keyword arguments passed to to pyarrow.feather.write_feather().
         """
 
-        from geopandas.io.parquet import _to_parquet
+        from geopandas.io.arrow import _to_feather
 
-        _to_parquet(self, filename, compression=compression, index=index, **kwargs)
+        _to_feather(self, path, index=index, compression=compression, **kwargs)
 
     def to_file(
         self, filename, driver="ESRI Shapefile", schema=None, index=None, **kwargs
@@ -877,6 +915,9 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
         """
         df_copy = self.copy()
 
+        if "level_1" in df_copy.columns:  # GH1393
+            df_copy = df_copy.rename(columns={"level_1": "__level_1"})
+
         exploded_geom = df_copy.geometry.explode().reset_index(level=-1)
         exploded_index = exploded_geom.columns[0]
 
@@ -887,6 +928,10 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
         # exploded GeoSeries index.
         df.set_index(exploded_index, append=True, inplace=True)
         df.index.names = list(self.index.names) + [None]
+
+        if "__level_1" in df.columns:
+            df = df.rename(columns={"__level_1": "level_1"})
+
         geo_df = df.set_geometry(self._geometry_column_name)
         return geo_df
 
diff --git a/geopandas/io/parquet.py b/geopandas/io/arrow.py
similarity index 64%
rename from geopandas/io/parquet.py
rename to geopandas/io/arrow.py
index 36b56c02..acd574f6 100644
--- a/geopandas/io/parquet.py
+++ b/geopandas/io/arrow.py
@@ -1,3 +1,4 @@
+from distutils.version import LooseVersion
 import json
 import warnings
 
@@ -128,11 +129,11 @@ def _validate_dataframe(df):
     """
 
     if not isinstance(df, DataFrame):
-        raise ValueError("to_parquet only supports IO with DataFrames")
+        raise ValueError("Writing to Parquet/Feather only supports IO with DataFrames")
 
     # must have value column names (strings only)
     if df.columns.inferred_type not in {"string", "unicode", "empty"}:
-        raise ValueError("parquet must have string column names")
+        raise ValueError("Writing to Parquet/Feather requires string column names")
 
     # index level names must be strings
     valid_names = all(
@@ -154,13 +155,13 @@ def _validate_metadata(metadata):
     """
 
     if not metadata:
-        raise ValueError("Missing or malformed geo metadata in Parquet file")
+        raise ValueError("Missing or malformed geo metadata in Parquet/Feather file")
 
     required_keys = ("primary_column", "columns")
     for key in required_keys:
         if metadata.get(key, None) is None:
             raise ValueError(
-                "'geo' metadata in Parquet file is missing required key: "
+                "'geo' metadata in Parquet/Feather file is missing required key: "
                 "'{key}'".format(key=key)
             )
 
@@ -173,7 +174,7 @@ def _validate_metadata(metadata):
         for key in required_col_keys:
             if key not in column_metadata:
                 raise ValueError(
-                    "'geo' metadata in Parquet file is missing required key "
+                    "'geo' metadata in Parquet/Feather file is missing required key "
                     "'{key}' for column '{col}'".format(key=key, col=col)
                 )
 
@@ -181,7 +182,44 @@ def _validate_metadata(metadata):
             raise ValueError("Only WKB geometry encoding is supported")
 
 
-def _to_parquet(df, path, compression="snappy", index=None, **kwargs):
+def _geopandas_to_arrow(df, index=None):
+    """
+    Helper function with main, shared logic for to_parquet/to_feather.
+    """
+    from pyarrow import Table
+
+    warnings.warn(
+        "this is an initial implementation of Parquet/Feather file support and "
+        "associated metadata.  This is tracking version 0.1.0 of the metadata "
+        "specification at "
+        "https://github.com/geopandas/geo-arrow-spec\n\n"
+        "This metadata specification does not yet make stability promises.  "
+        "We do not yet recommend using this in a production setting unless you "
+        "are able to rewrite your Parquet/Feather files.\n\n"
+        "To further ignore this warning, you can do: \n"
+        "import warnings; warnings.filterwarnings('ignore', "
+        "message='.*initial implementation of Parquet.*')",
+        UserWarning,
+        stacklevel=4,
+    )
+
+    _validate_dataframe(df)
+
+    # create geo metadata before altering incoming data frame
+    geo_metadata = _create_metadata(df)
+
+    df = _encode_wkb(df)
+
+    table = Table.from_pandas(df, preserve_index=index)
+
+    # Store geopandas specific file-level metadata
+    # This must be done AFTER creating the table or it is not persisted
+    metadata = table.schema.metadata
+    metadata.update({b"geo": _encode_metadata(geo_metadata)})
+    return table.replace_schema_metadata(metadata)
+
+
+def _to_parquet(df, path, index=None, compression="snappy", **kwargs):
     """
     Write a GeoDataFrame to the Parquet format.
 
@@ -204,54 +242,123 @@ def _to_parquet(df, path, compression="snappy", index=None, **kwargs):
     Parameters
     ----------
     path : str, path object
-    compression : {'snappy', 'gzip', 'brotli', None}, default 'snappy'
-        Name of the compression to use. Use ``None`` for no compression.
     index : bool, default None
         If ``True``, always include the dataframe's index(es) as columns
         in the file output.
         If ``False``, the index(es) will not be written to the file.
         If ``None``, the index(ex) will be included as columns in the file
         output except `RangeIndex` which is stored as metadata only.
+    compression : {'snappy', 'gzip', 'brotli', None}, default 'snappy'
+        Name of the compression to use. Use ``None`` for no compression.
     kwargs
         Additional keyword arguments passed to pyarrow.parquet.write_table().
     """
-
-    import_optional_dependency(
+    parquet = import_optional_dependency(
         "pyarrow.parquet", extra="pyarrow is required for Parquet support."
     )
-    from pyarrow import parquet, Table
 
-    warnings.warn(
-        "this is an initial implementation of Parquet file support and "
-        "associated metadata.  This is tracking version 0.1.0 of the metadata "
-        "specification at "
-        "https://github.com/geopandas/geo-arrow-spec\n\n"
-        "This metadata specification does not yet make stability promises.  "
-        "We do not yet recommend using this in a production setting unless you "
-        "are able to rewrite your Parquet files.\n\n"
-        "To further ignore this warning, you can do: \n"
-        "import warnings; warnings.filterwarnings('ignore', "
-        "message='.*initial implementation of Parquet.*')",
-        UserWarning,
-        stacklevel=3,
+    table = _geopandas_to_arrow(df, index=index)
+    parquet.write_table(table, path, compression=compression, **kwargs)
+
+
+def _to_feather(df, path, index=None, compression=None, **kwargs):
+    """
+    Write a GeoDataFrame to the Feather format.
+
+    Any geometry columns present are serialized to WKB format in the file.
+
+    Requires 'pyarrow' >= 0.17.
+
+    WARNING: this is an initial implementation of Feather file support and
+    associated metadata.  This is tracking version 0.1.0 of the metadata
+    specification at:
+    https://github.com/geopandas/geo-arrow-spec
+
+    This metadata specification does not yet make stability promises.  As such,
+    we do not yet recommend using this in a production setting unless you are
+    able to rewrite your Feather files.
+
+    .. versionadded:: 0.8
+
+    Parameters
+    ----------
+    path : str, path object
+    index : bool, default None
+        If ``True``, always include the dataframe's index(es) as columns
+        in the file output.
+        If ``False``, the index(es) will not be written to the file.
+        If ``None``, the index(ex) will be included as columns in the file
+        output except `RangeIndex` which is stored as metadata only.
+    compression : {'zstd', 'lz4', 'uncompressed'}, optional
+        Name of the compression to use. Use ``"uncompressed"`` for no
+        compression. By default uses LZ4 if available, otherwise uncompressed.
+    kwargs
+        Additional keyword arguments passed to pyarrow.feather.write_feather().
+    """
+    feather = import_optional_dependency(
+        "pyarrow.feather", extra="pyarrow is required for Feather support."
     )
+    # TODO move this into `import_optional_dependency`
+    import pyarrow
 
-    _validate_dataframe(df)
+    if pyarrow.__version__ < LooseVersion("0.17.0"):
+        raise ImportError("pyarrow >= 0.17 required for Feather support")
 
-    # create geo metadata before altering incoming data frame
-    geo_metadata = _create_metadata(df)
+    table = _geopandas_to_arrow(df, index=index)
+    feather.write_feather(table, path, compression=compression, **kwargs)
 
-    df = _encode_wkb(df)
 
-    table = Table.from_pandas(df, preserve_index=index)
+def _arrow_to_geopandas(table):
+    """
+    Helper function with main, shared logic for read_parquet/read_feather.
+    """
+    df = table.to_pandas()
 
-    # Store geopandas specific file-level metadata
-    # This must be done AFTER creating the table or it is not persisted
     metadata = table.schema.metadata
-    metadata.update({b"geo": _encode_metadata(geo_metadata)})
+    if b"geo" not in metadata:
+        raise ValueError(
+            """Missing geo metadata in Parquet/Feather file.
+            Use pandas.read_parquet/read_feather() instead."""
+        )
 
-    table = table.replace_schema_metadata(metadata)
-    parquet.write_table(table, path, compression=compression, **kwargs)
+    try:
+        metadata = _decode_metadata(metadata.get(b"geo", b""))
+
+    except (TypeError, json.decoder.JSONDecodeError):
+        raise ValueError("Missing or malformed geo metadata in Parquet/Feather file")
+
+    _validate_metadata(metadata)
+
+    # Find all geometry columns that were read from the file.  May
+    # be a subset if 'columns' parameter is used.
+    geometry_columns = df.columns.intersection(metadata["columns"])
+
+    if not len(geometry_columns):
+        raise ValueError(
+            """No geometry columns are included in the columns read from
+            the Parquet/Feather file.  To read this file without geometry columns,
+            use pandas.read_parquet/read_feather() instead."""
+        )
+
+    geometry = metadata["primary_column"]
+
+    # Missing geometry likely indicates a subset of columns was read;
+    # promote the first available geometry to the primary geometry.
+    if len(geometry_columns) and geometry not in geometry_columns:
+        geometry = geometry_columns[0]
+
+        # if there are multiple non-primary geometry columns, raise a warning
+        if len(geometry_columns) > 1:
+            warnings.warn(
+                "Multiple non-primary geometry columns read from Parquet/Feather "
+                "file. The first column read was promoted to the primary geometry."
+            )
+
+    # Convert the WKB columns that are present back to geometry.
+    for col in geometry_columns:
+        df[col] = from_wkb(df[col].values, crs=metadata["columns"][col]["crs"])
+
+    return GeoDataFrame(df, geometry=geometry)
 
 
 def _read_parquet(path, columns=None, **kwargs):
@@ -289,58 +396,59 @@ def _read_parquet(path, columns=None, **kwargs):
     GeoDataFrame
     """
 
-    import_optional_dependency(
-        "pyarrow", extra="pyarrow is required for Parquet support."
+    parquet = import_optional_dependency(
+        "pyarrow.parquet", extra="pyarrow is required for Parquet support."
     )
-    from pyarrow import parquet
 
     kwargs["use_pandas_metadata"] = True
     table = parquet.read_table(path, columns=columns, **kwargs)
 
-    df = table.to_pandas()
+    return _arrow_to_geopandas(table)
 
-    metadata = table.schema.metadata
-    if b"geo" not in metadata:
-        raise ValueError(
-            """Missing geo metadata in Parquet file.
-            Use pandas.read_parquet() instead."""
-        )
 
-    try:
-        metadata = _decode_metadata(metadata.get(b"geo", b""))
+def _read_feather(path, columns=None, **kwargs):
+    """
+    Load a Feather object from the file path, returning a GeoDataFrame.
 
-    except (TypeError, json.decoder.JSONDecodeError):
-        raise ValueError("Missing or malformed geo metadata in Parquet file")
+    You can read a subset of columns in the file using the ``columns`` parameter.
+    However, the structure of the returned GeoDataFrame will depend on which
+    columns you read:
 
-    _validate_metadata(metadata)
+    * if no geometry columns are read, this will raise a ``ValueError`` - you
+      should use the pandas `read_feather` method instead.
+    * if the primary geometry column saved to this file is not included in
+      columns, the first available geometry column will be set as the geometry
+      column of the returned GeoDataFrame.
 
-    # Find all geometry columns that were read from the file.  May
-    # be a subset if 'columns' parameter is used.
-    geometry_columns = df.columns.intersection(metadata["columns"])
+    Requires 'pyarrow' >= 0.17.
 
-    if not len(geometry_columns):
-        raise ValueError(
-            """No geometry columns are included in the columns read from
-            the Parquet file.  To read this file without geometry columns,
-            use pandas.read_parquet() instead."""
-        )
+    .. versionadded:: 0.8
 
-    geometry = metadata["primary_column"]
+    Parameters
+    ----------
+    path : str, path object
+    columns : list-like of strings, default=None
+        If not None, only these columns will be read from the file.  If
+        the primary geometry column is not included, the first secondary
+        geometry read from the file will be set as the geometry column
+        of the returned GeoDataFrame.  If no geometry columns are present,
+        a ``ValueError`` will be raised.
+    **kwargs
+        Any additional kwargs passed to pyarrow.feather.read_table().
 
-    # Missing geometry likely indicates a subset of columns was read;
-    # promote the first available geometry to the primary geometry.
-    if len(geometry_columns) and geometry not in geometry_columns:
-        geometry = geometry_columns[0]
+    Returns
+    -------
+    GeoDataFrame
+    """
 
-        # if there are multiple non-primary geometry columns, raise a warning
-        if len(geometry_columns) > 1:
-            warnings.warn(
-                "Multiple non-primary geometry columns read from Parquet file.  "
-                "The first column read was promoted to the primary geometry."
-            )
+    feather = import_optional_dependency(
+        "pyarrow.feather", extra="pyarrow is required for Feather support."
+    )
+    # TODO move this into `import_optional_dependency`
+    import pyarrow
 
-    # Convert the WKB columns that are present back to geometry.
-    for col in geometry_columns:
-        df[col] = from_wkb(df[col].values, crs=metadata["columns"][col]["crs"])
+    if pyarrow.__version__ < LooseVersion("0.17.0"):
+        raise ImportError("pyarrow >= 0.17 required for Feather support")
 
-    return GeoDataFrame(df, geometry=geometry)
+    table = feather.read_table(path, columns=columns, **kwargs)
+    return _arrow_to_geopandas(table)
diff --git a/geopandas/io/sql.py b/geopandas/io/sql.py
index 56512e0b..16996c7e 100644
--- a/geopandas/io/sql.py
+++ b/geopandas/io/sql.py
@@ -103,8 +103,8 @@ def _read_postgis(
     -------
     GeoDataFrame
 
-    Example
-    -------
+    Examples
+    --------
     PostGIS
     >>> sql = "SELECT geom, kind FROM polygons"
     SpatiaLite
diff --git a/geopandas/tools/_show_versions.py b/geopandas/tools/_show_versions.py
index c7c59379..c977172a 100644
--- a/geopandas/tools/_show_versions.py
+++ b/geopandas/tools/_show_versions.py
@@ -124,9 +124,12 @@ def show_versions():
     """
     Print system information and installed module versions.
 
-    Example
-    -------
-    > python -c "import geopandas; geopandas.show_versions()"
+    Examples
+    --------
+
+    ::
+
+        $ python -c "import geopandas; geopandas.show_versions()"
     """
     sys_info = _get_sys_info()
     deps_info = _get_deps_info()
diff --git a/geopandas/tools/clip.py b/geopandas/tools/clip.py
index dd7cda85..8a1d6d1e 100644
--- a/geopandas/tools/clip.py
+++ b/geopandas/tools/clip.py
@@ -13,6 +13,7 @@ import pandas as pd
 from shapely.geometry import Polygon, MultiPolygon
 
 from geopandas import GeoDataFrame, GeoSeries
+from geopandas.array import _check_crs, _crs_mismatch_warn
 
 
 def _clip_points(gdf, poly):
@@ -129,6 +130,10 @@ def clip(gdf, mask, keep_geom_type=False):
             "(Multi)Polygon, got {}".format(type(gdf))
         )
 
+    if isinstance(mask, (GeoDataFrame, GeoSeries)):
+        if not _check_crs(gdf, mask):
+            _crs_mismatch_warn(gdf, mask, stacklevel=3)
+
     if isinstance(mask, (GeoDataFrame, GeoSeries)):
         box_mask = mask.total_bounds
     else:
diff --git a/geopandas/tools/overlay.py b/geopandas/tools/overlay.py
index e21f4607..422bad5f 100644
--- a/geopandas/tools/overlay.py
+++ b/geopandas/tools/overlay.py
@@ -1,9 +1,11 @@
+import warnings
 from functools import reduce
 
 import numpy as np
 import pandas as pd
 
 from geopandas import GeoDataFrame, GeoSeries
+from geopandas.array import _check_crs, _crs_mismatch_warn
 
 
 def _ensure_geometry_column(df):
@@ -179,6 +181,10 @@ def overlay(df1, df2, how="intersection", make_valid=True, keep_geom_type=True):
         raise NotImplementedError(
             "overlay currently only implemented for " "GeoDataFrames"
         )
+
+    if not _check_crs(df1, df2):
+        _crs_mismatch_warn(df1, df2, stacklevel=3)
+
     polys = ["Polygon", "MultiPolygon"]
     lines = ["LineString", "MultiLineString", "LinearRing"]
     points = ["Point", "MultiPoint"]
@@ -199,17 +205,19 @@ def overlay(df1, df2, how="intersection", make_valid=True, keep_geom_type=True):
     if df2.geom_type.isin(polys).all():
         df2[df2._geometry_column_name] = df2.geometry.buffer(0)
 
-    if how == "difference":
-        return _overlay_difference(df1, df2)
-    elif how == "intersection":
-        result = _overlay_intersection(df1, df2)
-    elif how == "symmetric_difference":
-        result = _overlay_symmetric_diff(df1, df2)
-    elif how == "union":
-        result = _overlay_union(df1, df2)
-    elif how == "identity":
-        dfunion = _overlay_union(df1, df2)
-        result = dfunion[dfunion["__idx1"].notnull()].copy()
+    with warnings.catch_warnings():  # CRS checked above, supress array-level warning
+        warnings.filterwarnings("ignore", message="CRS mismatch between the CRS")
+        if how == "difference":
+            return _overlay_difference(df1, df2)
+        elif how == "intersection":
+            result = _overlay_intersection(df1, df2)
+        elif how == "symmetric_difference":
+            result = _overlay_symmetric_diff(df1, df2)
+        elif how == "union":
+            result = _overlay_union(df1, df2)
+        elif how == "identity":
+            dfunion = _overlay_union(df1, df2)
+            result = dfunion[dfunion["__idx1"].notnull()].copy()
 
     if keep_geom_type:
         type = df1.geom_type.iloc[0]
diff --git a/geopandas/tools/sjoin.py b/geopandas/tools/sjoin.py
index 7f67c19a..79133b00 100644
--- a/geopandas/tools/sjoin.py
+++ b/geopandas/tools/sjoin.py
@@ -1,5 +1,3 @@
-from warnings import warn
-
 import numpy as np
 import pandas as pd
 
@@ -7,6 +5,7 @@ from shapely import prepared
 
 from geopandas import GeoDataFrame
 from geopandas import _compat as compat
+from geopandas.array import _check_crs, _crs_mismatch_warn
 
 
 def sjoin(
@@ -55,13 +54,8 @@ def sjoin(
             '`op` was "%s" but is expected to be in %s' % (op, allowed_ops)
         )
 
-    if left_df.crs != right_df.crs:
-        warn(
-            (
-                "CRS of frames being joined does not match!"
-                "(%s != %s)" % (left_df.crs, right_df.crs)
-            )
-        )
+    if not _check_crs(left_df, right_df):
+        _crs_mismatch_warn(left_df, right_df, stacklevel=3)
 
     index_left = "index_%s" % lsuffix
     index_right = "index_%s" % rsuffix

ENH: add support for to_feather / read_feather
This would build on #1180 to enable I/O with `feather` format as of `pyarrow` >= 0.17.

Once this lands, the standalone [`geofeather`](https://github.com/brendan-ward/geofeather) package can be deprecated and replaced with this instead.
diff --git a/geopandas/io/tests/test_parquet.py b/geopandas/io/tests/test_arrow.py
similarity index 74%
rename from geopandas/io/tests/test_parquet.py
rename to geopandas/io/tests/test_arrow.py
index 6eed0bac..ecfa37cd 100644
--- a/geopandas/io/tests/test_parquet.py
+++ b/geopandas/io/tests/test_arrow.py
@@ -1,4 +1,6 @@
 from __future__ import absolute_import
+
+from distutils.version import LooseVersion
 import os
 
 import pytest
@@ -7,10 +9,10 @@ from pandas.testing import assert_frame_equal
 import numpy as np
 
 import geopandas
-from geopandas import GeoDataFrame, read_file, read_parquet
+from geopandas import GeoDataFrame, read_file, read_parquet, read_feather
 from geopandas.array import to_wkb
 from geopandas.datasets import get_path
-from geopandas.io.parquet import (
+from geopandas.io.arrow import (
     _create_metadata,
     _decode_metadata,
     _encode_metadata,
@@ -23,12 +25,31 @@ from geopandas.testing import assert_geodataframe_equal, assert_geoseries_equal
 
 
 # Skip all tests in this module if pyarrow is not available
-pyarrow_skip = pytest.importorskip("pyarrow")
+pyarrow = pytest.importorskip("pyarrow")
 
 # TEMPORARY: hide warning from to_parquet
 pytestmark = pytest.mark.filterwarnings("ignore:.*initial implementation of Parquet.*")
 
 
+@pytest.fixture(
+    params=[
+        "parquet",
+        pytest.param(
+            "feather",
+            marks=pytest.mark.skipif(
+                pyarrow.__version__ < LooseVersion("0.17.0"),
+                reason="needs pyarrow >= 0.17",
+            ),
+        ),
+    ]
+)
+def file_format(request):
+    if request.param == "parquet":
+        return read_parquet, GeoDataFrame.to_parquet
+    elif request.param == "feather":
+        return read_feather, GeoDataFrame.to_feather
+
+
 def test_create_metadata():
     test_dataset = "naturalearth_lowres"
     df = read_file(get_path(test_dataset))
@@ -104,14 +125,14 @@ def test_validate_metadata_valid():
 @pytest.mark.parametrize(
     "metadata,error",
     [
-        ({}, "Missing or malformed geo metadata in Parquet file"),
+        ({}, "Missing or malformed geo metadata in Parquet/Feather file"),
         (
             {"primary_column": "foo"},
-            "'geo' metadata in Parquet file is missing required key:",
+            "'geo' metadata in Parquet/Feather file is missing required key:",
         ),
         (
             {"primary_column": "foo", "columns": None},
-            "'geo' metadata in Parquet file is missing required key",
+            "'geo' metadata in Parquet/Feather file is missing required key",
         ),
         (
             {"primary_column": "foo", "columns": []},
@@ -120,17 +141,17 @@ def test_validate_metadata_valid():
         (
             {"primary_column": "foo", "columns": {"foo": {}}},
             (
-                "'geo' metadata in Parquet file is missing required key 'crs' "
+                "'geo' metadata in Parquet/Feather file is missing required key 'crs' "
                 "for column 'foo'"
             ),
         ),
         (
             {"primary_column": "foo", "columns": {"foo": {"crs": None}}},
-            "'geo' metadata in Parquet file is missing required key",
+            "'geo' metadata in Parquet/Feather file is missing required key",
         ),
         (
             {"primary_column": "foo", "columns": {"foo": {"encoding": None}}},
-            "'geo' metadata in Parquet file is missing required key",
+            "'geo' metadata in Parquet/Feather file is missing required key",
         ),
         (
             {
@@ -198,10 +219,11 @@ def test_pandas_parquet_roundtrip2(test_dataset, tmpdir):
 @pytest.mark.parametrize(
     "test_dataset", ["naturalearth_lowres", "naturalearth_cities", "nybb"]
 )
-def test_parquet_roundtrip(test_dataset, tmpdir):
+def test_roundtrip(tmpdir, file_format, test_dataset):
     """Writing to parquet should not raise errors, and should not alter original
     GeoDataFrame
     """
+    reader, writer = file_format
 
     df = read_file(get_path(test_dataset))
     orig = df.copy()
@@ -210,7 +232,7 @@ def test_parquet_roundtrip(test_dataset, tmpdir):
 
     # TEMP: Initial implementation should raise a UserWarning
     with pytest.warns(UserWarning, match="initial implementation"):
-        df.to_parquet(filename)
+        writer(df, filename)
 
     assert os.path.exists(filename)
 
@@ -218,28 +240,29 @@ def test_parquet_roundtrip(test_dataset, tmpdir):
     assert_geodataframe_equal(df, orig)
 
     # make sure that we can roundtrip the data frame
-    pq_df = read_parquet(filename)
+    pq_df = reader(filename)
 
     assert isinstance(pq_df, GeoDataFrame)
     assert_geodataframe_equal(df, pq_df)
 
 
-def test_parquet_index(tmpdir):
+def test_index(tmpdir, file_format):
     """Setting index=`True` should preserve index in output, and
     setting index=`False` should drop index from output.
     """
+    reader, writer = file_format
 
     test_dataset = "naturalearth_lowres"
     df = read_file(get_path(test_dataset)).set_index("iso_a3")
 
     filename = os.path.join(str(tmpdir), "test_with_index.pq")
-    df.to_parquet(filename, index=True)
-    pq_df = read_parquet(filename)
+    writer(df, filename, index=True)
+    pq_df = reader(filename)
     assert_geodataframe_equal(df, pq_df)
 
     filename = os.path.join(str(tmpdir), "drop_index.pq")
-    df.to_parquet(filename, index=False)
-    pq_df = read_parquet(filename)
+    writer(df, filename, index=False)
+    pq_df = reader(filename)
     assert_geodataframe_equal(df.reset_index(drop=True), pq_df)
 
 
@@ -260,21 +283,43 @@ def test_parquet_compression(compression, tmpdir):
     assert_geodataframe_equal(df, pq_df)
 
 
-def test_parquet_multiple_geom_cols(tmpdir):
+@pytest.mark.skipif(
+    pyarrow.__version__ < LooseVersion("0.17.0"),
+    reason="Feather only supported for pyarrow >= 0.17",
+)
+@pytest.mark.parametrize("compression", ["uncompressed", "lz4", "zstd"])
+def test_feather_compression(compression, tmpdir):
+    """Using compression options should not raise errors, and should
+    return identical GeoDataFrame.
+    """
+
+    test_dataset = "naturalearth_lowres"
+    df = read_file(get_path(test_dataset))
+
+    filename = os.path.join(str(tmpdir), "test.feather")
+    df.to_feather(filename, compression=compression)
+    pq_df = read_feather(filename)
+
+    assert isinstance(pq_df, GeoDataFrame)
+    assert_geodataframe_equal(df, pq_df)
+
+
+def test_parquet_multiple_geom_cols(tmpdir, file_format):
     """If multiple geometry columns are present when written to parquet,
     they should all be returned as such when read from parquet.
     """
+    reader, writer = file_format
 
     test_dataset = "naturalearth_lowres"
     df = read_file(get_path(test_dataset))
     df["geom2"] = df.geometry.copy()
 
     filename = os.path.join(str(tmpdir), "test.pq")
-    df.to_parquet(filename)
+    writer(df, filename)
 
     assert os.path.exists(filename)
 
-    pq_df = read_parquet(filename)
+    pq_df = reader(filename)
 
     assert isinstance(pq_df, GeoDataFrame)
     assert_geodataframe_equal(df, pq_df)
@@ -302,21 +347,23 @@ def test_parquet_missing_metadata(tmpdir):
     df.to_parquet(filename)
 
     # missing metadata will raise ValueError
-    with pytest.raises(ValueError, match="Missing geo metadata in Parquet file."):
+    with pytest.raises(
+        ValueError, match="Missing geo metadata in Parquet/Feather file."
+    ):
         read_parquet(filename)
 
 
 @pytest.mark.parametrize(
     "geo_meta,error",
     [
-        ({"geo": b""}, "Missing or malformed geo metadata in Parquet file"),
+        ({"geo": b""}, "Missing or malformed geo metadata in Parquet/Feather file"),
         (
             {"geo": _encode_metadata({})},
-            "Missing or malformed geo metadata in Parquet file",
+            "Missing or malformed geo metadata in Parquet/Feather file",
         ),
         (
             {"geo": _encode_metadata({"foo": "bar"})},
-            "'geo' metadata in Parquet file is missing required key",
+            "'geo' metadata in Parquet/Feather file is missing required key",
         ),
     ],
 )
@@ -348,24 +395,25 @@ def test_parquet_invalid_metadata(tmpdir, geo_meta, error):
         read_parquet(filename)
 
 
-def test_parquet_subset_columns(tmpdir):
+def test_subset_columns(tmpdir, file_format):
     """Reading a subset of columns should correctly decode selected geometry
     columns.
     """
+    reader, writer = file_format
 
     test_dataset = "naturalearth_lowres"
     df = read_file(get_path(test_dataset))
 
     filename = os.path.join(str(tmpdir), "test.pq")
-    df.to_parquet(filename)
-    pq_df = read_parquet(filename, columns=["name", "geometry"])
+    writer(df, filename)
+    pq_df = reader(filename, columns=["name", "geometry"])
 
     assert_geodataframe_equal(df[["name", "geometry"]], pq_df)
 
     with pytest.raises(
         ValueError, match="No geometry columns are included in the columns read"
     ):
-        read_parquet(filename, columns=[])
+        reader(filename, columns=["name"])
 
 
 def test_parquet_repeat_columns(tmpdir):
@@ -384,53 +432,56 @@ def test_parquet_repeat_columns(tmpdir):
     assert pq_df.columns.tolist() == ["name", "iso_a3", "geometry"]
 
 
-def test_parquet_promote_secondary_geometry(tmpdir):
+def test_promote_secondary_geometry(tmpdir, file_format):
     """Reading a subset of columns that does not include the primary geometry
     column should promote the first geometry column present.
     """
+    reader, writer = file_format
 
     test_dataset = "naturalearth_lowres"
     df = read_file(get_path(test_dataset))
     df["geom2"] = df.geometry.copy()
 
     filename = os.path.join(str(tmpdir), "test.pq")
-    df.to_parquet(filename)
-    pq_df = read_parquet(filename, columns=["name", "geom2"])
+    writer(df, filename)
+    pq_df = reader(filename, columns=["name", "geom2"])
 
     assert_geodataframe_equal(df.set_geometry("geom2")[["name", "geom2"]], pq_df)
 
     df["geom3"] = df.geometry.copy()
 
-    df.to_parquet(filename)
+    writer(df, filename)
     with pytest.warns(
         UserWarning,
-        match="Multiple non-primary geometry columns read from Parquet file.",
+        match="Multiple non-primary geometry columns read from Parquet/Feather file.",
     ):
-        pq_df = read_parquet(filename, columns=["name", "geom2", "geom3"])
+        pq_df = reader(filename, columns=["name", "geom2", "geom3"])
 
     assert_geodataframe_equal(
         df.set_geometry("geom2")[["name", "geom2", "geom3"]], pq_df
     )
 
 
-def test_parquet_columns_no_geometry(tmpdir):
+def test_columns_no_geometry(tmpdir, file_format):
     """Reading a parquet file that is missing all of the geometry columns
     should raise a ValueError"""
+    reader, writer = file_format
 
     test_dataset = "naturalearth_lowres"
     df = read_file(get_path(test_dataset))
 
     filename = os.path.join(str(tmpdir), "test.pq")
-    df.to_parquet(filename)
+    writer(df, filename)
 
     with pytest.raises(ValueError):
-        read_parquet(filename, columns=["name"])
+        reader(filename, columns=["name"])
 
 
-def test_parquet_missing_crs(tmpdir):
+def test_missing_crs(tmpdir, file_format):
     """If CRS is `None`, it should be properly handled
     and remain `None` when read from parquet`.
     """
+    reader, writer = file_format
 
     test_dataset = "naturalearth_lowres"
 
@@ -438,9 +489,23 @@ def test_parquet_missing_crs(tmpdir):
     df.crs = None
 
     filename = os.path.join(str(tmpdir), "test.pq")
-    df.to_parquet(filename)
-    pq_df = read_parquet(filename)
+    writer(df, filename)
+    pq_df = reader(filename)
 
     assert pq_df.crs is None
 
     assert_geodataframe_equal(df, pq_df, check_crs=True)
+
+
+@pytest.mark.skipif(
+    pyarrow.__version__ >= LooseVersion("0.17.0"),
+    reason="Feather only supported for pyarrow >= 0.17",
+)
+def test_feather_arrow_version(tmpdir):
+    df = read_file(get_path("naturalearth_lowres"))
+    filename = os.path.join(str(tmpdir), "test.feather")
+
+    with pytest.raises(
+        ImportError, match="pyarrow >= 0.17 required for Feather support"
+    ):
+        df.to_feather(filename)
diff --git a/geopandas/tests/test_array.py b/geopandas/tests/test_array.py
index 90b28c18..8710af73 100644
--- a/geopandas/tests/test_array.py
+++ b/geopandas/tests/test_array.py
@@ -21,6 +21,7 @@ from geopandas.array import (
     to_wkb,
     to_wkt,
     _check_crs,
+    _crs_mismatch_warn,
 )
 import geopandas._compat as compat
 
@@ -822,3 +823,36 @@ def test_check_crs():
     assert _check_crs(t1, T) is False
     assert _check_crs(t1, t1) is True
     assert _check_crs(t1, T, allow_none=True) is True
+
+
+def test_crs_mismatch_warn():
+    t1 = T.copy()
+    t2 = T.copy()
+    t1.crs = 4326
+    t2.crs = 3857
+
+    # two different CRS
+    with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
+        _crs_mismatch_warn(t1, t2)
+
+    # left None
+    with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
+        _crs_mismatch_warn(T, t2)
+
+    # right None
+    with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
+        _crs_mismatch_warn(t1, T)
+
+
+@pytest.mark.parametrize("NA", [None, np.nan])
+def test_isna(NA):
+    t1 = T.copy()
+    t1[0] = NA
+    assert t1[0] is None
+
+
+@pytest.mark.skipif(not compat.PANDAS_GE_10, reason="pd.NA introduced in pandas 1.0")
+def test_isna_pdNA():
+    t1 = T.copy()
+    t1[0] = pd.NA
+    assert t1[0] is None
diff --git a/geopandas/tests/test_geom_methods.py b/geopandas/tests/test_geom_methods.py
index f7151463..4fcdb006 100644
--- a/geopandas/tests/test_geom_methods.py
+++ b/geopandas/tests/test_geom_methods.py
@@ -12,6 +12,7 @@ from geopandas import GeoDataFrame, GeoSeries
 from geopandas.base import GeoPandasBase
 
 from geopandas.tests.util import assert_geoseries_equal, geom_almost_equals, geom_equals
+from geopandas._compat import PANDAS_GE_024
 from pandas.testing import assert_frame_equal, assert_series_equal
 import pytest
 
@@ -658,6 +659,27 @@ class TestGeomMethods:
         expected_df = expected_df.set_index(expected_index)
         assert_frame_equal(test_df, expected_df)
 
+    @pytest.mark.parametrize("index_name", [None, "test"])
+    def test_explode_geodataframe_level_1(self, index_name):
+        # GH1393
+        s = GeoSeries([MultiPoint([Point(1, 2), Point(2, 3)]), Point(5, 5)])
+        df = GeoDataFrame({"level_1": [1, 2], "geometry": s})
+        df.index.name = index_name
+
+        test_df = df.explode()
+
+        expected_s = GeoSeries([Point(1, 2), Point(2, 3), Point(5, 5)])
+        expected_df = GeoDataFrame({"level_1": [1, 1, 2], "geometry": expected_s})
+        expected_index = MultiIndex(
+            [[0, 1], [0, 1]],  # levels
+            [[0, 0, 1], [0, 1, 0]],  # labels/codes
+            names=[index_name, None],
+        )
+        expected_df = expected_df.set_index(expected_index)
+        if not PANDAS_GE_024:
+            expected_df = expected_df[["level_1", "geometry"]]
+        assert_frame_equal(test_df, expected_df)
+
     #
     # Test '&', '|', '^', and '-'
     #
diff --git a/geopandas/tests/test_overlay.py b/geopandas/tests/test_overlay.py
index cf250f44..467fbb91 100644
--- a/geopandas/tests/test_overlay.py
+++ b/geopandas/tests/test_overlay.py
@@ -327,6 +327,14 @@ def test_preserve_crs(dfs, how):
     assert result.crs == crs
 
 
+def test_crs_mismatch(dfs, how):
+    df1, df2 = dfs
+    df1.crs = 4326
+    df2.crs = 3857
+    with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
+        overlay(df1, df2, how=how)
+
+
 def test_empty_intersection(dfs):
     df1, df2 = dfs
     polys3 = GeoSeries(
diff --git a/geopandas/tools/tests/test_clip.py b/geopandas/tools/tests/test_clip.py
index 5c8f552b..40abaa04 100644
--- a/geopandas/tools/tests/test_clip.py
+++ b/geopandas/tools/tests/test_clip.py
@@ -360,3 +360,8 @@ def test_warning_geomcoll(single_rectangle_gdf, geomcol_gdf):
     called on a GDF with GeometryCollection"""
     with pytest.warns(UserWarning):
         clip(geomcol_gdf, single_rectangle_gdf, keep_geom_type=True)
+
+
+def test_warning_crs_mismatch(point_gdf, single_rectangle_gdf):
+    with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
+        clip(point_gdf, single_rectangle_gdf.to_crs(3857))
diff --git a/geopandas/tools/tests/test_sjoin.py b/geopandas/tools/tests/test_sjoin.py
index 5c0f9b0a..b3013476 100644
--- a/geopandas/tools/tests/test_sjoin.py
+++ b/geopandas/tools/tests/test_sjoin.py
@@ -95,7 +95,7 @@ class TestSpatialJoin:
     def test_crs_mismatch(self, dfs):
         index, df1, df2, expected = dfs
         df1.crs = "epsg:4326"
-        with pytest.warns(UserWarning):
+        with pytest.warns(UserWarning, match="CRS mismatch between the CRS"):
             sjoin(df1, df2)
 
     @pytest.mark.parametrize(

environment.yml
pip install -e .
parse_log_pytest
environment.yml
pytest
3.9
pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
attrs @ file:///home/conda/feedstock_root/build_artifacts/attrs_1741918516150/work
black @ file:///home/conda/feedstock_root/build_artifacts/black-recipe_1742502760723/work
Brotli @ file:///home/conda/feedstock_root/build_artifacts/brotli-split_1648883617327/work
certifi @ file:///home/conda/feedstock_root/build_artifacts/certifi_1739515848642/work/certifi
cffi @ file:///home/conda/feedstock_root/build_artifacts/cffi_1636046055389/work
cfgv @ file:///home/conda/feedstock_root/build_artifacts/cfgv_1734267080977/work
charset-normalizer @ file:///home/conda/feedstock_root/build_artifacts/charset-normalizer_1735929714516/work
click @ file:///home/conda/feedstock_root/build_artifacts/click_1734858813237/work
click-plugins @ file:///home/conda/feedstock_root/build_artifacts/click-plugins_1733731077999/work
cligj @ file:///home/conda/feedstock_root/build_artifacts/cligj_1733749956636/work
codecov @ file:///home/conda/feedstock_root/build_artifacts/codecov_1734975286850/work
colorama @ file:///home/conda/feedstock_root/build_artifacts/colorama_1733218098505/work
contourpy @ file:///home/conda/feedstock_root/build_artifacts/contourpy_1652954513473/work
coverage @ file:///home/conda/feedstock_root/build_artifacts/coverage_1743381221492/work
cycler @ file:///home/conda/feedstock_root/build_artifacts/cycler_1733332471406/work
descartes @ file:///home/conda/feedstock_root/build_artifacts/descartes_1734602598660/work
distlib @ file:///home/conda/feedstock_root/build_artifacts/distlib_1733238395481/work
exceptiongroup @ file:///home/conda/feedstock_root/build_artifacts/exceptiongroup_1733208806608/work
filelock @ file:///home/conda/feedstock_root/build_artifacts/filelock_1741969488311/work
Fiona==1.8.20
fonttools @ file:///home/conda/feedstock_root/build_artifacts/fonttools_1651017733844/work
GDAL==3.3.2
geographiclib @ file:///home/conda/feedstock_root/build_artifacts/geographiclib_1734342349249/work
-e git+https://github.com/geopandas/geopandas.git@6f86fcde3c2402073a3b5f023f5bfa26551252db#egg=geopandas
geopy @ file:///home/conda/feedstock_root/build_artifacts/geopy_1734341931581/work
greenlet @ file:///home/conda/feedstock_root/build_artifacts/greenlet_1648882382645/work
h2 @ file:///home/conda/feedstock_root/build_artifacts/h2_1738578511449/work
hpack @ file:///home/conda/feedstock_root/build_artifacts/hpack_1737618293087/work
hyperframe @ file:///home/conda/feedstock_root/build_artifacts/hyperframe_1737618333194/work
identify @ file:///home/conda/feedstock_root/build_artifacts/identify_1741502659866/work
idna @ file:///home/conda/feedstock_root/build_artifacts/idna_1733211830134/work
importlib_resources @ file:///home/conda/feedstock_root/build_artifacts/importlib_resources_1736252299705/work
iniconfig @ file:///home/conda/feedstock_root/build_artifacts/iniconfig_1733223141826/work
kiwisolver @ file:///home/conda/feedstock_root/build_artifacts/kiwisolver_1648854392795/work
matplotlib @ file:///croot/matplotlib-suite_1713336378214/work
mock @ file:///home/conda/feedstock_root/build_artifacts/mock_1741073951612/work
munch @ file:///home/conda/feedstock_root/build_artifacts/munch_1734441240299/work
munkres==1.1.4
mypy_extensions @ file:///home/conda/feedstock_root/build_artifacts/mypy_extensions_1733230897951/work
nodeenv @ file:///home/conda/feedstock_root/build_artifacts/nodeenv_1734112117269/work
numpy @ file:///home/conda/feedstock_root/build_artifacts/numpy_1651020388495/work
olefile @ file:///home/conda/feedstock_root/build_artifacts/olefile_1734769783178/work
packaging @ file:///home/conda/feedstock_root/build_artifacts/packaging_1733203243479/work
pandas==1.4.2
pathspec @ file:///home/conda/feedstock_root/build_artifacts/pathspec_1733233363808/work
Pillow @ file:///home/conda/feedstock_root/build_artifacts/pillow_1630696601414/work
platformdirs @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_platformdirs_1742485085/work
pluggy @ file:///home/conda/feedstock_root/build_artifacts/pluggy_1733222765875/work
pre_commit @ file:///home/conda/feedstock_root/build_artifacts/pre-commit_1742475552107/work
psycopg2 @ file:///home/conda/feedstock_root/build_artifacts/psycopg2-split_1636701485031/work
pycparser @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_pycparser_1733195786/work
pyparsing @ file:///home/conda/feedstock_root/build_artifacts/pyparsing_1652235407899/work
pyproj @ file:///home/conda/feedstock_root/build_artifacts/pyproj_1650803091911/work
PyQt5==5.12.3
PyQt5_sip==4.19.18
PyQtChart==5.12
PyQtWebEngine==5.12.1
PySocks @ file:///home/conda/feedstock_root/build_artifacts/pysocks_1733217236728/work
pytest @ file:///home/conda/feedstock_root/build_artifacts/pytest_1740946542080/work
pytest-cov @ file:///home/conda/feedstock_root/build_artifacts/pytest-cov_1733223023082/work
python-dateutil @ file:///home/conda/feedstock_root/build_artifacts/python-dateutil_1733215673016/work
pytz @ file:///home/conda/feedstock_root/build_artifacts/pytz_1742920838005/work
PyYAML @ file:///home/conda/feedstock_root/build_artifacts/pyyaml_1648757097602/work
requests @ file:///home/conda/feedstock_root/build_artifacts/requests_1733217035951/work
rtree @ file:///home/conda/feedstock_root/build_artifacts/rtree_1741378561624/work
Shapely @ file:///home/conda/feedstock_root/build_artifacts/shapely_1635194355088/work
six @ file:///home/conda/feedstock_root/build_artifacts/six_1733380938961/work
SQLAlchemy @ file:///home/conda/feedstock_root/build_artifacts/sqlalchemy_1651017976253/work
toml @ file:///home/conda/feedstock_root/build_artifacts/toml_1734091811753/work
tomli @ file:///home/conda/feedstock_root/build_artifacts/tomli_1733256695513/work
tornado @ file:///home/conda/feedstock_root/build_artifacts/tornado_1648827245914/work
typing_extensions @ file:///home/conda/feedstock_root/build_artifacts/bld/rattler-build_typing_extensions_1743201626/work
ukkonen @ file:///home/conda/feedstock_root/build_artifacts/ukkonen_1649407036294/work
unicodedata2 @ file:///home/conda/feedstock_root/build_artifacts/unicodedata2_1649111919389/work
urllib3 @ file:///home/conda/feedstock_root/build_artifacts/urllib3_1734859416348/work
virtualenv @ file:///home/conda/feedstock_root/build_artifacts/virtualenv_1741337798015/work
zipp @ file:///home/conda/feedstock_root/build_artifacts/zipp_1732827521216/work
zstandard @ file:///croot/zstandard_1677013143055/work

name: geopandas
channels:
  - defaults
  - https://repo.anaconda.com/pkgs/main
  - https://repo.anaconda.com/pkgs/r
  - conda-forge
dependencies:
  - _libgcc_mutex=0.1=main
  - _openmp_mutex=5.1=1_gnu
  - alsa-lib=1.2.3.2=h166bdaf_0
  - attrs=25.3.0=pyh71513ae_0
  - black=25.1.0=pyha5154f8_0
  - boost-cpp=1.74.0=h312852a_4
  - brotli=1.0.9=h166bdaf_7
  - brotli-bin=1.0.9=h166bdaf_7
  - brotli-python=1.0.9=py39h5a03fae_7
  - bzip2=1.0.8=h7f98852_4
  - c-ares=1.18.1=h7f98852_0
  - ca-certificates=2025.2.25=h06a4308_0
  - cairo=1.16.0=h6cf1ce9_1008
  - certifi=2025.1.31=pyhd8ed1ab_0
  - cffi=1.15.0=py39h4bc2ebd_0
  - cfgv=3.3.1=pyhd8ed1ab_1
  - cfitsio=3.470=hb418390_7
  - charset-normalizer=3.4.1=pyhd8ed1ab_0
  - click=8.1.8=pyh707e725_0
  - click-plugins=1.1.1=pyhd8ed1ab_1
  - cligj=0.7.2=pyhd8ed1ab_2
  - codecov=2.1.13=pyhd8ed1ab_1
  - colorama=0.4.6=pyhd8ed1ab_1
  - contourpy=1.0.2=py39hf939315_2
  - coverage=7.8.0=pyhe1237c8_0
  - curl=7.79.1=h2574ce0_1
  - cycler=0.12.1=pyhd8ed1ab_1
  - dbus=1.13.6=h48d8840_2
  - descartes=1.1.0=pyhd8ed1ab_5
  - distlib=0.3.9=pyhd8ed1ab_1
  - exceptiongroup=1.2.2=pyhd8ed1ab_1
  - expat=2.4.8=h27087fc_0
  - filelock=3.18.0=pyhd8ed1ab_0
  - fiona=1.8.20=py39h427c1bf_2
  - font-ttf-dejavu-sans-mono=2.37=hab24e00_0
  - font-ttf-inconsolata=3.000=h77eed37_0
  - font-ttf-source-code-pro=2.038=h77eed37_0
  - font-ttf-ubuntu=0.83=h77eed37_3
  - fontconfig=2.14.0=h8e229c2_0
  - fonts-conda-ecosystem=1=0
  - fonts-conda-forge=1=0
  - fonttools=4.33.3=py39hb9d737c_0
  - freetype=2.10.4=h0708190_1
  - freexl=1.0.6=h7f98852_0
  - gdal=3.3.2=py39h218ed2d_2
  - geographiclib=2.0=pyhd8ed1ab_1
  - geopy=2.4.1=pyhd8ed1ab_2
  - geos=3.9.1=h9c3ff4c_2
  - geotiff=1.7.0=h4f31c25_0
  - gettext=0.19.8.1=h73d1719_1008
  - giflib=5.2.1=h36c2ea0_2
  - glib=2.68.4=h9c3ff4c_1
  - glib-tools=2.68.4=h9c3ff4c_1
  - greenlet=1.1.2=py39h5a03fae_2
  - gst-plugins-base=1.18.5=hf529b03_0
  - gstreamer=1.18.5=h76c114f_0
  - h2=4.2.0=pyhd8ed1ab_0
  - hdf4=4.2.15=h10796ff_3
  - hdf5=1.12.1=nompi_h2750804_100
  - hpack=4.1.0=pyhd8ed1ab_0
  - hyperframe=6.1.0=pyhd8ed1ab_0
  - icu=68.2=h9c3ff4c_0
  - identify=2.6.9=pyhd8ed1ab_0
  - idna=3.10=pyhd8ed1ab_1
  - importlib_resources=6.5.2=pyhd8ed1ab_0
  - iniconfig=2.0.0=pyhd8ed1ab_1
  - jbig=2.1=h7f98852_2003
  - jpeg=9e=h166bdaf_1
  - json-c=0.15=h98cffda_0
  - kealib=1.4.14=h87e4c3c_3
  - keyutils=1.6.1=h166bdaf_0
  - kiwisolver=1.4.2=py39hf939315_1
  - krb5=1.19.3=h3790be6_0
  - lcms2=2.12=hddcbb42_0
  - ld_impl_linux-64=2.40=h12ee557_0
  - lerc=2.2.1=h9c3ff4c_0
  - libblas=3.9.0=16_linux64_openblas
  - libbrotlicommon=1.0.9=h166bdaf_7
  - libbrotlidec=1.0.9=h166bdaf_7
  - libbrotlienc=1.0.9=h166bdaf_7
  - libcblas=3.9.0=16_linux64_openblas
  - libclang=11.1.0=default_ha53f305_1
  - libcurl=7.79.1=h2574ce0_1
  - libdap4=3.20.6=hd7c4107_2
  - libdeflate=1.7=h7f98852_5
  - libedit=3.1.20191231=he28a2e2_2
  - libev=4.33=h516909a_1
  - libevent=2.1.10=h9b69904_4
  - libffi=3.4.4=h6a678d5_1
  - libgcc-ng=11.2.0=h1234567_1
  - libgdal=3.3.2=hd2dd7ad_2
  - libgfortran-ng=13.2.0=h69a702a_0
  - libgfortran5=13.2.0=ha4646dd_0
  - libglib=2.68.4=h174f98d_1
  - libgomp=11.2.0=h1234567_1
  - libiconv=1.17=h166bdaf_0
  - libkml=1.3.0=h238a007_1014
  - liblapack=3.9.0=16_linux64_openblas
  - libllvm11=11.1.0=hf817b99_2
  - libnetcdf=4.8.1=nompi_hb3fd0d9_101
  - libnghttp2=1.43.0=h812cca2_1
  - libnsl=2.0.0=h7f98852_0
  - libogg=1.3.4=h7f98852_1
  - libopenblas=0.3.21=h043d6bf_0
  - libopus=1.3.1=h7f98852_1
  - libpng=1.6.37=h21135ba_2
  - libpq=13.3=hd57d9b9_0
  - librttopo=1.1.0=h1185371_6
  - libspatialindex=1.9.3=h9c3ff4c_4
  - libspatialite=5.0.1=h8694cbe_6
  - libssh2=1.10.0=ha56f1ee_2
  - libstdcxx-ng=11.2.0=h1234567_1
  - libtiff=4.3.0=hf544144_1
  - libuuid=2.32.1=h7f98852_1000
  - libvorbis=1.3.7=h9c3ff4c_0
  - libwebp-base=1.2.2=h7f98852_1
  - libxcb=1.13=h7f98852_1004
  - libxkbcommon=1.0.3=he3ba5ed_0
  - libxml2=2.9.12=h72842e0_0
  - libzip=1.8.0=h4de3113_1
  - lz4-c=1.9.3=h9c3ff4c_1
  - matplotlib=3.8.4=py39hf3d152e_2
  - matplotlib-base=3.8.4=py39h1128e8f_0
  - mock=5.2.0=pyhd8ed1ab_0
  - munch=4.0.0=pyhd8ed1ab_1
  - munkres=1.1.4=pyh9f0ad1d_0
  - mypy_extensions=1.0.0=pyha770c72_1
  - mysql-common=8.0.25=ha770c72_2
  - mysql-libs=8.0.25=hfa10184_2
  - ncurses=6.4=h6a678d5_0
  - nodeenv=1.9.1=pyhd8ed1ab_1
  - nspr=4.32=h9c3ff4c_1
  - nss=3.69=hb5efdd6_1
  - numpy=1.22.3=py39hc58783e_2
  - olefile=0.47=pyhd8ed1ab_1
  - openjpeg=2.4.0=hb52868f_1
  - openssl=1.1.1o=h166bdaf_0
  - packaging=24.2=pyhd8ed1ab_2
  - pandas=1.4.2=py39h1832856_1
  - pathspec=0.12.1=pyhd8ed1ab_1
  - pcre=8.45=h9c3ff4c_0
  - pillow=8.3.2=py39ha612740_0
  - pip=25.0=py39h06a4308_0
  - pixman=0.40.0=h36c2ea0_0
  - platformdirs=4.3.7=pyh29332c3_0
  - pluggy=1.5.0=pyhd8ed1ab_1
  - poppler=21.09.0=ha39eefc_3
  - poppler-data=0.4.12=hd8ed1ab_0
  - postgresql=13.3=h2510834_0
  - pre-commit=4.2.0=pyha770c72_0
  - proj=8.0.1=h277dcde_0
  - psycopg2=2.9.2=py39h3811e60_0
  - pthread-stubs=0.4=h36c2ea0_1001
  - pycparser=2.22=pyh29332c3_1
  - pyparsing=3.0.9=pyhd8ed1ab_0
  - pyproj=3.3.1=py39h02a9fcc_0
  - pyqt=5.12.3=py39hf3d152e_8
  - pyqt-impl=5.12.3=py39hde8b62d_8
  - pyqt5-sip=4.19.18=py39he80948d_8
  - pyqtchart=5.12=py39h0fcd23e_8
  - pyqtwebengine=5.12.1=py39h0fcd23e_8
  - pysocks=1.7.1=pyha55dd90_7
  - pytest=8.3.5=pyhd8ed1ab_0
  - pytest-cov=6.0.0=pyhd8ed1ab_1
  - python=3.9.7=hb7a2778_3_cpython
  - python-dateutil=2.9.0.post0=pyhff2d567_1
  - python_abi=3.9=5_cp39
  - pytz=2025.2=pyhd8ed1ab_0
  - pyyaml=6.0=py39hb9d737c_4
  - qt=5.12.9=hda022c4_4
  - readline=8.2=h5eee18b_0
  - requests=2.32.3=pyhd8ed1ab_1
  - rtree=1.4.0=pyh11ca60a_1
  - setuptools=75.8.0=py39h06a4308_0
  - shapely=1.8.0=py39ha61afbd_0
  - six=1.17.0=pyhd8ed1ab_0
  - sqlalchemy=1.4.36=py39hb9d737c_0
  - sqlite=3.45.3=h5eee18b_0
  - tiledb=2.3.4=he87e0bf_0
  - tk=8.6.14=h39e8969_0
  - toml=0.10.2=pyhd8ed1ab_1
  - tomli=2.2.1=pyhd8ed1ab_1
  - tornado=6.1=py39hb9d737c_3
  - typing_extensions=4.13.0=pyh29332c3_1
  - tzcode=2022a=h166bdaf_0
  - tzdata=2025a=h04d1e81_0
  - ukkonen=1.0.1=py39hf939315_2
  - unicodedata2=14.0.0=py39hb9d737c_1
  - urllib3=2.3.0=pyhd8ed1ab_0
  - virtualenv=20.29.3=pyhd8ed1ab_0
  - wheel=0.45.1=py39h06a4308_0
  - xerces-c=3.2.3=h9d8b166_3
  - xorg-kbproto=1.0.7=h7f98852_1002
  - xorg-libice=1.0.10=h7f98852_0
  - xorg-libsm=1.2.3=hd9c2040_1000
  - xorg-libx11=1.7.2=h7f98852_0
  - xorg-libxau=1.0.9=h7f98852_0
  - xorg-libxdmcp=1.1.3=h7f98852_0
  - xorg-libxext=1.3.4=h7f98852_1
  - xorg-libxrender=0.9.10=h7f98852_1003
  - xorg-renderproto=0.11.1=h7f98852_1002
  - xorg-xextproto=7.3.0=h7f98852_1002
  - xorg-xproto=7.0.31=h7f98852_1007
  - xz=5.6.4=h5eee18b_1
  - yaml=0.2.5=h7f98852_2
  - zipp=3.21.0=pyhd8ed1ab_1
  - zlib=1.2.13=h5eee18b_1
  - zstandard=0.19.0=py39h5eee18b_0
  - zstd=1.5.0=ha95c52a_0
prefix: /opt/conda/envs/geopandas

geopandas/tests/test_array.py::test_points geopandas/tests/test_array.py::test_points_from_xy geopandas/tests/test_array.py::test_from_shapely geopandas/tests/test_array.py::test_from_shapely_geo_interface geopandas/tests/test_array.py::test_from_wkb geopandas/tests/test_array.py::test_to_wkb geopandas/tests/test_array.py::test_from_wkt[str] geopandas/tests/test_array.py::test_from_wkt[bytes] geopandas/tests/test_array.py::test_to_wkt geopandas/tests/test_array.py::test_predicates_vector_scalar[contains-args0] geopandas/tests/test_array.py::test_predicates_vector_scalar[covers-args1] geopandas/tests/test_array.py::test_predicates_vector_scalar[crosses-args2] geopandas/tests/test_array.py::test_predicates_vector_scalar[disjoint-args3] geopandas/tests/test_array.py::test_predicates_vector_scalar[geom_equals-args4] geopandas/tests/test_array.py::test_predicates_vector_scalar[intersects-args5] geopandas/tests/test_array.py::test_predicates_vector_scalar[overlaps-args6] geopandas/tests/test_array.py::test_predicates_vector_scalar[touches-args7] geopandas/tests/test_array.py::test_predicates_vector_scalar[within-args8] geopandas/tests/test_array.py::test_predicates_vector_scalar[geom_equals_exact-args9] geopandas/tests/test_array.py::test_predicates_vector_scalar[geom_almost_equals-args10] geopandas/tests/test_array.py::test_predicates_vector_vector[contains-args0] geopandas/tests/test_array.py::test_predicates_vector_vector[covers-args1] geopandas/tests/test_array.py::test_predicates_vector_vector[crosses-args2] geopandas/tests/test_array.py::test_predicates_vector_vector[disjoint-args3] geopandas/tests/test_array.py::test_predicates_vector_vector[geom_equals-args4] geopandas/tests/test_array.py::test_predicates_vector_vector[intersects-args5] geopandas/tests/test_array.py::test_predicates_vector_vector[overlaps-args6] geopandas/tests/test_array.py::test_predicates_vector_vector[touches-args7] geopandas/tests/test_array.py::test_predicates_vector_vector[within-args8] geopandas/tests/test_array.py::test_predicates_vector_vector[geom_equals_exact-args9] geopandas/tests/test_array.py::test_predicates_vector_vector[geom_almost_equals-args10] geopandas/tests/test_array.py::test_equals_deprecation[equals_exact-args0] geopandas/tests/test_array.py::test_equals_deprecation[almost_equals-args1] geopandas/tests/test_array.py::test_unary_geo[boundary] geopandas/tests/test_array.py::test_unary_geo[centroid] geopandas/tests/test_array.py::test_unary_geo[convex_hull] geopandas/tests/test_array.py::test_unary_geo[envelope] geopandas/tests/test_array.py::test_unary_geo[exterior] geopandas/tests/test_array.py::test_unary_geo_callable[representative_point] geopandas/tests/test_array.py::test_binary_geo_vector[difference] geopandas/tests/test_array.py::test_binary_geo_vector[symmetric_difference] geopandas/tests/test_array.py::test_binary_geo_vector[union] geopandas/tests/test_array.py::test_binary_geo_vector[intersection] geopandas/tests/test_array.py::test_binary_geo_scalar[difference] geopandas/tests/test_array.py::test_binary_geo_scalar[symmetric_difference] geopandas/tests/test_array.py::test_binary_geo_scalar[union] geopandas/tests/test_array.py::test_binary_geo_scalar[intersection] geopandas/tests/test_array.py::test_unary_predicates[is_closed] geopandas/tests/test_array.py::test_unary_predicates[is_valid] geopandas/tests/test_array.py::test_unary_predicates[is_empty] geopandas/tests/test_array.py::test_unary_predicates[is_simple] geopandas/tests/test_array.py::test_unary_predicates[has_z] geopandas/tests/test_array.py::test_unary_predicates[is_ring] geopandas/tests/test_array.py::test_unary_float[area] geopandas/tests/test_array.py::test_unary_float[length] geopandas/tests/test_array.py::test_geom_types geopandas/tests/test_array.py::test_geom_types_null_mixed geopandas/tests/test_array.py::test_binary_distance geopandas/tests/test_array.py::test_binary_relate geopandas/tests/test_array.py::test_binary_project[True] geopandas/tests/test_array.py::test_binary_project[False] geopandas/tests/test_array.py::test_buffer[16-1-1] geopandas/tests/test_array.py::test_buffer[16-1-3] geopandas/tests/test_array.py::test_buffer[16-3-1] geopandas/tests/test_array.py::test_buffer[16-3-3] geopandas/tests/test_array.py::test_buffer[25-1-1] geopandas/tests/test_array.py::test_buffer[25-1-3] geopandas/tests/test_array.py::test_buffer[25-3-1] geopandas/tests/test_array.py::test_buffer[25-3-3] geopandas/tests/test_array.py::test_simplify geopandas/tests/test_array.py::test_unary_union geopandas/tests/test_array.py::test_affinity_methods[affine_transform-arg0] geopandas/tests/test_array.py::test_affinity_methods[translate-arg1] geopandas/tests/test_array.py::test_affinity_methods[rotate-arg2] geopandas/tests/test_array.py::test_affinity_methods[scale-arg3] geopandas/tests/test_array.py::test_affinity_methods[skew-arg4] geopandas/tests/test_array.py::test_coords_x_y geopandas/tests/test_array.py::test_bounds geopandas/tests/test_array.py::test_total_bounds geopandas/tests/test_array.py::test_getitem geopandas/tests/test_array.py::test_equality_ops geopandas/tests/test_array.py::test_dir geopandas/tests/test_array.py::test_chaining geopandas/tests/test_array.py::test_pickle geopandas/tests/test_array.py::test_raise_on_bad_sizes geopandas/tests/test_array.py::test_buffer_single_multipolygon geopandas/tests/test_array.py::test_astype_multipolygon geopandas/tests/test_array.py::test_check_crs geopandas/tests/test_array.py::test_crs_mismatch_warn geopandas/tests/test_array.py::test_isna[None] geopandas/tests/test_array.py::test_isna[nan] geopandas/tests/test_array.py::test_isna_pdNA geopandas/tests/test_geom_methods.py::TestGeomMethods::test_intersection geopandas/tests/test_geom_methods.py::TestGeomMethods::test_union_series geopandas/tests/test_geom_methods.py::TestGeomMethods::test_union_polygon geopandas/tests/test_geom_methods.py::TestGeomMethods::test_symmetric_difference_series geopandas/tests/test_geom_methods.py::TestGeomMethods::test_symmetric_difference_poly geopandas/tests/test_geom_methods.py::TestGeomMethods::test_difference_series geopandas/tests/test_geom_methods.py::TestGeomMethods::test_difference_poly geopandas/tests/test_geom_methods.py::TestGeomMethods::test_geo_op_empty_result geopandas/tests/test_geom_methods.py::TestGeomMethods::test_boundary geopandas/tests/test_geom_methods.py::TestGeomMethods::test_area geopandas/tests/test_geom_methods.py::TestGeomMethods::test_bounds geopandas/tests/test_geom_methods.py::TestGeomMethods::test_bounds_empty geopandas/tests/test_geom_methods.py::TestGeomMethods::test_unary_union geopandas/tests/test_geom_methods.py::TestGeomMethods::test_contains geopandas/tests/test_geom_methods.py::TestGeomMethods::test_length geopandas/tests/test_geom_methods.py::TestGeomMethods::test_crosses geopandas/tests/test_geom_methods.py::TestGeomMethods::test_disjoint geopandas/tests/test_geom_methods.py::TestGeomMethods::test_relate geopandas/tests/test_geom_methods.py::TestGeomMethods::test_distance geopandas/tests/test_geom_methods.py::TestGeomMethods::test_intersects geopandas/tests/test_geom_methods.py::TestGeomMethods::test_overlaps geopandas/tests/test_geom_methods.py::TestGeomMethods::test_touches geopandas/tests/test_geom_methods.py::TestGeomMethods::test_within geopandas/tests/test_geom_methods.py::TestGeomMethods::test_is_valid geopandas/tests/test_geom_methods.py::TestGeomMethods::test_is_empty geopandas/tests/test_geom_methods.py::TestGeomMethods::test_is_ring geopandas/tests/test_geom_methods.py::TestGeomMethods::test_is_simple geopandas/tests/test_geom_methods.py::TestGeomMethods::test_has_z geopandas/tests/test_geom_methods.py::TestGeomMethods::test_xy_points geopandas/tests/test_geom_methods.py::TestGeomMethods::test_xy_polygons geopandas/tests/test_geom_methods.py::TestGeomMethods::test_centroid geopandas/tests/test_geom_methods.py::TestGeomMethods::test_convex_hull geopandas/tests/test_geom_methods.py::TestGeomMethods::test_exterior geopandas/tests/test_geom_methods.py::TestGeomMethods::test_interiors geopandas/tests/test_geom_methods.py::TestGeomMethods::test_interpolate geopandas/tests/test_geom_methods.py::TestGeomMethods::test_interpolate_distance_array geopandas/tests/test_geom_methods.py::TestGeomMethods::test_interpolate_distance_wrong_length geopandas/tests/test_geom_methods.py::TestGeomMethods::test_interpolate_distance_wrong_index geopandas/tests/test_geom_methods.py::TestGeomMethods::test_project geopandas/tests/test_geom_methods.py::TestGeomMethods::test_affine_transform geopandas/tests/test_geom_methods.py::TestGeomMethods::test_translate_tuple geopandas/tests/test_geom_methods.py::TestGeomMethods::test_rotate geopandas/tests/test_geom_methods.py::TestGeomMethods::test_scale geopandas/tests/test_geom_methods.py::TestGeomMethods::test_skew geopandas/tests/test_geom_methods.py::TestGeomMethods::test_buffer geopandas/tests/test_geom_methods.py::TestGeomMethods::test_buffer_args geopandas/tests/test_geom_methods.py::TestGeomMethods::test_buffer_distance_array geopandas/tests/test_geom_methods.py::TestGeomMethods::test_buffer_distance_wrong_length geopandas/tests/test_geom_methods.py::TestGeomMethods::test_buffer_distance_wrong_index geopandas/tests/test_geom_methods.py::TestGeomMethods::test_buffer_empty_none geopandas/tests/test_geom_methods.py::TestGeomMethods::test_envelope geopandas/tests/test_geom_methods.py::TestGeomMethods::test_total_bounds geopandas/tests/test_geom_methods.py::TestGeomMethods::test_explode_geoseries geopandas/tests/test_geom_methods.py::TestGeomMethods::test_explode_geodataframe[None] geopandas/tests/test_geom_methods.py::TestGeomMethods::test_explode_geodataframe[test] geopandas/tests/test_geom_methods.py::TestGeomMethods::test_explode_geodataframe_level_1[None] geopandas/tests/test_geom_methods.py::TestGeomMethods::test_explode_geodataframe_level_1[test] geopandas/tests/test_geom_methods.py::TestGeomMethods::test_intersection_operator geopandas/tests/test_geom_methods.py::TestGeomMethods::test_union_operator geopandas/tests/test_geom_methods.py::TestGeomMethods::test_union_operator_polygon geopandas/tests/test_geom_methods.py::TestGeomMethods::test_symmetric_difference_operator geopandas/tests/test_geom_methods.py::TestGeomMethods::test_difference_series2 geopandas/tests/test_geom_methods.py::TestGeomMethods::test_difference_poly2 geopandas/tests/test_overlay.py::test_overlay[default-index-union] geopandas/tests/test_overlay.py::test_overlay[default-index-intersection] geopandas/tests/test_overlay.py::test_overlay[default-index-difference] geopandas/tests/test_overlay.py::test_overlay[default-index-symmetric_difference] geopandas/tests/test_overlay.py::test_overlay[default-index-identity] geopandas/tests/test_overlay.py::test_overlay[int-index-union] geopandas/tests/test_overlay.py::test_overlay[int-index-intersection] geopandas/tests/test_overlay.py::test_overlay[int-index-difference] geopandas/tests/test_overlay.py::test_overlay[int-index-symmetric_difference] geopandas/tests/test_overlay.py::test_overlay[int-index-identity] geopandas/tests/test_overlay.py::test_overlay[string-index-union] geopandas/tests/test_overlay.py::test_overlay[string-index-intersection] geopandas/tests/test_overlay.py::test_overlay[string-index-difference] geopandas/tests/test_overlay.py::test_overlay[string-index-symmetric_difference] geopandas/tests/test_overlay.py::test_overlay[string-index-identity] geopandas/tests/test_overlay.py::test_overlay_nybb[difference] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[union-False] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[union-True] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[intersection-False] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[intersection-True] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[difference-False] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[difference-True] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[symmetric_difference-False] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[symmetric_difference-True] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[identity-False] geopandas/tests/test_overlay.py::test_geometry_not_named_geometry[identity-True] geopandas/tests/test_overlay.py::test_bad_how geopandas/tests/test_overlay.py::test_duplicate_column_name geopandas/tests/test_overlay.py::test_geoseries_warning geopandas/tests/test_overlay.py::test_preserve_crs[union] geopandas/tests/test_overlay.py::test_preserve_crs[intersection] geopandas/tests/test_overlay.py::test_preserve_crs[difference] geopandas/tests/test_overlay.py::test_preserve_crs[symmetric_difference] geopandas/tests/test_overlay.py::test_preserve_crs[identity] geopandas/tests/test_overlay.py::test_crs_mismatch[union] geopandas/tests/test_overlay.py::test_crs_mismatch[intersection] geopandas/tests/test_overlay.py::test_crs_mismatch[difference] geopandas/tests/test_overlay.py::test_crs_mismatch[symmetric_difference] geopandas/tests/test_overlay.py::test_crs_mismatch[identity] geopandas/tests/test_overlay.py::test_empty_intersection geopandas/tests/test_overlay.py::test_correct_index geopandas/tests/test_overlay.py::test_overlay_strict[union-True-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[union-True-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-True-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-True-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-True-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-True-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-False-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-False-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-False-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-False-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[difference-True-polys] geopandas/tests/test_overlay.py::test_overlay_strict[difference-True-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[difference-True-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[difference-False-polys] geopandas/tests/test_overlay.py::test_overlay_strict[difference-False-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[difference-False-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-True-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-True-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[identity-True-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[identity-True-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[identity-False-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[identity-False-point_poly] geopandas/tests/test_overlay.py::test_mixed_geom_error geopandas/tests/test_overlay.py::test_keep_geom_type_error geopandas/tools/tests/test_clip.py::test_not_gdf geopandas/tools/tests/test_clip.py::test_returns_gdf geopandas/tools/tests/test_clip.py::test_returns_series geopandas/tools/tests/test_clip.py::test_non_overlapping_geoms geopandas/tools/tests/test_clip.py::test_clip_points geopandas/tools/tests/test_clip.py::test_clip_poly geopandas/tools/tests/test_clip.py::test_clip_poly_series geopandas/tools/tests/test_clip.py::test_clip_multipoly_keep_slivers geopandas/tools/tests/test_clip.py::test_clip_multipoly_keep_geom_type geopandas/tools/tests/test_clip.py::test_clip_single_multipoly_no_extra_geoms geopandas/tools/tests/test_clip.py::test_clip_multiline geopandas/tools/tests/test_clip.py::test_clip_multipoint geopandas/tools/tests/test_clip.py::test_clip_lines geopandas/tools/tests/test_clip.py::test_clip_with_multipolygon geopandas/tools/tests/test_clip.py::test_mixed_geom geopandas/tools/tests/test_clip.py::test_mixed_series geopandas/tools/tests/test_clip.py::test_clip_warning_no_extra_geoms geopandas/tools/tests/test_clip.py::test_clip_with_polygon geopandas/tools/tests/test_clip.py::test_clip_with_line_extra_geom geopandas/tools/tests/test_clip.py::test_clip_line_keep_slivers geopandas/tools/tests/test_clip.py::test_clip_no_box_overlap geopandas/tools/tests/test_clip.py::test_clip_box_overlap geopandas/tools/tests/test_clip.py::test_warning_extra_geoms_mixed geopandas/tools/tests/test_clip.py::test_warning_geomcoll geopandas/tools/tests/test_clip.py::test_warning_crs_mismatch geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_crs_mismatch[default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_crs_mismatch[string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[intersects-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[intersects-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[intersects-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[intersects-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[intersects-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[contains-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[contains-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[contains-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[contains-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[contains-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[within-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[within-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[within-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[within-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_inner[within-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[intersects-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[intersects-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[intersects-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[intersects-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[intersects-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[contains-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[contains-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[contains-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[contains-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[contains-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[within-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[within-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[within-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[within-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_left[within-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_empty_join geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_join_with_empty[empty0-intersects] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_join_with_empty[empty0-contains] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_join_with_empty[empty0-within] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_join_with_empty[empty1-intersects] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_join_with_empty[empty1-contains] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_join_with_empty[empty1-within] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_sjoin_invalid_args[default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_sjoin_invalid_args[string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[intersects-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[intersects-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[intersects-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[intersects-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[intersects-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[contains-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[contains-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[contains-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[contains-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[contains-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[within-default-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[within-string-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[within-named-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[within-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoin::test_right[within-named-multi-index] geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_geometry_name geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_left geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_right geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_inner geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_op geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_bad_op geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_duplicate_column_name geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_named_index[left] geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_named_index[right] geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_named_index[inner] geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_values geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNYBB::test_sjoin_empty_geometries geopandas/tools/tests/test_sjoin.py::TestSpatialJoinNaturalEarth::test_sjoin_inner
geopandas/tests/test_overlay.py::test_overlay_nybb[union] geopandas/tests/test_overlay.py::test_overlay_nybb[intersection] geopandas/tests/test_overlay.py::test_overlay_nybb[symmetric_difference] geopandas/tests/test_overlay.py::test_overlay_nybb[identity] geopandas/tests/test_overlay.py::test_overlay_overlap[union] geopandas/tests/test_overlay.py::test_overlay_overlap[intersection] geopandas/tests/test_overlay.py::test_overlay_overlap[difference] geopandas/tests/test_overlay.py::test_overlay_overlap[symmetric_difference] geopandas/tests/test_overlay.py::test_overlay_strict[union-True-polys] geopandas/tests/test_overlay.py::test_overlay_strict[union-True-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[union-True-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[union-False-polys] geopandas/tests/test_overlay.py::test_overlay_strict[union-False-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[union-False-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[union-False-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[union-False-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-True-polys] geopandas/tests/test_overlay.py::test_overlay_strict[intersection-False-polys] geopandas/tests/test_overlay.py::test_overlay_strict[difference-True-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[difference-True-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[difference-False-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[difference-False-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-True-polys] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-True-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-True-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-False-polys] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-False-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-False-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-False-line_poly] geopandas/tests/test_overlay.py::test_overlay_strict[symmetric_difference-False-point_poly] geopandas/tests/test_overlay.py::test_overlay_strict[identity-True-polys] geopandas/tests/test_overlay.py::test_overlay_strict[identity-True-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[identity-True-poly_point] geopandas/tests/test_overlay.py::test_overlay_strict[identity-False-polys] geopandas/tests/test_overlay.py::test_overlay_strict[identity-False-poly_line] geopandas/tests/test_overlay.py::test_overlay_strict[identity-False-poly_point]
geopandas/tools/sjoin.py geopandas/tools/clip.py geopandas/io/parquet.py geopandas/array.py benchmarks/io.py benchmarks/parquet.py geopandas/base.py geopandas/tools/_show_versions.py doc/source/reference.rst geopandas/io/sql.py geopandas/tools/overlay.py geopandas/__init__.py geopandas/geodataframe.py
geopandas/tools/sjoin.py geopandas/tools/clip.py geopandas/array.py benchmarks/io.py benchmarks/parquet.py geopandas/base.py geopandas/tools/_show_versions.py doc/source/reference.rst geopandas/io/sql.py geopandas/tools/overlay.py geopandas/__init__.py geopandas/io/arrow.py geopandas/geodataframe.py