Repository: geopandas/geopandas
Pull request #2179 (2021-10-13 13:13:17)

BUG: avoid warning from deprecated explode in clip()
xref https://github.com/geopandas/geopandas/issues/1667#issuecomment-940303204




Patch:
diff --git a/geopandas/_vectorized.py b/geopandas/_vectorized.py
index 7569e37d..611551f7 100644
--- a/geopandas/_vectorized.py
+++ b/geopandas/_vectorized.py
@@ -891,7 +891,11 @@ def unary_union(data):
     if compat.USE_PYGEOS:
         return _pygeos_to_shapely(pygeos.union_all(data))
     else:
-        return shapely.ops.unary_union(data)
+        data = [g for g in data if g is not None]
+        if data:
+            return shapely.ops.unary_union(data)
+        else:
+            return None
 
 
 #
diff --git a/geopandas/explore.py b/geopandas/explore.py
index b8b3bb7c..4d032206 100644
--- a/geopandas/explore.py
+++ b/geopandas/explore.py
@@ -396,8 +396,8 @@ GON (((180.00000 -16.06713, 180.00000...
             color = list(map(lambda x: cmap(x), df[column]))
 
         else:
-            vmin = gdf[column].min() if not vmin else vmin
-            vmax = gdf[column].max() if not vmax else vmax
+            vmin = gdf[column].min() if vmin is None else vmin
+            vmax = gdf[column].max() if vmax is None else vmax
 
             # get bins
             if scheme is not None:
diff --git a/geopandas/tools/clip.py b/geopandas/tools/clip.py
index dda5e630..27ff372b 100644
--- a/geopandas/tools/clip.py
+++ b/geopandas/tools/clip.py
@@ -182,7 +182,7 @@ def clip(gdf, mask, keep_geom_type=False):
             elif new_collection or more_types:
                 orig_type = gdf.geom_type.iloc[0]
                 if new_collection:
-                    clipped = clipped.explode()
+                    clipped = clipped.explode(index_parts=False)
                 if orig_type in polys:
                     clipped = clipped.loc[clipped.geom_type.isin(polys)]
                 elif orig_type in lines:
diff --git a/geopandas/tools/overlay.py b/geopandas/tools/overlay.py
index 75dcb550..babb67a5 100644
--- a/geopandas/tools/overlay.py
+++ b/geopandas/tools/overlay.py
@@ -312,7 +312,7 @@ def overlay(df1, df2, how="intersection", keep_geom_type=None, make_valid=True):
     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":
@@ -323,6 +323,9 @@ def overlay(df1, df2, how="intersection", keep_geom_type=None, make_valid=True):
             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]
 
@@ -340,16 +343,18 @@ def overlay(df1, df2, how="intersection", keep_geom_type=None, make_valid=True):
 
             orig_num_geoms_exploded = exploded.shape[0]
             if geom_type in polys:
-                exploded = exploded.loc[exploded.geom_type.isin(polys)]
+                exploded.loc[~exploded.geom_type.isin(polys), geom_col] = None
             elif geom_type in lines:
-                exploded = exploded.loc[exploded.geom_type.isin(lines)]
+                exploded.loc[~exploded.geom_type.isin(lines), geom_col] = None
             elif geom_type in points:
-                exploded = exploded.loc[exploded.geom_type.isin(points)]
+                exploded.loc[~exploded.geom_type.isin(points), geom_col] = None
             else:
                 raise TypeError(
                     "`keep_geom_type` does not support {}.".format(geom_type)
                 )
-            num_dropped_collection = orig_num_geoms_exploded - exploded.shape[0]
+            num_dropped_collection = (
+                orig_num_geoms_exploded - exploded.geometry.isna().sum()
+            )
 
             # level_0 created with above reset_index operation
             # and represents the original geometry collections
@@ -387,5 +392,4 @@ def overlay(df1, df2, how="intersection", keep_geom_type=None, make_valid=True):
             )
 
     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_explore.py b/geopandas/tests/test_explore.py
index b72e6188..4b7c9321 100644
--- a/geopandas/tests/test_explore.py
+++ b/geopandas/tests/test_explore.py
@@ -466,6 +466,20 @@ class TestExplore:
         assert 'case"119":return{"color":"#414287","fillColor":"#414287"' in out_str
         assert 'case"3":return{"color":"#482173","fillColor":"#482173"' in out_str
 
+        # test 0
+        df2 = self.nybb.copy()
+        df2["values"] = df2["BoroCode"] * 10.0
+        m = df2[df2["values"] >= 30].explore("values", vmin=0)
+        out_str = self._fetch_map_string(m)
+        assert 'case"1":return{"color":"#7ad151","fillColor":"#7ad151"' in out_str
+        assert 'case"2":return{"color":"#22a884","fillColor":"#22a884"' in out_str
+
+        df2["values_negative"] = df2["BoroCode"] * -10.0
+        m = df2[df2["values_negative"] <= 30].explore("values_negative", vmax=0)
+        out_str = self._fetch_map_string(m)
+        assert 'case"1":return{"color":"#414487","fillColor":"#414487"' in out_str
+        assert 'case"2":return{"color":"#2a788e","fillColor":"#2a788e"' in out_str
+
     def test_missing_vals(self):
         m = self.missing.explore("continent")
         assert '"fillColor":null' in self._fetch_map_string(m)
diff --git a/geopandas/tests/test_geom_methods.py b/geopandas/tests/test_geom_methods.py
index 1c70e855..221eca38 100644
--- a/geopandas/tests/test_geom_methods.py
+++ b/geopandas/tests/test_geom_methods.py
@@ -354,6 +354,12 @@ class TestGeomMethods:
 
         self._test_unary_topological("unary_union", expected, g)
 
+        g2 = GeoSeries([p1, None])
+        self._test_unary_topological("unary_union", p1, g2)
+
+        g3 = GeoSeries([None, None])
+        assert g3.unary_union is None
+
     def test_cascaded_union_deprecated(self):
         p1 = self.t1
         p2 = Polygon([(2, 0), (3, 0), (3, 1)])
diff --git a/geopandas/tests/test_overlay.py b/geopandas/tests/test_overlay.py
index 058da69b..f1458937 100644
--- a/geopandas/tests/test_overlay.py
+++ b/geopandas/tests/test_overlay.py
@@ -628,6 +628,67 @@ def test_keep_geom_type_geometry_collection2():
     assert_geodataframe_equal(result1, expected1)
 
 
+def test_keep_geom_type_geomcoll_different_types():
+    polys1 = [box(0, 1, 1, 3), box(10, 10, 12, 12)]
+    polys2 = [
+        Polygon([(1, 0), (3, 0), (3, 3), (1, 3), (1, 2), (2, 2), (2, 1), (1, 1)]),
+        box(11, 11, 13, 13),
+    ]
+    df1 = GeoDataFrame({"left": [0, 1], "geometry": polys1})
+    df2 = GeoDataFrame({"right": [0, 1], "geometry": polys2})
+    result1 = overlay(df1, df2, keep_geom_type=True)
+    expected1 = GeoDataFrame(
+        {
+            "left": [1],
+            "right": [1],
+            "geometry": [box(11, 11, 12, 12)],
+        }
+    )
+    assert_geodataframe_equal(result1, expected1)
+
+    result2 = overlay(df1, df2, keep_geom_type=False)
+    expected2 = GeoDataFrame(
+        {
+            "left": [0, 1],
+            "right": [0, 1],
+            "geometry": [
+                GeometryCollection([LineString([(1, 2), (1, 3)]), Point(1, 1)]),
+                box(11, 11, 12, 12),
+            ],
+        }
+    )
+    assert_geodataframe_equal(result2, expected2)
+
+
+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)])