Ruby には RSS Parser というライブラリが標準添付されてて、このライブラリの RSS::Maker というクラスが RSS や Atom を生成することができるんだけど、どうも対応していないモジュールや知らない要素については使えないっぽい(そんなに深く調べてないけど)。

なので、GeoRSS を出力するなら RSS::Maker 使うより自前で REXML 使って出力したほうが良さそう。

ただし、あまり複雑なことはしない。RSS 2.0 な GeoRSS を生成するのみ。

以下、サンプルコード。


#!/usr/bin/env ruby
$KCODE='u'
 
require 'time' # 組み込みの Time クラスを拡張する library time
require "rexml/document"
 
# GeoRSSを生成して返す。
def create_georss(channel, items)
 
  doc = REXML::Document.new()
  doc.add(REXML::XMLDecl.new(version="1.0", encoding="UTF-8"))
 
  rss = REXML::Element.new("rss")
  # バージョンと名前空間の接頭辞は決め打ち
  rss.add_attributes({
    "version" => "2.0",
    "xmlns:georss" => "http://www.georss.org/georss",
    "xmlns:gml" => "http://www.opengis.net/gml",
    "xmlns:geo" => "http://www.w3.org/2003/01/geo/wgs84_pos#",
  })
  doc.add_element(rss)
 
  # <channel>
  c = REXML::Element.new("channel", rss)
  channel.each{|k,v|
    if k == '_imageurl'
      # ロゴ画像があったら追加
      image = REXML::Element.new('image', c)
      REXML::Element.new('title', image).add_text(channel['title']) if channel['title']
      REXML::Element.new('link',  image).add_text(channel['link'])  if channel['link']
      REXML::Element.new('url',   image).add_text(channel['_imageurl'])
    else
      # TimeオブジェクトだったらRFC2822形式に
      v = v.rfc2822() if v.kind_of?(Time)
      REXML::Element.new(k, c).add_text(v)
    end
  }
 
  # <item>
  items.each{|item|
    i = REXML::Element.new('item', c)
    item.each{|k,a|
      # 値が配列の場合は複数の要素を生成する
      a = [a] if a.kind_of?(Array) == false
      a.each{|v|
        if k == '_geo'
          # 位置情報があったら追加
          # _geoはカンマで区切られてるとして処理#{緯度},#{経度}
          latlon = v.split(',')
          # georss.org, GML, w3c の3パターンの位置情報を埋め込む
          # 読み込むほうがどれかのモジュールに対応してくれることを期待して
          # GeoRSS-Simple 
          # xmlns:georss="http://www.georss.org/georss"
          REXML::Element.new('georss:point', i).add_text(latlon[0] + ' ' + latlon[1])
          # GeoRSS-GML
          # xmlns:gml="http://www.opengis.net/gml"
          where = REXML::Element.new('georss:where', i)
          point = REXML::Element.new('gml:Point', where)
          REXML::Element.new('gml:pos', point).add_text(latlon[0] + ' ' + latlon[1])
          # WGS84 Geo Positioning
          # xmlns:geo="http://www.w3.org/2003/01/geo/wgs84_pos#"
          REXML::Element.new('geo:lat',  i).add_text(latlon[0])
          REXML::Element.new('geo:long', i).add_text(latlon[1])
        else
          # TimeオブジェクトだったらRFC2822形式に
          v = v.rfc2822() if v.kind_of?(Time)
          REXML::Element.new(k, i).add_text(v)
        end
      }
    }
  }
 
  str = ""
  #formatter = REXML::Formatters::Default.new
  formatter = REXML::Formatters::Pretty.new(2, true)
  formatter.write(doc.root, str)
  #doc.write(str, -1, true, true)
  #doc.write(str, 2)
  return str
end
 
# テスト用サンプル
 
channel ={
  'title'         => 'ヅラッシュ!',
  'link'          => 'http://www.nilab.info/z3/',
  'description'   => '職業/趣味プログラマの日記・備忘録',
  'copyright'     => 'Copyright(c) 2000-2011 NI-Lab. All Right Reserved.',
  'lastBuildDate' => Time.now,
  '_imageurl'     => 'http://www.nilab.info/z3/z3_profile.jpg', # logo image url
}
 
