Python と Google Maps API で複数地点を近い順に並べ替える手順

  • URLをコピーしました!
目次

はじめに

営業の外回りや設備の定期点検、出張時の移動計画などで、訪問先の住所を Google マップに 1 件ずつ入力し、地図を見ながら回る順番を考える作業に時間を取られることがあります。

「出発地から近い順に、自動で並べ替えられれば」と感じる場面は少なくありません。

この記事でわかること
  • Google Maps Platform の Routes API(Compute Route Matrix)の基礎
  • Python で複数地点の距離を計算し、近い順に並べ替えるコード
  • 2025 年の料金改定をふまえた費用感と、利用上の上限

本記事では、リスト化した住所を読み込み、出発地から近い順に並べ替えて、Google マップのルート案内 URL を発行するまでを Python で自動化する手順を紹介します。距離と所要時間の計算には、従来の Distance Matrix API ではなく、現在 Google が推奨する Routes API の Compute Route Matrix を使用します。料金は 2025 年 3 月の改定で月 200 ドルのクレジットが廃止されているため、新しい無料枠の考え方もあわせて整理します。

使用する技術: Routes API とは?

今回のツールは、Google Maps Platform の Routes API を中心に構成します。経路や距離を外部プログラムから扱うための仕組みで、本記事では主に次の 2 つの機能を組み合わせます。

Compute Route Matrix(距離・所要時間の計算)

出発地から各目的地までの距離と所要時間を、道路網にもとづいて計算する機能です。直線距離ではなく実際の移動距離を求められるため、これを使って出発地と各目的地の距離を比較します。

かつて同じ用途で広く使われていた Distance Matrix API は、現在はレガシー扱いとなっています。Google は Directions API・Distance Matrix API をレガシーサービスに指定し、Routes API(Compute Routes / Compute Route Matrix)への移行を案内しているため、本記事でも Routes API を前提とします。

参考: Routes API Documentation(Google for Developers)
“Compute Route Matrix returns distances and travel times for a matrix of routes.”
(Compute Route Matrix は、複数ルートの距離と所要時間を返します)
https://developers.google.com/maps/documentation/routes

Google マップのルート URL(Directions)

計算結果をもとに、実際のナビゲーションを開く機能です。Python で求めた並び順を、Google マップが解釈できる URL 形式(Directions リンク)に変換します。この URL を開くと、PC やスマートフォンの Google マップが起動し、経由地が設定された状態でナビを始められます。

事前準備: API キーとライブラリのインストール

スクリプトを作成する前に、Google のサービスを利用するための準備を行います。

STEP 1: Google Cloud Platform で API キーを取得

Routes API を利用するには、Google Cloud Platform(GCP)でプロジェクトを作成し、API キーを発行します。大まかな流れは次のとおりです。

  1. Google Cloud Console にアクセスし、新しいプロジェクトを作成する。
  2. 「API とサービス」のライブラリから Routes API を検索し、有効にする。
  3. 「認証情報」から API キーを作成する(発行されるキーは AIzaSyで始まる文字列で、後のプログラムで使用するため控えておく)。

旧バージョンの解説では「Distance Matrix API を有効化」と案内されることがありますが、新規に作成する場合は Routes API を有効化 します。

STEP 2: Python ライブラリのインストール

本記事のスクリプトは、Python から HTTP リクエストを送る requestsライブラリのみを使用します。未インストールの場合は次のコマンドを実行します。

pip install requests

公式の Python クライアントライブラリ(google-maps-routing)も用意されていますが、こちらは ADC(Application Default Credentials)による認証設定が必要で手順がやや増えます。本記事では API キーをそのまま使える REST 直接呼び出しの方式を採用します。

料金: 2025 年の改定をふまえた費用感

Google Maps Platform の料金は 2025 年 3 月 1 日に大きく変わりました。従来は全 API 共通の月 200 ドル分の無料クレジットがありましたが、これは廃止され、API(SKU)ごとの月次無料枠に置き換わっています。

