nysol-view 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
|