# 機能設計書 20-位置情報検索

## 概要

本ドキュメントは、Wikipedia iOSアプリにおける「位置情報検索」機能の設計を記述する。この機能は、地理座標に基づいてWikipedia記事を検索し、特定の位置周辺の記事を取得するものである。

### 本機能の処理概要

**業務上の目的・背景**：Wikipediaには多くの記事が位置情報（緯度・経度）を持っており、これらを活用して「ある場所の周辺にどのような記事があるか」を検索できる。観光、教育、調査などの目的で、場所に関連した情報を効率的に見つけることができる。

**機能の利用シーン**：Places画面でユーザーが地図を操作（パン、ズーム）すると、表示領域の中心座標と半径に基づいてWikipedia GeoData APIを呼び出し、その領域内の記事を取得する。また、テキスト検索と位置情報検索を組み合わせることで、特定の場所に関連する記事を絞り込むことも可能。

**主要な処理内容**：
1. 地図表示領域から検索パラメータを算出
2. Wikipedia GeoData API（geosearch）の呼び出し
3. 検索結果の取得とパース
4. 結果をWMFArticleオブジェクトに変換
5. 地図上へのピン表示用データ提供

**関連システム・外部連携**：Wikipedia MediaWiki API（action=query&generator=geosearch）、MapKitと連携する。

**権限による制御**：本機能は認証不要で利用可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Places画面 | 主画面 | 地図領域に基づく記事検索 |

## 機能種別

API検索 / 位置情報処理 / データ取得

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| siteURL | URL | Yes | 検索対象のWikipediaサイトURL | 有効なURLであること |
| region | CLCircularRegion | Yes | 検索領域（中心座標と半径） | 有効な座標と正の半径 |
| searchTerm | String? | No | 追加の検索キーワード | - |
| sortStyle | WMFLocationSearchSortStyle | Yes | ソート方法 | - |
| resultLimit | Int | Yes | 取得件数上限 | 通常50件 |

### 入力データソース

- 地図操作：MKMapViewの表示領域
- 検索入力：検索バーのテキスト

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| results | [MWKSearchResult] | 検索結果の記事配列 |
| coordinate | CLLocationCoordinate2D | 各記事の座標 |
| displayTitle | String | 記事タイトル |
| thumbnailURL | URL? | サムネイル画像URL |

### 出力先

- データモデル：PlaceSearchResult
- 画面表示：MapView上のArticlePlaceアノテーション

## 処理フロー

### 処理シーケンス

```
1. 地図領域変更検知
   └─ mapViewRegionDidChange
2. 検索パラメータ算出
   └─ 中心座標と半径を計算
3. API呼び出し準備
   └─ WMFLocationSearchFetcher.fetchArticles()
4. GeoData API呼び出し
   └─ action=query&generator=geosearch
5. 結果パース
   └─ MWKSearchResult配列に変換
6. 検索結果返却
   └─ completion handler呼び出し
```

### フローチャート

