logoChibiham
cover
🗾

ABR-Geocoderを試す

目的

  • ABR-geocoderでできることを確認する
  • ABR-geocoderの精度を体感する
  • セットアップと運用の注意点を確認する
  • ABR Geocoder 概要

    デジタル庁が提供する、日本国内の住所文字列を正規化し、緯度経度を返すジオコーディングツール。アドレス・ベース・レジストリ(全国の住所マスターデータ)をデータソースとし、政府により月次で更新されるため信頼性が高い。MITライセンスで提供され、オフライン(外部API利用なし)で動作させることができる

    主な機能

  • 住所正規化: 「千代田区霞が関1−3−1」→「東京都千代田区霞が関一丁目3番1号」
  • ジオコーディング: 住所から緯度・経度を取得
  • 町字ID付与: 住所の一意識別子を返却
  • 表記ゆれ吸収: 漢数字/算用数字、旧字体/新字体などを統一
  • 技術仕様

  • 言語: Node.js / TypeScript
  • DB: SQLite(ローカルDB、全国データで約50GB)
  • 特徴

  • 無料: オープンソース(MIT License)
  • オフライン動作: 外部API不要
  • 高精度: 政府公式データ使用
  • 定期更新: デジタル庁が月次でデータ更新
  • ローカルでの起動方法

    markdown
    # Node.js 20以上が必要
    npm install -g @digital-go-jp/abr-geocoder
    
    # データのダウンロード(全国データ)
    abrg download
    
    # 特定地域のみダウンロード(例:東京都)
    abrg download -c 130001
    
    # RESTサーバーとして起動
    abrg serve start
    
    # ポートを指定して起動(デフォルトは3000)
    abrg serve start -p 8080

    ダウンロードの実行について

    ローカルでの実行なので、ネットワーク、実行環境にもよるだろうが、ちょうど1hほどかかった

    markdown
    ❯ abrg download --debug
    download: 1:00:53.900 (h:mm:ss.mmm)

    実行してみる

    curlでの実行

    curl 'http://localhost:3000/geocode?address=東京都千代田区霞が関1-3-1' | jq
    早くも面倒になったのでテストとして向いている住所での実行をClaude + playwright mcpにお任せした結果

  • 住居番号までの完全な登録ABRに登録がない場合がある
    • 千代田区1-9-1 東京駅の出力結果として、othersが-1 東京駅 となる
      • 実際には1ではグラントウキョウノースタワー、2ではグラントウキョウサウスタワーとなっている模様
    • 特に大規模ビルの場合、街区番号(1-9)までしか登録されていない可能性
    • → ビル名、部屋番号などの抽出も完璧にはできないかも

  • スコアの傾向

    入力パターンscorematch_level特徴
    千代田区霞が関1−3−10.57residential_block都道府県省略
    東京都港区六本木6-10-10.87residential_block都道府県明記
    千代田区永田町1-7-10.57residential_block都道府県省略
    札幌市中央区北3条西6丁目0.82machiaza_detail都道府県省略だが高スコア
    京都市中京区寺町通御池上る上本能寺前町4880.88parcel複雑な京都住所だが高スコア
    澁谷区澁谷1-1-10.5residential_detail旧字体使用
    千代田区丸の内1-9-1 東京駅0.7residential_blockビル名付き
    千代田区0.58city市区町村のみ
    大阪市中央区大手前2-1-220.74residential_detail都道府県省略
  • 入力の完全性スコア

      都道府県が省略されている場合、一貫して低めのスコア(0.5〜0.6台)になる傾向

  • 表記の標準度スコア
    1. 旧字体(澁谷)→ 0.5(最低値)
    2. 標準的な表記 → 0.7〜0.9
  • 省略されても高スコアのケースもあり
  • 実運用の判定基準はこのようになるか?
    • 0.8以上: そのまま信頼
    • 0.6〜0.8: 念のため確認推奨
    • 0.6未満: 入力の見直しを促す
  • ECRでABR Geocoderを使用する場合の方針

    デフォルトで~/.abr-geocoder に関連ファイルが保存される。

    このフォルダのサイズは、全国データをdownload後で58GB程度

    markdown
    ❯ du -sk .abr-geocoder | awk '{print $1/1024 " MiB"}'                                                      ─╯
    57419.9 MiB

    SQLiteとマウントファイルシステムのパフォーマンス影響

    docker image build時に全国データを含んだコンテナを生成するのは現実的ではなく(ECRだとImage Layerの最大サイズ制限10GBなので実質的に無理)、ABR Geocoderは内部的にSQLiteを使用しているため、ストレージタイプの選択はパフォーマンスに直結する

    Claude いわく

    SQLiteの特性

  • ファイルベースのデータベース
  • 頻繁なディスクI/Oが発生
  • ランダムアクセスが多い
  • ストレージタイプの影響

  • EBS (gp3/io2): 比較的高速、IOPS設定可能
  • EFS: ネットワーク経由なので遅延が発生
  • FSx: 高性能だがコスト高
  • ストレージ選択による影響

    ストレージタイプレスポンス時間(目安)推奨用途
    EBS gp320-100ms本番環境
    EFS50-500ms複数コンテナ共有時
    FSx for Lustre15-80ms高性能要求時

    SQLite最適化設定

    shell
    # SQLiteのパフォーマンスチューニング
    export SQLITE_TMPDIR=/dev/shm  # メモリ上の一時ファイル
    export PRAGMA_CACHE_SIZE=10000  # キャッシュサイズ増加
    export PRAGMA_MMAP_SIZE=268435456  # メモリマップサイズ

    アーキテクチャ

    EBSマウント(推奨)

  • Fargate非対応だがEC2で高速
  • データ永続化が容易
  • IOPS調整可能
  • デプロイ戦略(データ更新)

    EBSのボリュームは単一Fargate Taskにしかアタッチできない制約あり(そもそもSQLite接続するので別にした方がよさそうではある)

    以下の構成でコスト最適化 / デプロイ時間短縮化 / 可用性担保ができそう

  • EBSスナップショットを日次バッチ処理バッチ処理で更新
  • スナップショットから作成したvolumeをマウントしたTaskをB/Gデプロイ
  • まとめ

    以下の構成がよさそう

  • Fargate + EBSを使用
  • abr-geocoderのデータ更新したEBS スナップショットを作成するバッチ処理を作成
  • B/Gデプロイ。デプロイ時には、最新のスナップショットから作成したEBSボリュームを使用
    • データ更新の反映とabr-geocoder更新をデプロイで実施する
  • abrg update checkの実行

    更新可能データの取得基準がわからないが、更新対象ファイルがあると毎回出てしまった。

    この時の処理時間は3.5min

    Code
    ❯ abrg update-check                                                                                        ─╯
     ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0% | 0/1total = 10698
    利用可能な更新データ(3793)があります。
    続けてデータをダウンロードしますか? [Y/N] y
        ~                                                                           3m 24s   14:54:00  ─╮
    ❯ abrg update-check -y                                                                                     ─╯
     ░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░░ 0% | 0/1total = 10698
    利用可能な更新データ(3793)があります。

    api実行負荷テスト

    直列実行で、ローカル起動したときのテスト、100回計測の平均値を100回計測

    shell
    #!/bin/bash
    
    # 負荷テストスクリプト - 100回の平均時間を100回測定
    echo "負荷テスト開始: 100回の平均時間を100回測定します"
    echo "URL: http://localhost:3000/geocode?address=東京都千代田区霞が関1-3-1"
    echo "----------------------------------------"
    
    # 100回の測定を実行
    for round in {1..100}; do
        total_time=0
        
        # 100回のリクエストを実行して時間を測定
        for i in {1..100}; do
            # timeコマンドの出力をファイルに保存
            { time curl -s "http://localhost:3000/geocode?address=東京都千代田区霞が関1-3-1" > /dev/null; } 2> temp_time.txt
            
            # real時間を抽出(秒単位)
            real_time=$(grep "real" temp_time.txt | awk '{print $2}' | sed 's/m/ * 60 + /' | sed 's/s//' | bc -l)
            total_time=$(echo "$total_time + $real_time" | bc -l)
        done
        
        # 平均時間を計算
        average_time=$(echo "scale=4; $total_time / 100" | bc -l)
        
        # 結果を出力
        echo "ラウンド $round: 平均応答時間 = ${average_time}秒"
        
        # 一時ファイルを削除
        rm -f temp_time.txt
    done
    
    echo "----------------------------------------"
    echo "すべての測定が完了しました"
    data

    0.015 - 0.060s程度で実行される。あくまでローカル実行なので参考程度。リソースのモニタリングもしていない。

    並列処理についてgithubに言及があるため、ランタイムはnode.jsのようだがクラスター使用しているかも。その場合、CPU数は上げておいた方がいい可能性がある。また、memory使用量もモニタリングして決定した方がよさそう。このあたりは本格的に負荷テストを行なって確認した方がよい