flak@mcgp1.UUCP (Dan Flak) (03/01/91)
line is a poor man's graphics program to produce line graphs using troff. It has a rather limited scope in that it produces only one line per plot and its corresponding trend line, and takes as its x-axis only months at fixed intervals. However, this should be a common enough application to be of some use to someone else besides me. No special make instructions are needed, Just "cc" it. #----- cut here and sh the remainder ----- #!/bin/sh # This is a shell archive (produced by shar 3.49) # To extract the files from this archive, save it to a file, remove # everything above the "!/bin/sh" line above, and type "sh file_name". # # made 02/28/1991 20:19 UTC by flak@mcgp1 # Source directory /usr/users/flak/News # # existing files will NOT be overwritten unless -c is specified # # This shar contains: # length mode name # ------ ---------- ------------------------------------------ # 2377 -rw-r--r-- line.doc # 7244 -rw-rw-rw- line.c # # ============= line.doc ============== if test -f 'line.doc' -a X"$1" != X"-c"; then echo 'x - skipping line.doc (File already exists)' else echo 'x - extracting line.doc (Text)' sed 's/^X//' << 'SHAR_EOF' > 'line.doc' && .de )k .. .TH line l local .SH NAME line \- create a line graph .SH SYNOPSIS .B line [-iltu] <datafile> .SH DESCRIPTION .I line crates a stream of troff 'pic' commands to the standard output to produce a line graph and trend analysis (least squares fit of a straight line) for a 12 month period of data. .SH OPTIONS .TP .B i increment. This value determines how many horizontal partitions there will be on the graph. (E.G. "5" means place a horizontal scale every 5 units). The horizontal lines on the graph are dashed. The vertical lines extending from each month are dotted. The program is arranged so that whatever increment is selected, there will be 10 "dots" per increment. Default is 10. .TP .B l lower limit. The lower limit for the data to be plotted. Default is 0. .TP .B t title. The title string for the graph. .TP .B u upper limit. The upper limit for the data to be plotted. Default is 100. .SH EXAMPLES DATA FILE FORMAT: The data file is an ascii file containing one "record" per line (maximum of 12 records). A record consists of the month to be plotted (in YYMM format - e.g. 9103 is March 1991). and its value separated by a vertical bar \f(CW(|)\fR. .sp .nf datafile \f(CW9001|95.4 9002|91.2 9003|93.8 9004|ND 9005|94.2 9006|99.3 9007|95.2 9008|97.3 9009|96.1 9010|95.3 9011|97.4 9012|96.6\fR .fi .sp line -l 90 -u 100 -i 5 -t "TEST TITLE" datafile .sp The example above will produce the commands to draw a line graph containing; an x-axis labeled with the months from "Jan" through "Dec", a y-axis labeled with "90", "95", and "100", a solid line showing the actual plot of data, a dotted line showing the trend line, the values associated with the data points on top of the graph, and the title "TEST TITLE" centered on top of the page. .SH DIAGNOSTICS The program will issue a warning message if the upper or lower limits are exceeded by any of the data values. The keyword "ND" will be interpreted by the program to mean "NO DATA". Put in the "value" position in a record, it will cause a skip in the plot (the actual data line will extend from the previous data plot to the next) and the letters "ND" displayed instead of a data value. .SH AUTHOR Dan Flak, McCaw Cellular Communications Inc., 201 Elliott Ave W, Suite 105, Seattle, Wa 98119, 206-286-4355 (usenet: nwnexus!mcgp1!flak) .SH BUGS The program is very picky about the format of the data. SHAR_EOF chmod 0644 line.doc || echo 'restore of line.doc failed' Wc_c="`wc -c < 'line.doc'`" test 2377 -eq "$Wc_c" || echo 'line.doc: original size 2377, current size' "$Wc_c" fi # ============= line.c ============== if test -f 'line.c' -a X"$1" != X"-c"; then echo 'x - skipping line.c (File already exists)' else echo 'x - extracting line.c (Text)' sed 's/^X//' << 'SHAR_EOF' > 'line.c' && /*+ X * File: line.c X * X * Description: Produce a line graph using pic and troff. X * X * This module is the property of McCaw Cellular Communications, Inc X * Copyright 1990, all rights reserved X * X * Audit Trail: X * Original Author and Date: Dan Flak - 13 Jul 1990. X * -*/ #ifdef VERSION static char *SCCS = "%Z% %M% %I% %G% %U%" #endif X /* #includes */ #include <stdio.h> X /* #defines */ #define SAY printf #define BACKSLASH '\134' X /* external variables */ extern char *optarg; extern int optind; extern int opterr; extern int optopt; X /* referenced external functions */ float atof(); X /* internal functions */ int basemonth(); X X /* global variables */ X struct record X { X char mdate[5]; X float f; X char m[8]; X } rec[12]; X float data_x; float data_x2; float data_y; float data_y2; float data_xy; X float newx, newy, oldx, oldy; float factor1, factor2, factor3; float m, b, bmonth; X float lower = 0, upper = 100, lineat = 10, hbar, eachmo; char title[80]; int nrecs = 0; int npts = 0; X char *month[12] = X { X "Jan", "Feb", "Mar", "Apr", "May", "Jun", X "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" X }; X FILE *fin, *fopen(); X X /* static variables */ X X /*< X * Function: main X * X * Description: Parse the data and make a pic file X * X * Data Type: int X * X * Arguments: X * argv[] = input file name X * X * Flags X * -l lower limit X * -u upper limit X * -i increment X * -t title string X * X * Returns: X * X * Side Effects: X * X * Calls: X * X * PDL X * >*/ X int main (argc, argv) int argc; char *argv[]; { int i, k, nfound; int c; float range, perinch; char linebuf[1000]; *title = '\0'; X while ((c = getopt (argc, argv, "i:l:u:t:")) != EOF) X { X switch (c) X { X case 'i': X lineat = atof (optarg); X break; X case 'l': X lower = atof (optarg); X break; X case 'u': X upper = atof (optarg); X break; X case 't': X sprintf (title, "%s", optarg); X break; X default: X ; X break; X } X } X if (argc < 2) X { X fprintf (stderr, "syntax: %s [-i increment] [-l lower limit] [-u upper limit] [-t title]\n", X argv[0]); X fprintf (stderr, " <data file>\n"); X exit (0); X } X /* Read data file */ if ((fin = fopen (argv[optind], "r")) == NULL) X { X fprintf (stderr, "%s: can't open %s\n", argv[0], argv[1]); X exit (-1); X } X X while (fgets (linebuf, 1000, fin) != NULL) X { X nfound = sscanf (linebuf, "%[^|]|%[^'\n']", X rec[nrecs].mdate, rec[nrecs].m); X /* SAY ("nfound = %d / %2d - %s | %s\n", X nfound, nrecs, rec[nrecs].mdate, rec[nrecs].m); */ X if (nfound == 2) X { X if (strcmp (rec[nrecs].m, "ND")) X { X rec[nrecs].f = atof (rec[nrecs].m); X } X else X { X rec[nrecs].f = -1; X } X nrecs++; X if (nrecs > 12 ) X { X fprintf (stderr, X "%s: Can only accomodate a maximum of 12 data points\n", X argv[0]); X exit (-1); X } X } X } X fclose (fin); X /* Check upper and lower bounds */ X for (i = 0; i < nrecs; i++) X { X if (rec[i].f < lower && rec[i].f != -1) X { X fprintf (stderr, "Warning: lower limit exceeded %s\n", X rec[i].mdate); X } X X if (rec[i].f > upper) X { X fprintf (stderr, "Warning: upper limit exceeded %s\n", X rec[i].mdate); X } X } X X /* Build the file that will plot the graph */ X printf (".de )k\n..\n"); printf (".PH\n"); printf (".ps 12\n"); printf (".vs 12\n"); X data_x = 0; data_x2 = 0; data_y = 0; data_y2 = 0; data_xy = 0; X printf (".ce 3\n"); X printf ("%cs+4%cfB%s%cfP%cs0\n", X BACKSLASH, BACKSLASH, title, BACKSLASH, BACKSLASH); X printf (".br\n"); X printf (".DS CB\n"); X printf (".PS\n"); X printf ("\n# set invisible refernece box\n"); X printf ("boxht = 3.45; boxwid = 4.65\n"); X printf ("move to (0, -2.5); OB: box invis\n"); X printf ("\n# workaround for bug in 'old' pic\n"); X printf ("%c %c at OB.nw + (0, .2)\n", '"', '"'); X printf ("%c %c at OB.sw\n", '"', '"'); X printf ("\n# Print horizontal axis\n"); X printf ("HA: line from OB.sw to OB.se\n"); X printf ("\n# Print vertical axis\n"); X printf ("VA: line from OB.nw to OB.sw\n"); X range = upper - lower; X if (range == 0) X { X fprintf (stderr, X "Warning: range = 0. Sorry, Dave, I can't do that\n"); X exit (-1); X } X perinch = 3.45 / range; X SAY ("# range = %f, perinch = %f\n", range, perinch); X printf ("\n# put in horizontal scale marks\n"); X printf ("%c%4.1f%c rjust at OB.sw + (-.1, 0)\n", '"', lower, '"'); X hbar = lower + lineat; X eachmo = 4.65 / 11; X while (hbar <= upper) X { X printf ("line right 4.65 at OB.sw + (0, %f) dashed; ", X (hbar - lower) * perinch); X printf ("%c%4.1f%c rjust at OB.sw + (-.1, %f)\n", X '"', hbar, '"', (hbar - lower) * perinch); X hbar += lineat; X } X printf ("\n# Put in vertical scale marks\n"); X for (i = 0; i < nrecs; i++) X { X k = atoi (rec[i].mdate + 2) - 1; X printf ("%c%s%c at OB.sw + (%f, -.1); ", X '"', month[k], '"', i * eachmo); X printf ("%c%s%c at OB.sw + (%f, 3.60)\n", X '"', rec[i].m, '"', i * eachmo); X } X putchar ('\n'); X for (i = 1; i < nrecs; i++) X { X if (lineat == 0) X { X lineat = 1; X } X printf ("line from OB.sw + (%f, 0) to OB.sw + (%f, 3.45) dotted %f\n", X i * eachmo, i * eachmo, perinch * lineat / 10); X } X printf ("\n# Drawing Actual lines\n"); X oldx = -1; oldy = -1; X for (i = 0; i < nrecs; i++) X { X if (rec[i].f != -1) X { X newx = i * eachmo; X newy = (rec[i].f - lower) * perinch; X if (oldx != -1) X { X printf ("line from OB.sw + (%f, %f) to OB.sw + (%f, %f)\n", X oldx, oldy, newx, newy); X } X oldx = newx; oldy = newy; X } X } X printf ("\n# Drawing trend lines\n"); X npts = 0; X for (i = 0; i < nrecs; i++) X { X if (rec[i].f != -1) X { X bmonth = basemonth (rec[i].mdate); X /* SAY ("%s = %d\n", rec[i].mdate, bmonth); */ X data_x += bmonth; X data_x2 += bmonth * bmonth; X data_y += rec[i].f; X data_y2 += rec[i].f * rec[i].f; X data_xy += bmonth * rec[i].f; X npts++; X } X } X factor1 = npts * data_x2 - data_x * data_x; X factor2 = npts * data_y2 - data_y * data_y; X factor3 = npts * data_xy - data_x * data_y; X if (factor1 != 0) X { X m = factor3 / factor1; X } X else X { X fprintf (stderr, "Warning: slope not calcualted for %d\n"); X m = 0; X } X X if (npts != 0) X { X b = (data_y - m * data_x) / npts; X } X else X { X fprintf (stderr, "Warning: intercept not calculated\n"); X b = (upper - lower) / 2; X } X printf ("\n# Slope = %f, Intercept = %f\n", m, b); X printf ("line from OB.sw + (0, %f) to OB.sw + (4.65, %f) dotted\n", X (m * basemonth (rec[0].mdate) + b - lower) * perinch, X (m * basemonth (rec[nrecs - 1].mdate) + b -lower) * perinch); X X printf ("\n# Put in ledgend\n"); X printf ("line right 1.0 at HA.start + (0.5, -.333)\n %c Actual%c ljust\n", X '"', '"'); X printf ("move right 1.0\n"); X printf ("line right 1.0 dotted\n %c Trend%c ljust\n", '"', '"'); X printf (".PE\n"); X printf (".DE\n"); X printf (".bp\n"); } X /*< X * Function: basemonth (yymm) X * X * Description: Feed it a YYMM and it will give you number of months X * since 8901. X * X * Data Type: int X * X * Arguments: X * yymm = the date to convert X * X * Returns: X * The number of months since 8901 X * X * Side Effects: X * X * Calls: X * X * PDL: X * >*/ X int basemonth (yymm) char *yymm; { int zzzz; zzzz = atoi (yymm); return (12 * ((zzzz - 8901) / 100) + (zzzz - 8901) % 100) ; } SHAR_EOF chmod 0666 line.c || echo 'restore of line.c failed' Wc_c="`wc -c < 'line.c'`" test 7244 -eq "$Wc_c" || echo 'line.c: original size 7244, current size' "$Wc_c" fi exit 0 -- Dan Flak - McCaw Cellular Communications Inc., 201 Elliot Ave W., Suite 105, Seattle, Wa 98119, 206-286-4355, (usenet: nwnexus!mcgp1!flak)