nysol-view 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/m2gv.rb ADDED
@@ -0,0 +1,812 @@
1
+ #!/usr/bin/env ruby
2
+ #-*- coding: utf-8 -*-
3
+
4
+ require "rubygems"
5
+ require "nysol/mcmd"
6
+ require "set"
7
+
8
+ # ver="1.0" # 初期リリース 2016/12/13
9
+ $cmd=$0.sub(/.*\//,"")
10
+
11
+ $version=1.0
12
+ $revision="###VERSION###"
13
+
14
+ def help
15
+
16
+ STDERR.puts <<EOF
17
+ ------------------------
18
+ #{$cmd} version #{$version}
19
+ ------------------------
20
+ 概要) CSVによるグラフ構造データをDOTフォーマットで出力する
21
+
22
+ 書式1) #{$cmd} [type=flat|nest] [k=] [ni=] [nf=] [nv=] [nc=] [col=] [nl=] [nw=]
23
+ [-clusterLabel] [-noiso] ei= ef= [ev=] [er=] [el=] [-d] [o=]
24
+
25
+ type= : グラフのタイプ(省略時はflat)
26
+ flat: key項目をクラスタとした木構造グラフ
27
+ nest: 木構造であることを前提にした入れ子構造グラフ(データが木構造でない場合の描画は不定)
28
+ k= : 入れ子グラフのクラスタ項目名(ni=を指定した場合は同じ項目名でなければならない)
29
+ 複数項目指定不可
30
+ k=を省略すればtype=に関わらずクラスタを伴わない普通のグラフ描画となる。
31
+
32
+ ni= : 節点集合ファイル名
33
+ nf= : 節点ID項目名
34
+ nv= : 節点の大きさ項目名(この値に応じて節点の楕円の大きさが変化する,1項目のみ指定可)。
35
+ nc= : 節点カラー項目名(この値に応じて枠線カラーが変化する,1項目のみ指定可)。
36
+ カラーは、RGBを16進数2桁づつ6桁で表現する。ex) FF00FF:紫
37
+ さらに最後に2桁追加すればそれは透過率となる。
38
+ nl= : ノードラベルの項目(複数指定したら"_"で区切って結合される)
39
+ 省略すればnf=でしてした項目をラベルとする。
40
+ nw= : 節点の枠線の幅を指定する(デフォルトは1)
41
+ -clusterLabel : k=を指定して入れ子グラフを作成する場合、クラスタのラベルも表示する
42
+ -noiso : 孤立節点(隣接節点のない節点で、本コマンドではni=に出てきてei=に出てこない節点のこと)は出力しない
43
+
44
+ ei= : 枝集合ファイル名
45
+ ef= : 開始節点ID項目名,終了節点ID項目名
46
+ ev= : 枝の幅項目名
47
+ ec= : 枝の色を表す項目(色の値はnc=と同じ)
48
+ ed= : 枝の矢印を表す項目(-dの指定、未指定に関わらず優先される)
49
+ 値としては、F,B,W,N,nullの5つの値のいずれかでなければならない。
50
+ ef=e1,e2とした場合、それぞれで描画される矢印は以下の通り。
51
+ F: e1->e2, B: e1<-e2, W: e1<->e2, N:e1-e2(矢印なし),null:デフォルト
52
+ デフォルトは、-dが指定されていればF、-dの指定がなければNとなる。
53
+ el= : エッジラベルの項目(複数指定したら"_"で区切って結合される)
54
+ 省略すれば、エッジラベルは表示されない。
55
+
56
+ -d : 有向グラフと見なす。"edge [dir=none]"を記述する。
57
+ o= : 出力ファイル名
58
+
59
+ -h,--help : ヘルプの表示
60
+
61
+ 基本例)
62
+ $ cat edge.csv
63
+ e1,e2,v
64
+ a,b,11
65
+ a,c,20
66
+ b,d,11
67
+ d,e,8
68
+ c,e,9
69
+
70
+ $ #{$cmd} ei=edge.csv ef=e1,e2 ev=v el=v er=20 o=result.dot
71
+ $ cat result.dot
72
+ digraph G {
73
+ edge [dir=none]
74
+ n_2 [label="a" style="setlinewidth(1)" ]
75
+ n_3 [label="b" style="setlinewidth(1)" ]
76
+ n_4 [label="c" style="setlinewidth(1)" ]
77
+ n_5 [label="d" style="setlinewidth(1)" ]
78
+ n_6 [label="e" style="setlinewidth(1)" ]
79
+ n_2 -> n_3 [label="11" style="setlinewidth(5.75)" ]
80
+ n_2 -> n_4 [label="20" style="setlinewidth(20)" ]
81
+ n_3 -> n_5 [label="11" style="setlinewidth(5.75)" ]
82
+ n_4 -> n_6 [label="9" style="setlinewidth(2.583333333)" ]
83
+ n_5 -> n_6 [label="8" style="setlinewidth(1)" ]
84
+ }
85
+
86
+ ネストグラフの例)
87
+ $ cat edge4.csv
88
+ cluster,node1,node2,support
89
+ #1_1,a,b,0.1
90
+ #1_2,d,e,0.1
91
+ #2_1,#1_1,c,0.2
92
+ #3_1,#1_2,#2_1,0.3
93
+ #3_1,#2_1,f,0.4
94
+
95
+ $ #{$cmd} mgv.rb type=flat k=cluster ei=edge4.csv ef=node1,node2 o=result.dot
96
+ # fdpはgraphVizのコマンド, nested graphはdotコマンドでは描画できない
97
+ $ fdp -Tpdf result.dot >result.pdf
98
+ $ open result.pdf
99
+ $ cat result.dot
100
+ digraph G {
101
+ edge [dir=none]
102
+ subgraph cluster_1 {
103
+ n_5 [label="a" style="setlinewidth(1)" ]
104
+ n_6 [label="b" style="setlinewidth(1)" ]
105
+ n_5 -> n_6 []
106
+ }
107
+ subgraph cluster_2 {
108
+ n_8 [label="d" style="setlinewidth(1)" ]
109
+ n_9 [label="e" style="setlinewidth(1)" ]
110
+ n_8 -> n_9 []
111
+ }
112
+ subgraph cluster_3 {
113
+ cluster_1 []
114
+ n_7 [label="c" style="setlinewidth(1)" ]
115
+ cluster_1 -> n_7 []
116
+ }
117
+ subgraph cluster_4 {
118
+ cluster_2 []
119
+ cluster_3 []
120
+ n_10 [label="f" style="setlinewidth(1)" ]
121
+ cluster_2 -> cluster_3 []
122
+ cluster_3 -> n_10 []
123
+ }
124
+ }
125
+ EOF
126
+ exit
127
+ end
128
+ def ver()
129
+ $revision ="0" if $revision =~ /VERSION/
130
+ STDERR.puts "version #{$version} revision #{$revision}"
131
+ exit
132
+ end
133
+
134
+ help() if ARGV.size <= 0 or ARGV[0]=="--help"
135
+ ver() if ARGV[0]=="--version"
136
+
137
+ # ===================================================================
138
+ # パラメータ処理
139
+ args=MCMD::Margs.new(ARGV,"type=,k=,ni=,nf=,nv=,nc=,ei=,ef=,ev=,ec=,o=,nl=,el=,ed=,-d,nw=,-clusterLabel,-noiso,-debug","ei=,ef=")
140
+
141
+ # mcmdのメッセージは警告とエラーのみ
142
+ ENV["KG_VerboseLevel"]="2" unless args.bool("-mcmdenv")
143
+
144
+ type = args. str("type=","flat") # グラフタイプ
145
+ if type!="nest" and type!="flat"
146
+ raise "type= takes `nest' or `flat'"
147
+ end
148
+
149
+ ni = args. file("ni=","r") # nodeファイル名
150
+ nf = args.field("nf=", ni) # nodeID項目名
151
+ nv = args.field("nv=", ni) # node value項目名
152
+ nc = args.field("nc=", ni) # node color項目名
153
+ nl = args.field("nl=", ni) # nodeラベル項目名
154
+ nw =args.int("nw=",1,1) # nodeの枠線の太さ
155
+ unless args.keyValue["ni="]
156
+ if args.keyValue["nf="] or args.keyValue["nv="] or args.keyValue["nc="] or args.keyValue["-nl"] or args.keyValue["-cl"] or args.keyValue["nw="]
157
+ raise "nf=,nv=,nc=,-nl,-cl cannot be specified without ni="
158
+ end
159
+ end
160
+
161
+ if args.keyValue["ni="]
162
+ #unless args.keyValue["nf="] or args.keyValue["nv="]
163
+ unless args.keyValue["nf="]
164
+ raise "nf= must be specified when ni= is given"
165
+ end
166
+ end
167
+
168
+ ei = args. file("ei=","r") # edgeファイル名
169
+ key= args.field("k=", ei,nil,1,1) # nested graph クラスタ項目
170
+ key=key["names"][0] if key
171
+ ef = args.field("ef=", ei) # edge始点node項目名,終了節点項目名
172
+ ev = args.field("ev=", ei) # edge value項目名
173
+ ec = args.field("ec=", ei) # edge color項目名
174
+ ed = args.field("ed=", ei) # edge direction項目名
175
+ el = args.field("el=", ei) # edgeラベル項目
176
+ ef1=ef["names"][0]
177
+ ef2=ef["names"][1]
178
+ if ef1==nil or ef2==nil then
179
+ raise "ef= takes two field names"
180
+ end
181
+
182
+ if ni
183
+ nf=nf["names"][0]
184
+ nv=nv["names"][0] if nv
185
+ nc=nc["names"][0] if nc
186
+ end
187
+
188
+ ev=ev["names"][0] if ev
189
+ ed=ed["names"][0] if ed
190
+ ec=ec["names"][0] if ec
191
+
192
+ directed = args. bool("-d") #有向グラフ
193
+ directedStr="edge []"
194
+ directedStr="edge [dir=none]" unless directed
195
+ oFile = args.file("o=","w") # 出力ファイル名
196
+
197
+ clusterLabel=args.bool("-clusterLabel")
198
+ noiso=args.bool("-noiso")
199
+
200
+ # edgeデータからnested graphのtree構造を作る
201
+ # clusterのみの構造を作る
202
+ def mkTree(iFile,oFile)
203
+ temp=MCMD::Mtemp.new
204
+ xxroot =temp.file
205
+ xxbase=[]
206
+ xxbase << temp.file
207
+
208
+ # #{iFile}
209
+ # key,nam%0,keyNum,num,nv,nc
210
+ # #2_1,#1_1,4,1,6,1
211
+ # #2_1,#1_2,4,2,0.9999999996,1
212
+ xxiFile1=temp.file
213
+ xxiFile2=temp.file
214
+ xxkey =temp.file
215
+ xxnum =temp.file
216
+ xxleaf =temp.file
217
+ xxcheck =temp.file
218
+
219
+ # keyNumとnum項目のuniqリストを作り、お互いの包含関係でrootノードとleafノードを識別する。
220
+ system "mcut f=keyNum,num i=#{iFile} | msortf f=keyNum o=#{xxiFile1}"
221
+ system "mcut f=keyNum i=#{xxiFile1} | muniq k=keyNum o=#{xxkey}"
222
+ system "mcut f=num i=#{xxiFile1} | muniq k=num o=#{xxnum}"
223
+
224
+ # leaf nodesの選択
225
+ system "mcommon k=num K=keyNum m=#{xxkey} -r i=#{xxnum} | mcut f=num o=#{xxleaf}"
226
+
227
+ # root nodesの選択
228
+ system "mcommon k=keyNum K=num m=#{xxnum} -r i=#{xxkey} | mcut f=keyNum:node0 o=#{xxbase[0]}"
229
+
230
+ # leaf nodeの構造を知る必要はないので入力ファイルのnodeからleafを除外
231
+ system "mcommon k=num m=#{xxleaf} -r i=#{xxiFile1} o=#{xxiFile2}"
232
+
233
+ # root nodesファイルから親子関係noodeを次々にjoinしていく
234
+ # xxbase0 : root nodes
235
+ # node0%0
236
+ # 3
237
+ # 4
238
+ # xxbase1
239
+ # node0%0,node1
240
+ # 3,
241
+ # 4,1
242
+ # 4,2
243
+ # xxbase2
244
+ # node0,node1%0,node2
245
+ # 3,,
246
+ # 4,1,
247
+ # 4,2,
248
+ # join項目(node2)の非null項目が0件で終了
249
+
250
+ # system "cat #{xxbase[0]}"
251
+ # node0%0
252
+ # #1_3
253
+ # #2_1
254
+ i=0
255
+ depth=nil
256
+ while true
257
+ # puts "xxbase[#{i}]"
258
+ # system "cat #{xxbase[i]}"
259
+ xxbase << temp.file
260
+ system "mnjoin k=node#{i} K=keyNum m=#{xxiFile2} f=num:node#{i+1} -n i=#{xxbase[i]} o=#{xxbase[i+1]}"
261
+ system "mdelnull f=node#{i+1} i=#{xxbase[i+1]} o=#{xxcheck}"
262
+ size=MCMD::mrecount("i=#{xxcheck}")
263
+ if size==0
264
+ system "msortf f=* i=#{xxbase[i]} o=#{oFile}"
265
+ depth=i+1
266
+ break
267
+ end
268
+ i+=1
269
+ end
270
+ # system "cat #{oFile}"
271
+ # node0%0,node1
272
+ # 3,
273
+ # 4,1
274
+ # 4,2
275
+ #puts "depth=#{depth}"
276
+ # depth=2
277
+ return depth
278
+ end
279
+
280
+ # edgeデータからflat graph構造を作る
281
+ # clusterのみの構造を作る
282
+ def mkFlat(iFile,oFile)
283
+ f=""
284
+ f << "mcut f=keyNum:node0 i=#{iFile} |"
285
+ f << "muniq k=node0 o=#{oFile}"
286
+ system(f)
287
+ return 1
288
+ end
289
+
290
+
291
+ def keyBreakDepth(newFlds,oldFlds)
292
+ return 0 unless oldFlds # 先頭行
293
+ (0...newFlds.size).each{|i|
294
+ return i if newFlds[i]!=oldFlds[i]
295
+ }
296
+ end
297
+
298
+ ##########################
299
+ # creating tree structure
300
+ #
301
+ # digraph G {edge [dir=none]
302
+ # subgraph n_3 {
303
+ # ##3
304
+ # }
305
+ # subgraph n_4 {
306
+ # ##4
307
+ # subgraph n_1 {
308
+ # ##1
309
+ # }
310
+ # subgraph n_2 {
311
+ # ##2
312
+ # }
313
+ # }
314
+ # }
315
+ def dotTree(iFile,depth,header,footer,oFile)
316
+ File.open(oFile,"w"){|fpw|
317
+ fpw.puts header
318
+ fpw.puts "##0" # 孤立node(keyがnullのnode)
319
+ iCSV=MCMD::Mcsvin.new("i=#{iFile} -array")
320
+ oldFlds=nil
321
+ stack=[] # "subgraph {"に対応する終了括弧"}"のスタック
322
+ lastDepth=0 # 前行で出力されたsubgraphの深さ
323
+ iCSV.each{|newFlds|
324
+ next if newFlds[0]=="0" # 孤立nodeはスキップ
325
+ kbd=keyBreakDepth(newFlds,oldFlds) # 前行に比べてどの位置でkeybreakがあったか
326
+ (0...lastDepth-kbd).each{|i| # 前行より深さが戻った分終了括弧"}"を出力
327
+ fpw.puts stack.pop
328
+ }
329
+ # keybreakした位置から最深の位置までsubgraphを出力
330
+ (kbd...depth).each{|i|
331
+ break unless newFlds[i] # nullはその深さにsubgraphなしということ
332
+ indent=' '*(i+1) # インデント
333
+ fpw.puts "#{indent}subgraph cluster_#{newFlds[i]} {"
334
+ fpw.puts "###{newFlds[i]}"
335
+ stack.push("#{indent}}") # 対応する終了括弧をスタックしておく
336
+ lastDepth=i+1 # 出力した最深位置の更新
337
+ }
338
+ oldFlds=newFlds
339
+ }
340
+ (0...lastDepth).each{|i| # 深さが戻った分終了括弧"}"を出力
341
+ fpw.puts stack.pop
342
+ }
343
+ fpw.puts footer
344
+ }
345
+ end
346
+
347
+ # 全てをsubgraphを付けずにnodeとして出力する
348
+ def dotPlain(iFile,depth,header,footer,oFile)
349
+ File.open(oFile,"w"){|fpw|
350
+ fpw.puts header
351
+ fpw.puts "##0" # 孤立node(keyがnullのnode)
352
+ iCSV=MCMD::Mcsvin.new("i=#{iFile} -array")
353
+ iCSV.each{|flds|
354
+ flds.each{|fld|
355
+ break if fld==nil or fld==""
356
+ fpw.puts "###{fld}"
357
+ }
358
+ }
359
+ fpw.puts footer
360
+ }
361
+ end
362
+
363
+ def replace(treeFile,nodePath,edgePath,clusterLabel,oFile)
364
+ File.open(oFile,"w"){|dot|
365
+ File.open(treeFile,"r"){|tree|
366
+ while line=tree.gets
367
+ if line[0]=="#"
368
+ num=line.strip.sub("##","")
369
+ # 孤立nodeのclusterラベル(null)は出力しない
370
+ if clusterLabel and File.exist?("#{nodePath}/L_#{num}") and num!="0"
371
+ File.open("#{nodePath}/L_#{num}","r"){|label|
372
+ dot.puts label.read
373
+ }
374
+ end
375
+ if File.exist?("#{nodePath}/c_#{num}") # このifにマッチしないケースはないけど念のため
376
+ File.open("#{nodePath}/c_#{num}","r"){|node|
377
+ dot.puts node.read
378
+ }
379
+ end
380
+ if File.exist?("#{edgePath}/c_#{num}") # 孤立nodeはedgeなしなのでマッチする
381
+ File.open("#{edgePath}/c_#{num}","r"){|edge|
382
+ dot.puts edge.read
383
+ }
384
+ end
385
+ else
386
+ dot.puts line
387
+ end
388
+ end
389
+ }
390
+ }
391
+ end
392
+
393
+ ####################
394
+ # mapファイルの作成
395
+ # 1) key,node名の値に一対一対応するnodeIDを作成(niがなければeiから作成)
396
+ def mkMap(key,nf,ni,ef1,ef2,ei,oFile)
397
+ temp=MCMD::Mtemp.new
398
+ xxa=temp.file
399
+ xxb=temp.file
400
+ xxc=temp.file
401
+ xxL1=temp.file
402
+ xxL2=temp.file
403
+ xxleaf=temp.file
404
+
405
+ # leaf nodeの構築
406
+ system "mcommon k=#{ef1} K=#{key} m=#{ei} -r i=#{ei} | mcut f=#{ef1}:nam o=#{xxa}"
407
+ system "mcommon k=#{ef2} K=#{key} m=#{ei} -r i=#{ei} | mcut f=#{ef2}:nam o=#{xxb}"
408
+ system "mcommon k=#{nf} K=#{key} m=#{ei} -r i=#{ni} | mcut f=#{nf}:nam o=#{xxc}" if ni
409
+ f=""
410
+ if ni
411
+ f << "mcat i=#{xxa},#{xxb},#{xxc} |"
412
+ else
413
+ f << "mcat i=#{xxa},#{xxb} |"
414
+ end
415
+ f << "muniq k=nam |"
416
+ f << "msetstr v=1 a=leaf o=#{xxleaf}"
417
+ system(f)
418
+
419
+ f=""
420
+ if ni then
421
+ system "mcut f=#{nf}:nam i=#{ni} o=#{xxa}"
422
+ system "mcut f=#{key}:nam i=#{ni} o=#{xxb}"
423
+ f=""
424
+ f << "mcat i=#{xxa},#{xxb} |"
425
+ f << "muniq k=nam |"
426
+ f << "mjoin k=nam m=#{xxleaf} f=leaf -n |"
427
+ # nullは最初に来るはずなので、mcalでなくmnumberでもnullを0に採番できるはずだが念のために
428
+ f << "mcal c='if(isnull($s{nam}),0,line()+1)' a=num |"
429
+ f << "mnullto f=nam v=##NULL## o=#{oFile}"
430
+ system(f)
431
+ else
432
+ system "mcut f=#{ef1}:nam i=#{ei} o=#{xxa}"
433
+ system "mcut f=#{ef2}:nam i=#{ei} o=#{xxb}"
434
+ system "mcut f=#{key}:nam i=#{ei} o=#{xxc}"
435
+ f=""
436
+ f << "mcat i=#{xxa},#{xxb},#{xxc} |"
437
+ f << "muniq k=nam |"
438
+ f << "mjoin k=nam m=#{xxleaf} f=leaf -n |"
439
+ f << "mcal c='if(isnull($s{nam}),0,line()+1)' a=num |"
440
+ f << "mnullto f=nam v=##NULL## o=#{oFile}"
441
+ system(f)
442
+ end
443
+ end
444
+
445
+ ####################
446
+ # nodeファイルの作成
447
+ # 1) key,node名すべての値に一対一対応するnodeIDを作成=>xxmap
448
+ # niがなければeiから作成
449
+ # 1) key,node名に対応するnodeIDをjoinする
450
+ # 2) nvがなければ全データ1をセット
451
+ # 3) ncがなければ全データnullをセット
452
+ #
453
+ # オリジナルのkey,node名に一意のnodeID(num)をつけて、nodeマスターを作成する
454
+ def mkNode(key,nf,nl,nv,nc,ni,ef1,ef2,ei,noiso,mapFile,oFile)
455
+ temp=MCMD::Mtemp.new
456
+ xxbyEdge=temp.file
457
+ xxbyNode=temp.file
458
+ xxa=temp.file
459
+ xxb=temp.file
460
+
461
+ # edgeファイルからnode情報を生成
462
+ # noiso(孤立node排除)の場合は、edgeにあってnodeにないidを省く必要があるので計算する。
463
+ if ni==nil or (ni!=nil and noiso)
464
+ system "mcut f=#{key}:key,#{ef1}:nam,#{ef1}:nl i=#{ei} o=#{xxa}"
465
+ system "mcut f=#{key}:key,#{ef2}:nam,#{ef2}:nl i=#{ei} o=#{xxb}"
466
+ f=""
467
+ f << "mcat i=#{xxa},#{xxb} |"
468
+ f << "mnullto f=key v=##NULL## |"
469
+ f << "muniq k=key,nam |"
470
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
471
+ f << "mjoin k=nam K=nam m=#{mapFile} f=num,leaf |"
472
+ f << "msetstr v=,,,, a=nv,nc,nlKey,nvKey,ncKey |"
473
+ f << "mcut f=key,nam,keyNum,num,nl,nv,nc,leaf,nvKey,ncKey o=#{xxbyEdge}"
474
+ system(f)
475
+ end
476
+
477
+ # nodeファイルから作成
478
+ if ni
479
+ # mcal cat用のlabel項目の作成
480
+ label=[]
481
+ if nl
482
+ nl["names"].each{|name|
483
+ label << "$s{#{name}}"
484
+ }
485
+ else
486
+ label << "$s{#{nf}}"
487
+ end
488
+
489
+ nvcStr=""
490
+ nvcStr << ",#{nv}:nv" if nv
491
+ nvcStr << ",#{nc}:nc" if nc
492
+
493
+ # map
494
+ # nam,leaf,num
495
+ # ##NULL##,,0
496
+ # #1_1,,2
497
+ # #1_2,,3
498
+ # #1_3,,4
499
+ # #2_1,,5
500
+ # a,1,6
501
+ # b,1,7
502
+ # c,1,8
503
+ f=""
504
+ f << "mcal c='cat(\"_\",#{label.join(',')})' a=##label i=#{ni} |"
505
+ f << "mcut f=#{key}:key,#{nf}:nam,##label:nl#{nvcStr} |"
506
+ f << "mnullto f=key v=##NULL## |"
507
+ f << "msetstr v= a=nv |" unless nv
508
+ f << "msetstr v= a=nc |" unless nc
509
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
510
+ f << "mjoin k=nam K=nam m=#{mapFile} f=num,leaf |"
511
+ f << "mcut f=key,nam,keyNum,num,nl,nv,nc,leaf o=#{xxa}"
512
+ system(f)
513
+ # key(cluster)のnvとncを結合しておく
514
+ f=""
515
+ f << "mjoin k=keyNum K=num m=#{xxa} f=nl:nlk,nv:nvKey,nc:ncKey -n i=#{xxa} |"
516
+ # rootのclusterはnlkがnullになるので、keyをlabelとしておく
517
+ f << "mcal c='if(isnull($s{nlk}),$s{key},$s{nlk})' a=nlKey |"
518
+ f << "mcut f=nlk -r o=#{xxbyNode}"
519
+ system(f)
520
+ end
521
+
522
+ if ni!=nil and noiso
523
+ system "mcommon k=key,nam m=#{xxbyEdge} i=#{xxbyNode} o=#{oFile}"
524
+ elsif ni!=nil
525
+ system "mv #{xxbyNode} #{oFile}"
526
+ else
527
+ system "mv #{xxbyEdge} #{oFile}"
528
+ end
529
+ # system "head #{oFile}"
530
+ # key,nam,keyNum%0,num,nv,nvv,nc,leaf,nvKey,ncKey
531
+ # ##NULL##,j,0,15,0.09090909091,1,FF0000,1,,
532
+ # ##NULL##,i,0,14,0.09090909091,1,FF0000,1,,
533
+ # #1_1,a,2,6,0.3636363636,1.857142857,FF0000,1,0.7272727273,
534
+ # #1_1,b,2,7,0.3636363636,1.857142857,00FF00,1,0.7272727273,
535
+ # #1_1,d,2,9,0.3636363636,1.857142857,00FF00,1,0.7272727273,
536
+ # #1_1,e,2,10,0.4545454545,2.142857143,0000FF,1,0.7272727273,
537
+ # #1_2,c,3,8,0.2727272727,1.571428571,FF0000,1,0.2727272727,
538
+ # #1_2,f,3,11,0.1818181818,1.285714286,FF0000,1,0.2727272727,
539
+ # #1_3,g,4,12,0.1818181818,1.285714286,00FF00,1,,
540
+ end
541
+
542
+ #####################
543
+ # edgeフィアルの作成
544
+ # 1) key,node名に対応するnodeIDをjoinする
545
+ # 2) ev項目を基準化
546
+ # 3) evがなければ全データ1をセット
547
+ def mkEdge(key,ef1,ef2,el,ec,ed,ev,ei,mapFile,oFile)
548
+ # mcal cat用のlabel項目の作成
549
+ label=[]
550
+ if el
551
+ el["names"].each{|name|
552
+ label << "$s{#{name}}"
553
+ }
554
+ end
555
+
556
+ evcdStr=""
557
+ evcdStr << ",#{ev}:ev" if ev
558
+ evcdStr << ",#{ec}:ec" if ec
559
+ evcdStr << ",#{ed}:ed" if ed
560
+ f=""
561
+ if el
562
+ f << "mcal c='cat(\"_\",#{label.join(',')})' a=##label i=#{ei} |"
563
+ else
564
+ f << "msetstr v= a=##label i=#{ei} |"
565
+ end
566
+ f << "mcut f=#{key}:key,#{ef1}:nam1,#{ef2}:nam2,##label:el#{evcdStr} |"
567
+ f << "msetstr v= a=ev |" unless ev
568
+ f << "msetstr v= a=ed |" unless ed
569
+ f << "msetstr v= a=ec |" unless ec
570
+ f << "mnullto f=key v=##NULL## |"
571
+ f << "mjoin k=key K=nam m=#{mapFile} f=num:keyNum |"
572
+ f << "mjoin k=nam1 K=nam m=#{mapFile} f=num:num1,leaf:leaf1 |"
573
+ f << "mjoin k=nam2 K=nam m=#{mapFile} f=num:num2,leaf:leaf2 |"
574
+ f << "mcut f=key,nam1,nam2,keyNum,num1,num2,el,ev,ed,ec,leaf1,leaf2 o=#{oFile}"
575
+ system(f)
576
+ system "cp #{oFile} xxo"
577
+ end
578
+
579
+ #########################################
580
+ # dot用のnodeデータをcluster別に作成する
581
+ def dotNode(iFile,nw,type,clusterLabel,oPath)
582
+ #system "cat #{iFile}"
583
+ # key,nam,keyNum%0,num,nl,nv,nvv,nc,leaf,nvKey,ncKey
584
+ # ##NULL##,j,0,15,j_A,0.09090909091,1,FF0000,1,,
585
+ # ##NULL##,i,0,14,i_A,0.09090909091,1,FF0000,1,,
586
+ # #1_1,a,2,6,a_A,0.3636363636,1.857142857,FF0000,1,0.7272727273,
587
+ # #1_1,b,2,7,b_B,0.3636363636,1.857142857,00FF00,1,0.7272727273,
588
+ # #1_1,d,2,9,d_B,0.3636363636,1.857142857,00FF00,1,0.7272727273,
589
+ # #1_1,e,2,10,e_C,0.4545454545,2.142857143,0000FF,1,0.7272727273,
590
+ # #1_2,c,3,8,c_A,0.2727272727,1.571428571,FF0000,1,0.2727272727,
591
+ # #1_2,f,3,11,f_A,0.1818181818,1.285714286,FF0000,1,0.2727272727,
592
+ # #1_3,g,4,12,g_B,0.1818181818,1.285714286,00FF00,1,,
593
+ # #1_3,h,4,13,h_C,0.1818181818,1.285714286,0000FF,1,,
594
+ # #2_1,#1_2,5,3,#1_2_,0.2727272727,1.571428571,,,,
595
+ # #2_1,#1_1,5,2,#1_1_,0.7272727273,3,,,,
596
+ iCSV=MCMD::Mcsvin.new("k=keyNum i=#{iFile}")
597
+ block=""
598
+ iCSV.each{|flds,top,bot|
599
+ nam=flds["nam"]
600
+ nl =flds["nl"]
601
+ nv =flds["nv"]
602
+ nc =flds["nc"]
603
+ leaf=flds["leaf"]
604
+
605
+ prefix="n"
606
+ prefix="cluster" unless leaf
607
+ nStr ="#{prefix}_#{flds["num"]}"
608
+ attrStr=""
609
+
610
+ unless prefix=="cluster"
611
+ # node label
612
+ # labelがleafでなければ、-clusterLabelが指定されていない限りlabelを表示しない
613
+ if leaf or clusterLabel
614
+ attrStr << "label=\"#{nl}\" "
615
+ else
616
+ attrStr << "label=\"\" "
617
+ end
618
+
619
+ # node shape
620
+ if nv
621
+ nRatioNorm=nv.to_f
622
+ attrStr << "height=#{0.5*nRatioNorm} width=#{0.75*nRatioNorm} "
623
+ end
624
+
625
+ # node color
626
+ if nc
627
+ attrStr << "color=\"##{nc}\" "
628
+ end
629
+
630
+ # node linewidth
631
+ if nw
632
+ attrStr << "style=\"setlinewidth(#{nw})\" "
633
+ end
634
+ end
635
+ block << "#{nStr} [#{attrStr}]\n"
636
+
637
+ if bot
638
+ keyNum=flds["keyNum"]
639
+ key=flds["key"]
640
+ nlKey=flds["nlKey"]
641
+ nvKey=flds["nvKey"]
642
+ ncKey=flds["ncKey"]
643
+ File.open("#{oPath}/c_#{keyNum}","w"){|fpw|
644
+ fpw.write(block)
645
+ }
646
+ # クラスタのラベルや色も出力しておく
647
+ attrStr=""
648
+ attrStr << "label=\"#{nlKey}\"\n"
649
+
650
+ # node color
651
+ if ncKey
652
+ attrStr << "color=\"##{ncKey}\"\n"
653
+ end
654
+ # node linewidth
655
+ if nw and ncKey
656
+ if ncKey
657
+ attrStr << "style=\"setlinewidth(#{nw})\"\n"
658
+ end
659
+ end
660
+ File.open("#{oPath}/L_#{keyNum}","w"){|fpw|
661
+ fpw.write(attrStr)
662
+ }
663
+ block=""
664
+ end
665
+ }
666
+ end
667
+
668
+ #########################################
669
+ # dot用のedgeデータをcluster別に作成する
670
+ def dotEdge(iFile,oPath)
671
+ # key,nam1,nam2%0,keyNum,num1,num2,ev,evv
672
+ # #2_1,#1_1,#1_2,4,1,2,0.2727272727,20
673
+ # #1_1,a,b,1,5,6,0.1818181818,0
674
+ iCSV=MCMD::Mcsvin.new("k=keyNum i=#{iFile}")
675
+ block=""
676
+ iCSV.each{|flds,top,bot|
677
+ num1=flds["num1"]
678
+ num2=flds["num2"]
679
+ el=flds["el"]
680
+ ev=flds["ev"]
681
+ ec=flds["ec"]
682
+ ed=flds["ed"]
683
+ leaf1=flds["leaf1"]
684
+ leaf2=flds["leaf2"]
685
+
686
+ prefix1="n"
687
+ prefix2="n"
688
+ prefix1="cluster" unless leaf1
689
+ prefix2="cluster" unless leaf2
690
+ e1Str ="#{prefix1}_#{num1}"
691
+ e2Str ="#{prefix2}_#{num2}"
692
+
693
+ attrStr=""
694
+ attrStr << "label=\"#{el}\" " if el
695
+ attrStr << "style=\"setlinewidth(#{ev})\" " if ev
696
+ attrStr << "color=\"##{ec}\" " if ec
697
+ if ed
698
+ if ed=="F"
699
+ attrStr << "dir=forward "
700
+ elsif ed=="B"
701
+ attrStr << "dir=back "
702
+ elsif ed=="W"
703
+ attrStr << "dir=both "
704
+ elsif ed=="N"
705
+ attrStr << "dir=none "
706
+ end
707
+ end
708
+
709
+ block << "#{e1Str} -> #{e2Str} [#{attrStr}]\n"
710
+
711
+ if bot
712
+ keyNum=flds["keyNum"]
713
+ File.open("#{oPath}/c_#{keyNum}","w"){|fpw|
714
+ fpw.write(block)
715
+ }
716
+ block=""
717
+ end
718
+ }
719
+ end
720
+
721
+ #############
722
+ # entry point
723
+
724
+ temp=MCMD::Mtemp.new
725
+ xxni =temp.file
726
+ xxei =temp.file
727
+ xxmap =temp.file
728
+ xxnode=temp.file
729
+ xxedge=temp.file
730
+ xxnode2=temp.file
731
+ xxedge2=temp.file
732
+ xxtree=temp.file
733
+ xxdotNode=temp.file
734
+ xxdotEdge=temp.file
735
+ MCMD::mkDir(xxdotNode)
736
+ MCMD::mkDir(xxdotEdge)
737
+
738
+ # 処理前のデータ修正
739
+ unless key
740
+ if ni
741
+ system "msetstr v= a=#key i=#{ni} o=#{xxni}"
742
+ ni=xxni
743
+ end
744
+ system "msetstr v= a=#key i=#{ei} o=#{xxei}"
745
+ ei=xxei
746
+ key="#key"
747
+ end
748
+
749
+ # ノードファイルの作成
750
+ mkMap(key,nf,ni,ef1,ef2,ei,xxmap)
751
+ mkNode(key,nf,nl,nv,nc,ni,ef1,ef2,ei,noiso,xxmap,xxnode)
752
+ mkEdge(key,ef1,ef2,el,ec,ed,ev,ei,xxmap,xxedge)
753
+ #system "head #{xxmap}"
754
+ # nam%0,num
755
+ # ,0
756
+ # #1_1,1
757
+ # #1_2,2
758
+ #
759
+ #system "cat #{xxnode}"
760
+ #exit
761
+ # key,nam,keyNum%0,num,nv,nvv,nc,leaf,nvKey,ncKey
762
+ # #1_1,a,1,5,0.4666666667,3.222222223,,1,0.6666666667,
763
+ # #1_1,b,1,6,0.4,2.666666666,,1,0.6666666667,
764
+ #system "cat #{xxedge}"
765
+ # key,nam1,nam2%0,keyNum,num1,num2,ev,evv,leaf1,leaf2
766
+ # #3_1,#1_2,#2_1,4,2,3,0.3,13.66666667,,
767
+ # #1_1,a,b,1,5,6,0.1,1,1,1
768
+ #exit
769
+ # dot用のnodeとedgeデータをcluster別ファイルとして生成
770
+ dotNode(xxnode,nw,type,clusterLabel,xxdotNode)
771
+ dotEdge(xxedge ,xxdotEdge)
772
+ #system "rm -rf ./xxdotNode"
773
+ #system "cp -R #{xxdotNode} ./xxdotNode"
774
+ #system "rm -rf ./xxdotEdge"
775
+ #system "cp -R #{xxdotEdge} ./xxdotEdge"
776
+
777
+ depth=nil
778
+ if type=="flat"
779
+ depth=mkFlat(xxnode,xxtree)
780
+ elsif type=="nest"
781
+ # tree構造の処理
782
+ # クラスタのみtree構造に格納する
783
+ depth=mkTree(xxnode,xxtree)
784
+ # puts "xxtree"
785
+ # system "cat #{xxtree}"
786
+ # node0%0,node1%1
787
+ # 3,
788
+ # 4,1
789
+ # 4,2
790
+ end
791
+
792
+ # tree構造をdotとして書き出す。その時、node,edgeを置換するためのキーワードを埋め込む
793
+ xxdotTree=temp.file
794
+ header=""
795
+ header << "digraph G {\n"
796
+ header << " #{directedStr}\n"
797
+ footer=""
798
+ footer << "}\n"
799
+ #system "cat #{xxtree}"
800
+ dotTree(xxtree,depth,header,footer,xxdotTree)
801
+ #puts "--------"
802
+ #system "cat #{xxdotTree}"
803
+ #exit
804
+ # xxdotTreeのnode,edge keywordをxxdotNode,xxdotEdgeで置換してdotの完成
805
+ replace(xxdotTree,xxdotNode,xxdotEdge,clusterLabel,oFile)
806
+ #system "cp #{xxdotTree} ./xxdotTree"
807
+ #system "cp -R #{xxdotNode} ./xxdotNode"
808
+ #system "cp -R #{xxdotEdge} ./xxdotEdge"
809
+
810
+ # 終了メッセージ
811
+ MCMD::endLog(args.cmdline)
812
+