使用 PyQGISOSRM 将 GPS 捕捉轨迹应用到道路

网友投稿 953 2022-05-30

如果您收集了 GPS 轨迹,您就会知道结果可能具有不同的准确性。沿路线收集的轨迹点并不总是在路上,可能会很紧张。

如果您是物流、送货或出租车公司——这会带来一个大问题。使用这些点计算的距离将不准确——尤其是如果这些点是间隔开的。此外,您无法比较在不同设备或人员处收集的轨迹,因为即使它们在同一条路线上,它们的几何形状也会不同。

此问题的解决方案是将每个点捕捉到最近的路段。虽然这在原则上听起来很容易,但准确地做到这一点是具有挑战性的。你不能为一个点选择最近的路段——因为最近的点可能在交叉的街道上。您需要考虑上一个点和下一个点之间的路线,以找到最合理的捕捉位置。

幸运的是,一个名为Open Source Routing Machine (OSRM)的开源项目通过快速且可扩展的算法解决了这个问题。我们可以使用 OSRM 的匹配服务将 GPS 点捕捉到最合适的路段。OSRM 引擎使用来自 OpenStreetMap (OSM) 项目的数据。OSM 在世界大部分地区拥有相当不错的街道网络覆盖,并且还在不断改进。通过利用来自 OSM 的开放数据和来自 OSRM 的开放路由算法,我们可以实现捕捉服务。

OSRM 的工作原理是通过HTTP API获取输入,计算结果并通过 JSON 对象返回它们。

运行 OSRM 服务

OSRM 提供了一个演示服务器和一个演示 HTTP 服务。但是我发现演示服务器经常过载,不适合用于偶尔测试以外的用途。

如果您想在您的项目中使用 OSRM 引擎,最好的选择是在您的计算机或服务器上运行您自己的服务。运行您自己的服务实例可能听起来很吓人,但使用 Docker 设置它非常简单。该文档有很好的说明。以下是我使用印度班加罗尔市的数据运行本地实例的步骤。

获取数据

在城市级别获取 OpenStreetMap 提取的一种简单方法是Interline。如果您需要国家和大陆级别的数据,可以从GeoFabrik下载。

我注册了一个免费的 API 密钥,并为班加罗尔下载了作为begaluru_india.osm.pbf文件的提取物。我在我的系统上创建了一个新文件夹,将数据文件复制到那里,启动 Docker 并在终端中运行以下命令。文档中唯一的变化是–max-matching-size参数,我将其增加到 5000,以便我们可以匹配大型 GPS 轨迹。

docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-extract -p /opt/car.lua /data/bengaluru_india.osm.pbf

docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-partition /data/bengaluru_india.osrm

docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-customize /data/bengaluru_india.osrm

docker run -t -i -p 5000:5000 -v "${PWD}:/data" osrm/osrm-backend osrm-routed --algorithm mld --max-matching-size 5000 /data/bengaluru_india.osrm

运行最后一条命令后,服务器将在您的机器上启动,它可以接受 URL http://127.0.0.1:5000 的匹配请求

匹配请求的格式如下,其中关键部分是 {coordinates} 参数,它是轨迹上每个点的坐标,格式为longitude1, latitude1;longitude2, latitude2。

docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-extract -p /opt/car.lua /data/bengaluru_india.osm.pbf

docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-partition /data/bengaluru_india.osrm

docker run -t -v "${PWD}:/data" osrm/osrm-backend osrm-customize /data/bengaluru_india.osrm

docker run -t -i -p 5000:5000 -v "${PWD}:/data" osrm/osrm-backend osrm-routed --algorithm mld --max-matching-size 5000 /data/bengaluru_india.osrm

我们需要通过读取 GPS 轨迹以编程方式编译此 URL,并将其发送到我们在上一步中启动的本地匹配服务。还需要对结果进行处理并转换为轨迹线进行可视化。这就是 QGIS 的用武之地。使用 PyQGIS,我们可以编写一个处理脚本,使这种交互变得简单直观。

匹配 GPS 轨迹

打开 QGIS。转到处理 → 工具箱 → 创建新脚本

在脚本编辑器中复制/粘贴以下代码并将其保存为snap_to_road.py

import requests

from PyQt5.QtCore import QCoreApplication

