ゼンリンAPIを使って半径の範囲を一瞬で可視化！便利ツールを作ってみた

## はじめに 👋
みなさん、こんにちは！  
「この駅から徒歩10分圏内ってどこまで？」「この施設の半径1km以内に何がある？」  
こんな疑問をサクッと解決できるツールを作っちゃいました！✨  

今回は、その開発の過程と結果をご紹介します！🎨💻

---

## 🏗️ どうして作ったの？背景と目的 🎯
日常生活やビジネスで、**特定の場所から○mの範囲を知りたい！**って思うことありませんか？🤔  

そんなときに **直感的に範囲を確認できるツール** があったら便利！💡  
そう思って、今回の開発にチャレンジしました！🔥  

### ✅ このツールでできること
- 駅・住所・施設名・緯度経度から**中心点を設定**
- 指定した半径の**円を地図に表示**
- **使いやすいUI** でサクッと操作できる

---

## 🗺️ ZENRIN Maps APIってなに？📌
今回の開発では、**ZENRIN Maps API** というWebマッピングサービスを使いました！🌍  
🌍 [ZENRIN Maps APIは２か月お試し無料！](https://www.zenrin-datacom.net/solution/zenrin-maps-api/trial?fm_cp=6757baf3203e3a00bb118509&fm_mu=6763a117bdef2d06851aa1f9&utm_campaign=6757baf3203e3a00bb118509&utm_medium=else&utm_source=Qiita62)🚀

### 🏆 ZENRIN Maps APIのスゴイところ
✅ 日本の詳細な地図データ  
✅ 多彩な地図表示オプション  
✅ 住所検索・ルート検索ができる  
✅ マーカーやオーバーレイのカスタマイズもOK  

ゼンリンのAPIを活用して、高精度なマップを実現しています！✨

---

## 🛠️ 完成したツールの概要 🎨
### 🎯 主要機能
✅ **入力フォーム**
  - 駅名 / 住所 / 施設名 / 緯度経度の入力OK！  

✅ **中心点設定**
  - 入力内容に基づいてマップの中心を設定！  

✅ **半径設定**
  - 100m 〜 2,000m の範囲を自由に指定！  

✅ **円の描画**
  - 設定した中心点＆半径で円を表示！  

✅ **マーカー表示**
  - 中心点にマーカーをセット！  

### 🎮 使い方は超シンプル！👍
1️⃣ 入力フォームに情報を入力！  
2️⃣ **「中心点を更新」ボタンをクリック！**  
3️⃣ 半径を設定して **「半径を更新」ボタンをクリック！**  

たったこれだけで、指定範囲を一瞬でマップに可視化できます！🚀  
[ツール](https://goldiek24.github.io/sample_tool/map_circle.html)
![displaycircle.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3965389/efcfba37-0ed1-466e-b48e-0fdd2de1b233.png)
---

## 実装方法
### １．HTML構造
HTMLの構造は、地図を表示する<div>要素と、ユーザー入力を受け付けるコントロールパネルで構成されています。主要な部分は以下の通りです。
```html
<div id="ZMap"></div>
<div id="controls">
  <div id="input-group">
    <input type="text" id="stationInput" placeholder="駅名">
    <input type="text" id="addressInput" placeholder="住所">
    <input type="text" id="facilityInput" placeholder="施設名">
    <input type="text" id="latLngInput" placeholder="緯度,経度">
  </div>
  <button onclick="updateCenter()">中心点を更新</button>
  <input type="number" id="radius" value="2000" min="100" max="10000">
  <button onclick="updateRadius()">半径を更新</button>
</div>
```

### ２．JavaScript実装
#### 2-1.ZENRIN Maps APIの初期化
ZENRIN Maps APIのAPIはZMALoader.setOnLoad関数を使用して初期化します
```javascript
ZMALoader.setOnLoad(function (mapOptions, error) {
  if (error) {
    console.error(error);
    return;
  }

  // マップの初期化処理
  map = new ZDC.Map(mapElement, mapOptions, function() {
    // マップ初期化成功時の処理
  }, function() {
    // マップ初期化失敗時の処理
  });
});
```

#### 2-2．JavaScript実装
中心点の設定（駅、住所、施設、緯度経度）
中心点の設定はupdateCenter関数で行います。
入力タイプに応じて適切なAPI呼び出しを行い、結果を元に地図の中心を更新します。
```javascript
async function updateCenter() {
  const stationInput = document.getElementById('stationInput').value;
  const addressInput = document.getElementById('addressInput').value;
  const facilityInput = document.getElementById('facilityInput').value;
  const latLngInput = document.getElementById('latLngInput').value;

  if (latLngInput) {
    if (isLatLng(latLngInput)) {
      const [lat, lng] = latLngInput.split(',').map(coord => parseFloat(coord.trim()));
      setCenterAndUpdate(lat, lng);
    } else {
      alert("緯度経度の形式が正しくありません。");
    }
  } else if (stationInput) {
    searchAndSetCenter('station', stationInput);
  } else if (addressInput) {
    searchAndSetCenter('address', addressInput);
  } else if (facilityInput) {
    searchAndSetCenter('facility', facilityInput);
  } else {
    alert("いずれかの入力フィールドに値を入力してください。");
  }
}
```

#### 2-3.円の描画
円の描画はdrawOval関数で行います。
```javascript
unction drawOval(radius) {
  if (ovalWidget) {
    map.removeWidget(ovalWidget);
  }
  ovalWidget = new ZDC.Oval(
    map.getCenter(),
    {x: radius, y: radius},
    {
      fillPattern: 'none',
      stroke: '#ff0000',
      strokePattern: 'dash',
      strokeWidth: 5,
      opacity: 0.8
    }
  );
  map.addWidget(ovalWidget);
}
```

### ３．CSSスタイリング
```css
#ZMap {position: absolute; top: 0; bottom: 0; width: 100%;}
#controls {
  position: absolute;
  top: 10px;
  left: 10px;
  background-color: white;
  padding: 10px;
  border-radius: 5px;
  box-shadow: 0 0 10px rgba(0,0,0,0.2);
}
```

## コードの解説
### １．入力フィールドの排他制御 
```javascript
function disableOtherInputs(currentInputId) {
  const inputIds = ['stationInput', 'addressInput', 'facilityInput', 'latLngInput'];
  inputIds.forEach(id => {
    const inputElement = document.getElementById(id);
    if (id !== currentInputId) {
      inputElement.value = '';
      inputElement.disabled = !!document.getElementById(currentInputId).value;
    }
  });
}
```
この関数は、一つの入力フィールドに値が入力されたとき、他の入力フィールドを無効化します。これにより、ユーザーは一度に一つの入力方法のみを使用できます。

### ２．API呼び出しと中心点の設定
```javascript
async function searchAndSetCenter(type, query) {
  // APIエンドポイントとパラメータの設定
  // ...

  try {
    const response = await fetch(apiEndpoint, {
      // リクエスト設定
    });

    const data = await response.json();

    if (data.status === "OK" && data.result.info.hit > 0) {
      let location = data.result.item[0].position;
      const lng = location[0];
      const lat = location[1];
      setCenterAndUpdate(lat, lng);
    }
  } catch (error) {
    // エラー処理
  }
}
```
この関数は、指定された検索タイプ（駅、住所、施設）に応じて適切なAPIを呼び出し、結果を使用して地図の中心点を更新します。

***

## 使用したAPI
### [駅検索（station）](https://developers.zmaps-api.com/v20/reference/webAPI/station.html)
#### 特徴
・駅名フリーワード検索、駅リスト取得、緯度経度検索が可能
・多言語対応（英語、中国語簡体・繁体、韓国語）
#### 使い方
・HTTP POST/GETメソッドで利用
・入力パラメータに駅名や路線コード、緯度経度などを指定
・結果として駅のID、名称、所在地、路線情報などが取得可能
### [住所検索（address）](https://developers.zmaps-api.com/v20/reference/webAPI/address.html)
#### 特徴
・住所クレンジング機能を提供
・地番検索や郵便番号検索も可能
#### 使い方
・フリーワードや住所コード、経緯度座標などから検索
・結果として正規化された住所情報や緯度経度が取得可能
### [企業情報検索（phonenumber_townpage）](https://developers.zmaps-api.com/v20/reference/webAPI/phonenumber_townpage.html)
#### 特徴
・POI（Point of Interest）情報の検索が可能
・多言語対応（英語、中国語簡体・繁体、韓国語）
・企業情報検索機能も提供
#### 使い方
・フリーワードや電話番号、業種から検索
・緯度経度や半径を指定して周辺施設を検索可能
・結果として施設名、住所、電話番号、カテゴリなどの情報が取得可能

# 🎯 開発中に苦戦したポイントと工夫した点 💡

開発って、スムーズに進むことばかりじゃないですよね…💦  
実装中にぶち当たった課題と、その解決策をまとめました！💪✨  

---

## 🚧 実装中に遭遇した課題と解決策 🛠️

### 1️⃣ **複数の入力フィールドの排他制御**
💡 **課題**：  
ユーザーが **同時に複数の入力方法を使えないように** する必要があった 🤯  

✅ **解決策**：  
`disableOtherInputs` 関数を実装し、**1つの入力に値が入ると他のフィールドを無効化**！👌  

---


### 3️⃣ **非同期処理のエラーハンドリング**
💡 **課題**：  
API呼び出しや地図の更新でエラーが発生することも...😱  

✅ **解決策**：  
`try-catch` 文を活用し、  
**エラー時にユーザーへわかりやすいメッセージを表示**！💬  

---

### 4️⃣ **円の描画と更新の最適化**
💡 **課題**：  
半径変更時に、円の描画を **効率的に更新する必要がある** 🎯  

✅ **解決策**：  
`drawOval` 関数を実装！  
**既存の円を削除 → 新しい円を描画** することでスムーズに更新！🔄  

---

# 🎓 今回学んだこと ✨

## 🌍 Web APIの効果的な活用
✅ **複数のAPIを組み合わせて** ひとつの機能を実現  
✅ **レスポンスデータの解析＆処理を最適化**  

---

## ⚡ 非同期処理の重要性
✅ `async/await` を活用した **効率的な非同期処理**  
✅ `Promise` を使って **コードの可読性UP！**  

---

## 🎨 ユーザーインターフェースの設計
✅ 直感的で **使いやすいUI** の大切さを実感  
✅ **入力エラーの適切な処理＆フィードバック** を実装  

---

## 🗺️ **ZENRIN Maps APIの活用と可能性**
今回の開発を通して、**ZENRIN Maps APIの魅力** を改めて実感しました！✨  

### ✅ **ZENRIN Maps APIのスゴイところ！**
🚀 **高精度な地図データ** → 住所や施設情報もバッチリ！  
🔍 **豊富なAPI** → 駅・住所・施設など、検索の幅が広い！  
🎨 **カスタマイズ性バツグン** → マーカーや円の描画も自由自在！  
⚡ **パフォーマンス抜群** → 地図の描画がサクサク！  

---

# 🚀 まとめ 🎯
このプロジェクトを通じて、**ZENRIN Maps APIは単なる地図表示ツールじゃなく、強力なプラットフォーム** だと確信しました！💡  
今後も、この知識を活かして **もっと便利なアプリを開発** していきたいと思います！🔥  

# 📢 みなさんのアイデア募集中！💡✨

今回は、**駅や施設から半径の範囲を可視化できるツール** を作ってみました！🗺️✨  
でも、地図を使ったツールって、もっと色々なことに応用できそうじゃないですか？🤔  

例えば…  
🚀 **複数の地点をつなぐ最短経路表示ツール**  
🏪 **指定エリア内のコンビニや公園をピックアップするツール**  
…なんてアイデアも面白そうですよね！😆🎯  

🌟 **「こんな地図ツールがあったら便利！」** と思うものがあれば、ぜひコメントで教えてください！💬✨  
面白いアイデアがあったら、**次回のチャレンジとして実装してみるかも…！？** 🔥💻  

みなさんのアイデア、お待ちしてます！🙌🚀