0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

HTML5のWebAPIを使用して位置情報を取得して住所に変換

0
Posted at

この記事では、JavaScriptでWebサイトからユーザーの現在位置を取得する方法を説明しています。

難しそうに見えますが、基本はシンプルです。

たとえば、スマホでWebサイトを開いて「現在地を表示」ボタンを押すと、地図上に自分の位置を表示する。これを実現するための技術が Geolocation API(位置情報API) です

1. まず、全体の仕組みを理解する

① ユーザーがボタンを押す

「現在地を取得する」

② ブラウザーが許可を求める

ユーザーが位置情報の共有を許可する

③ JavaScriptが位置情報を受け取る

緯度・経度などを取得

④ 地図などに表示する

地図へのリンク、近くのお店の検索など

大事なのは、位置情報を取得する機能と、地図に表示する機能は別物だという点です。

Geolocation APIは位置を取得するところまで担当します。地図の表示には、OpenStreetMapやGoogle Mapsなど、別の仕組みを利用できます。

2. 最も重要なコード

本記事で中心となるのは、このコードです。

navigator.geolocation.getCurrentPosition(
  (position) => {
    console.log(position.coords.latitude);
    console.log(position.coords.longitude);
  }
);

一つずつ分解してみましょう。

コードを分解する

コード 意味
navigator ブラウザーの機能を利用するためのオブジェクト
geolocation 位置情報を取得する機能
getCurrentPosition() 現在位置を一度取得する
position 取得できた位置情報
coords.latitude 緯度
coords.longitude 経度
console.log() 値を開発者ツールに表示する

例えば、取得結果が次のようになったとします。

latitude: 35.1709
longitude: 136.8815

この2つの数値があれば、地図上の場所を指定できます。

なぜ position が関数の引数なの?

ここがJavaScriptの学習ポイントです。

(position) => {
  console.log(position.coords.latitude);
}

これは「位置情報が取得できたら、そのデータを position という変数で受け取り、処理を実行する」という意味です。

位置情報の取得には時間がかかるため、結果を待ってから処理する仕組みになっています。これを非同期処理と呼びます。

3. 位置情報を取得する実用的なコード

まずは、ボタンを押すと緯度と経度を表示するWebページを作ってみましょう。


<button id="find-me">現在地を取得</button>
<p id="status">まだ取得していません</p>

<script>
  const button = document.querySelector("#find-me");
  const status = document.querySelector("#status");

  button.addEventListener("click", () => {
    if (!navigator.geolocation) {
      status.textContent = "位置情報に対応していません";
      return;
    }

    status.textContent = "取得中...";

    navigator.geolocation.getCurrentPosition(
      (position) => {
        const lat = position.coords.latitude;
        const lon = position.coords.longitude;

        status.textContent =
          `緯度: ${lat}, 経度: ${lon}`;
      },
      (error) => {
        status.textContent =
          `取得できませんでした: ${error.message}`;
      }
    );
  });
</script>

このコードの流れは次のとおりです。

  1. querySelector() でHTMLの要素を取得する。
  2. addEventListener() でボタンのクリックを検知する。
  3. getCurrentPosition() で位置情報を要求する。
  4. 成功したら緯度・経度を表示する。
  5. 失敗したらエラーメッセージを表示する。

なお、位置情報APIを使うには、基本的にHTTPSなどの安全なコンテキストが必要です。ローカル開発では localhost も利用できます。また、ユーザーが許可しない場合は位置を取得できません。

4. 現在位置を取得する方法は2種類ある

getCurrentPosition()

一度だけ取得

現在地を一度取得します。近くのお店を探す、現在地から目的地までのルートを検索するといった用途に向いています。

watchPosition()

繰り返し取得

位置が変わったときなどに更新情報を受け取ります。移動中の位置表示や位置追跡などに向いています。

watchPosition() を停止するには、取得時に返されるIDを clearWatch() に渡します。

const watchId = navigator.geolocation.watchPosition(
  (position) => {
    console.log(position.coords.latitude);
  }
);

// 位置の監視を停止
navigator.geolocation.clearWatch(watchId);

5. 精度や待ち時間も設定できる

位置取得時のオプションも紹介します。

const options = {
  enableHighAccuracy: true,
  maximumAge: 30000,
  timeout: 27000
};