参考: Google Maps Platform pricing overview(Google for Developers)
“Free usage caps replace monthly $200 credit.”
(無料利用枠が、月 200 ドルのクレジットを置き換えます)
https://developers.google.com/maps/billing-and-pricing/overview

Routes API の Compute Route Matrix は、出発地数 × 目的地数で求まる「要素(element)」単位で課金されます。例えば出発地 1 件・目的地 5 件なら 5 要素です。個人の学習や、数件の訪問先を月に数回計算する程度の小規模な利用であれば、無料枠の範囲に収まることが多いと考えられます。ただし「月 200 ドルまで無料」という以前の前提は当てはまらないため、利用前に最新の料金ページで無料枠と単価を確認することをおすすめします。

なお、利用には GCP 側で請求先アカウント(クレジットカード情報など)の登録が必要です。

ルート最適化スクリプトの作成

手順は「訪問先リストを作る」「プログラムを実行する」の 2 ステップです。

STEP
目的地のリスト作成(target.txt)

訪問したい場所のリストをテキストファイルで作成します。ファイル名は target.txtとし、プログラムと同じフォルダーに保存します。記述ルールは 住所,場所名 の形式で 1 行ずつです。

千葉県浦安市舞浜1-1-24,ボン・ヴォヤージュ
千葉県浦安市舞浜1-4,イクスピアリ
千葉県浦安市舞浜29-1,ビビディ・バビディ・ブティック
千葉県浦安市舞浜1-13,東京ディズニーシー
千葉県浦安市舞浜2-50,舞浜アンフィシアター
STEP
Python コード本体(maps_search.py)

以下のコードを maps_search.pyという名前で保存します。

import requests
import urllib.parse

# =========================================================
#  設定エリア
# =========================================================
# 取得した API キーをここに貼り付けてください
API_KEY = 'YOUR_API_KEY'

# 出発地点の住所
START_ADDRESS = '千葉県浦安市舞浜1-1'

# 移動手段 (DRIVE:車, WALK:徒歩, BICYCLE:自転車, TWO_WHEELER:二輪, TRANSIT:公共交通機関)
TRAVEL_MODE = 'WALK'

# 読み込むファイル名
TARGET_FILE = 'target.txt'

# Routes API (Compute Route Matrix) のエンドポイント
ENDPOINT = 'https://routes.googleapis.com/distanceMatrix/v2:computeRouteMatrix'
# =========================================================


def load_targets(path):
    """target.txt を読み込み、住所リストと名称リストを返す"""
    addresses, names = [], []
    with open(path, 'r', encoding='utf-8') as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            parts = line.split(',')
            addresses.append(parts[0])
            names.append(parts[1] if len(parts) >= 2 else parts[0])
    return addresses, names


def compute_matrix(api_key, origin, destinations, mode):
    """出発地から各目的地までの距離・時間を Compute Route Matrix で取得する"""
    headers = {
        'Content-Type': 'application/json',
        'X-Goog-Api-Key': api_key,
        # status を含めないと全要素が正常扱いになるため必ず含める
        'X-Goog-FieldMask': (
            'originIndex,destinationIndex,'
            'status,condition,distanceMeters,duration'
        ),
    }
    body = {
        'origins': [{'waypoint': {'address': origin}}],
        'destinations': [{'waypoint': {'address': d}} for d in destinations],
        'travelMode': mode,
    }
    res = requests.post(ENDPOINT, headers=headers, json=body)
    res.raise_for_status()
    return res.json()


