task: metadata: install: pip install -e .[plus]
patch: diff --git a/momepy/preprocessing.py b/momepy/preprocessing.py
index 196331a..839cdca 100644
--- a/momepy/preprocessing.py
+++ b/momepy/preprocessing.py
@@ -166,8 +166,8 @@ def preprocess(
 def remove_false_nodes(gdf):
     """
     Clean topology of existing LineString geometry by removal of nodes of degree 2.
-
-    Returns the original gdf if there's no node of degree 2.
+    Returns the original gdf if there's no node of degree 2. Some geometries may
+    be forced to 2D where a Z coordinate is present.
 
     Parameters
     ----------
@@ -178,7 +178,7 @@ def remove_false_nodes(gdf):
     -------
     gdf : GeoDataFrame, GeoSeries
 
-    See also
+    See Also
     --------
     momepy.extend_lines
     momepy.close_gaps
@@ -247,14 +247,16 @@ def remove_false_nodes(gdf):
             ),
             lines=False,
         )
+
         loops = combined[combined.is_ring]
+
         node_ix, loop_ix = loops.sindex.query(nodes.geometry, predicate="intersects")
         for ix in np.unique(loop_ix):
             loop_geom = loops.geometry.iloc[ix]
             target_nodes = nodes.geometry.iloc[node_ix[loop_ix == ix]]
             if len(target_nodes) == 2:
                 node_coords = shapely.get_coordinates(target_nodes)
-                coords = np.array(loop_geom.coords)
+                coords = shapely.get_coordinates(loop_geom)
                 new_start = (
                     node_coords[0]
                     if (node_coords[0] != coords[0]).all()

problem_statement: remove_false_nodes throws an unexpected error
### Describe the problem

When trying to remove false nodes from a [street network obtained from Ordnance Survey](https://www.ordnancesurvey.co.uk/products/os-open-roads) I get the following error:

```
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
Cell In[56], line 1
----> 1 streets_BR = mm.remove_false_nodes(streets_BR)
      2 len(streets_BR)

File [~\AppData\Local\miniconda3\envs\ox\Lib\site-packages\momepy\preprocessing.py:260](http://localhost:8889/lab/tree/OneDrive%20-%20University%20of%20Strathclyde/FB/Birmingham/_scripts/~/AppData/Local/miniconda3/envs/ox/Lib/site-packages/momepy/preprocessing.py#line=259), in remove_false_nodes(gdf)
    256 node_coords = shapely.get_coordinates(target_nodes)
    257 coords = np.array(loop_geom.coords)
    258 new_start = (
    259     node_coords[0]
--> 260     if (node_coords[0] != coords[0]).all()
    261     else node_coords[1]
    262 )
    263 new_start_idx = np.where(coords == new_start)[0][0]
    264 rolled_coords = np.roll(coords[:-1], -new_start_idx, axis=0)

ValueError: operands could not be broadcast together with shapes (2,) (3,)
```

Any solution?

### Steps to reproduce

```
import geopandas as gpd
import momepy as mm
import pandas as pd

sk = gpd.read_file('data/OS Open Roads/SK_RoadLink.shp')
so = gpd.read_file('data/OS Open Roads/SO_RoadLink.shp')
sp = gpd.read_file('data/OS Open Roads/SP_RoadLink.shp')

streets = pd.concat([sk, so, sp], ignore_index=True)

streets = streets.reset_index(drop=True).drop_duplicates('identifier')

bound_BR = gpd.read_file('data/boundary.shp')

streets_BR = streets[streets.within(bound_BR)]

streets_BR = mm.remove_false_nodes(streets_BR)
```

### Versions of your packages

momepy = 0.8.0
geopandas = 1.0.1
libpysal = 4.12.1
networkx = 3.3

### Your operating system

Windows 10

### Additional context

_No response_
repo: pysal/momepy
test_cmd: pytest --no-header -rA --tb=line --color=no -p no:cacheprovider -W ignore::DeprecationWarning
test_patch: diff --git a/momepy/tests/test_preprocess.py b/momepy/tests/test_preprocess.py
index 98f8ee4..0180337 100644
--- a/momepy/tests/test_preprocess.py
+++ b/momepy/tests/test_preprocess.py
@@ -2,7 +2,7 @@ import geopandas as gpd
 import numpy as np
 import pytest
 from geopandas.testing import assert_geodataframe_equal
-from shapely import affinity
+from shapely import affinity, force_3d
 from shapely.geometry import LineString, MultiPoint, Point, Polygon
 from shapely.ops import polygonize
 
@@ -67,6 +67,16 @@ class TestPreprocessing:
         df = self.df_streets.drop([4, 7, 17, 22])
         assert_geodataframe_equal(df, mm.remove_false_nodes(df))
 
+        # check 3d coords in loop
+        line_1 = LineString((Point(1, 1), Point(2, 2)))
+        line_2 = LineString((Point(2, 2), Point(3, 3)))
+        line_3 = force_3d(LineString((Point(2, 2), Point(1, 2))))
+        line_4 = LineString((Point(1, 2), Point(1, 3)))
+        line_5 = LineString((Point(1, 3), Point(2, 2)))
+        with_3d = np.array([line_1, line_2, line_3, line_4, line_5])
+        no_3d = mm.remove_false_nodes(with_3d)
+        assert len(no_3d) == 3
+
     def test_CheckTessellationInput(self):
         df = self.df_buildings
         df.loc[144, "geometry"] = Polygon([(0, 0), (0, 1), (1, 0)])