世界の測量

Sibling of "Relevant, Timely, and Accurate, " but much lighter and shorter ※自らの所属する組織の見解を示すものでない

国土数値情報(避難施設データ)のベクトルタイル変換

国土数値情報(避難施設データ)のベクトルタイル変換を行ってみた。国土数値情報(地価公示)データの変換と同じだが、47都道府県分一つ一つクリックする必要はある。OS Xの場合、~/Downloads フォルダに展開されるので、そこをスタートラインにして、次のスクリプトを実行した。

map.rb

require 'find'
require 'json'
require 'rubygems'
require 'geo_ruby'
require 'geo_ruby/shp'
include GeoRuby::Shp4r

module Math
  def self.sec(x)
    1.0 / cos(x)
  end
end

def process(r, z)
  n = 2 ** z
  lon_deg = r.geometry.x
  lat_rad = r.geometry.y * 2 * Math::PI / 360
  x = n * ((lon_deg + 180) / 360)
  y = n *
    (1 - (Math::log(Math::tan(lat_rad) + Math::sec(lat_rad)) / Math::PI)) / 2
  prop = {}
  r.data.attributes.each {|k, v|
    prop[k] = case v
    when 'true'
      true
    when 'false'
      false
    else
      s = v.to_s
      /^\d*$/.match(s) ? s.to_i : s
    end
  }
  f = {:type => 'Feature', :geometry => {:type => 'Point', 
    :coordinates => [r.geometry.x, r.geometry.y]}, 
    :properties => prop}
  print [z, x.to_i, y.to_i, JSON.dump(f)].join("\t"), "\n"
end

Dir.glob('/Users/hfu/Downloads/P20-*') {|dir|
  Dir.glob("#{dir}/*.shp") {|path|
    ShpFile.open(path) {|shp|
      shp.each {|r|
        ARGV.each {|z| process(r, z.to_i)}
      }
    }
  }
}

reduce.rb

require 'fileutils'
require 'json'

last = nil
geojson = nil
$n_tiles = 0

def write(geojson, zxy)
  path = zxy.join('/') + '.geojson'
  print "writing #{geojson[:features].size} features to #{path}\n"
  [File.dirname(path)].each {|d| FileUtils.mkdir_p(d) unless File.exist?(d)}
  File.open(path, 'w') {|w| w.print(JSON.dump(geojson))}
  $n_tiles += 1
end

while gets
  r = $_.strip.split("\t")
  current = r[0..2].map{|v| v.to_i}
  if current != last
    write(geojson, last) unless last.nil?
    geojson = {:type => 'FeatureCollection', :features => []}
  end
  geojson[:features] << JSON.parse(r[3])
  last = current
end
write(geojson, last)
print "finished. #{$n_tiles} tiles written.\n"

実行

$ ruby map.rb 14 | sort | ruby reduce.rb

これでズームレベル14のベクトルタイルが作成される。34684タイル。