# 機能設計書 18-近くの記事（Places）

## 概要

本ドキュメントは、Wikipedia iOSアプリにおける「近くの記事（Places）」機能の設計を記述する。この機能は、ユーザーの現在地や地図上の任意の場所周辺にあるWikipedia記事を地図上に表示するものである。

### 本機能の処理概要

**業務上の目的・背景**：ユーザーが現在地や興味のある場所周辺のWikipedia記事を発見できるようにする。観光地、歴史的建造物、自然景観など、位置情報を持つ記事を地図上に可視化することで、「近くに何があるか」を直感的に探索できる体験を提供する。

**機能の利用シーン**：Places タブをタップすると地図が表示され、ユーザーの現在地周辺の記事がピンとして表示される。地図をスクロール/ズームすると、その領域の記事が動的に読み込まれる。ピンをタップすると記事のポップオーバーが表示され、詳細ページへ遷移できる。

**主要な処理内容**：
1. 現在地の取得と地図表示
2. 表示領域に基づく記事検索（Location Search API）
3. 検索結果を地図上にピンとして表示
4. ピンのクラスタリング（複数記事のグループ化）
5. ピンタップ時のポップオーバー表示
6. フィルタリング（トップ記事/保存記事）

**関連システム・外部連携**：Wikipedia GeoData API（位置情報検索）、MapKit、CoreLocation（位置情報取得）と連携する。

**権限による制御**：位置情報の使用許可が必要。許可がない場合でも地図の手動操作は可能。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 2 | Places画面 | 主画面 | 地図と記事ピンの表示 |

## 機能種別

地図表示 / 位置情報検索 / ピン表示

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| region | MKCoordinateRegion | Yes | 検索対象の地図領域 | 有効な座標範囲 |
| filter | PlaceFilterType | Yes | フィルタタイプ（top/saved） | - |
| searchTerm | String? | No | 検索キーワード | - |

### 入力データソース

- デバイス：CoreLocation（現在地）
- 画面操作：MapKitの地図操作（パン、ズーム）
- フィルタ選択：UIActionSheet

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| annotations | [ArticlePlace] | 地図上のピン配列 |
| articles | [WMFArticle] | 表示対象の記事配列 |
| selectedArticle | WMFArticle? | 選択中の記事 |

### 出力先

- 画面表示：MapView上のアノテーション
- ポップオーバー：ArticlePopoverViewController

## 処理フロー

### 処理シーケンス

```
1. Places画面表示
   └─ viewDidLoad で地図とUIを初期化
2. 位置情報許可確認
   └─ 許可あれば現在地取得、なければデフォルト表示
3. 地図表示領域確定
   └─ mapRegion 設定
4. 記事検索実行
   └─ performSearch() → PlaceSearchService.performSearch()
5. 検索結果取得
   └─ updatePlaces(withSearchResults:) または articleFetchedResultsController
6. ピン表示
   └─ ArticlePlace アノテーションを地図に追加
7. ピンタップ
   └─ selectArticlePlace() → ポップオーバー表示
8. 地図移動
   └─ mapViewRegionDidChange → 「この地域を検索」ボタン表示
```

### フローチャート

