We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
IndexError when conducting spatial join with empty polygons
Hey, not sure if this addressed elsewhere, but a search of past results suggests it's not. 

```python
import geopandas, numpy, shapely.geometry as geom

center = geom.Point((0,0))
circle = center.buffer(1)
empty = center.buffer(0)

empty_df = geopandas.GeoDataFrame(geometry=[circle, empty])

points = [geom.Point(x) for x in numpy.random.random(size=(10,2))]
points_df = geopandas.GeoDataFrame(geometry=points)

geopandas.sjoin(points_df, empty_df, op='intersects') # raises an IndexError but completes
```
I get a result from this join, but I also get an `IndexError`:

```
---------------------------------------------------------------------------
IndexError                                Traceback (most recent call last)
_ctypes/callbacks.c in 'calling callback function'()

~/anaconda3/envs/ana/lib/python3.7/site-packages/rtree/index.py in py_next_item(p_id, p_mins, p_maxs, p_dimension, p_data, p_length)
    708             # xmin, xmax, ymin, ymax, zmin, zmax
    709             for i in range(dimension):
--> 710                 mins[i] = coordinates[i*2]
    711                 maxs[i] = coordinates[(i*2)+1]
    712 

IndexError: list index out of range
```
Not sure how this `IndexError` relates to the result, but I didn't see another issue mentioning this, so I thought I'd raise it. 

rtree 0.8.3
geopandas 0.4.1
shapely 1.6.4.post1


