module Jipcode::AddressLocator

Constants

GAIKU_HYOUJI

丁目・番・号・地割・番地をハイフンに置換する

1丁目2番3号 -> 1-2-3
1丁目2番3 -> 1-2-3
1丁目2-3 -> 1-2-3
1丁目2番 -> 1-2
1地割2番地 -> 1-2
1丁目2番地の3 -> 1-2-3
etc..

@private @see ja.wikipedia.org/wiki/%E4%BD%8F%E5%B1%85%E8%A1%A8%E7%A4%BA

INDEX_PATH
INDEX_VERSION_FILE
VERSION

Public Class Methods

create_index!() click to toggle source

Create index files by Jipcode data.

# File lib/jipcode/address_locator/indexer.rb, line 19
def self.create_index!
  refresh_index_dir

  index = collect_index

  export_index(index)

  File.open(INDEX_VERSION_FILE, 'w') do |f|
    f.write(Jipcode::VERSION)
  end
end
exist_latest_index?() click to toggle source

Make sure Jipcode version and index version are the same. @return [Boolean] result

# File lib/jipcode/address_locator/indexer.rb, line 11
def self.exist_latest_index?
  return false unless File.exist?(INDEX_VERSION_FILE)

  version = File.read(INDEX_VERSION_FILE)
  version == Jipcode::VERSION
end
locate(search_address) click to toggle source

Locate zipcode data by address @param [String] search_address @return [Array<Hash>] zipcode data

# File lib/jipcode/address_locator/locator.rb, line 13
def self.locate(search_address)
  normalized = normalize_address(search_address)

  find_by_address(normalized)
    .map { |address| calc_and_add_distance!(address, normalized) }
    .sort_by { |address| address['distance'] }
    .reverse
end
normalize_address(raw_address) click to toggle source

Normalize japanese address for search. @example

Jipcode::AddressLocator.normalize_address('稲穂県ミドリ市一番町一丁目2の3番')
# => '稲穂県ミドリ市一番町1-2-3'

@param [String] raw_address @return [String] normalized address

# File lib/jipcode/address_locator/normalizer.rb, line 11
def self.normalize_address(raw_address)
  zenkaku2hankaku(raw_address)
    .yield_self { |address| kansuji2hankaku(address) }
    .yield_self { |address| jukyohyouji2hyphen(address) }
end

Private Class Methods

calc_and_add_distance!(address, search_address) click to toggle source

@private

# File lib/jipcode/address_locator/locator.rb, line 38
def self.calc_and_add_distance!(address, search_address)
  distance = JaroWinkler.distance(address['normalized_address'], search_address)
  address['distance'] = distance
  address
end
collect_index() click to toggle source

@private

# File lib/jipcode/address_locator/indexer.rb, line 38
def self.collect_index
  index = 47.times.each_with_object({}) { |item, memo| memo[item + 1] = []; }

  Dir.glob("#{ZIPCODE_PATH}/*.csv").each do |file_name|
    CSV.read(file_name).each do |row|
      _zipcode, prefecture_name, city, town = row
      row << normalize_address("#{prefecture_name}#{city}#{town}")
      prefecture_code = extract_prefecture_code(prefecture_name)
      index[prefecture_code] << row
    end
  end

  index
end
export_index(index) click to toggle source

@private

# File lib/jipcode/address_locator/indexer.rb, line 54
def self.export_index(index)
  index.each do |prefecture_code, rows|
    rows.sort_by! { |row| [row[0], row[2], row[3]] }
    CSV.open("#{INDEX_PATH}/#{prefecture_code}.csv", 'wb') do |csv|
      csv << %w[zipcode prefecture_name city town normalized_address]
      rows.each { |row| csv << row }
    end
  end
end
extract_prefecture_code(address) click to toggle source

@private

# File lib/jipcode/address_locator/helper.rb, line 6
def self.extract_prefecture_code(address)
  prefecture_name = address.match(/\A(#{prefecture_names.join('|')})/).to_s
  PREFECTURE_CODE.invert[prefecture_name]
end
find_by_address(search_address) click to toggle source

@private

# File lib/jipcode/address_locator/locator.rb, line 23
def self.find_by_address(search_address)
  prefecture_code = extract_prefecture_code(search_address)
  path = "#{INDEX_PATH}/#{prefecture_code}.csv"
  return [] if prefecture_code.nil?

  CSV.read(path, headers: true).select do |row|
    address = row['normalized_address']
    # 長いほうが短い方に含まれてるか判別
    long = [address, search_address].max
    short = [address, search_address].min
    long.start_with?(short)
  end
end
jukyohyouji2hyphen(string) click to toggle source
# File lib/jipcode/address_locator/normalizer.rb, line 52
def self.jukyohyouji2hyphen(string)
  string.gsub(/\d(#{GAIKU_HYOUJI.join('|')})/) { |match| match.gsub(/(#{GAIKU_HYOUJI.join('|')})/, '-') }
        .gsub(/-$/, '')
end
kansuji2hankaku(string) click to toggle source

住居表示の漢数字を半角数字に置き換える @private

# File lib/jipcode/address_locator/normalizer.rb, line 26
def self.kansuji2hankaku(string)
  # 漢数字は「丁目」の前に使われてるもののみを半角にする
  string.gsub(/[一二三四五六七八九十]+丁目/) do |match|
    match.tr('一二三四五六七八九', '1-9')
    # TODO: 十丁目 -> 10、十三丁目 -> 13 二十丁目 -> 20
  end
end
prefecture_names() click to toggle source

@private

# File lib/jipcode/address_locator/helper.rb, line 12
def self.prefecture_names
  PREFECTURE_CODE.values
end
refresh_index_dir() click to toggle source

@private

# File lib/jipcode/address_locator/indexer.rb, line 32
def self.refresh_index_dir
  FileUtils.rm_rf(INDEX_PATH) if File.exist?(INDEX_PATH)
  Dir.mkdir(INDEX_PATH)
end
zenkaku2hankaku(string) click to toggle source

全角数字ハイフン→半角数字ハイフン @private

# File lib/jipcode/address_locator/normalizer.rb, line 19
def self.zenkaku2hankaku(string)
  string.tr('0-9', '0-9')
        .tr('–−', '-')
end