go-hep.org/x/hep@v0.38.1/groot/cmd/root-print/main.go (about) 1 // Copyright ©2017 The go-hep Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 // root-print prints ROOT files contents to PDF, PNG, ... files. 6 // 7 // Examples: 8 // 9 // $> root-print -f pdf ./testdata/histos.root 10 // $> root-print -f pdf ./testdata/histos.root:h1 11 // $> root-print -f pdf ./testdata/histos.root:h.* 12 // $> root-print -f pdf -o output ./testdata/histos.root:h1 13 // 14 // $> root-print -h 15 // Usage: root-print [options] file.root [file.root [...]] 16 // 17 // options: 18 // -f string 19 // output format for plots (pdf, png, svg, ...) (default "pdf") 20 // -o string 21 // output directory for plots 22 // -v enable verbose mode 23 package main // import "go-hep.org/x/hep/groot/cmd/root-print" 24 25 import ( 26 "flag" 27 "fmt" 28 "log" 29 "os" 30 stdpath "path" 31 "path/filepath" 32 "regexp" 33 "strings" 34 35 "go-hep.org/x/hep/groot" 36 "go-hep.org/x/hep/groot/rhist" 37 "go-hep.org/x/hep/groot/riofs" 38 _ "go-hep.org/x/hep/groot/riofs/plugin/http" 39 _ "go-hep.org/x/hep/groot/riofs/plugin/xrootd" 40 "go-hep.org/x/hep/groot/root" 41 "go-hep.org/x/hep/hbook/rootcnv" 42 "go-hep.org/x/hep/hplot" 43 "gonum.org/v1/plot/plotutil" 44 "gonum.org/v1/plot/vg" 45 ) 46 47 var ( 48 colors = plotutil.SoftColors 49 ) 50 51 func main() { 52 log.SetPrefix("root-print: ") 53 log.SetFlags(0) 54 55 var ( 56 odirFlag = flag.String("o", "", "output directory for plots") 57 fmtFlag = flag.String("f", "pdf", "output format for plots (pdf, png, svg, ...)") 58 verboseFlag = flag.Bool("v", false, "enable verbose mode") 59 ) 60 61 flag.Usage = func() { 62 fmt.Fprintf( 63 os.Stderr, 64 `Usage: root-print [options] file.root [file.root [...]] 65 ex: 66 $> root-print -f pdf ./testdata/histos.root 67 $> root-print -f pdf ./testdata/histos.root:h1 68 $> root-print -f pdf ./testdata/histos.root:h.* 69 $> root-print -f pdf -o output ./testdata/histos.root:h1 70 71 options: 72 `, 73 ) 74 flag.PrintDefaults() 75 } 76 77 flag.Parse() 78 79 if flag.NArg() < 1 { 80 flag.Usage() 81 log.Fatalf("need at least 1 input ROOT file") 82 } 83 84 err := rootprint(*odirFlag, flag.Args(), *fmtFlag, *verboseFlag) 85 if err != nil { 86 log.Fatalf("%+v", err) 87 } 88 } 89 90 func rootprint(odir string, fnames []string, otype string, verbose bool) error { 91 err := os.MkdirAll(odir, 0755) 92 if err != nil { 93 return fmt.Errorf("could not create output directory %q: %w", odir, err) 94 } 95 96 for _, fname := range fnames { 97 err := process(odir, fname, otype, verbose) 98 if err != nil { 99 return fmt.Errorf("could not process %q: %w", fname, err) 100 } 101 } 102 103 return nil 104 } 105 106 func process(odir, name, otyp string, verbose bool) error { 107 fname, hname, err := splitArg(name) 108 if err != nil { 109 return fmt.Errorf( 110 "invalid input file format. got %q. want: \"file.root:histo\"", 111 name, 112 ) 113 } 114 115 f, err := groot.Open(fname) 116 if err != nil { 117 return err 118 } 119 defer f.Close() 120 121 re, err := regexp.CompilePOSIX(hname) 122 if err != nil { 123 return err 124 } 125 126 var objs []root.Object 127 err = riofs.Walk(f, func(path string, obj root.Object, err error) error { 128 if err != nil { 129 return err 130 } 131 name := path[len(f.Name()):] 132 if name == "" { 133 return nil 134 } 135 if !re.MatchString(name) { 136 return nil 137 } 138 139 if !filter(obj) { 140 return nil 141 } 142 143 objs = append(objs, obj) 144 return nil 145 }) 146 if err != nil { 147 return fmt.Errorf("could not inspect input ROOT file: %w", err) 148 } 149 150 for _, obj := range objs { 151 err := printObject(odir, otyp, obj, verbose) 152 if err != nil { 153 return err 154 } 155 } 156 return err 157 } 158 159 func printObject(odir, otyp string, obj root.Object, verbose bool) error { 160 p := hplot.New() 161 name := obj.(root.Named).Name() 162 title := obj.(root.Named).Title() 163 if title == "" { 164 title = name 165 } 166 p.Title.Text = title 167 168 oname := stdpath.Join(odir, name+"."+otyp) 169 if verbose { 170 log.Printf("printing %q to %s...", name, oname) 171 } 172 173 switch o := obj.(type) { 174 case rhist.H2: 175 h := rootcnv.H2D(o) 176 p.Add(hplot.NewH2D(h, nil)) 177 178 case rhist.H1: 179 h := rootcnv.H1D(o) 180 hh := hplot.NewH1D(h) 181 hh.Color = colors[2] 182 hh.LineStyle.Color = colors[2] 183 hh.LineStyle.Width = vg.Points(1.5) 184 hh.Infos.Style = hplot.HInfoSummary 185 186 p.Add(hh) 187 188 case rhist.GraphErrors: 189 h := rootcnv.S2D(o) 190 if name := h.Name(); name != "" { 191 p.Title.Text = name 192 } 193 g := hplot.NewS2D(h, hplot.WithXErrBars(true), hplot.WithYErrBars(true)) 194 g.Color = colors[0] 195 p.Add(g) 196 197 case rhist.Graph: 198 h := rootcnv.S2D(o) 199 if name := h.Name(); name != "" { 200 p.Title.Text = name 201 } 202 g := hplot.NewS2D(h) 203 g.Color = colors[0] 204 p.Add(g) 205 206 default: 207 return fmt.Errorf("unknown type %T for %q", o, name) 208 } 209 210 p.Add(hplot.NewGrid()) 211 212 // ext := strings.ToLower(stdpath.Ext(oname)) 213 // if len(ext) > 0 { 214 // ext = ext[1:] 215 // } 216 217 err := p.Save(20*vg.Centimeter, -1, oname) 218 if err != nil { 219 return fmt.Errorf("could not print %q to %q: %w", name, oname, err) 220 } 221 222 return nil 223 } 224 225 func filter(obj root.Object) bool { 226 switch obj.(type) { 227 case rhist.Graph, rhist.GraphErrors: 228 return true 229 230 case rhist.H2: 231 return true 232 233 case rhist.H1: 234 return true 235 } 236 return false 237 } 238 239 func splitArg(cmd string) (fname, sel string, err error) { 240 fname = cmd 241 prefix := "" 242 for _, p := range []string{"https://", "http://", "root://", "file://"} { 243 if strings.HasPrefix(cmd, p) { 244 prefix = p 245 break 246 } 247 } 248 fname = fname[len(prefix):] 249 250 vol := filepath.VolumeName(fname) 251 if vol != fname { 252 fname = fname[len(vol):] 253 } 254 255 if strings.Count(fname, ":") > 1 { 256 return "", "", fmt.Errorf("root-cp: too many ':' in %q", cmd) 257 } 258 259 i := strings.LastIndex(fname, ":") 260 switch { 261 case i > 0: 262 sel = fname[i+1:] 263 fname = fname[:i] 264 default: 265 sel = ".*" 266 } 267 if sel == "" { 268 sel = ".*" 269 } 270 fname = prefix + vol + fname 271 switch { 272 case strings.HasPrefix(sel, "/"): 273 case strings.HasPrefix(sel, "^/"): 274 case strings.HasPrefix(sel, "^"): 275 sel = "^/" + sel[1:] 276 default: 277 sel = "/" + sel 278 } 279 return fname, sel, err 280 }