OSM & ZENRIN Maps APIで活用！地番対応の住所正規化エンジンを試す

# 📌 今回やりたかったこと

[📍「住所検索の正確さを比較！Google Maps APIとZENRIN Maps API」](https://qiita.com/goldie9/items/73729d353d08f3d6f7e0)では、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**](https://www.zenrin-datacom.net/solution/zenrin-maps-api/trial?fm_cp=6757baf3203e3a00bb118509&fm_mu=6763a0bb1fd14c05d0cd427b&utm_campaign=6757baf3203e3a00bb118509&utm_medium=else&utm_source=Qiita59) 🆓  

🗺 **ZENRIN Maps API** は、  
日本国内の詳細な地図情報を提供する高精度なマップサービスです！ 📍✨  

💡 **なんと、2か月間も無料でお試し可能！** 🎊  
このチャンスに、**高精度な住所検索や地図機能** を体験してみませんか？ 🚀  

APIキーの取得方法はこちら！ 🔽
[ZENRIN Maps APIを使ってみた](https://qiita.com/goldie9/items/1983bf8c4ad796290cbf)

## 🔎 難読住所の検索結果  

日本には**一見すると読みにくい住所**がたくさんあります！📜💦  
ここでは、**「OpenStreetMap」** と **「ZENRIN Maps API」** で検索した結果を比較してみます！ 🧐  

---

### 🏠 1. 埼玉県 **蕨市（わらびし）**  
📌 **OpenStreetMap**  
![osm_address_warabi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/adf63d1b-b03e-4c26-b0db-b8c707cf6808.png)  

📍 **ZENRIN Maps API**  
![ZMA_address_warabi.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/0fb804a0-8fc2-4d81-95e7-a69a816c3c99.png)  

---

### 🏠 2. 千葉県 **匝瑳市（そうさし）**  
📌 **OpenStreetMap**  
![osm_address_sousa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/817b4c63-6191-44ef-8ff7-7b5a17e09f0c.png)  

📍 **ZENRIN Maps API**  
![ZMA_address_sousa.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/37771445-a857-46b4-b9b8-6ee17443a23d.png)  

---

### 🏠 3. 石川県 **羽咋市（はくいし）**  
📌 **OpenStreetMap**  
![osm_address_hakui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/de08b6ad-f649-422d-ab30-84cae6719ec9.png)  

📍 **ZENRIN Maps API**  
![ZMA_address_hakui.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/d9b292fd-0505-422f-b6c8-a17efc317af2.png)  

---

 ## サンプルコード
<strong>Open Street Map</strong>
```osm_address_search.html
<!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>
```
```osm_address_searche.js
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: '&copy; <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);
    });
}
```

<strong>ZENRIN Maps API</strong>
```zma_address_search.html
<!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>
```
```zma_address_search.js
// グローバル変数として `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("住所の検索中にエラーが発生しました");
  }
}
```


 ## 地図表示
<strong>Open Street Map</strong>
![osm_address.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/660c076e-e49e-4009-8ee1-cc6d7dc8a502.png)
<strong>ZENRIN Maps API</strong>
[ZENRINMapsAPI 住所検索](https://goldiek24.github.io/qiita_adress_search/zma_address_search.html)
![zma_address_kucchanchou.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/9605efb5-1e71-43b8-8ec9-91dfe4dd47e0.png)

## 解説
### Open Street Map
#### １．準備：必要なライブラリの読み込み（HTML）
まず、HTMLファイルで必要なライブラリを読み込みます。
`<head>`タグ内で、Google Maps APIのスクリプトを読み込み、callbackとしてinitMap関数を指定しています。
```osm_address_search.html
<!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>
```
<strong>Leaflet:</strong> 地図表示のための基本ライブラリです。CSSとJavaScriptファイルを読み込みます。
<strong>leaflet-control-geocoder:</strong> 住所検索機能を追加するためのライブラリです。CSSとJavaScriptファイルを読み込みます。
<strong>osm_address_searche.js:</strong> アプリケーションの主要なロジックを記述するJavaScriptファイルです。type="module"属性をつけて読み込むことで、ES Modules形式で記述されたJavaScriptファイルとして扱われます。
<strong>CSS:</strong> 地図表示領域のスタイルを定義しています。


#### ２．住所正規化ライブラリのインポート（JavaScript）
JavaScriptファイル（osm_address_searche.js）で、住所正規化ライブラリをインポートします。
```javascript
import { normalize } from './address-normalizer/node_modules/@geolonia/normalize-japanese-addresses/dist/main-esm.mjs';
```
<strong>@geolonia/normalize-japanese-addresses：</strong> 住所を正規化するためのライブラリです。この例では、ローカルにインストールされたモジュールを参照しています。（<strong>注意：このパスはプロジェクトの構成によって異なります。プロジェクトのnode_modulesディレクトリ内の実際のパスに合わせてください。</strong>）

#### ３．地図の初期設定（JavaScript）
```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: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
});
tileLayer.addTo(map);

```
・<strong>L.map('mapid', { ... })：</strong> 指定されたID（mapid）の要素に地図を埋め込みます。centerオプションで初期表示の中心座標を、zoomオプションで初期ズームレベルを設定します。
・<strong>L.tileLayer(...)：</strong> 地図のタイルレイヤーを追加します。この例では、OpenStreetMapのタイルを使用しています。attributionオプションで、著作権表示を設定します。

#### ４．Geocoderの設定と地図への追加（JavaScript）
leaflet-control-geocoderを使って、住所検索機能を設定し、地図に追加します。
```javascript
// Nominatim Geocoderの設定
var geocoder = L.Control.Geocoder.nominatim();

