How to find out which pipes are facing wrong direction

Viewed 340

I am trying to calculate the district heating system. I get the info from shapefiles – for pipes I have a geometry of linestrings with start and end coordinates. I created a geopandas dataframe:

+-------+----------------------------------------------------------------------------------------------------------------------+
|       |                                                                                                                      |
| ID    | geometry                                                                                                             |
+-------+----------------------------------------------------------------------------------------------------------------------+
|       |                                                                                                                      |
| 0     | LINESTRING (1679896.423 5802688.586,   1679896.034 5802688.745)                                                      |
+-------+----------------------------------------------------------------------------------------------------------------------+
|       |                                                                                                                      |
| 1     | LINESTRING (1679896.034 5802688.745, 1679892.419 5802689.660)                                                        |
+-------+----------------------------------------------------------------------------------------------------------------------+
|       |                                                                                                                      |
| 2     | LINESTRING (1679908.185 5802692.533, 1679911.830 5802691.936,   1679927.258 5802689.562, 1679945.682 5802686.042)    |
+-------+----------------------------------------------------------------------------------------------------------------------+
|       |                                                                                                                      |
| 3     | LINESTRING (1679945.667 5802685.478, 1679945.653 5802685.247)                                                        |
+-------+----------------------------------------------------------------------------------------------------------------------+
|       |                                                                                                                      |
| 4     | ….                                                                                                                   |
+-------+----------------------------------------------------------------------------------------------------------------------+

For calculations, I have to determine all the connections between the lines (current pipe has to have defined pipe previous and pipe next). I did it in a way where I created a starting point and ending point of a linestring, and merged dataframe with itself (paired ending points with starting points). Problem is, some of the linestrings (about 1/5 of all) are facing the wrong direction, so I simply get NaN results for them (because now ending point of current pipe doesn’t touch starting point of next, but simply another ending point).

+-------+-------------+-----------------+------------------------------------+------------------------------------+--------------------------------------------------------------------+
|       |             |                 |                                    |                                    |                                                                    |
| ID    | PipeNext    | PipePrevious    | StartPoint                         | EndPoint                           | Geometry                                                           |
+-------+-------------+-----------------+------------------------------------+------------------------------------+--------------------------------------------------------------------+
|       |             |                 |                                    |                                    |                                                                    |
| 1     | 2           | 477             | POINT (1679896.423 5802688.586)    | POINT (1679896.034 5802688.745)    | LINESTRING (1679896.423 5802688.586,   1679896.034 5802688.745)    |
|       |             |                 |                                    |                                    |                                                                    |
|       |             |                 |                                    |                                    |                                                                    |
+-------+-------------+-----------------+------------------------------------+------------------------------------+--------------------------------------------------------------------+
|       |             |                 |                                    |                                    |                                                                    |
| 7     | 8           | 477             | POINT (1679896.423 5802688.586)    | POINT (1679898.491 5802694.799)    | LINESTRING (1679896.423 5802688.586, 1679898.491 5802694.799)      |
|       |             |                 |                                    |                                    |                                                                    |
|       |             |                 |                                    |                                    |                                                                    |
+-------+-------------+-----------------+------------------------------------+------------------------------------+--------------------------------------------------------------------+
|       |             |                 |                                    |                                    |                                                                    |
| 3     | 467, 468    | Nan             | POINT (1679077.671 5802691.617)    | POINT (1679060.406 5802674.810)    | LINESTRING (1679077.671 5802691.617,   1679060.406 5802674.810)    |
|       |             |                 |                                    |                                    |                                                                    |
|       |             |                 |                                    |                                    |                                                                    |
+-------+-------------+-----------------+------------------------------------+------------------------------------+--------------------------------------------------------------------+

Additional problem is, that there are examples where there are 10 pipes consecutively facing wrong direction (so between them, I actually get results, but they are wrong) – another problem is, that sometimes PipeNext is NaN simply because the current line is the end line. Is there any way to be able to determine the pipes facing the wrong direction? If not, is it possible to make the direction of the lines visible on plot?

