ODS Graphics take on a brewpub's NYE party invite. Happy New Year, all!
* Fun w/SAS ODS Graphics: New Year's Eve Analog Watch Countdown;
data clock1; * Generate points for clock seconds (text plot - filled circles);
pi=constant('pi');
do remaining=0 to 10; * Generate one "frame" for each countdown second;
do seconds=0 to 59;
tick=.;
xTxt=cos(seconds/60*2*pi); * Second markers aranged around circle of radius=1;
yTxt=sin(seconds/60*2*pi);
if mod(seconds,5) then output; * Every 5th second is plotted as a bar (see next step);
end;
end;
data clock2; * Generate points for clock seconds (vector plot line segments);
pi=constant('pi');
do remaining=0 to 10; * Generate one "frame" for each countdown second;
do seconds=0 to 55 by 5; * Every 5th second is plotted as a bar instead of circle;
vX1=.925*cos(seconds/60*2*pi); * Line segments extend beyond seconds markers;
vX2=1.075*cos(seconds/60*2*pi);
vY1=.925*sin(seconds/60*2*pi);
vY2=1.075*sin(seconds/60*2*pi);
output;
end;
end;
data clock3; * Generate points for clock seconds hand (vector plot line);
pi=constant('pi');
do seconds=50 to 60; * Generate one "frame" for each countdown second;
remaining=60-seconds;
vXsec=1.075*sin(seconds/60*2*pi); * Seconds hand extends to length of bars;
vYsec=1.075*cos(seconds/60*2*pi);
output;
end;
data clock4; * Generate points for clock minute hand (vector plot line);
pi=constant('pi');
do seconds=50 to 60; * Generate one "frame" for each countdown second;
remaining=60-seconds;
vXmin=1.075*sin((59*60+seconds)/(60*60)*2*pi); * Minutes hand extends to length of bars;
vYmin=1.075*cos((59*60+seconds)/(60*60)*2*pi);
output;
end;
run;
data clock5; * Generate points for clock hour hand (vector plot line);
pi=constant('pi');
do seconds=50 to 60; * Generate one "frame" for each countdown second;
remaining=60-seconds; * Hours hand is shorter than seconds/minutes hands;
vXhr=.7*sin((11*60*60+59*60+seconds)/(12*60*60)*2*pi);
vYhr=.7*cos((11*60*60+59*60+seconds)/(12*60*60)*2*pi);
output;
end;
run;
data clock6; * Generate countdown text, points (text plot);
do remaining=10 to 1 by -1; * Generate one "frame" for each countdown second;
text=compress(put(remaining,2.));
txtX=0; txtY=0;
output;
end;
data clock7; * Generate "Happy New Year" text, point (text plot);
remaining=0; * Last frame;
text2="Happy*New Year!*2018";
txt2X=0; txt2Y=0;
data clock; * Merge chart datasets;
set clock1 clock2 clock3 clock4 clock5 clock6 clock7;
proc sort data=clock; * Sort by countdown (desc seconds remaining);
by descending remaining;
proc format; * Unicode filled circle character for circles on clock;
value tickfmt other="(*ESC*){unicode '25CF'x}";
proc template; * Create animated GIF "clock" with countdown;
define statgraph newyearplot;
begingraph / opaque=true border=false drawspace=layoutpercent backgroundcolor=black;
layout overlayequated / equatetype=square border=false
WALLDISPLAY=NONE commonaxisopts=(viewmin=-1.15 viewmax=1.15 tickvaluelist=(-1.15 1.15))
xaxisopts=(display=none OFFSETMAX=0 OFFSETMIN=0) yaxisopts=(display=none OFFSETMAX=0 OFFSETMIN=0);
/* Countdown text (10.9.8.7.6.5.4.3.2.1) */
textplot x=txtX y=txtY text=text / position=center vcenter=bbox splitchar='*' contributeoffsets=none
textattrs=(size=216pt weight=bold color=silver) strip=true;
/* Watch dial seconds markers (cirlces) */
textplot x=xTxt y=yTxt text=tick / position=center vcenter=bbox strip=true contributeoffsets=none
textattrs=(size=18pt weight=bold color=silver);
/* Watch dial seconds markers (every 5th marker is a bar) */
vectorplot xorigin=vX1 x=vX2 yorigin=vY1 y=vY2 / lineattrs=(thickness=7pt color=silver)arrowheads=false;
/* Seconds hand */
vectorplot xorigin=0 x=vXsec yorigin=0 y=vYsec / lineattrs=(thickness=3pt color=white) arrowheads=false;
/* Minutes hand */
vectorplot xorigin=0 x=vXmin yorigin=0 y=vYmin / lineattrs=(thickness=4pt color=white) arrowheads=false;
/* Hours hand */
vectorplot xorigin=0 x=vXhr yorigin=0 y=vYhr / lineattrs=(thickness=9pt color=white) arrowheads=false;
/* Center of watch dial (white circle) */
ellipseparm xorigin=0 yorigin=0 semimajor=.07 semiminor=.07 slope=0 / display=(fill outline) fillattrs=(color=white) outlineattrs=(color=black);
/* Center of watch dial (black dot) */
ellipseparm xorigin=0 yorigin=0 semimajor=.03 semiminor=.03 slope=0 / display=(fill outline) fillattrs=(color=black) outlineattrs=(color=black);
/* Happy New Year! 2018 greeting */
textplot x=txt2X y=txt2Y text=text2 / position=center vcenter=bbox splitchar='*' splitpolicy=splitalways backlight=1
textattrs=(size=50pt weight=bold color=silver) contributeoffsets=none strip=true;
endlayout;
endgraph;
end;
options nobyline; * Animated GIF initialization;
ods _all_ close;
options papersize=('5 in', '5 in') printerpath=gif animation=start
nodate nonumber animloop=YES animduration=1 NOANIMOVERLAY ;
ods printer file='/folders/myfolders/HappyAnalogNewYear/HappyAnalogNewYear.gif';
ods graphics / width=5in height=5in imagefmt=GIF border=off;
* Generate 10-9-8-7-6-5-4-3-2-1 countdown frames;
proc sgrender data=clock(where=(remaining>0)) template=newyearplot;
by descending remaining;
format tick tickfmt.;
run; * Generate Happy New Year! 2018 frame;
options animduration=5; * Freeze last frame for 5 seconds;
proc sgrender data=clock(where=(remaining=0)) template=newyearplot;
format tick tickfmt.;
options printerpath=gif animation=stop; * Wrap-up animated GIF creation;
run;
ods printer close;
I love it! Just enough geometry at work to provide a math nerd fix.
Sorry I didn't see this before New Year's Eve - I would have projected it during our gathering as midnight approached.
SAS Innovate 2025 is scheduled for May 6-9 in Orlando, FL. Sign up to be first to learn about the agenda and registration!
Learn how use the CAT functions in SAS to join values from multiple variables into a single value.
Find more tutorials on the SAS Users YouTube channel.
Ready to level-up your skills? Choose your own adventure.