```mermaid
flowchart TD
    A[地図領域変更] --> B[検索パラメータ算出]
    B --> C[中心座標取得]
    C --> D[半径計算]
    D --> E[WMFLocationSearchFetcher]
    E --> F[GeoData API呼び出し]
    F --> G{API成功?}
    G -->|Yes| H[結果パース]
    G -->|No| I[エラー処理]
    H --> J[MWKSearchResult配列]
    J --> K[completion呼び出し]
    I --> L[エラーcallback]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-20-01 | 検索上限 | 1回の検索で最大50件取得 | API呼び出し時 |
| BR-20-02 | 半径計算 | 表示領域の幅と高さの大きい方の半分を半径とする | 検索パラメータ算出時 |
| BR-20-03 | ソートオプション | none/links/pageviews/random | sortStyle指定時 |
| BR-20-04 | 言語指定 | siteURLで検索対象言語を指定 | 検索実行時 |

### 計算ロジック

半径計算:
```swift
let center = region.center
let halfLatitudeDelta = region.span.latitudeDelta * 0.5
let halfLongitudeDelta = region.span.longitudeDelta * 0.5
let top = CLLocation(latitude: center.latitude + halfLatitudeDelta, longitude: center.longitude)
let bottom = CLLocation(latitude: center.latitude - halfLatitudeDelta, longitude: center.longitude)
let left = CLLocation(latitude: center.latitude, longitude: center.longitude - halfLongitudeDelta)
let right = CLLocation(latitude: center.latitude, longitude: center.longitude + halfLongitudeDelta)
let height = top.distance(from: bottom)
let width = right.distance(from: left)
let radius = round(0.5 * max(width, height))
```

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 記事情報取得/作成 | WMFArticle | SELECT/INSERT | 検索結果からWMFArticleを取得または作成 |
| 座標情報更新 | WMFArticle | UPDATE | 記事の座標情報を更新 |

### テーブル別操作詳細

#### WMFArticle

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT/UPDATE | latitude | 記事の緯度 | - |
| INSERT/UPDATE | longitude | 記事の経度 | - |
| INSERT/UPDATE | signedQuadKey | QuadKey値 | 位置情報インデックス用 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| noResults | 検索エラー | 該当記事なし | WMFLocationSearchErrorCode.noResults |
| networkError | 通信エラー | API接続失敗 | エラーメッセージ表示 |

### リトライ仕様

ネットワークエラー時は自動リトライなし。ユーザーが地図を操作することで再検索可能。

## トランザクション仕様

検索結果のCore Data保存はトランザクション内で実行。

## パフォーマンス要件

- 検索結果は2秒以内に返却
- 地図操作中は検索を抑制（debounce）

## セキュリティ考慮事項

- 検索パラメータはHTTPS経由でAPIに送信
- ユーザーの位置情報は送信しない（地図表示領域のみ送信）

## 備考

WMFLocationSearchFetcherはWMF Framework内に実装されており、GeoData APIの呼び出しを抽象化している。

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

検索結果のデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | MWKSearchResult | `WMF Framework/` | 検索結果の記事情報、coordinate属性 |
| 1-2 | CLCircularRegion | iOS標準 | 検索領域（中心と半径）|

**読解のコツ**: MWKSearchResultは検索APIの結果を表すクラス。coordinateプロパティで記事の位置情報を取得できる。

#### Step 2: 検索サービスを理解する

PlaceSearchServiceが検索処理の入口。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PlaceSearchService.swift | `Wikipedia/Code/PlaceSearchService.swift` | 検索処理のエントリーポイント |

**主要処理フロー**:
1. **46-109行目**: performSearch - 検索実行
2. **87-108行目**: top検索時の処理、半径計算
3. **88-99行目**: 中心座標、幅、高さ、半径の計算
4. **101-107行目**: locationSearchFetcher.fetchArticles呼び出し

#### Step 3: API呼び出しを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | WMFLocationSearchFetcher | `WMF Framework/` | GeoData API呼び出し |

**主要処理フロー**:
- fetchArticles(withSiteURL:in:matchingSearchTerm:sortStyle:resultLimit:) - メインメソッド
- API URLの構築
- 結果のパース

#### Step 4: 結果処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | PlacesViewController.swift | `Wikipedia/Code/PlacesViewController.swift` | 結果の処理と表示 |

**主要処理フロー**:
- **766-768行目**: updatePlaces(withSearchResults:) - 検索結果の処理
- 結果からArticlePlaceアノテーション作成

### プログラム呼び出し階層図

```
PlacesViewController
    │
    ├─ performSearch
    │      └─ PlaceSearchService.performSearch
    │             └─ WMFLocationSearchFetcher.fetchArticles
    │                    └─ GeoData API呼び出し
    │                           └─ action=query&generator=geosearch
    │
    └─ updatePlaces(withSearchResults:)
           └─ ArticlePlace作成
                  └─ MapViewに追加
```

### データフロー図

```
[入力]                [処理]                        [出力]

地図領域 ───▶ PlaceSearchService ───▶ MWKSearchResult配列
                   │
                   ├─▶ 半径計算
                   │
                   └─▶ WMFLocationSearchFetcher
                          └─▶ GeoData API
                                 └─▶ JSON結果
                                        └─▶ パース
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PlaceSearchService.swift | `Wikipedia/Code/` | ソース | 検索サービス |
| WMFLocationSearchFetcher | `WMF Framework/` | ソース | 位置検索API呼び出し |
| MWKSearchResult | `WMF Framework/` | ソース | 検索結果モデル |
| WMFLocationSearchSortStyle | `WMF Framework/` | ソース | ソートスタイル定義 |
| PlacesViewController.swift | `Wikipedia/Code/` | ソース | 結果の表示処理 |
