mhlw-ec-pharmacy-finder 機能追加指示書
概要
緊急避妊薬の薬局検索サイト https://odakin.github.io/mhlw-ec-pharmacy-finder/ に以下の機能を追加する。 競合サイト(EPARKくすりの窓口 https://www.kusurinomadoguchi.com/s?is_ecp=1 )との差分を埋めつつ、既存の強み(厚労省公式データ11,000件超、女性薬剤師フィルター、事前連絡不要フィルター等)は維持する。
制約: - GitHub Pages での静的ホスティング。サーバーサイド処理なし - 外部APIキーが必要なサービス(Google Maps Platform等)は避け、無料・APIキー不要のものを優先 - 既存のUI/UXを壊さない。既存機能(都道府県選択、フリーワード検索、チェックボックスフィルター)はそのまま残す
機能4: Google Mapリンク(各薬局への外部導線)✅ 実装済み
- 各薬局カードに「📍 Google Mapで見る」リンクを追加済み(コミット d83b795)
- リンク形式:
https://www.google.com/maps/search/?api=1&query={encodeURIComponent(薬局名 + ' ' + 住所)} - APIキー不要。クリックで写真・口コミ・ストリートビュー・ナビが確認できる
機能1: 地図表示 ✅ 実装済み(コミット 514ad56, 3c3c685)
要件
- 検索結果の薬局を地図上にピン表示する
- ピンをクリック/タップで薬局名・住所・電話番号等のポップアップを表示
- ポップアップ内に「Google Mapで見る」リンクを含める
- 検索結果のリスト表示と地図表示を切り替え可能にする(タブまたはトグル)
- モバイルでも使いやすいレスポンシブ対応
技術方針
- 地図エンジン: Leaflet.js + OpenStreetMap タイル(無料、APIキー不要)
- ジオコーディング: 東大CSIS 簡易ジオコーディング実験
- エンドポイント:
https://geocode.csis.u-tokyo.ac.jp/cgi-bin/simple_geocode.cgi?charset=UTF8&addr={住所} - 無料、APIキー不要、ユーザー登録不要
- 番地・号レベルの高精度(iLvl 5〜8)
- 検証済み: 10件中9件で番地レベル以上の精度を確認。Nominatim は日本語住所で全滅だったため不採用
- キャッシュ設計:
data/geocode_cache.json(append-only) - 構造:
{ "66": {"lat": 43.762955, "lng": 142.359726, "lvl": 8, "addr": "元住所"}, ... } - id は厚労省の「薬局等番号」で安定
- 削除された薬局のデータも保持(再登録時に再利用可能)
- 新規/住所変更分のみ CSIS に問い合わせ。CSIS 障害時はスキップ(GitHub Actions の日次実行で自動リトライ)
- 差分更新: 0.5秒間隔。初回10,128件 ≒ 約85分。以降の定期更新は新規数十件 ≒ 数秒
- フロントエンド配信: ビルド時に
docs/geocode_cache.jsonにコピー。地図タブ表示時のみ fetch(軽さ維持) - クラスタリング: Leaflet.markercluster(CDN読み込み)
- 検索結果が200件超の場合はガイド表示。ただし表示はブロックしない
- 初期表示(検索前)では地図は非表示
機能2: 現在地からの距離・近い順ソート ✅ 実装済み(コミット 3c3c685)
要件
- 「現在地から近い順」ソートボタンを追加
- ボタン押下時にブラウザの Geolocation API で現在地を取得し、各薬局との距離を計算してソート
- 各薬局の行に「約○○km」の距離表示を追加
- 位置情報の取得に失敗した場合(拒否/非対応)は適切なエラーメッセージを表示し、既存のソート(表示順)にフォールバック
- 地図表示モードでは、現在地もピン(色を変える)で表示する
技術方針
navigator.geolocation.getCurrentPosition()を使用- 距離計算は Haversine 公式(直線距離)で十分。ルート距離は不要
- 位置情報の許可ダイアログが出ることをユーザーに事前に伝えるUI
- ソート切替: デフォルト(元の順序)/ 近い順 をトグルで切り替え
- 距離計算には緯度経度が必要なので、機能1のジオコーディングが前提
機能3: 営業時間表示 ✅ 実装済み(パーサーカバー率 薬局 98.2% / 医療機関 88.3%)
課題
hoursフィールドのフォーマット揺れが 6,933 種類- 約80%は簡易パターンでパース可能だが、20%は自由記述
- 誤判定リスク(緊急避妊薬の性質上、「営業中」を信じて行ったら閉まってた、は深刻)
- Google Mapリンク(機能4)でGoogle Maps上の営業情報が確認できるため、機能が重複する面もある
実装内容
- 多段正規化パーサー(
~→-、月曜日→月、年中無休→毎日、平日→月-金、24時間→毎日:0:00-24:00等) splitHoursSegments(): カンマなし連結パターンを lookbehind で分割parseDaySpecExtended():土日祝、月-水・金、月火水金等に対応(祝はスキップ)- 今日の営業状況をハイライト(営業中/営業時間外/休み)
- 全曜日スケジュールを折りたたみ表示
- 医療機関データにも対応(AM/PM変換、午前午後、括弧内除外注記、注記セグメントスキップ等)
- パース不能分(薬局 1.8% / 医療機関 11.7%)は生データのまま表示。設計詳細は HOURS_PARSER 参照
- 除外情報の保全と表示: 定休日・除外曜日・部分休業等の情報をパース前に抽出し、折りたたみ「全営業時間」内のスケジュールグリッド上部に amber 背景の注記として表示。自明な休業日はスケジュールにも反映(保守的適用)。設計原則: Correct > Correct + caveat > Unknown > Wrong(DESIGN.md §6 参照)
- 祝日対応: 国民の祝日を純粋計算で判定し、祝日休み/祝日営業時間を正しく表示
- 深夜営業(close > 24:00)の日跨ぎ判定対応
- 全件に「※営業状況は店舗にご確認ください」の注記
実装の優先順位
- ~~機能4(Google Mapリンク)~~ ✅ 完了(d83b795)
- ~~機能1(地図表示)~~ ✅ 完了(514ad56, 3c3c685)
- ~~機能2(近い順ソート)~~ ✅ 完了(3c3c685)
- ~~機能3(営業時間表示)~~ ✅ 完了(薬局 98.2% / 医療機関 88.3% カバー率)
データ構造(調査済み)
- 緯度経度: なし → 東大CSISでジオコーディング(番地レベル精度、無料)
- 営業時間: あり →
hoursフィールド。薬局 98.2% / 医療機関 88.3% パース可能(HOURS_PARSER 参照) - データ読み込み:
docs/data.jsonを fetch。10,128件 - 座標データ読み込み:
docs/geocode_cache.jsonを地図表示時のみ fetch(別ファイルで軽さ維持) - 公開ディレクトリ:
docs/(GitHub Pages) - フロントエンド:
docs/index.html+docs/app.js+docs/style.css(バニラJS、フレームワークなし)
その他の判断
- ネット予約連携: 薬局側のシステムが必要なため対象外
- 薬局の写真直接表示: データソースがなく、APIコストも非現実的なため対象外。Google Mapリンク(機能4)で代替
- 口コミ表示: 同上。Google Mapリンクで代替
- Nominatim: 日本語住所で全滅(10件中0件ヒット)。不採用
- Google Geocoding API: 高精度だが有料(~$50)。東大CSISで同等精度が無料で得られるため不採用