```mermaid
flowchart TD
    A[Places画面表示] --> B{位置情報許可?}
    B -->|Yes| C[現在地取得]
    B -->|No| D[デフォルト表示]
    C --> E[地図表示]
    D --> E
    E --> F[performSearch]
    F --> G{フィルタ?}
    G -->|top| H[Location Search API]
    G -->|saved| I[Core Data検索]
    H --> J[検索結果取得]
    I --> J
    J --> K[ピン表示]
    K --> L{ユーザー操作}
    L -->|ピンタップ| M[ポップオーバー表示]
    L -->|地図移動| N[「この地域を検索」表示]
    N --> O[再検索]
    O --> J
    M --> P[記事詳細へ遷移]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-18-01 | 検索上限 | 1回の検索で最大50件取得 | API呼び出し時 |
| BR-18-02 | クラスタリング | 近接する複数記事はグループ化してカウント表示 | ピン表示時 |
| BR-18-03 | 再検索ボタン | 地図が大きく移動したら「この地域を検索」ボタン表示 | 地図移動時 |
| BR-18-04 | VoiceOver対応 | VoiceOver有効時はリストビューをデフォルト表示 | アクセシビリティ |
| BR-18-05 | 保存記事フィルタ | savedフィルタ選択時はCore Dataから取得 | フィルタ選択時 |

### 計算ロジック

移動量判定:
```swift
func isDistanceSignificant(betweenRegion searchRegion: MKCoordinateRegion, andRegion visibleRegion: MKCoordinateRegion) -> Bool {
    // 中心点の移動距離と領域サイズの比率で判定
    let distance = ... // 2点間の距離計算
    return distance/searchRegionMinDimension > 0.33
}
```

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

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

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| 保存記事検索 | WMFArticle | SELECT | 位置情報を持つ保存記事を取得 |
| 記事情報更新 | WMFArticle | INSERT/UPDATE | 検索結果から記事情報を更新 |
| 検索履歴保存 | PlaceSearch履歴 | INSERT | 検索条件を履歴に保存 |

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

#### WMFArticle

| 操作 | 項目（カラム名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| SELECT | signedQuadKey | != NULL | 位置情報あり |
| SELECT | savedDate | != NULL | 保存済み |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| noResults | 検索エラー | 該当記事なし | 「Did you mean?」ボタン表示 |
| locationError | 位置情報エラー | 位置取得失敗 | エラーメッセージ表示 |

### リトライ仕様

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

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

検索結果の保存は一括で行う。

## パフォーマンス要件

- 地図表示は即座に行う
- 検索結果は2秒以内に表示
- ピンのアニメーションはスムーズに実行

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

- 位置情報はAPIに送信される（HTTPS）
- ユーザーの位置情報はローカルに保存しない

## 備考

MapKitのMKMapViewを拡張したMapViewクラスを使用。記事ピンはArticlePlaceクラスで表現される。

---

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

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

### 推奨読解順序

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

地図上のピンと検索のデータ構造を理解することが重要。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | PlaceSearch.swift | `Wikipedia/Code/PlaceSearch.swift` | 検索条件を表す構造体 |
| 1-2 | ArticlePlace | `Wikipedia/Code/` | 地図上のピンを表すMKAnnotation実装 |
| 1-3 | PlaceSearchResult | `Wikipedia/Code/PlaceSearchService.swift` | 検索結果を表す構造体 |

**読解のコツ**: PlaceSearchは検索条件（filter/type/region/string）を持つ。ArticlePlaceはMKAnnotationを実装し、1つまたは複数のWMFArticleを保持。

#### Step 2: メインViewControllerを理解する

PlacesViewControllerがPlaces機能の中心。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | PlacesViewController.swift | `Wikipedia/Code/PlacesViewController.swift` | Places画面のメインViewController |

**主要処理フロー**:
1. **116-188行目**: viewDidLoad - 地図、ロケーションマネージャ、UI初期化
2. **190-229行目**: viewWillAppear - 位置情報許可チェック、監視開始
3. **562-584行目**: currentSearch - 検索実行のエントリーポイント
4. **669-770行目**: performSearch - 検索処理の実装
5. **315-328行目**: selectVisibleArticle - ピン選択処理

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

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

**主要処理フロー**:
- **46-109行目**: performSearch - 検索実行、topとsavedで分岐
- **80-86行目**: savedフィルタ時のCore Data検索
- **87-108行目**: topフィルタ時のAPI検索
- **111-150行目**: fetchSavedArticles - 保存記事取得

#### Step 4: 位置情報管理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | LocationManager.swift | `WMF Framework/LocationManager.swift` | 位置情報管理 |

**主要処理フロー**:
- **56-78行目**: startMonitoringLocation - 位置監視開始
- **174-181行目**: locationManager(_:didUpdateLocations:) - 位置更新受信
- **209-218行目**: locationManagerDidChangeAuthorization - 許可状態変更

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

```
PlacesViewController
    │
    ├─ MapView (MKMapView拡張)
    │      └─ ArticlePlace (MKAnnotation)
    │             └─ ArticlePlaceView
    │
    ├─ LocationManager
    │      └─ CLLocationManager
    │             └─ 位置情報取得
    │
    ├─ PlaceSearchService
    │      ├─ WMFLocationSearchFetcher (top検索)
    │      └─ Core Data (saved検索)
    │
    └─ ArticlePopoverViewController
           └─ 記事プレビュー表示
```

### データフロー図

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

位置情報 ───▶ PlacesViewController ───▶ 地図表示
地図操作            │                    ピン表示
                    ├─▶ PlaceSearchService
                    │      ├─▶ Location API (top)
                    │      └─▶ Core Data (saved)
                    │
                    └─▶ updatePlaces()
                           └─▶ ArticlePlace作成
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| PlacesViewController.swift | `Wikipedia/Code/` | ソース | Places画面ViewController |
| PlaceSearch.swift | `Wikipedia/Code/` | ソース | 検索条件モデル |
| PlaceSearchService.swift | `Wikipedia/Code/` | ソース | 検索サービス |
| LocationManager.swift | `WMF Framework/` | ソース | 位置情報管理 |
| WMFLocationSearchFetcher | `WMF Framework/` | ソース | 位置検索API |
| ArticlePlace | `Wikipedia/Code/` | ソース | ピンアノテーション |
| ArticlePlaceView | `Wikipedia/Code/` | ソース | ピン表示ビュー |
| ArticlePopoverViewController | `Wikipedia/Code/` | ソース | 記事ポップオーバー |
| MapView | `Wikipedia/Code/` | ソース | カスタムMapView |
