windows上配置FMM很麻烦,一直没整好,于是尝试了在colab上执行FMM
参考内容:jalal1/fmm_jupyter: Install Fast map matching (FMM) using Jupyter Notebook (github.com)
1 下载数据
# download file from GitHub
! wget https://raw.githubusercontent.com/jalal1/fmm_jupyter/master/data/100_trajectories.txt'''
--2024-02-26 06:55:39-- https://raw.githubusercontent.com/jalal1/fmm_jupyter/master/data/100_trajectories.txt
Resolving raw.githubusercontent.com (raw.githubusercontent.com)... 185.199.108.133, 185.199.109.133, 185.199.110.133, ...
Connecting to raw.githubusercontent.com (raw.githubusercontent.com)|185.199.108.133|:443... connected.
HTTP request sent, awaiting response... 200 OK
Length: 25479 (25K) [text/plain]
Saving to: ‘100_trajectories.txt.1’100_trajectories.tx 100%[===================>] 24.88K --.-KB/s in 0.002s 2024-02-26 06:55:39 (14.5 MB/s) - ‘100_trajectories.txt.1’ saved [25479/25479]
'''
!head /content/100_trajectories.txt
'''
Participant2_2019_March2019_Shot0140.jpg:33.509364058739486 -86.78954869584962,33.50978487475548 -86.78860322799623,33.51365016246786 -86.792410503766
Participant2_2019_May2019_Shot0142.jpg:33.51094614347225 -86.78923299192616,33.509571534630986 -86.78998517826301,33.51938097370663 -86.79837386198179,33.50822037079172 -86.82131054613122,33.51100887200205 -86.82332334728943,33.51131870158742 -86.83317062553503
Participant2_2019_November2019_Shot0127.jpg:33.50679887410579 -86.80424681374738,33.51400856087657 -86.80915731051758,33.51335332100002 -86.81056606651244,33.51288259528744 -86.81025033563954
Participant2_2019_November2019_Shot0198.jpg:33.51188921624417 -86.78723301871021,33.509581833511554 -86.79156995015465,33.48366945634628 -86.78709029437786,33.4661537869968 -86.76220036737358
Participant2_2019_November2019_Shot0267.jpg:33.49995290121026 -86.79772386799166,33.50518863176492 -86.80128439451919,33.51110099859621 -86.78887415090277,33.51283854022087 -86.79006323287805
Participant2_2019_October2019_Shot0187.jpg:33.431744652524394 -86.78865692928392,33.41219602478363 -86.80614290578389,33.39369496042756 -86.78484564702798,33.44019957219545 -86.73004735468466,33.432929592595286 -86.71506989666614,33.42762893295855 -86.71850312293473
Participant2_2019_October2019_Shot0260.jpg:33.51279925614481 -86.79913689098413,33.503445430924174 -86.81408181202305,33.520344991596886 -86.82636479131318,33.51047914218212 -86.87755295215898,33.51026907691468 -86.86043852784368
Participant2_2019_October2019_Shot0332.jpg:33.51122148954738 -86.78863346669883,33.504151067453506 -86.80345417768335
Participant2_2019_September2019_Shot0144.jpg:33.51200390143716 -86.78691735071938,33.50896338168748 -86.79209170963797,33.491358620011404 -86.78773912055813,33.48207783715377 -86.81082652404592,33.481094654107274 -86.8041556616955
Participant2_2019_September2019_Shot0216.jpg:33.488167281458445 -86.80800831137027,33.4910545025647 -86.80109811986188,33.4971313972488 -86.8035525597632,33.49960965872394 -86.79837052923209
'''
1.1 预处理数据
import os
raw_trajectories = []with open('/content/100_trajectories.txt', 'r') as f:for line in f:line_values = line.split(":")traj = line_values[1]#去掉trajectories每一行前面的.jpg部分raw_trajectory = []for x in traj.split(','):#每一个逗号分割的是一组经纬度lat, lon = x.split(" ")#将每一个经纬度record 分成经度 & 纬度raw_trajectory.append((float(lat), float(lon)))raw_trajectories.append(raw_trajectory)print("Number of trajectories: ", len(raw_trajectories))
#Number of trajectories: 100
2 在colab上安装FMM
# Install all the requirements with:
! sudo apt-get install libboost-dev libboost-serialization-dev \
gdal-bin libgdal-dev make cmake libbz2-dev libexpat1-dev swig python-dev-is-python3!git clone https://github.com/cyang-kth/fmm.gitimport os
# change working directory
os.chdir("fmm")if not os.path.exists('build'):os.mkdir('build')
# ! mkdir build
os.chdir("build")
# ! cd build
! cmake ..
! make -j4
! sudo make install
2.1 验证是否安装成功
# Verfication of installation
!fmm
'''
[info][fmm_app_config.cpp:49 ] Start reading FMM configuration from arguments
fmm argument lists:
--ubodt (required) <string>: Ubodt file name
--network (required) <string>: Network file name
--network_id (optional) <string>: Network id name (id)
--source (optional) <string>: Network source name (source)
--target (optional) <string>: Network target name (target)
--gps (required) <string>: GPS file name
--gps_id (optional) <string>: GPS id name (id)
--gps_x (optional) <string>: GPS x name (x)
--gps_y (optional) <string>: GPS y name (y)
--gps_timestamp (optional) <string>: GPS timestamp name (timestamp)
--gps_geom (optional) <string>: GPS geometry name (geom)
--gps_point (optional): if specified read input data as gps point, otherwise (default) read input data as trajectory
--output (required) <string>: Output file name
--output_fields (optional) <string>: Output fieldsopath,cpath,tpath,mgeom,pgeom,offset,error,spdist,tp,ep,length,duration,speed,all
-k/--candidates (optional) <int>: Number of candidates (8)
-r/--radius (optional) <double>: search radius (network data unit) (300)
-e/--error (optional) <double>: GPS error (network data unit) (50)
--reverse_tolerance (optional) <double>: proportion of reverse movement allowed on an edge
-l/--log_level (optional) <int>: log level (2)
-s/--step (optional) <int>: progress report step (100)
--use_omp: use OpenMP for multithreaded map matching
-h/--help:print help information
For xml configuration, check example folder
'''
# Change to the parent folder which contains fmm_test.py
if os.getcwd() != "/content/fmm/example/python":os.chdir("/content/fmm/example/python")
os.system('python fmm_test.py')
#256
3 在colab上安装其他需要的包
!pip install osmnx! pip install folium
4 从OSM中提取需要的shp文件
osmnx笔记:从OpenStreetMap中提取点和边的shp文件(FMM文件准备内容)-CSDN博客
import osmnx as ox
import time
from shapely.geometry import Polygon
import os
import numpy as np
from fmm import Network,NetworkGraph,STMATCH,STMATCHConfigdef save_graph_shapefile_directional(G, filepath=None, encoding="utf-8"):if filepath is None:filepath = os.path.join(ox.settings.data_folder, "graph_shapefile")#保存shp文件的默认路径if not filepath == "" and not os.path.exists(filepath):os.makedirs(filepath)#如果文件保存路径不存在,那么创建之filepath_nodes = os.path.join(filepath, "nodes.shp")filepath_edges = os.path.join(filepath, "edges.shp")#点和边shp文件的保存路径gdf_nodes, gdf_edges = ox.utils_graph.graph_to_gdfs(G)#提取graph中的点和边gdf_nodes = ox.io._stringify_nonnumeric_cols(gdf_nodes)gdf_edges = ox.io._stringify_nonnumeric_cols(gdf_edges)# 将点和边的geoDataFrame中非数值的部分转化成字符串gdf_edges["fid"] = np.arange(0, gdf_edges.shape[0], dtype='int')#每一条边一个idgdf_nodes.to_file(filepath_nodes, encoding=encoding)gdf_edges.to_file(filepath_edges, encoding=encoding)#保存边和点的shp文件
bounds = (-86.9671,-86.5901,33.3472,33.6598)x1,x2,y1,y2 = bounds
boundary_polygon = Polygon([(x1,y1),(x2,y1),(x2,y2),(x1,y2)])
G = ox.graph_from_polygon(boundary_polygon, network_type='drive')
#用ox.graph_from_bbox也可以save_graph_shapefile_directional(G, filepath='./network-new')
!mv '/content/network-new' '/content/fmm/example/python/network-new'
5 进行地图匹配(st-matching)
论文笔记:Map-Matching for low-sampling-rate GPS trajectories(ST-matching)-CSDN博客
def match(path,points):network = Network(path,"fid","u","v")#边的shp文件,后面的三个参数分别是边的id index, source index, target indexgraph = NetworkGraph(network)print (graph.get_num_vertices())#图有多少个点#后续输出的结果是:32303model = STMATCH(network,graph)#创建一个st-matching算法模型对象,使用前面创建的network和graph作为参数wkt = str(points)#将points参数转换为字符串格式,这里points是以WKT(Well-Known Text)格式表示的点集合'''通过STMATCHConfig为st-matching算法配置对象'''config = STMATCHConfig()config.k = 4#k——candidate的数量config.gps_error = 0.5#gps误差config.radius = 0.4#搜索半径config.vmax = 30#最大速度为30,这个参数用于限制匹配算法中考虑的最大速度config.factor =1.5#st-matching的因子为1.5result = model.match_wkt(wkt,config)#调用STMATCH模型的match_wkt方法,传入WKT格式的点集合和配置对象,进行地图匹配print (type(result))print ("Opath ",list(result.opath))print ("Cpath ",list(result.cpath))print ("WKT ",result.mgeom.export_wkt())'''打印:匹配结果对象的类型——<class 'fmm.PyMatchResult'>原始路径(opath)——Opath [70255, 48283, 56497]修正路径(cpath)——Cpath [70255, 48282, 48283, 48621, 48285, 48281, 42749, 40410, 31151, 56497]匹配结果的WKT格式几何(mgeom)——WKT LINESTRING(-86.789547 33.50936,-86.789539 33.509363,-86.789062 33.509602,-86.788979 33.509635,-86.788736 33.509737,-86.788613 33.509786,-86.788736 33.509737,-86.788979 33.509635,-86.789062 33.509602,-86.789539 33.509363,-86.789591 33.509442,-86.789787 33.509709,-86.789832 33.50977,-86.789942 33.509946,-86.790265 33.510457,-86.790654 33.511026,-86.79103 33.511605,-86.791425 33.512179,-86.791805 33.512752,-86.792107 33.513209,-86.792414 33.513648)'''return result.mgeom.export_wkt()#返回匹配结果的WKT格式几何,这是一个字符串表示的几何形状
def match_trajs(network_path, traj):"""match [traj] on [network_path]. [traj] should be [(lon1, lat1), (lon2, lat2), ...]Output: matched traj as "LINESTRING(-86.797211 33.499194, -86.7973...""""from shapely.geometry import LineStringtraj_ = LineString(traj)#创建一个 LineString 对象 traj_,将输入的轨迹点 traj 转换成LineStringreturn match(network_path, traj_)'''调用之前定义的 match 函数,将路网数据的路径 network_path 和转换后的轨迹几何对象 traj_ 作为参数传入。这个函数的目的是执行地图匹配过程,并返回匹配后的轨迹(作为WKT格式的 LINESTRING)'''
import csv
from shapely.geometry import Point
from collections import defaultdictedges_path = '/content/fmm/example/python/network-new/edges.shp'matched_trajectories = []# !! FMM the first x values !!
x = 10
#进行多少条轨迹的快速地图匹配for traj in raw_trajectories[:x]:#遍历原始轨迹列表 raw_trajectories 的前 x条轨迹# swap (lat, lon) --> (lon, lat) as FMM requires lon before lattraj_m = [(sub[1], sub[0]) for sub in traj]fmm_traj = match_trajs('/content/fmm/example/python/network-new/edges.shp', traj_m)#使用 match_trajs 函数将转换后的轨迹与路网进行匹配。match_trajs 函数需要路网数据文件的路径和轨迹点列表作为参数。print('fmm_traj',fmm_traj)#fmm_traj LINESTRING(-86.789547 33.50936,-86.789539 33.509363,-86.789062 33.509602,-86.788979 33.509635,-86.788736 33.509737,-86.788613 33.509786,-86.788736 33.509737,-86.788979 33.509635,-86.789062 33.509602,-86.789539 33.509363,-86.789591 33.509442,-86.789787 33.509709,-86.789832 33.50977,-86.789942 33.509946,-86.790265 33.510457,-86.790654 33.511026,-86.79103 33.511605,-86.791425 33.512179,-86.791805 33.512752,-86.792107 33.513209,-86.792414 33.513648)# remove LINESTRING and extra parenthesesfmm_traj = fmm_traj.split("LINESTRING(")[1]fmm_traj = fmm_traj.split(")")[0]traj_list = []#初始化一个空列表 traj_list,用于存储处理后的轨迹点for lon_lat in fmm_traj.split(','):lon__lat = lon_lat.split(" ")# print(lon__lat)if len(lon__lat) == 2:traj_list.append((float(lon__lat[1]), float(lon__lat[0])))else:print("!! lon and lat should be 2 !!", lon__lat)matched_trajectories.append(traj_list)#将处理后的轨迹 traj_list 添加到 matched_trajectories 列表中
6 可视化匹配结果
6.1 单条结果
import random
import foliummap = folium.Map(zoom_start=13, height=700, location=[33.5186, -86.8104])# choose random trajectory to plot
x = random.randrange(len(matched_trajectories))
#随机选择一个id# original trajectory
folium.PolyLine(raw_trajectories[x], color='red', radiuses=2, connect=True).add_to(map)
# matched trajectory
folium.PolyLine(matched_trajectories[x], color='green', radiuses=2, connect=True).add_to(map)map