// Geocoderを地図に追加
var osmGeocoder = new L.Control.Geocoder({
    geocoder: geocoder
});
osmGeocoder.addTo(map);
```
・<strong>L.Control.Geocoder.nominatim()：</strong> Nominatim Geocoderのインスタンスを作成します。
・<strong>L.Control.Geocoder({ geocoder: geocoder })：</strong> Geocoderコントロールを作成し、Nominatim Geocoderのインスタンスを渡します。
・<strong>osmGeocoder.addTo(map)：</strong> Geocoderコントロールを地図に追加します。

#### ５．検索結果の処理（JavaScript）
Geocoderで検索された住所情報を取得し、正規化して地図上に表示します。
```javascript
// 検索結果の表示
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);
});
```
・<strong>osmGeocoder.on('markgeocode', function(e) { ... })：</strong> Geocoderで検索が実行され、結果が地図上に表示されたときに実行されるイベントリスナーを設定します。
・<strong>result.center：</strong> 検索結果の中心座標（緯度、経度）を取得します。
・<strong>result.name：</strong> 検索結果の住所文字列を取得します。
・<strong>normalize(address)：</strong> 取得した住所を@geolonia/normalize-japanese-addressesで正規化します。
・normalizedAddress.metadataから読み仮名情報を取得します。 normalizedAddress.metadata の prefecture、city、machiAza プロパティが存在し、かつそれぞれの pref_k、city_k、oaza_cho_k プロパティが存在する場合に、その値を変数に格納します。
・<strong>L.popup()：</strong> 地図上にポップアップを作成し、住所、読み仮名、緯度、経度を表示します。

### ZENRIN Maps API
#### １．地図の初期化
```zma_address_search.html
<!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>
```
<strong>基本構造:</strong> HTML文書の基本的な構造を定義しています。
<strong>スタイル設定:</strong> CSSスタイルで、検索バー、結果表示エリア、地図のレイアウトを設定しています。
<strong>ローダーの読み込み:</strong> ZENRIN Maps APIのローダーを読み込みます。


#### ２．初期関数
 ```zma_address_search.js
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
        }
    );
})
 ```
<strong>地図の初期化:</strong> 東京駅を中心に地図を初期化し、ズームボタンやコンパスなどのコントロールを追加します。

#### ３．住所検索関数 `searchAddress()`
 ```zma_address_search.js
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("住所の検索中にエラーが発生しました");
    }
}
 ```
<strong>住所入力のチェック:</strong> 住所が入力されていない場合、アラートを表示します。
<strong>住所検索APIの呼び出し:</strong> ZENRIN Maps APIの住所検索APIを使用して、入力された住所の位置情報を取得します。
<strong>地図の更新と結果表示:</strong> 検索結果に基づいて地図を更新し、住所や緯度経度を表示します。

# 🗺 **OpenStreetMap vs ZENRIN Maps API 徹底比較！**  

地図サービスにはそれぞれ特徴があります。  
ここでは **OpenStreetMap** と **ZENRIN Maps API** のメリット・デメリットを比較しながら、  
さらに改善できるポイントも考えてみました！🔍  

---

## 🌍 **OpenStreetMap（OSM）**  

### ✅ **良いところ**  
🔹 **無料で利用可能**：  
　OpenStreetMapは**完全無料**！オープンソースの地図データを活用できます。  

🔹 **カスタマイズ性が高い**：  
　地図データや表示方法を自由に変更でき、特定のニーズに合わせたアプリ開発が可能。  

🔹 **世界中の地図データが利用可能**：  
　地域に関係なく、**グローバルな地図データ**を利用可能！🌏  

### ⚠️ **改善点**  
❌ **データ精度が地域によって異なる**：  
　ボランティア更新のため、データの精度や更新頻度にばらつきあり。  

❌ **商用利用には制限がある**：  
　商用利用には**特定の条件**が必要。  

---

## 🗾 **ZENRIN Maps API**  

### ✅ **良いところ**  
🔹 **高精度な地図データ**：  
　専門の調査員による詳細な地図データで、日本国内の利用に最適！📍  

🔹 **リアルタイムデータの提供**：  
　交通情報🚗や天候🌤をリアルタイムで提供。  
　ナビゲーションや配送ルートの最適化に活用できる！  

🔹 **高度なルート検索機能**：  
　車種や時間帯ごとの規制を考慮した**精密なルート検索**が可能。  

### ⚠️ **改善点**  
❌ **有料サービス**：  
　利用には契約が必要で、コストが発生。💰  

❌ **カスタマイズ性が低い**：  
　商用APIのため、OpenStreetMapほど自由なカスタマイズができない。  
 
---

## 🎉 **無料で使える住所正規化エンジン！** 🏠  

[🚀 **オープンソース住所正規化エンジンを地番住所に対応したメジャーバージョンをリリースしました！**](https://blog.geolonia.com/2024/10/10/geolonia-address-normalization-engine-update.html)  
⚡ **使う前にチェック！** ⚡  
📝 使用方法を確認して、安全に活用しましょう！