반응형


< 교통망 시각화를 통한 교통량 파악(잠실) > 


목적 : 서울 시내 교통량의 시간대별, 종류별 분류


방법 : 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 3csv 파일을 저장하고 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 합니다.



반응형

'토이 프로젝트' 카테고리의 다른 글

가치투자 프로젝트 (1) Data Gathering  (0) 2020.03.10

+ Recent posts