“通報アプリ × 軽いSNS × 地図 × ポイント × AI(AIは黒子)” 簡易実装イメージ

“通報アプリ × 軽いSNS × 地図 × ポイント × AI(AIは黒子)” 簡易実装イメージ

“通報アプリ × 軽いSNS × 地図 × ポイント × AI(AIは黒子)” 簡易実装イメージ(アイデアメモ パート2)

主役はデータ収集と利活用。AIは整理・匿名化・推計の黒子。ぼかしは顔/ナンバーではなく車体まるごと。生データは管理団体のみ

本稿は「いますぐ作り始められる」ことを最優先に、Web+Webアプリ前提での最小構成、DB(マスタ/業務/監査)、API、非同期タスク、重い演算を切り離した“AI API/ワーカー”の形を一気に示します。難しいところは後回し、でも穴は開けない。直球でいきます。

1. 全体観(最小でも回る構成)

フロント
API
DB/PostGIS
S3(anon/raw)
Queue
AIワーカー
定点カメラ
[利用者PWA/ブラウザ] ─── HTTPS ───> [API Gateway/ALB → Web API(FastAPI)]
       │                                 │
       │(事前署名URLで直UP)             │SQL(PostGIS)
       ├──────────────> [S3 anon/]      ├────> [Aurora PG + PostGIS]
       │                                 │
       │                                 └────> [SQS] → [AIワーカー群(内部API)]
       │                                              ├─ anonymize_vehicle(車体ぼかし)
       │                                              ├─ classify(カテゴリ推定/NG検知)
       │                                              ├─ dedupe(重複クラスタ)
       │                                              └─ traffic_aggregate(利用率集計)
[定点カメラ/エッジ] ──(匿名化/カウント済みメタ)──> [API /devices/{id}/events]
  

ポイント:アップロードは匿名化側 S3 anon/ をデフォ。S3 raw/ は原則使わず、管理団体の明示ワークフロー時のみ。重い処理はSQS/ワーカーへ。

2. 初期に用意するクラウド要素(MVP)

  • 静的フロント: S3 + CDN(PWA、Map描画、オフライン対応)
  • Web API: FastAPI(Fargate か Functions/Lambda)
  • DB: Aurora PostgreSQL + PostGIS
  • ストレージ: S3(anon/=ぼかし済み公開用、raw/=管理団体限定)
  • キュー: SQS(ingest/anonymize/classify/dedupe/traffic_aggregate)
  • 認証: Cognito/OIDC(LINEログイン併用可)
  • 監視: CloudWatch + APM(例:OpenTelemetry)

3. データモデル(最小セット)

マスタ

  • organizations:自治体/団体
  • categories:通報カテゴリ(10個程度に絞る)
  • areas:担当エリア(MultiPolygon)
  • road_links:道路リンク(LineString)

業務

  • reports:通報(Point, status, attributes)
  • report_media:画像/動画(anon/, raw/鍵)
  • report_status_history:進捗履歴
  • devices:定点カメラ/エッジ
  • camera_events:カウント結果(車/人)
  • traffic_agg_hourly:道路利用率(リンク×時刻)
-- 位置はPostGIS。公開時は丸める(例:50m格子)
CREATE TABLE core.reports(
  id UUID PRIMARY KEY,
  org_id UUID NOT NULL,
  user_id UUID NULL,
  category_id INT NOT NULL,
  title TEXT, body TEXT,
  status TEXT CHECK(status IN ('received','triaged','in_progress','resolved','rejected')) DEFAULT 'received',
  location geometry(Point,4326),
  location_precision_m INT DEFAULT 50,
  attributes JSONB DEFAULT '{}'::jsonb,
  dedupe_group_id UUID,
  created_at timestamptz DEFAULT now(), updated_at timestamptz DEFAULT now()
);
CREATE INDEX ON core.reports USING GIST(location);

4. API(公開REST)

メソッドパス概要
GET/api/v1/master/categoriesカテゴリ一覧
POST/api/v1/reports通報作成(メタ登録→S3事前署名返却)
POST/api/v1/media/presignS3事前署名の発行
POST/api/v1/media/confirmアップ完了通知→anonymize_vehicle投入
GET/api/v1/reports?bbox&status地図用リスト(クラスタリング済み)
PATCH/api/v1/reports/{id}/status進捗変更(operator以上)
POST/api/v1/devices/{id}/events定点の集計メタ投入(匿名化済み計数)
GET/api/v1/traffic/links/{id}/hourly道路利用率(時系列)

例:通報登録→アップロード

// 1) メタ登録
POST /api/v1/reports
{
  "category_code": "road_damage",
  "title": "舗装の穴",
  "body": "夜間に危ない",
  "event_time": "2025-09-14T09:30:00+09:00",
  "location": {"lat": 35.68, "lng": 139.76, "precision_m": 50},
  "media": [{"kind":"image","presign":true}]
}

// 2) レスポンス(S3 anon/ への事前署名)
{
  "report_id": "d1c7-...",
  "uploads": [
    {"media_id":"m-1","url":"https://s3/anon/...","fields":{"key":"anon/..","policy":".."}}
  ]
}

