この記事では、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>
このコードの流れは次のとおりです。
-
querySelector()でHTMLの要素を取得する。 -
addEventListener()でボタンのクリックを検知する。 -
getCurrentPosition()で位置情報を要求する。 - 成功したら緯度・経度を表示する。
- 失敗したらエラーメッセージを表示する。
なお、位置情報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 |
制限時間内に取得できなかった |
そのため、成功時の処理だけでなく、失敗時の処理も用意するのが実用的です
まとめ:まず覚えるべきこと
学習の優先順位はこの順番で十分です。
-
navigator.geolocation— 位置情報APIにアクセスする。 -
getCurrentPosition()— 現在地を取得する。 -
position.coords.latitude / longitude— 緯度と経度を取り出す。 - 成功・失敗のコールバック — 結果に応じて処理する。
-
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>
使い方
- 上のコードを
index.htmlに保存します。 - HTTPSのWebサーバー、またはローカルの
localhostで開きます。 - 「現在地を取得」を押して、ブラウザーの位置情報利用を許可します。
- 日本式の住所、緯度・経度、APIから返された住所データを確認します。
重要な補足
このコードは住所の各項目を日本式の順番に並べますが、すべての地域で正式な住民票上の住所になるとは限りません。Nominatimのデータには、町名・丁目・番地が欠けていたり、地区名が重複していたりすることがあります。
また、公開Nominatimサーバーは利用制限があり、本番サービスで大量にアクセスする用途には向きません。継続利用する場合は、公式の利用ポリシーを確認してください。