最強の住所検索フォーム作ってみた！建物名補完で簡単入力


## 概要

本ページは、住所を検索して地図に表示するためのウェブフォームです。ユーザーが住所を入力すると、選択した住所の詳細（郵便番号、都道府県、市区町村、町名、緯度経度）を表示します。また、自分で実装した機能により、検索した住所の位置を地図上にマーカーとして表示しています。さらに、建物情報があればその詳細も表示します。

本記事では、特定の API を利用してデータを取得し、解析・表示する方法について解説します。  
API の基本的な使い方から、取得したデータの処理方法まで、具体的なコードを交えて説明します。

## 📌 **主な機能**

### **住所クレンジング API（高機能）による住所データの精度向上**

住所を入力すると、住所クレンジング API が誤字や表記ゆれを補完し、正確な住所情報を返却します。この機能により、ユーザーが入力した住所を正規化し、郵便番号、都道府県、市区町村、町名、緯度経度といった詳細な情報を自動的に表示します。

#### **住所クレンジング機能の詳細**

- **重複する住所の処理を最適化**

  - 例：神奈川県横浜市港北区大豆戸町 316-6 → 住所クレンジング後、建物名を付与すると「菊名ハイツ 3 号棟」と「菊名ハイツ 4 号棟」を返却。

- **複雑な住所検索の処理**

  - 例：鹿児島県志布志市志布志町志布志１－１－１や、京都府京都市中京区寺町通御池上る上本能寺前町 488 番地など。

- **市区町村変更履歴の対応**

  - 例：（旧住所）福岡県前原市前原東一丁目 → 住所クレンジング後、現在の住所「福岡県糸島市前原東一丁目」を返却。

- **住所詳細情報の自動表示**
  - 住所を選択すると、郵便番号や緯度経度などの詳細情報が表示されます。

---

### **Zenrin Maps API と連携した地図表示機能**

住所を選択すると、Zenrin Maps API を活用して地図上に住所の位置を表示します。正確な場所にマーカーが表示され、建物名の詳細も表示されます。

- **地図上に住所の位置をマーカー表示**
  - 選択した住所の位置にマーカーを表示し、複数の建物に関連付けられた場合は、マーカーに番号を付けて表示します。

## 📌 **対象読者**

本ドキュメントは、**住所検索フォーム**を実装しようとしている開発者向けの解説です。  
特に以下のような方に役立ちます：

- **JavaScript を使用して住所検索機能を追加したい方**
- **地図（ZENRIN Maps API）と連携したフォームを作成したい方**
- **建物名や市区町村の変更を適切に処理したい方**

# API キーの取得

**ZENRIN Maps API**

１．検証用 ID とパスワード（PW）取得

ZENRIN Maps API を利用するには、検証用 ID とパスワード（PW）を取得し、API キーを取得する必要があります。以下のリンクから申請・取得を行ってください