navigator.geolocation.getCurrentPosition(
  success,
  error,
  options
);
設定 意味
enableHighAccuracy 高精度な位置情報を要求する
maximumAge 何ミリ秒まで過去の位置情報を再利用できるか
timeout 位置情報の取得を何ミリ秒で打ち切るか

例えば maximumAge: 30000 は、条件が合えば30秒以内の取得済みデータを再利用してよい、という意味です。

ただし、enableHighAccuracy: true にすれば必ず正確になるわけではありません。端末や電波環境によって精度は変わります。

6. エラー処理も必要

位置情報が取得できない理由はいくつかあります。

エラーコード 意味
1 ユーザーが許可しなかった
2 位置情報を取得できなかった
3 制限時間内に取得できなかった

そのため、成功時の処理だけでなく、失敗時の処理も用意するのが実用的です

まとめ:まず覚えるべきこと

学習の優先順位はこの順番で十分です。

  1. navigator.geolocation — 位置情報APIにアクセスする。
  2. getCurrentPosition() — 現在地を取得する。
  3. position.coords.latitude / longitude — 緯度と経度を取り出す。
  4. 成功・失敗のコールバック — 結果に応じて処理する。
  5. watchPosition() / clearWatch() — 位置の継続的な監視と停止。

特に理解してほしいのは、位置情報API自体はそれほど複雑ではなく、非同期処理とコールバックの理解が重要だということです。

この仕組みがわかれば、現在地を使った地図アプリ、近隣施設の検索、移動記録などの基礎を作れるようになります。

逆ジオコーディングによる緯度・経度からの住所割り出し

位置情報APIから住所を割り出すには、Geolocation API + 逆ジオコーディング(Reverse Geocoding)API を組み合わせます。

Geolocation APIで取得できるのは緯度・経度であり、住所そのものではありません。緯度・経度を別のサービスに渡して、住所に変換します。
位置情報APIから住所を割り出すには、Geolocation API + 逆ジオコーディング(Reverse Geocoding)API を組み合わせます。

Geolocation APIで取得できるのは緯度・経度であり、住所そのものではありません。緯度・経度を別のサービスに渡して、住所に変換します。

仕組み

① Geolocation API

現在地の緯度・経度を取得

35.1709, 136.8815

② 逆ジオコーディングAPI

緯度・経度を住所に変換

③ 住所を画面に表示

都道府県・市区町村・町名など

今回は、無料で試しやすい OpenStreetMapのデータを利用するNominatim API を使ったJavaScriptの例を紹介します。

1. HTML+JavaScriptの完成例

このコードを index.html として保存して、ブラウザーで開いてください。


<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>現在地から住所を取得</title>
</head>
<body>
  <button id="get-address">現在地から住所を取得</button>
  <p id="result">まだ取得していません</p>

  <script>
    const button = document.querySelector("#get-address");
    const result = document.querySelector("#result");

    button.addEventListener("click", () => {
      result.textContent = "位置情報を取得中...";

      navigator.geolocation.getCurrentPosition(
        async (position) => {
          const { latitude, longitude } = position.coords;

          try {
            const url =
              "https://nominatim.openstreetmap.org/reverse" +
              `?lat=${latitude}&lon=${longitude}` +
              "&format=jsonv2&accept-language=ja";

            const response = await fetch(url);

            if (!response.ok) {
              throw new Error("住所の取得に失敗しました");
            }

            const data = await response.json();

            result.textContent =
              data.display_name || "住所が見つかりません";

          } catch (error) {
            result.textContent = error.message;
          }
        },
        (error) => {
          result.textContent =
            "位置情報を取得できませんでした: " +
            error.message;
        },
        {
          enableHighAccuracy: true,
          timeout: 10000
        }
      );
    });
  </script>
</body>
</html>

位置情報の取得には、ブラウザーでの許可とHTTPSなどの安全な接続が必要です。

2. 重要なJavaScriptの解説

① 緯度と経度を取り出す

const { latitude, longitude } = position.coords;

これは分割代入という書き方です。次のコードと同じ意味です。

const latitude = position.coords.latitude;
const longitude = position.coords.longitude;

② APIに緯度・経度を渡す

const url =
  "https://nominatim.openstreetmap.org/reverse" +
  `?lat=${latitude}&lon=${longitude}` +
  "&format=jsonv2&accept-language=ja";

例えば、URLは次のようになります。

