ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

GraphHopper 权重(Weighting)机制完全指南:从边权成本函数到自定义权重实现

GraphHopper 权重(Weighting)机制完全指南:从边权成本函数到自定义权重实现 GraphHopper 权重Weighting机制完全指南从边权成本函数到自定义权重实现【免费下载链接】graphhopperOpen source routing engine for OpenStreetMap. Use it as Java library or standalone web server.项目地址: https://gitcode.com/GitHub_Trending/gr/graphhopper导读本文聚焦 GraphHopper 路由引擎的核心抽象——Weighting权重深入讲解边的权重如何定义最短路径的成本函数、为什么新项目应优先采用 CustomWeighting以及如何从零实现一个自定义 Weighting 并通过WeightingFactory接入GraphHopper。读完本文你将掌握 Weighting 接口的全部方法语义、基于AbstractAdjustedWeighting做局部改写的技巧以及速度模式CH下按请求动态改权的约束与ch.disabletrue的正确用法。Weighting 是什么一条边的成本在 GraphHopper 中Weighting 负责为每一条边计算一个权重weight这个权重反映了沿着该边行驶的代价。所有 RoutingAlgorithm 实现计算的都是最短路径即 A 到 B 之间总体成本最低的路径经典的最短路径问题。成本函数的定义完全由你决定——你可以定义一个最快路径的成本函数GraphHopper 称之为 weighting此时引擎依然计算成本最低的路径只是成本被你定义为距离、速度等因素的某种组合。也就是说Weighting 是路径偏好的载体同样的路网把权重定义为纯距离得到最短路径把权重定义为时间得到最快路径把权重定义为时间加拥堵惩罚就得到避开拥堵的最优路径。核心接口Weighting的方法语义Weighting是com.graphhopper.routing.weighting包下的核心接口定义于 core/src/main/java/com/graphhopper/routing/weighting/Weighting.java。其核心方法如下方法作用关键约束来自源码 Javadoc 与实现calcMinWeightPerDistance()仅用于 A* 的启发式估计返回每米的最小权重最快路径返回1/max_velocity最短路径返回1calcEdgeWeight(EdgeIteratorState, boolean reverse)计算给定边的权重返回值必须在0到Infinity之间要求是整数建议用roundWeight后处理不能返回NaN如 0/0 场景该方法会被调用数百万次必须极度优化calcEdgeMillis(EdgeIteratorState, boolean reverse)计算沿边行驶的时间毫秒主要用于后处理只会在几千条边上调用calcTurnWeight(int inEdge, int viaNode, int outEdge)计算转弯权重用于基于边edge-based的转向代价calcTurnMillis(int inEdge, int viaNode, int outEdge)计算转弯耗时毫秒同上hasTurnCosts()是否产生非零转弯代价用于安全校验并非所有图算法都能以 edge-based 运行若实际有转弯代价却以 node-based 运行可能产生错误结果getName()返回权重名称名称必须通过Weighting.isValidName校验Weighting接口还提供两个重要的静态工具方法isValidName(name)名称只能由小写字母、下划线和竖线组成正则[\\|_a-z]WeightingTest 中的testToString用例验证了blup、blup_a、blup|a合法而Blup、Blup!非法roundWeight(w)权重必须取整。特别地0 w 0.5时会向上取整为1——因为权重为 0 会给最短路径带来歧义NaN会被断言拒绝Infinity原样保留。一个直观的参照实现是 SpeedWeighting速度权重其calcEdgeWeight展示了权重 时间的典型算法Override public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { double speed reverse ? edgeState.getReverse(speedEnc) : edgeState.get(speedEnc); if (speed 0) return Double.POSITIVE_INFINITY; // 不可通行 return Weighting.roundWeight(10 * edgeState.getDistance() / speed); }可以看到权重 10 × 距离 ÷ 速度即边权重以秒 × 10为单位无法通行的边速度为 0直接返回Infinity。calcMinWeightPerDistance则返回10.0 / speedEnc.getMaxStorableDecimal()为 A* 提供每米最小权重的下界估计。首选方案使用 CustomWeighting 而非新写一个 Weighting原文档明确建议与其实现一个新的 Weighting强烈推荐使用 CustomWeighting其完整说明见 profiles 与 custom models 章节。CustomWeighting 位于 core/src/main/java/com/graphhopper/routing/weighting/custom/CustomWeighting.java名称固定为custom。它的边权重核心公式calcEdgeWeight是return roundWeight(10 * (Math.min(seconds / priority, seconds 100_000) distanceCosts));其中seconds由边速speed与距离换算得到SPEED_CONV 3.6即 km/h → m/s 的换算系数priority优先级来自自定义模型中的优先级表达式若优先级为 0 则边不可通行返回InfinityMath.min(seconds / priority, seconds 100_000)将优先级导致的权重放大限制在约 28 小时以内防止极小优先级因子例如对同一条边反复应用 0.001造成权重爆炸distanceInfluence距离影响因子以s/km为单位传入构造时除以 1000 换算为s/m且不允许为负它把距离作为独立成本项叠加进权重——这正是老版short_fastest/shortest权重所表达的思想。这一趋势在当前仓库中体现得更加彻底在 DefaultWeightingFactory 的createWeighting中shortest、fastest、curvature、short_fastest等旧权重名称已不再被支持直接抛出带迁移提示的IllegalArgumentExceptionInstead of weightingshortest use weightingcustom with a high distance_influence Instead of weightingfastest use weightingcustom with a custom model that avoids road_access DESTINATION Instead of weightingshort_fastest use weightingcustom with a distance_influence从当前仓库源码结构看原文档中链接的ShortestWeighting、FastestWeighting类已从core模块移除其能力被 CustomWeighting 的distance_influence、优先级表达式等参数完全承接。因此写权重逻辑的首选入口是 JSON 自定义模型而不是 Java 类。实现自己的 Weighting三步接入如果你的需求确实无法用 CustomWeighting 表达例如需要引入自定义的边属性或复杂状态原文档给出了完整的三步流程实现Weighting接口与对应的WeightingFactory让GraphHopper类知道你的WeightingFactory——通过覆盖createWeightingFactory方法。第一步实现 Weighting 接口一个良好的起点是参照 SpeedWeighting 这类简单实现在本文当前版本中短路径权重可参考 CustomWeighting 的distance_influence参数化方式。你的实现需要覆盖接口中的全部方法并至少满足calcEdgeWeight必须足够快——它可能在一次路由中被调用数百万次预处理阶段调用次数更多权重必须非负、非 NaN 且为整数用Weighting.roundWeight收尾calcMinWeightPerDistance要为 A* 提供正确且尽量紧的下界否则影响启发式搜索的效率getName()返回的字符串必须通过Weighting.isValidName校验且各实现之间必须唯一因为该名称会参与 profile 的解析与匹配。第二步实现 WeightingFactoryWeightingFactory是com.graphhopper.routing包下的工厂接口定义于 core/src/main/java/com/graphhopper/routing/WeightingFactory.java。仓库内置的默认实现是 DefaultWeightingFactory其工厂方法签名为public Weighting createWeighting(Profile profile, PMap requestHints, boolean disableTurnCosts)从DefaultWeightingFactory的实现可以看到工厂内部的真实逻辑合并 hints将 profile 的 hints 与请求级 hints 合并请求级参数优先源码注释明确说明目前尚未校验覆盖 profile hints 是否与 CH/LM 预处理兼容按名称分发profile.getWeighting()小写化后与custom比较命中则走 CustomWeighting 分支——解析custom_model、合并profile.getCustomModel()与请求级模型、按需装配TurnCostProvider处理 turn_costs仅当 profile 开启turn_costs且未禁用时才创建带转向代价的DefaultTurnCostProvider否则使用NO_TURN_COST_PROVIDER兜底报错未知的 weighting 名称统一抛出Weighting xxx not supported。自定义工厂可以完全复刻这一骨架解析配置 → 构造你的 Weighting 实例 → 返回。第三步覆盖createWeightingFactoryGraphHopper基类中createWeightingFactory是一个可覆盖的 protected 方法见 core/src/main/java/com/graphhopper/GraphHopper.javaprotected WeightingFactory createWeightingFactory() { return new DefaultWeightingFactory(baseGraph.getBaseGraph(), getEncodingManager()); }你的子类只需覆盖该方法并返回自定义工厂实例即可例如public class MyGraphHopper extends GraphHopper { Override protected WeightingFactory createWeightingFactory() { return new MyWeightingFactory(baseGraph.getBaseGraph(), getEncodingManager()); } }此后路由内部Router.java 的createWeighting调用链会通过该工厂为每个 profile 创建权重你的自定义权重即被全流程使用。小改动优先继承 AbstractAdjustedWeighting如果你只想在已有权重的基础上修改一小部分行为直接继承 AbstractAdjustedWeighting 是个好主意。这个抽象类采用装饰器模式内部持有superWeighting所有方法默认透传给被包装的权重calcEdgeWeight、calcEdgeMillis、calcTurnWeight、calcTurnMillis、hasTurnCosts、calcMinWeightPerDistance你只需覆盖想改的方法并在toString()中以getName() | superWeighting的形式标明包装关系。仓库中现成的范例是 AvoidEdgesWeighting——它把一组边 ID 加入黑名单并给这些边乘以一个惩罚因子从而降低它们被选入最短路径的可能性Override public double calcEdgeWeight(EdgeIteratorState edgeState, boolean reverse) { double weight superWeighting.calcEdgeWeight(edgeState, reverse); if (avoidedEdges.contains(edgeState.getEdge())) return weight * edgePenaltyFactor; // 默认惩罚因子 5.0 return weight; } Override public String getName() { return avoid_edges; }其构造器要求传入一个非空的superWeighting并提供setEdgePenaltyFactor(double)默认5.0与setAvoidedEdges(IntSet)两个链式配置入口。这种包装 局部重写的模式非常适合实现避开收费路段、避开施工路段等策略。性能模式约束为什么动态权重必须用ch.disabletrue这是原文档强调的关键坑点如果你的权重会随请求而变化per-request就不能使用速度模式speed mode / CH而必须使用混合模式hybrid mode / LM或灵活模式flexible mode。原因在于 CHContraction Hierarchies在预处理阶段就把边权重固化进了压缩图结构任何按请求动态改权的行为都会使预处理结果失效。若你的配置没有禁用速度模式就需要在请求 URL 中追加ch.disabletrue来为本次请求关闭 CH。这一约束在源码中有多处硬性校验见 core/src/main/java/com/graphhopper/routing/Router.javaheading参数The heading parameter is currently not supported for speed mode, you need to disable speed mode with ch.disabletruepass_through参数同样要求ch.disabletrue请求级custom_modelThe custom_model parameter is currently not supported for speed mode, you need to disable speed mode with ch.disabletrue兜底建议You can try disabling CH using ch.disabletrue。从 Router.java 看Router通过hints.getBool(Parameters.CH.DISABLE, false)读取该开关默认false即默认走 CH。对应地在 config-example.yml 的 profiles 配置段中可以看到三种模式的配置方式profiles: - name: car custom_model_files: [car.json] # 默认 weighting 为 custom - name: foot custom_model_files: [foot.json, foot_elevation.json] # 速度模式为指定 profile 构建 CH更快但权重被固化 profiles_ch: - profile: car # 混合模式Landmarks 预处理速度略慢于 CH 但更灵活 profiles_lm: []配置注释还特别提醒只有在某个 profile 上所有边的权重都等于或大于≥预处理所用 profile 的权重时多个 profile 复用同一份 LM 预处理数据才是安全的——这正是权重必须与预处理假设一致这一原则的另一体现。从请求到权重的完整调用链把以上内容串起来一次路由请求中 Weighting 的完整生命周期是客户端发起请求携带 profile 名称与请求 hints如ch.disabletrue、custom_model等GraphHopper 通过createWeightingFactory()默认DefaultWeightingFactory拿到工厂工厂的createWeighting(profile, hints, disableTurnCosts)合并 profile 与请求 hints、解析自定义模型、装配转向代价提供者产出最终的Weighting实例GraphHopper.java路由算法如 Dijkstra、A*在遍历边时反复调用calcEdgeWeight以该权重作为搜索代价路径确定后calcEdgeMillis、calcTurnMillis用于生成时间类后处理信息ETA、指令等。如果你决定实现自己的WeightingFactory只需要在第 2、3 步之间插入你的工厂即可其余流程完全复用。小结Weighting 定义边的成本最短路径 总权重最低的路径权重公式由你决定优先 CustomWeighting当前版本已移除shortest/fastest/curvature/short_fastest等旧权重类统一由custom 自定义模型distance_influence、priority、speed 表达式承担配置入口见 config-example.yml 与 profiles自研三步骤实现Weighting→ 实现WeightingFactory→ 覆盖GraphHopper.createWeightingFactory局部改写用装饰器继承 AbstractAdjustedWeighting参照 AvoidEdgesWeighting按请求动态改权必须绕过 CH在请求 URL 中追加ch.disabletrue这是 Router.java 中的硬性约束。深入阅读profilesprofile 与预处理模式、custom-models自定义模型语法、weighting 包源码全部内置实现、CustomWeightingTest权重公式的测试用例。【免费下载链接】graphhopperOpen source routing engine for OpenStreetMap. Use it as Java library or standalone web server.项目地址: https://gitcode.com/GitHub_Trending/gr/graphhopper创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表