items = [
  {
    'title'       => 'おなかすいた',
    'link'        => 'http://www.nilab.info/z3/20110328_AA.html',
    'description' => '<html><body>I am hungry. はらへ。</body></html>',
    'pubDate'     => 'Tue, 14 Dec 2010 00:07:23 +0900',
    'guid'        => 'http://www.nilab.info/z3/20110328_AA.html',
    'category'    => 'food',
  },
  {
    'title'       => 'ジオアールエスエスはデンキヒツジの夢',
    'link'        => 'http://www.nilab.info/z3/20110328_BB.html',
    'description' => '<html><body>GeoRSS is a dream of electric sheep.</body></html>',
    'pubDate'     => Time.local(2011, 1, 1, 11, 11, 11),
    'guid'        => 'http://www.nilab.info/z3/20110328_BB.html',
    'category'    => ['ruby', 'georss'],
    '_geo'        => '35.0001,135.0001'
  },
]
 
# タイムゾーンの情報を参考までに出力
$stderr.puts "Time.new.zone=#{Time.new.zone}"
 
# GeoRSSを生成
georss = create_georss(channel, items)
puts georss

実行結果


$ uname -mrsv
Linux 2.6.26-2-amd64 #1 SMP Tue Jan 25 05:59:43 UTC 2011 x86_64
 
$ ruby -v
ruby 1.8.7 (2008-08-11 patchlevel 72) [x86_64-linux]
 
$ locale
LANG=ja_JP.UTF-8
LC_CTYPE="ja_JP.UTF-8"
LC_NUMERIC="ja_JP.UTF-8"
LC_TIME="ja_JP.UTF-8"
LC_COLLATE="ja_JP.UTF-8"
LC_MONETARY="ja_JP.UTF-8"
LC_MESSAGES="ja_JP.UTF-8"
LC_PAPER="ja_JP.UTF-8"
LC_NAME="ja_JP.UTF-8"
LC_ADDRESS="ja_JP.UTF-8"
LC_TELEPHONE="ja_JP.UTF-8"
LC_MEASUREMENT="ja_JP.UTF-8"
LC_IDENTIFICATION="ja_JP.UTF-8"
LC_ALL=
 
$ date
2011年  3月 28日 月曜日 23:07:56 JST
 
$ ruby ./ruby_rexml_georss.rb > 20110328_georss.xml
Time.new.zone=JST
 
$ cat ./20110328_georss.xml
<rss xmlns:georss='http://www.georss.org/georss' xmlns:gml='http://www.opengis.net/gml' xmlns:geo='http://www.w3.org/2003/01/geo/wgs84_pos#' version='2.0'>
  <channel>
    <title>
      ヅラッシュ!
    </title>
    <copyright>
      Copyright(c) 2000-2011 NI-Lab. All Right Reserved.
    </copyright>
    <description>
      職業/趣味プログラマの日記・備忘録
    </description>
    <link>
      http://www.nilab.info/z3/
    </link>
    <image>
      <title>
        ヅラッシュ!
      </title>
      <link>
        http://www.nilab.info/z3/
      </link>
      <url>
        http://www.nilab.info/z3/z3_profile.jpg
      </url>
    </image>
    <lastBuildDate>
      Mon, 28 Mar 2011 23:08:00 +0900
    </lastBuildDate>
    <item>
      <category>
        food
      </category>
      <guid>
        http://www.nilab.info/z3/20110328_AA.html
      </guid>
      <pubDate>
        Tue, 14 Dec 2010 00:07:23 +0900
      </pubDate>
      <title>
        おなかすいた
      </title>
      <description>
        &lt;html&gt;&lt;body&gt;I am hungry.
        はらへ。&lt;/body&gt;&lt;/html&gt;
      </description>
      <link>
        http://www.nilab.info/z3/20110328_AA.html
      </link>
    </item>
    <item>
      <category>
        ruby
      </category>
      <category>
        georss
      </category>
      <guid>
        http://www.nilab.info/z3/20110328_BB.html
      </guid>
      <pubDate>
        Sat, 01 Jan 2011 11:11:11 +0900
      </pubDate>
      <title>
        ジオアールエスエスはデンキヒツジの夢
      </title>
      <description>
        &lt;html&gt;&lt;body&gt;GeoRSS is a dream of electric
        sheep.&lt;/body&gt;&lt;/html&gt;
      </description>
      <link>
        http://www.nilab.info/z3/20110328_BB.html
      </link>
      <georss:point>
        35.0001 135.0001
      </georss:point>
      <georss:where>
        <gml:Point>
          <gml:pos>
            35.0001 135.0001
          </gml:pos>
        </gml:Point>
      </georss:where>
      <geo:lat>
        35.0001
      </geo:lat>
      <geo:long>
        135.0001
      </geo:long>
    </item>
  </channel>
</rss>

・生成したGeoRSSファイル: 20110328_georss.xml

Google Maps で GeoRSS を表示させるとこんな感じ。

GeoRSS: ヅラッシュ! - Google Maps
ヅラッシュ! - Google Maps

tags: ruby georss rss rexml

Posted by NI-Lab. (@nilab)