1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

到達圏分析をローカル環境で実現する — GraphHopperで徒歩10分圏を生成する方法

1
Posted at

はじめに

Google Maps Platform や Mapbox など、クラウドAPIを利用した経路探索や到達圏分析が一般的になっています。
しかし、こうした外部サービスには コスト・API制限・データ共有の制約 など、運用上の課題も少なくありません。

そこで本記事では、OpenStreetMap(OSM)データを使ってローカル環境でGraphHopperを動かし、
徒歩10分圏などの到達圏(Isochrone)を生成する方法
を解説します。

GraphHopperはJava製の高性能なルーティングエンジンであり、
クラウドに依存せずに、自前でOSMデータを解析・APIサーバとして提供 できます。

本記事では、徒歩到達圏をローカル環境で生成・可視化する手順をハンズオン形式で紹介します。

対象読者

  • OpenStreetMapを活用したルーティング・到達圏分析を行いたいエンジニア
  • 商用APIを避けて自社・ローカル環境で完結させたい方
  • GraphHopperを実際に動かし、Isochrone APIを試してみたい方

このあとの章では、GraphHopperの構成と仕組みを確認しながら、
実際に .osm.pbf ファイルを読み込み、徒歩到達圏をGeoJSONで出力・地図上で可視化するまでを解説します。

ローカル実装の意義とGraphHopperの位置づけ

Google Maps Platform や Mapbox などのクラウドAPIで経路探索や到達圏分析を行うことが一般的になりました。
しかし、商用APIにはコストや仕様変更、データ共有の制約といった課題もあります。
到達圏検索をローカル環境で実現する3つの方法 — OSMデータの活用

こうした課題を解決する方法の一つが、
OpenStreetMap(OSM)データを使って自前のルーティングサーバを構築すること です。

GraphHopperはその代表的な実装であり、
シンプルな設定で起動でき、徒歩・自転車・車など複数モードの経路探索に対応しています。
また、到達圏(Isochrone)機能を標準で備えており、
ローカル環境で商圏分析や徒歩圏解析を行いたい場面に最適です。

GraphHopperとは

GraphHopperは、OpenStreetMap(OSM)の道路データを基に経路探索を行う Java製のルーティングエンジン です。
ローカル環境で .osm.pbf ファイルを解析し、HTTP API を通じてルート検索や到達圏(Isochrone)を提供できます。

特徴

  • ローカル実行可能:クラウドに依存せず、自社環境やオフライン環境で動作
  • 複数モード対応:徒歩・自転車・車など、用途に応じたルーティングを切り替え可能
  • Isochrone API 搭載:任意の地点から到達できる範囲をポリゴンとして取得
  • カスタムモデル対応:速度制限や道路条件を反映したルーティング設定が可能
  • 拡張機能:標高データ(Elevation)やターンコストなどにも対応

GraphHopperは単なるルーティングエンジンではなく、
OSMデータを前処理して独自のグラフ構造を生成し、そこに最適化アルゴリズムを適用する仕組み を持ちます。
そのため、同じデータでもプロファイル設定(車・徒歩など)によって異なる探索結果が得られます。

OSRMとの違い

項目 GraphHopper OSRM
実装言語 Java C++
主な用途 ルーティング、到達圏分析 ルーティング専用
API構成 Directions / Isochrone / Matrix など Route / Table / Match / Trip
設定方法 YAML構成ファイル+カスタムモデル コマンドライン+Luaプロファイル
拡張性 高い(カスタムモデル対応) 限定的
実行方式 Java実行(JAR) Docker / Linux中心

実行環境の目安

  • Java 21 以上(OpenJDK / Temurinなど)
  • メモリ 4〜6GB 程度を推奨(対象エリアにより変動)
  • OS:Windows / macOS / Linux いずれでも実行可能

GraphHopper公式では Java 17 以上が推奨されていますが、
本記事では安定動作を確認した Java 21 環境 を使用します。

OSMデータを取得する

GraphHopperは、OpenStreetMap(OSM)の道路ネットワークをもとにグラフを生成します。
そのため、まずは対象エリアのOSMデータ(.osm.pbf 形式)と、
GraphHopper本体(.jar ファイルと設定ファイル)を用意する必要があります。

GraphHopper本体の取得

  1. 公式リリースページ にアクセス
  2. 最新の安定版(例:graphhopper-web-11.0.jar)をダウンロード
  3. config-example.yml も合わせて取得し、同一ディレクトリに保存します
