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.
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
+