郵便番号マップ作成記 (1) - 郵便番号データをデータベースに入れる

前回予告した通り、郵便番号マップを作った手順を紹介していこう。

ビジュアライジングのためにはデータ収集が重要だ。今回はデータベースに郵便番号データを入れていくところを説明する。泥臭いけど避けては通れない作業だ。

郵便番号データを入手

まずは郵便番号のデータを手に入れる。といっても日本郵便が郵便番号データを CSV の形で提供してくれてるので、特に凝ったことはしなくてもよい。

以下のサイトから全国一括のファイルをダウンロードするだけ。ありがたや。

Shift-JIS なので utf-8 に変換しておいた。

% wget http://www.post.japanpost.jp/zipcode/dl/oogaki/lzh/ken_all.lzh
--00:23:50--  http://www.post.japanpost.jp/zipcode/dl/oogaki/lzh/ken_all.lzh
           => `ken_all.lzh.1'
www.post.japanpost.jp をDNSに問いあわせています... 122.215.192.22
www.post.japanpost.jp|122.215.192.22|:80 に接続しています... 接続しました。
HTTP による接続要求を送信しました、応答を待っています... 200 OK
長さ: 1,726,157 (1.6M) [application/octet-stream]

100%[=============================================>] 1,726,157      1.11M/s

00:23:52 (1.11 MB/s) - `ken_all.lzh.1' を保存しました [1726157/1726157]

% lha -x ken_all.lzh
ken_all.csv     - Melted   :  ooooooooooooooooooooooooooooooooooooooooooooooo
% nkf -w ken_all.csv > ken_all_utf8.csv

データベースへの作成

ビジュアライジング・データ ―Processingによる情報視覚化手法 でのアメリカ版では CSV データに経度緯度データも入っていたんだけど、日本郵便のデータには入っていない。ならば、ジオコーディングで取得するしかない。

今回はその準備のためにデータベース環境を整えていく。言語は Ruby を選択した。O/R マッパーとして Ruby on Rails の ActiveRecord を使って省エネを狙う。DB は手軽に使える sqlite3 を使う。

バージョンはこんな感じ。

  • Ruby 1.8.5
    • ActiveRecord 1.15
    • sqlite3-ruby 1.2.4
  • sqlite3 3.6.6.2

テーブルの準備

まずは、DB とテーブルの作成。ActiveRecord の Migration 機能を使ってテーブル構造を定義してやる。

# EntrySchema.rb
require 'rubygems'
require 'active_record'

ActiveRecord::Base.establish_connection(
  :adapter => 'sqlite3',
  :dbfile => "geocode.db"
  )

class EntrySchema < ActiveRecord::Migration
  def self.up
    create_table(:codes){ |t|
      t.column :high, :string, :null => false
      t.column :low, :string, :null => false
      t.column :pref, :string, :null => false
      t.column :city, :string, :null => false
      t.column :lat, :float
      t.column :lng, :float
    }
  end

  def self.down
    drop_table :codes
   end
end

class Code < ActiveRecord::Base
end

こう書いておくと、次のようなコードを実行させるだけでテーブルを作成してくれる。

% ruby -e 'require "EntrySchema"; EntrySchema.migrate(:up)'
 == EntrySchema: migrating
 ====================================================
 -- create_table(:codes)
   -&gt; 0.0397s
 == EntrySchema: migrated (0.0402s) ============================================

素晴らしい。

Migration については、以下のサイトを参考にした。

CSV を DB に流し込む

お次は DB に CSV のデータを流し込むところ。

Ruby には標準で CSV クラスがついてくる。こいつを使えば1行を配列としてパースしてくれる。

DB 側は ActiveRecord と併用すればいとも簡単。コードがこれ。

require 'EntrySchema'
require 'csv'

i = 1
CSV.open('ken_all_utf8.csv', 'r') do |row|
  # 下4桁が 0000 のだけ突っ込む
  next unless row[2].to_s.slice(3, 4) == "0000"

  # 既に突っ込んだ場合はそのデータを読み取る
  code = Code.find_by_id(i)
  code = Code.new if code.nil?

  # CSV のデータを保存する
  code.high = row[2].to_s.slice(0, 3)
  code.low = row[2].to_s.slice(3, 4)
  code.pref = row[6]
  code.city = row[7]
  code.save

  # 出力
  puts "#{code.pref} #{code.city}"
  i += 1
end

ActiveRecord のおかげで、Code オブジェクトのプロパティを設定して save するだけで DB に格納してくれる。抽象度が高くて幸せ。

実行してみる。

% ruby script/parse_postal.rb
<font color="#999999">北海道 札幌市中央区
北海道 札幌市北区
北海道 札幌市東区
北海道 札幌市白石区
北海道 札幌市豊平区
  : (略)
沖縄県 沖縄市
沖縄県 宮古島市
沖縄県 八重山郡竹富町</font>

いい具合に DB に突っ込まれていく。

ActiveRecord については以下のサイトがとても参考になる。

sqlite3 コンソールで確認

ちゃんと入ってることを確認する。

sqlite> select * from codes;
      :
784|904|0000|沖縄県|沖縄市||
785|906|0000|沖縄県|宮古島市||
786|907|0000|沖縄県|八重山郡竹富町||

786件のデータが入力されている。やったね。

次回は、経度緯度を埋めていくところから。