GraphHopper Java API 路由实战指南:Speed / Hybrid / Flexible 三种模式、Heading 方向偏好与备选路线
2026/9/17 13:24:31 网站建设 项目流程

GraphHopper Java API 路由实战指南:Speed / Hybrid / Flexible 三种模式、Heading 方向偏好与备选路线

【免费下载链接】graphhopperOpen source routing engine for OpenStreetMap. Use it as Java library or standalone web server.项目地址: https://gitcode.com/GitHub_Trending/gr/graphhopper

本篇技术指南以 GraphHopper 官方文档 Routing via Java API 为核心,讲解如何将 GraphHopper 路由引擎嵌入你自己的 Java(或任意 JVM 语言)应用:从 Maven 依赖、GraphHopper 实例初始化与GHRequest发起路由,到速度模式(CH)、混合模式(LM)、灵活模式(Dijkstra/A*)三种算法的选择与切换,再到 Heading 起终点方向约束、备选路线(Alternative Routes)以及基于 client-hc 的 HTTP 客户端调用。读完本文,你将掌握在纯 Java 代码中完成一次完整、可定制、可上线部署的路线规划所需的全部核心能力,并了解底层算法与参数的实际来源。

前置条件:Maven 依赖与示例工程

要以 Java 库的形式使用 GraphHopper 路由能力,首先需要在构建配置中声明graphhopper-core依赖(README.md#maven):

<dependency> <groupId>com.graphhopper</groupId> <artifactId>graphhopper-core</artifactId> <version>[LATEST-VERSION]</version> </dependency>

其中[LATEST-VERSION]需要替换为你在仓库 CHANGELOG.md 中看到的当前版本号。除了核心模块,官方示例工程位于 example/src/main/java/com/graphhopper/example/RoutingExample.java,它一次性覆盖了本文接下来要讲的全部场景:基础路由、三种模式对比、备选路线、以及基于自定义模型(custom model)的可定制路由。对应的测试 example/src/test/java/com/graphhopper/example/RoutingExampleTest.java 会以RoutingExample.main(new String[]{"../"})的方式运行整个示例,验证示例中的各项断言(如il.size() == 6、距离取整后为 600 米等),是观察"跑通即正确"的最直接入口。

示例主方法接受一个可选参数作为 OSM 数据文件所在目录(默认使用core/files/andorra.osm.pbf这类 OpenStreetMap 数据文件;仓库 core/files 目录中还提供andorra.osm.gzmonaco.osm.gzkrautsand.osm.gz等可直接用于测试的小型地图):

public static void main(String[] args) { String relDir = args.length == 1 ? args[0] : ""; GraphHopper hopper = createGraphHopperInstance(relDir + "core/files/andorra.osm.pbf"); routing(hopper); speedModeVersusFlexibleMode(hopper); alternativeRoute(hopper); customizableRouting(relDir + "core/files/andorra.osm.pbf"); // release resources to properly shutdown or start a new instance hopper.close(); }

注意最后调用的hopper.close()GraphHopper实例持有图存储、CH/LM 预计算数据等资源,显式关闭才能正确释放,或在需要时重新启动新实例。

Speed 模式 vs. Hybrid 模式 vs. Flexible 模式

文档中 GraphHopper 将路由算法划分为三种模式(详见 docs/core/routing.md 与 README.md 技术概览):

模式底层算法是否需要预计算速度灵活性
Speed 模式Contraction Hierarchies(CH,收缩层次结构)需要(导入阶段做 CH 准备)最快最低,仅支持预定义 profile
Hybrid 模式Landmarks(LM,地标)+ A*需要(LM 准备)较快,介于二者之间较高,支持请求时修改部分属性
Flexible 模式Dijkstra 或 A*(及其双向变体)不需要较慢,长路线尤其明显完全灵活

三种模式的取舍非常清晰:Speed 模式查询最快、单请求内存占用最低且不使用启发式,但只能使用预先定义好的 profile;Hybrid 模式需要额外的时间和内存做 LM 准备,但可以在请求层面调整权重(例如集成实时交通数据),速度比 Flexible 模式快一个数量级;Flexible 模式无需任何索引数据,换取的是完全的灵活性和较慢的查询。如果对应的准备数据都存在,你甚至可以在每次请求时动态切换模式。

配置与请求时切换

模式对应的 profile 在config.yml(仓库中为 config-example.yml)里通过profiles_chprofiles_lm两个配置段声明,这与 profile 定义相互独立:

# Speed mode: profiles you want to use with speed mode need to go here profiles_ch: - profile: car - profile: some_other_profile # Hybrid mode: profiles you want to use with hybrid mode need to go here profiles_lm: - profile: car - profile: some_other_profile

config-example.yml中默认只对car开启 CH(注释说明 foot/bike 默认不做 CH 准备以节省资源),profiles_lm默认为空。profile 的完整定义方式(profiles段、custom_modelturn_costs等)请参见 docs/core/profiles.md。

在请求时,可以通过 hint 覆盖默认模式选择:

  • ch.disable=true:关闭速度模式。此时若所选 profile 存在 LM 准备数据,则使用 Hybrid 模式,否则退回 Flexible 模式;
  • lm.disable=true:在存在 LM 准备数据的情况下,强制使用 Flexible 模式。

这两个参数名的常量定义可以在 web-api/src/main/java/com/graphhopper/util/Parameters.java 中找到(Parameters.CH.DISABLEParameters.Landmark.DISABLE),其余常用算法常量同样集中定义在此类中,例如dijkstradijkstrabiastarastarbialternative_routeround_trip等。

示例中speedModeVersusFlexibleMode展示了这一用法——即使 profile 配了 CH 准备,请求时显式选择双向 A* 并禁用 CH 也能得到与速度模式相同的路径:

public static void speedModeVersusFlexibleMode(GraphHopper hopper) { GHRequest req = new GHRequest(42.508552, 1.532936, 42.507508, 1.528773). setProfile("car").setAlgorithm(Parameters.Algorithms.ASTAR_BI).putHint(Parameters.CH.DISABLE, true); GHResponse res = hopper.route(req); if (res.hasErrors()) throw new RuntimeException(res.getErrors().toString()); assert Helper.round(res.getBest().getDistance(), -2) == 600; }

实例初始化:profile、编码值与 CH 准备

createGraphHopperInstance展示了以编程方式构建一个带 CH 速度模式的GraphHopper实例的完整流程:

static GraphHopper createGraphHopperInstance(String ghLoc) { GraphHopper hopper = new GraphHopper(); hopper.setOSMFile(ghLoc); // specify where to store graphhopper files hopper.setGraphHopperLocation("target/routing-graph-cache"); // add all encoded values that are used in the custom model, these are also available as path details or for client-side custom models hopper.setEncodedValuesString("car_access, car_average_speed, road_access, road_environment, max_speed, ferry_speed"); // see docs/core/profiles.md to learn more about profiles hopper.setProfiles(new Profile("car").setCustomModel(GHUtility.loadCustomModelFromJar("car.json"))); // this enables speed mode for the profile we called car hopper.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 import hopper.importOrLoad(); return hopper; }

几个关键点:

  • setOSMFile指定输入地图,setGraphHopperLocation指定图数据的落盘缓存目录;importOrLoad()首次运行会导入 OSM 并构建 CH,之后再次运行则直接从缓存加载,耗时取决于地图范围;
  • setEncodedValuesString声明参与自定义模型的编码值(encoded values),这些值同时可作为 path details 或客户端自定义模型使用;所有内置编码值在 core/src/main/java/com/graphhopper/routing/ev/DefaultEncodedValueFactory.java 中定义,但只有声明进graph.encoded_values的才会进入图存储(对应 docs/core/profiles.md 中"Setting up Encoded Values"一节);
  • setProfiles注册名为car的 profile(这里直接加载 jar 内置的car.json自定义模型,与 YAML 配置中custom_model_files: [car.json]等价);
  • getCHPreparationHandler().setCHProfiles(...)对该 profile 启用 CH 准备,即开启 Speed 模式。

对应的 YAML 等价配置可参考 config-example.yml 中的profilesprofiles_ch段。

发起第一次路由请求:GHRequest 与 GHResponse

路由请求的核心对象是GHRequest(定义在 web-api/src/main/java/com/graphhopper/GHRequest.java),其字段包括points(途经点列表)、profile(profile 名称)、headingspointHintscurbsidespathDetailsalgolocalecustomModel以及一个通用的hints(PMap,承载ch.disable等所有 hint 参数)。GHRequest提供了GHRequest(fromLat, fromLon, toLat, toLon)new GHPoint(...)addPoint(...)等多种构造方式,天然支持带 via 点的多段路由。

一个最小完整示例:

public static void routing(GraphHopper hopper) { // simple configuration of the request object GHRequest req = new GHRequest(42.508552, 1.532936, 42.507508, 1.528773). // note that we have to specify which profile we are using even when there is only one like here setProfile("car"). // define the language for the turn instructions setLocale(Locale.US); GHResponse rsp = hopper.route(req); // handle errors if (rsp.hasErrors()) throw new RuntimeException(rsp.getErrors().toString()); // use the best path, see the GHResponse class for more possibilities. ResponsePath path = rsp.getBest(); // points, distance in meters and time in millis of the full path PointList pointList = path.getPoints(); double distance = path.getDistance(); long timeInMs = path.getTime(); Translation tr = hopper.getTranslationMap().getWithFallBack(Locale.UK); InstructionList il = path.getInstructions(); // iterate over all turn instructions for (Instruction instruction : il) { // System.out.println("distance " + instruction.getDistance() + " for instruction: " + instruction.getTurnDescription(tr)); } assert il.size() == 6; assert Helper.round(path.getDistance(), -2) == 600; }

要点解读:

  • setProfile("car")必须的——即使实例中只有一个 profile 也要显式指定;
  • setLocale(Locale.US)决定转向指示(turn instructions)的语言,可配合hopper.getTranslationMap()按需获取其他语言(GraphHopper 支持超过 45 种语言的转向指示);
  • rsp.hasErrors()是标准的错误处理入口,rsp.getErrors()会返回异常集合(如不可达点、参数非法等);
  • rsp.getBest()取最优路径,返回ResponsePath,可继续读取getPoints()(几何点列)、getDistance()(米)、getTime()(毫秒)、getInstructions()(转向指示列表)等;
  • 文档中提示,本示例与后续章节的完整代码均位于 example/src/main/java/com/graphhopper/example/RoutingExample.java。

Heading:约束起终点方向

文档指出,Flexible 与 Hybrid 模式下可以为任意一个点添加期望的 heading(以正北为基准的方位角,0~360 度)。添加 heading 后,朝向其他方向的道路会被惩罚,从而让路线更可能以指定方向出发/到达。Speed 模式(CH)目前不支持 heading 参数,因此使用 heading 时需要通过ch.disable=true关闭速度模式(详见 docs/core/heading.md)。

关于 heading 的几个硬性规则:

  • NaN表示该点不强制方向;
  • 超出[0, 360]的取值会抛出IllegalArgumentException
  • 关键语义:在 via 点或终点上强制方向时,需要指定的是"离开该点的方向"(outgoing heading)。例如想强制"从南边进入终点",应当填写"朝北离开"即 0 度。

GHRequest.setHeadings(List<Double>)是 Java API 的入口(GHRequest.java),headings 列表长度可以为 0、1(仅起点)或等于点数。

惩罚参数 heading_penalty

惩罚力度由heading_penalty参数控制,其语义是"放弃指定 heading 所接受的额外时间延迟(秒)"。当前源码中默认值为 300 秒,定义于 web-api/src/main/java/com/graphhopper/util/Parameters.java(Parameters.Routing.DEFAULT_HEADING_PENALTY),并在 core/src/main/java/com/graphhopper/routing/DefaultWeightingFactory.java 中作为请求 hint 的兜底默认值使用;该值也可在自定义模型中显式设置(见 core/src/main/java/com/graphhopper/routing/weighting/custom/CustomModelParser.java)。将heading_penalty调低(例如 10 秒)会使 heading 约束显著放松,路线可能回到与不设 heading 时一致的结果。

实测效果

heading.md中给出了一个完整用例:从42.566757, 1.59775142.567396, 1.597807,分别演示了不设 heading、设置起点朝西(270°)、起点 270° + 终点朝南(180°,即强制"从北边来")、以及仅设置终点方向(起点填NaN)四种情况。对比可见设置 heading 后返回了不同的路线:

(上方两图由 heading 文档将结果坐标 LineString 转成 GeoJSON 后绘制,用于直观对比方向约束对路径选择的影响。)

对应的 Java 示例为 example/src/main/java/com/graphhopper/example/HeadingExample.java;底层行为的单元测试可参见 core/src/test/java/com/graphhopper/routing/HeadingAndCustomModelRoutingTest.java,其中testStartDirectiontestStartEndDirectiontestViaDirection等用例在小图上验证了起点、终点、via 点方向约束对所选边序列的影响,testHeadingWithSnapFilter则验证了 heading 与点吸附(snap)过滤的交互。

备选路线(Alternative Routes)

所有模式都支持计算备选路线,但 CH 使用的算法与 LM/Flexible 模式不同:CH 场景走AlternativeRouteCH(core/src/main/java/com/graphhopper/routing/ch 模块),而非 CH 场景使用AlternativeRoute。无论哪种模式,开启备选路线都会对查询性能产生影响。

请求时通过setAlgorithm(Parameters.Algorithms.ALT_ROUTE)(即"alternative_route")启用,并通过 hints 调整三个核心参数(常量见 Parameters.java):

参数常量作用
alternative_route.max_pathsAltRoute.MAX_PATHS最多返回多少条备选路线
alternative_route.max_weight_factorAltRoute.MAX_WEIGHT备选路线权重相对最优路线权重的最大倍数,大于该倍数的备选会被过滤
alternative_route.max_share_factorAltRoute.MAX_SHARE备选路线与最优路线共享路段的最大比例(权重单位),用于剔除过于相似的路线

示例中的用法:

public static void alternativeRoute(GraphHopper hopper) { // calculate alternative routes between two points (supported with and without CH) GHRequest req = new GHRequest().setProfile("car"). addPoint(new GHPoint(42.506701, 1.521668)).addPoint(new GHPoint(42.509533, 1.540185)). setAlgorithm(Parameters.Algorithms.ALT_ROUTE); req.getHints().putObject(Parameters.Algorithms.AltRoute.MAX_PATHS, 3); GHResponse res = hopper.route(req); if (res.hasErrors()) throw new RuntimeException(res.getErrors().toString()); assert res.getAll().size() == 2; assert Helper.round(res.getBest().getDistance(), -2) == 1800; }

注意示例中虽然请求了max_paths = 3,实际返回 2 条(res.getAll().size() == 2)——因为其余候选未通过 weight/share 过滤条件。res.getAll()返回全部候选路径,res.getBest()是最优的那条。

从 core/src/main/java/com/graphhopper/routing/AlternativeRoute.java 的源码注释可以确认其实现思路:采用论文中描述的plateau 方法(并部分采用 penalty 方法)寻找备选路径,构造基于"共同前缀/后缀"的思想保证备选路线与最优路线有明显差异;该类继承自AStarBidirection,本身就是一个双向 A* 算法。源码还特别提示:该算法在长路线上可能较慢,备选路线最实用的组合是配合 CH(见AlternativeRouteCH)。maxWeightFactor(默认过滤权重超长的备选)、maxShareFactor(限制与最优路线的重叠度)、explorationFactor(控制探索范围,调高可能得到更多备选但查询更慢)等内部参数都在该类的字段注释中有详细说明。测试用例可参考 core/src/test/java/com/graphhopper/routing/AlternativeRouteTest.java,其中通过putObject("alternative_route.max_paths", 3)等写法验证了这些 hint 的实际效果。

Java 与 Android 客户端:client-hc

如果你要调用的是 GraphHopper Directions API 或自建 GraphHopper 服务的 HTTP 接口,而不是把引擎嵌入进程内,可以直接使用官方client-hc(Java 与 Android 客户端,位于 client-hc,用法见 client-hc/README.md)。其核心类是 client-hc/src/main/java/com/graphhopper/api/GraphHopperWeb.java,基于 OkHttp 实现,支持 GZIP 压缩、GET/POST 两种请求方式,并内置X-GH-Client-Version版本头。

GraphHopperAPI gh = new GraphHopperWeb(); gh.load("http://your-graphhopper-service.com"); // or for the GraphHopper Directions API https://graphhopper.com/#directions-api // gh.load("https://graphhopper.com/api/1/route"); GHResponse rsp = gh.route(new GHRequest(...));

要点:

  • GraphHopperWeb无参构造默认指向https://graphhopper.com/api/1/route(即 Directions API 的路由端点,GraphHopperWeb.java),也可以通过构造参数或load(url)指向自建服务;
  • gh.route(...)接受与内嵌 API 相同的GHRequest对象,返回相同的GHResponse结构,因此切换"进程内引擎"与"远程服务"两种模式几乎不改变业务代码;
  • 与 Directions API 或自建服务通信时,请求/响应模型由 web-api 模块的 Jackson 序列化层定义(GraphHopperModule.java 等),client-hc 复用了同一套模型;
  • 相关测试见 client-hc/src/test/java/com/graphhopper/api/GraphHopperWebTest.java 与 client-hc/src/test/java/com/graphhopper/api/Examples.java。

除 Java 外,GraphHopper 生态还提供 JavaScript 等多种语言的服务端客户端,但那是独立仓库,不在本仓库范围内。

进一步阅读

  • docs/core/profiles.md:profile 定义、custom_model/custom_model_filesturn_costs以及profiles_ch/profiles_lm配置详解;
  • docs/core/heading.md:Heading 参数完整文档与 HTTP 请求示例;
  • docs/core/ch.md 与 docs/core/landmarks.md:Speed 模式(Contraction Hierarchies)与 Hybrid 模式(Landmarks)的原理细节;
  • docs/core/custom-models.md:自定义模型规范,支持请求级别的自定义路由;
  • docs/core/low-level-api.md:更底层的 Java 使用方式;
  • docs/web/api-doc.md:HTTP Web API 参数说明(profilepointheadingch.disable等均可在 URL 中直接使用);
  • docs/index.md:GraphHopper 全部文档索引。

【免费下载链接】graphhopperOpen source routing engine for OpenStreetMap. Use it as Java library or standalone web server.项目地址: https://gitcode.com/GitHub_Trending/gr/graphhopper

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询