Repository: martinfleis/momepy (python)

Issue:
`SettingWithCopyWarning` in the intensity notebook/user guide
In the `intensity.ipynb` notebook , the call to:
```python
tess_car = momepy.AreaRatio(tessellation, buildings, 'area', 'area', 'uID')
```
throws a pandas `SettingWithCopyWarning` as in:

```
/home/martibosch/anaconda3/envs/momepy/lib/python3.7/site-packages/pandas/core/frame.py:4238: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  return super().rename(**kwargs)
```
and the same happens for:

```python
tessellation['FAR'] = momepy.AreaRatio(tessellation, buildings,
                                                     'area', 'floor_area', 'uID').ar
```

```
/home/martibosch/anaconda3/envs/momepy/lib/python3.7/site-packages/pandas/core/frame.py:4238: SettingWithCopyWarning: 
A value is trying to be set on a copy of a slice from a DataFrame

See the caveats in the documentation: http://pandas.pydata.org/pandas-docs/stable/user_guide/indexing.html#returning-a-view-versus-a-copy
  return super().rename(**kwargs)
```

Given how the classes to compute characters work, it is likely that the `SettingWithCopyWarning` occurs elsewhere. While this is just a warning, I believe that it is generally a good practice to avoid it (if possible).

Pull request:
BUG: `SettingWithCopyWarning` in AreaRatio
Fixes #109 

Avoid `SettingWithCopyWarning` in `AreaRatio`.

Patch:
diff --git a/momepy/dimension.py b/momepy/dimension.py
index c55e8e9..4ba8639 100644
--- a/momepy/dimension.py
+++ b/momepy/dimension.py
@@ -282,6 +282,12 @@ class CourtyardArea:
         )
 
 
+# calculate the radius of circumcircle
+def _longest_axis(points):
+    circ = _make_circle(points)
+    return circ[2] * 2
+
+
 class LongestAxisLength:
     """
     Calculates the length of the longest axis of object.
@@ -315,15 +321,10 @@ class LongestAxisLength:
     def __init__(self, gdf):
         self.gdf = gdf
         self.lal = gdf.apply(
-            lambda row: self._longest_axis(row.geometry.convex_hull.exterior.coords),
+            lambda row: _longest_axis(row.geometry.convex_hull.exterior.coords),
             axis=1,
         )
 
-    # calculate the radius of circumcircle
-    def _longest_axis(self, points):
-        circ = _make_circle(points)
-        return circ[2] * 2
-
 
 class AverageCharacter:
     """
diff --git a/momepy/intensity.py b/momepy/intensity.py
index 059fbc3..d8e9954 100644
--- a/momepy/intensity.py
+++ b/momepy/intensity.py
@@ -115,7 +115,7 @@ class AreaRatio:
 
         look_for = right[
             [right_unique_id, right_areas]
-        ]  # keeping only necessary columns
+        ].copy()  # keeping only necessary columns
         look_for.rename(index=str, columns={right_areas: "lf_area"}, inplace=True)
         objects_merged = left[[left_unique_id, left_areas]].merge(
             look_for, left_on=left_unique_id, right_on=right_unique_id
diff --git a/paper/paper.md b/paper/paper.md
index 2b9d130..b3d155a 100644
--- a/paper/paper.md
+++ b/paper/paper.md
@@ -33,7 +33,7 @@ The application of morphological tessellation and PySAL’s [@pysal2007] spatial
 
 Additionally, the ``utils`` module helps, among others, to pre-process data, by cleaning either the topology of street networks or of ancillary structures from the building footprint data, both needed to eliminate possible error later in the analysis.
 
-Internally, ``momepy`` is built on the GeoPandas Python package [@kelsey_jordahl_2019_3463125], using its GeoSeries and GeoDataFrame objects to store and handle large amounts of geospatial information. Under the hood ``momepy`` uses PySAL, mostly taking care of spatial weights matrices capturing the adjacency of elements of urban form. The graph module uses the capabilities of networkX [@SciPyProceedings_11] for the analysis of urban street networks. Basic Python knowledge is required to use ``momepy``, but exemplar Jupyter notebooks should provide enough information to allow using momepy with a standard GIS knowledge only.
+Internally, ``momepy`` is built on the GeoPandas Python package [@kelsey_jordahl_2019_3463125], using its GeoSeries and GeoDataFrame objects to store and handle large amounts of geospatial information. ``momepy`` uses PySAL under the hood, mostly to take care of spatial weights matrices capturing the adjacency of elements of urban form. The graph module uses the capabilities of networkX [@SciPyProceedings_11] for the analysis of urban street networks. Basic Python knowledge is required to use ``momepy``, but example Jupyter notebooks should provide enough information to allow using momepy with a standard GIS knowledge only.
 Momepy depends on Python geospatial libraries, so the recommended installation uses the community-led conda-forge channel of conda to ensure compatibility of all dependencies: ``conda install -c conda-forge momepy``. Eventually, ``pip install momepy`` is possible considering all dependency requirements are met.
 ``momepy``  is currently being used to measure the specific aspects of urban form that contribute to its resilience [@Feliciotti:2016vu], as part of *The Urban Form Resilience Project*, and to describe urban form using extensive number of morphometric characters (300+) aimed at recognition of homogenous areas as part of the doctoral research *The Urban Atlas: Developing tools and frameworks for quantitative analysis of urban form through Geographical Information Systems and big data*, both based at the University of Strathclyde, Glasgow. At the same time, ``momepy`` is being used as a tool for climate-induced risk assessment of Portuguese seashore streets, through a cooperation between the Universidade de Lisboa and the University of Strathclyde.
 


Test patch:
diff --git a/tests/test_shape.py b/tests/test_shape.py
index 547d9f8..d0b84af 100644
--- a/tests/test_shape.py
+++ b/tests/test_shape.py
@@ -186,6 +186,7 @@ class TestShape:
         assert self.df_buildings["elo"][0] == check
 
     def test_CentroidCorners(self):
+        self.df_buildings.loc[144] = [145, Point(0, 0).buffer(10), 0, 0]
         cc = mm.CentroidCorners(self.df_buildings)
         self.df_buildings["ccd"] = cc.mean
         self.df_buildings["ccddev"] = cc.std