%let name=fall_leaves_2017; filename odsout '.'; /* SAS version of: https://rud.is/b/2017/09/18/mapping-fall-foliage-with-sf/ Using data from: https://smokymountains.com/wp-content/themes/smcom-2015/js/ such as ... https://smokymountains.com/wp-content/themes/smcom-2015/js/foliage-2017.csv */ data my_map; set mapsgfk.us_counties (where=(density<=2 and statecode not in ('AK' 'HI' 'PR')) drop=resolution); run; proc gproject data=my_map out=my_map latlong eastlong degrees; id state county; run; /* annotate state outlines on the county map */ data my_map; set my_map; original_order=_n_; run; proc sort data=my_map out=my_map; by state original_order; run; proc gremove data=my_map out=anno_outline; id state county; by state; run; data anno_outline; set anno_outline; by state segment notsorted; xsys='2'; ysys='2'; hsys='3'; when='a'; length function $8; color='gray77'; style='empty'; size=.01; if first.state or first.segment then function='poly'; else function='polycont'; run; /* Read the data from the Internet */ filename myfile url "https://smokymountains.com/wp-content/themes/smcom-2015/js/foliage-2017.csv"; /* filename myfile 'foliage-2017.csv'; */ data my_data (drop = whole_line id); infile myfile firstobs=2 truncover dlm='09'x termstr=CR; input whole_line $ 1-150; length id $10; id=scan(whole_line,1,'09'x); state=.; state=substr(put(input(id,best5.),z5.),1,2); county=.; county=substr(put(input(id,best5.),z5.),3,5); rate_1=.; rate_1= scan(whole_line,2, '09'x); rate_2=.; rate_2= scan(whole_line,3, '09'x); rate_3=.; rate_3= scan(whole_line,4, '09'x); rate_4=.; rate_4= scan(whole_line,5, '09'x); rate_5=.; rate_5= scan(whole_line,6, '09'x); rate_6=.; rate_6= scan(whole_line,7, '09'x); rate_7=.; rate_7= scan(whole_line,8, '09'x); rate_8=.; rate_8= scan(whole_line,9, '09'x); rate_9=.; rate_9= scan(whole_line,10,'09'x); rate_10=.; rate_10=scan(whole_line,11,'09'x); rate_11=.; rate_11=scan(whole_line,12,'09'x); rate_12=.; rate_12=scan(whole_line,13,'09'x); rate_13=.; rate_13=scan(whole_line,14,'09'x); run; proc sort data=my_data out=my_data; by state county; run; proc transpose data=my_data out=my_data; by state county; run; data my_data (drop=_name_); set my_data (rename=(col1=rate)); weeknum=.; weeknum=scan(_name_,2,'_'); format date nldate20.; date='27aug2017'd+((weeknum-1)*7); run; proc sort data=my_data out=my_data; by date; run; proc format; value leaffmt .01='No Change' 0.1='Minimal' 0.2='Patchy' 0.3='Partial' 0.4='Near Peak' 0.5='Peak' 0.6='Past Peak' ; run; proc sql noprint; create table anno_date as select unique date from my_data order by date; quit; run; data anno_date; set anno_date; xsys='3'; ysys='3'; hsys='3'; when='a'; function='label'; position='5'; size=7.0; color='gray33'; x=50; y=90; length text $100; text=trim(left(put(date,nldate20.))); run; /* This is the 'magic' that makes the gif animation! */ options dev=sasprtc printerpath=gif animduration=1.1 animloop=0 animoverlay=no animate=start center nobyline; ODS LISTING CLOSE; ODS HTML path=odsout body="&name..htm" (title="Fall Leaves - 2017") style=htmlblue; goptions gunit=pct htitle=3.5 htext=2.5 ftitle="albany amt/bold" ftext="albany amt/bold"; goptions ctext=gray33; pattern1 v=s c=cx80a97c; pattern2 v=s c=cxfdf6af; pattern3 v=s c=cxffca5e; pattern4 v=s c=cxf18d44; pattern5 v=s c=cxf03b21; pattern6 v=s c=cxbd2122; pattern7 v=s c=cx9a3420; legend1 label=none across=1 position=(bottom right) mode=share value=(j=l) order=descending shape=bar(.15in,.15in) offset=(.8,13); options nobyline; title1 ls=2.0 "Fall leaf colors during the week of ..."; title2 a=-90 h=3 ' '; /* extra white-space on east side, for legend */ proc gmap data=my_data map=my_map all uniform anno=anno_outline; by date; id state county; format rate leaffmt.; choro rate / discrete midpoints = .01 0.1 0.2 0.3 0.4 0.5 0.6 coutline=same legend=legend1 anno=anno_date des='' name="&name"; run; /* Make the gif animation delay on the last frame for a few seconds */ proc gmap data=my_data (where=(date='19nov2017'd)) map=my_map all uniform anno=anno_outline; by date; id state county; format rate leaffmt.; choro rate / discrete midpoints = .01 0.1 0.2 0.3 0.4 0.5 0.6 coutline=same legend=legend1 anno=anno_date des='' name="&name"; /* the delay frame will be repeated however many times you use 'run' */ run; run; /* Add mouse-over text with state name on very last frame */ data anno_outline; set anno_outline; length html $100; html='title='||quote(trim(left(fipnamel(state)))); run; proc gmap data=my_data (where=(date='19nov2017'd)) map=my_map all uniform anno=anno_outline; by date; id state county; format rate leaffmt.; choro rate / discrete midpoints = .01 0.1 0.2 0.3 0.4 0.5 0.6 coutline=same legend=legend1 anno=anno_date des='' name="&name"; run; quit; ODS HTML CLOSE; ODS LISTING;