--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `geopandas/tools/sjoin.py`
Content:
```
1 from warnings import warn
2 
3 import numpy as np
4 import pandas as pd
5 
6 from shapely import prepared
7 
8 from geopandas import GeoDataFrame
9 
10 
11 def sjoin(
12     left_df, right_df, how="inner", op="intersects", lsuffix="left", rsuffix="right"
13 ):
14     """Spatial join of two GeoDataFrames.
15 
16     Parameters
17     ----------
18     left_df, right_df : GeoDataFrames
19     how : string, default 'inner'
20         The type of join:
21 
22         * 'left': use keys from left_df; retain only left_df geometry column
23         * 'right': use keys from right_df; retain only right_df geometry column
24         * 'inner': use intersection of keys from both dfs; retain only
25           left_df geometry column
26     op : string, default 'intersection'
27         Binary predicate, one of {'intersects', 'contains', 'within'}.
28         See http://shapely.readthedocs.io/en/latest/manual.html#binary-predicates.
29     lsuffix : string, default 'left'
30         Suffix to apply to overlapping column names (left GeoDataFrame).
31     rsuffix : string, default 'right'
32         Suffix to apply to overlapping column names (right GeoDataFrame).
33 
34     """
35     if not isinstance(left_df, GeoDataFrame):
36         raise ValueError(
37             "'left_df' should be GeoDataFrame, got {}".format(type(left_df))
38         )
39 
40     if not isinstance(right_df, GeoDataFrame):
41         raise ValueError(
42             "'right_df' should be GeoDataFrame, got {}".format(type(right_df))
43         )
44 
45     allowed_hows = ["left", "right", "inner"]
46     if how not in allowed_hows:
47         raise ValueError(
48             '`how` was "%s" but is expected to be in %s' % (how, allowed_hows)
49         )
50 
51     allowed_ops = ["contains", "within", "intersects"]
52     if op not in allowed_ops:
53         raise ValueError(
54             '`op` was "%s" but is expected to be in %s' % (op, allowed_ops)
55         )
56 
57     if left_df.crs != right_df.crs:
58         warn(
59             (
60                 "CRS of frames being joined does not match!"
61                 "(%s != %s)" % (left_df.crs, right_df.crs)
62             )
63         )
64 
65     index_left = "index_%s" % lsuffix
66     index_right = "index_%s" % rsuffix
67 
68     # due to GH 352
69     if any(left_df.columns.isin([index_left, index_right])) or any(
70         right_df.columns.isin([index_left, index_right])
71     ):
72         raise ValueError(
73             "'{0}' and '{1}' cannot be names in the frames being"
74             " joined".format(index_left, index_right)
75         )
76 
77     # Attempt to re-use spatial indexes, otherwise generate the spatial index
78     # for the longer dataframe
79     if right_df._sindex_generated or (
80         not left_df._sindex_generated and right_df.shape[0] > left_df.shape[0]
81     ):
82         tree_idx = right_df.sindex
83         tree_idx_right = True
84     else:
85         tree_idx = left_df.sindex
86         tree_idx_right = False
87 
88     # the rtree spatial index only allows limited (numeric) index types, but an
89     # index in geopandas may be any arbitrary dtype. so reset both indices now
90     # and store references to the original indices, to be reaffixed later.
91     # GH 352
92     left_df = left_df.copy(deep=True)
93     left_df.index = left_df.index.rename(index_left)
94     left_df = left_df.reset_index()
95     right_df = right_df.copy(deep=True)
96     right_df.index = right_df.index.rename(index_right)
97     right_df = right_df.reset_index()
98 
99     if op == "within":
100         # within implemented as the inverse of contains; swap names
101         left_df, right_df = right_df, left_df
102         tree_idx_right = not tree_idx_right
103 
104     r_idx = np.empty((0, 0))
105     l_idx = np.empty((0, 0))
106     # get rtree spatial index
107     if tree_idx_right:
108         idxmatch = left_df.geometry.apply(lambda x: x.bounds).apply(
109             lambda x: list(tree_idx.intersection(x))
110         )
111         idxmatch = idxmatch[idxmatch.apply(len) > 0]
112         # indexes of overlapping boundaries
113         if idxmatch.shape[0] > 0:
114             r_idx = np.concatenate(idxmatch.values)
115             l_idx = np.concatenate([[i] * len(v) for i, v in idxmatch.iteritems()])
116     else:
117         # tree_idx_df == 'left'
118         idxmatch = right_df.geometry.apply(lambda x: x.bounds).apply(
119             lambda x: list(tree_idx.intersection(x))
120         )
121         idxmatch = idxmatch[idxmatch.apply(len) > 0]
122         if idxmatch.shape[0] > 0:
123             # indexes of overlapping boundaries
124             l_idx = np.concatenate(idxmatch.values)
125             r_idx = np.concatenate([[i] * len(v) for i, v in idxmatch.iteritems()])
126 
127     if len(r_idx) > 0 and len(l_idx) > 0:
128         # Vectorize predicate operations
129         def find_intersects(a1, a2):
130             return a1.intersects(a2)
131 
132         def find_contains(a1, a2):
133             return a1.contains(a2)
134 
135         predicate_d = {
136             "intersects": find_intersects,
137             "contains": find_contains,
138             "within": find_contains,
139         }
140 
141         check_predicates = np.vectorize(predicate_d[op])
142 
143         result = pd.DataFrame(
144             np.column_stack(
145                 [
146                     l_idx,
147                     r_idx,
148                     check_predicates(
149                         left_df.geometry.apply(lambda x: prepared.prep(x))[l_idx],
150                         right_df[right_df.geometry.name][r_idx],
151                     ),
152                 ]
153             )
154         )
155 
156         result.columns = ["_key_left", "_key_right", "match_bool"]
157         result = pd.DataFrame(result[result["match_bool"] == 1]).drop(
158             "match_bool", axis=1
159         )
160 
161     else:
162         # when output from the join has no overlapping geometries
163         result = pd.DataFrame(columns=["_key_left", "_key_right"], dtype=float)
164 
165     if op == "within":
166         # within implemented as the inverse of contains; swap names
167         left_df, right_df = right_df, left_df
168         result = result.rename(
169             columns={"_key_left": "_key_right", "_key_right": "_key_left"}
170         )
171 
172     if how == "inner":
173         result = result.set_index("_key_left")
174         joined = left_df.merge(result, left_index=True, right_index=True).merge(
175             right_df.drop(right_df.geometry.name, axis=1),
176             left_on="_key_right",
177             right_index=True,
178             suffixes=("_%s" % lsuffix, "_%s" % rsuffix),
179         )
180         joined = joined.set_index(index_left).drop(["_key_right"], axis=1)
181         joined.index.name = None
182     elif how == "left":
183         result = result.set_index("_key_left")
184         joined = left_df.merge(
185             result, left_index=True, right_index=True, how="left"
186         ).merge(
187             right_df.drop(right_df.geometry.name, axis=1),
188             how="left",
189             left_on="_key_right",
190             right_index=True,
191             suffixes=("_%s" % lsuffix, "_%s" % rsuffix),
192         )
193         joined = joined.set_index(index_left).drop(["_key_right"], axis=1)
194         joined.index.name = None
195     else:  # how == 'right':
196         joined = (
197             left_df.drop(left_df.geometry.name, axis=1)
198             .merge(
199                 result.merge(
200                     right_df, left_on="_key_right", right_index=True, how="right"
201                 ),
202                 left_index=True,
203                 right_on="_key_left",
204                 how="right",
205             )
206             .set_index(index_right)
207         )
208         joined = joined.drop(["_key_left", "_key_right"], axis=1)
209 
210     return joined
211
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/geopandas/tools/sjoin.py b/geopandas/tools/sjoin.py
--- a/geopandas/tools/sjoin.py
+++ b/geopandas/tools/sjoin.py
@@ -106,7 +106,7 @@
     # get rtree spatial index
     if tree_idx_right:
         idxmatch = left_df.geometry.apply(lambda x: x.bounds).apply(
-            lambda x: list(tree_idx.intersection(x))
+            lambda x: list(tree_idx.intersection(x)) if not x == () else []
         )
         idxmatch = idxmatch[idxmatch.apply(len) > 0]
         # indexes of overlapping boundaries
@@ -116,7 +116,7 @@
     else:
         # tree_idx_df == 'left'
         idxmatch = right_df.geometry.apply(lambda x: x.bounds).apply(
-            lambda x: list(tree_idx.intersection(x))
+            lambda x: list(tree_idx.intersection(x)) if not x == () else []
         )
         idxmatch = idxmatch[idxmatch.apply(len) > 0]
         if idxmatch.shape[0] > 0:

{"golden_diff": "diff --git a/geopandas/tools/sjoin.py b/geopandas/tools/sjoin.py\n--- a/geopandas/tools/sjoin.py\n+++ b/geopandas/tools/sjoin.py\n@@ -106,7 +106,7 @@\n     # get rtree spatial index\n     if tree_idx_right:\n         idxmatch = left_df.geometry.apply(lambda x: x.bounds).apply(\n-            lambda x: list(tree_idx.intersection(x))\n+            lambda x: list(tree_idx.intersection(x)) if not x == () else []\n         )\n         idxmatch = idxmatch[idxmatch.apply(len) > 0]\n         # indexes of overlapping boundaries\n@@ -116,7 +116,7 @@\n     else:\n         # tree_idx_df == 'left'\n         idxmatch = right_df.geometry.apply(lambda x: x.bounds).apply(\n-            lambda x: list(tree_idx.intersection(x))\n+            lambda x: list(tree_idx.intersection(x)) if not x == () else []\n         )\n         idxmatch = idxmatch[idxmatch.apply(len) > 0]\n         if idxmatch.shape[0] > 0:\n", "issue": "IndexError when conducting spatial join with empty polygons\nHey, not sure if this addressed elsewhere, but a search of past results suggests it's not. \r\n\r\n```python\r\nimport geopandas, numpy, shapely.geometry as geom\r\n\r\ncenter = geom.Point((0,0))\r\ncircle = center.buffer(1)\r\nempty = center.buffer(0)\r\n\r\nempty_df = geopandas.GeoDataFrame(geometry=[circle, empty])\r\n\r\npoints = [geom.Point(x) for x in numpy.random.random(size=(10,2))]\r\npoints_df = geopandas.GeoDataFrame(geometry=points)\r\n\r\ngeopandas.sjoin(points_df, empty_df, op='intersects') # raises an IndexError but completes\r\n```\r\nI get a result from this join, but I also get an `IndexError`:\r\n\r\n```\r\n---------------------------------------------------------------------------\r\nIndexError                                Traceback (most recent call last)\r\n_ctypes/callbacks.c in 'calling callback function'()\r\n\r\n~/anaconda3/envs/ana/lib/python3.7/site-packages/rtree/index.py in py_next_item(p_id, p_mins, p_maxs, p_dimension, p_data, p_length)\r\n    708             # xmin, xmax, ymin, ymax, zmin, zmax\r\n    709             for i in range(dimension):\r\n--> 710                 mins[i] = coordinates[i*2]\r\n    711                 maxs[i] = coordinates[(i*2)+1]\r\n    712 \r\n\r\nIndexError: list index out of range\r\n```\r\nNot sure how this `IndexError` relates to the result, but I didn't see another issue mentioning this, so I thought I'd raise it. \r\n\r\nrtree 0.8.3\r\ngeopandas 0.4.1\r\nshapely 1.6.4.post1\r\n\n", "before_files": [{"content": "from warnings import warn\n\nimport numpy as np\nimport pandas as pd\n\nfrom shapely import prepared\n\nfrom geopandas import GeoDataFrame\n\n\ndef sjoin(\n    left_df, right_df, how=\"inner\", op=\"intersects\", lsuffix=\"left\", rsuffix=\"right\"\n):\n    \"\"\"Spatial join of two GeoDataFrames.\n\n    Parameters\n    ----------\n    left_df, right_df : GeoDataFrames\n    how : string, default 'inner'\n        The type of join:\n\n        * 'left': use keys from left_df; retain only left_df geometry column\n        * 'right': use keys from right_df; retain only right_df geometry column\n        * 'inner': use intersection of keys from both dfs; retain only\n          left_df geometry column\n    op : string, default 'intersection'\n        Binary predicate, one of {'intersects', 'contains', 'within'}.\n        See http://shapely.readthedocs.io/en/latest/manual.html#binary-predicates.\n    lsuffix : string, default 'left'\n        Suffix to apply to overlapping column names (left GeoDataFrame).\n    rsuffix : string, default 'right'\n        Suffix to apply to overlapping column names (right GeoDataFrame).\n\n    \"\"\"\n    if not isinstance(left_df, GeoDataFrame):\n        raise ValueError(\n            \"'left_df' should be GeoDataFrame, got {}\".format(type(left_df))\n        )\n\n    if not isinstance(right_df, GeoDataFrame):\n        raise ValueError(\n            \"'right_df' should be GeoDataFrame, got {}\".format(type(right_df))\n        )\n\n    allowed_hows = [\"left\", \"right\", \"inner\"]\n    if how not in allowed_hows:\n        raise ValueError(\n            '`how` was \"%s\" but is expected to be in %s' % (how, allowed_hows)\n        )\n\n    allowed_ops = [\"contains\", \"within\", \"intersects\"]\n    if op not in allowed_ops:\n        raise ValueError(\n            '`op` was \"%s\" but is expected to be in %s' % (op, allowed_ops)\n        )\n\n    if left_df.crs != right_df.crs:\n        warn(\n            (\n                \"CRS of frames being joined does not match!\"\n                \"(%s != %s)\" % (left_df.crs, right_df.crs)\n            )\n        )\n\n    index_left = \"index_%s\" % lsuffix\n    index_right = \"index_%s\" % rsuffix\n\n    # due to GH 352\n    if any(left_df.columns.isin([index_left, index_right])) or any(\n        right_df.columns.isin([index_left, index_right])\n    ):\n        raise ValueError(\n            \"'{0}' and '{1}' cannot be names in the frames being\"\n            \" joined\".format(index_left, index_right)\n        )\n\n    # Attempt to re-use spatial indexes, otherwise generate the spatial index\n    # for the longer dataframe\n    if right_df._sindex_generated or (\n        not left_df._sindex_generated and right_df.shape[0] > left_df.shape[0]\n    ):\n        tree_idx = right_df.sindex\n        tree_idx_right = True\n    else:\n        tree_idx = left_df.sindex\n        tree_idx_right = False\n\n    # the rtree spatial index only allows limited (numeric) index types, but an\n    # index in geopandas may be any arbitrary dtype. so reset both indices now\n    # and store references to the original indices, to be reaffixed later.\n    # GH 352\n    left_df = left_df.copy(deep=True)\n    left_df.index = left_df.index.rename(index_left)\n    left_df = left_df.reset_index()\n    right_df = right_df.copy(deep=True)\n    right_df.index = right_df.index.rename(index_right)\n    right_df = right_df.reset_index()\n\n    if op == \"within\":\n        # within implemented as the inverse of contains; swap names\n        left_df, right_df = right_df, left_df\n        tree_idx_right = not tree_idx_right\n\n    r_idx = np.empty((0, 0))\n    l_idx = np.empty((0, 0))\n    # get rtree spatial index\n    if tree_idx_right:\n        idxmatch = left_df.geometry.apply(lambda x: x.bounds).apply(\n            lambda x: list(tree_idx.intersection(x))\n        )\n        idxmatch = idxmatch[idxmatch.apply(len) > 0]\n        # indexes of overlapping boundaries\n        if idxmatch.shape[0] > 0:\n            r_idx = np.concatenate(idxmatch.values)\n            l_idx = np.concatenate([[i] * len(v) for i, v in idxmatch.iteritems()])\n    else:\n        # tree_idx_df == 'left'\n        idxmatch = right_df.geometry.apply(lambda x: x.bounds).apply(\n            lambda x: list(tree_idx.intersection(x))\n        )\n        idxmatch = idxmatch[idxmatch.apply(len) > 0]\n        if idxmatch.shape[0] > 0:\n            # indexes of overlapping boundaries\n            l_idx = np.concatenate(idxmatch.values)\n            r_idx = np.concatenate([[i] * len(v) for i, v in idxmatch.iteritems()])\n\n    if len(r_idx) > 0 and len(l_idx) > 0:\n        # Vectorize predicate operations\n        def find_intersects(a1, a2):\n            return a1.intersects(a2)\n\n        def find_contains(a1, a2):\n            return a1.contains(a2)\n\n        predicate_d = {\n            \"intersects\": find_intersects,\n            \"contains\": find_contains,\n            \"within\": find_contains,\n        }\n\n        check_predicates = np.vectorize(predicate_d[op])\n\n        result = pd.DataFrame(\n            np.column_stack(\n                [\n                    l_idx,\n                    r_idx,\n                    check_predicates(\n                        left_df.geometry.apply(lambda x: prepared.prep(x))[l_idx],\n                        right_df[right_df.geometry.name][r_idx],\n                    ),\n                ]\n            )\n        )\n\n        result.columns = [\"_key_left\", \"_key_right\", \"match_bool\"]\n        result = pd.DataFrame(result[result[\"match_bool\"] == 1]).drop(\n            \"match_bool\", axis=1\n        )\n\n    else:\n        # when output from the join has no overlapping geometries\n        result = pd.DataFrame(columns=[\"_key_left\", \"_key_right\"], dtype=float)\n\n    if op == \"within\":\n        # within implemented as the inverse of contains; swap names\n        left_df, right_df = right_df, left_df\n        result = result.rename(\n            columns={\"_key_left\": \"_key_right\", \"_key_right\": \"_key_left\"}\n        )\n\n    if how == \"inner\":\n        result = result.set_index(\"_key_left\")\n        joined = left_df.merge(result, left_index=True, right_index=True).merge(\n            right_df.drop(right_df.geometry.name, axis=1),\n            left_on=\"_key_right\",\n            right_index=True,\n            suffixes=(\"_%s\" % lsuffix, \"_%s\" % rsuffix),\n        )\n        joined = joined.set_index(index_left).drop([\"_key_right\"], axis=1)\n        joined.index.name = None\n    elif how == \"left\":\n        result = result.set_index(\"_key_left\")\n        joined = left_df.merge(\n            result, left_index=True, right_index=True, how=\"left\"\n        ).merge(\n            right_df.drop(right_df.geometry.name, axis=1),\n            how=\"left\",\n            left_on=\"_key_right\",\n            right_index=True,\n            suffixes=(\"_%s\" % lsuffix, \"_%s\" % rsuffix),\n        )\n        joined = joined.set_index(index_left).drop([\"_key_right\"], axis=1)\n        joined.index.name = None\n    else:  # how == 'right':\n        joined = (\n            left_df.drop(left_df.geometry.name, axis=1)\n            .merge(\n                result.merge(\n                    right_df, left_on=\"_key_right\", right_index=True, how=\"right\"\n                ),\n                left_index=True,\n                right_on=\"_key_left\",\n                how=\"right\",\n            )\n            .set_index(index_right)\n        )\n        joined = joined.drop([\"_key_left\", \"_key_right\"], axis=1)\n\n    return joined\n", "path": "geopandas/tools/sjoin.py"}], "after_files": [{"content": "from warnings import warn\n\nimport numpy as np\nimport pandas as pd\n\nfrom shapely import prepared\n\nfrom geopandas import GeoDataFrame\n\n\ndef sjoin(\n    left_df, right_df, how=\"inner\", op=\"intersects\", lsuffix=\"left\", rsuffix=\"right\"\n):\n    \"\"\"Spatial join of two GeoDataFrames.\n\n    Parameters\n    ----------\n    left_df, right_df : GeoDataFrames\n    how : string, default 'inner'\n        The type of join:\n\n        * 'left': use keys from left_df; retain only left_df geometry column\n        * 'right': use keys from right_df; retain only right_df geometry column\n        * 'inner': use intersection of keys from both dfs; retain only\n          left_df geometry column\n    op : string, default 'intersection'\n        Binary predicate, one of {'intersects', 'contains', 'within'}.\n        See http://shapely.readthedocs.io/en/latest/manual.html#binary-predicates.\n    lsuffix : string, default 'left'\n        Suffix to apply to overlapping column names (left GeoDataFrame).\n    rsuffix : string, default 'right'\n        Suffix to apply to overlapping column names (right GeoDataFrame).\n\n    \"\"\"\n    if not isinstance(left_df, GeoDataFrame):\n        raise ValueError(\n            \"'left_df' should be GeoDataFrame, got {}\".format(type(left_df))\n        )\n\n    if not isinstance(right_df, GeoDataFrame):\n        raise ValueError(\n            \"'right_df' should be GeoDataFrame, got {}\".format(type(right_df))\n        )\n\n    allowed_hows = [\"left\", \"right\", \"inner\"]\n    if how not in allowed_hows:\n        raise ValueError(\n            '`how` was \"%s\" but is expected to be in %s' % (how, allowed_hows)\n        )\n\n    allowed_ops = [\"contains\", \"within\", \"intersects\"]\n    if op not in allowed_ops:\n        raise ValueError(\n            '`op` was \"%s\" but is expected to be in %s' % (op, allowed_ops)\n        )\n\n    if left_df.crs != right_df.crs:\n        warn(\n            (\n                \"CRS of frames being joined does not match!\"\n                \"(%s != %s)\" % (left_df.crs, right_df.crs)\n            )\n        )\n\n    index_left = \"index_%s\" % lsuffix\n    index_right = \"index_%s\" % rsuffix\n\n    # due to GH 352\n    if any(left_df.columns.isin([index_left, index_right])) or any(\n        right_df.columns.isin([index_left, index_right])\n    ):\n        raise ValueError(\n            \"'{0}' and '{1}' cannot be names in the frames being\"\n            \" joined\".format(index_left, index_right)\n        )\n\n    # Attempt to re-use spatial indexes, otherwise generate the spatial index\n    # for the longer dataframe\n    if right_df._sindex_generated or (\n        not left_df._sindex_generated and right_df.shape[0] > left_df.shape[0]\n    ):\n        tree_idx = right_df.sindex\n        tree_idx_right = True\n    else:\n        tree_idx = left_df.sindex\n        tree_idx_right = False\n\n    # the rtree spatial index only allows limited (numeric) index types, but an\n    # index in geopandas may be any arbitrary dtype. so reset both indices now\n    # and store references to the original indices, to be reaffixed later.\n    # GH 352\n    left_df = left_df.copy(deep=True)\n    left_df.index = left_df.index.rename(index_left)\n    left_df = left_df.reset_index()\n    right_df = right_df.copy(deep=True)\n    right_df.index = right_df.index.rename(index_right)\n    right_df = right_df.reset_index()\n\n    if op == \"within\":\n        # within implemented as the inverse of contains; swap names\n        left_df, right_df = right_df, left_df\n        tree_idx_right = not tree_idx_right\n\n    r_idx = np.empty((0, 0))\n    l_idx = np.empty((0, 0))\n    # get rtree spatial index\n    if tree_idx_right:\n        idxmatch = left_df.geometry.apply(lambda x: x.bounds).apply(\n            lambda x: list(tree_idx.intersection(x)) if not x == () else []\n        )\n        idxmatch = idxmatch[idxmatch.apply(len) > 0]\n        # indexes of overlapping boundaries\n        if idxmatch.shape[0] > 0:\n            r_idx = np.concatenate(idxmatch.values)\n            l_idx = np.concatenate([[i] * len(v) for i, v in idxmatch.iteritems()])\n    else:\n        # tree_idx_df == 'left'\n        idxmatch = right_df.geometry.apply(lambda x: x.bounds).apply(\n            lambda x: list(tree_idx.intersection(x)) if not x == () else []\n        )\n        idxmatch = idxmatch[idxmatch.apply(len) > 0]\n        if idxmatch.shape[0] > 0:\n            # indexes of overlapping boundaries\n            l_idx = np.concatenate(idxmatch.values)\n            r_idx = np.concatenate([[i] * len(v) for i, v in idxmatch.iteritems()])\n\n    if len(r_idx) > 0 and len(l_idx) > 0:\n        # Vectorize predicate operations\n        def find_intersects(a1, a2):\n            return a1.intersects(a2)\n\n        def find_contains(a1, a2):\n            return a1.contains(a2)\n\n        predicate_d = {\n            \"intersects\": find_intersects,\n            \"contains\": find_contains,\n            \"within\": find_contains,\n        }\n\n        check_predicates = np.vectorize(predicate_d[op])\n\n        result = pd.DataFrame(\n            np.column_stack(\n                [\n                    l_idx,\n                    r_idx,\n                    check_predicates(\n                        left_df.geometry.apply(lambda x: prepared.prep(x))[l_idx],\n                        right_df[right_df.geometry.name][r_idx],\n                    ),\n                ]\n            )\n        )\n\n        result.columns = [\"_key_left\", \"_key_right\", \"match_bool\"]\n        result = pd.DataFrame(result[result[\"match_bool\"] == 1]).drop(\n            \"match_bool\", axis=1\n        )\n\n    else:\n        # when output from the join has no overlapping geometries\n        result = pd.DataFrame(columns=[\"_key_left\", \"_key_right\"], dtype=float)\n\n    if op == \"within\":\n        # within implemented as the inverse of contains; swap names\n        left_df, right_df = right_df, left_df\n        result = result.rename(\n            columns={\"_key_left\": \"_key_right\", \"_key_right\": \"_key_left\"}\n        )\n\n    if how == \"inner\":\n        result = result.set_index(\"_key_left\")\n        joined = left_df.merge(result, left_index=True, right_index=True).merge(\n            right_df.drop(right_df.geometry.name, axis=1),\n            left_on=\"_key_right\",\n            right_index=True,\n            suffixes=(\"_%s\" % lsuffix, \"_%s\" % rsuffix),\n        )\n        joined = joined.set_index(index_left).drop([\"_key_right\"], axis=1)\n        joined.index.name = None\n    elif how == \"left\":\n        result = result.set_index(\"_key_left\")\n        joined = left_df.merge(\n            result, left_index=True, right_index=True, how=\"left\"\n        ).merge(\n            right_df.drop(right_df.geometry.name, axis=1),\n            how=\"left\",\n            left_on=\"_key_right\",\n            right_index=True,\n            suffixes=(\"_%s\" % lsuffix, \"_%s\" % rsuffix),\n        )\n        joined = joined.set_index(index_left).drop([\"_key_right\"], axis=1)\n        joined.index.name = None\n    else:  # how == 'right':\n        joined = (\n            left_df.drop(left_df.geometry.name, axis=1)\n            .merge(\n                result.merge(\n                    right_df, left_on=\"_key_right\", right_index=True, how=\"right\"\n                ),\n                left_index=True,\n                right_on=\"_key_left\",\n                how=\"right\",\n            )\n            .set_index(index_right)\n        )\n        joined = joined.drop([\"_key_left\", \"_key_right\"], axis=1)\n\n    return joined\n", "path": "geopandas/tools/sjoin.py"}]}