Repository: martinfleis/momepy
Pull request #40 (2019-07-31 10:14:22)

REF: remove osmnx dependency
Remove `osmnx` dependency as it was used only once in tessellation. `_split_lines` is introduced instead. It is 10x faster than previous in division, but might be slower, but more precise in rtree selection of cells to cut.


Patch:
diff --git a/README.md b/README.md
index bcf56bd..13477d2 100644
--- a/README.md
+++ b/README.md
@@ -24,7 +24,7 @@ from GitHub:
 
 Please ensure that you have all dependencies installed as `momepy` setup
 currently does not specify dependency requirements.
-Momepy depends on the following packages: `geopandas`, `libpysal`, `osmnx` and `tqdm`. All of the packages are currently required, which will be changed in future.
+Momepy depends on the following packages: `geopandas`, `libpysal`, and `tqdm`. All of the packages are currently required, which will be changed in future.
 
 ## References
 - Dibble J, Prelorendjos A, Romice O, et al. (2017) On the origin of spaces: Morphometric foundations of urban form evolution. Environment and Planning B: Urban Analytics and City Science 24(1): 239980831772507–24.
diff --git a/docs/index.rst b/docs/index.rst
index 4ff5686..9d9118c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -39,7 +39,7 @@ from GitHub::
 
 Please ensure that you have all dependencies installed as ``momepy`` setup
 currently does not specify dependency requirements.
-Momepy depends on the following packages: ``geopandas``, ``libpysal``, ``osmnx``
+Momepy depends on the following packages: ``geopandas``, ``libpysal``
 and ``tqdm``. All of the packages are currently required, which will be
 changed in future.
 
diff --git a/docs/install.rst b/docs/install.rst
index 1cde9ba..3e9f63b 100644
--- a/docs/install.rst
+++ b/docs/install.rst
@@ -9,7 +9,7 @@ from GitHub via PIP::
 
 Please ensure that you have all dependencies installed as ``momepy`` setup
 currently does not specify dependency requirements.
-Momepy depends on the following packages: ``geopandas``, ``libpysal``, ``osmnx``
+Momepy depends on the following packages: ``geopandas``, ``libpysal``
 and ``tqdm``. All of the packages are currently required, which will be
 changed in future.
 
@@ -21,6 +21,6 @@ to install all dependencies via `conda` from `conda-forge`::
 
     conda config --add channels conda-forge  # ensure that conda-forge is in your list of channels on the top
     conda config --set channel_priority strict  # strict priority to install all from conda-forge
-    conda create --name momepy_env geopandas libpysal osmnx tqdm
+    conda create --name momepy_env geopandas libpysal tqdm
     conda activate momepy_env
     pip install git+git://github.com/martinfleis/momepy.git
diff --git a/momepy/elements.py b/momepy/elements.py
index c5aa41d..1bca5d5 100644
--- a/momepy/elements.py
+++ b/momepy/elements.py
@@ -14,7 +14,6 @@ import numpy as np
 from scipy.spatial import Voronoi
 from shapely.geometry import Point, LineString, Polygon, MultiPolygon
 import shapely
-import osmnx as ox
 import operator
 from libpysal.weights import Queen
 
@@ -37,7 +36,7 @@ def buffered_limit(gdf, buffer=100):
 
     """
     study_area = gdf.copy()
-    study_area['geometry'] = study_area.buffer(100)
+    study_area['geometry'] = study_area.buffer(buffer)
     study_area['diss'] = 1
     built_up_df = study_area.dissolve(by='diss')
     built_up = built_up_df.geometry[1]
@@ -132,6 +131,30 @@ def _regions(voronoi_diagram, unique_id, ids, crs):
     return regions_gdf
 
 
+def _split_lines(polygon, distance, crs):
+    dense = _densify(polygon, distance)
+    boundary = dense.boundary
+
+    def _pair(coords):
+        '''Iterate over pairs in a list -> pair of points '''
+        for i in range(1, len(coords)):
+            yield coords[i - 1], coords[i]
+
+    segments = []
+    if boundary.type == 'LineString':
+        for seg_start, seg_end in _pair(boundary.coords):
+            segment = LineString([seg_start, seg_end])
+            segments.append(segment)
+    elif boundary.type == 'MultiLineString':
+        for ls in boundary:
+            for seg_start, seg_end in _pair(ls.coords):
+                segment = LineString([seg_start, seg_end])
+                segments.append(segment)
+
+    cutted = gpd.GeoSeries(segments, crs=crs)
+    return cutted
+
+
 def _cut(tessellation, limit, unique_id):
     """
     Cut tessellation by the limit (Multi)Polygon.
@@ -139,9 +162,8 @@ def _cut(tessellation, limit, unique_id):
     ADD: add option to delete everything outside of limit. Now it keeps it.
     """
     # cut infinity of voronoi by set buffer (thanks for script to Geoff Boeing)
-    print('Preparing buffer zone for edge resolving (quadrat cut)...')
-    geometry = limit.boundary
-    geometry_cut = ox.quadrat_cut_geometry(geometry, quadrat_width=100)
+    print('Preparing buffer zone for edge resolving...')
+    geometry_cut = _split_lines(limit, 100, tessellation.crs)
 
     print('Building R-tree...')
     sindex = tessellation.sindex


Test patch:
diff --git a/tests/test_elements.py b/tests/test_elements.py
index a391699..8e3487a 100644
--- a/tests/test_elements.py
+++ b/tests/test_elements.py
@@ -46,3 +46,11 @@ class TestElements:
         self.df_buildings['nID'] = mm.get_network_id(self.df_buildings, self.df_streets, 'uID', 'nID')
         ids = mm.get_node_id(self.df_buildings, nodes, edges, 'nodeID', 'nID')
         assert not ids.isna().any()
+
+    def test__split_lines(self):
+        large = mm.buffered_limit(self.df_buildings, 100)
+        dense = mm.elements._split_lines(large, 100, self.df_buildings.crs)
+        small = mm.buffered_limit(self.df_buildings, 30)
+        dense2 = mm.elements._split_lines(small, 100, self.df_buildings.crs)
+        assert len(dense) == 273
+        assert len(dense2) == 667