人流データなどの位置情報データを使って二地点間の距離を測定する際には、地理座標系(赤道を0°、北極/南極を90°、東西は東経/西経各180°とした座標系)のままでは単純にユークリッド距離を使うことはできません。
そこで、座標変換を行い平面上に投影した上でユークリッド距離を測定する必要があります。
このページでは緯度・経度の位置情報データの座標変換と距離測定の流れを説明します。
位置座標から距離測定
位置情報データは通常、北緯と東経で表される地理座標系で表されます。
例:35.680965, 139.766685(東京駅)
緯度や経度が1°進んだ場合の距離が地球上の場所によって異なります。
このため、2つの位置座標のユークリッド距離を計算すると誤った値になるため、座標変換を行って正確に距離を計算できる座標系に直す必要があります。
座標系(地理座標系や座標参照系など)については、以下のページで解説しています。
参考測地系と座標参照系の違い
QGISなどのGISソフトウェアを使い背景地図の上に別の地図を重ね合わせても、何も表示されないことがあります。 これは、重ね合わせた地図に対して適切な座標参照系(CRS, Coordinate Ref ...
続きを見る
座標参照系とEPSGコード
座標変換にあたってはじめに変換前後の座標参照系(空間座標系)を確認する必要があります。
座標参照系は4桁の数字からなるEPSGコードによって指定します。
EPSGコードは1985年にEuropean Petroleum Survey Group(EPSG, 欧州石油調査グループ)によって制定されたコードです。
EPSGの例としては、WGS84の一種であるWebメルカトル(Google Mapが採用)はEPSG:3857、日本で使われている平面直角座標系(直交座標系の一種)である日本測地系2011((JGD2011)は、EPSG:6674(Ⅵ(6)系、大阪府~三重県)です。
距離測定の際には、地理座標系から平面直角座標系に変換します。
変換前の座標参照系は、人流データの仕様書・テーブル定義書などを参照して確認します。
変換先の座標経緯については、日本国内のどの地域の位置情報を使用するかに応じて、JGD2011のうちⅠ(1)系~ⅩⅨ(19)系のいずれかを指定します。
本来はエリアごとに変換先を変えますが、厳密性を要求しない場合は中間的な位置のものを利用します。
例えば、日本全域のデータに対して一律にⅥ(6)系(大阪府~三重県、EPSG:6674)に変換するといった具合です。
地域と平面直角座標系の対応:平面直角座標系(平成十四年国土交通省告示第九号) 国土地理院 2024/12/16閲覧
EPSGコードの一覧表(日本関連):EPSGコード一覧表/日本でよく利用される空間座標系(座標参照系) lemuls.me 2024/12/16閲覧
座標変換
ここでは、Pythonのgeopandas.GeoDataFrame.to_crs関数を使用します。
座標変換を行うにあたって入力データは、あらかじめ座標参照系が設定されている必要があります。
国土数値情報からダウンロードしたシェープファイルなどは、geopandas.read_file関数で読み込んだ時点で、データとして座標参照系が設定されています。
import pandas as pd
import geopandas as gpd
# 鉄道駅の位置情報データ(国土数値情報)
# https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N05-2023.html
file_path = '../../00_common_data/01_国土数値情報/02_鉄道ライン/2023/N05-23_GML/N05-23_Station2.shp'
# シェープファイルの読み込み
gdf_station = gpd.read_file(file_path, encoding='shift-jis')
# 座標参照系の確認
print(gdf_station.crs)
# epsg:6668
CSVファイルなどで座標参照系が設定されていないデータの場合は、geopandas.GeoDataFrame.set_crs関数で座標参照系を設定します。
# 位置情報データの読み込み
file_path = '../00_data/GPS_241109_SJIS_LF_新橋渋谷.CSV'
df = pd.read_csv(file_path, encoding='cp932')
df['geometry'] = gpd.points_from_xy(df['経度'], df['緯度'])
gdf = gpd.GeoDataFrame(df, geometry='geometry')
# WGS84として座標参照系を設定(EPSG:4326)
gdf = gdf.set_crs(epsg=4326)
print(gdf.crs)
# epsg:4326
座標参照系を設定した上で、geopandas.GeoDataFrame.to_crs関数で座標変換を行います。
# 座標変換の実施
crs_after = 'epsg:6677' # JGD2011 Ⅸ(9)系
gdf_crs = gdf.to_crs(crs=crs_after)
print(gdf_crs.crs)
# epsg:6677
距離測定
ここからは、渋谷駅と座標変換を行った人流データの距離を測定します。
距離を測定する際には、測定する2か所の位置情報の座標系を揃える必要があります。
はじめに、前処理として国土数値情報から取得した渋谷駅の位置情報を抽出し、座標変換を行います。
# 渋谷駅との距離を計算
# 渋谷駅の位置情報データを抽出
gdf_station = gdf_station.rename(columns={
'N05_001':'事業者種別', 'N05_002':'路線名', 'N05_003':'運営会社', 'N05_004':'供用開始年', 'N05_005b':'設置期間(設置開始)', 'N05_005e':'設置期間(設置終了)',
'N05_006':'関係ID', 'N05_007':'変遷ID', 'N05_008':'変遷備考', 'N05_009':'備考', 'N05_011':'駅名',
})
gdf_station = gdf_station[(gdf_station['路線名']=='山手線')&(gdf_station['駅名']=='渋谷')]
display(gdf_station)
# POINT (139.70165 35.65809)
# 座標変換の実施(地理座標系から平面直角座標系へ)
crs_after = 'epsg:6677' # JGD2011 Ⅸ(9)系 ※人流データと座標系を揃える
gdf_station_crs = gdf_station.to_crs(crs=crs_after)
print(gdf_station_crs.crs)
display(gdf_station_crs)
# POINT (-11923.357 -37924.796)
# 渋谷駅の位置座標(座標変換後)
loc_shibuya = gdf_station_crs['geometry'].iloc[0]
print(loc_shibuya)
前処理をして座標系を揃えたデータで距離を測定します。
# 渋谷駅との距離を測定
gdf_dist = gdf_crs.copy()
gdf_dist['渋谷駅との距離'] = gdf_dist.distance(loc_shibuya)
display(gdf_dist.head())
print(gdf_dist['渋谷駅との距離'].head(6))
# epsg:6677 # JGD2011 Ⅸ(9)系
# 0 78.943404
# 1 78.943404
# 2 78.943404
# 3 78.943404
# 4 88.596504
# 5 88.596504
以上の処理では平面直角座標系(JGD2011 Ⅸ(9)系)を使用していますが、同様にユークリッド距離を測定可能な投影座標系であるUTM(ユニバーサル横メルカトル)座標系(EPSG: 3100, 東経138-144°)に座標変換して距離測定を行うと、以下のようなに1-2cmほどずれた結果になります。
print(gdf_dist['渋谷駅との距離'].head(6))
# epsg:3100 UTM54N系
# 0 78.933013
# 1 78.933013
# 2 78.933013
# 3 78.933013
# 4 88.584843
# 5 88.584843
地理座標から直接距離測定
最後に参考として、地理座標から一気に距離を測定するgeopy.distance.geodesic関数を紹介します。
この関数を使えば、座標変換の処理をいちいち書かなくて済みます。
from shapely.geometry import Point, LineString, Polygon
from geopy.distance import geodesic
gdf_dist = gdf.copy()
# 距離測定用前処理
gdf_dist['経度緯度'] = gdf_dist.apply(lambda x: Point(x['経度'], x['緯度']), axis=1)
# 渋谷駅の位置座標
print(loc_shibuya.y, loc_shibuya.x)
# 35.658093 139.701645
# 渋谷駅との距離を測定
gdf_dist['渋谷駅との距離'] = [geodesic((loc.y, loc.x), (loc_shibuya.y, loc_shibuya.x)).meters for loc in gdf_dist['geometry']]
print(gdf_dist['渋谷駅との距離'].head(10))
# 0 78.951161
# 1 78.951161
# 2 78.951161
# 3 78.951161
# 4 88.605210
# 5 88.605210
個別に座標系を指定していないため、先程の結果と数cmの誤差が出ています。
そこまで厳密に距離を決める必要がない場合は、こちらの関数で地理座標から一気に距離測定を行う方法で問題ありません。
参考文献
鉄道時系列データ 国土数値情報 2024/12/8閲覧
EPSGコードとは|コードの定義情報・検索方法 空間情報クラブ 2024/12/16閲覧
EPSGコード一覧表/日本でよく利用される空間座標系(座標参照系) lemuls.me 2024/12/16閲覧
EPSG Geodetic Parameter Dataset, Wikipedia 2024/12/16閲覧
平面直角座標系(平成十四年国土交通省告示第九号) 国土地理院 2024/12/16閲覧