使用 PyQGISOSRM 將 GPS 捕捉軌跡應用到道路

      網(wǎng)友投稿 1144 2022-05-30

      如果您收集了 GPS 軌跡,您就會知道結果可能具有不同的準確性。沿路線收集的軌跡點并不總是在路上,可能會很緊張。

      如果您是物流、送貨或出租車公司——這會帶來一個大問題。使用這些點計算的距離將不準確——尤其是如果這些點是間隔開的。此外,您無法比較在不同設備或人員處收集的軌跡,因為即使它們在同一條路線上,它們的幾何形狀也會不同。

      此問題的解決方案是將每個點捕捉到最近的路段。雖然這在原則上聽起來很容易,但準確地做到這一點是具有挑戰(zhàn)性的。你不能為一個點選擇最近的路段——因為最近的點可能在交叉的街道上。您需要考慮上一個點和下一個點之間的路線,以找到最合理的捕捉位置。

      幸運的是,一個名為Open Source Routing Machine (OSRM)的開源項目通過快速且可擴展的算法解決了這個問題。我們可以使用 OSRM 的匹配服務將 GPS 點捕捉到最合適的路段。OSRM 引擎使用來自 OpenStreetMap (OSM) 項目的數(shù)據(jù)。OSM 在世界大部分地區(qū)擁有相當不錯的街道網(wǎng)絡覆蓋,并且還在不斷改進。通過利用來自 OSM 的開放數(shù)據(jù)和來自 OSRM 的開放路由算法,我們可以實現(xiàn)捕捉服務。

      OSRM 的工作原理是通過HTTP API獲取輸入,計算結果并通過 JSON 對象返回它們。

      運行 OSRM 服務

      OSRM 提供了一個演示服務器和一個演示 HTTP 服務。但是我發(fā)現(xiàn)演示服務器經(jīng)常過載,不適合用于偶爾測試以外的用途。

      如果您想在您的項目中使用 OSRM 引擎,最好的選擇是在您的計算機或服務器上運行您自己的服務。運行您自己的服務實例可能聽起來很嚇人,但使用 Docker 設置它非常簡單。該文檔有很好的說明。以下是我使用印度班加羅爾市的數(shù)據(jù)運行本地實例的步驟。

      獲取數(shù)據(jù)

      在城市級別獲取 OpenStreetMap 提取的一種簡單方法是Interline。如果您需要國家和大陸級別的數(shù)據(jù),可以從GeoFabrik下載。

      我注冊了一個免費的 API 密鑰,并為班加羅爾下載了作為begaluru_india.osm.pbf文件的提取物。我在我的系統(tǒng)上創(chuàng)建了一個新文件夾,將數(shù)據(jù)文件復制到那里,啟動 Docker 并在終端中運行以下命令。文檔中唯一的變化是–max-matching-size參數(shù),我將其增加到 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} 參數(shù),它是軌跡上每個點的坐標,格式為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,并將其發(fā)送到我們在上一步中啟動的本地匹配服務。還需要對結果進行處理并轉換為軌跡線進行可視化。這就是 QGIS 的用武之地。使用 PyQGIS,我們可以編寫一個處理腳本,使這種交互變得簡單直觀。

      匹配 GPS 軌跡

      打開 QGIS。轉到處理 → 工具箱 → 創(chuàng)建新腳本

      在腳本編輯器中復制/粘貼以下代碼并將其保存為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]

      )

      )

      使用 PyQGIS 和 OSRM 將 GPS 捕捉軌跡應用到道路

      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()

      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()

      保存后,新算法將出現(xiàn)在 Processing → Toolbox → Scripts → Snap To Roads 中。在 QGIS 中加載您的 GPS 跟蹤點并雙擊腳本以運行它。

      生成的捕捉道路線將添加到 QGIS 圖層面板。您可以看到 OSRM 的工作非常有魅力,并且結果正如人們所期望的那樣。

      如果您想試用該算法,可以下載sample_gps_track.gpx。從 Interline獲取Bengaluru OSM 摘錄。如果您遇到問題,請發(fā)表評論并告訴我。

      API

      版權聲明:本文內(nèi)容由網(wǎng)絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實后本網(wǎng)站將在24小時內(nèi)刪除侵權內(nèi)容。

      上一篇:Python編程:supervisor模塊管理進程實例
      下一篇:Java應用之圖片美化增強AI接口調用手冊
      相關文章
      久久久久亚洲AV无码专区桃色| 亚洲精品无码久久久久秋霞 | 国产成人亚洲精品播放器下载 | 亚洲精品免费观看| 国产成人A亚洲精V品无码 | 亚洲成年网站在线观看| 亚洲精品一二三区| 亚洲综合无码无在线观看| 最新国产成人亚洲精品影院| 亚洲性线免费观看视频成熟 | 亚洲熟妇中文字幕五十中出| 亚洲无人区一区二区三区| 亚洲中文字幕无码不卡电影| 亚洲国产精品无码成人片久久| 国产亚洲精品a在线无码| 亚洲Av综合色区无码专区桃色| 亚洲国产精品特色大片观看完整版| 亚洲精品无码久久久久去q| 亚洲国产精品福利片在线观看| 久久精品亚洲一区二区| 亚洲最新永久在线观看| 亚洲精品免费在线视频| 国产精品亚洲精品观看不卡| 亚洲色无码专区一区| 无码一区二区三区亚洲人妻| 亚洲AⅤ视频一区二区三区| 中文字幕亚洲第一| 日韩亚洲一区二区三区| 亚洲综合色丁香麻豆| 亚洲综合色7777情网站777| 亚洲国产精品无码久久久秋霞1| 国产精品亚洲一区二区在线观看 | 亚洲自偷自偷图片| 久久精品国产精品亚洲蜜月| 久久久亚洲裙底偷窥综合| 亚洲制服丝袜第一页| 久久无码av亚洲精品色午夜| 国产福利电影一区二区三区,亚洲国模精品一区 | 亚洲乱色熟女一区二区三区丝袜 | 久久亚洲精品成人无码网站| 97久久国产亚洲精品超碰热|