コンテンツにスキップ
Documents for MorningStatusApp
Esc
移動開く⌘Jプレビュー
このページの内容

メンバー関係性マップ レーダーチャート画面設計書

最終更新: 2026-08-06 関連 Issue: #1198(関係性スコアリングマップ)/ #1268(関係性スコアリング設計書作成)/ #1271(レーダーチャート画面の新設)/ #1282(関係性ランキングモードの追加)/ #1290(ランキングモードのモバイル表示改善)/ #1308(歴史構造の時系列化)/ #11(時系列化設計書作成)


1. 画面概要

項目 内容
URL /member-map/radar
レンダリング方式 Client Side('use client'
ライブラリ 自作 SVG コンポーネント components/MemberMapRadarChart.tsx(既存 TimelineActivityChart 等と同様、外部チャートライブラリは追加しない)
表示モード 「比較グループ(手動)」(§2〜§8)/「関係性ランキング(自動)」(§9) の2モードをタブ切り替え
対象データ 比較グループモード: ユーザーが選択した任意のメンバー集合の関係性スコア平均値。ランキングモード: 選択した1名と他の全メンバーとのペアスコア

2. 表示目的

ユーザーが選んだメンバー同士の「関係性の形状」を比較できるようにする。選択したメンバー集合内の全ペアスコア(データ設計書 §8 参照)を4軸で平均し、レーダーチャートとして可視化する。

期(generation)単位の平均集計を採用しなかった理由

初版では期ごとに全メンバーを自動集計する方式を検討したが、以下の理由で軸の情報量が乏しくなるため不採用とした。

  • 師弟関係は世代を跨いだ関係が中心のため、同期内のペアではほぼ0ptになる
  • 出身地距離も、同期内でたまたま同じ都道府県・地方でない限り0ptになりやすい
  • 結果として「在籍重なり」以外の軸が潰れ、期ごとの形状の違いが見えにくい

そのため、ユーザーが世代を跨いで任意にメンバーを選べる方式とし、師弟関係・出身地距離の軸にも意味のある値が出せるようにした。


3. 軸定義(4軸固定)

対応するスコア 配点上限
在籍重なり 在籍期間重なりスコア 40pt
師弟関係 師弟関係スコア 30pt
カラー距離 メンバーカラー距離スコア 20pt
出身地距離 出身地距離スコア 10pt

各軸の配点上限が異なるため(40/30/20/10pt)、レーダーチャート上では各軸を 0〜100% に正規化して表示する(value / axisMax × 100)。上限の異なる軸をそのまま重ねると軸間の比較が誤読を招くため。


4. データ集計ロジック

  1. ユーザーが「比較グループ」を作成し、任意のメンバー(現役 + OG、ステータスによる絞り込みは行わない)を2名以上追加する
  2. グループ内の全メンバーペアを列挙し、各ペアについて4軸のスコアを算出する(データ設計書 §8 参照)
  3. 軸ごとにペア平均を算出する(sum(score) / pairCount
  4. メンバーが1名以下でペアが作れないグループは、レーダーチャート・凡例に表示しない
  5. 複数の比較グループを作成すると、グループごとに多角形を重ねて表示する(比較表示)

現役限定にしない理由

既存の相関図(/member-map)には「OGを含む」トグルがあるが、これは表示上の切り替えであり、member_relationships 自体は現役・OG を区別せず全メンバーの関係性を保持している(データ設計書 §2-1)。

レーダーチャートは任意のメンバー同士の関係性を比較する機能であり、現役・OGを問わず比較したいという利用目的を妨げないよう、相関図のような Active/OG 切り替えトグルは設けず、選択肢は常に全メンバーを対象とする。


5. 画面遷移

5-1. 遷移元

/member-map(メンバー関係性マップ画面)のヘッダーに「↗ レーダーチャートで見る」リンクを追加し、/member-map/radar へ遷移できるようにする(既存のタイムラインパネルの「↗ タイムラインで見る」リンクと同様のパターン。画面設計書 §9-1 参照)。

5-2. 復帰導線

レーダーチャート画面のヘッダーに「← メンバー関係性マップに戻る」リンクを設置し、/member-map に戻れるようにする。


6. 画面構成

┌───────────────────────────────────────────┐
│ ヘッダー(タイトル + タイトル右の説明文)    │
├───────────────────────────────────────────┤
│ 「比較するメンバーを選択してください         │
│   (グループごとに2名以上)」+ グループ追加  │
│ ┌─ グループ1 ─────────────────────────┐   │
│ │ [Aさん ×][Bさん ×]  [+ メンバーを追加▼] │  │
│ │                          [グループを削除]│  │
│ └───────────────────────────────────┘   │
│ ┌─ グループ2 ─────────────────────────┐   │
│ │ ...                                  │  │
│ └───────────────────────────────────┘   │
├───────────────────────────────────────────┤
│                                           │
│         レーダーチャート本体(SVG)         │
│         ・4軸: 在籍重なり/師弟/色/出身地    │
│         ・グループごとに色分けした多角形    │
│                                           │
├───────────────────────────────────────────┤
│ 「凡例」                                   │
│ 凡例(グループごとの色 + メンバー名一覧)    │
└───────────────────────────────────────────┘
  • タイトル「メンバー関係性レーダーチャート」の右に、画面の目的を説明するテキスト(「在籍重なり・師弟関係・カラー距離・出身地距離の4軸で、選択したメンバー同士の関係性を比較できます」)を表示する
  • 「比較するメンバーを選択してください(グループごとに2名以上)」という見出しと「+ グループを追加」ボタンを表示する。グループが1つもない場合はその旨の案内文を表示する
  • 各グループカードには以下を表示する
    • グループの識別色(凡例と共通のスウォッチ)
    • グループ名(「グループN」、Nは表示上の連番。グループ削除で繰り上がる)
    • 選択済みメンバーのチップ一覧(チップ内の「×」ボタンでそのメンバーをグループから外す)
    • メンバー追加用の <select>(既存 MemberSelect と同様、現役/OGのoptgroup構成。既にグループに含まれるメンバーは選択肢から除外する)
    • 「グループを削除」ボタン(グループごと削除)
  • メンバーの追加・削除・グループの追加・削除を行うと、レーダーチャート・凡例に即時反映する(API再フェッチは行わず、取得済みデータをクライアント側で再計算するのみ)
  • 凡例の上に見出し「凡例」を表示する。各グループの色とメンバー名一覧(「Aさん・Bさん」形式)を表示する
  • グループの識別色は固定パレット(GROUP_COLOR_PALETTE、8色)から表示順に割り当てる。9グループ目以降は先頭から色を再利用する

7. コンポーネント方針

既存の TimelineActivityChart 等と同様、外部チャートライブラリ(recharts 等)を追加せず、SVG の <polygon>(多角形)・<line>(軸線)・<text>(軸ラベル)を組み合わせた自作コンポーネント components/MemberMapRadarChart.tsx として実装する。このコンポーネントはグループ・期などのデータ種別に依存しない汎用的な props(軸ラベル・系列データ)で構成し、比較対象の単位(グループ/期など)を問わず再利用できる。

  • 4軸は上(在籍重なり)から時計回りに90度間隔で配置する(師弟関係→右、カラー距離→下、出身地距離→左)
  • 目盛りは4段階のグリッド多角形(25%/50%/75%/100%)を表示する
  • グループごとの多角形は塗り15%不透明度+輪郭線2pxで重ね描画し、複数グループを選択しても重なりが視認できるようにする
  • 配色は既存のダークテーマ(背景 #0f0f0f、テキスト白系半透明)を踏襲し、グリッド線・軸線は低コントラストの白系半透明線とする

8. ローディング・エラー状態

状態 表示内容
初期ロード中 「読み込み中…」
グループ未作成・ペアが作れないグループのみ レーダーチャートは多角形なしで表示(グリッド・軸のみ)。「+ グループを追加」からメンバーを選ぶよう案内文を表示
API 取得エラー エラーメッセージ

9. 関係性ランキングモード(自動・#1282)

9-1. 概要

比較グループモード(§2〜§8)とは別に、選択した1名のメンバーと関係性が高いメンバーを自動抽出する「関係性ランキング(自動)」モードを設ける。画面上部のタブで「比較グループ(手動)」/「関係性ランキング(自動)」を切り替える。

9-2. 集計ロジック

  1. ユーザーが基準メンバーを1名選択する(<select>、現役/OGのoptgroup構成。比較グループモードのメンバー追加セレクトと同様の構成)
  2. 基準メンバーと、それ以外の全メンバー(現役 + OG)とのペアスコア(§4のデータ設計と同じ4軸・getRelationshipScoretotal)を算出する
  3. 総合スコア(total)降順に標準競技順位方式で順位付けする(同率は同順位。同率が N 人いた場合、次の異なるスコアの順位は N 人分スキップする。例: 1位が2名タイの場合、次のスコアは3位になる)
  4. 順位が3以内(rank <= 3)のメンバーのみを表示対象とする(タイにより4人以上になることもある)

対象範囲は既存の比較グループモードと同様、現役・OGを区別せず全メンバーを対象とする(Active/OG切り替えトグルは設けない、§4「現役限定にしない理由」と同じ方針)。

9-2-1. 同率タイの表示順(#1291)

同一順位内で複数名がタイになった場合、以下の優先順位でタイブレークして並べる(memberId のアルファベット順は使わない)。

  1. グループの優先順位: 現役グループ → OGグループ の順
  2. 現役同士のタイブレーク: 基準メンバーの joinDate との差(絶対値)が小さい順
  3. OG同士のタイブレーク: gradDate が新しい(最近卒業した)順。卒業日が同一の場合は、基準メンバーの joinDate との差が小さい順で副次タイブレーク

「加入期が近い順」は期番号(generation)ではなく、加入日(joinDate)ベースで統一する(OG側の卒業日という日付ベースの基準と揃えるため)。

9-3. 画面構成

┌───────────────────────────────────────────┐
│ 「基準メンバーを選択してください」            │
│ [<select> 基準メンバー▼]                    │
├───────────────────────────────────────────┤
│  1位          │  2位          │  3位        │
│ ┌──────────┐ │ ┌──────────┐ │ ┌────────┐ │
│ │ 氏名      │ │ │ 氏名      │ │ │ 氏名    │ │
│ │ NNpt      │ │ │ NNpt      │ │ │ NNpt    │ │
│ │ レーダー   │ │ │ レーダー   │ │ │ レーダー │ │
│ └──────────┘ │ └──────────┘ │ └────────┘ │
│ (同率タイの場合は同じ列に縦に複数枚並ぶ)      │
└───────────────────────────────────────────┘
  • グリッドは横3列(1位・2位・3位)。各列の見出しに「N位」を表示する
  • 同率タイで同じ順位に複数名いる場合、その列内に縦に複数のカードを積む
  • 各カードには、対象メンバーの氏名・総合スコア(NNpt)・4軸レーダーチャート(基準メンバーとのペアスコアを正規化した単一系列)を表示する
  • レーダーチャートの色は getScoreColor(総合スコアに応じてグレー〜琥珀のグラデーション)を用いる。比較グループモードのグループ識別色パレットとは異なる配色軸のため区別する
  • 基準メンバー未選択時は案内文(「メンバーを選択すると、関係性が高い上位3人を表示します。」)を表示する

9-4. モバイル表示(#1290)

3列固定グリッドは最小幅240px×3列=720pxを要求するため、モバイル幅では2位・3位の列が画面外にはみ出し、かつ本画面はページ全体で横スクロールを許可していない(overflow-x: hidden)ため閲覧できなくなる。

  • @media (min-width: 768px) 未満(モバイル幅)ではグリッドを1列縦積みにする(既存の app/timeline/page.module.css と同様、PC幅でのみ複数列グリッドに切り替えるmobile-firstパターンに合わせる)
  • @media (min-width: 768px) 以上では現状通り3列グリッドを維持する
  • 縦積み時も「1位→2位→3位」の順で上から表示する。順位ごとの見出し(「N位」)は1つのみ表示し、同率タイのカードはその見出しの下に複数枚並べる(列の概念自体は変えず、単に画面幅に応じて列数を切り替えるのみ)

10. 時系列表示(#1308)

10-1. 概要

比較グループモード(§2〜§8)・関係性ランキングモード(§9)の両方に対し、メンバーの加入・卒業に伴うスコア変化をアニメーションで可視化する機能を追加する。§2〜9で定義した静的な表示に、時間軸操作(キーフレーム送り・自動再生)を追加する形で提供し、既存の静的表示自体は変更しない。

年表/タイムラインとの連携は本設計の対象外とする(詳細は§10-7)。

10-2. キーフレーム算出ロジック(共通)

  • 対象(比較グループモードではグループ内の各メンバー、ランキングモードでは基準メンバー1名)の加入日・卒業日をキーフレームの基点とする
  • 直近のキーフレームから半年以上間隔が空く場合、その間を半年おきの定期チェックポイントで補完する
  • 4軸(在籍重なり・師弟関係・カラー距離・出身地距離)のうち、時間で変化するのは「在籍重なり」のみ(既存の getOverlapScorenow 引数を持つ設計のため)。他3軸は時間不変のまま常時表示する(4軸構成自体の見直しは行わない)

このロジックは比較グループモード・ランキングモード共通とし、#1310(計算ロジックの実装)で具体化する。

10-3. アニメーション・UI操作

  • キーフレーム間の値はなめらかに線形補間する
  • 画面下部に時系列操作バーを新設し、以下の操作方法を両方提供する
    • 手動スライダー: つまみをドラッグして任意のキーフレーム位置へ直接移動できる
    • 自動再生(Playボタン): 押下すると現在位置から最終キーフレームまで一定間隔で自動的に進行する。再生中は一時停止ボタンに切り替わる
  • スライダー上には算出済みキーフレームの位置をマーカーとして表示する
  • 現在のキーフレーム日付(例:「2015年4月」)を操作バー付近に表示する
┌───────────────────────────────────────────┐
│  (既存のレーダーチャート・凡例。§6/§9-3)    │
├───────────────────────────────────────────┤
│ 現在位置: 2015年4月                         │
│ [▶再生] ●━━━━━━━◯───────────────           │
│         ↑キーフレームマーカー                │
└───────────────────────────────────────────┘

この操作バーは#1311(アニメーションUIの実装)で具体化する。

10-4. 比較グループモード固有仕様

10-4-1. グループ種別の再編

既存の「手動選択によるグループ作成」に加え、以下2種類の自動生成グループ種別を新設する。既存の手動選択グループは、時系列表示の主眼ではないオプション(補助的な使い方)に位置づけを変更する。

グループ種別 生成方法 メンバー構成
期グループ 自動(期ごとに1グループ) 同一期のメンバーで固定(時間に依存せず不変)
グループ全体 自動(現役・OGを問わず全メンバーを対象) キーフレーム日付ごとに動的算出(加入日 ≤ キーフレーム日 < 卒業日 を満たすメンバーを再計算)
手動選択グループ ユーザー操作(既存同様、オプション) ユーザー選択のまま固定

「グループ全体」は「モーニング娘。が年々どう変化してきたか」を検証する趣旨のため、今日時点の固定ロスター(現役メンバーのみ)ではなく、現役・OGを問わず、各キーフレーム時点で実際に在籍していたメンバーに動的に変える。期グループ・手動選択グループはメンバー構成自体は固定だが、「在籍重なり」軸は時間で変化するため、キーフレーム送りによる形状変化は起こりうる。

10-4-2. 初期表示・表示開始キーフレーム

グループ種別 初期表示 表示開始キーフレーム
(未選択) グループ未選択(案内文を表示。§8と同様) -
期グループ選択時 そのグループの最初のキーフレームから表示 グループ内メンバーの最も早い加入日
グループ全体選択時 グループ結成年のキーフレームから表示 1997年(モーニング娘。結成年)
手動選択グループ選択時 そのグループの最初のキーフレームから表示 グループ内メンバーの最も早い加入日

10-4-3. 卒業後のスコア凍結表現

追加ロジックは不要。両者が卒業済みのペアは now を進めても overlapEnd が過去の固定値になるため、既存関数(getOverlapScore)の性質として自然に凍結する(期グループ・手動選択グループ共通)。

10-5. 関係性ランキングモード固有仕様

  • キーフレーム基点は基準メンバー本人の加入日・卒業日(§10-2の「対象」を基準メンバー1名とする)
  • 時系列の表示開始点は基準メンバーの加入日以降のみとする(加入前は表示しない)
  • 上位ランクインメンバー自体の入れ替わり:
    • ランク外に出るメンバー: フェードアウト
    • 新規ランクインするメンバー: フェードイン(浮かび上がる演出)
  • 同一メンバー内の順位変動: 順位が変わったメンバーの氏名テキストを強調表示する(§9-3のカード内氏名表示に適用)

10-6. 終端日設定機能

10-6-1. 概要

§10-2のキーフレーム算出における配列の終端日(keyframeEndDate)を、コード改修なしに更新できるようにする設定機能。メンバーの卒業“時期”が発表されても正確な卒業日が確定するまでの期間、最新の状況を反映した終端日を暫定的に設定できるようにする。

10-6-2. データ

Vercel Blobファイル radar-chart-timeline-config.json を新設する。

{
  "keyframeEndDate": "2026-12-31"
}

キーフレーム算出関数(#1310)は、対象内の最後の既知イベント日(加入日・卒業日)と keyframeEndDate のうち遅い方をキーフレーム配列の終端とする。

10-6-3. 画面

デスクトップモード限定(DESKTOP_MODE=1 時のみ)で、レーダーチャート画面(比較グループ・ランキング両モード共通)に設定アイコンを表示する。

┌───────────────────────────────────────────┐
│  (既存のレーダーチャート・凡例。§6/§9-3)    │  ⚙
├───────────────────────────────────────────┤
│ 現在位置: 2015年4月                         │
│ [▶再生] ●━━━━━━━◯───────────────           │
└───────────────────────────────────────────┘

設定アイコンをクリックすると、keyframeEndDate を変更するダイアログを表示する。日付入力は <input type="date"> を用いる(本画面の期セレクタ等と同様、新規ライブラリは導入しない)。

保存すると keyframeEndDate がBlobに書き込まれ、画面はこの値を使ってキーフレームを再計算する。

10-6-4. 警告表示

現在日時が keyframeEndDate を過ぎている場合、デスクトップモード限定で警告を表示する(例:「終端日が過去の日付になっています。設定を更新してください」)。一般ユーザーには表示しない(設定変更の権限がなく対処できないため)。

10-6-5. API

GET /api/radar-chart-timeline-config

keyframeEndDate を取得する。公開(本画面の表示処理・#1310のロジック双方から参照する)。

  • 実装: app/api/radar-chart-timeline-config/route.ts
  • レスポンス: { "keyframeEndDate": "2026-12-31" }
  • 未設定時: Blobファイル未作成(初回アクセス)の場合は { "keyframeEndDate": null } を200で返す(設定UI・#1310のロジックともに、null は「終端日未設定」として扱う)
  • エラー: 500 → { "error": "終端日設定の取得に失敗しました。" }
PUT /api/radar-chart-timeline-config

keyframeEndDate を更新する。デスクトップモード限定(app/link-editorrequireDesktopMode() パターンを踏襲し、DESKTOP_MODE=1 でない場合は403を返す)。

  • 実装: app/api/radar-chart-timeline-config/route.ts
  • リクエストボディ: { "keyframeEndDate": "2026-12-31" }
  • レスポンス: { "keyframeEndDate": "2026-12-31" }
  • エラー: 400 → { "error": "keyframeEndDate は YYYY-MM-DD 形式で指定してください。" }(不正な日付形式の場合)
  • エラー: 403 → { "error": "この操作はデスクトップモードでのみ使用できます。" }DESKTOP_MODE=1 でない場合)
  • エラー: 500 → { "error": "終端日設定の保存に失敗しました。" }

10-7. スコープ外(別Issueで対応)

年表/タイムラインとの連携(比較グループモード: #1312、ランキングモード: #1316)は本設計の対象外とし、保留とする。


改訂履歴(バージョン降順に記載)

バージョン 日付 変更内容
2.7 2026-08-06 §10-6-5 GET APIに「未設定時」の仕様を追加。Blobファイル未作成(初回アクセス)の場合は { "keyframeEndDate": null } を200で返す実装(#1477実装時に判明した、設計書に明記のなかったエッジケース)
2.6 2026-08-06 §10-6「終端日設定機能」を追加。キーフレーム配列の終端日(keyframeEndDate)を、Vercel Blobファイル・デスクトップモード限定の設定UIからコード改修なしに更新できる仕組みを新設。「スコープ外」節を§10-7へ移動し、in-scope機能を先に配置する構成に整理。API仕様にエンドポイントパス・実装ファイルパス・リクエスト/レスポンス例・エラーケースを明記(#34)
2.5 2026-08-05 PR #33 レビュー指摘を反映。§10-4「グループ全体」の候補メンバーを「現役・OGを問わず全メンバー」に修正(説明文の「実際に在籍していたメンバー」と矛盾していたOG除外の記載を削除)。§10-1 の年表/タイムライン連携に関する記述を §10-6 への参照に統一し重複を解消。§10-4 配下の見出しに節番号を付与
2.4 2026-08-05 §10「時系列表示」を追加。比較グループ・ランキング両モードに、加入・卒業に伴うスコア変化をキーフレームアニメーション(手動スライダー+自動再生の両対応)で可視化する仕様を追記。比較グループモードのグループ種別を「期グループ」「グループ全体」「手動選択グループ」の3種に再編(#1308、#11)
2.3 2026-07-09 §9-2-1「同率タイの表示順」を追加。memberId アルファベット順だったタイブレークを、グループ優先順位(現役→OG)と加入日・卒業日ベースの順序に変更(#1291)
2.2 2026-07-07 §9-4「モバイル表示」を追加。ランキングモードの3列グリッドがモバイル幅で閲覧不能になる問題を修正するため、768px未満は1列縦積みにするmobile-firstのブレークポイントを新設(#1290)
2.1 2026-07-07 §9「関係性ランキングモード(自動)」を追加。選択した1名と関係性が高い上位3人(同率タイ含む)を標準競技順位方式で自動抽出するモードを新設(#1282)
2.0 2026-07-05 期(generation)単位の自動平均集計から、ユーザーが任意のメンバーを選択して比較グループを作る方式に変更。師弟関係・出身地距離の軸が同期内でほぼ0ptになり情報量が乏しくなるため(#1271)
1.2 2026-07-05 §6 に、期セレクタ・凡例の見分けがつきにくいというレビュー指摘を受けた見出しテキスト(タイトル右の説明文・「表示する期を選択してください」・「凡例」)を追記(#1271)
1.1 2026-07-05 §1 URL・§7 コンポーネント方針(配色・軸配置等)を実装確定内容に更新。§6 に期セレクタのインタラクション詳細を追記し、§9「フェーズ・実装予定」を削除(#1271)
1.0 2026-07-04 初版作成(#1268)

このページは役に立ちましたか?