经过多天的摸索,整理基于开源工具 graphhopper 做导航的一些浅显思路,这里做一个分享,抛砖引玉,希望能够获得正在研究相关领域大佬的一些建议。

如何运行graphhopper的一个导航服务这里就不再赘述了,感兴趣的可以去GitHub上按照教程学习。

这里分享的内容包括 OSM数据 和 graphhopper 开源的导航引擎的一些具体用法和代码层次的集成。

OSM

OSM(OpenStreetMap),是一款由网络大众共同打造的免费开源、可编辑的地图服务,但是在国内使用OpenStreetMap数据似乎并不能满足上述法律要求。

理解 OSM 的数据结构和相关概念:node 、way、relation、tag。

node(节点):存储经纬度,表示位置。但不存储节点在地图上的实际大小,比如说一个景点或者山峰,或一间商店或餐厅,或是最为路径的一部分。节点可依附于路径与关系。

way(路径):有序排列的节点,以折线的形式呈现,也能循环回起始节点形成封闭路径,可以循环路径或以多边形区域的方式呈现。这类原始资料可用于呈为线型资料,例如街道、河流等,或一个多边形区域,例如农田、公园、停车场、建筑物、校园或者是湖泊、森林。路径必须有节点才能显示在地图上,可依附于关系,路径资料可以计算出长度,或多边形的面积、周长。

relation(关系):有排序的节点、路径和关系(三类原始资料在这里统称为“成员” menber),在这里每个成员选择性拥有一个“角色” (role),以决定改成员于此关系中的性质。关系用来表示各个原始资料(节点、路径和关系)的关系,例如道路的转弯限制,由不同的路径所组成的边界,一条国道、省道或者铁路路线。或者一个区域中中间孔雀区域(例如被环形建筑物包围的中庭,或水体中的小岛)的多重多边形,这时“角色”就能用来形容他们之间的关系。

tag(标签):键值对(key- value pairs, 键值都是字符串),用来存储地图上物价的元数据(物件的类型,名字和物理特性),赋予OSM资料其意义,而能表示现实世界中存在的某件事物,与有关其事物的资讯。标签无法独立存在,他们必须依附在一个已存在的物件,也就是节点,路径或关系。一个原始资料称为一个物件,每个物件的同一键,只能设定一个值。例如建筑物是building=yes,至于住宅区街道,则为highway=residential,这是OSM中最频繁被使用的标签。

OSM 数据组织如下:

