< 교통망 시각화를 통한 교통량 파악(잠실) >
목적 : 서울 시내 교통량의 시간대별, 종류별 분류
방법 : 1. 교통망 시각화하기
2. CCTV 분석을 통한 교통량 파악
3. 교통망에 교통량 더하기
4. 통계 및 데이터 분석
[Step 1] 교통망 시각화하기
교통망의 시각화는 아래 사이트를 참고하였습니다.
정확히 각 패키지와 코드의 구성은 알 수 없지만, 어렴풋이 이해하려 노력했습니다.
https://junpyopark.github.io/road-network-construction-1/
박준표 님께서 올려주신 코드를 조금만 수정하면 자신이 원하는 부분의 원하는 만큼의 데이터를 추출해낼 수 있습니다. (시간이 매우 오래 걸립니다. 자세한 내용은 해당 github을 참고하시기 바랍니다.)
아래와 같이 입력을 하면 제가 사는 잠실 주위의 노드들을 연결하여 확인할 수 있습니다. 다만 저는 중첩되는 노드들을 간소화해야 하는데 이 방법은 더 찾아봐야할 것 같습니다.
import shapefile #the pyshp module : Should install pyshp module.
import pandas as pd
from pyproj import Proj, transform #Should install pyproj module.
# read data (Copy all files from nodelink into nodelink folder: I made it.)
# using old_data
shp_path_node = 'C:/Users/Sunghyun/Python3/Toy Project1_도로망 시각화하기/전국표준노드링크/MOCT_NODE.shp'
sf_node = shapefile.Reader(shp_path_node)
shp_path_link = 'C:/Users/Sunghyun/Python3/Toy Project1_도로망 시각화하기/전국표준노드링크/MOCT_LINK.shp'
sf_link = shapefile.Reader(shp_path_link)
# construct pandas dataframe
#grab the shapefile's field names
# node
fields_node = [x[0] for x in sf_node.fields][1:]
records_node = sf_node.records()
shps = [s.points for s in sf_node.shapes()] # node has coordinate data.
# link
fields_link = [x[0] for x in sf_link.fields][1:]
records_link = sf_link.records()
#write the records into a dataframe
#node
node_dataframe = pd.DataFrame(columns=fields_node, data=records_node)
#add the coordinate data to a column called "coords"
node_dataframe = node_dataframe.assign(coords=shps)
# link
link_dataframe = pd.DataFrame(columns=fields_link, data=records_link)
"""
광역/특별시의 권역코드(STNL_REG)는 다음과 같습니다.
서울 : 100 ~ 124
부산 : 130 ~ 145
대구 : 150 ~ 157
인천 : 161 ~ 170
광주 : 175 ~ 179
대전 : 183 ~ 187
울산 : 192 ~ 196
"""
# Data restriction
range_STNL_REG=range(123, 124) # STNL_REG for Seoul
df_node = pd.DataFrame()
df_link = pd.DataFrame()
for ii in range_STNL_REG:
res_node = node_dataframe[node_dataframe['STNL_REG'] == str(ii) ] # STNL_REG is not int.
res_link = link_dataframe[link_dataframe['STNL_REG'] == str(ii) ]
df_node = pd.concat([df_node,res_node],ignore_index=True) # marge data
df_link = pd.concat([df_link,res_link],ignore_index=True)
# Change node name in korean
for idx,row in df_node.iterrows():
if type(row['NODE_NAME']) == bytes :
# row['NODE_NAME'] = row['NODE_NAME'].decode('euc_kr')
row['NODE_NAME'] = row['NODE_NAME'].decode('cp949')
print(row['NODE_NAME'])
# Change coordinate system
# korea 2000/central belt 2010 (epsg:5186) to wgs84(epsg:4326)
inProj = Proj(init = 'epsg:5186')
outProj= Proj(init = 'epsg:4326')
latitude = []
longitude= []
for idx,row in df_node.iterrows():
x,y = row.coords[0][0],row.coords[0][1] # korea 2000 좌표계
nx,ny = transform(inProj,outProj,x,y) # 새로운 좌표계
latitude.append(ny)
longitude.append(nx)
df_node['latitude'] = latitude
df_node['longitude']= longitude
del df_node['coords'] # delete coords
# Change column name to draw network in Gephi
df_node.rename(columns={'NODE_ID':'Id'},inplace = True)
df_link.rename(columns={'F_NODE':'Source','T_NODE':'Target'},inplace = True)
df_node.to_csv('seoul_nodes.csv') # node list
df_link.to_csv('seoul_links.csv') # edge(=link) list
import os
import folium
import pandas as pd
import numpy as np
import networkx as nx
nodes = pd.read_csv('seoul_nodes.csv', encoding = 'cp949')
links = pd.read_csv('seoul_links.csv')
#nodes = nodes[['Id','NODE_NAME','latitude','longitude']]
links = links[['Source','Target']]
links.head()
source_in = links['Source'].apply(lambda x : x in list(nodes['Id'])) # check Sources are in incheon_id
target_in = links['Target'].apply(lambda x : x in list(nodes['Id'])) # check Targets are in incheon_id
# source_in and target_in are boolean type pandas.Series which contains True or False
seoul_links = links[source_in & target_in] # contain if both target and source are contained in incheon_id
G = nx.Graph()
# R is the Earth's radius
R = 6371e3
for idx,row in nodes.iterrows():
# add node to Graph G
G.add_node(row['Id'],Label=row['NODE_NAME'],latitude=row['latitude'], longitude=row['longitude'])
for idx,row in seoul_links.iterrows():
## Calculate the distance between Source and Target Nodes
lon1 = float(nodes[nodes['Id'] == row['Source']]['longitude'] * np.pi/180)
lat1 = float(nodes[nodes['Id'] == row['Source']]['latitude'] * np.pi/180)
lon2 = float(nodes[nodes['Id'] == row['Target']]['longitude'] * np.pi/180)
lat2 = float(nodes[nodes['Id'] == row['Target']]['latitude'] * np.pi/180)
d_lat = lat2 - lat1
d_lon = lon2 - lon1
a = np.sin(d_lat/2) ** 2 + np.cos(lat1) * np.cos(lat2) * np.sin(d_lon/2) ** 2
c = 2 * np.arctan2(a**0.5, (1-a) ** 0.5)
d = R * c
# Link attribute : 'Source', 'Target' and weight = 'Length between them'
G.add_edge(row['Source'],row['Target'],weight = d)
# Positioning the Standard Point for our Folium Map
std_point = tuple(nodes.head(1)[['latitude','longitude']].iloc[0])
std_point
map_osm = folium.Map(location=std_point, zoom_start=10)
# location : 기준이 되는 점, zoom_start : 지도 상의 zoom level 을 나타냅니다.
for ix, row in nodes.iterrows():
location = (row['latitude'], row['longitude']) # 위도, 경도 튜플
folium.Circle(
location=location,
radius=G.degree[row['Id']] * 30, # 지름이 degree에 비례하도록 설정
color='white',
weight=1,
fill_opacity=0.6,
opacity=1,
fill_color='red',
fill=True, # gets overridden by fill_color
# popup=str(row['Id'])
).add_to(map_osm)
# folium.Marker(location, popup=row['NODE_NAME']).add_to(map_osm)
kw = {'opacity': 0.5, 'weight': 2}
for ix, row in seoul_links.iterrows():
start = tuple(nodes[nodes['Id']==row['Source']][['latitude','longitude']].iloc[0])
end = tuple(nodes[nodes['Id']==row['Target']][['latitude','longitude']].iloc[0])
folium.PolyLine(
locations=[start, end],
color='blue',
line_cap='round',
**kw,
).add_to(map_osm)
# it takes some time.....
map_osm
< 위의 작업을 실행하며 발생한 문제들 >
Issue 1) Tistory에 임시저장을 한 후 다시 확인을 해보니 보이지 않아 인터넷으로 검색해보니 임시저장 기능이 완벽하지 않은 것 같습니다.
저와 같이 Chrome을 사용하시는 분들은
chrome://settings/content -> flash -> 허용 사이트에 자신의 티스토리 주소를 추가해줍니다.
이렇게 추가한 뒤에 임시저장을 위해 본인의 사이트를 다시 실행하게 되면, 임시저장 버튼 위에 퍼즐 모양의 아이콘이 나타나게 됩니다.
이 버튼을 클릭 후 이번만 실행 버튼을 누르게 되면, 임시 저장 관련된 버튼 및 기능이 제대로 작동하게 됩니다.
Issue 2) pip를 통해 다른 패키지들을 설치하는데 문제가 있습니다. pip install pyshp 를 실행했을 때 [transport_encoding] 관련 Type_Error
가 발견됩니다. 이를 해결하기 위해서 python이 설치된 경로로 들어가 pip 패키지 폴더를 찾습니다.
ex) 저의 경우는 C:\Users\사용자명\Anaconda3\Lib\site-packages\pip 입니다.
pip 폴더 내의 index.py 파일에서 transport_encoding=encoding, 이란 항목이 있는데 이 줄을 주석처리하면 해당 Error를 없앨 수 있습니다.
다만 이 주석처리가 어떤 결과를 불러올진 잘 모르겠으니 에러가 발생한다면 해당 파라미터를 주석처리했다는 것을 생각해보시길..
Issue 3) distutils.errors.DistutilsPlatformError: Unable to find vcvarsall.bat
pyproj 패키지를 설치하려고 하는데 위와 같은 오류가 발생했습니다. 해당 패키지를 설치하려면 Visual Studio C++ 관련 Build Tool 이 깔려
있어야 한다고 합니다. 이를 위해 다음 링크를 통해 Tool 만 다운받아 설치합니다.
https://visualstudio.microsoft.com/ko/thank-you-downloading-visual-studio/?sku=BuildTools&rel=15
이후 패키지 설치를 다시 시도하면 완료!
Issue 3) csv 파일을 저장하고 Pandas 라이브러리를 통해 불러오려 했을 때, 아래와 같은 에러에 직면합니다.
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xba in position 0: invalid start byte
이 에러는 encoding type과 decoding type이 다르기 때문에 발생하는 에러입니다. 따라서 read_csv('file_name.csv', encoding = 'cp949')과 같이 인코딩 type을 파라미터로 전달해주면 해결됩니다! 저의 경우에는 encoding type을 cp949(eur_kr이나 다른 방식으로 encoding하셔도 같은 방법으로 decoding하시면 됩니다.)으로 전달해줘서 해결하였습니다. 이 인자에 아무것도 전달하지 않으면 'utf-8' 방식으로 decoding 합니다.