Repository: martinfleis/momepy
Pull request #52 (2019-08-09 15:57:36)

ENH: snap_street_network_edge allow limit to avoid dissolve
Dissolve part of `snap_street_network_edge` might not be necessary as `limit` in `tessellation` is basically the same geometry we need. This PR allows passing it directly.

codecov[bot]: # [Codecov](https://codecov.io/gh/martinfleis/momepy/pull/52?src=pr&el=h1) Report
> Merging [#52](https://codecov.io/gh/martinfleis/momepy/pull/52?src=pr&el=desc) into [master](https://codecov.io/gh/martinfleis/momepy/commit/8580ec62112781a7f49028afdedd965dc6f62f33?src=pr&el=desc) will **increase** coverage by `0.02%`.
> The diff coverage is `84.61%`.

[![Impacted file tree graph](https://codecov.io/gh/martinfleis/momepy/pull/52/graphs/tree.svg?width=650&token=i04999UxkK&height=150&src=pr)](https://codecov.io/gh/martinfleis/momepy/pull/52?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master      #52      +/-   ##
==========================================
+ Coverage   91.27%   91.29%   +0.02%     
==========================================
  Files           9        9              
  Lines        2155     2160       +5     
==========================================
+ Hits         1967     1972       +5     
  Misses        188      188
```


| [Impacted Files](https://codecov.io/gh/martinfleis/momepy/pull/52?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [momepy/distribution.py](https://codecov.io/gh/martinfleis/momepy/pull/52/diff?src=pr&el=tree#diff-bW9tZXB5L2Rpc3RyaWJ1dGlvbi5weQ==) | `94.11% <ø> (ø)` | :arrow_up: |
| [momepy/utils.py](https://codecov.io/gh/martinfleis/momepy/pull/52/diff?src=pr&el=tree#diff-bW9tZXB5L3V0aWxzLnB5) | `77.84% <84.61%> (+0.32%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/martinfleis/momepy/pull/52?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/martinfleis/momepy/pull/52?src=pr&el=footer). Last update [8580ec6...c6ee91a](https://codecov.io/gh/martinfleis/momepy/pull/52?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/momepy/distribution.py b/momepy/distribution.py
index 6c42f97..ad83669 100644
--- a/momepy/distribution.py
+++ b/momepy/distribution.py
@@ -460,7 +460,7 @@ def mean_interbuilding_distance(gdf, spatial_weights, unique_id, spatial_weights
     Calculate the mean interbuilding distance within x topological steps
 
     Interbuilding distances are calculated between buildings on adjacent cells based on `spatial_weights`,
-    while the extend is defined in `spatial_weights_higher`.
+    while the extent is defined in `spatial_weights_higher`.
 
     .. math::
 
@@ -653,7 +653,8 @@ def building_adjacency(gdf, spatial_weights_higher, unique_id, spatial_weights=N
     Calculate the level of building adjacency
 
     Building adjacency reflects how much buildings tend to join together into larger structures.
-    It is calculated as a ratio of joined built-up structures and buildings within k topological steps.
+    It is calculated as a ratio of joined built-up structures and buildings within
+    the extent defined in `spatial_weights_higher`.
 
     .. math::
 
@@ -662,13 +663,13 @@ def building_adjacency(gdf, spatial_weights_higher, unique_id, spatial_weights=N
     ----------
     gdf : GeoDataFrame
         GeoDataFrame containing objects to analyse
-    spatial_weights_higher : libpysal.weights, optional
+    spatial_weights_higher : libpysal.weights
         spatial weights matrix
     unique_id : str
         name of the column with unique id used as spatial_weights index
     spatial_weights : libpysal.weights, optional
         spatial weights matrix - If None, Queen contiguity matrix will be calculated
-        based on gdf. It is to denote adjacent buildings (note: based on index, not ID).
+        based on gdf. It is to denote adjacent buildings (note: based on unique ID).
 
     Returns
     -------
diff --git a/momepy/utils.py b/momepy/utils.py
index 98f3b75..ca801f8 100644
--- a/momepy/utils.py
+++ b/momepy/utils.py
@@ -217,7 +217,7 @@ def preprocess(buildings, size=30, compactness=True, islands=True):
     If feature area is smaller than set size it will be a) deleted if it does not
     touch any other feature; b) will be joined to feature with which it shares the
     longest boundary. If feature is fully within other feature, these will be joined.
-    If feature's circular compactness (:py:func:`momepy.shape.circular compactness`)
+    If feature's circular compactness (:py:func:`momepy.circular_compactness`)
     is < 0.2, it will be joined to feature with which it shares the longest boundary.
     Function does two loops through.
 
@@ -409,7 +409,7 @@ def network_false_nodes(gdf):
     return streets
 
 
-def snap_street_network_edge(edges, buildings, tolerance_street, tessellation=None, tolerance_edge=None):
+def snap_street_network_edge(edges, buildings, tolerance_street, tessellation=None, tolerance_edge=None, edge=None):
     """
     Fix street network before performing blocks()
 
@@ -424,9 +424,11 @@ def snap_street_network_edge(edges, buildings, tolerance_street, tessellation=No
     tolerance_street : float
         tolerance in snapping to street network (by how much could be street segment extended).
     tessellation : GeoDataFrame (default None)
-        GeoDataFrame containing morphological tessellation
+        GeoDataFrame containing morphological tessellation. If edge is not passed it will be used as edge.
     tolerance_edge : float (default None)
         tolerance in snapping to edge of tessellated area (by how much could be street segment extended).
+    edge : Polygon
+        edge of area covered by morphological tessellation (same as `limit` in :py:func:`momepy.tessellation`)
 
     Returns
     -------
@@ -574,9 +576,16 @@ def snap_street_network_edge(edges, buildings, tolerance_street, tessellation=No
     sindex = network.sindex
     print('Building R-tree for buildings...')
     bindex = buildings.sindex
-    if tessellation is not None:
-        print('Dissolving tesselation...')
-        geometry = tessellation.geometry.unary_union.boundary
+
+    def _get_geometry():
+        if edge is not None:
+            return edge.boundary
+        if tessellation is not None:
+            print('Dissolving tesselation...')
+            return tessellation.geometry.unary_union.boundary
+        return None
+
+    geometry = _get_geometry()
 
     print('Snapping...')
     # iterating over each street segment
@@ -605,22 +614,22 @@ def snap_street_network_edge(edges, buildings, tolerance_street, tessellation=No
         # start connected, extend  end
         elif first and not second:
             if extend_line(tolerance_street, idx) is False:
-                if tessellation is not None:
+                if geometry is not None:
                     extend_line_edge(tolerance_edge, idx)
         # end connected, extend start
         elif not first and second:
             l_coords.reverse()
             if extend_line(tolerance_street, idx) is False:
-                if tessellation is not None:
+                if geometry is not None:
                     extend_line_edge(tolerance_edge, idx)
         # unconnected, extend both ends
         elif not first and not second:
             if extend_line(tolerance_street, idx) is False:
-                if tessellation is not None:
+                if geometry is not None:
                     extend_line_edge(tolerance_edge, idx)
             l_coords.reverse()
             if extend_line(tolerance_street, idx) is False:
-                if tessellation is not None:
+                if geometry is not None:
                     extend_line_edge(tolerance_edge, idx)
         else:
             print('Something went wrong.')


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index a2f66f2..1533a32 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -77,5 +77,8 @@ class TestUtils:
     def test_snap_street_network_edge(self):
         snapped = mm.snap_street_network_edge(self.df_streets, self.df_buildings, 20, self.df_tessellation, 70)
         snapped_nonedge = mm.snap_street_network_edge(self.df_streets, self.df_buildings, 20)
+        snapped_edge = mm.snap_street_network_edge(self.df_streets, self.df_buildings, 20,
+                                                   tolerance_edge=70, edge=mm.buffered_limit(self.df_buildings, buffer=50))
         assert sum(snapped.geometry.length) == 5980.041004739525
+        assert sum(snapped_edge.geometry.length) == 5980.718889937014
         assert sum(snapped_nonedge.geometry.length) < 5980.041004739525