Python 도로네트워크 구현 - 교차점 노드 가져오기

범모 ( bbeom dev) ·2022년 9월 9일
1
post-thumbnail

Python 도로네트워크 구현 - 교차점 노드 가져오기

최적 경로 알고리즘을 사용하기 위해서 도로 노드와 장소를 값으로 가지는 그래프 자료구조를 만들어야 한다. 제일 먼저 교차로(노드)들의 좌표를 가져와야겠다고 생각했다.

😶도로네트워크를 시각화하는 라이브러리가 있을까?

도로네트워크를 시각화할 때 필요한 데이터라면 내가 필요한 데이터랑 같을 것 같아서 구글링 하던중 OSMnx라는 라이브러리를 찾았다. 코랩에 OSMnx.ipynb가 있어서 사용했고, 간단하게 GIS 데이터를 시각화 해봤는데 교점(노드)가 표시되어 있다. 이 노드를 데이터로 뽑아낼 수 있지 않을까라는 생각을 했다.

adr = '경남대학교, 무학로, 신월동, 마산합포구, 창원시, 경상남도, 51772, 대한민국' 
G = ox.graph_from_address(address = adr, dist=1000, 
dist_type='bbox', network_type='all') 
fig, ax = ox.plot_graph(G) 

adr = '경남대학교, 무학로, 신월동, 마산합포구, 창원시, 경상남도, 51772, 대한민국'
data = ox.geometries.geometries_from_address(adr, dist=1000, tags= {'highway':'residential'})


https://osmnx.readthedocs.io/en/stable/osmnx.html

OSMnx reference를 참고했고 테스트를 해보기 위해서 osm 태그인 residential을 넣으면 주거지역의 교차점(노드)들이 좌표로 나오는 모습을 볼 수 있다.

😤교차점 시각화 (시행착오)

data['points'] = data.apply(lambda x: [y for y in x['geometry'].coords], axis=1)
m = folium.Map(location=[35.18009,128.55603], zoom_start = 16)
for num,i in enumerate(data.index):
    for num_t,i in enumerate(data['points'][num - 1]): 
        point = list(data['points'][num - 1][num_t - 1])
				point.reverse()
        folium.Marker(location = point).add_to(m)

data[’geometry’]는 linestring으로 되어있길래 일단 전부 point로 바꿔주고, 해당 포인트를 맵에 표시했다.

도로의 교차점 노드만이 표시되어야 하는데 linestring의 좌표를 다 가져와서 그런지 이상하다. ( highway : residential은 잘못되지 않았음.

G = ox.graph_from_place('경남대학교, 무학로, 신월동, 마산합포구, 창원시, 경상남도, 51772, 대한민국', network_type='all')
a = ox.utils_graph.graph_to_gdfs(G, nodes=True, edges=True)
df = pd.DataFrame(a[0]) # 튜플에서 노드의 좌표가 있는부분만 추출하기 위해서 a[0]을 사용 

OSMnx 내장함수인 graph_to_gdfs를 통해서 nodes 와 edges를 가져오면 튜플로 반환된다. 그 튜플을 DataFrame에 저장해서 시각화해보았다.

m = folium.Map(location=[35.18009,128.55603], zoom_start = 16) 
for num, i in enumerate(df.index): 
    y_p = df['y'].iloc[num-1]
    x_p = df['x'].iloc[num-1]
    folium.Marker(location = (y_p, x_p)).add_to(m)
m

교차점들만 추출된 것을 확인할 수 있다.

이때까지만 해도 nodes와 edges를 이용해서 직접 그래프 자료구조를 만들 생각을 하고 있었다.

그러나 관점을 바꿔보면 OSMnx는 이미 최단 경로를 구하는 내장함수가 있다. 그렇다면 그래프는 이미 만들어져 있을 것이고 가져와서 쓰면 되겠다고 생각했다.


😀도로네트워크 vertex, edge 추출 ( 결론 )

OSMnx 라이브러리 사용하면 NetworkX 라이브러리 클래스로 노드 정보 반환된다.

import geopandas as gpd 
import pandas as pd 
import osmnx as ox 
import numpy as np 
import folium 
import matplotlib.pyplot as plt 
import networkx as nx

G = ox.graph_from_place('마산합포구, 창원시, 경상남도, 대한민국', network_type='all')
nodes = G.nodes()
edges = G.edges()
print(type(G))

networkx로 이루어진것을 볼 수 있고 클래스 내장함수를 사용하여 G에서 nodes와 edges를 저장했다. nodes에는 정점들의 osmid값이 저장되어 있다.

nodes.data() 를 통해서 해당 노드의 osmid값이랑 좌표를 추출할 수 있다.

m = folium.Map(location=[35.18009,128.55603], zoom_start = 16) 
node_data = nodes.data() 

for num, node_index in enumerate(nodes): 
    y_p = node_data[node_index]['y']
    x_p = node_data[node_index]['x']
    folium.Marker(location = (y_p, x_p)).add_to(m) 
m

위의 코드를 통해서 시각화를 해보았다.

이제 노드에 연결된 인근 노드들을 추출해봐야 한다.

Graph.neighbors - NetworkX 2.8.6 documentation

NetworkX 공식 레퍼런스에 보면 연결된 인근 노드들을 반환해주는 함수가 있다.

G.neighbors(n) 을 통해서 인근 노드들의 iterator 값을 받고 반복문으로 돌려주면 된다.

도로에 있는 모든 노드들의 인근 노드들을 추출해보았다.

list_neighbors = list(map(G.neighbors, nodes))
for v in nodes:
  iter = G.neighbors(v) # 정점별 인접 노드 추출
  for adj in iter:
    print(adj, end=' ') # 정점별 인접 노드 출력
  print()

이렇게 도로의 노드, 간선, 인근 노드들을 추출해보았다.
다음 단계는 간선들의 좌표와 속성을 데이터프레임으로 저장하는 작업을 할 것이다.

profile
AI로 사회에 긍정적인 영향을 줄 수 있는 개발자가 되기 위해 성장하고 있습니다.

0개의 댓글