# GraphHopper公式から取得したJARと設定ファイルを同一フォルダに配置
graphhopper/
├─ graphhopper-web-11.0.jar
└─ config-example.yml

GraphHopper本体をcurlで取得する場合

# 作業ディレクトリを作成
mkdir -p graphhopper && cd graphhopper

# GraphHopper本体(JAR)をダウンロード
curl -L -O https://github.com/graphhopper/graphhopper/releases/download/11.0/graphhopper-web-11.0.jar

# 設定ファイル(YAML)をダウンロード
curl -L -O https://github.com/graphhopper/graphhopper/releases/download/11.0/config-example.yml

Geofabrikとは

Geofabrik は、OpenStreetMapのデータを地域別に配布しているサイトです。
国単位・地方単位で .osm.pbf ファイルをダウンロードでき、GraphHopperやOSRMなど多くのGISツールで利用されています。

OSMデータのダウンロード

  1. Geofabrikにアクセス
    https://download.geofabrik.de/

  2. 対象エリアを選択

  3. ページ内のリンクから .osm.pbf ファイルを取得します。
    今回は kanto-latest.osm.pbf を使用します。

    ダウンロード後、GraphHopperを実行するディレクトリ(例:graphhopper/)に配置します。

ディレクトリ構成例

graphhopper/
├─ graphhopper-web-11.0.jar      ← GraphHopper本体
├─ config-example.yml            ← 設定ファイル
└─ kanto-latest.osm.pbf          ← Geofabrikから取得したOSMデータ

GraphHopperを起動する(ローカル実行)

ここからは、ローカル環境でGraphHopperを起動し、.osm.pbf をもとにHTTP APIを動かします。設定は「最小で確実に動く」構成に絞ります。

設定ファイルの編集(最小構成)

まずは config-example.yml を、本記事で実際に動作確認した最小構成に整えます。
(Elevationはサブ要素のため触れず、到達圏と基本ルーティングに必要な項目だけを残します)

# === 最小で確実に動く GraphHopper v11 用設定 ===
logging:
  level: INFO
  loggers:
    com.graphhopper: INFO

graphhopper:
  # 読み込む PBF
  datareader.file: kanto-latest.osm.pbf
  # キャッシュ出力先(削除で再生成)
  graph.location: graph-cache

  # ビルトインのカスタムモデルを使用(ローカルに同名jsonを置かないこと)
  profiles:
    - name: car
      custom_model_files: [car.json]
    - name: foot
      custom_model_files: [foot.json, foot_elevation.json]

  graph.encoded_values: car_access, car_average_speed, foot_access, hike_rating, foot_priority, country, road_class, foot_road_access, mtb_rating, foot_average_speed, average_slope

  graph.elevation.provider: srtm
  graph.elevation.dataaccess: RAM_STORE

  # まずは前処理なしでOK(必要なら後で追加)
  profiles_ch: []
  profiles_lm: []

  import.osm.ignored_highways: []
  graph.dataaccess.default_type: RAM_STORE

# Dropwizard server configuration
server:
  application_connectors:
  - type: http
    port: 8989
    # for security reasons bind to localhost
    bind_host: localhost
    # increase GET request limit - not necessary if /maps UI is not used or used without custom models
    max_request_header_size: 50k
  request_log:
      appenders: []
  admin_connectors:
  - type: http
    port: 8990
    bind_host: localhost
# See https://www.dropwizard.io/en/latest/manual/core.html#logging
logging:
  appenders:
    - type: file
      time_zone: UTC
      current_log_filename: logs/graphhopper.log
      log_format: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
      archive: true
      archived_log_filename_pattern: ./logs/graphhopper-%d.log.gz
      archived_file_count: 30
      never_block: true
    - type: console
      time_zone: UTC
      log_format: "%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"
  loggers:
    "com.graphhopper.osm_warnings":
      level: DEBUG
      additive: false
      appenders:
        - type: file
          currentLogFilename: logs/osm_warnings.log
          archive: false
          logFormat: '[%level] %msg%n'

  • profilescarfoot の2つを有効化
  • custom_model_files はビルトイン(JAR内)を参照し、ローカルに同名JSONを置かない
  • import.osm.ignored_highways は空配列でも キーを必ず定義
  • キャッシュは graph-cache/ に生成(設定変更時は削除→再生成)

