Repository: geopandas/geopandas

Issue:
API (BUG?): GeoDataFrame(gdf) constructor: preserve geometry column name?
When passing a GeoDataFrame object to the GeoDataFrame object, it currently does not preserve the geometry column name if not "geometry":

```
In [1]: df = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))

In [2]: df2 = df.rename_geometry("polygons")

In [3]: res = geopandas.GeoDataFrame(df2)

In [4]: res.geometry
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-4-ec92b07bcf43> in <module>
----> 1 res.geometry

~/scipy/pandas/pandas/core/generic.py in __getattr__(self, name)
   5464         ):
   5465             return self[name]
-> 5466         return object.__getattribute__(self, name)
   5467 
   5468     def __setattr__(self, name: str, value) -> None:

~/scipy/geopandas/geopandas/geodataframe.py in _get_geometry(self)
    196     def _get_geometry(self):
    197         if self._geometry_column_name not in self:
--> 198             raise AttributeError(
    199                 "No geometry data set yet (expected in"
    200                 " column '%s'.)" % self._geometry_column_name

AttributeError: No geometry data set yet (expected in column 'geometry'.)

In [5]: res._geometry_column_name
Out[5]: 'geometry'
```

Of course, this somewhat follows from "geometry" being the default column name for the geometry column if not specified by the user. But, it seems we should preserve the name if passing a GeoDataFrame and `geometry=None` in the constructor?

cc @m-richards 


Discussion:
> But, it seems we should preserve the name if passing a GeoDataFrame and geometry=None in the constructor?

Yes, I think that would make sense here.
There is also the slightly more pathlogical case where geometry is present, but isn't the active geometry column, 
```python
In [19]: df = geopandas.read_file(geopandas.datasets.get_path("naturalearth_lowres"))
In [20]: df['polygons'] = df['geometry']
In [21]: df2 = df.set_geometry('polygons')
In [22]: res = geopandas.GeoDataFrame(df2)
In [23]: res.geometry.name
Out[23]: 'geometry'
```
but this still seems incorrect, should be pretty simple to fix.

Ah, yes, good catch. I think that also in that case we should preserve the active geometry column.

Resolving patch:
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -47,6 +47,20 @@ def _ensure_geometry(data, crs=None):
             return out
 
 