<?xml version="1.0" encoding="UTF-8"?>
<osm version="0.6" generator="CGImap 0.8.6 (1769772 spike-07.openstreetmap.org)" copyright="OpenStreetMap and contributors" attribution="http://www.openstreetmap.org/copyright" license="http://opendatacommons.org/licenses/odbl/1-0/"><bounds minlat="31.2264000" minlon="121.4598000" maxlat="31.2378000" maxlon="121.4832000"/><node id="63242769" visible="true" version="8" changeset="53519844" timestamp="2017-11-05T07:07:04Z" user="SteniLee" uid="3836466" lat="31.2333299" lon="121.4624429"/>
<node id="9127191400" visible="true" version="1" changeset="111778257" timestamp="2021-09-27T16:55:46Z" user="Herman Lee" uid="6921384" lat="31.2401334" lon="121.4749206">
<tag k="bus" v="yes"/>
<tag k="name" v="福建中路北京东路"/>
<tag k="public_transport" v="stop_position"/>
</node><way id="51317339" visible="true" version="12" changeset="114338570" timestamp="2021-11-29T06:41:25Z" user="wheelmap_visitor" uid="290680">
<nd ref="654577835"/>
<nd ref="1490098834"/>
<nd ref="6975896994"/>
<nd ref="1490098840"/>
<nd ref="6975896995"/>
<nd ref="654577837"/>
<nd ref="654577838"/>
<nd ref="654577839"/>
<nd ref="654577840"/>
<nd ref="654577841"/>
<nd ref="6975896993"/>
<nd ref="654577835"/>
<tag k="building" v="retail"/>
<tag k="building:levels" v="9"/>
<tag k="internet_access" v="wlan"/>
<tag k="name" v="新世界城"/>
<tag k="name:en" v="New World City"/>
<tag k="name:ko" v="신세계백화점"/>
<tag k="name:zh" v="新世界城"/>
<tag k="opening_hours" v="Mo-Su 08:00-22:00"/>
<tag k="shop" v="mall"/>
<tag k="wheelchair" v="no"/>
</way>
<way id="51317372" visible="true" version="10" changeset="94502123" timestamp="2020-11-20T12:53:01Z" user="wvdp" uid="436419">
<nd ref="654578187"/>
<nd ref="654578188"/>
<nd ref="654578442"/>
<nd ref="1728696583"/>
<nd ref="654578190"/>
<nd ref="654578187"/>
<tag k="building" v="yes"/>
<tag k="building:use" v="office"/>
<tag k="height" v="54"/>
<tag k="name" v="百联世茂国际广场"/>
<tag k="name:en" v="Bailian Shimao International Plaza"/>
<tag k="name:ko" v="브릴리안스 인터내셔널 플라자"/>
</way><relation id="5611326" visible="true" version="34" changeset="115631320" timestamp="2022-01-01T10:41:50Z" user="Lepuse" uid="9560399">
<member type="node" ref="3800386189" role="stop"/>
<member type="node" ref="3800385632" role="stop"/>
<member type="node" ref="3800385630" role="stop"/>
<member type="node" ref="3800386173" role="stop"/>
<member type="node" ref="3800385603" role="stop"/>
<member type="node" ref="3800386181" role="stop"/>
<member type="node" ref="3800385649" role="stop"/>
<member type="node" ref="3800386198" role="stop"/>
<member type="node" ref="3800385635" role="stop"/>
<member type="node" ref="3800385637" role="stop"/>
<member type="node" ref="3800386183" role="stop"/>
<member type="node" ref="3800386159" role="stop"/>
<member type="node" ref="3800385619" role="stop"/>
<member type="node" ref="3800385651" role="stop"/>
<member type="node" ref="3800385615" role="stop"/>
<member type="node" ref="3800385607" role="stop"/>
<member type="node" ref="3800386169" role="stop"/>
<member type="node" ref="3800385609" role="stop"/>
<member type="node" ref="5308572449" role="stop"/>
<member type="node" ref="3800386195" role="stop"/>
<member type="node" ref="3800385639" role="stop"/>
<member type="node" ref="3800385622" role="stop"/>
<member type="node" ref="3800386179" role="stop"/>
<member type="node" ref="3800385653" role="stop"/>
<member type="node" ref="3800385617" role="stop"/>
<member type="node" ref="3800385613" role="stop"/>
<member type="node" ref="3800385641" role="stop"/>
<member type="node" ref="3800386193" role="stop"/>
<member type="node" ref="3800385624" role="stop"/>
<member type="node" ref="3800386161" role="stop"/>
<member type="way" ref="376634457" role=""/>
<member type="way" ref="461893586" role=""/>
<member type="way" ref="461881036" role=""/>
<member type="way" ref="376634459" role=""/>
<member type="way" ref="120944643" role=""/>
<member type="way" ref="120944645" role=""/>
<member type="way" ref="376634461" role=""/>
<member type="way" ref="108731289" role=""/>
<member type="way" ref="108731285" role=""/>
<member type="way" ref="840501845" role=""/>
<member type="way" ref="230069191" role=""/>
<tag k="colour" v="#97D700"/>
<tag k="colour:ref" v="PANTONE 375 C"/>
<tag k="duration" v="01:31"/>
<tag k="from" v="徐泾东"/>
<tag k="from:en" v="East Xujing"/>
<tag k="name" v="2号线:徐泾东→浦东国际机场"/>
<tag k="name:en" v="Line 2: East Xujing → Pudong International Airport"/>
<tag k="name:zh" v="2号线:徐泾东→浦东国际机场"/>
<tag k="network" v="上海地铁"/>
<tag k="network:en" v="Shanghai Metro"/>
<tag k="network:wikidata" v="Q462201"/>
<tag k="network:wikipedia" v="zh:上海地铁"/>
<tag k="network:zh" v="上海地铁"/>
<tag k="operator" v="上海地铁第二运营有限公司"/>
<tag k="public_transport:version" v="2"/>
<tag k="ref" v="2"/>
<tag k="route" v="subway"/>
<tag k="to" v="浦东国际机场"/>
<tag k="to:en" v="Pudong International Airport"/>
<tag k="type" v="route"/>
<tag k="wikidata" v="Q1325437"/>
</relation></osm>