from qgis.core import (QgsProcessing, QgsProcessingAlgorithm,

QgsProcessingParameterFeatureSource, QgsProcessingParameterFeatureSink,

QgsProcessingParameterString, QgsProcessingParameterNumber, QgsWkbTypes,

QgsGeometry, QgsFeatureSink, QgsFields, QgsPoint, QgsFeature)

from PyQt5.QtXml import QDomDocument

class ExportLayoutAlgorithm(QgsProcessingAlgorithm):

"""Exports the current map view to PDF"""

INPUT = 'INPUT'

OUTPUT = 'OUTPUT'

SERVICE = 'SERVICE'

TOLERANCE = 'TOLERANCE'

def flags(self):

return super().flags() | QgsProcessingAlgorithm.FlagNoThreading

def initAlgorithm(self, config=None):

self.addParameter(

QgsProcessingParameterFeatureSource(

'INPUT',

self.tr('Input vector layer'),

types=[QgsProcessing.TypeVectorPoint]

)

)

self.addParameter(

QgsProcessingParameterString(

self.SERVICE,

self.tr('OSRM Service URL'),

'http://127.0.0.1:5000'

)

)

self.addParameter(

QgsProcessingParameterNumber(

self.TOLERANCE,

self.tr('Snapping Tolerance (meters)'),

QgsProcessingParameterNumber.Integer,

10

)

)

self.addParameter(

QgsProcessingParameterFeatureSink(

self.OUTPUT,

'Snapped Line',

QgsProcessing.TypeVectorLine

)

)

def processAlgorithm(self, parameters, context, feedback):

source = self.parameterAsSource(parameters, self.INPUT, context)

service = self.parameterAsString(parameters, self.SERVICE, context)

tolerance = self.parameterAsInt(parameters, self.TOLERANCE, context)

sink, dest_id = self.parameterAsSink(

parameters,

self.OUTPUT,

context,

QgsFields(),

QgsWkbTypes.LineString,

source.sourceCrs()

)

# Compute the number of steps to display within the progress bar and

# get features from source

total = 100.0 / source.featureCount() if source.featureCount() else 0

features = source.getFeatures()

coordinate_list = []

for current, f in enumerate(features):

# Stop the algorithm if cancel button has been clicked

if feedback.isCanceled():

break

geom = f.geometry().asPoint()

coordinates = '{},{}'.format(geom.x(), geom.y())

coordinate_list.append(coordinates)

feedback.setProgress(int(current * total))

coordinate_str = ';'.join(coordinate_list)

radius = ['{}'.format(tolerance)]

radius_str = ';'.join(radius*len(coordinate_list))

service_url = '/match/v1/driving/{}'.format(coordinate_str)

request_url = service + service_url

payload = {'geometries': 'geojson', 'steps': 'false', 'radiuses': radius_str}

r = requests.get(request_url, params=payload)

results = r.json()

使用 PyQGIS 和 OSRM 将 GPS 捕捉轨迹应用到道路

for match in results['matchings']:

coords = match['geometry']['coordinates']

point_list = [QgsPoint(coord[0], coord[1]) for coord in coords]

out_f = QgsFeature()

out_f.setGeometry(QgsGeometry.fromPolyline(point_list))

sink.addFeature(out_f, QgsFeatureSink.FastInsert)

return {self.OUTPUT: sink}

def name(self):

return 'snap_to_roads'

def displayName(self):

return self.tr('Snap to Roads')

def shortHelpString(self):

return self.tr('Snaps GPS Trackpoints to OSM roads using OSRM service')

def group(self):

return self.tr(self.groupId())

def groupId(self):

return ''

def tr(self, string):

return QCoreApplication.translate('Processing', string)

def createInstance(self):

return ExportLayoutAlgorithm()

保存后,新算法将出现在 Processing → Toolbox → Scripts → Snap To Roads 中。在 QGIS 中加载您的 GPS 跟踪点并双击脚本以运行它。

生成的捕捉道路线将添加到 QGIS 图层面板。您可以看到 OSRM 的工作非常有魅力,并且结果正如人们所期望的那样。

如果您想试用该算法,可以下载sample_gps_track.gpx。从 Interline获取Bengaluru OSM 摘录。如果您遇到问题,请发表评论并告诉我。

API

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:小美赛(认证杯)ABCD题翻译
下一篇:造个‘’轮子‘’!只要掌握了这几点,你也可以撸一个写在简历上的轮子(附手撸过程)
相关文章