https://nominatim.openstreetmap.org/reverse?lat=35.17&lon=136.88&format=jsonv2&accept-language=ja
  • lat:緯度
  • lon:経度
  • format=jsonv2:JSON形式で結果を受け取る
  • accept-language=ja:日本語での住所表記を要求する

Nominatimは、指定された座標に近い地図データを検索して住所情報を返します。座標と住所が常に完全一致するわけではありません。

(https://www.google.com/s2/favicons?domain=https://nominatim.org\&sz=32)

Nominatim 5.3.3 Manual

③ fetch() で住所情報を受け取る

const response = await fetch(url);
const data = await response.json();

ここがAPI通信の中心です。

  • fetch(url):APIにリクエストを送る
  • await:通信結果が返るまで待つ
  • response.json():JSONデータをJavaScriptのオブジェクトに変換する

返ってきたデータの display_name に、住所を含む文字列が入っています。

④ 住所の一部だけを取り出すこともできる

display_name は長い住所文字列になることがあります。市区町村や郵便番号だけが欲しい場合は、返却された address オブジェクトを使います。

const address = data.address;

console.log(address.postcode);     // 郵便番号
console.log(address.prefecture);   // 日本では通常、prefectureキーではない
console.log(address.city);
console.log(address.town);

注意点として、日本の住所で都道府県を取得するキーは通常 state です。実際には次のように書くのがおすすめです。

const address = data.address;

const prefecture = address.state;
const city =
  address.city ||
  address.town ||
  address.village ||
  address.municipality;

console.log(prefecture, city);

住所データの項目は場所によって異なるので、存在しない項目も想定しておきましょう。

3. 実際に使うときの注意点

  • 無料サービスの利用制限: Nominatimの公開サーバーには利用ポリシーがあります。大量アクセスや本番サービスでの継続利用には注意が必要です。
  • ブラウザーからの直接通信: CORSなどの制約により、ブラウザーからのリクエストが拒否される場合があります。その場合はサーバー側からAPIを呼び出す構成を検討します。
  • 住所の精度: 位置情報の誤差や地図データの不足により、隣の道路や建物の住所が返ることもあります。

参考:Nominatim公式ドキュメント(Reverse Geocoding)。

日本式のコードへの変換方法

Nominatimの data.address から日本式の住所(都道府県 → 市区町村 → 町名 → 丁目・番地)に並べ替えて連結するなら、配列に順番を定義して filter() と join() を使うのが簡単です。

1. 基本コード

const address = data.address;

const japaneseAddress = [
  address.province || address.state, // 都道府県
  address.city || address.town || address.village, // 市区町村
  address.suburb,        // 地区・町域
  address.city_district, // 区など
  address.quarter,       // 地区
  address.neighbourhood, // 近隣地区
  address.road,          // 道路名
  address.house_number   // 建物番号
]
  .filter(Boolean)
  .join("");

console.log(japaneseAddress);

ただし、Nominatimの住所項目は地域によって異なります。日本では province より state に都道府県が入ることが多く、市区町村も city ではなく town や village に入る場合があります。

2. 日本の住所として整える実用版

市区町村に区が含まれる場合なども考慮し、重複を避けて組み立てます。

function formatJapaneseAddress(address) {
  const prefecture = address.province || address.state || "";
  const municipality =
    address.city ||
    address.town ||
    address.village ||
    "";

  const parts = [
    prefecture,
    municipality,
    address.city_district,
    address.suburb,
    address.quarter,
    address.neighbourhood,
    address.road,
    address.house_number
  ];

  return [...new Set(parts.filter(Boolean))].join("");
}

const japaneseAddress = formatJapaneseAddress(data.address);
console.log(japaneseAddress);

3. 注意点

  • city_district や suburb は地域によって意味が異なり、必ずしも日本の住所の「区」「町」に一対一で対応しません。
  • road は道路名なので、日本の一般的な住所では必ずしも必要ありません。
  • 丁目・番地・号がすべて取得できるとは限りません。

おすすめは、まず console.log(data.address) で実際のデータ構造を確認し、その地域に合わせて順序を調整することです。

まとめ

これまでの内容をまとめて、現在地の取得から、日本式の住所への並べ替え・連結、画面表示までできるHTMLにしました。

index.html として保存して使えます。


<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>現在地から日本式住所を取得</title>
  <style>
    body {
      font-family: sans-serif;
      max-width: 640px;
      margin: 40px auto;
      padding: 0 16px;
      line-height: 1.7;
    }

    button {
      padding: 12px 20px;
      cursor: pointer;
    }

    #result {
      overflow-wrap: anywhere;
      padding: 12px;
      background: #f3f4f6;
      border-radius: 8px;
    }

    #details {
      overflow-wrap: anywhere;
      font-size: 14px;
    }
  </style>