+def _crs_mismatch_warning():
+    # TODO: raise error in 0.9 or 0.10.
+    warnings.warn(
+        "CRS mismatch between CRS of the passed geometries "
+        "and 'crs'. Use 'GeoDataFrame.set_crs(crs, "
+        "allow_override=True)' to overwrite CRS or "
+        "'GeoDataFrame.to_crs(crs)' to reproject geometries. "
+        "CRS mismatch will raise an error in the future versions "
+        "of GeoPandas.",
+        FutureWarning,
+        stacklevel=3,
+    )
+
+
 class GeoDataFrame(GeoPandasBase, DataFrame):
     """
     A GeoDataFrame object is a pandas.DataFrame that has a column
@@ -103,9 +117,9 @@ class GeoDataFrame(GeoPandasBase, DataFrame):
 
     _geometry_column_name = DEFAULT_GEO_COLUMN_NAME
 
-    def __init__(self, *args, geometry=None, crs=None, **kwargs):
+    def __init__(self, data=None, *args, geometry=None, crs=None, **kwargs):
         with compat.ignore_shapely2_warnings():
-            super().__init__(*args, **kwargs)
+            super().__init__(data, *args, **kwargs)
 
         # need to set this before calling self['geometry'], because
         # getitem accesses crs
@@ -117,6 +131,15 @@ def __init__(self, *args, geometry=None, crs=None, **kwargs):
         # but within a try/except because currently non-geometries are
         # allowed in that case
         # TODO do we want to raise / return normal DataFrame in this case?
+
+        # if gdf passed in and geo_col is set, we use that for geometry
+        if geometry is None and isinstance(data, GeoDataFrame):
+            self._geometry_column_name = data._geometry_column_name
+            if crs is not None and data.crs != crs:
+                _crs_mismatch_warning()
+                # TODO: raise error in 0.9 or 0.10.
+            return
+
         if geometry is None and "geometry" in self.columns:
             # Check for multiple columns with name "geometry". If there are,
             # self["geometry"] is a gdf and constructor gets recursively recalled
@@ -136,16 +159,7 @@ def __init__(self, *args, geometry=None, crs=None, **kwargs):
                     and crs
                     and not self["geometry"].values.crs == crs
                 ):
-                    warnings.warn(
-                        "CRS mismatch between CRS of the passed geometries "
-                        "and 'crs'. Use 'GeoDataFrame.set_crs(crs, "
-                        "allow_override=True)' to overwrite CRS or "
-                        "'GeoDataFrame.to_crs(crs)' to reproject geometries. "
-                        "CRS mismatch will raise an error in the future versions "
-                        "of GeoPandas.",
-                        FutureWarning,
-                        stacklevel=2,
-                    )
+                    _crs_mismatch_warning()
                     # TODO: raise error in 0.9 or 0.10.
                 self["geometry"] = _ensure_geometry(self["geometry"].values, crs)
             except TypeError:
@@ -165,16 +179,7 @@ def __init__(self, *args, geometry=None, crs=None, **kwargs):
                 and crs
                 and not geometry.crs == crs
             ):
-                warnings.warn(
-                    "CRS mismatch between CRS of the passed geometries "
-                    "and 'crs'. Use 'GeoDataFrame.set_crs(crs, "
-                    "allow_override=True)' to overwrite CRS or "
-                    "'GeoDataFrame.to_crs(crs)' to reproject geometries. "
-                    "CRS mismatch will raise an error in the future versions "
-                    "of GeoPandas.",
-                    FutureWarning,
-                    stacklevel=2,
-                )
+                _crs_mismatch_warning()
                 # TODO: raise error in 0.9 or 0.10.
             self.set_geometry(geometry, inplace=True)
 


Test patch:
diff --git a/geopandas/tests/test_crs.py b/geopandas/tests/test_crs.py
--- a/geopandas/tests/test_crs.py
+++ b/geopandas/tests/test_crs.py
@@ -220,16 +220,16 @@ def test_dataframe(self):
         assert df.geometry.values.crs == self.osgb
 
         # different passed CRS than array CRS is ignored
-        with pytest.warns(FutureWarning):
+        with pytest.warns(FutureWarning, match="CRS mismatch"):
             df = GeoDataFrame(geometry=s, crs=4326)
         assert df.crs == self.osgb
         assert df.geometry.crs == self.osgb
         assert df.geometry.values.crs == self.osgb
-        with pytest.warns(FutureWarning):
+        with pytest.warns(FutureWarning, match="CRS mismatch"):
             GeoDataFrame(geometry=s, crs=4326)
-        with pytest.warns(FutureWarning):
+        with pytest.warns(FutureWarning, match="CRS mismatch"):
             GeoDataFrame({"data": [1, 2], "geometry": s}, crs=4326)
-        with pytest.warns(FutureWarning):
+        with pytest.warns(FutureWarning, match="CRS mismatch"):
             GeoDataFrame(df, crs=4326).crs
 
         # manually change CRS
diff --git a/geopandas/tests/test_geodataframe.py b/geopandas/tests/test_geodataframe.py
--- a/geopandas/tests/test_geodataframe.py
+++ b/geopandas/tests/test_geodataframe.py
@@ -930,11 +930,9 @@ def test_from_frame_specified_geometry(self):
             res = GeoDataFrame(df, geometry="other_geom")
             check_geodataframe(res, "other_geom")
 
-        # when passing GeoDataFrame with custom geometry name to constructor
-        # an invalid geodataframe is the result TODO is this desired ?
+        # gdf from gdf should preserve active geometry column name
         df = GeoDataFrame(gpdf)
-        with pytest.raises(AttributeError):
-            df.geometry
+        check_geodataframe(df, "other_geom")
 
     def test_only_geometry(self):
         exp = GeoDataFrame(