Edit - added current code:

import pandas as pd
import geopandas as gpd
import folium

import shapefile
import matplotlib.pyplot as plt
from geopandas import GeoDataFrame
import contextily as ctx
df_pipes=gpd.read_file("DO_Hrastnik_odsek.shp")
df_user=gpd.read_file("DO_Hrastnik_objekt.shp")

df_pipes.crs = {'init': 'epsg:3912'}
df_pipes=df_pipes.to_crs("EPSG:3857")
df_user.crs = {'init': 'epsg:3912'}
df_user=df_user.to_crs("EPSG:3857")

df_pipes["ID"] = df_pipes.index + 1
df_pipes['ID']=df_pipes['ID'].astype(str)

df_pipes=df_pipes[["geometry","ID","LENGTH_3D"]].copy()

from shapely.geometry import Point, LineString

df_pipes["node_ups"] = gpd.GeoSeries([Point(list(pt['geometry'].coords)[0]) for i,pt in df_pipes.iterrows()])
df_pipes["node_dws"] = gpd.GeoSeries([Point(list(pt['geometry'].coords)[-1]) for i,pt in df_pipes.iterrows()])

tmp = df_pipes[['ID', 'node_dws', 'node_ups']]

df_connections=pd.merge(tmp, tmp, left_on='node_dws', right_on='node_ups',how="outer")
df_connections=pd.merge(df_connections, tmp, left_on='node_ups_x', right_on='node_dws',how="outer")

df_connections.drop(['node_dws_y', 'node_ups_y','node_dws', 'node_ups'], axis=1, inplace=True)

df_connections = df_connections[df_connections['ID_x'].notna()]

df_connections = df_connections.rename(columns={'ID_x': 'ID_pipe', 'node_dws_x': 'node_dws','node_ups_x': 'node_ups', 'ID_y': 'pipeNext','ID': 'pipePrevious'})

tmp = df_connections[['ID_pipe', 'pipeNext', 'pipePrevious']]
df_connections=pd.merge(tmp, df_pipes, left_on='ID_pipe', right_on='ID',how="outer")
1 Answers

Your idea from the comments to check where the endpoints meet is the way to go and it isn't as complex as you say. Start at the starting pipe of your network (where all the water comes from – I assume that's a single pipe) and work your way down the network with a recursive function:

from shapely.geometry import Point, LineString # You can delete your own code after this line.

# df.apply is faster than iterrows, so I rewrote the following two lines
df_pipes['node_ups'] = df_pipes.geometry.apply(lambda linestring: Point(list(linestring.coords)[0])
df_pipes['node_dws'] = df_pipes.geometry.apply(lambda linestring: Point(list(linestring.coords)[-1])

def check_downstream(pipe_id, pipes):
    """Set next and previous pipe ID for all downstream pipes"""

    endpoint = pipes.loc[pipe_id, 'node_dws']
    next_ids = list(pipes['node_ups' == endpoint]['ID'])
    reversed_ids = list(pipes[(pipes['node_dws'] == endpoint) & (pipes['ID'] != pipe_id)]['ID'])
    for rev_id in reversed_ids:
        pipes.loc[rev_id, 'node_ups'], pipes.loc[rev_id, 'node_dws'] = pipes.loc[rev_id, 'node_dws'], pipes.loc[rev_id, 'node_ups']
    next_ids = next_ids + reversed_ids
    for nxt_id in next_ids:
        pipes.loc[pipe_id, 'pipeNext'] = nxt_id
        pipes.loc[nxt_id, 'pipePrevious'] = pipe_id
        check_downstream(nxt_id, pipes) # This is the recursive function call.


df_connections = df_pipes.copy()
df_connections['pipePrevious'] = 'undetermined'
df_connections['pipeNext'] = 'undetermined'

first_id = 99 # or whatever it is
check_downstream(first_id, df_connections)

Note that this does not reverse the Linestrings themselves but you could do that too if you need to.

Related