初回インポートとキャッシュ生成

初回起動時は、.osm.pbf からルーティング用グラフを構築します。
対象エリアが大きいほど時間とメモリを消費します(関東で数分〜十数分目安)。

次のコマンドで起動します。

java -Xmx4g -jar graphhopper-web-11.0.jar server config-example.yml

補足

  • -Xmx はPCのメモリに合わせて調整(4〜8GB目安)
  • 画面にインポートや前処理のログが流れ、完了後にサーバがListen状態になる

この状態で http://localhost:8989/ にアクセスします。

dba226edfa46d2df397e-1.png

ルート検索結果例

dba226edfa46d2df397e-2.png

APIの動作確認(footルート)

APIを直接叩いて動作確認します。
例:東京駅 → 新宿駅(徒歩)

curl "http://localhost:8989/route?point=35.681236,139.767125&point=35.689592,139.700413&profile=foot&locale=ja"

レスポンス(抜粋)は以下のような構造です。

{
  "hints": {
    "visited_nodes.sum": 55120,
    "visited_nodes.average": 55120.0
  },
  "info": {
    "copyrights": [
      "GraphHopper",
      "OpenStreetMap contributors"
    ],
    "took": 60,
    "road_data_timestamp": "2025-10-18T20:20:55Z"
  },
  "paths": [
    {
      "distance": 7938.0,
      "time": 6176793,
      "points_encoded": true,
      "bbox": [139.70027, 35.67736, 139.768168, 35.690526],
      "points": "(中略: LineString 形式の座標データ)",
      "instructions": [
        { "text": "進む", "distance": 9.4 },
        { "text": "右に曲がる(中央通路)", "distance": 1.5 },
        { "text": "左に曲がる", "distance": 12.4 },
        { "text": "(中略)" },
        { "text": "右に曲がる(JR新宿駅, 南口改札に入る)", "distance": 23.4 },
        { "text": "目標達成", "distance": 0.0 }
      ],
      "ascend": 217.0,
      "descend": 179.0
    }
  ]
}
  • 利用可能なプロファイルは /info で確認できます(car / foot など)。
  • /route が通れば /isochrone も同様の profile 指定で呼び出せます。

ハマりどころ

症状 / メッセージ 主な原因 対処
Missing 'import.osm.ignored_highways' 必須キー未定義 import.osm.ignored_highways: [] を定義
vehicle no longer accepted in profile 旧形式(v9+非対応) vehicle: を使わず custom_model_files: を使う
Custom model file name 'car.json' is already used ローカルに同名JSONを配置 ローカル同名ファイルを削除 or 別名化
起動はするがUIが開けない ポート競合/localhost制限 server設定を見直す or 競合プロセス停止
設定を変えても反映されない 旧キャッシュが残存 graph-cache/ を削除して再起動
OutOfMemory系で落ちる メモリ不足 -Xmx6g などへ増量、対象エリアを小さく

徒歩到達圏(Isochrone API)をGeoJSONで取得する

GraphHopperは、経路探索だけでなく「到達圏(Isochrone)」も標準APIとして備えています。
特定の地点から、指定した移動時間(または距離)内で到達可能な範囲をGeoJSON形式で取得できます。
ここでは、東京駅を中心に「徒歩10分圏(600秒)」を取得してみます。

Isochrone APIの概要

Isochrone APIは /isochrone エンドポイントを使用します。
主なパラメータは以下の通りです。

パラメータ 説明
point 出発地点の緯度・経度 35.681236,139.767125
time_limit 到達時間(秒) 600 = 10分
profile 使用プロファイル(car, foot など) foot
buckets 段階的に等時間帯を出す場合の分割数(任意) 3 など

APIを呼び出す

徒歩10分圏をGeoJSONで取得します。

curl "http://localhost:8989/isochrone?point=35.681236,139.767125&time_limit=600&profile=foot" -o isochrone_10min.geojson

このリクエストにより、指定座標から徒歩600秒以内に到達可能な範囲をポリゴンで返します。

レスポンス例(GeoJSON抜粋)

