nysol-view 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/bin/m2gv.rb +812 -0
- data/bin/mautocolor.rb +244 -0
- data/bin/mbar.rb +818 -0
- data/bin/mdtree.rb +1017 -0
- data/bin/mgv.rb +809 -0
- data/bin/mnest2tree.rb +159 -0
- data/bin/mpie.rb +733 -0
- data/bin/msankey.rb +644 -0
- data/lib/nysol/viewjs.rb +13490 -0
- metadata +101 -0
data/bin/mpie.rb
ADDED
@@ -0,0 +1,733 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
#-*- coding: utf-8 -*-
|
3
|
+
|
4
|
+
require "rubygems"
|
5
|
+
require "nysol/mcmd"
|
6
|
+
require "nysol/viewjs"
|
7
|
+
|
8
|
+
# ver="1.0" # 初期リリース 2014/5/11
|
9
|
+
$cmd = $0.sub(/.*\//,"")
|
10
|
+
|
11
|
+
$version = 1.0
|
12
|
+
$revision = "###VERSION###"
|
13
|
+
|
14
|
+
def help ()
|
15
|
+
|
16
|
+
#ヒアドキュメント
|
17
|
+
STDERR.puts <<EOF
|
18
|
+
------------------------
|
19
|
+
#{$cmd} version #{$version}
|
20
|
+
------------------------
|
21
|
+
概要) CSVデータから円グラフ(HTML)を作成する
|
22
|
+
1次元グリッド、2次元グリッドのグラフ表示が可能
|
23
|
+
マウススクロールで拡大、縮小、マウスドラッグで画像の移動が可能
|
24
|
+
|
25
|
+
書式1) #{$cmd} [i=] [o=] [title=] [pr=] [k=] [cc=] f= share= [--help]
|
26
|
+
i= : 入力データファイル名(CSV形式)
|
27
|
+
o= : 出力ファイル名(HTMLファイル)
|
28
|
+
title= : グラフのタイトル文字列を指定する
|
29
|
+
pr= : 円グラフの半径を指定する(default:160)
|
30
|
+
k= : x軸,y軸に展開する属性項目名
|
31
|
+
k=なしの場合は円グラフを1つ作成する
|
32
|
+
項目を1つ指定した場合は1次元の円グラフ行列を、
|
33
|
+
項目を2つ指定した場合は2次元の円グラフ行列を作成する
|
34
|
+
(y軸項目,x軸項目の順に指定)
|
35
|
+
cc= : 1行に表示する円グラフの最大数を指定する(default:5)
|
36
|
+
1次元グラフのみで指定可能(k=1つ指定の場合)
|
37
|
+
f= : 構成要素項目名を指定する(必須)
|
38
|
+
データにnullが含まれる場合は無視する
|
39
|
+
v= : 構成比項目(円グラフの円弧の長さを決定する項目)を指定する(必須)
|
40
|
+
データにnullが含まれる場合は0として扱う
|
41
|
+
先頭の0は無視する
|
42
|
+
数字以外の場合はエラーとなる
|
43
|
+
--help : ヘルプの表示
|
44
|
+
|
45
|
+
注意1)コマンドには、f=パラメータやk=パラメータで指定した項目を自動的に並べ替える機能はない
|
46
|
+
グラフに表示したい順に、あらかじめ並べ替えておく必要がある。
|
47
|
+
|
48
|
+
例1) 円グラフを1つ描画する
|
49
|
+
dat1.csvファイルのAgeを構成要素項目に、Populationを構成比項目として円グラフを1つ描画する
|
50
|
+
|
51
|
+
dat1.csv
|
52
|
+
Age,Population
|
53
|
+
10,310504
|
54
|
+
20,552339
|
55
|
+
30,259034.5555
|
56
|
+
40,0450818
|
57
|
+
50,1231572
|
58
|
+
60,1215966
|
59
|
+
70,641667
|
60
|
+
|
61
|
+
$ #{$cmd} i=dat1.csv v=Population f=Age o=result1.html
|
62
|
+
|
63
|
+
例2) 1次元の円グラフ行列を描画する
|
64
|
+
dat2.csvファイルのAgeを構成要素項目に、Populationを構成比項目として円グラフを描画する
|
65
|
+
k=パラメータにPref項目を指定しているので、
|
66
|
+
Pref項目の値をx軸(横方向)に展開した1次元の円グラフ行列が描画される
|
67
|
+
title=パラメータでグラフのタイトルも指定している
|
68
|
+
|
69
|
+
dat2.csv
|
70
|
+
Pref,Age,Population
|
71
|
+
奈良,10,310504
|
72
|
+
奈良,20,552339
|
73
|
+
奈良,30,259034
|
74
|
+
奈良,40,450818
|
75
|
+
奈良,50,1231572
|
76
|
+
奈良,60,1215966
|
77
|
+
奈良,70,641667
|
78
|
+
北海道,10,310504
|
79
|
+
北海道,20,252339
|
80
|
+
北海道,30,859034
|
81
|
+
北海道,40,150818
|
82
|
+
北海道,50,9231572
|
83
|
+
北海道,60,4215966
|
84
|
+
北海道,70,341667
|
85
|
+
|
86
|
+
$ #{$cmd} i=dat2.csv k=Pref v=Population f=Age o=result2.html
|
87
|
+
|
88
|
+
例3) x軸上に表示する円グラフの最大数を1とする
|
89
|
+
|
90
|
+
$ #{$cmd} i=dat2.csv k=Pref v=Population f=Age o=result3.html cc=1
|
91
|
+
|
92
|
+
例4) 2次元の円グラフ行列を描画する
|
93
|
+
dat3.csvファイルのテーマパーク名を構成要素項目に、
|
94
|
+
Numberを構成比項目として円グラフを描画する
|
95
|
+
k=パラメータにGenderとAge項目を指定して、Gender項目の値をx軸(横方向)に、
|
96
|
+
Age項目の値をy軸(縦方向)に展開した2次元の円グラフ行列を描画する
|
97
|
+
|
98
|
+
dat3.csv
|
99
|
+
Gender,Age,テーマパーク名,Number
|
100
|
+
男性,30,デズニ,100
|
101
|
+
男性,30,UFJ,59
|
102
|
+
男性,30,梅屋敷,180
|
103
|
+
男性,40,デズニ,200
|
104
|
+
男性,40,UFJ,3
|
105
|
+
男性,40,梅屋敷,10
|
106
|
+
男性,50,デズニ,110
|
107
|
+
男性,50,UFJ,40
|
108
|
+
女性,30,梅屋敷,100
|
109
|
+
女性,30,デズニ,80
|
110
|
+
女性,30,UFJ,200
|
111
|
+
女性,40,デズニ,90
|
112
|
+
女性,40,UFJ,80
|
113
|
+
女性,40,梅屋敷,120
|
114
|
+
女性,50,デズニ,99
|
115
|
+
女性,50,UFJ,80
|
116
|
+
女性,50,梅屋敷,110
|
117
|
+
|
118
|
+
$ #{$cmd} i=dat3.csv k=Gender,Age v=Number f=テーマパーク名 o=result3.html title=性別と年代ごとのテーマパーク訪問回
|
119
|
+
EOF
|
120
|
+
exit
|
121
|
+
end
|
122
|
+
|
123
|
+
def ver()
|
124
|
+
$revision = "0" if $revision =~ /VERSION/
|
125
|
+
STDERR.puts "version #{$version} revision #{$revision}"
|
126
|
+
exit
|
127
|
+
end
|
128
|
+
|
129
|
+
def checkNull(key)
|
130
|
+
if key == nil then # 値の項目がnullの場合0にする
|
131
|
+
return "0"
|
132
|
+
end
|
133
|
+
return key
|
134
|
+
end
|
135
|
+
|
136
|
+
def checkNumeric(key)
|
137
|
+
unless key =~ /^([0-9]\d*|0)(\.\d+)?$/ # 棒グラフの値の整数チェック
|
138
|
+
raise "#{key} is not a numeric"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def getStrLength(key, maxLeg)
|
143
|
+
if key != nil
|
144
|
+
tmpSize = key.split(//).size
|
145
|
+
if tmpSize > maxLeg
|
146
|
+
return tmpSize
|
147
|
+
end
|
148
|
+
end
|
149
|
+
return maxLeg
|
150
|
+
end
|
151
|
+
|
152
|
+
# (dataStr, countKey, maxLeg) = makeZeroDemData(iFile, legendKey, pieShare)
|
153
|
+
# iFile(inputファイル)
|
154
|
+
# legendKey(キーの項目) (例:年代)
|
155
|
+
# pieShare(値の項目) (例:人口)
|
156
|
+
def makeZeroDemData(iFile, legendKey, pieShare)
|
157
|
+
dataStr = "var data = [\n{"
|
158
|
+
countKey = 0
|
159
|
+
maxLeg = 0
|
160
|
+
if iFile then
|
161
|
+
MCMD::Mcsvin.new("i=#{iFile}"){|csv|
|
162
|
+
csv.each{|flds| # 行処理
|
163
|
+
csv.names.each {|header| # 一行の項目分ループ
|
164
|
+
if header == "#{legendKey}" then # 棒グラフのキー項目
|
165
|
+
maxLeg = getStrLength(flds[header], maxLeg)
|
166
|
+
end
|
167
|
+
if header == pieShare # header読み込み(headerがvalueの時)
|
168
|
+
flds[header] = checkNull(flds[header])
|
169
|
+
checkNumeric(flds[header])
|
170
|
+
dataStr << "\"_#{flds[legendKey]}\":\"#{flds[pieShare]}\","
|
171
|
+
end
|
172
|
+
}
|
173
|
+
countKey +=1 # 行数カウント(0次元の場合はヘッダを除いたデータの行数)
|
174
|
+
}
|
175
|
+
dataStr = dataStr.chop
|
176
|
+
dataStr << "}\n];\n"
|
177
|
+
}
|
178
|
+
end
|
179
|
+
return dataStr, countKey, maxLeg
|
180
|
+
end
|
181
|
+
|
182
|
+
# (dataStr, xcount, keycount, maxLeg) = makeOneDemData(iFile, primKey, legendKey, pieShare)
|
183
|
+
# iFile(inputファイル)
|
184
|
+
# primKey(主キー) (例:Pref)
|
185
|
+
# legendKey(キーの項目) (例:年代)
|
186
|
+
# pieShare(値の項目) (例:人口)
|
187
|
+
def makeOneDemData(iFile, primKey, legendKey, pieShare)
|
188
|
+
dataStr = "var data = ["
|
189
|
+
xcount = {} # hash
|
190
|
+
keycount = {}
|
191
|
+
maxLeg = 0
|
192
|
+
if iFile then
|
193
|
+
MCMD::Mcsvin.new("i=#{iFile} k=#{primKey} -q"){|csv|
|
194
|
+
csv.each{|flds,top,bot| # 行処理
|
195
|
+
if top == true then # 先頭行の場合
|
196
|
+
dataStr << "\n{"
|
197
|
+
end
|
198
|
+
csv.names.each {|header| # 一行の項目分ループ
|
199
|
+
unless xcount.has_key?("#{flds[primKey]}")
|
200
|
+
xcount[flds[primKey]] = 1 # x軸の項目ハッシュ(カウント用)
|
201
|
+
end
|
202
|
+
if header == "#{primKey}" && xcount[flds[primKey]] == 1 # 1回目に出たときだけ作る
|
203
|
+
dataStr << "\"#{header}\":\"#{flds[header]}\","
|
204
|
+
xcount[flds[primKey]] +=1
|
205
|
+
end
|
206
|
+
if header == "#{legendKey}" then # 円グラフのキー項目
|
207
|
+
maxLeg = getStrLength(flds[header], maxLeg)
|
208
|
+
keycount[flds[legendKey]] = 1 # 円グラフのキー項目ハッシュ(カウント用)
|
209
|
+
end
|
210
|
+
if header == "#{pieShare}" then
|
211
|
+
flds[header] = checkNull(flds[header])
|
212
|
+
checkNumeric(flds[header])
|
213
|
+
dataStr << "\"_#{flds[legendKey]}\":\"#{flds[pieShare]}\"," # key項目:value項目値
|
214
|
+
end
|
215
|
+
}
|
216
|
+
if bot == true then # primKeyの同じ項目の終わり
|
217
|
+
dataStr = dataStr.chop
|
218
|
+
dataStr << "},"
|
219
|
+
end
|
220
|
+
}
|
221
|
+
dataStr = dataStr.chop
|
222
|
+
dataStr << "\n];\n"
|
223
|
+
}
|
224
|
+
end
|
225
|
+
return dataStr, xcount, keycount, maxLeg
|
226
|
+
end
|
227
|
+
|
228
|
+
# (dataStr, xcount, ycount, keycount, maxLeg) = makeTwoDemData(iFile, yBar, xBar, legendKey, pieShare)
|
229
|
+
# iFile(inputファイル)
|
230
|
+
# yBar(keyの値:行の項目)
|
231
|
+
# xBar(keyの値:列の項目)
|
232
|
+
# legendKey(キーの項目)
|
233
|
+
# pieShare(値の項目)
|
234
|
+
def makeTwoDemData(iFile, yBar, xBar, legendKey, pieShare)
|
235
|
+
dataStr = "var data = ["
|
236
|
+
xcount = {}
|
237
|
+
ycount = {}
|
238
|
+
maxLeg = 0
|
239
|
+
xycount = Hash.new{|h,k| h[k]=Hash.new(&h.default_proc)} # hashネスト宣言
|
240
|
+
keycount = {}
|
241
|
+
if iFile then
|
242
|
+
MCMD::Mcsvin.new("i=#{iFile} k=#{yBar},#{xBar} -q"){|csv|
|
243
|
+
csv.each{|flds,top,bot|
|
244
|
+
if top == true then # 先頭
|
245
|
+
dataStr << "\n{"
|
246
|
+
end
|
247
|
+
csv.names.each {|header| # 一行の項目分ループ
|
248
|
+
xcount[flds[xBar]] = 1 # x軸の項目ハッシュ(カウント用)
|
249
|
+
ycount[flds[yBar]] = 1 # y軸の項目ハッシュ(カウント用)
|
250
|
+
unless xycount[flds[xBar]].has_key?("#{flds[yBar]}")
|
251
|
+
xycount[flds[xBar]][flds[yBar]] = 1 # y軸の項目ハッシュ(カウント用)
|
252
|
+
end
|
253
|
+
if header == "#{xBar}" && xycount[flds[xBar]][flds[yBar]] == 1 then
|
254
|
+
dataStr << "\"#{xBar}\":\"#{flds[xBar]}\",\"#{yBar}\":\"#{flds[yBar]}\","
|
255
|
+
xycount[flds[xBar]][flds[yBar]] +=1
|
256
|
+
end
|
257
|
+
if header == "#{legendKey}" then # 円グラフのキー項目
|
258
|
+
maxLeg = getStrLength(flds[header], maxLeg)
|
259
|
+
keycount[flds[legendKey]] = 1 # 円グラフのキー項目ハッシュ
|
260
|
+
end
|
261
|
+
if header == "#{pieShare}" then # 円グラフの値項目
|
262
|
+
flds[header] = checkNull(flds[header])
|
263
|
+
checkNumeric(flds[header])
|
264
|
+
dataStr << "\"_#{flds[legendKey]}\":\"#{flds[pieShare]}\"," # key項目:value項目値
|
265
|
+
end
|
266
|
+
}
|
267
|
+
if bot == true then
|
268
|
+
dataStr = dataStr.chop
|
269
|
+
dataStr << "},"
|
270
|
+
end
|
271
|
+
}
|
272
|
+
dataStr = dataStr.chop
|
273
|
+
dataStr << "\n];\n"
|
274
|
+
}
|
275
|
+
end
|
276
|
+
return dataStr, xcount, ycount, keycount, maxLeg
|
277
|
+
end
|
278
|
+
|
279
|
+
help() if ARGV.size <= 0 or ARGV[0] == "--help"
|
280
|
+
ver() if ARGV[0] == "--version"
|
281
|
+
|
282
|
+
# ===================================================================
|
283
|
+
# パラメータ処理 f=,v=は必須
|
284
|
+
args=MCMD::Margs.new(ARGV,"i=,o=,title=,cc=,pr=,k=,f=,v=,--help","f=,v=")
|
285
|
+
input_args = ARGV.join(" ")
|
286
|
+
command = $cmd
|
287
|
+
|
288
|
+
# mcmdのメッセージは警告とエラーのみ
|
289
|
+
ENV["KG_VerboseLevel"] = "2" unless args.bool("-mcmdenv")
|
290
|
+
|
291
|
+
iFile = args.file("i=","r") # inputファイル名を取得(readable)
|
292
|
+
oFile = args.file("o=","w") # outputファイル名を取得(writable)
|
293
|
+
|
294
|
+
title = args.str("title=") # タイトル取得
|
295
|
+
pieRadius = args.int("pr=") # pieの半径
|
296
|
+
|
297
|
+
keyFld = args.field("k=", iFile) # key項目値取得
|
298
|
+
key1 = key2 = nil
|
299
|
+
if keyFld then
|
300
|
+
key1 = keyFld["names"][0] # 行キー
|
301
|
+
key2 = keyFld["names"][1] # 列キー
|
302
|
+
end
|
303
|
+
xMax = args.int("cc=") # x軸に並べる円グラフの数取得
|
304
|
+
legendKey = args.str("f=") # 円グラフの構成要素項目
|
305
|
+
pieShare = args.str("v=") # 円グラフの値のキー
|
306
|
+
|
307
|
+
args.field("v=", iFile) if args.keyValue["v="] # 項目値をヘッダからチェック
|
308
|
+
args.field("f=", iFile) if args.keyValue["f="] # 凡例項目をヘッダからチェック
|
309
|
+
|
310
|
+
if xMax then
|
311
|
+
if keyFld == nil or key2 then
|
312
|
+
raise "cc= takes only k=A"
|
313
|
+
end
|
314
|
+
if xMax < 1 then
|
315
|
+
raise "cc= takes more than 1"
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
# ===================================================================
|
320
|
+
# デフォルト値
|
321
|
+
# デフォルトは0次元グラフの設定値
|
322
|
+
pieRadius = 160 unless pieRadius # 半径
|
323
|
+
pieBelt = pieRadius # 表示半径
|
324
|
+
|
325
|
+
# グラフ用SVGのマージン
|
326
|
+
innerMargin = 10
|
327
|
+
|
328
|
+
svgWidth = pieRadius * 2 + innerMargin *2
|
329
|
+
svgHeight = pieRadius * 2 + innerMargin *2
|
330
|
+
|
331
|
+
# キャンパスのマージン
|
332
|
+
outerMarginL = 30
|
333
|
+
outerMarginR = 30
|
334
|
+
outerMarginT = 30
|
335
|
+
outerMarginB = 30
|
336
|
+
|
337
|
+
xMax = 5 unless xMax
|
338
|
+
|
339
|
+
# ===================================================================
|
340
|
+
# 1次元,2次元グラフの場合,円チャートの真ん中に文字入れる(keyText)
|
341
|
+
# pieBeltで円グラフの幅を変更し、真ん中を空白にする
|
342
|
+
keyText = ""
|
343
|
+
|
344
|
+
# デフォルト値設定(keyの指定があれば円チャートの真ん中に文字入れる)
|
345
|
+
# 2次元グラフ処理
|
346
|
+
if key1 != nil and key2 != nil then
|
347
|
+
pieBelt = pieRadius / 2
|
348
|
+
keyText= <<EOTE
|
349
|
+
svg.append("text")
|
350
|
+
.attr("dy", ".35em")
|
351
|
+
.style("text-anchor", "middle")
|
352
|
+
.text(function(d) { return d.#{key1} + d.#{key2}; });
|
353
|
+
EOTE
|
354
|
+
# 1次元グラフ処理
|
355
|
+
elsif key1 != nil and key2 == nil then
|
356
|
+
pieBelt = pieRadius / 2
|
357
|
+
keyText = <<EOTE
|
358
|
+
svg.append("text")
|
359
|
+
.attr("dy", ".35em")
|
360
|
+
.style("text-anchor", "middle")
|
361
|
+
.text(function(d) { return d.#{key1}; });
|
362
|
+
EOTE
|
363
|
+
end
|
364
|
+
|
365
|
+
# ============
|
366
|
+
# INPUTファイルの読み込み
|
367
|
+
xcount = {}
|
368
|
+
ycount = {}
|
369
|
+
keycount = {}
|
370
|
+
xNum = 1
|
371
|
+
yNum = 1
|
372
|
+
keyNum = 0
|
373
|
+
pieMargin = 10
|
374
|
+
dataStr = ""
|
375
|
+
maxLeg = 0
|
376
|
+
# 2次元グラフ処理
|
377
|
+
if key1 != nil and key2 != nil then
|
378
|
+
(dataStr, xcount, ycount, keycount, maxLeg) = makeTwoDemData(iFile, key1, key2, legendKey, pieShare)
|
379
|
+
xNum = xcount.length # x軸の円グラフ数
|
380
|
+
yNum = ycount.length # y軸の円グラフ数
|
381
|
+
keyNum = keycount.length # 凡例用キー数
|
382
|
+
# 1次元グラフ処理
|
383
|
+
elsif key1 != nil and key2 == nil then
|
384
|
+
(dataStr, xcount, countKey, maxLeg) = makeOneDemData(iFile, key1, legendKey, pieShare)
|
385
|
+
xNum = xcount.length #主キーの数
|
386
|
+
keyNum = countKey.length #keyの数
|
387
|
+
if xNum > xMax
|
388
|
+
yNum = xNum / xMax
|
389
|
+
xNum = xMax
|
390
|
+
end
|
391
|
+
# 0次元グラフ処理
|
392
|
+
elsif key1 == nil and key2 == nil then
|
393
|
+
(dataStr, countKey, maxLeg) = makeZeroDemData(iFile, legendKey, pieShare)
|
394
|
+
keyNum = countKey
|
395
|
+
end
|
396
|
+
legendWidth = maxLeg * 10 + 30 + 30
|
397
|
+
xCampusSize = xNum * svgWidth + legendWidth + outerMarginL + outerMarginR * 2
|
398
|
+
yCampusSize = yNum * svgHeight + outerMarginT + outerMarginB
|
399
|
+
outAxisHeight = yCampusSize - outerMarginT - outerMarginB
|
400
|
+
|
401
|
+
# ============
|
402
|
+
# 文字列作成
|
403
|
+
colorStyle = ""
|
404
|
+
outLineStr = ""
|
405
|
+
legendStr = ""
|
406
|
+
svgStr = ""
|
407
|
+
|
408
|
+
if key1 != nil and key2 != nil then
|
409
|
+
#2次元グラフの場合
|
410
|
+
colorStyle = <<EOT
|
411
|
+
color.domain(d3.keys(data[0]).filter(function(key) { if(key !== "#{key1}" && key !== "#{key2}") { return key.substr(1); }}));
|
412
|
+
EOT
|
413
|
+
#0,1次元グラフの場合
|
414
|
+
else
|
415
|
+
colorStyle = <<EOT
|
416
|
+
color.domain(d3.keys(data[0]).filter(function(key) { if(key !== "#{key1}") { return key.substr(1);} }));
|
417
|
+
EOT
|
418
|
+
end
|
419
|
+
|
420
|
+
# ============
|
421
|
+
# TITLE用文字列作成
|
422
|
+
titleMargin = 20
|
423
|
+
tmpX = (xCampusSize - outerMarginR * 2 - legendWidth) / 2
|
424
|
+
titleStr = ""
|
425
|
+
if title then
|
426
|
+
titleStr = <<EOT
|
427
|
+
var title = d3.select("svg").append("text")
|
428
|
+
.attr("x", (#{tmpX}))
|
429
|
+
.attr("y", "#{titleMargin}")
|
430
|
+
.attr("text-anchor", "middle")
|
431
|
+
.style("font-size", "13pt")
|
432
|
+
.text("#{title}");
|
433
|
+
EOT
|
434
|
+
end
|
435
|
+
|
436
|
+
# 2次元
|
437
|
+
if key1 != nil and key2 != nil then
|
438
|
+
xdomain = xcount.keys
|
439
|
+
ydomain = ycount.keys
|
440
|
+
xAxistransY = outerMarginL + outAxisHeight
|
441
|
+
|
442
|
+
outLineStr = <<EOT
|
443
|
+
var out_x = d3.scale.ordinal()
|
444
|
+
.domain(#{xdomain})
|
445
|
+
.rangeBands([0, out_axis_width]);
|
446
|
+
|
447
|
+
var out_x_axis = d3.svg.axis()
|
448
|
+
.scale(out_x) //スケールの設定
|
449
|
+
.orient("bottom");
|
450
|
+
|
451
|
+
var out_xaxis = d3.select("svg")
|
452
|
+
.append("g")
|
453
|
+
.attr("class", "axis")
|
454
|
+
.attr("transform", "translate(" + #{outerMarginL} + "," + #{xAxistransY} + ")")
|
455
|
+
.call(out_x_axis);
|
456
|
+
|
457
|
+
var out_y = d3.scale.ordinal()
|
458
|
+
.domain(#{ydomain})
|
459
|
+
.rangeBands([0, out_axis_height]);
|
460
|
+
|
461
|
+
var out_y_axis = d3.svg.axis()
|
462
|
+
.scale(out_y)
|
463
|
+
.orient("left");
|
464
|
+
|
465
|
+
var out_yaxis = d3.select("svg")
|
466
|
+
.append("g")
|
467
|
+
.attr("class", "axis")
|
468
|
+
.attr("transform", "translate(" + #{outerMarginL} + "," + #{outerMarginT} + ")") // x方向,y方向
|
469
|
+
.call(out_y_axis);
|
470
|
+
EOT
|
471
|
+
end
|
472
|
+
|
473
|
+
textHeight = 30
|
474
|
+
legendMargin = 30
|
475
|
+
legendHeight = keyNum * textHeight + legendMargin
|
476
|
+
legendTextX = textHeight + 4
|
477
|
+
legendTextY = textHeight / 2
|
478
|
+
|
479
|
+
legendStr = <<EOT
|
480
|
+
var legend = d3.select("svg").append("svg")
|
481
|
+
.attr("class", "legend")
|
482
|
+
.attr("width", "#{legendWidth}")
|
483
|
+
.attr("height", "#{legendHeight}")
|
484
|
+
.attr("x", leg_x)
|
485
|
+
.selectAll("g")
|
486
|
+
.data(color.domain().slice())
|
487
|
+
.enter().append("g")
|
488
|
+
.attr("transform", function(d, i) { return "translate(0, " + (i*#{textHeight}+#{legendMargin}) + ")"; });
|
489
|
+
|
490
|
+
legend.append("rect")
|
491
|
+
.attr("width", #{textHeight})
|
492
|
+
.attr("height", #{textHeight})
|
493
|
+
.style("fill", color);
|
494
|
+
|
495
|
+
legend.append("text")
|
496
|
+
.attr("x", #{legendTextX})
|
497
|
+
.attr("y", #{legendTextY})
|
498
|
+
.attr("dy", ".35em")
|
499
|
+
.text(function(d) { return d.substr(1); });
|
500
|
+
EOT
|
501
|
+
|
502
|
+
tmpX = innerMargin + pieRadius
|
503
|
+
svgStr = <<EOT
|
504
|
+
// 描画領域を作成(svgをデータ行分つくる)
|
505
|
+
var svg = d3.select("svg").selectAll(".pie")
|
506
|
+
.data(data)
|
507
|
+
.enter().append("svg")
|
508
|
+
.attr("width", svgWidth)
|
509
|
+
.attr("height", svgHeight)
|
510
|
+
.attr("x", function(d,i) {
|
511
|
+
if (i < #{xNum}) { return svgWidth*i + outer_margin.left ; }
|
512
|
+
else { return svgWidth*(i % #{xNum}) + outer_margin.left; } }) // width+0, width+1,...width+xNum, width+0..
|
513
|
+
.attr("y", function(d,i) { return outer_margin.top + svgHeight * parseInt(i/#{xNum}); }) // i/3を整数値で取得
|
514
|
+
.attr("class", "pie")
|
515
|
+
.append("g")
|
516
|
+
.attr("transform", "translate(#{tmpX},#{tmpX})");
|
517
|
+
EOT
|
518
|
+
|
519
|
+
comStr = <<EOT
|
520
|
+
var command = d3.select("body").append("div")
|
521
|
+
.attr("x", "430")
|
522
|
+
.attr("y", "20")
|
523
|
+
.attr("text-anchor", "left")
|
524
|
+
.style("font-size", "10pt")
|
525
|
+
.text("#{command} #{input_args}");
|
526
|
+
EOT
|
527
|
+
|
528
|
+
# ============
|
529
|
+
# 描画
|
530
|
+
html = STDOUT
|
531
|
+
if oFile != nil then
|
532
|
+
html = open(oFile,"w")
|
533
|
+
end
|
534
|
+
|
535
|
+
html.puts <<HEOF
|
536
|
+
<!DOCTYPE html>
|
537
|
+
<html>
|
538
|
+
<head>
|
539
|
+
<meta charset="utf-8">
|
540
|
+
<style>
|
541
|
+
|
542
|
+
body {
|
543
|
+
font: 10px sans-serif;
|
544
|
+
}
|
545
|
+
|
546
|
+
svg {
|
547
|
+
padding: 10px 0 0 10px;
|
548
|
+
}
|
549
|
+
|
550
|
+
.arc {
|
551
|
+
stroke: #fff;
|
552
|
+
}
|
553
|
+
|
554
|
+
#tooltip {
|
555
|
+
position: absolute;
|
556
|
+
width: 150px;
|
557
|
+
height: auto;
|
558
|
+
padding: 10px;
|
559
|
+
background-color: white;
|
560
|
+
-webkit-border-radius: 10px;
|
561
|
+
-moz-border-radius: 10px;
|
562
|
+
border-radius: 5px;
|
563
|
+
-webkit-box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
|
564
|
+
-moz-box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
|
565
|
+
box-shadow: 4px 4px 10px rgba(0,0,0,0.4);
|
566
|
+
pointer-events: none;
|
567
|
+
}
|
568
|
+
|
569
|
+
#tooltip.hidden {
|
570
|
+
display: none;
|
571
|
+
}
|
572
|
+
|
573
|
+
#tooltip p {
|
574
|
+
margin: 0;
|
575
|
+
font-family: sans-serif;
|
576
|
+
font-size: 10px;
|
577
|
+
line-height: 14px;
|
578
|
+
}
|
579
|
+
|
580
|
+
.axis text {
|
581
|
+
font: 11px
|
582
|
+
font-family: sans-serif;
|
583
|
+
}
|
584
|
+
|
585
|
+
.axis path,
|
586
|
+
.axis line {
|
587
|
+
fill: none;
|
588
|
+
stroke: #000;
|
589
|
+
shape-rendering: crispEdges;
|
590
|
+
}
|
591
|
+
</style>
|
592
|
+
</head>
|
593
|
+
<body>
|
594
|
+
<script>
|
595
|
+
#{ViewJs::d3jsMin()}
|
596
|
+
|
597
|
+
#{dataStr}
|
598
|
+
// キャンパスサイズ
|
599
|
+
var out_w = #{xCampusSize};
|
600
|
+
var out_h = #{yCampusSize};
|
601
|
+
|
602
|
+
// キャンパスのマージン
|
603
|
+
var outer_margin = {top: #{outerMarginT}, right: #{outerMarginR}, bottom: #{outerMarginB}, left: #{outerMarginL}};
|
604
|
+
|
605
|
+
// 凡例の位置
|
606
|
+
var leg_x = out_w - outer_margin.right - #{legendWidth};
|
607
|
+
|
608
|
+
// 外側のx軸サイズ
|
609
|
+
var out_axis_width = out_w - outer_margin.left - outer_margin.right * 2 - #{legendWidth};
|
610
|
+
var out_axis_height = out_h - outer_margin.top - outer_margin.bottom;
|
611
|
+
|
612
|
+
var radius = #{pieRadius};
|
613
|
+
var pieBelt = #{pieBelt};
|
614
|
+
|
615
|
+
var svgWidth = #{svgWidth};
|
616
|
+
var svgHeight = #{svgHeight};
|
617
|
+
|
618
|
+
var vbox_x = 0;
|
619
|
+
var vbox_y = 0;
|
620
|
+
var vbox_default_width = vbox_width = out_w;
|
621
|
+
var vbox_default_height = vbox_height = out_h;
|
622
|
+
|
623
|
+
var outline = d3.select("body").append("svg")
|
624
|
+
.attr("width", out_w )
|
625
|
+
.attr("height", out_h)
|
626
|
+
.attr("viewBox", "" + vbox_x + " " + vbox_y + " " + vbox_width + " " + vbox_height);
|
627
|
+
|
628
|
+
var drag = d3.behavior.drag().on("drag", function(d) {
|
629
|
+
vbox_x -= d3.event.dx;
|
630
|
+
vbox_y -= d3.event.dy;
|
631
|
+
return outline.attr("translate", "" + vbox_x + " " + vbox_y);
|
632
|
+
});
|
633
|
+
outline.call(drag);
|
634
|
+
zoom = d3.behavior.zoom().on("zoom", function(d) {
|
635
|
+
var befere_vbox_width, before_vbox_height, d_x, d_y;
|
636
|
+
befere_vbox_width = vbox_width;
|
637
|
+
before_vbox_height = vbox_height;
|
638
|
+
vbox_width = vbox_default_width * d3.event.scale;
|
639
|
+
vbox_height = vbox_default_height * d3.event.scale;
|
640
|
+
d_x = (befere_vbox_width - vbox_width) / 2;
|
641
|
+
d_y = (before_vbox_height - vbox_height) / 2;
|
642
|
+
vbox_x += d_x;
|
643
|
+
vbox_y += d_y;
|
644
|
+
return outline.attr("viewBox", "" + vbox_x + " " + vbox_y + " " + vbox_width + " " + vbox_height);
|
645
|
+
});
|
646
|
+
outline.call(zoom);
|
647
|
+
|
648
|
+
#{titleStr}
|
649
|
+
|
650
|
+
//outlineのX軸
|
651
|
+
//outlineのY軸
|
652
|
+
#{outLineStr}
|
653
|
+
|
654
|
+
var innerRadius = radius - pieBelt;
|
655
|
+
|
656
|
+
// make colorlist
|
657
|
+
var color = d3.scale.category10();
|
658
|
+
var color2 = d3.scale.category20b();
|
659
|
+
var color3 = d3.scale.category20();
|
660
|
+
var color4 = d3.scale.category20c();
|
661
|
+
var cl = color.range();
|
662
|
+
var colorList = cl.concat(color.range());
|
663
|
+
colorList = colorList.concat(color2.range());
|
664
|
+
colorList = colorList.concat(color3.range());
|
665
|
+
colorList = colorList.concat(color4.range());
|
666
|
+
|
667
|
+
// set color
|
668
|
+
var color = d3.scale.ordinal()
|
669
|
+
.range(colorList);
|
670
|
+
|
671
|
+
// set domain to d3.scale.ordinal
|
672
|
+
#{colorStyle}
|
673
|
+
|
674
|
+
// set inner radius and outer radius of pie chart
|
675
|
+
var arc = d3.svg.arc()
|
676
|
+
.outerRadius(radius) //pie outer radius
|
677
|
+
.innerRadius(innerRadius); //pie inner radius
|
678
|
+
|
679
|
+
// make pie chart
|
680
|
+
var pie = d3.layout.pie()
|
681
|
+
.sort(null) // invalidate sort
|
682
|
+
.value(function(d) { return d.value; }); // set value
|
683
|
+
|
684
|
+
// process of data
|
685
|
+
data.forEach(function(d) {
|
686
|
+
d.datasets = color.domain().map(function(d1) {
|
687
|
+
//convert value from string to numeric(+)
|
688
|
+
return {name: d1, value: +d[d1]};
|
689
|
+
});
|
690
|
+
});
|
691
|
+
|
692
|
+
#{svgStr}
|
693
|
+
|
694
|
+
svg.selectAll(".arc")
|
695
|
+
.data(function(d) { return pie(d.datasets); })
|
696
|
+
.enter().append("path")
|
697
|
+
.attr("class", "arc")
|
698
|
+
.attr("d", arc)
|
699
|
+
.style("fill", function(d) { return color(d.data.name); })
|
700
|
+
.on("mouseover", function(d) {
|
701
|
+
d3.select("#tooltip")
|
702
|
+
.style("left", (d3.event.pageX+10) +"px")
|
703
|
+
.style("top", (d3.event.pageY-10) +"px")
|
704
|
+
.select("#value")
|
705
|
+
.text( d.data.name.substr(1) + " : " + d.data.value );
|
706
|
+
|
707
|
+
d3.select("#tooltip").classed("hidden",false);
|
708
|
+
})
|
709
|
+
.on("mouseout", function() {
|
710
|
+
d3.select("#tooltip").classed("hidden", true);
|
711
|
+
});
|
712
|
+
|
713
|
+
#{keyText}
|
714
|
+
|
715
|
+
#{legendStr}
|
716
|
+
|
717
|
+
var tooltip = d3.select("body").append("div")
|
718
|
+
.attr("id", "tooltip")
|
719
|
+
.attr("class", "hidden")
|
720
|
+
.append("p")
|
721
|
+
.attr("id", "value")
|
722
|
+
.text("0");
|
723
|
+
|
724
|
+
#{comStr}
|
725
|
+
</script>
|
726
|
+
</body>
|
727
|
+
</html>
|
728
|
+
|
729
|
+
HEOF
|
730
|
+
|
731
|
+
# 終了メッセージ
|
732
|
+
MCMD::endLog(args.cmdline)
|
733
|
+
|