Osmxapi で任意地区の OpenStreetMap を HTTP 取得

OpenStreetMap には、任意地区のデータを HTTP GET で取得できる API があります。これを使って、小さな地域の OpenStreetMapShapefile として取得するスクリプトを作ってみました。

The OSM Extended API (or osmxapi, pronounced OSM zappy) is an InformationFreeway service, based on a modified version of the standard API, that provides enhanced search and querying capabilities.

Xapi - OpenStreetMap Wiki

という API があります。geotools.rb を使って、OpenStreetMap の LineString データ(OpenStreetMap のフォーマットで言う way データ) を Shapefile に落とすスクリプトを作ってみました。

スクリプト

# osm-rest.rb
# このコードは、LineString の数が 40 本程度の取得までしか使えない
# (REXML のパフォーマンスの問題か、処理が遅い。)
$KCODE = 'u'
require 'uri'
require 'open-uri'
require 'rexml/document'
require 'geotools'

def collect_tags(e)
  tags = {}
  e.each_element('tag') do |tag|
    tags[tag.attribute('k').value] = tag.attribute('v').value
  end
  tags
end

def create_geometry(doc, e)
  wkt = "LINESTRING ("
  e.each_element('nd') do |nd|
    id = nd.attribute('ref').value
    node = REXML::XPath.first(doc, "//osm/node[@id='#{id}']")
    wkt += "#{node.attribute('lon').value} #{node.attribute('lat').value}, "
  end
  wkt = wkt.chop.chop + ")"
  Geo::import_wkt_geometry(wkt)
end

bbox = [9.75, 52.35, 9.76, 52.36]
uri = "http://www.informationfreeway.org/api/0.5/*%5bbbox%3d#{bbox[0]},#{bbox[1]},#{bbox[2]},#{bbox[3]}%5d"
doc = REXML::Document.new(open(uri).read).root

print "Downloaded the data. constructing line data.\n"
$stdout.flush

Geo::FeatureList.open('osm-rest.shp') do |w|
  ways = REXML::XPath.match(doc, '//osm/way')
  count = 0
  ways.each do |way|
    print "#{sprintf('%.2f', 100.0 * count / ways.size)}% of #{ways.size} finished.\n"
    $stdout.flush
    tags = collect_tags(way)
    geom = create_geometry(doc, way)
    w.write({:the_geom => geom, :tags => tags.inspect})
    count += 1
  end
end

スクリプトの中の配列 bbox で、データの包含矩形を変更することができます。
このスクリプトは実行速度が遅いです。これは、

  • Osmxapi の反応がちょっと遅い。
  • REXML の XPath 処理がちょっとおそい

というあたりが原因ではないかと考えられます。(ただし、きちんと切り分けはしていません。)

感想

この API、なかなか分かりやすくてさわやかだと思いました。戻ってくるデータのデータモデルも、これ以上単純にはできないほど単純なのもので、美しいものだと感じました。
一方で、OpenStreetMap から大量のデータを取得したい場合には、やはり planet-latest.osm.gz を取得して、XPath などに頼らずにもう少し高速な手段で way と 構成 node を組み合わせる処理を実現する必要があるように感じました。XPath の高速な処理系を試してみるという別解もあるかもしれません。