import random

from geopandas import GeoDataFrame, GeoSeries, sjoin
from shapely.geometry import Point, LineString, Polygon
import numpy as np


class Bench:

    param_names = ['op']
    params = [('intersects', 'contains', 'within')]

    def setup(self, *args):
        triangles = GeoSeries(
            [Polygon([(random.random(), random.random()) for _ in range(3)])
             for _ in range(1000)])

        points = GeoSeries(
            [Point(x, y) for x, y in zip(np.random.random(10000),
                                         np.random.random(10000))])

        df1 = GeoDataFrame({'val1': np.random.randn(len(triangles)),
                            'geometry': triangles})
        df2 = GeoDataFrame({'val1': np.random.randn(len(points)),
                            'geometry': points})

        self.df1, self.df2 = df1, df2

    def time_sjoin(self, op):
        sjoin(self.df1, self.df2, op=op)

benchmarks/sjoin.py
import random

from geopandas import GeoDataFrame, GeoSeries, sjoin
from shapely.geometry import Point, LineString, Polygon
import numpy as np


class Bench:

    param_names = ['op']
    params = [('intersects', 'contains', 'within')]

    def setup(self, *args):
        triangles = GeoSeries(
            [Polygon([(random.random(), random.random()) for _ in range(3)])
             for _ in range(1000)])

        points = GeoSeries(
            [Point(x, y) for x, y in zip(np.random.random(10000),
                                         np.random.random(10000))])

        df1 = GeoDataFrame({'val1': np.random.randn(len(triangles)),
                            'geometry': triangles})
        df2 = GeoDataFrame({'val1': np.random.randn(len(points)),
                            'geometry': points})

        self.df1, self.df2 = df1, df2

    def time_sjoin(self, predicate):
        sjoin(self.df1, self.df2, predicate=predicate)

benchmarks/sjoin.py
diff --git a/benchmarks/sjoin.py b/benchmarks/sjoin.py
index 0d06391169..81dbacf1a3 100644
--- a/benchmarks/sjoin.py
+++ b/benchmarks/sjoin.py
@@ -26,5 +26,5 @@ def setup(self, *args):
 
         self.df1, self.df2 = df1, df2
 
-    def time_sjoin(self, op):
-        sjoin(self.df1, self.df2, op=op)
+    def time_sjoin(self, predicate):
+        sjoin(self.df1, self.df2, predicate=predicate)
diff --git a/doc/source/gallery/spatial_joins.ipynb b/doc/source/gallery/spatial_joins.ipynb
index a295c41f4d..38f06e3194 100644
--- a/doc/source/gallery/spatial_joins.ipynb
+++ b/doc/source/gallery/spatial_joins.ipynb
@@ -2,6 +2,7 @@
  "cells": [
   {
    "cell_type": "markdown",
+   "metadata": {},
    "source": [
     "# Spatial Joins\n",
     "\n",
@@ -12,11 +13,11 @@
     "A common use case might be a spatial join between a point layer and a polygon layer where you want to retain the point geometries and grab the attributes of the intersecting polygons.\n",
     "\n",
     "![illustration](https://web.natur.cuni.cz/~langhamr/lectures/vtfg1/mapinfo_1/about_gis/Image23.gif)"
-   ],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "markdown",
+   "metadata": {},
    "source": [
     "\n",
     "## Types of spatial joins\n",
@@ -84,21 +85,22 @@
     " 0101000000F0D88AA0E1A4EEBF7052F7E5B115E9BF |    2 |     20\n",
     "(4 rows) \n",
     "```"
-   ],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "markdown",
+   "metadata": {},
    "source": [
     "## Spatial Joins between two GeoDataFrames\n",
     "\n",
     "Let's take a look at how we'd implement these using `GeoPandas`. First, load up the NYC test data into `GeoDataFrames`:"
-   ],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "%matplotlib inline\n",
     "from shapely.geometry import Point\n",
@@ -118,101 +120,99 @@
     "\n",
     "# Make sure they're using the same projection reference\n",
     "pointdf.crs = polydf.crs"
-   ],
-   "outputs": [],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "pointdf"
-   ],
-   "outputs": [],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "polydf"
-   ],
-   "outputs": [],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "pointdf.plot()"
-   ],
-   "outputs": [],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "polydf.plot()"
-   ],
-   "outputs": [],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "markdown",
+   "metadata": {},
    "source": [
     "## Joins"
-   ],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "join_left_df = pointdf.sjoin(polydf, how=\"left\")\n",
     "join_left_df\n",
     "# Note the NaNs where the point did not intersect a boro"
-   ],
-   "outputs": [],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "join_right_df = pointdf.sjoin(polydf, how=\"right\")\n",
     "join_right_df\n",
     "# Note Staten Island is repeated"
-   ],
-   "outputs": [],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
+   "metadata": {},
+   "outputs": [],
    "source": [
     "join_inner_df = pointdf.sjoin(polydf, how=\"inner\")\n",
     "join_inner_df\n",
     "# Note the lack of NaNs; dropped anything that didn't intersect"
-   ],
-   "outputs": [],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "markdown",
+   "metadata": {},
    "source": [
     "We're not limited to using the `intersection` binary predicate. Any of the `Shapely` geometry methods that return a Boolean can be used by specifying the `op` kwarg."
-   ],
-   "metadata": {}
+   ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
-   "source": [
-    "pointdf.sjoin(polydf, how=\"left\", op=\"within\")"
-   ],
+   "metadata": {},
    "outputs": [],
-   "metadata": {}
+   "source": [
+    "pointdf.sjoin(polydf, how=\"left\", predicate=\"within\")"
+   ]
   }
  ],
  "metadata": {
@@ -236,4 +236,4 @@
  },
  "nbformat": 4,
  "nbformat_minor": 4
-}
\ No newline at end of file
+}

DOC: Address GeoPandas op deprecation in docs
While working on #2211 I noticed instances of the `op` parameter still being used.

This `op` parameter was deprecated in pull request #1626 in favour of `predicate`.

Locations where op is still present includes:
* [sjoin benchmark](https://github.com/geopandas/geopandas/blob/master/benchmarks/sjoin.py)
* [Spatial Joins notebook](https://github.com/geopandas/geopandas/blob/master/doc/source/gallery/spatial_joins.ipynb)
 
I can address the notebook instance but I don't know what the benchmark instance of `op` does so wouldn't want to change it without a thumbs up from a maintainer.
