Repository: geopandas/geopandas

Issue:
BUG: "ValueError: The truth value of an array with more than one element is ambiguous" when using groupby.agg() in "geometry" column.
- [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 main branch of geopandas.

---
#### Code Sample, a copy-pastable example
```python
import geopandas as gpd
from shapely import Point, LineString

gdf = gpd.GeoDataFrame(
	{
		"geometry": [Point(0, 0), Point(1, 0), Point(1, 5), Point(0, 5), Point(0, 6)],
		"episode": [1, 1, 2, 2, 2]
	}
)
print(gdf.groupby("episode").agg(
	{"geometry": lambda x: LineString(x.tolist())}
))
#                           geometry
#episode                            
#1             LINESTRING (0 0, 1 0)
#2        LINESTRING (1 5, 0 5, 0 6)

print(gdf.groupby("episode").agg(
	geometry=("geometry", lambda x: LineString(x.tolist()))
))

# ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```
#### Output
```sh
episode                            
1             LINESTRING (0 0, 1 0)
2        LINESTRING (1 5, 0 5, 0 6)

ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()
```

#### Expected Output

```sh     
                   geometry
episode                            
1             LINESTRING (0 0, 1 0)
2        LINESTRING (1 5, 0 5, 0 6)

          
                 geometry
episode                            
1             LINESTRING (0 0, 1 0)
2        LINESTRING (1 5, 0 5, 0 6)
```
#### Explications
This is due to the check of \_\_finalize\_\_. (geodataframe.py:1924-1923). 
```python
elif method == "concat":
            for name in self._metadata:
                object.__setattr__(self, name, getattr(other.objs[0], name, None))

            if (self.columns == self._geometry_column_name).sum() > 1: # <----
                    raise ValueError(
                    "Concat operation has resulted in multiple columns using "
                    f"the geometry column name '{self._geometry_column_name}'.\n"
                    "Please ensure this column from the first DataFrame is not "
                    "repeated."
            )
```

In traditional groupby.agg({"geometry": lambda....}), we have a one dimensional  
```python 
self.columns
>>> Index(['geometry'], dtype='object')
```

Wherer in a form of groupby.agg(geometry=("geometry","lambda...)) 
```python 
self.columns
>>>MultiIndex([('geometry', '<lambda>')],)
```

#### Solution suggested

Add a condition to check if the number of levels of self.columns is the same dimention of self._geometry_column_name.
```python
if self._geometry_column_name and self.columns.nlevels == self._geometry_column_name.size and  (self.columns == self._geometry_column_name).sum() > 1:
```
#### Output of ``geopandas.show_versions()``
<details>
geopandas  : 1.0.0
numpy      : 1.26.4
pandas     : 2.2.2
pyproj     : 3.6.1
shapely    : 2.0.3
pyogrio    : 0.9.0
geoalchemy2: None
geopy      : 2.4.1
matplotlib : 3.8.3
mapclassify: None
fiona      : 1.9.6
psycopg    : None
psycopg2   : None
pyarrow    : 15.0.2
</details>


Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7a983fa3..51217980 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,11 @@
 # Changelog
 
+## Version 1.0.2 (???)
+
+Bug fixes:
+
+- Properly support named aggregations over a geometry column in `GroupBy.agg` (#3368).
+
 ## Version 1.0.1 (July 2, 2024)
 
 Bug fixes:
diff --git a/geopandas/base.py b/geopandas/base.py
index 5e2729e2..8d86cf15 100644
--- a/geopandas/base.py
+++ b/geopandas/base.py
@@ -1152,10 +1152,10 @@ GeometryCollection
         By default, you get back a GeoSeries of polygons:
 
         >>> s.voronoi_polygons()
-        0     POLYGON ((-2 5, 1 2, -2 -1, -2 5))
-        1        POLYGON ((4 5, 1 2, -2 5, 4 5))
-        2    POLYGON ((-2 -1, 1 2, 4 -1, -2 -1))
-        3       POLYGON ((4 -1, 1 2, 4 5, 4 -1))
+        0        POLYGON ((-2 5, 1 2, -2 -1, -2 5))
+        1           POLYGON ((4 5, 1 2, -2 5, 4 5))
+        2       POLYGON ((-2 -1, 1 2, 4 -1, -2 -1))
+        3    POLYGON ((4 -1, 4 -1, 1 2, 4 5, 4 -1))
         dtype: geometry
 
         If you set only_edges to True, you get back LineStrings representing the
@@ -1561,7 +1561,7 @@ GeometryCollection
         0                   POINT (1 1)
         1             POINT Z (1 1 0.9)
         2    LINESTRING (0 0, 0 1, 1 1)
-        3            LINESTRING Z EMPTY
+        3              LINESTRING EMPTY
         dtype: geometry
 
         >>> s.set_precision(1, mode="pointwise")
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
index 339315d6..d2e889b4 100644
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -1925,7 +1925,16 @@ properties': {'col1': 'name1'}, 'geometry': {'type': 'Point', 'coordinates': (1.
             for name in self._metadata:
                 object.__setattr__(self, name, getattr(other.objs[0], name, None))
 
-            if (self.columns == self._geometry_column_name).sum() > 1:
+            if (
+                self.columns.nlevels == 1
+                and (self.columns == self._geometry_column_name).sum() > 1
+            ) or (
+                self.columns.nlevels > 1
+                and (
+                    self.columns.get_level_values(0) == self._geometry_column_name
+                ).sum()
+                > 1
+            ):
                 raise ValueError(
                     "Concat operation has resulted in multiple columns using "
                     f"the geometry column name '{self._geometry_column_name}'.\n"


Test patch:
diff --git a/geopandas/tests/test_merge.py b/geopandas/tests/test_merge.py
index fbcdc3ed..c28ed91b 100644
--- a/geopandas/tests/test_merge.py
+++ b/geopandas/tests/test_merge.py
@@ -133,7 +133,7 @@ class TestMerging:
         with pytest.warns(
             UserWarning, match=r"CRS not set for some of the concatenation inputs.*"
         ):
-            partial_none_case = self.gdf[["geometry"]]
+            partial_none_case = self.gdf[["geometry"]].copy()
             partial_none_case.iloc[0] = None
             pd.concat([single_geom_col, partial_none_case])
 
diff --git a/geopandas/tests/test_pandas_methods.py b/geopandas/tests/test_pandas_methods.py
index 7230c3ea..ce49d0a9 100644
--- a/geopandas/tests/test_pandas_methods.py
+++ b/geopandas/tests/test_pandas_methods.py
@@ -650,6 +650,25 @@ def test_groupby(df):
     assert_series_equal(res, exp)
 
 
+def test_groupby_agg_tuple(df):
+    res_dict = (
+        df.groupby("value2")
+        .agg({"geometry": lambda x: x.union_all()})
+        .set_geometry("geometry")  # groupby does not set active geom
+    )
+    res_tup = (
+        df.groupby("value2")
+        .agg(geometry=("geometry", lambda x: x.union_all()))
+        .set_geometry("geometry")
+    )
+    exp = GeoDataFrame(
+        geometry=[shapely.geometry.MultiPoint([(0, 0), (2, 2)]), Point(1, 1)],
+        index=pd.Index([1, 2], name="value2"),
+    )
+    assert_geodataframe_equal(res_tup, exp)
+    assert_geodataframe_equal(res_dict, res_tup)
+
+
 def test_groupby_groups(df):
     g = df.groupby("value2")
     res = g.get_group(1)