1## Xrt3d.pm is a sub-module of Graph.pm. It has all the subroutines 2## needed for the Xrt3d part of the package. 3## 4## $Id: Xrt3d.pm,v 1.30 2006/06/07 21:09:33 emile Exp $ $Name: $ 5## 6## This software product is developed by Michael Young and David Moore, 7## and copyrighted(C) 1998 by the University of California, San Diego 8## (UCSD), with all rights reserved. UCSD administers the CAIDA grant, 9## NCR-9711092, under which part of this code was developed. 10## 11## There is no charge for this software. You can redistribute it and/or 12## modify it under the terms of the GNU General Public License, v. 2 dated 13## June 1991 which is incorporated by reference herein. This software is 14## distributed WITHOUT ANY WARRANTY, IMPLIED OR EXPRESS, OF MERCHANTABILITY 15## OR FITNESS FOR A PARTICULAR PURPOSE or that the use of it will not 16## infringe on any third party's intellectual property rights. 17## 18## You should have received a copy of the GNU GPL along with this program. 19## 20## 21## IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY 22## PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL 23## DAMAGES, INCLUDING LOST PROFITS, ARISING OUT OF THE USE OF THIS 24## SOFTWARE, EVEN IF THE UNIVERSITY OF CALIFORNIA HAS BEEN ADVISED OF 25## THE POSSIBILITY OF SUCH DAMAGE. 26## 27## THE SOFTWARE PROVIDED HEREIN IS ON AN "AS IS" BASIS, AND THE 28## UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO PROVIDE MAINTENANCE, 29## SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. THE UNIVERSITY 30## OF CALIFORNIA MAKES NO REPRESENTATIONS AND EXTENDS NO WARRANTIES 31## OF ANY KIND, EITHER IMPLIED OR EXPRESS, INCLUDING, BUT NOT LIMITED 32## TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A 33## PARTICULAR PURPOSE, OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE 34## ANY PATENT, TRADEMARK OR OTHER RIGHTS. 35## 36## 37## Contact: graph-dev@caida.org 38## 39## 40package Chart::Graph::Xrt3d; 41use Exporter (); 42 43@ISA = qw(Exporter); 44@EXPORT = qw(); 45@EXPORT_OK = qw(&xrt3d); 46 47use FileHandle; # to create generic filehandles 48use Carp; # for carp() and croak() 49use Chart::Graph::Utils qw(:UTILS); # get global subs and variables 50use Chart::Graph::XrtUtils qw(:UTILS); 51 52$cvs_Id = '$Id: Xrt3d.pm,v 1.30 2006/06/07 21:09:33 emile Exp $'; 53$cvs_Author = '$Author: emile $'; 54$cvs_Name = '$Name: $'; 55$cvs_Revision = '$Revision: 1.30 $'; 56 57$VERSION = 3.2; 58 59use strict; 60 61# 62# xrt graphing package 63# 64 65 66my %def_xrt_global_opts; # xrt specific globals 67 68%def_xrt_global_opts = ( 69 "output file" => "untitled-xrt3d.gif", 70 "output type" => "gif", 71 "x-axis title" => "x-axis", 72 "y-axis title" => "y-axis", 73 "z-axis title" => "z-axis", 74 "x-min" => "0", 75 "y-min" => "0", 76 "x-step" => "1", 77 "y-step" => "1", 78 "x-ticks" => undef, 79 "y-ticks" => undef, 80 "header" => ["header"], 81 "footer" => ["footer"], 82 ); 83# 84# 85# Subroutine: xrt() 86# 87# Description: this is the main function you will be calling from 88# our scripts. please see 89# www.caida.org/Tools/Graph/ for a full description 90# and how-to of this subroutine 91# 92 93sub xrt3d { 94 my $user_global_opts_ref = shift; 95 my $data_set_ref = shift; 96 my $matrix_data_ref; 97 my $data_filename; 98 my (%global_opts); 99 100 # variables to be written to the command file 101 my ($plot_file, $x_axis, $y_axis, $z_axis, $x_step, $y_step); 102 my ($x_min, $y_min, $x_ticks, $y_ticks, $header, $footer); 103 my ($x_cnt, $y_cnt, $hdr_cnt, $ftr_cnt); 104 my ($output_file); 105 106 if (@_) { 107 carp 'Too many arguments. Usage: xrt3d(\%options, \@data_set)'; 108 return 0; 109 } 110 111 _make_tmpdir("_Xrt3d_"); 112 113 # set paths for external programs 114 if (not _set_xrtpaths("xrt3d")) { 115 _cleanup_tmpdir(); 116 return 0; 117 } 118 119 # check first arg for hash 120 if (ref($user_global_opts_ref) ne "HASH") { 121 carp "Global options must be a hash."; 122 _cleanup_tmpdir(); 123 return 0; 124 } 125 126 # call to combine user options with default options 127 %global_opts = _mesh_opts($user_global_opts_ref, \%def_xrt_global_opts); 128 129 # check for values in command file 130 while (my ($key, $value) = each %global_opts) { 131 132 if ($key eq "output file") { 133 $output_file = $value; 134 unless (defined $global_opts{"output type"}) { 135 carp "Must have an output type defined"; 136 _cleanup_tmpdir(); 137 return 0; 138 } 139 } 140 141 # If the file is PostScript ... what XRT makes is PostScript 142 if ($global_opts{"output type"} eq "ps") { 143 $plot_file = _make_tmpfile("plot", "ps"); 144 } 145 # For all raster formats XRT starts out with 146 # X-Windows XWD format. 147 elsif (($global_opts{"output type"} eq "gif") or 148 ($global_opts{"output type"} eq "xwd") or 149 ($global_opts{"output type"} eq "png") or 150 ($global_opts{"output type"} eq "jpg") 151 ) { 152 $plot_file = _make_tmpfile("plot", "xwd"); 153 } else { 154 # Default is XWD 155 carp "Unknown output type, defaulting to xwd"; 156 $plot_file = _make_tmpfile("plot", "xwd"); 157 } 158 159 160 if ($key eq "x-axis title") { 161 if(defined($value)) { 162 $x_axis = $value; 163 } 164 } 165 166 if ($key eq "y-axis title") { 167 if(defined($value)) { 168 $y_axis = $value; 169 } 170 } 171 172 if ($key eq "z-axis title") { 173 if(defined($value)) { 174 $z_axis = $value; 175 } 176 } 177 178 if ($key eq "x-min") { 179 if(defined($value)) { 180 $x_min = $value; 181 } 182 } 183 184 if ($key eq "y-min") { 185 if(defined($value)) { 186 $y_min = $value; 187 } 188 } 189 190 if ($key eq "x-step") { 191 if(defined($value)) { 192 $x_step = $value; 193 } 194 } 195 196 if ($key eq "y-step") { 197 if(defined($value)) { 198 $y_step = $value; 199 } 200 } 201 202 if ($key eq "x-ticks") { 203 if(defined($value)) { 204 $x_ticks = $value; 205 } 206 } 207 208 if ($key eq "y-ticks") { 209 if(defined($value)) { 210 $y_ticks = $value; 211 } 212 } 213 214 if ($key eq "header") { 215 if(defined($value)) { 216 $header = $value; 217 } 218 } 219 220 if ($key eq "footer") { 221 if(defined($value)) { 222 $footer = $value; 223 } 224 } 225 } 226 227 228 # Extract options for data. 229 my $data_opts = shift @{$data_set_ref}; 230 while (my ($key, $value) = each %{$data_opts}) { 231 if ($key eq "type") { 232 if ($value eq "matrix") { 233 $matrix_data_ref = $data_set_ref; 234 } 235 elsif ($value eq "file") { 236 $data_filename = pop @{$data_set_ref}; 237 } else { 238 carp "Unsupported or unknown format for data"; 239 } 240 } 241 } 242 243 # because xrt allows multiline headers 244 # get the length of the header array 245 # each line of the header is one index 246 # in the array 247 248 $hdr_cnt = $#{$global_opts{"header"}} + 1; 249 $ftr_cnt = $#{$global_opts{"footer"}} + 1; 250 251 252 if (defined($matrix_data_ref)) { 253 # get the number of columns and number of rows 254 # and verify that each row has same number of 255 # columns 256 257 $x_cnt = $#{$matrix_data_ref} + 1; 258 my $tmp = $#{$matrix_data_ref->[0]} + 1; 259 260 foreach my $i (@{$matrix_data_ref}) { 261 if ($tmp != $#{$i} + 1) { 262 carp "each row must have the same number of columns"; 263 _cleanup_tmpdir(); 264 return 0; 265 } 266 } 267 268 $y_cnt = $tmp; 269 270 271 # verify that number of tick marks == corresponds 272 # to that of xy matrix. One tick mark for every x 273 # y. 274 275 if (not _verify_ticks($x_cnt, $global_opts{"x-ticks"})) { 276 _cleanup_tmpdir(); 277 return 0; 278 } 279 280 if (not _verify_ticks($y_cnt, $global_opts{"y-ticks"})) { 281 _cleanup_tmpdir(); 282 return 0; 283 } 284 } else { 285 # XXX 286 # Poor man's hack to compute rows and columns in data file. This will 287 # make a second pass through file, but is probably faster than doing it 288 # in Perl. 289 my ($lead, $words, $bytes); 290 ($lead, $x_cnt, $words, $bytes) = split(/\D+/, `wc $data_filename`); 291 if (($x_cnt > 0) and ($words > 0)) { 292 $x_cnt++; 293 $y_cnt = $words/$x_cnt; 294 } else { 295 $x_cnt = 0; 296 $y_cnt = 0; 297 carp "Cannot compute number of rows and/or columns in file data"; 298 } 299 } 300 301 ## 302 ## print command file using this format 303 ## 304 305 # output.file 306 # x_min (normally 0) 307 # y_min (normally 0) 308 # x_step (normally 1) 309 # y_step (normally 1) 310 # x_cnt (number of rows of input) 311 # y_cnt (number of columns of input) 312 # data11 data12 data13 data14 .... (x by y matrix of doubles) 313 # data21 data22 data23 .... 314 # . 315 # . 316 # . 317 # datax1 datax2 ... dataxy 318 # Number of header lines (multiple header lines available) 319 # header1 320 # header2 321 # ... 322 # Number of header lines (multiple header lines available) 323 # foot1 324 # foot2 325 # ... 326 # Title of x-axis 327 # Title of y-axis 328 # Title of z-axis 329 # xlabel0 (x_cnt number of labels for ticks along x-axis) 330 # ... 331 # xlabelx 332 # ylabel0 (y_cnt number of labels for ticks along y-axis) 333 # ... 334 # ylabely 335 336 # create command file and open file handle 337 my $command_file = _make_tmpfile("command"); 338 my $handle = new FileHandle; 339 if (not $handle->open(">$command_file")) { 340 carp "could not open $command_file"; 341 _cleanup_tmpdir(); 342 return 0; 343 } 344 345 print $handle "$plot_file\n"; 346 print $handle "$x_min\n"; 347 print $handle "$y_min\n"; 348 print $handle "$x_step\n"; 349 print $handle "$y_step\n"; 350 print $handle "$x_cnt\n"; 351 print $handle "$y_cnt\n"; 352 if (defined($matrix_data_ref)) { 353 _print_matrix($handle, @{$matrix_data_ref}); 354 } else { 355 _transfer_file($handle, $data_filename) 356 } 357 print $handle "$hdr_cnt\n"; 358 _print_array($handle, @{$header}); 359 print $handle "$ftr_cnt\n"; 360 _print_array($handle, @{$footer}); 361 print $handle "$x_axis\n"; 362 print $handle "$y_axis\n"; 363 print $handle "$z_axis\n"; 364 _print_array($handle, @{$x_ticks}); 365 _print_array($handle, @{$y_ticks}); 366 $handle->close(); 367 368 # call xrt and convert file to gif 369 if (not _exec_xrt3d($command_file)) { 370 _cleanup_tmpdir(); 371 return 0; 372 } 373 my $graph_format = $global_opts{"output type"}; 374 if ($graph_format eq "ps") { 375 if (not _chk_status(system("cp $plot_file $output_file"))) { 376 _cleanup_tmpdir(); 377 return 0; 378 } 379 } elsif ($graph_format eq "xwd") { 380 if (not _chk_status(system("cp $plot_file $output_file"))) { 381 _cleanup_tmpdir(); 382 return 0; 383 } 384 } else { 385 if(not _convert_raster($graph_format, $plot_file, $output_file)) { 386 _cleanup_tmpdir(); 387 return 0; 388 } 389 } 390 391 _cleanup_tmpdir(); 392 return 1; 393} 394 3951; 396 397 398__END__ 399 400=head1 NAME 401 402Chart::Graph::Xrt3d 403 404=head1 SYNOPSIS 405 406 #Include module 407 use Chart::Graph::Xrt3d qw(xrt3d); 408 409 # Function call 410 xrt3d(\%options, 411 \@data_set 412 ); 413 414 415=head1 DESCRIPTION 416 417This module is unmaintained, it worked with Sitraka's XRT, and hasn't been 418tested against newer versions. 419 420Sitraka (now Quest) makes a number of graphics packages for UNIX systems. XRT is 421a Motif-based commercial software product that has been adapted by 422CAIDA using a combination of C drivers and Perl function I<xrt3d()>. 423The Perl function I<xrt3d()> provides access to the three dimensional 424graphing capabilities of XRT from Perl. To access the two dimensional 425graphing using XRT, use I<xrt2d()> also supplied in the 426I<Chart::Graph> package. 427 428=head1 ARGUMENTS 429 430The options to I<xrt3d()> are listed below. Additional control over the 431resulting graph is possible by using the XRT application itself once 432the graph has been created. 433 434 +--------------------------------------------------------------------------+ 435 | OPTIONS | 436 +----------------+--------------------------+------------------------------+ 437 | Name | Options | Default | 438 |"output file" | (set your own) | "untitled-xrt3d.gif" | 439 |"output type" | "ps","xwd", "png", "jpg"| "xwd" | 440 |"x-axis title" | (set your own) | "x-axis" | 441 |"y-axis title" | (set your own) | "y-axis" | 442 |"z-axis title" | (set your own) | "z-axis" | 443 |"x-min" | "0" or "1"(normally 0) | "0" | 444 |"y-min" | "0" or "1"(normally 0) | "0" | 445 |"x-step" | "0" or "1"(normally 1) | "1" | 446 |"y-step" | "0" or "1"(normally 1) | "1" | 447 |"x-ticks" | (set your own) | none | 448 |"y-ticks" | (set your own) | none | 449 |"header" | (set your own) | Array ref of "header" text | 450 |"footer" | (set your own) | Array ref of "footer" text | 451 +----------------+--------------------------+------------------------------+ 452 453The I<xrt3d> function only accepts data in one of two forms. The 454choices are: either C<[\%data1_opts, \@data_matrix]> or 455C<[\%data1_opts, "filename"]> The data options are listed below. 456 457 +--------------------------------------------------------------------------+ 458 | DATA OPTIONS | 459 +----------------+--------------------------+------------------------------+ 460 | Name | Options | Default | 461 +----------------+--------------------------+------------------------------+ 462 | "type" | Data format: "matrix" or | none | 463 | | "file" | | 464 +----------------+--------------------------+------------------------------+ 465 466=head2 DETAILS ON GRAPHICS CONVERTER OPTIONS 467 468The xrt package supports only two graphics formats internally: 469Postscript and the X windows format XWD. Additional raster graphics 470formats are supported with Chart::Graph by using one of two graphics 471converter packages: I<Imagemagick> and I<Netpbm>. 472 473If you need to install a converter package,I<Imagemagick> 474I<http://www.imagemagick.org/> is probably preferable 475simply for its comparatively simplicity. It uses one program 476I<convert> for all of it's conversion needs, so it is easy to manage 477and simple for Chart::Graph to use. Many UNIX systems come with some 478collection of the I<Netpbm> utilities already installed, thus users 479may be able to start using Chart::Graph without adding any additional 480converters. Alas, it is unlikely any distributions would include all 481the converters for the newest graphics formats used by Chart::Graph. 482In that case it may still preferable to use I<Imagemagick> simply for 483the sake of avoiding installing over 80 utilities that come with 484current distributions of I<Netpbm>. For more information on the 485current distribution of I<Netpbm> go to the current website at: 486I<http://netpbm.sourceforge.net/> 487 488The xrt package also allows for multiple header and footers with each 489graph. As a result, instead of just the usual string, an array 490reference containing the multiple strings for the header and footer 491text. 492 493=head1 EXAMPLES 494 495The following four examples show Chart::Graph::Xrt3d in different roles 496and producing different styles of output. 497 498=head2 EXAMPLE: STOCK PRICES FOR JOE'S RESTAURANT 499 500The first example creates a three dimensional bar chart of 501fictitious stock data that is displayed in the graphic file 502F<xrt3d-1.gif>. Note that I<xrt3d()> uses the older gif file format, 503but can use others as noted above if you have the available converters 504provided. 505 506 #make sure to include Chart::Graph 507 use Chart::Graph::Xrt3d qw(xrt3d); 508 509 #using a 3 by 6 matrix for the data set 510 xrt3d({"output file" => "xrt3d-1.gif", 511 "output type" => "gif", 512 "header" => 513 ["Stock prices for Joe's restaurant chain", 514 "Compiled from local records" 515 ], 516 "footer" => 517 ["Joe's Restaurant"], 518 "y-ticks"=>["Jan/Feb", "Mar/Apr", "May/Jun", "Jul/Aug", 519 "Sep/Oct", "Nov/Dec"], 520 "x-axis title" => "Years monitored", 521 "y-axis title" => "Month's tracked", 522 "z-axis title" => "Stock prices", 523 }, 524 [{"type" => "matrix"}, 525 ["4", "5", "3", "6", "6", "5"], 526 ["8", "13", "20", "45", "100", "110" ], 527 ["70", "45", "10", "5", "4", "3"]]) 528 529=for html 530<p><center><img src="http://www.caida.org/tools/utilities/graphing/xrt3d-1.jpg"></center></p> 531<p><center><em>xrt3d-1.jpg</em></center></p> 532 533=head2 EXAMPLE: EARLY GROWTH OF THE INTERNET 534 535The following example creates a three dimensional bar chart of data 536collected on the early growth of the Internet (URL and corporate 537source included on graph.) The result in this case is display in one 538of the newest graphics formats the PNG format: F<xrt3d-2.png>. 539 540 #make sure to include Chart::Graph 541 use Chart::Graph::Xrt3d qw(xrt3d); 542 543 xrt3d({"output file" => "xrt3d-2.png", 544 "output type" => "png", 545 "header" => 546 ["Growth of Early Internet", 547 "(according to Internet Wizards - http://www.nw.com/)", 548 ], 549 "footer" => 550 ["http://www.mit.edu/people/mkgray/net/internet-growth-raw-data.html"], 551 "y-ticks"=>["Jan 93", "Apr 93", "Jul 93", 552 "Oct 93", "Jan 94", "Jul 94", 553 "Oct 94", "Jan 95", "Jul 95", 554 "Jan 96" 555 ], 556 "x-ticks"=>["Hosts", "Domains", "Replied to Ping"],}, 557 [{"type" => "matrix"}, 558 ["1.3e6", "1.5e6", "1.8e6", "2.1e6", "2.2e6", "3.2e6", 559 "3.9e6","4.9e6", "6.6e6", "9.5e6" 560 ], 561 ["21000","22000", "26000", "28000", "30000", "46000", 562 "56000", "71000", "120000", "240000" 563 ], 564 ["NA", "0.4e6", "NA", "0.5e6", "0.6e6", "0.7e6", 565 "1.0e6", "1.0e6", "1.1e6", "1.7e6" 566 ] 567 ] 568 ); 569 570=for html 571<p><center><img src="http://www.caida.org/tools/utilities/graphing/xrt3d-2.png"></center></p> 572<p><center><em>xrt3d-2.png</em></center></p> 573 574=head2 EXAMPLE: USING A DATA FILE FOR INPUT 575 576The next example uses a file instead of a array for it's data source. 577The file is listed below the Perl code. 578 579 #make sure to include Chart::Graph 580 use Chart::Graph::Xrt3d qw(xrt3d); 581 582 if (xrt3d({"output file" => "xrt3d-3.gif", 583 "output type" => "gif", 584 "x-ticks"=>["a", "b", "c"], 585 "y-ticks"=>["w", "x", "y", "z"],}, 586 [{"type" => "file"}, 587 "xrt3d_data.txt"])) { 588 print "ok\n"; 589 } else { 590 print "not ok\n"; 591 } 592 593The data file used in the above example is as follows. 594 595 10 15 23 10 596 4 13 35 45 597 29 15 64 24 598 599=for html 600<p><center><img src="http://www.caida.org/tools/utilities/graphing/xrt3d-3.gif"></center></p> 601<p><center><em>xrt3d-3.gif</em></center></p> 602 603 604=head1 MORE INFO 605 606For more information on XRT: 607 608 http://www.quest.com/xrt_pds/ 609 610=head1 CONTACT 611 612Send email to graph-dev@caida.org is you have problems, questions, 613or comments. To subscribe to the mailing list send mail to 614graph-dev-request@caida.org with a body of "subscribe your@email.com" 615 616=head1 AUTHOR 617 618 CAIDA Perl development team (cpan@caida.org) 619 620=cut 621