Repository: geopandas/geopandas

Issue:
BUG: overlay how=difference keep_geom_type=True returns GeometryCollection
- [x] I have checked that this issue has not already been reported.

- [x] I have confirmed this bug exists on the latest version of geopandas.

- [ ] (optional) I have confirmed this bug exists on the master branch of geopandas.

---

I originally made my report on stack overflow here: https://stackoverflow.com/questions/69502708/bug-with-keep-geom-type-using-overlay-how-difference-returning-geometrycollect and was told to report it here by @martinfleis 

I also have attached the left_df and right_df shape files into the [samples.zip](https://github.com/geopandas/geopandas/files/7318933/samples.zip)

#### Code Sample, a copy-pastable example

```python
import geopandas

geopandas.show_versions()

left_df = geopandas.read_file('.../left_df.shp')
right_df = geopandas.read_file('.../right_df.shp')

res_difference = left_df.overlay(right_df, 
                                 how='difference', 
                                 keep_geom_type=True,
                                 make_valid=True)
print(left_df)
print(right_df)
print(res_difference)

```

#### Problem description
Currently the issue is that when I do an overlay how='difference' with these polygons, I get an issue where it is still returning geometry collections even though this bug was supposed to have been fixed with #1582 . All my polygons in `left_df` are either Polygons or MultiPolygons. It should then return Polygons or MultiPolygons in `res_difference` but I get a GeometryCollection.

The result of printing each GeoDataFrame

left_df
   FID                                           geometry
0    0  MULTIPOLYGON (((-15973.541 1603937.095, -15929...
1    1  MULTIPOLYGON (((420819.664 1595602.361, 420931...
2    2  MULTIPOLYGON (((374487.841 1922630.575, 374452...

right_df
   FID                                           geometry
0    0  MULTIPOLYGON (((-20483.848 1606773.812, -21153...
1    1  MULTIPOLYGON (((417191.271 1585449.007, 417240...
2    2  MULTIPOLYGON (((333826.662 1885983.061, 333875...
3    3  POLYGON ((347965.596 1950312.020, 347830.575 1...

res_difference
   FID                                           geometry
0    0  MULTIPOLYGON (((-15929.518 1603957.346, -15887...
1    1  MULTIPOLYGON (((420931.129 1595536.289, 421142...
2    2  GEOMETRYCOLLECTION (LINESTRING (370322.856 192...



#### Expected Output


left_df
   FID                                           geometry
0    0  MULTIPOLYGON (((-15973.541 1603937.095, -15929...
1    1  MULTIPOLYGON (((420819.664 1595602.361, 420931...
2    2  MULTIPOLYGON (((374487.841 1922630.575, 374452...

right_df
   FID                                           geometry
0    0  MULTIPOLYGON (((-20483.848 1606773.812, -21153...
1    1  MULTIPOLYGON (((417191.271 1585449.007, 417240...
2    2  MULTIPOLYGON (((333826.662 1885983.061, 333875...
3    3  POLYGON ((347965.596 1950312.020, 347830.575 1...

res_difference
   FID                                           geometry
0    0  MULTIPOLYGON (((-15929.518 1603957.346, -15887...
1    1  MULTIPOLYGON (((420931.129 1595536.289, 421142...
2    2  MULTIPOLYGON (...



#### Output of ``geopandas.show_versions()``

<details>

SYSTEM INFO
-----------
python     : 3.9.7 | packaged by conda-forge | (default, Sep 29 2021, 20:33:18)  [Clang 11.1.0 ]
executable : /Users/jkim/opt/anaconda3/envs/sif_env/bin/python
machine    : macOS-11.6-x86_64-i386-64bit

GEOS, GDAL, PROJ INFO
---------------------
GEOS       : 3.9.1
GEOS lib   : /Users/jkim/opt/anaconda3/envs/sif_env/lib/libgeos_c.dylib
GDAL       : 3.3.2
GDAL data dir: /Users/jkim/opt/anaconda3/envs/sif_env/share/gdal
PROJ       : 8.1.1
PROJ data dir: /Users/jkim/opt/anaconda3/envs/sif_env/share/proj

PYTHON DEPENDENCIES
-------------------
geopandas  : 0.10.1
pandas     : 1.3.3
fiona      : 1.8.20
numpy      : 1.21.2
shapely    : 1.7.1
rtree      : 0.9.7
pyproj     : 3.2.1
matplotlib : 3.4.3
mapclassify: 2.4.3
geopy      : None
psycopg2   : None
geoalchemy2: None
pyarrow    : None
pygeos     : 0.10.2

</details

[samples.zip](https://github.com/geopandas/geopandas/files/7318933/samples.zip)
>



Discussion:
@jinhyukekim Thanks for the report. Interestingly, I get the expected output when I run your snippet in my own environment:
```
In [6]: res_difference                                                          
Out[6]: 
   FID                                           geometry
0    0  MULTIPOLYGON (((-15973.541 1603937.095, -15929...
1    1  MULTIPOLYGON (((420819.664 1595602.361, 420931...
2    2  MULTIPOLYGON (((374487.841 1922630.575, 374452...
```

I'll see if I can create an environment locally that recreates the behavior you're seeing. 

Resolving patch:
diff --git a/geopandas/tools/overlay.py b/geopandas/tools/overlay.py
--- a/geopandas/tools/overlay.py
+++ b/geopandas/tools/overlay.py
@@ -310,7 +310,7 @@ def _make_valid(df):
     with warnings.catch_warnings():  # CRS checked above, suppress array-level warning
         warnings.filterwarnings("ignore", message="CRS mismatch between the CRS")
         if how == "difference":
-            return _overlay_difference(df1, df2)
+            result = _overlay_difference(df1, df2)
         elif how == "intersection":
             result = _overlay_intersection(df1, df2)
         elif how == "symmetric_difference":
@@ -321,6 +321,9 @@ def _make_valid(df):
             dfunion = _overlay_union(df1, df2)
             result = dfunion[dfunion["__idx1"].notnull()].copy()
 
+        if how in ["intersection", "symmetric_difference", "union", "identity"]:
+            result.drop(["__idx1", "__idx2"], axis=1, inplace=True)
+
     if keep_geom_type:
         geom_type = df1.geom_type.iloc[0]
 
@@ -385,5 +388,4 @@ def _make_valid(df):
             )
 
     result.reset_index(drop=True, inplace=True)
-    result.drop(["__idx1", "__idx2"], axis=1, inplace=True)
     return result


Test patch:
diff --git a/geopandas/tests/test_overlay.py b/geopandas/tests/test_overlay.py
--- a/geopandas/tests/test_overlay.py
+++ b/geopandas/tests/test_overlay.py
@@ -628,6 +628,35 @@ def test_keep_geom_type_geometry_collection2():
     assert_geodataframe_equal(result1, expected1)
 
 
+def test_keep_geom_type_geometry_collection_difference():
+    # GH 2163
+
+    polys1 = [
+        box(0, 0, 1, 1),
+        box(1, 1, 2, 2),
+    ]
+
+    # the tiny sliver in the second geometry may be converted to a
+    # linestring during the overlay process due to floating point errors
+    # on some platforms
+    polys2 = [
+        box(0, 0, 1, 1),
+        box(1, 1, 2, 3).union(box(2, 2, 3, 2.00000000000000001)),
+    ]
+    df1 = GeoDataFrame({"left": [0, 1], "geometry": polys1})
+    df2 = GeoDataFrame({"right": [0, 1], "geometry": polys2})
+
+    result1 = overlay(df2, df1, keep_geom_type=True, how="difference")
+    expected1 = GeoDataFrame(
+        {
+            "right": [1],
+            "geometry": [box(1, 2, 2, 3)],
+        },
+    )
+
+    assert_geodataframe_equal(result1, expected1)
+
+
 @pytest.mark.parametrize("make_valid", [True, False])
 def test_overlap_make_valid(make_valid):
     bowtie = Polygon([(1, 1), (9, 9), (9, 1), (1, 9), (1, 1)])