看完上边相关介绍,大致能理解OSM是如何组织地图上的点和路线以及相关关系。

目前在OSM中存在路线最大速度等约束,比如说:

<way id="40297715" visible="true" version="19" changeset="115611104" timestamp="2021-12-31T14:19:02Z" user="Herman Lee" uid="6921384">
<nd ref="152878617"/>
<nd ref="479290062"/>
<nd ref="479288912"/>
<nd ref="479286375"/>
<nd ref="1315785561"/>
<nd ref="385113975"/>
<tag k="bicycle" v="no"/>
<tag k="highway" v="secondary"/>
<tag k="lanes" v="2"/>
<tag k="maxspeed" v="30"/>
<tag k="maxspeed:type" v="sign"/>
<tag k="name" v="淮海东路"/>
</way>

那么我们所感兴趣的自定义导航比如说限行、限高等约束是否也可以用 relation 来定义呢?

答案是肯定的。

Grasshopper

Grasshopper 是一个开源的地图导航引擎,Java 语言开发,因此可以通过导入依赖很好的集成到Java项目中。

talk is cheap,show you the code!

首先,导入grasshopper的依赖。

<dependency><groupId>com.graphhopper</groupId><artifactId>graphhopper-core</artifactId><version>4.0</version>
</dependency>

有了依赖,我们便可以调用它的接口。

读取OSM数据,加载到内存中,加速后续计算。

static GraphHopper createGraphHopperInstance(String ghLoc) {GraphHopper hopper = new GraphHopper();// OSM 文件路径hopper.setOSMFile(ghLoc);// 读取完OSM数据之后会构建路线图,此处配置图的存储路径hopper.setGraphHopperLocation("target/routing-graph-cache");// 目前免费包仅支持car、bike、foot三种交通方式的导航hopper.setProfiles(new Profile("car").setVehicle("car").setWeighting("fastest").setTurnCosts(false));// this enables speed mode for the profile we called carhopper.getCHPreparationHandler().setCHProfiles(new CHProfile("car"));// now this can take minutes if it imports or a few seconds for loading of course this is dependent on the area you importhopper.importOrLoad();return hopper;
}

然后给定出发点和目的地的经纬度,调用 route(),便可以得到按照默认配置导航出来的结果。