</head>
<body>
  <h1>現在地から住所を取得</h1>

  <button id="get-address">現在地を取得</button>

  <h2>日本式の住所</h2>
  <p id="result">まだ取得していません。</p>

  <h2>緯度・経度</h2>
  <p id="coordinates">---</p>

  <h2>住所データの詳細</h2>
  <pre id="details">---</pre>

  <script>
    const button = document.querySelector("#get-address");
    const result = document.querySelector("#result");
    const coordinates = document.querySelector("#coordinates");
    const details = document.querySelector("#details");

    // 日本式の住所に並べ替えて連結する
    function formatJapaneseAddress(address) {
      const prefecture =
        address.province || address.state || "";

      // 市区町村
      const municipality =
        address.city ||
        address.town ||
        address.village ||
        address.municipality ||
        "";

      // 区・地区など
      const districts = [
        address.city_district,
        address.suburb,
        address.quarter,
        address.neighbourhood
      ];

      // 住所の各要素を連結
      const parts = [
        prefecture,
        municipality,
        ...districts,
        address.road,
        address.house_number
      ];

      // 空の値と重複を除いて連結
      return [...new Set(parts.filter(Boolean))].join("");
    }

    // ボタンをクリックしたときの処理
    button.addEventListener("click", () => {
      if (!navigator.geolocation) {
        result.textContent =
          "このブラウザーは位置情報に対応していません。";
        return;
      }

      button.disabled = true;
      result.textContent = "位置情報を取得しています...";
      coordinates.textContent = "---";
      details.textContent = "---";

      navigator.geolocation.getCurrentPosition(
        async (position) => {
          const { latitude, longitude } = position.coords;

          coordinates.textContent =
            `緯度: ${latitude}, 経度: ${longitude}`;

          try {
            const params = new URLSearchParams({
              lat: latitude,
              lon: longitude,
              format: "jsonv2",
              "accept-language": "ja"
            });

            const url =
              "https://nominatim.openstreetmap.org/reverse?" +
              params.toString();

            const response = await fetch(url);

            if (!response.ok) {
              throw new Error(
                `住所APIエラー: HTTP ${response.status}`
              );
            }

            const data = await response.json();

            if (!data.address) {
              throw new Error("住所情報が見つかりません。");
            }

            // 日本式の住所を作成
            const japaneseAddress =
              formatJapaneseAddress(data.address);

            result.textContent =
              japaneseAddress || "住所を組み立てられませんでした。";

            // APIから返された住所情報を確認
            details.textContent =
              JSON.stringify(data.address, null, 2);

          } catch (error) {
            result.textContent =
              `エラー: ${error.message}`;
          } finally {
            button.disabled = false;
          }
        },

        (error) => {
          const messages = {
            1: "位置情報の利用が許可されていません。",
            2: "現在地を特定できませんでした。",
            3: "位置情報の取得がタイムアウトしました。"
          };

          result.textContent =
            messages[error.code] ||
            `位置情報エラー: ${error.message}`;

          button.disabled = false;
        },

        {
          enableHighAccuracy: true,
          timeout: 10000,
          maximumAge: 30000
        }
      );
    });
  </script>
</body>
</html>

使い方

  1. 上のコードを index.html に保存します。
  2. HTTPSのWebサーバー、またはローカルの localhost で開きます。
  3. 「現在地を取得」を押して、ブラウザーの位置情報利用を許可します。
  4. 日本式の住所、緯度・経度、APIから返された住所データを確認します。

重要な補足

このコードは住所の各項目を日本式の順番に並べますが、すべての地域で正式な住民票上の住所になるとは限りません。Nominatimのデータには、町名・丁目・番地が欠けていたり、地区名が重複していたりすることがあります。

また、公開Nominatimサーバーは利用制限があり、本番サービスで大量にアクセスする用途には向きません。継続利用する場合は、公式の利用ポリシーを確認してください。

0
0
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
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?