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>
<html><body>I am hungry.
はらへ。</body></html>
</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>
<html><body>GeoRSS is a dream of electric
sheep.</body></html>
</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 を表示させるとこんな感じ。
tags: ruby georss rss rexml
Posted by NI-Lab. (@nilab)