nysol-view 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
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
+