// 3) クライアントがS3へ直接PUTした後
POST /api/v1/media/confirm
{ "media_id": "m-1", "sha256":"abc...", "raw_requested": false }

5. “難しい演算部”はAPI/ワーカーに切り離す

原則: フロントとWeb APIは薄く。重い処理(画像処理・類似検索・時系列集計)はキュー経由でワーカーが実施。Web APIは結果だけを見る。

(A) 内部API(サービス間)

  • POST /_internal/anonymize/vehicle:車体まるごとぼかし
  • POST /_internal/classify:カテゴリ推定・NG判定
  • POST /_internal/dedupe:重複クラスタ更新
  • POST /_internal/traffic/aggregate:道路利用率集計

VPC内/認証必須。外部からアクセス不可。

(B) ワーカー(擬似コード/Python)

@app.task(name="anonymize_vehicle")
def anonymize_vehicle(media_id: str):
    media = db.get_media(media_id)
    img = load_from_s3(media.s3_key)       # ぼかしは車体全体
    boxes = detect_vehicles(img)           # car/bus/truck/bike...
    img2 = mosaic_outside_safe_margin(img, boxes)
    save_to_s3(img2, media.s3_key)         # anon/ に上書き保存
    enqueue("classify", {"report_id": media.report_id})
    

重複クラスタ(dedupe)イメージ

def recompute_cluster(report_id):
  r = db.reports[report_id]
  candidates = search_nearby(
     point=r.location, radius_m=120,
     time_window="±24h",
     text_embed=r.attributes['text_embed'])
  cluster_id = union_find_by_similarity(candidates + [r])
  db.update(report_id, dedupe_group_id=cluster_id)

道路利用率(定点カメラ→リンク集計)

POST /api/v1/devices/{id}/events
{
  "captured_at": "2025-09-14T00:00:00Z",
  "counters": {"vehicle": 86, "pedestrian": 240},
  "link_id": 10234
}

# 集計ジョブ(hourly)
INSERT INTO traffic_agg_hourly(link_id,hour,vehicle_cnt,ped_cnt)
VALUES(...) ON CONFLICT (...) DO UPDATE ...

6. フロント(PWA/Map)実装イメージ