{
  "polygons": [
    {
      "type": "Feature",
      "geometry": {
        "type": "Polygon",
        "coordinates": [
          [
            [139.7603893, 35.68285085],
            [139.7603567, 35.6826253],
            [139.7607457, 35.6825358],
            "...(中略:多数の頂点座標が続く)...",
            [139.7603893, 35.68285085]
          ]
        ]
      },
      "properties": { "bucket": 0 }
    }
  ],
  "info": {
    "took": 116,
    "road_data_timestamp": "2025-10-18T20:20:55Z"
  }
}

GraphHopper の /isochrone API は、GeoJSON 互換の構造でレスポンスを返します。
ただし、トップレベルは "FeatureCollection" ではなく、"polygons" 配列の中に複数の Feature が含まれます。
Folium などで直接読み込む場合は、{"type": "FeatureCollection", "features": data["polygons"]} のように明示的に FeatureCollection 形式に変換して利用します。

Foliumで可視化(GeoJSON描画)

/isochrone で取得した GeoJSON(例:isochrone_10min.geojson)を、ローカルで Folium(Leaflet)を使って地図上に重ねて表示します。

GeoJSONを読み込み・描画して保存

以下のスクリプトは、GeoJSON を読み込み、中心点(東京駅)を初期表示に設定し、ポリゴンを重ねて isochrone_10min.html として保存します。

import json
import folium

# 中心(東京駅)
center = [35.681236, 139.767125]
m = folium.Map(location=center, zoom_start=14, tiles="OpenStreetMap")

# GraphHopper Isochrone のレスポンスを読み込み
with open("isochrone_10min.geojson", "r", encoding="utf-8") as f:
    data = json.load(f)

# GraphHopper形式 → FeatureCollection へ変換
if "polygons" in data:
    feature_collection = {
        "type": "FeatureCollection",
        "features": data["polygons"],  # そのままfeaturesへ
    }
elif data.get("type") == "FeatureCollection":
    feature_collection = data
else:
    # 単一Featureで返ってきた場合などに備えたフォールバック
    feature_collection = {
        "type": "FeatureCollection",
        "features": [data],
    }

# GeoJSONを重ねる
folium.GeoJson(
    feature_collection,
    name="Isochrone 10min (foot)",
    style_function=lambda feature: {
        "fillColor": "#3186cc",
        "color": "#3186cc",
        "weight": 2,
        "fillOpacity": 0.4,
    },
    tooltip=folium.GeoJsonTooltip(fields=[], aliases=[]),
).add_to(m)

# 中心点(任意)
folium.Marker(center, tooltip="Origin").add_to(m)

# 保存
m.save("isochrone_10min.html")
print("saved: isochrone_10min.html")

作成したHTMLを確認

dba226edfa46d2df397e-3.png

補足:到達圏ポリゴンの「尖り」について
/isochrone API が生成するポリゴンは、到達可能ノードを基に自動的に外周を補間して形成されます。
そのため、一部の辺が“尖った形状”になりますが、これは「地理的なネットワーク構造(道路の枝状部分)」を反映した結果です。
ただし、塗りつぶされた領域すべてが到達可能とは限らない点に注意が必要です。
ポリゴンは「外接形」として描かれるため、実際には通行不能な空間(河川・施設内部など)を含む場合があります。
厳密な可達範囲を評価する際は、ノード単位での可視化やルートサンプリングを併用するとより正確です。

まとめ:ローカルで動かす到達圏分析の意義

本記事では、GraphHopper v11 を用いて ローカル環境で徒歩到達圏を算出・可視化 するまでの手順を解説しました。
SaaSや外部APIに依存せず、手元で地理解析が完結する点が大きな特徴です。

  • OSMデータ(PBF)を取得し、GraphHopperへ読み込む
  • config-example.yml を最小構成に調整し、徒歩プロファイルを有効化
  • /isochrone API で到達圏ポリゴンを取得
  • Python+FoliumでGeoJSONを描画し、地図上で可視化

これにより、都市・地域スケールでの徒歩圏分析をクラウド環境を使わず再現できるようになりました。

今後の発展例

  • 複数条件の到達圏比較(徒歩5分/10分圏など)
  • 勾配情報(elevation)を考慮した徒歩コスト評価
  • 公共交通や自転車ルートとの組み合わせ
  • PostGIS・QGISとの統合分析

GraphHopperは商用ルーティングエンジンにも採用される成熟度を持ちながら、
個人のローカル環境でも軽量に動作します。
これを活用することで、オープンデータを用いた再現性の高い地理分析 が可能となります。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?