| name | area-coding |
| description | area-manager,area-displayなどの、エリアに関する機能のcodingをするためのスキル.このskillにknowledgeがある.これを読んでからareaに関するcodingをすること.trigger:area-manager,area-display,エリア |
Area Feature Knowledge
Overview
エリア機能は、マップ上にユーザ定義のポリゴン領域を描画・管理する機能。
content側の AreaManager クラスがストレージ・状態管理・UIダイアログを担当し、
inject側の area-display.ts がマップ上のSVGオーバーレイ描画・頂点編集を担当する。
!!!codeの内容を変更/構造を変えた場合、このドキュメントも更新すること!!!
Architecture
Context Boundary
| 役割 | Context | ファイル |
|---|
| 状態管理・永続化・UIダイアログ | content | src/features/area-manager/area-manager.ts |
| 表示設定ダイアログ | content | src/features/area-manager/modules/display-settings/dialog.ts |
| グループ合体ダイアログ | content | src/features/area-manager/modules/group/dialog.ts |
| インポート/エクスポートダイアログ | content | src/features/area-manager/modules/import-export/dialog.ts |
| インポート/エクスポート処理 | content | src/features/area-manager/modules/import-export/usecase.ts |
| GeoJSON変換ユーティリティ | content | src/features/area-manager/modules/import-export/utils.ts |
| リセット処理 | content | src/features/area-manager/modules/reset/usecase.ts |
| Content↔Inject通信ゲートウェイ | content | src/features/area-manager/modules/inject-gateway.ts |
| Areaメッセージ定数 | shared | src/constants/area-message.ts |
| Area共通ユーティリティ | shared | src/utils/area-region.ts |
| マップ上SVG描画・頂点編集 | inject | src/inject/features/area-display.ts |
| メッセージルーティング | inject | src/inject/bridge.ts |
Types
src/types/area-region.ts — 共有型定義
AreaRegion: id, name, color, vertices, visible, createdAt, updatedAt
AreaRegionVertex: { lng, lat }
AreaRegionBounds: { west, south, east, north }
AreaRegionEditSnapshot: { regionId, name, vertices }
AreaNameDisplayMode: "always" | "off"
AreaNameStyleMode: "halo" | "color-badge"
AreaDisplayOptions: { fillOpacityPercent, nameDisplayMode, nameFontSizePx, nameStyleMode }
src/features/area-manager/types.ts — content側専用
AreaRegionGroup: { id, name, regionIds, createdAt, updatedAt }
Content↔Inject Messaging Protocol
すべて window.postMessage による通信。source フィールドで識別。
Content → Inject (一方向)
| source | 用途 | payload |
|---|
mr-wplace-area-measure-update | エリア表示ON/OFF | { visible: boolean } |
mr-wplace-area-regions-sync | 全リージョンの同期 | { regions: AreaRegion[] } |
mr-wplace-area-display-options-update | 表示オプション変更 | { options: AreaDisplayOptions } |
mr-wplace-area-region-edit-start | 編集モード開始 | { regionId, name, color, vertices, saveLabel, cancelLabel } |
mr-wplace-area-region-edit-stop | 編集モード終了 | (なし) |
mr-wplace-area-region-goto | エリアへ移動 | { regionId, lng, lat, bounds? } |
Content → Inject (リクエスト/レスポンス)
| source (req) | source (res) | 用途 |
|---|
mr-wplace-area-region-edit-request | mr-wplace-area-region-edit-response | 編集中のスナップショット取得 |
Inject → Content (一方向)
| source | 用途 | トリガ |
|---|
mr-wplace-area-region-save-click | 保存ボタン押下 | SVGオーバーレイ上のSaveボタン |
mr-wplace-area-region-cancel-click | キャンセルボタン押下 | SVGオーバーレイ上のCancelボタン |
Storage Keys (chrome.storage)
| Key | Type | Default | 説明 |
|---|
mapFilter_areaMeasure | boolean | false | エリア表示ON/OFF |
areaRegions_v1 | AreaRegion[] | [] | 全リージョンデータ |
areaRegionGroups_v1 | AreaRegionGroup[] | [] | グループデータ |
mapFilter_areaSyncUrl | string | "" | オンライン同期URL |
mapFilter_areaFillOpacityPercent | number | 14 | 塗り透明度(0-100) |
mapFilter_areaNameDisplayMode | AreaNameDisplayMode | "always" | 名前表示モード |
mapFilter_areaNameFontSizePx | number | 14 | 名前文字サイズ(px) |
mapFilter_areaNameStyleMode | AreaNameStyleMode | "halo" | 名前の見え方 |
Content側: AreaManager クラス
初期化フロー
init() でストレージから全設定をロード
mapReady 判定 → ready なら syncMapDependentState() で inject に通知
bindAreaManagerMessageHandlers() で save/cancel クリックと map-ready を監視
主要メソッド
setAreaMeasureEnabled(enabled) — ON/OFF切替、ストレージ保存、inject通知
openAreaManager() — モーダルを生成・表示
startAreaEditing(regionId, closeModal) — 編集モード開始、inject に edit-start 送信
stopAreaEditing(skipRender) — 編集モード終了、inject に edit-stop 送信
saveAreaEditing() — inject に snapshot リクエスト → 応答を受けて保存
renderAreaManager() — モーダル内のリージョン/グループ一覧をDOM生成
resetAllAreas() — 全エリアを確認ダイアログ付きでリセット (表示設定ダイアログ・popup経由)
データフロー (保存時の例)
- Save ボタン click → inject が
mr-wplace-area-region-save-click を postMessage
- content の
saveAreaEditing() が起動
requestAreaEditSnapshot() で inject に edit-request 送信
- inject が
respondAreaRegionEditRequest() で edit-response 返送
- content が vertices を受取り、storage に保存、
notifyAreaRegions() で inject に regions 同期
グループ機能
- 複数リージョンを1つのグループにまとめる
- グループ内リージョンは同名・同色に統一
- グループ解除は regionIds を解除するだけ (リージョン自体は残る)
cleanupAreaRegionGroups() で不整合なグループを自動除去
インポート/エクスポート
- GeoJSON (FeatureCollection) 形式で入出力
mrWplaceAreaGroups カスタムフィールドでグループも保存
- merge: 既存とIDベースでマージ / replace: 全置換
- URL同期: fetchしたJSONをインポート
- ファイルインポート時は事前確認ダイアログでファイルサイズを表示し、merge/replace を選択してから読み込む
- GeoJSON のエリア名は
properties.name がデフォルトだが、インポート時に任意のプロパティキー(例: N03_004)を指定して名寄せ可能
Inject側: area-display.ts
描画構造 (DOM + MapLibre Layer)
div#mr-wplace-area-measure (container, pointer-events: none)
├── svg (viewBox=マップサイズ)
│ └── polygon (editPolygon: 互換用途, 通常は非表示)
├── div (edgeHitLayer: エッジクリック用透明hit領域)
├── div (areaLabel: 編集中の面積表示)
└── div (editActionLayer: Save/Cancelボタン)
├── button (Save)
└── button (Cancel)
MapLibre style layer:
mr-wplace-area-regions-source (GeoJSON)
mr-wplace-area-regions-fill (fill)
mr-wplace-area-regions-line (line)
mr-wplace-area-regions-label (symbol)
mr-wplace-area-edit-source (GeoJSON)
mr-wplace-area-edit-fill (fill)
mr-wplace-area-edit-line (line)
描画フロー
addAreaOverlay(map) — コンテナ生成、マップイベント(move/zoom/rotate/pitch/resize)にバインド
scheduleAreaOverlayRender(map) — 描画要求を requestAnimationFrame で1フレームに集約
renderAreaOverlayNow(map) — 実描画の中心関数
- SVG viewBox をマップコンテナサイズに合わせる
- 確定リージョン:
renderRegionLayer() で MapLibre source/layer を更新
- 編集モード:
renderEditingOverlay() で edit layer + 頂点UIを更新
頂点編集
- 頂点ドラッグ:
startVertexDrag() → pointerMove でリアルタイム更新 → stopVertexDrag()
- 頂点追加: エッジ(辺)をクリック →
insertVertexOnEdge() で中点挿入
- 頂点削除: 頂点をダブルクリック (3頂点以下にはならない)
- ドラッグ中はマップの
dragPan を無効化
Export されている関数
setAreaMeasureEnabled(enabled) — オーバーレイの表示/非表示
setAreaRegions(regions) — 確定リージョン一覧を更新、再描画
setAreaDisplayOptions(options) — 透明度・名前表示モード・名前文字サイズ・名前見え方変更、再描画
startAreaRegionEdit(payload) — 編集モード開始
stopAreaRegionEdit() — 編集モード終了
respondAreaRegionEditRequest(data) — 編集スナップショット応答
setupAreaMeasureOnMapReady(mapInstance) — マップ ready 時のセットアップ
面積計算
calculateGeodesicAreaSquareMeters(vertices) — 測地線面積 (m²)
calculatePixelAreaSquare(vertices) — ピクセル面積 (px²)
- 両方とも
@/utils/coordinate からインポート
Coding Notes
新しいエリア設定を追加する場合
src/types/area-region.ts に型追加 (必要に応じて)
AreaManager に storage key、デフォルト値、normalize メソッド追加
init() の storage.get() に key 追加
notifyAreaDisplayOptions() (または個別メッセージ) で inject に通知
area-display.ts 側で受信・適用・再描画
bridge.ts のルーティングは既存パターンに従う
注意点
- inject 側は
chrome.storage を使えない → content が storage 管理
- inject 側のモジュール変数 (let) で状態を保持 (クラスではない)
scheduleAreaOverlayRender() は高頻度イベントの間引き目的。重い処理は renderAreaOverlayNow() に集約
- 確定リージョン名ラベルは
symbol layer で表示専用 (クリック移動なし)
- エリア名ラベルは
always/off で表示切替し、文字サイズは設定値(px)をそのまま適用する
- エリア名ラベルの見え方は
halo(従来)と color-badge(エリア色の丸背景)を切替可能
- 頂点ドラッグ中は
renderEditingOverlay() を優先し、確定リージョン再生成を避ける
- 色は
#rrggbb 6桁hex のみ対応 (normalizeAreaColor で検証)
- リージョンは
updatedAt 降順でソート
- Content↔Inject の area message source は
src/constants/area-message.ts を使って定義を一元化する
- AreaManager から直接
window.postMessage を呼ぶより modules/inject-gateway.ts 経由を優先する
Refactor Worklog (Handover)
2026-02-18 Phase 1 (完了)
- 目的: 重複ロジックとメッセージ文字列散在を削減
- 実施:
src/constants/area-message.ts を追加して area message source を一元化
src/utils/area-region.ts を追加して color/name-mode/bounds/pixel-area を共通化
area-manager.ts, area-display.ts, bridge.ts に適用
- 結果:
- 文字列タイポリスク低減
- content/inject 間の仕様変更追従コストを削減
2026-02-18 Phase 2 (完了)
- 目的:
AreaManager の責務から通信境界を分離
- 実施:
src/features/area-manager/modules/inject-gateway.ts を追加
- 以下を
AreaManager から gateway へ移動:
- map-ready/save/cancel の message listener バインド
- measure/regions/display-options/goto/edit-start/edit-stop の postMessage 送信
- edit snapshot request/response (timeout付き)
AreaManager 側は gateway 呼び出しに置換
- 結果:
AreaManager の通信詳細依存を縮小
- 通信仕様変更時の変更点を gateway に集約可能
2026-02-18 Phase 3 (完了)
- 目的: inject 側オーバーレイ再描画の負荷を削減
- 実施:
area-display.ts に scheduleAreaOverlayRender() を追加し、描画を requestAnimationFrame に集約
renderAreaOverlayNow() / renderRegionLayer() / renderEditingOverlay() に責務分割
- 頂点ドラッグ時は編集レイヤーのみ更新し、確定リージョンの再生成を回避
- map container 解決結果をキャッシュし、毎回DOM探索を避ける
- 結果:
- move/zoom/drag連打時の無駄な再描画回数を削減
- エディット時の体感応答を改善しやすい構造へ整理
2026-02-18 Phase 4 (完了)
- 目的:
renderRegionLayer() の全DOM再生成を削減
- 実施:
- リージョン polygon/label をIDベースで再利用するキャッシュを導入
renderRegionLayer() を差分更新化し、不要ノードのみ削除する方式に変更
- label click は要素使い回し前提で dataset + 現在state参照へ切替
- 結果:
- map操作時のDOM作成/破棄回数をさらに削減
- ラベル再描画時のイベント再登録コストを回避
2026-02-18 Phase 5 (完了)
- 目的: 3D pitch時の頂点投影破綻を回避
- 実施:
- 確定リージョン描画を SVG から MapLibre
fill/line/symbol layer に移行
- 編集中ポリゴンも MapLibre
fill/line layer へ移行
- 既存DOMは頂点ハンドル・エッジhit・面積ラベル・Save/CancelのUI専用に縮小
- disable時に area source/layer を確実に remove する後始末を追加
- 結果:
- 3Dで地平線付近に入った頂点をMapエンジン側のクリップ/投影に委譲可能
map.project() 直描画由来の「上に吹き飛ぶ」症状の根本要因を除去
2026-02-19 Phase 6 (完了)
- 目的: エリアリセット機能の追加とDanger Zone UIの統合
- 実施:
src/features/area-manager/modules/reset/usecase.ts を追加し、再利用可能なリセットロジックを実装
- 表示設定ダイアログに赤いリセットボタンを追加
- popup.html に Danger Zone セクションを追加し、ギャラリーリセットとエリアリセットを統合
resetAllAreas() は常に確認ダイアログを表示する統一実装
- popup側では確認を行わず、
resetAllAreas() を呼び出すのみ
- i18n キーを追加:
danger_zone, danger_zone_show/hide, reset_areas, confirm_reset_areas, areas_reset_success
- 結果:
- エリアリセット機能が複数の場所から呼び出し可能に
- popup の Danger Zone で危険な操作を明示的に分離
- 確認ダイアログは常に
resetAllAreas() 内で一元管理
2026-02-19 Phase 7 (完了)
- 目的: map操作時の不要な
setData 連打を抑止して表示負荷を削減
- 実施:
area-display.ts に region/edit 用の dirty flag を追加
renderRegionLayer() / renderEditingOverlay() 内の GeoJSON source 更新を dirty 時のみ実行
- 通常表示時 (
editMode=false) は map update event で再描画を走らせないよう変更
styledata 発火時は dirty を立てて source/layer 再同期を強制
- 頂点ドラッグ/追加/削除時のみ edit source を dirty にするよう更新
- 結果:
- pan/zoom/rotate/pitch 中の全リージョン再生成・
setData 更新を回避
- 編集時も頂点未変更フレームでは edit source 更新を回避
- 体感上の引っかかりやCPU使用率悪化を抑えやすい構造に改善
2026-02-19 Phase 8 (完了)
- 目的: 大量リージョン同期時の起動直後フリーズを軽減 (best-effort 読み込み)
- 実施:
setAreaRegions() の同期 map/filter 処理を廃止し、requestAnimationFrame ベースのチャンク同期へ変更
- 1チャンクあたりの処理件数/時間予算を設定し、メインスレッド占有を抑制
- 一定件数ごとにのみ
setData 更新をコミットし、更新回数を制限
- best-effort 上限 (
AREA_REGION_BEST_EFFORT_MAX) を導入し、過剰件数は描画対象から除外
- 新規同期開始時に進行中ジョブをキャンセルする仕組みを追加
- 結果:
- 何千件規模のリージョンでも一括処理を避け、UI操作不能時間を短縮しやすくなった
- 読み込み途中でも段階的に表示されるため、初期表示の体感が改善
- 最悪ケースでは一部リージョンを省略しても操作継続を優先できる
Review Result
- 実行確認:
npm run build 成功
- 挙動回帰リスク(低):
- listener は
init() 再実行時に重複登録されないようガード済み
- 既存 message source と payload 形は維持
- 未対応/次フェーズ候補:
renderAreaManager() の巨大化 (UI構築責務分割)
- polygon points 文字列生成コストのさらなる削減 (頂点数が多い場合の最適化)
- map-ready source (
mr-wplace-map-instance-captured) も将来的に定数化候補
- 編集オーバーレイの edge hit 要素再生成を差分更新化 (大量頂点時)
- content→inject の postMessage 自体も chunk 送信化して structured clone 負荷を分散
Mermaid Diagram
graph TB
subgraph Content["Content Context"]
AM["AreaManager<br/>(area-manager.ts)"]
Storage["chrome.storage<br/>areaMeasure, areaRegions_v1,<br/>areaRegionGroups_v1, display options"]
DSD["DisplaySettings<br/>Dialog"]
GCD["GroupCompose<br/>Dialog"]
IED["ImportExport<br/>Dialog"]
UC["import-export/<br/>usecase.ts"]
UTIL["import-export/<br/>utils.ts"]
GW["inject-gateway.ts<br/>(message gateway)"]
AM -->|read/write| Storage
AM -->|open| DSD
AM -->|open| GCD
AM -->|open| IED
AM -->|delegate| GW
IED -->|delegate| UC
UC -->|GeoJSON| UTIL
end
subgraph Inject["Inject Context (page window)"]
Bridge["bridge.ts<br/>(message router)"]
AD["area-display.ts<br/>(SVG overlay)"]
MI["map-instance/<br/>(flyTo, goto)"]
Map["maplibregl Map"]
Bridge -->|dispatch| AD
Bridge -->|dispatch| MI
AD -->|project/unproject| Map
AD -->|on move/zoom| Map
MI -->|flyTo/fitBounds| Map
end
subgraph Types["Shared Types"]
AT["area-region.ts<br/>AreaRegion, AreaRegionVertex,<br/>AreaDisplayOptions, etc."]
AGT["area-manager/types.ts<br/>AreaRegionGroup"]
end
GW -- "postMessage<br/>area-measure-update<br/>area-regions-sync<br/>area-display-options-update<br/>area-region-edit-start/stop<br/>area-region-goto" --> Bridge
AD -- "postMessage<br/>area-region-save-click<br/>area-region-cancel-click<br/>area-region-edit-response" --> AM
AM -.->|uses| AT
AM -.->|uses| AGT
AD -.->|uses| AT
style Content fill:#1a1a2e,color:#fff
style Inject fill:#16213e,color:#fff
style Types fill:#0f3460,color:#fff
sequenceDiagram
participant User
participant AM as AreaManager<br/>(content)
participant GW as inject-gateway.ts<br/>(content)
participant Bridge as bridge.ts<br/>(inject)
participant AD as area-display.ts<br/>(inject)
participant Map as maplibregl
Note over AM: init() → storage.get → syncMapDependentState
User->>AM: Open Area Manager
AM->>AM: renderAreaManager() (DOM)
User->>AM: Click "Add" or "Edit"
AM->>GW: startAreaRegionEdit(...)
GW->>Bridge: postMessage(edit-start, {regionId, vertices, color})
Bridge->>AD: startAreaRegionEdit(payload)
AD->>Map: project vertices → screen coords
AD->>AD: render SVG polygon + vertex handles
User->>AD: Drag vertex / Click edge
AD->>Map: unproject → update lngLat
AD->>AD: renderEditingOverlay() (drag時)
User->>AD: Click Save button
AD->>AM: postMessage(save-click)
AM->>GW: requestAreaEditSnapshot()
GW->>Bridge: postMessage(edit-request, {requestId})
Bridge->>AD: respondAreaRegionEditRequest()
AD->>AM: postMessage(edit-response, {snapshot})
AM->>AM: persist to storage
AM->>Bridge: postMessage(regions-sync)
Bridge->>AD: setAreaRegions() → scheduleAreaOverlayRender()