
GraphHopper Profiles 完全指南从 config.yml 配置到自定义模型与预处理模式【免费下载链接】graphhopperOpen source routing engine for OpenStreetMap. Use it as Java library or standalone web server.项目地址: https://gitcode.com/GitHub_Trending/gr/graphhopper本文围绕 GraphHopper 路由引擎的核心概念Profile路由画像展开它决定了引擎如何为不同出行方式汽车、自行车、步行等评估与优先选择道路。通过阅读本文你将掌握在config.yml中定义 Profile 的完整语法、turn_costs 与自定义模型Custom Model的用法、CH/LM 预处理模式的启用方式以及如何在路由请求中按需合并自定义模型实现细粒度、可动态调整的路径规划。Profile 是什么一次路由计算的画像GraphHopper 允许你自定义不同类型道路在路径计算中被优先选择的程度。例如长途驾车时通常希望优先高速公路以最小化行驶时间而骑行时则不希望走高速公路更愿意选择较短的路线、专用的自行车道等。GraphHopper 为此提供了内置的车辆 Profilecar、bike、foot 等覆盖常见场景并允许通过**自定义模型custom model**对其做精细修改例如针对特定道路类型调整行驶速度。从源码结构看Profile 在 Java 侧对应 Profile.java对应config.yml中profiles段的每一项其核心字段包括name唯一的字符串标识符后续 CH/LM 预处理、路由请求均以此引用weighting默认为custom即使用自定义权重模型turn_costs转向成本配置是否启用转向限制、U 型转弯惩罚等custom_model/custom_model_files自定义模型本体或模型文件列表hints其他提示项如instructions_base_mode。一个 Profile 由自定义模型与转向限制共同定义。所有 Profile 都在config.yml的profiles段中声明且至少需要定义一个 Profile否则 GraphHopper 无法启动路由服务。在 config.yml 中定义 Profiles所有 Profile 均定义在config.yml的profiles段中最简单的形式如下profiles: - name: car custom_model_files: [car.json] - name: my_bike custom_model_files: [bike_elevation.json]通过指定自定义模型文件GraphHopper 会据此确定各道路类型的可达性accessibility与默认行驶速度。在仓库自带的 config-example.yml 中可以看到官方推荐的三段式配置profiles: - name: car custom_model_files: [car.json] - name: foot custom_model_files: [foot.json, foot_elevation.json] - name: bike custom_model_files: [bike.json, bike_elevation.json]注意一个 Profile 可以引用多个自定义模型文件它们会被依次合并使用例如bike.json定义基础骑行行为bike_elevation.json再叠加坡度影响。关于 name 的命名约束从 Profile.java 的validateProfileName方法可以看到Profile 名称必须匹配正则^[a-z0-9_\-]$即只允许小写字母、数字、下划线和连字符否则会抛出IllegalArgumentException。该约束同样作用于 CH/LM 预处理条目中引用的 Profile 名见 CHProfile.java 与 LMProfile.java。在路由请求中选择 ProfileProfile 名称用于在路由查询时选择使用哪个 Profile通过profile请求参数指定/route?point49.5,11.1profilecar /route?point49.5,11.1profilesome_other_profile即服务端配置了哪些 Profile客户端就只能用这些名称发起请求除非使用后文介绍的按请求自定义模型仍然需要指定一个已存在的profile参数作为基础。启用转向成本turn_costs另一个重要的 Profile 设置是turn_costs用于为每个 Profile 启用转向限制turn restrictionsprofiles: - name: car turn_costs: vehicle_types: [motorcar, motor_vehicle] custom_model_files: [car.json]config-example.yml 中给出了更完整的turn_costs可选字段turn_costs: vehicle_types: [motorcar, motor_vehicle] # 用于车辆特定转向限制的车辆类型 u_turn_costs: 60 # U 型转弯的时间惩罚秒 allow_turn_penalty_in_request: true # 允许在请求时动态设置 turn_penaltyvehicle_types声明该 Profile 识别哪些 OSM 车辆类型如motorcar、motor_vehicle、bicycle、bus用于车辆特定的转向限制u_turn_costs执行一次 U 型转弯所付出的时间惩罚秒allow_turn_penalty_in_request是否允许在运行时请求中设置自定义的 turn_penalty。在 Java API 侧Profile.hasTurnCosts()通过判断turnCostsConfig ! null来确定是否启用了转向成本。关于转向限制的完整机制可参考 docs/core/turn-restrictions.md。值得注意的是迁移指南 docs/migration/config-migration-08-09.md 提到旧版直接在 profile 中写u_turn_costs或vehicle的方式已不再被接受统一收敛到turn_costs配置段——这一点在 Profile.java 的putHint中也有硬性校验使用旧写法会直接抛出异常。custom_model在配置中内联自定义模型除引用外部模型文件外还可以直接在 Profile 定义中内联custom_model。自定义模型构建在vehicle基础之上Profile 从基础车辆继承道路可达性规则与不同道路类型的默认速度但可以通过一组规则改写这些默认值profiles: - name: my_custom_profile vehicle: car custom_model: { speed: [ { if: road_class MOTORWAY, multiply_by: 0.8 } ] }上述配置的含义是以car为基础车辆对road_class MOTORWAY高速公路的道路将速度乘以 0.8。自定义模型支持speed速度、priority优先级、turn_penalty转向惩罚等语句块其完整语法规范在 docs/core/custom-models.md 中有详尽说明。从 CustomModel.java 的源码可以看到一个 CustomModel 包含四个主要部分distanceInfluence距离影响因子Double类型用包装类型是为了区分显式设为 0与未指定两种情况headingPenalty朝向惩罚speedStatements、priorityStatements、turnPenaltyStatements三类规则语句Statement列表areas自定义地理区域集合JsonFeatureCollection用于基于区域的规则。使用 custom_model_files 与 custom_models.directory除了custom_model内联写法还可以用custom_model_files指定模型文件路径并可配合custom_models.directory指定模型文件所在目录。前面第一个示例就是用这种方式通过内置自定义模型bike_elevation.json基于海拔变化修改速度。所有内置自定义模型位于仓库目录core/src/main/resources/com/graphhopper/custom_models下包括模型文件适用场景car.json标准小汽车car4wd.json四驱越野车truck.json卡车bus.json公交车motorcycle.json摩托车bike.json/mtb.json/racingbike.json/cargo_bike.json各类自行车foot.json/hike.json步行/徒步bike_elevation.json/foot_elevation.json坡度对速度的影响curvature.json道路弯曲程度影响avoid_turns.json减少转弯次数以 car.json 为例其内容展示了自定义模型的典型结构{ distance_influence: 90, priority: [ { if: !car_access, multiply_by: 0 } ], speed: [ { if: road_environment FERRY, limit_to: ferry_speed }, { else: , limit_to: car_average_speed }, { if: true, limit_to: max_speed * 0.9 } ] }而 bike_elevation.json 展示了基于average_slope平均坡度的分段速度调整逻辑{ speed: [ { if: average_slope 15, limit_to: 3 }, { else_if: average_slope 12, limit_to: 6 }, { else_if: average_slope 8, multiply_by: 0.60 }, { else_if: average_slope 4, multiply_by: 0.90 }, { else_if: average_slope -4, multiply_by: 1.10 } ] }如果不想做任何速度/优先级调整可以保持自定义模型为空custom_model: {}或custom_model_files: []。配置custom_models.directory后custom_model_files中的文件名会在该目录中查找官方注释建议将自建模型放入该目录参见 config-example.yml 中的# custom_models.directory: custom_models示例。设置 Encoded Values自定义模型依赖encoded values编码值这些值通常由 OSM 道路标签派生而来。所有内置 encoded values 定义在 DefaultEncodedValueFactory.java 中但只有在config.yml的graph.encoded_values字段中列出的 encoded values 才会被写入图存储graph storage也才能在自定义模型中使用。config-example.yml 给出了典型配置graph.encoded_values: | car_access, car_average_speed, country, road_class, roundabout, max_speed, road_environment, foot_access, foot_average_speed, foot_priority, foot_road_access, hike_rating, average_slope, bike_access, bike_average_speed, bike_priority, bike_road_access, bike_network, mtb_rating, ferry_speed常见编码值还包括average_slope、country、curvature、hazmat、hgv、hike_rating、lanes、max_height、max_length等。值得注意的是car_access、bike_access等可达性编码值默认会阻止私有道路private roads如需放行可写成car_access|block_privatefalse。具体某个内置自定义模型需要哪些 encoded values通常在模型文件头部注释中都有说明——例如 car.json 开头注明了所需配置。若配置缺失GraphHopper 启动时会打印提示告知需要把哪些 encoded values 加入graph.encoded_values。Speed 与 Hybrid 模式启用预处理加速GraphHopper 可以在导入import阶段对路由 Profile 做预处理从而大幅提升路径计算速度。做法是在config.yml中把需要预处理的 Profile 分别列入profiles_ch与profiles_lm段# 需要使用 speed mode速度模式的 profiles 放在这里 profiles_ch: - profile: car - profile: some_other_profile # 需要使用 hybrid mode混合模式的 profiles 放在这里 profiles_lm: - profile: car - profile: some_other_profile其中CH是Contraction Hierarchies收缩层级的缩写是 speed mode 的底层技术查询最快但灵活性最低LM是Landmarks地标的缩写是 hybrid mode 使用的算法速度不如 CH 快但更灵活例如支持按请求自定义模型。profile下给出的值必须与profiles段中定义的 Profile 名称完全一致。在 GraphHopper.java 的初始化逻辑中会逐一校验profiles_ch/profiles_lm引用的名称是否在profiles中存在否则直接抛出异常同时禁止同一个 Profile 被多个 LM 条目重复引用也要求preparation_profile指向已存在的 Profile。关于不同模式的更多细节参见 docs/core/routing.md。config-example.yml 中还给出了一些预处理调优参数# prepare.ch.threads: 1 # 多 Profile 同时做 CH 预处理时的线程数需足够内存 # prepare.lm.landmarks: 16 # hybrid 模式的地标数量用于权衡性能与内存 # prepare.lm.threads: 1 # 并行执行地标预处理时的线程数这些参数默认值即可满足大多数场景仅在有充分理由时才调整。Hybrid 模式的预处理复用preparation_profilehybrid 模式LM有一个特殊能力为不同 Profile 复用同一份预处理数据。做法如下profiles_lm: - profile: car - profile: some_other_profile preparation_profile: car含义是some_other_profile将复用carProfile 的 LM 预处理结果从而节省内存与预处理时间。在 LMProfile.java 中preparationProfile的默认值为this即使用自己的预处理一旦显式设置为其他 Profile 名称usesOtherPreparation()返回true。但该特性有严格前提只有当some_other_profile在所有边上的权重都大于或等于carProfile 的权重时复用才是正确的否则会破坏算法的最优性保证。源码中还有一条硬性约束preparation_profile与maximum_lm_weight不能同时使用同时设置会抛出IllegalArgumentException。官方文档明确建议除非你确切知道自己在做什么否则不要使用这个特性。按请求自定义模型per-request custom model前面讨论的都是服务端在config.yml中预先配置的 Profile。但在flex灵活模式与 hybrid混合模式下还可以在每次路由请求中动态传入自定义模型从而使用服务端配置时未曾预料到的自定义规则。使用方式在路由请求POST /route中以 JSON 格式附带custom_model字段。其语法与服务端自定义模型相同只是 JSON 而非 YAML 记法同时仍然必须设置profile参数。例如{ points: [ [ 11.58199, 50.0141 ], [ 11.5865, 50.0095 ] ], profile: my_custom_car, custom_model: { speed: [ { if: road_class MOTORWAY, multiply_by: 0.8 }, { else: , multiply_by: 0.9 } ], priority: [ { if: road_environment TUNNEL, multiply_by: 0.95 } ], distance_influence: 0.7 } }而config.yml中对应配置为custom_models.directory: path/to/my/custom/models profiles: - name: my_custom_car vehicle: car custom_model_files: [my_custom_car.json]其中my_custom_car.json内容如下{ speed: [ { if: surface GRAVEL, limit_to: 100 } ] }两个自定义模型如何合并请求中同时存在两个自定义模型——请求里传入的与profile参数对应 Profile 自带的。答案很简单两者会被合并为一个。合并规则是将请求自定义模型的所有表达式追加到服务端自定义模型之后distance_influence字段若在请求自定义模型中指定则覆盖服务端值未指定则保留服务端值。于是上述例子最终用于本次路由计算的自定义模型为{ speed: [ { if: surface GRAVEL, limit_to: 100 }, { if: road_class MOTORWAY, multiply_by: 0.8 }, { else: , multiply_by: 0.9 } ], priority: [ { if: road_environment TUNNEL, multiply_by: 0.95 } ], distance_influence: 0.7 }注意合并顺序服务端模型surface GRAVEL规则在前请求模型MOTORWAY、else 规则在后。这一行为在 CustomModel.java 的merge静态方法中有直接实现它先深拷贝服务端模型避免修改服务端缓存随后若请求模型的distanceInfluence、headingPenalty非空则覆盖对应字段再把请求模型的 speed/priority/turn_penalty 语句与 areas 追加进合并结果。Hybrid 模式下的两条硬性限制如果使用 hybrid 模式即使用 Landmarks 而非纯 Dijkstra 或 A*合并过程必须保证合并后自定义模型产生的所有边的权重都等于或大于预处理时基础 Profile 的权重这是维持底层路由算法最优性的必要条件。由此带来两条限制请求自定义模型中所有multiply_by的值必须位于[0, 1]区间内否则会抛出错误请求自定义模型的distance_influence不得小于已有服务端的值。原因直观multiply_by大于 1 或distance_influence变小都会让某些边在请求模型下的权重小于预处理基准从而破坏 LM 的启发式保证。flex 模式无预处理则不受这两条限制。从配置到图存储Profile 与预处理的完整生命周期将上述内容串联起来一个 Profile 从配置到可用的完整流程大致如下配置解析config.yml的profiles段被反序列化为Profile对象列表profiles_ch/profiles_lm分别对应 CHProfile.java 与 LMProfile.java校验名称正则校验、CH/LM 引用存在性校验、preparation_profile与maximum_lm_weight互斥校验等见 GraphHopper.java 中setProfiles与预处理设置逻辑导入与预处理导入 OSM 数据时仅将graph.encoded_values中声明的编码值写入图存储对列入profiles_ch/profiles_lm的 Profile 分别执行 CH/LM 预处理结果存入图缓存请求路由客户端通过profile参数选择 Profileflex/hybrid 模式下可附加custom_model经CustomModel.merge合并后参与计算。关于 Profile 与图存储的版本一致性Profile.java 的getVersion()会基于 Profile 内容生成哈希GraphHopper 加载已有图时会比对存储的 Profile 哈希若新增或修改 Profile 会提示需要重新导入避免旧缓存与新配置不匹配导致的错误路由结果。总结Profile 是 GraphHopper 路由定制化的基石通过profiles段定义基础出行方式与默认行为通过turn_costs控制转向限制通过自定义模型内联或文件精细调整各类道路的速度与优先级通过profiles_ch/profiles_lm按需启用预处理加速再配合 per-request custom model 实现请求级的灵活覆盖。理解这些配置项及其底层合并、校验逻辑即可按业务场景搭建出准确、高效的路径规划服务。完整的自定义模型语法请继续阅读 docs/core/custom-models.md不同路由模式的选型说明见 docs/core/routing.md。【免费下载链接】graphhopperOpen source routing engine for OpenStreetMap. Use it as Java library or standalone web server.项目地址: https://gitcode.com/GitHub_Trending/gr/graphhopper创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考