Repository: geopandas/geopandas

Issue:
BUG: GeoDataFrame.crs not updated if geometry GeoSeries is reprojected manually
Consider following example.

```py
gdf = geopandas.read_file(geopandas.datasets.get_path("nybb"))
gdf.crs

<Projected CRS: EPSG:2263>
...

gdf["geometry"] = gdf.geometry.to_crs(4326)
gdf.crs

<Projected CRS: EPSG:2263>
...
```

GeoDataFrame doesn't load the CRS of its active geometry column if it is re-projected in this way. Assigning re-projected GeoSeries as an attribute (`gdf.geometry = gdf.geometry.to_crs(4326)`) works as expected.

`gdf.crs` should directly return the CRS of the active geometry column in this case.


Discussion:

Resolving patch:
diff --git a/geopandas/geodataframe.py b/geopandas/geodataframe.py
--- a/geopandas/geodataframe.py
+++ b/geopandas/geodataframe.py
@@ -33,7 +33,9 @@ def _ensure_geometry(data, crs=None):
     """
     if is_geometry_type(data):
         if isinstance(data, Series):
-            return GeoSeries(data)
+            data = GeoSeries(data)
+        if data.crs is None:
+            data.crs = crs
         return data
     else:
         if isinstance(data, Series):
@@ -1320,6 +1322,7 @@ def __setitem__(self, key, value):
                 value = [value] * self.shape[0]
             try:
                 value = _ensure_geometry(value, crs=self.crs)
+                self._crs = value.crs
             except TypeError:
                 warnings.warn("Geometry column does not contain geometry.")
         super(GeoDataFrame, self).__setitem__(key, value)


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
@@ -268,6 +268,8 @@ def test_dataframe(self):
         assert df.geometry.crs == self.wgs
         assert df.geometry.values.crs == self.wgs
 
+        arr = from_shapely(self.geoms)
+        s = GeoSeries(arr, crs=27700)
         df = GeoDataFrame()
         df = df.set_geometry(s)
         assert df.crs == self.osgb
@@ -298,6 +300,42 @@ def test_dataframe(self):
         df.crs = 27700
         assert df.crs == self.osgb
 
+    def test_dataframe_setitem(self):
+        # new geometry CRS has priority over GDF CRS
+        arr = from_shapely(self.geoms)
+        s = GeoSeries(arr, crs=27700)
+        df = GeoDataFrame()
+        df["geometry"] = s
+        assert df.crs == self.osgb
+        assert df.geometry.crs == self.osgb
+        assert df.geometry.values.crs == self.osgb
+
+        arr = from_shapely(self.geoms, crs=27700)
+        df = GeoDataFrame()
+        df["geometry"] = arr
+        assert df.crs == self.osgb
+        assert df.geometry.crs == self.osgb
+        assert df.geometry.values.crs == self.osgb
+
+        # test to_crs case (GH1960)
+        arr = from_shapely(self.geoms)
+        df = GeoDataFrame({"col1": [1, 2], "geometry": arr}, crs=4326)
+        df["geometry"] = df["geometry"].to_crs(27700)
+        assert df.crs == self.osgb
+        assert df.geometry.crs == self.osgb
+        assert df.geometry.values.crs == self.osgb
+
+        # test changing geometry crs not in the geometry column doesn't change the crs
+        arr = from_shapely(self.geoms)
+        df = GeoDataFrame(
+            {"col1": [1, 2], "geometry": arr, "other_geom": arr}, crs=4326
+        )
+        df["other_geom"] = arr.to_crs(27700)
+        assert df.crs == self.wgs
+        assert df.geometry.crs == self.wgs
+        assert df["geometry"].crs == self.wgs
+        assert df["other_geom"].crs == self.osgb
+
     @pytest.mark.parametrize(
         "scalar", [None, Point(0, 0), LineString([(0, 0), (1, 1)])]
     )