public static void routing(GraphHopper hopper) {// simple configuration of the request objectGHRequest req = new GHRequest(30.633627, 114.324787,  30.557590, 114.533759).// note that we have to specify which profile we are using even when there is only one like heresetProfile("car").// define the language for the turn instructionssetLocale(Locale.CHINA);GHResponse rsp = hopper.route(req);// handle errorsif (rsp.hasErrors())throw new RuntimeException(rsp.getErrors().toString());// use the best path, see the GHResponse class for more possibilities.ResponsePath path = rsp.getBest();// 导航结果点位集合 PointList pointList = path.getPoints();// 总距离 mdouble distance = path.getDistance();// 总耗时 mslong timeInMs = path.getTime();System.out.println("路线点位: " + pointList);System.out.println("总距离: " + distance + ", 总用时: " + timeInMs);Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.CHINA);InstructionList il = path.getInstructions();// iterate over all turn instructionsfor (Instruction instruction : il) {System.out.println("distance " + instruction.getDistance() + " for instruction: " + instruction.getTurnDescription(tr));}}

如果我们自己有一些定制化的需求,例如国道优先,不同道路类型不同的最大速度、限高或者限行等等约束,或者通过改变一些参数来满足不同车型的导航需求,那便需要我们对导航的模型做一些配置,代码层面的操作如下:

public static void customizableRouting(String ghLoc) {GraphHopper hopper = new GraphHopper();hopper.setOSMFile(ghLoc);hopper.setGraphHopperLocation("target/routing-custom-graph-cache");hopper.setProfiles(new CustomProfile("car_custom").setCustomModel(new CustomModel()).setVehicle("car"));// The hybrid mode uses the "landmark algorithm" and is up to 15x faster than the flexible mode (Dijkstra).// Still it is slower than the speed mode ("contraction hierarchies algorithm") ...hopper.getLMPreparationHandler().setLMProfiles(new LMProfile("car_custom"));hopper.importOrLoad();// ... but for the hybrid mode we can customize the route calculation even at request time:// 1. a request with default preferencesGHRequest req = new GHRequest().setProfile("car_custom").addPoint(new GHPoint(30.633627, 114.324787)).addPoint(new GHPoint(30.557590, 114.533759));// 2. now avoid primary roads and reduce maximum speedCustomModel model = new CustomModel();model.addToPriority(If("road_class == PRIMARY", MULTIPLY, 0.5));// unconditional limit to 100km/hmodel.addToPriority(If("true", LIMIT, 80));req.setCustomModel(model);GHResponse  res = hopper.route(req);if (res.hasErrors())throw new RuntimeException(res.getErrors().toString());ResponsePath path = res.getBest();// points, distance in meters and time in millis of the full pathPointList pointList = path.getPoints();double distance = path.getDistance();long timeInMs = path.getTime();System.out.println("路线点位: " + pointList);System.out.println("总距离: " + distance + ", 总用时: " + timeInMs);Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.CHINA);InstructionList il = path.getInstructions();// iterate over all turn instructionsfor (Instruction instruction : il) {System.out.println("distance " + instruction.getDistance() + " for instruction: " + instruction.getTurnDescription(tr));}hopper.close();}

关于自定义需求配置,下面详细说一下

CustomModel model = new CustomModel();
model.addToPriority(If("road_class == PRIMARY", MULTIPLY, 0.5));
model.addToPriority(If("true", LIMIT, 80));
If("road_class == PRIMARY", MULTIPLY, 0.5)

MULTIPLY  是设置优先级关键字。下边这句话的意思是 如果路线的类型是“PRIMARY”的话,优先级 * 0.5 ,也就是降低一半。

If("true", LIMIT, 80)

LIMIT 是设置约束的关键字。下边这句话的意思是,将速度无条件的设置为 80 km/h

上边提到的对不同的道路类型设置不同的速度这种复杂的约束是怎么实现的呢 ?

If("road_class == TERTIARY", MULTIPLY, 0.2), // 降低三级路线的优先级
ElseIf("road_class == SECONDARY", LIMIT, 25), // 二级路线限速25km/h
If("road_environment == TUNNEL", LIMIT, 60), // 隧道环境限速60km/h
ElseIf("road_environment == BRIDGE", LIMIT, 50), // 桥梁环境限速50km/h
If("max_width < 3", MULTIPLY, 0.6), // 最大路宽 < 3m ,设置优先级 此处就是设置导航的时候对于限宽的道路的优先选择的权重,如果车辆超过这个宽度,可以设置为0,避免这些道路。
Else(MULTIPLY, 0.8)

以上便是自定义航线约束的配置规则,我们感兴趣的限高、限行等约束同样可以根据上边的规则进行配置。

关于速度的限制,主要是用来计算总用时,至于导航路径的准确性还是依赖优先级的配置。

你可能会对 road_class == TERTIARY、road_environment == BRIDGE 等属性有些许的疑问

Q:这些属性是怎么来的?

A:没错,就是上边讲OSM的时候提到的路径(way)的标签 (tag)例如:

  • 路线类型: <tag k="highway" v="tertiary"/>
  • 路线环境:<tag k="bridge" v="yes"/>

Q:现在的数据已经有限高、现行等属性吗?

A:至于限行限高等属性是否存在,以我目前看到的数据来说是没有的(也可能是有,我没看到),如果要对这些条件进行限制导航,就需要我们对OSM进行编辑,或者编辑自己的约束数据,读取后在内存中与OSM数据进行合并。

以上便是最近对导航的一些浅显领悟,后续的问题便是如何将OSM数据中没有的一些约束数据合并进来,具体怎么处理,敬请期待下篇!

基于Graphhopper的路线导航方案相关推荐

  1. 基于SLAM的机器人导航避障方案

    基于SLAM的机器人导航避障方案 在实现机器人智能导航中,SLAM发挥了重要作用,可帮助机器人实现地图构建与即时定位,但仅有SLAM是还不够的,还需要加入路径规划和运动控制.在SLAM技术帮助机器人确 ...

  2. js简单实现基于图片的路线规划导航

    js简单实现基于图片的路线规划导航 <%@ page language="java" import="java.util.*" pageEncoding= ...

  3. Android 停车地图及停车导航,停车场蓝牙定位导航方案

    停车场蓝牙定位导航方案基于微能信息开发的蓝牙定位系统方案,与固有停车场管理系统深度结合,为顾客在智能手机终端提供全方面的停车场空余车位导航.记录停车位.反向寻车.查找路线.查找公共设施等服务. 停车场 ...

  4. 思岚科技机器人自主定位导航方案:高效可靠、高精度、厘米级别地图多场景适用 | 百万人学AI评选

    2020 无疑是特殊的一年,而 AI 在开年的这场"战疫"中表现出了惊人的力量.站在"新十年"的起点上,CSDN[百万人学AI]评选活动正式启动.本届评选活动在 ...

  5. 基于MMS街景的导航数据采集方法研究

    基于MMS街景的导航数据采集方法研究 李观石,刘波,陆藩藩,宋法奇 (江苏省基础地理信息中心,江苏南京 210013) 摘  要       本文利用MMS街景数据,结合基础地理信息数据进行导航数据的 ...

  6. 一个超赞的智慧园区地图导航解决方案,如何实现园区路线导航?

    基于三维GIS平台的智慧园区建设主要目标是为用户提供高效.便捷.舒适.生态和谐的居住环境,通过以感知技术为核心智慧化途径来获取园区的管理信息,并以智慧化的管理支撑平台整合各种信息,实现园区内的信息互联 ...

  7. Java使用graphhopper完成路线规划

    首先graphhopper是一个开源的免费的的路线规划,里面涵盖了全世界的地图,采用的经度纬度上WGS84(Gps)坐标系.因为是开源的所以设计到一些很精细到城市道路路线,可能与国内的三大图商(百度. ...

  8. Beacon室内导航方案资料汇总(Android)

    Beacon室内导航方案资料汇总(Android) 最近在做基于beacon的室内导航,找了点资料做个记录也顺便分享一下: 一.Beacon文档汇总 开发ibeacon定位APP5大技术亮点地址 ht ...

  9. 寿命长性价比高的室内扫地机器人SLAM导航方案

    SLAM历史介绍 "舟师识地理,夜则观星,昼则观日,阴晦观指南针",几个世纪以前的古人便利用观星术和指南针实现了大航海中位置和航向的标定,走出世代居住的大陆走向广袤的大海,探索新大 ...

最新文章

  1. Python使用matplotlib可视化时间序列季节图、使用季节图可以比较不同年份相同月份的数据差异、或者相同(年/月/周等)的时间序列在同一天的数据差异(Seasonal Plot)
  2. ●观光(17.12.02多校联测题目)
  3. js输出php文件大小,前端js实现文件的断点续传 后端PHP文件接收
  4. Zookeeper的Windows安装
  5. 使用DBUtils编写通用的DAO
  6. pandas读取html并排序,使用pandas怎么实现按照列的值进行排序
  7. CSDN markdown 如何更改文字字体、样式、颜色、大小?
  8. Leaflet中加载离线OSM瓦片地图(使用OfflineMapMaker切割下载离线png地图文件)
  9. 【洛谷P1632】点的移动
  10. C语言动态二维数组,结构体动态申请内存空间
  11. 现在有哪些好用的程序员学习交流的网站或者app?
  12. 计算机格式化的作用,怎样把电脑格式化 电脑格式化方法【图文】
  13. 深度学习:将新闻报道按照不同话题性质进行分类
  14. 软考知识点之需求管理
  15. 计算机显卡型号中数字含义详解,显卡型号中字母和数字所代表的含义.doc
  16. Learning Deep Similarity Models with Focus Ranking for Fabric Image Retrieval 学习笔记
  17. IP命令详解(强大的命令)
  18. 【操作系统基础】操作系统的分类与发展
  19. CentOS安装mysql*.rpm提示conflicts with file from package的解决办法
  20. C语言计算数列1+(1+2)+...+(1+2+...n)的值

热门文章

  1. procdump 抓 dump文件
  2. 当你半夜失眠时,玩手机越玩越精神怎么办
  3. (转载)自定义View——弹性滑动
  4. SpringBoot 启动类 @SpringBootApplication 注解 以及执行流程
  5. Elasticsearch 7.X 性能优化
  6. constantlayout布局
  7. EXCEL中空白单元格如何快速填充为0
  8. python画笔速度调为最高_【python入门系类课程 第三课 神奇的画笔】
  9. 云呐|固定资产条码管理系统的应用
  10. 多御浏览器新出的手机版本有什么功能?