[ZENRIN Maps API 　無料お試し ID 　お申込みフォーム](https://www.zenrin-datacom.net/solution/zenrin-maps-api/trial?fm_cp=6757baf3203e3a00bb118509&fm_mu=67639def1fd14c05d0cd1c27&utm_campaign=6757baf3203e3a00bb118509&utm_medium=else&utm_source=Qiita35)

[ コンソール](https://test-console.zmaps-api.com/)

[ZENRIN Maps API リファレンス](https://developers.zmaps-api.com/v20/reference/)

## 📜 **コード解説**

# 住所入力フォームの作成方法

ウェブサイトやアプリケーションで住所入力フォームを作成する際、ユーザーが手軽に住所を入力できるようにすることが重要です。ここでは、**住所検索機能**を持つフォームを作成する方法について、具体的な HTML コードを紹介します。

## フォーム構造

このコードでは、住所入力フォームが表示され、住所が選択されると詳細情報（郵便番号、都道府県、市区町村など）が表示される仕組みになっています。

### address_Search.html

```html
<div id="addressContainer">
  <h2>住所入力フォーム</h2>

  <label>住所検索:</label>
  <div class="input-group">
    <i class="fas fa-search"></i>
    <input
      type="text"
      id="ADDRESS_INPUT"
      placeholder="例: 千葉県八千代市八千代北"
      autocomplete="on"
    />
  </div>
        <button id="submitBtn">住所を確認</button>

  <!-- 住所詳細情報を表示 -->
  <div id="addressDetails">
    <h3>住所詳細</h3>
    <label>郵便番号:</label>
    <div class="input-group">
      <i class="fas fa-envelope"></i>
      <input type="text" id="POSTAL_CODE" readonly />
    </div>
    <label>都道府県:</label>
    <div class="input-group">
      <i class="fas fa-map-marker-alt"></i>
      <input type="text" id="PREFECTURE" readonly />
    </div>
    <label>市区町村:</label>
    <div class="input-group">
      <i class="fas fa-city"></i>
      <input type="text" id="CITY" readonly />
    </div>
    <label>町名:</label>
    <div class="input-group">
      <i class="fas fa-home"></i>
      <input type="text" id="TOWN" readonly />
    </div>
    <label>経緯度:</label>
    <div class="input-group">
      <i class="fas fa-map-pin"></i>
      <input type="text" id="LATLON" readonly />
    </div>
  </div>
   <div id="BUILDING_NAME_DISPLAY"></div>
        </div>
    </div>


    <div id="ZMap"></div>

    <script
        src="https://test-js.zmaps-api.com/zma_loader.js?key=[YOUR_API_KEY]&auth=referer"></script>
    <script src="address_Search.js"></script>

</div>
```

このコードでは、住所検索用の入力フィールドがあり、その下に住所詳細が表示される仕組みになっています。詳細情報はユーザーが住所を選択した際に動的に表示されます。

**Zenrin Maps API の設定**:Zenrin Maps API を使用し地図を描画します。

### address_Search.js

```javascript
let map;
let marker;
const lat = 35.681406,
  lng = 139.767132;

ZMALoader.setOnLoad(function (mapOptions, error) {
  if (error) {
    console.error("Map Loader Error: ", error);
    return;
  }
  mapOptions.center = new ZDC.LatLng(lat, lng);
  mapOptions.mouseWheelReverseZoom = true;
  mapOptions.zipsMapType = "Ai3Y2Jwp"; // 地図タイプの設定

  const mapElement = document.getElementById("ZMap");

  map = new ZDC.Map(mapElement, mapOptions, function () {
    map.addControl(new ZDC.ZoomButton("top-right"));
    map.addControl(new ZDC.ScaleBar("bottom-left"));
  });
});
```

# 📌 ポイント：

**地図の初期化**: ZMALoader.setOnLoad で地図を初期化し、mapOptions を設定します。

**東京駅を初期座標として設定**: 中心位置を東京駅（緯度 35.681406、経度 139.767132）に設定します。

**ズームボタンとスケールバーの追加**: ズームボタンを右上、スケールバーを左下に追加します。

### 住所検索機能

```javascript
document.getElementById("submitBtn").addEventListener("click", async () => {
    const QUERY = ADDRESS_INPUT.value.trim(); // ユーザーが入力した住所

    if (QUERY.length < 2) {
        alert("住所を入力してください。");
        return;
    }

    let API_URL = `https://test-web.zmaps-api.com/data-coding/ac_premium?&word=${encodeURIComponent(QUERY)}&use_multi_addr=true&use_kana=true&use_pastmap=true&use_bluemap=true`;

```

ここで、QUERY 変数にユーザーが入力した住所を格納し、その住所を使って Zenrin Maps API にリクエストを送信します。住所検索が正常に終了した場合、その結果を処理します。

**入力が 2 文字未満の場合は検索を行わない**：if (QUERY.length < 2) で、入力文字数が 2 文字未満の場合、検索をスキップします。

```js
    try {
        const RESPONSE = await fetch(API_URL, {
            method: 'GET',
            headers: {
                'Content-Type': 'application/x-www-form-urlencoded',
                'x-api-key': '[YOUR_API_KEY]',
                'Authorization': 'referer',
            }
        });

        if (!RESPONSE.ok) throw new Error(`HTTP ERROR! STATUS: ${RESPONSE.status}`);

        const DATA = await RESPONSE.json();
        console.log(DATA);

        if (DATA.status === "OK" && DATA.result.info.hit > 0) {
            FILL_ADDRESS(DATA.result.item[0]); // 最初の住所候補を選択して表示
        } else {
            alert("該当する住所がありません");
        }
    } catch (ERROR) {
        console.error("エラー:", ERROR);
        alert("エラーが発生しました");
    }
});

```

# 📌 ポイント：

～ API から取得したデータが正常であれば、最初の住所候補を FILL_ADDRESS 関数で表示します。

～住所候補が見つからなかった場合は、「該当する住所がありません」と警告します。

# 検索結果を地図に反映

**選択した住所の緯度経度を取得し、地図上にマーカーを表示します。**
選択した住所に対応する地図の位置を表示し、その位置にマーカーを配置します。また、複数の建物が関連する場合は、それぞれの位置にマーカーを追加できます。

```javascript
function FILL_ADDRESS(ITEM) {
  // 住所情報をフォームに埋め込む
  document.getElementById("POSTAL_CODE").value = ITEM.post_code;
  document.getElementById("PREFECTURE").value = ITEM.address2;
  document.getElementById("CITY").value = ITEM.address3;
  document.getElementById("TOWN").value = ITEM.address4;
  ADDRESS_INPUT.value = ITEM.address;

  // 現在のセンターマーカーとそのウィジェットを削除
  if (marker) {
    map.removeControl(marker);
    map.removeWidget(mrk_widget);
    marker = null;
    mrk_widget = null;
  }

  // 以前の建物マーカーを削除
  if (buildingMarkers.length > 0) {
    buildingMarkers.forEach((buildingMarker) => {
      map.removeWidget(buildingMarker); // 各建物マーカーを地図から削除
    });
    buildingMarkers = []; // 建物マーカー配列をクリア
  }

  // match_positionが存在する場合、センターマーカーを追加
  if (ITEM.match_position && ITEM.match_position.length === 2) {
    document.getElementById(
      "LATLON"
    ).value = `${ITEM.match_position[1]},${ITEM.match_position[0]}`; // 緯度経度をフォームに設定
    const latLng = new ZDC.LatLng(
      ITEM.match_position[1],
      ITEM.match_position[0]
    );

    // 地図の位置を更新
    map.setCenter(latLng); // 地図のセンターを設定

    // マーカーを作成して地図に追加
    marker = new ZDC.CenterMarker(latLng); // センターマーカーを作成
    mrk_widget = new ZDC.Marker(latLng); // マーカーウィジェットを作成
    map.addWidget(mrk_widget); // ウィジェットを地図に追加
  } else {
    document.getElementById("LATLON").value = "位置情報なし";
  }

  // 複数の建物マーカーを追加
  if (
    ITEM.building_info &&
    Array.isArray(ITEM.building_info) &&
    ITEM.building_info.length > 0
  ) {
    ITEM.building_info.forEach((building, index) => {
      if (building.position && building.position.length === 2) {
        const buildingLatLng = new ZDC.LatLng(
          building.position[1],
          building.position[0]
        );

        // マーカーオプションの基本オブジェクトを作成
        let markerOptions = {
          styleId: ZDC.MARKER_COLOR_ID_RED_S, // 赤色のマーカーに設定
        };

        // 建物が複数ある場合、contentStyleIdを設定
        if (ITEM.building_info.length > 1) {
          // インデックスに基づいてcontentStyleIdを動的に生成
          let contentStyleId = `MARKER_NUMBER_ID_${index + 1}_S`;
          markerOptions.contentStyleId = ZDC[contentStyleId]; // 有効なcontentStyleIdを設定
        }

        // 動的オプションを使ってマーカーを作成
        const buildingMarker = new ZDC.Marker(buildingLatLng, markerOptions);

        // 地図にマーカーを追加
        map.addWidget(buildingMarker);

        // 追加したマーカーを後で使えるように配列に保存
        buildingMarkers.push(buildingMarker);
      }
    });
  }

  // 建物名があれば表示
  if (
    ITEM.building_info &&
    Array.isArray(ITEM.building_info) &&
    ITEM.building_info.length > 0
  ) {
    const buildingNames = ITEM.building_info
      .map((info) => info.building_name) // 建物名を抽出
      .join(", "); // カンマ区切りで結合
    BUILDING_NAME_DISPLAY.textContent = `建物名: ${buildingNames}`; // 建物名を表示
  } else {
    BUILDING_NAME_DISPLAY.textContent = ""; // 建物名がなければ空にする
  }

  // 詳細情報を表示
  ADDRESS_DETAILS.style.display = "block"; // 詳細情報を表示
}
```

# 📌 ポイント：

～住所を選択すると、選択された位置に地図がズームインし、中心にマーカーが表示されます。

～複数の建物が住所に関連付けられている場合、その位置にもマーカーを追加します。

～検索結果が複数ある場合、各建物の位置に番号付きのマーカーが表示されます。

～複数の建物情報がある場合、建物名が画面に表示されます。

### 地図表示

**初期表示**


[参考サイト](https://bikram-pandey.github.io/web_projects/address_Search.html)

![スクリーンショット 2025-03-06 114246.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981270/33e231ed-c4e4-48b5-bb88-ca371a1e2d90.png)

**住所検索した際**
![スクリーンショット 2025-03-06 114311.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3981270/e81480ed-35e0-4747-8d07-446c0c5853bd.png)



# 📝 結論

本記事では、住所検索フォームの作成方法について解説しました。具体的には、住所クレンジング API を使用して、ユーザーが住所を入力すると自動的にクレンジングをかけた住所が表示され、選択した住所の詳細情報（郵便番号、都道府県、市区町村、緯度経度など）が表示される仕組みを実装しました。また、Zenrin Maps API を活用し、地図上に住所の位置をマーカーで表示し、建物名や市区町村変更など、複雑な住所にも対応できるようにしました。

**主なポイント**

住所クレンジング API での住所の正規化と補完。

選択した住所の詳細情報を自動的に表示。

地図上への位置表示と、マーカーでの住所確認。

このように、住所検索フォームを作成する際、ユーザーが簡単に住所を入力できるような機能を提供し、精度の高い情報を基に地図と連携させることができます。