def main():
    try:
        dest_addresses, dest_names = load_targets(TARGET_FILE)
        if not dest_addresses:
            print('エラー: 目的地ファイルの中身が空です。')
            return

        print(f'[{len(dest_addresses)}] 件の目的地までの距離を計算中...')
        elements = compute_matrix(API_KEY, START_ADDRESS, dest_addresses, TRAVEL_MODE)

        # レスポンスは順序が保証されないため destinationIndex で対応付ける
        results = []
        for el in elements:
            if el.get('condition') != 'ROUTE_EXISTS':
                idx = el.get('destinationIndex')
                name = dest_names[idx] if idx is not None else '不明'
                print(f"警告: '{name}' へのルートが見つかりませんでした。")
                continue
            idx = el['destinationIndex']
            results.append({
                'name': dest_names[idx],
                'address': dest_addresses[idx],
                'distance': el['distanceMeters'],
            })

        # 距離が短い順(昇順)に並べ替え
        sorted_results = sorted(results, key=lambda x: x['distance'])

        # 結果の表示と URL 生成
        print('\n--- 並べ替え後の訪問順序(出発地に近い順) ---')
        base_url = 'https://www.google.com/maps/dir/'
        url_parts = [urllib.parse.quote(START_ADDRESS)]
        for item in sorted_results:
            print(f"{item['name']}: {item['distance']} meters")
            url_parts.append(urllib.parse.quote(item['address']))

        full_url = base_url + '/'.join(url_parts)
        print('\n--- Google Maps Route URL ---')
        print(full_url)

    except requests.exceptions.HTTPError as e:
        print(f'API エラー: {e.response.status_code} {e.response.text}')
    except Exception as e:
        print(f'予期せぬエラーが発生しました: {e}')


if __name__ == '__main__':
    main()

参考: Method: computeRouteMatrix(Google for Developers)
“This method requires that you specify a response field mask in the input.”
(このメソッドは、レスポンスのフィールドマスクの指定を必須とします)
https://developers.google.com/maps/documentation/routes/reference/rest/v2/TopLevel/computeRouteMatrix

コードの解説

距離で「近い順」に並べ替える仕組み

Compute Route Matrix が返すのは「場所と距離の組」の配列で、並び順は固定されていません。これを近い順に整えているのが次の 1 行です。

sorted_results = sorted(results, key=lambda x: x['distance'])

key=lambda x: x['distance'] で「distanceMeters(距離)の小さい順に並べる」と指定しています。これにより手作業で比較しなくても、出発地に最も近い場所が先頭に並びます。

レスポンスのインデックス対応付け(重要)

旧来の Distance Matrix API と異なり、Compute Route Matrix のレスポンスは要素の順序が保証されません。代わりに各要素が originIndexdestinationIndexを持つため、これらでリクエスト時の住所と対応付けます。コード内で el['destinationIndex']を使って名称・住所を引き当てているのはこのためです。また、statusをフィールドマスクに含めないと、ルートが見つからない要素も正常として扱われてしまう点に注意します。

文字化けを避ける URL エンコード

ブラウザの URL は半角英数字を基本とするため、日本語の住所をそのまま URL に含めると環境によっては正しく開けないことがあります。urllib.parse.quoteを通すことで、日本語をパーセントエンコーディング形式に変換し、PC でもスマートフォンでも開きやすいリンクにします。

実行結果と活用のヒント

ターミナル(またはコマンドプロンプト)でスクリプトを実行すると、API と通信して計算結果が表示されます。

[5] 件の目的地までの距離を計算中...

--- 並べ替え後の訪問順序(出発地に近い順) ---
東京ディズニーシー: 497 meters
舞浜アンフィシアター: 1102 meters
イクスピアリ: 1374 meters
ビビディ・バビディ・ブティック: 1957 meters
ボン・ヴォヤージュ: 2475 meters

--- Google Maps Route URL ---
https://www.google.com/maps/dir/...

生成された URL を開くと、ブラウザまたは Google マップが起動し、経由地が設定された状態で地図が開きます。

移動手段の切り替え

車での移動に変える場合は、設定エリアの TRAVEL_MODEを書き換えます。

# DRIVE:車, WALK:徒歩, BICYCLE:自転車, TWO_WHEELER:二輪, TRANSIT:公共交通機関
TRAVEL_MODE = 'DRIVE'

なお DRIVEまたは TWO_WHEELERの場合は、リクエストボディに 'routingPreference': 'TRAFFIC_AWARE' を加えると交通状況を考慮した距離・時間を取得できます(WALKなどでは指定できません)。

利用上の上限と制約

導入前に把握しておきたい主な制約です。