画面

  • ホーム:地図+進捗フィルタ(クラスタ表示
  • 新規通報:撮る→位置→送る(3タップ)
  • 詳細:写真(ぼかし済み)、進捗、類似報告
  • マイページ:ポイント、履歴
  • 運用:重複束ね、優先度キュー、道路利用率グラフ

コード(超要点)

// 位置丸めはサーバ側。クライアントは細かいことしない。
async function createReport(meta){
  const r = await fetch('/api/v1/reports',{method:'POST',
    headers:{'Content-Type':'application/json'},
    body: JSON.stringify(meta)});
  const {uploads} = await r.json();
  // presigned URLに直PUT
  await fetch(uploads[0].url,{method:'PUT',body: selectedFile});
  await fetch('/api/v1/media/confirm',{method:'POST',
    headers:{'Content-Type':'application/json'},
    body: JSON.stringify({media_id: uploads[0].media_id, sha256: fileHash})});
}
    

地図は MapLibre GL 等。アクセシビリティのためボタンは大きく、音声入力も許容。オフライン時はService Workerで下書き保存→復帰時に送信。

7. セキュリティ/プライバシ運用の肝

  • ぼかし原則: 顔/ナンバー単体ではなく車体まるごと。誤検知を避ける。
  • rawの管理: 原則使わない。必要時のみ管理団体の承認付きで保管/閲覧。アクセスは監査ログ必須。
  • 公開データ: 座標は丸め(例:50m)、期間限定、再識別チェック。
  • RBAC: resident / moderator / operator / admin を分離。
  • レート制御/WAF: 荒らし対策はフロントではなくゲートで止める。

8. 運用KPI(MVPで見る数字)

  • 重複統合率(dedupe成功率)
  • 匿名化成功率(車体ぼかし漏れ0を目標)
  • 受付→現地確認リードタイム
  • 道路リンク×時間のカバレッジ率
  • 初回→翌月継続率(ポイント依存度は低く)

9. ディレクトリと設定例

repo/
├─ frontend/               # Next.js PWA
├─ api/                    # FastAPI (REST)
│  ├─ routes/             # reports, media, devices...
│  ├─ services/           # db, s3, presign, auth
│  └─ schemas/            # pydantic
├─ workers/                # AIワーカー(anonymize/classify/dedupe/aggregate)
│  └─ tasks/
├─ infra/                  # IaC(Terraform) / pipelines
└─ docs/
# .env(例:API)
DATABASE_URL=postgresql+psycopg2://...
S3_BUCKET=city-app-bucket
ANON_PREFIX=anon/
RAW_PREFIX=raw/
QUEUE_URL_ANONYMIZE=...
JWT_AUDIENCE=...

10. フェーズ2で足すもの(必要になったら)

  • GraphQL(AppSync等)でリアルタイム更新
  • OpenSearchで全文/類似検索
  • Step Functionsでワークフローの可視化(失敗時の再試行/DLQ)
  • ダッシュボード(QuickSight/Looker Studio)
  • Open311互換API公開、オープンデータ自動エクスポート

11. 用語説明集(自治体担当者向け・横文字を噛み砕く)

技術用語を「要はどういうこと?」に言い換えました。会議で出てきたら、この表をそのまま使ってOKです。

用語意味(かんたん)役割・たとえ
Webアプリ / PWAブラウザで動くアプリ。ホーム画面に入れられ、電波が弱くても一部動く。「インストールいらずのアプリ」。
CDN全国にある中継所から近場で配る仕組み。配達拠点が多い宅配便。
S3画像や動画を置く大きな倉庫。「anon = ぼかし済み棚」「raw = 生データ棚」。
事前署名URL(Presigned URL)一時的に倉庫へ直接アップできる合鍵。期限つきの搬入口パス。
APIアプリ同士の受け口(やり取りの決まり)。役所の「窓口の様式」。
FastAPI / NestJSAPIを作るための道具。窓口の仕切りや帳票を作るツール。
ALB / API Gateway外からの通信の玄関。正面ロビーの受付。
ECS Fargateサーバ管理なしでアプリを動かす台。「レンタカー、燃料補給も自動」。
Lambda呼ばれたときだけ動く小プログラム。必要な時だけ呼ぶタクシー。
Aurora PostgreSQL信頼性の高いデータベース。台帳の本丸。
PostGISDBに地図の頭脳を足す。点や道路線を扱える地図機能。
OpenSearch全文検索・似ている文章探し。図書館の司書+レコメンド。
Redis超速い一時置き場。受付の手元メモ。
SQS(キュー)順番待ちの箱。重い処理はここに並べる。整理券発券機。
Step Functions処理の流れ図をそのまま機械に実行させる。業務フローの標準化。
AppSync / WebSocket画面を自動更新する仕組み。電光掲示板が勝手に更新。
Cognito / OIDCログインの仕組み。LINEログインも接続可。入館証の発行・確認。
RBAC役割ごとに権限を分ける。住民/職員/管理者で見える範囲が違う。
IAM誰が何をして良いかの許可証。庁内の権限台帳。
KMS / Secrets Manager暗号鍵とパスワードの金庫。鍵付きロッカー。
VPC / AZ自分専用ネットワーク/データセンターの区画。専用フロア/別館。
VPC Endpoint倉庫(S3)へ裏口直結で安全に接続。荷捌き場の専用通路。
CloudWatch / X-Ray / OpenTelemetry動作状況の見える化。監視カメラ+動線の可視化。
MapLibre GL地図を画面に描く道具。地図描画エンジン。
タイル / クラスタリング地図を小分けに配る/近いピンをまとめて表示。分割配布/人数カウントの丸め表示。
bbox今見えている地図の四角い範囲。地図の窓枠。
JSON/JSONB柔らかい形式の箱に情報を入れる。可変の追記事項メモ。
GiST / GIN(索引)地理/文章検索を速くする仕掛け。しおり・目次。
geometry(Point/LineString)点/線として位置情報を保存。地点/道路線。
位置丸め50mなど大まかな位置だけ公開。自宅の真上にピンを立てない。
匿名化個人が特定されないよう隠す。画角・解像度・位置の配慮。
車体まるごとぼかし顔/ナンバー単体より安全側で隠す。誤検知対策・再識別防止。
エッジ処理カメラ側で先に匿名化やカウントして送る。現場で下ごしらえ。
データレイク / Glue / Athena / QuickSight倉庫に貯めたデータをSQLで分析・可視化。統計室と壁一面のグラフ。
Open311通報の標準仕様(カテゴリ/進捗)。他システムと連携しやすい型。
Webhook外部システムへ自動でお知らせを送る。連絡票の自動FAX。
DLQ失敗タスクの保管箱。要再処理トレイ。
IQR外れ値(おかしな数)を見つける指標。異常検知の物差し。
A/Bテスト2案を同時に試して良い方を選ぶ。案内文AとBで比較。
Embedding(埋め込み)文章や画像を数列に変えて似ている度合いを見る。似てる度=コサイン類似度。
重複検知 / クラスタ似た報告をまとめる。同じ穴の報告を1束に。
マップマッチング / Link IDカウントを道路の区間番号に結びつける。集計のひと単位。

「この言葉が出たらこう理解」でOK(超要約)

  • PWA=ブラウザで動くアプリ。
  • API=システム間の窓口。
  • PostGIS=地図に強いDB。
  • キュー=順番待ち箱(重い処理を後で)。
  • 匿名化/ぼかし=個人に結びつく情報を隠す。原則は車体まるごと
  • データレイク=とりあえず貯めて後から分析。
  • Open311=通報の共通ルール。

まとめ:横文字は多いですが、要は「データを安全に集め、見える化し、根拠を持って手を打つ」ための道具立てです。
©株式会社ビー・ナレッジ・デザイン

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA