GeoJSON を入力として取る JavaScript マップを使用して、英国の毎年恒例の映画上映を視覚化しています。ここで 2011 年のデータで動作していることを確認できます: http://screened2011.herokuapp.com
GeoJSON の生成は非常に非効率的です。多くの場合、「タイル」ごとに 5 秒以上かかります。
バウンディングボックス内の一連の「スクリーニング」についてMongoDBにクエリを実行し(JSの要求に従って)、16x16のそれぞれで発生したスクリーニングの総数を表す2次元配列を生成するRubyアプリがあります。これが明らかにボトルネックです。サーバーにヒットし、これらすべてのスクリーニングを引き下げています。
これを、バウンディング ボックス内のすべてのスクリーニングの数を 16x16 の値の配列に集計する map/reduce クエリに置き換えたいのですが、あまり成功していません。これは、最初の map/reduce のタスクです。 !
これは、無関係なものを取り除いたコードの単純化されたバージョンです (これはひどいものであり、ハックが終わろうとしていなければ、リファクタリングしたいと思います)。
get :boxed, :map => "/screenings/year/:year/quadrant/:area/bbox/:bbox", :provides => [:json, :jsonp], :cache => false do
box = parameter_box(params[:bbox]) # returns [[minx,miny],[maxx,maxy]]
year = params[:year].to_i
screenings = Screening.where(:location.within(:box) => box).where(:year => year)
jsonp Screening.heatmap(screenings, box, 16)
end
def self.heatmap screenings, box, scale
grid = []
min_x = box[0][0]
min_y = box[0][1]
max_x = box[1][0]
max_y = box[1][1]
box_width = max_x.to_f - min_x.to_f
box_height = max_y.to_f - min_y.to_f
return [] if box_width == 0 || box_height == 0
# Set up an empty GeoJSON-style array to hold the results
scalef = scale.to_f
(0..(scale - 1)).each do |i|
grid[i] = []
(0..(scale - 1)).each do |j|
box_min_x = min_x + (i * ( box_width / scalef ))
box_max_x = min_x + ((i + 1) * ( box_width / scalef ))
box_min_y = min_y + (j * ( box_height / scalef ))
box_max_y = min_y + ((j + 1) * ( box_height / scalef ))
grid[i][j] = {
:count => 0,
#:id => "#{box_min_x}_#{box_max_x}_#{box_min_y}_#{box_max_y}",
:coordinates => [
[
[box_min_x,box_min_y], [box_min_x, box_max_y], [box_max_x, box_max_y], [box_max_x, box_min_y], [box_min_x,box_min_y]
]
]
}
end
end
# This loop is the bottleneck and I'd like to replace with a map-reduce
screenings.only(:location, :total_showings).each do |screening|
x = (scale * ((screening.location[0] - min_x) / box_width)).floor
y = (scale * ((screening.location[1] - min_y) / box_height)).floor
raise if x > (scale - 1)
raise if y > (scale - 1)
grid[x][y][:count] += screening.total_showings.to_i
end
# This converts the resulting 16x16 into GeoJSON
places = []
grid.each do |x|
x.each do |p|
if p[:count].to_i > 0
properties = {}
properties[:total_showings] = p[:count]
places << {
"id" => p[:id],
"type" => "Feature",
"geometry" => {
"type" => "Polygon",
"coordinates" => p[:coordinates]
},
"properties"=> properties
}
end
end
end
{
"type" => "FeatureCollection",
"features" => places
}
end
私は Mongoid を使用しているので、mapreduce をスクリーニング クエリに連鎖させることができました。これによりプロセスが大幅に高速化されることを期待していますが、次のようなものをこの関数に渡すにはどうすればよいでしょうか?:
[
[1,20000,30,3424,53,66,7586,54543,76764,4322,7664,43242,43,435,32,643],
...
]
...境界ボックス内の各レコードについて、この構造内のそれぞれ数百万のレコードに基づいています (基本的に total_showings を合計します)。
{"_id"=>BSON::ObjectId('50e481e653e6dfbc92057e8d'),
"created_at"=>2013-01-02 18:52:22 +0000,
"ended_at"=>Thu, 07 Jun 2012 00:00:00 +0100,
"events"=>["10044735484"],
"film_id"=>BSON::ObjectId('4f96a91153e6df5ebc001afe'),
"genre_ids"=>[],
"location"=>[-2.003309596016, 52.396317185921],
"performance_id"=>"9001923080",
"specialised"=>false,
"started_at"=>Fri, 01 Jun 2012 00:00:00 +0100,
"total_showings"=>1,
"updated_at"=>2013-01-02 18:52:22 +0000,
"venue_id"=>BSON::ObjectId('4f9500bf53e6df004000034d'),
"week"=>nil,
"year"=>2012}
よろしくお願いします!