要素数の上限:
要素数(出発地数 × 目的地数)は、TRANSIT 以外のルートで 625 まで、TRANSIT または TRAFFIC_AWARE_OPTIMAL では 100 まで。住所または place ID で指定する地点は合計 50 までです。

フィールドマスク:
指定が必要で、statusを含めることが推奨されます。

移動手段のステータス:
WALK・BICYCLE・TWO_WHEELER はベータ提供で、歩道や自転車レーンが明確でない場合があります。

ルート URL の経由地数:
Google マップの仕様上、1 つの URL に設定できる経由地には上限があります(出発・到着を含めて概ね 10〜25 箇所)。件数が多い場合は URL を分割します。

参考: Get a route matrix(Google for Developers)
“The number of elements cannot exceed 625 for routes that are not TRANSIT routes.”
(要素数は、TRANSIT 以外のルートでは 625 を超えられません)
https://developers.google.com/maps/documentation/routes/compute_route_matrix

なぜ Distance Matrix ではなく Routes API なのか

同じ用途には長らく Distance Matrix API が使われてきましたが、現在はレガシー扱いです。新規に作るなら Routes API の Compute Route Matrix が推奨され、いくつかの利点があります。ストリーミングにより行列全体の計算完了を待たずに要素を受け取れるため低レイテンシで、交通計算の制御が細かく、処理の優先度も高めに扱われます。

旧コードからの移行で押さえる主な差分は次のとおりです。

  • 認証: API キーをクエリパラメーターではなく X-Goog-Api-Keyヘッダーで渡す。
  • フィールドマスク: X-Goog-FieldMaskの指定が必要(旧 API には不要だった)。
  • 移動手段: 小文字の driving 等から、大文字の DRIVE 等に変更。
  • レスポンス: 順序非保証のため originIndex / destinationIndex で対応付ける。
  • 課金: リクエスト単位ではなく、要素(出発地数 × 目的地数)単位。

参考: Compute a Route Matrix(Routes Preferred API, Google for Developers)
“Streaming allows elements to be returned before the entire matrix has been calculated.”
(ストリーミングにより、行列全体の計算完了を待たずに要素を返せます)
https://developers.google.com/maps/documentation/routes_preferred/compute_route_matrix

公式の移行手順は次のドキュメントにまとまっています。

参考: Migrate from Directions or Distance Matrix APIs(Google for Developers)
https://developers.google.com/maps/documentation/routes/migrate-routes

まとめ

本記事では、Google Maps API と Python を使い、複数の訪問先を出発地から近い順に並べ替えてルート URL を発行する手順を紹介しました。距離計算には現行の Routes API(Compute Route Matrix)を用い、2025 年の料金改定もふまえて整理しました。旧来の Distance Matrix API ベースの実装からの移行を考えている場合の差分も確認できます。

  • 距離計算は Routes API の Compute Route Matrix を使用
  • Distance Matrix API はレガシー扱いで新規利用は非推奨
  • 認証は X-Goog-Api-Key ヘッダーで渡し、フィールドマスクの指定が必要
  • レスポンスは順序非保証で、index による対応付けが必要
  • 料金は月 200 ドルのクレジット廃止後、SKU 別の無料枠へ移行
  • 課金は要素(出発地数 × 目的地数)単位で算出
  • 要素数の上限は非 TRANSIT で 625、住所指定は合計 50 まで

以上、最後までお読みいただきありがとうございました。

よかったらシェアしてね!
  • URLをコピーしました!

この記事を書いた人

関西を拠点に活動する、現役インフラエンジニア。経験20年超。

大手通信キャリアにて、中〜大規模インフラ(ネットワーク・サーバ・クラウド・セキュリティ)の設計・構築およびプロジェクトマネジメントに従事。現場で直面した技術課題への対処や、最新の脆弱性情報への実務対応を、一次情報として発信しています。

保有資格
CCIE Lifetime Emeritus(取得から20年以上)/ VCAP-DCA / Azure Solutions Architect Expert

▶ 運営者プロフィール(詳細)

目次