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