Repository: martinfleis/momepy (python)

Issue:
No support for LINESTRING Z `nx_to_gdf` fails
Nice lib! 

I noticed that you wont be able to convert a network back to a gdf if it has elevation data `gdf.has_z == True` . This is due to how `_points_to_gdf` is implemented ->  

```
def _points_to_gdf(net, spatial_weights):
    """
    Generate point gdf from nodes.
    Helper for nx_to_gdf.
    """
    node_xy, node_data = zip(*net.nodes(data=True))
    gdf_nodes = gpd.GeoDataFrame(
        list(node_data), geometry=[Point(i, j) for i, j in node_xy]
    )
    gdf_nodes.crs = net.graph["crs"]
    return gdf_nodes
```

Either some changes has to be made to the code or there should be some mention of this in docs. 
potentially one could add a check  and remove it on the fly.  I had to do this of 3D->2D conversion (`LINESTRING Z` -> `LINESTRING`): 

```
gdf.geometry.apply(lambda geom: shapely.wkb.loads(shapely.wkb.dumps(geom, output_dimension=2)))
``` 




Pull request description:
BUG: LineString Z support in nx_to_gdf
Fixes #142 

Make sure Z coordinate is supported in `gdf_to_nx` and `nx_to_gdf`. If LineString Z is passed to `gdf_to_nx`, Point Z is returned for nodes from `nx_to_gdf`.

Patch:
diff --git a/momepy/utils.py b/momepy/utils.py
index 7112e3c..658fa77 100644
--- a/momepy/utils.py
+++ b/momepy/utils.py
@@ -222,9 +222,11 @@ def _points_to_gdf(net, spatial_weights):
     Helper for nx_to_gdf.
     """
     node_xy, node_data = zip(*net.nodes(data=True))
-    gdf_nodes = gpd.GeoDataFrame(
-        list(node_data), geometry=[Point(i, j) for i, j in node_xy]
-    )
+    if len(node_xy[0]) == 2:
+        geometry = [Point(i, j) for i, j in node_xy]
+    elif len(node_xy[0]) == 3:
+        geometry = [Point(i, j, k) for i, j, k in node_xy]
+    gdf_nodes = gpd.GeoDataFrame(list(node_data), geometry=geometry)
     gdf_nodes.crs = net.graph["crs"]
     return gdf_nodes
 


Test patch:
diff --git a/tests/test_utils.py b/tests/test_utils.py
index 40f5825..e1fd063 100644
--- a/tests/test_utils.py
+++ b/tests/test_utils.py
@@ -5,6 +5,8 @@ import networkx
 import numpy as np
 import pytest
 
+from shapely.geometry import LineString
+
 
 class TestUtils:
     def setup_method(self):
@@ -76,6 +78,15 @@ class TestUtils:
         with pytest.raises(KeyError):
             mm.nx_to_gdf(G)
 
+        # LineString Z
+        line1 = LineString([(0, 0, 0), (1, 1, 1)])
+        line2 = LineString([(0, 0, 0), (-1, -1, -1)])
+        gdf = gpd.GeoDataFrame(geometry=[line1, line2])
+        G = mm.gdf_to_nx(gdf)
+        pts, lines = mm.nx_to_gdf(G)
+        assert pts.iloc[0].geometry.wkt == "POINT Z (0 0 0)"
+        assert lines.iloc[0].geometry.wkt == "LINESTRING Z (0 0 0, 1 1 1)"
+
     def test_limit_range(self):
         assert list(mm.limit_range(range(10), rng=(25, 75))) == [2, 3, 4, 5, 6, 7]
         assert list(mm.limit_range(range(10), rng=(10, 90))) == [1, 2, 3, 4, 5, 6, 7, 8]