dm 'clear log'; dm 'clear output'; goptions reset=all cback=black rotate=landscape; options symbolgen macrogen mprint; ***************************************************************************; * Program: Annoaro6.sas *; * Function: To place annotated arrows and labels on gplots *; * 1. Take values including the positive and negative value *; * to determine whether the arrow goes from above the line *; * or below the line. The variable updown allows for this *; * capability. *; * 2. Run Proc Means to get the minimum and maximum values *; * 3. Add factors to the mean and max values to make space *; * for the annotation *; * 4. Draw the arrowheads using the annotate facility *; * 5. Combine the annotate values with the gplot values and *; * plot the extra values as an overlay so the annotation *; * will appear on the graph *; * Assumptions 1. X and Y variables are numeric *; * 2. X variable is called XVAR *; * 3. Y variable is called YVAR *; * 4. Variable to determine direction of arrow is UPDOWN *; * 5. Variable to determine relative postion of the label *; * is ADJLABEL. If the updown is positive then *; *; * adjlabel should be 1, 2, or 3. If updown is *; * negative then adjlabel should be 7, 8, or 9 *; * Each number stands for left, center, or right *; * 6. Variable to determine the direction of the arrow tail *; * and the label is ADJARROW. Positive values go to the *; * right and negative values go the left *; ***************************************************************************; * Set up the values *; * Tailclr - color of the 'tail' of the arrow *; * Tailsize - the thickness of the 'tail' *; * Taillen1 - length of the straight part of the tail *; * Taillen2 - length of the angled part of the tail *; * Headline - color of the outline of the arrow head *; * Headbord - thickness of the outline of the arrow head *; * Headwid - the width of the arrow head itself *; * Headsize - the length of the arrow head *; * Headfill - color of the interior of the arrowhead *; * Labelclr - color of the label at the end of tail *; * Labelsiz - the size of the labels *; ***************************************************************************; title 'Arrow Example with GPLOT'; data setup; tailclr='red'; /* color of the tail */; tailsize=8; /* thickness of the tail */; taillen1=.30 /* length of the 90 degree tail */; taillen2=.50; /* length of the angled tail */; headline='red'; /* color of the arrow head outline */; headbord=2; /* thickness of the arrow head outline */; headwid=.03; /* width of the arrow head */; headfill='yellow'; /* color of the interior of the arrow head */; headsize=.20; /* length of the arrow head */; labelclr='magenta'; /* color of the label of the variable */; labelsiz=1; /* size of the label */; shiftmov=.25; /* movement of the tail in either right or left */;; call symput('tailclr',tailclr); call symput('tailsize',tailsize); call symput('taillen1',taillen1); call symput('taillen2',taillen2); call symput('headline',headline); call symput('headbord',headbord); call symput('headfill',headfill); call symput('headwid',headwid); call symput('headsize',headsize); call symput('labelclr',labelclr); call symput('labelsiz',labelsiz); call symput('shiftmov',shiftmov); data temp1; /* updown controls whether the arrow goes up or down */; /* adjlabel controls the position of the label */; /* adjarrow controls whether the arrow goes left or right */; input xvar yvar mylabel $ updown adjlabel adjarrow; n=1; cards; -2 -200 labeln2 -1 7 -1 -1 -25 labeln -1 8 -1 1 100 label1 1 1 1 2 200 label2 -1 9 1 3 350 label3 1 2 -1 4 450 label4 -1 8 1 5 56 label5 1 2 -1 6 150 label6 -1 9 1 ; ***************************************************************************: * First we figure out the max and min variables *; ***************************************************************************; proc means n min max data=temp1; var xvar yvar; output out=limits min(xvar)=minxvar min(yvar)=minyvar max(xvar)=maxxvar max(yvar)=maxyvar ; *****************************************************************; * Once we know the limits we add some factors to increase the *; * plotting area so the annotation will always fit *; * We then merge it back into the original dataset *; *****************************************************************; data limits; set limits; n=1; minxvar=minxvar - (.10 * abs(maxxvar)); maxxvar=maxxvar + (.10 * abs(maxxvar)); minyvar=minyvar - ((3 * (&taillen1 + &taillen2)) * abs(maxyvar)); maxyvar=maxyvar + ((3 * (&taillen1 + &taillen2)) * abs(maxyvar)); movement=(((abs(maxxvar)) - (abs(minxvar))) * &shiftmov); data merge1; merge temp1 limits; by n; proc print data=merge1; run; ***************************************************************************; * Some key points here. first, note the xsys and ysys are set to 2 *; * and then they are matched to the x and y variables in your data. *; * You should also note the position variable as well I change it *; * depending on the location of the x variable so the annotate label *; * does not run off the graph. See position in the annotate dictonary *; * chapter in your graph reference for more details. *; ***************************************************************************; data anno1; set merge1; length function color style $ 8 text $ 10; function ='move'; color='red'; when= 'a'; style= 'swissb'; xsys= '2'; ysys= '2'; position= '1'; x = xvar ; y = yvar ; output; tailclr="&tailclr"; tailsize=&tailsize; taillen1=&taillen1; taillen2=&taillen2; headline="&headline"; headbord=&headbord; headfill="&headfill"; headwid=&headwid; headsize=&headsize; labelclr="&labelclr"; labelsiz=&labelsiz; shiftmov=&shiftmov; *** arrow head of pointer *********; function='move'; x=xvar;y=yvar; output; function='poly'; x=xvar-(headwid * abs(maxxvar)); y=yvar+((headsize * abs(maxyvar))) * updown; color=headfill; style='msolid'; size=2; output; function='polycont'; x=xvar+(headwid * abs(maxxvar)); y=yvar+((headsize * abs(maxyvar)) * updown); color=headfill; output; function='polycont'; x=xvar; y=yvar; color=headfill; output; ***** draw the arrowhead outline here ****; function='move'; x=xvar;y=yvar; output; function='draw'; x=xvar-(headwid * abs(maxxvar)); y=yvar+((headsize * abs(maxyvar)) * updown); color=headline; size=headbord; output; function='move'; x=xvar-(headwid * abs(maxxvar)); y=yvar+((headsize * abs(maxyvar)) * updown); function='draw'; x=xvar+(headwid * abs(maxxvar)); y=yvar+((headsize * abs(maxyvar)) *updown); color=headline; size=headbord; output; function='move'; x=xvar;y=yvar; output; function='draw'; x=xvar+(headwid * abs(maxxvar)); y=yvar+((headsize * abs(maxyvar)) * updown); color=headline; size=headbord; output; ****** draw the tail **********; ****** draw the first part of the tail ******; function='move'; x=xvar;y=yvar; output; function='draw'; x=xvar; y=yvar + (taillen1 * updown * (abs(maxyvar))); size=tailsize; when='b'; output; ****** draw the angled part of the tail ******; function='draw'; x=(xvar+(shiftmov * adjarrow)); y=(yvar + ((taillen1 + taillen2) * abs(maxyvar)* updown)); color=tailclr; size=tailsize; when='a'; output; ****** draw the label with the arrow ********; function='label'; x=(xvar+(shiftmov * adjarrow)); y=yvar +((taillen1 + taillen2) * abs(maxyvar)* updown); text=mylabel; size=labelsiz; style='swiss'; color=labelclr; position=adjlabel; output; run; ***************************************************************************; * now use the annotate dataset in your gplot *; ***************************************************************************; proc gplot data=merge1 anno=anno1; plot yvar * xvar maxyvar * maxxvar minyvar * minxvar/overlay; symbol1 i=join v=star c=blue; symbol2 v=none ; symbol3 v=none ; run;