📌 今回やりたかったこと
📍「住所検索の正確さを比較!Google Maps APIとZENRIN Maps API」では、Google Maps APIを使った住所検索について詳しく解説しましたが、
💡 標準機能では住所の「読み」を取得できない という課題がありました。
しかし、日本の住所を扱う上で 「読み」 は非常に重要な情報です!
そこで今回は、無料で使える「OpenStreetMap」 に
✨ @geolonia/normalize-japanese-addresses
を組み合わせて、
より高度な住所表示を実現する方法をご紹介します! 🚀
🤔 Google Maps APIでは「読み」の取得に工夫が必要
📌 記事内でも触れましたが、Google Maps APIのジオコーディング結果から「読み」を正確に取得するのは困難 です。
住所コンポーネントから無理やり抽出しても、
✅ 必ずしも正確な情報が得られるとは限らない のが現状です... 😢
✅ normalize-japanese-addressesの活用で「読み」もバッチリ!
🔧 @geolonia/normalize-japanese-addresses
は、
日本の住所を正規化 し、以下のような情報を付与してくれる便利なライブラリです!
🔹 都道府県コード
🔹 市区町村コード
🔹 緯度・経度
🔹 そして「読み」情報も! 🎉
このライブラリを活用すれば、
正規化された住所情報 + 読み仮名情報 を 簡単に取得できる ようになります! 💪✨
🎉 2か月無料でお試しできる ZENRIN Maps API 🆓
🗺 ZENRIN Maps API は、
日本国内の詳細な地図情報を提供する高精度なマップサービスです! 📍✨
💡 なんと、2か月間も無料でお試し可能! 🎊
このチャンスに、高精度な住所検索や地図機能 を体験してみませんか? 🚀
APIキーの取得方法はこちら! 🔽
ZENRIN Maps APIを使ってみた
🔎 難読住所の検索結果
日本には一見すると読みにくい住所がたくさんあります!📜💦
ここでは、「OpenStreetMap」 と 「ZENRIN Maps API」 で検索した結果を比較してみます! 🧐
🏠 1. 埼玉県 蕨市(わらびし)
🏠 2. 千葉県 匝瑳市(そうさし)
🏠 3. 石川県 羽咋市(はくいし)
サンプルコード
Open Street Map
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>住所検索</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css"/>
<style>
#mapid {
height: 400px;
}
</style>
</head>
<body>
<div id="mapid"></div>
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
<script type="module" src="osm_address_searche.js"></script>
</body>
</html>
import { normalize } from './address-normalizer/node_modules/@geolonia/normalize-japanese-addresses/dist/main-esm.mjs';
// 地図の初期設定
var map = L.map('mapid', {
center: [35.7122, 139.8117],
zoom: 15,
minZoom: 13,
maxZoom: 16
});
// OpenStreetMapのタイルを追加
var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
});
tileLayer.addTo(map);
// Nominatim Geocoderの設定
var geocoder = L.Control.Geocoder.nominatim();
// Geocoderを地図に追加
var osmGeocoder = new L.Control.Geocoder({
geocoder: geocoder
});
osmGeocoder.addTo(map);
// 検索結果の表示
osmGeocoder.on('markgeocode', async function(e) {
var result = e.geocode;
var latlng = result.center;
var address = result.name;
// 住所の正規化
const normalizedAddress = await normalize(address);
const prefecture = normalizedAddress.pref;
const city = normalizedAddress.city;
const town = normalizedAddress.town;
// metadataから読み方を取得
let prefread = '';
let cityread = '';
let machiread = '';
if (normalizedAddress.metadata && normalizedAddress.metadata.prefecture && normalizedAddress.metadata.prefecture.pref_k) {
prefread = normalizedAddress.metadata.prefecture.pref_k;
}
if (normalizedAddress.metadata && normalizedAddress.metadata.city && normalizedAddress.metadata.city.city_k) {
cityread = normalizedAddress.metadata.city.city_k;
}
if (normalizedAddress.metadata && normalizedAddress.metadata.machiAza && normalizedAddress.metadata.machiAza.oaza_cho_k) {
machiread = normalizedAddress.metadata.machiAza.oaza_cho_k;
}
const reading = prefread + cityread + machiread;
// ポップアップの内容
var popupContent = `
住所: ${address}<br>
読み方: ${reading}<br>
緯度: ${latlng.lat}<br>
経度: ${latlng.lng}
`;
// ポップアップを表示
var popup = L.popup()
.setLatLng(latlng)
.setContent(popupContent)
.openOn(map);
});
// 文字列をカタカナに変換する関数
function toKatakana(str) {
return str.replace(/[\u3041-\u3096]/g, function(match) {
var charCode = match.charCodeAt(0) + 96;
return String.fromCharCode(charCode);
});
}
ZENRIN Maps API
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Display a map</title>
<style>
body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
#search-bar {
position: absolute;
top: 10px;
left: 10px;
background-color: #fff;
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
z-index: 1;
}
#result {
position: absolute;
top: 70px;
left: 10px;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
z-index: 1;
display: none;
}
#ZMap {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
<!-- ローダーを読み込む -->
<script src="https://test-js.zmaps-api.com/zma_loader.js?key=YOUR_API_KEY&auth=referer"></script>
</head>
<body>
<div id="search-bar">
<input type="text" id="address" placeholder="住所を入力してください">
<button onclick="searchAddress()">検索</button>
</div>
<!-- 検索結果の表示エリア -->
<div id="result">
<p><strong>検索結果:</strong></p>
<p>住所: <span id="result-address">-</span></p>
<p>住所読み: <span id="result-address_read">-</span></p>
<p>緯度: <span id="result-lat">-</span></p>
<p>経度: <span id="result-lng">-</span></p>
</div>
<div id="ZMap"></div>
<script src="js/zma_addressmatch3.js"></script>
</body>
</html>
// グローバル変数として `map` と `mrk_widget` を定義
let map;
let mrk_widget;
// ローダーのメソッド実行
ZMALoader.setOnLoad(function (mapOptions, error) {
if (error) {
console.error(error)
return
}
// 地図オブジェクト
// var map;
// 中心点の緯度経度(東京駅)
const lat = 35.681406, lng = 139.767132;
// マップコンテナ要素を取得する
const mapElement = document.getElementById('ZMap');
// MapOptionsをデフォルト値から変更する場合各パラメータに値を設定
// 中心点の緯度経度を設定
mapOptions.center = new ZDC.LatLng(lat, lng);
mapOptions.zipsMapType = 'VeAmBrmV'
mapOptions.mouseWheelReverseZoom = true; // ★マウスホイールのズーム方向の反転を指定
// 地図を生成
map = new ZDC.Map(
mapElement,
mapOptions,
function() {
// Success callback
// コントロールを追加する
// 左上 拡大縮小ボタン表示
map.addControl(new ZDC.ZoomButton('bottom-right'));
// 右上 コンパス表示
map.addControl(new ZDC.Compass('top-right'));
// 左下 スケールバー表示
map.addControl(new ZDC.ScaleBar('bottom-left'));
},
function() {
// Failure callback
}
);
})
async function searchAddress() {
const address = document.getElementById("address").value;
if (!address) {
alert("住所を入力してください");
return;
}
try {
const response = await fetch('https://test-web.zmaps-api.com/search/address', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'x-api-key': 'YOUR_API_KEY',
'Authorization': 'referer'
},
body: new URLSearchParams({
word: address,
word_match_type: '3'
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(data.result.item);
if (data.status === "OK" && data.result.info.hit > 0) {
const location = data.result.item[0].position;
// 配列の順序が[経度, 緯度]であることを確認
const lng = location[0];
const lat = location[1];
const latLng = new ZDC.LatLng(lat, lng);
// 地図をその位置に移動
map.setCenter(latLng);
/* Markerの設置 */
center_mrk =new ZDC.CenterMarker();
// MarkerをMapに追加
map.addControl(center_mrk);
// 検索結果を表示
document.getElementById("result-address").textContent = data.result.item[0].address;
document.getElementById("result-address_read").textContent = data.result.item[0].address_read;
document.getElementById("result-lat").textContent = lat;
document.getElementById("result-lng").textContent = lng;
document.getElementById("result").style.display = "block";
console.log("緯度:", lat, "経度:", lng);
} else {
alert("住所の位置情報が見つかりませんでした");
}
} catch (error) {
console.error("エラーが発生しました:", error);
alert("住所の検索中にエラーが発生しました");
}
}
地図表示
Open Street Map
ZENRIN Maps API
ZENRINMapsAPI 住所検索
解説
Open Street Map
1.準備:必要なライブラリの読み込み(HTML)
まず、HTMLファイルで必要なライブラリを読み込みます。
<head>
タグ内で、Google Maps APIのスクリプトを読み込み、callbackとしてinitMap関数を指定しています。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>住所検索</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.3.1/dist/leaflet.css">
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css"/>
<style>
#mapid {
height: 400px;
}
</style>
</head>
<body>
<div id="mapid"></div>
<script src="https://unpkg.com/leaflet@1.3.1/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
<script type="module" src="osm_address_searche.js"></script>
</body>
</html>
Leaflet: 地図表示のための基本ライブラリです。CSSとJavaScriptファイルを読み込みます。
leaflet-control-geocoder: 住所検索機能を追加するためのライブラリです。CSSとJavaScriptファイルを読み込みます。
osm_address_searche.js: アプリケーションの主要なロジックを記述するJavaScriptファイルです。type="module"属性をつけて読み込むことで、ES Modules形式で記述されたJavaScriptファイルとして扱われます。
CSS: 地図表示領域のスタイルを定義しています。
2.住所正規化ライブラリのインポート(JavaScript)
JavaScriptファイル(osm_address_searche.js)で、住所正規化ライブラリをインポートします。
import { normalize } from './address-normalizer/node_modules/@geolonia/normalize-japanese-addresses/dist/main-esm.mjs';
@geolonia/normalize-japanese-addresses: 住所を正規化するためのライブラリです。この例では、ローカルにインストールされたモジュールを参照しています。(注意:このパスはプロジェクトの構成によって異なります。プロジェクトのnode_modulesディレクトリ内の実際のパスに合わせてください。)
3.地図の初期設定(JavaScript)
// 地図の初期設定
var map = L.map('mapid', {
center: [35.7122, 139.8117], // 初期表示の中心座標(緯度, 経度)
zoom: 15, // 初期ズームレベル
minZoom: 13, // 最小ズームレベル
maxZoom: 16 // 最大ズームレベル
});
// OpenStreetMapのタイルを追加
var tileLayer = L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
});
tileLayer.addTo(map);
・L.map('mapid', { ... }): 指定されたID(mapid)の要素に地図を埋め込みます。centerオプションで初期表示の中心座標を、zoomオプションで初期ズームレベルを設定します。
・L.tileLayer(...): 地図のタイルレイヤーを追加します。この例では、OpenStreetMapのタイルを使用しています。attributionオプションで、著作権表示を設定します。
4.Geocoderの設定と地図への追加(JavaScript)
leaflet-control-geocoderを使って、住所検索機能を設定し、地図に追加します。
// Nominatim Geocoderの設定
var geocoder = L.Control.Geocoder.nominatim();
// Geocoderを地図に追加
var osmGeocoder = new L.Control.Geocoder({
geocoder: geocoder
});
osmGeocoder.addTo(map);
・L.Control.Geocoder.nominatim(): Nominatim Geocoderのインスタンスを作成します。
・L.Control.Geocoder({ geocoder: geocoder }): Geocoderコントロールを作成し、Nominatim Geocoderのインスタンスを渡します。
・osmGeocoder.addTo(map): Geocoderコントロールを地図に追加します。
5.検索結果の処理(JavaScript)
Geocoderで検索された住所情報を取得し、正規化して地図上に表示します。
// 検索結果の表示
osmGeocoder.on('markgeocode', async function(e) {
var result = e.geocode;
var latlng = result.center;
var address = result.name;
// 住所の正規化
const normalizedAddress = await normalize(address);
const prefecture = normalizedAddress.pref;
const city = normalizedAddress.city;
const town = normalizedAddress.town;
// metadataから読み方を取得
let prefread = '';
let cityread = '';
let machiread = '';
if (normalizedAddress.metadata && normalizedAddress.metadata.prefecture && normalizedAddress.metadata.prefecture.pref_k) {
prefread = normalizedAddress.metadata.prefecture.pref_k;
}
if (normalizedAddress.metadata && normalizedAddress.metadata.city && normalizedAddress.metadata.city.city_k) {
cityread = normalizedAddress.metadata.city.city_k;
}
if (normalizedAddress.metadata && normalizedAddress.metadata.machiAza && normalizedAddress.metadata.machiAza.oaza_cho_k) {
machiread = normalizedAddress.metadata.machiAza.oaza_cho_k;
}
const reading = prefread + cityread + machiread;
// ポップアップの内容
var popupContent = `
住所: ${address}<br>
読み方: ${reading}<br>
緯度: ${latlng.lat}<br>
経度: ${latlng.lng}
`;
// ポップアップを表示
var popup = L.popup()
.setLatLng(latlng)
.setContent(popupContent)
.openOn(map);
});
・osmGeocoder.on('markgeocode', function(e) { ... }): Geocoderで検索が実行され、結果が地図上に表示されたときに実行されるイベントリスナーを設定します。
・result.center: 検索結果の中心座標(緯度、経度)を取得します。
・result.name: 検索結果の住所文字列を取得します。
・normalize(address): 取得した住所を@geolonia/normalize-japanese-addressesで正規化します。
・normalizedAddress.metadataから読み仮名情報を取得します。 normalizedAddress.metadata の prefecture、city、machiAza プロパティが存在し、かつそれぞれの pref_k、city_k、oaza_cho_k プロパティが存在する場合に、その値を変数に格納します。
・L.popup(): 地図上にポップアップを作成し、住所、読み仮名、緯度、経度を表示します。
ZENRIN Maps API
1.地図の初期化
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>Display a map</title>
<style>
/* スタイル設定 */
body { margin: 0; padding: 0; font-family: Arial, sans-serif; }
#search-bar {
position: absolute;
top: 10px;
left: 10px;
background-color: #fff;
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
z-index: 1;
}
#result {
position: absolute;
top: 70px;
left: 10px;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0,0,0,0.2);
z-index: 1;
display: none;
}
#ZMap {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
}
</style>
<!-- ローダーを読み込む -->
<script src="https://test-js.zmaps-api.com/zma_loader.js?key=YOUR_API_KEY&auth=referer"></script>
</head>
<body>
<div id="search-bar">
<input type="text" id="address" placeholder="住所を入力してください">
<button onclick="searchAddress()">検索</button>
</div>
<!-- 検索結果の表示エリア -->
<div id="result">
<p><strong>検索結果:</strong></p>
<p>住所: <span id="result-address">-</span></p>
<p>住所読み: <span id="result-address_read">-</span></p>
<p>緯度: <span id="result-lat">-</span></p>
<p>経度: <span id="result-lng">-</span></p>
</div>
<div id="ZMap"></div>
<script src="js/zma_address_search.js"></script>
</body>
</html>
基本構造: HTML文書の基本的な構造を定義しています。
スタイル設定: CSSスタイルで、検索バー、結果表示エリア、地図のレイアウトを設定しています。
ローダーの読み込み: ZENRIN Maps APIのローダーを読み込みます。
2.初期関数
ZMALoader.setOnLoad(function (mapOptions, error) {
if (error) {
console.error(error)
return
}
// 地図オブジェクト
const lat = 35.681406, lng = 139.767132;
const mapElement = document.getElementById('ZMap');
// MapOptionsを設定
mapOptions.center = new ZDC.LatLng(lat, lng);
mapOptions.zipsMapType = 'VeAmBrmV'
mapOptions.mouseWheelReverseZoom = true;
// 地図を生成
map = new ZDC.Map(
mapElement,
mapOptions,
function() {
// Success callback
map.addControl(new ZDC.ZoomButton('bottom-right'));
map.addControl(new ZDC.Compass('top-right'));
map.addControl(new ZDC.ScaleBar('bottom-left'));
},
function() {
// Failure callback
}
);
})
地図の初期化: 東京駅を中心に地図を初期化し、ズームボタンやコンパスなどのコントロールを追加します。
3.住所検索関数 searchAddress()
async function searchAddress() {
const address = document.getElementById("address").value;
if (!address) {
alert("住所を入力してください");
return;
}
try {
const response = await fetch('https://test-web.zmaps-api.com/search/address', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'x-api-key': 'YOUR_API_KEY',
'Authorization': 'referer'
},
body: new URLSearchParams({
word: address,
word_match_type: '3'
})
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const data = await response.json();
console.log(data.result.item);
if (data.status === "OK" && data.result.info.hit > 0) {
const location = data.result.item[0].position;
const lng = location[0];
const lat = location[1];
const latLng = new ZDC.LatLng(lat, lng);
// 地図をその位置に移動
map.setCenter(latLng);
/* Markerの設置 */
center_mrk = new ZDC.CenterMarker();
map.addControl(center_mrk);
// 検索結果を表示
document.getElementById("result-address").textContent = data.result.item[0].address;
document.getElementById("result-address_read").textContent = data.result.item[0].address_read;
document.getElementById("result-lat").textContent = lat;
document.getElementById("result-lng").textContent = lng;
document.getElementById("result").style.display = "block";
console.log("緯度:", lat, "経度:", lng);
} else {
alert("住所の位置情報が見つかりませんでした");
}
} catch (error) {
console.error("エラーが発生しました:", error);
alert("住所の検索中にエラーが発生しました");
}
}
住所入力のチェック: 住所が入力されていない場合、アラートを表示します。
住所検索APIの呼び出し: ZENRIN Maps APIの住所検索APIを使用して、入力された住所の位置情報を取得します。
地図の更新と結果表示: 検索結果に基づいて地図を更新し、住所や緯度経度を表示します。
🗺 OpenStreetMap vs ZENRIN Maps API 徹底比較!
地図サービスにはそれぞれ特徴があります。
ここでは OpenStreetMap と ZENRIN Maps API のメリット・デメリットを比較しながら、
さらに改善できるポイントも考えてみました!🔍
🌍 OpenStreetMap(OSM)
✅ 良いところ
🔹 無料で利用可能:
OpenStreetMapは完全無料!オープンソースの地図データを活用できます。
🔹 カスタマイズ性が高い:
地図データや表示方法を自由に変更でき、特定のニーズに合わせたアプリ開発が可能。
🔹 世界中の地図データが利用可能:
地域に関係なく、グローバルな地図データを利用可能!🌏
⚠️ 改善点
❌ データ精度が地域によって異なる:
ボランティア更新のため、データの精度や更新頻度にばらつきあり。
❌ 商用利用には制限がある:
商用利用には特定の条件が必要。
🗾 ZENRIN Maps API
✅ 良いところ
🔹 高精度な地図データ:
専門の調査員による詳細な地図データで、日本国内の利用に最適!📍
🔹 リアルタイムデータの提供:
交通情報🚗や天候🌤をリアルタイムで提供。
ナビゲーションや配送ルートの最適化に活用できる!
🔹 高度なルート検索機能:
車種や時間帯ごとの規制を考慮した精密なルート検索が可能。
⚠️ 改善点
❌ 有料サービス:
利用には契約が必要で、コストが発生。💰
❌ カスタマイズ性が低い:
商用APIのため、OpenStreetMapほど自由なカスタマイズができない。
🎉 無料で使える住所正規化エンジン! 🏠
🚀 オープンソース住所正規化エンジンを地番住所に対応したメジャーバージョンをリリースしました!
⚡ 使う前にチェック! ⚡
📝 使用方法を確認して、安全に活用しましょう!