COMPLETE IMAGE
DETAIL
After seeing Sanjay Matange's blog post on the use of a text plot to create a clock face, thought I'd take a shot at creating time-and-temperature clock faces from temperature forecast XML data obtained from weather.gov (cold week ahead in Chicago!). OK, it's no threat to the cool Apple Watch Weather app, but it does have a literal "heat map" going for it.
Btw, presenting temperature data "radially" was the subject of a recent Microsoft Research study. And way back in 2012, Sanjay Matange and Robert Alison respectively presented their SAS ODS Graphics and SAS/GRAPH takes on Ken Kleinman's interesting R-based approach.
CODE
*==> Fun w/SAS ODS Graphics: Time and Temperature "Watch Faces";
*==> Grab 7 days of forecasted hourly temperatures from weather.gov XML (Chicago);
filename TimeTemp url 'https://forecast.weather.gov/MapClick.php?lat=41.8843&lon=-87.6325&FcstType=digitalDWML';
filename map '/folders/myfolders/TimeTemp.map'; * XML map to grab times/temps;
libname TimeTemp xmlv2 xmlmap=map;
data timetemp; * Create dataset with 5 days of hourly temperatures;
set timetemp.times; * Merge time and temperature data;
set timetemp.temps(where=(type='hourly')); * Just hourly temps;
hr=hour(time); * Hour (0-23);
if hour(time)< 12 then AmPm='AM'; else AmPm='PM'; * AM or PM?;
hr12=mod(hr,12); * Change 0-23 to 0-11;
day=put(datepart(time),downame3.); * Grab day of week;
if '12nov2018'd<=datepart(time)<='16nov2018'd; * Select 5 complete days;
*==> Calc x/y points for "watch faces";
data watch;
array hrs[12] _TEMPORARY_ (3 2 1 0 11 10 9 8 7 6 5 4); * Map 30-degree wedges to hour of day;
do w=0 to 11; * Create 12 polygons for wedges;
hr12=hrs[w+1]; * Find hour for 30 degree wedge;
seq=0; * Polygon ID;
x=0; y=0; output; * Polygons start at 0,0;
do a=2*constant("pi")/12*(w-.5) to 2*constant("pi")/12*(w+.5) by 0.01;
seq+1; * Polygon sequence number;
x=cos(a); * Assume circle is radius=1;
y=sin(a);
output;
end;
xd=x; yd=y; * 0,0 to xd,yd are "divider line" points for wedges;
x=.; y=.;
output;
xd=.; yd=.;
a=2*constant("pi")/12*w; * xt, yt are points for temperature values;
xt=1.125*cos(a); * Place temperatures outside of unit circle (radius=1);
yt=1.125*sin(a);
output;
xt=.; yt=.;
end;
proc sql; * Merge temperature and "watch face" data;
create table tempswatch as
select w.*, put(t.temp,3.) as txt, t.day, t.temp, t.time, t.ampm
from timetemp t join watch w on t.hr12=w.hr12
order by datepart(t.time), w, seq;
ods _all_ close;
ods listing gpath='/folders/myfolders' image_dpi=300;
ods graphics / reset imagename='TimeTemp' height=15in width=6in noborder antialias imagefmt=png;
* Create chart - AM/PM panels for each day;
proc sgpanel data=tempswatch noautolegend aspect=1 subpixel;
panelby ampm day / onepanel uniscale=all columns=2 layout=lattice noheader noborder sort=data;
* Display day of week, AM/PM indicator in upper left corner;
inset Day ampm / nolabel position=topleft textattrs=(color=black weight=bold);
* Polygon plot hourly wedges - fill colors vary with temperature;
polygon x=x y=y id=w / fill nooutline colorresponse=temp colormodel=(lightblue darkblue);
* Vector plot divider lines;
vector x=xd y=yd / lineattrs=(color=white thickness=1.5 pattern=solid) noarrowheads;
* Text plot temperature values;
text x=xt y=yt text=txt /
position=center textattrs=(color=black weight=bold) strip;
colaxis display=none min=1.2 max=1.2;
rowaxis display=none min=1.2 max=1.2;
XML MAP
<?xml version="1.0" encoding="UTF-8"?> <SXLEMAP version="1.2" name="SXLEMap"> <!-- Create TIME/TEMP SAS Datasets From Weather.gov XML --> <TABLE name="TIMES"> <TABLE-PATH>//data/time-layout/start-valid-time</TABLE-PATH> <COLUMN name="TIME"> <PATH>//data/time-layout/start-valid-time</PATH> <TYPE>numeric</TYPE> <DATATYPE>date</DATATYPE> <FORMAT width="19">E8601DT</FORMAT> <INFORMAT width="19">B8601DT</INFORMAT> </COLUMN> </TABLE> <TABLE name="TEMPS"> <TABLE-PATH>//data/parameters/temperature/value</TABLE-PATH> <COLUMN name="TYPE" retain="YES"> <PATH>//data/parameters/temperature/@type</PATH> <TYPE>character</TYPE> <DATATYPE>string</DATATYPE> <LENGTH>20</LENGTH> </COLUMN> <COLUMN name="TEMP"> <PATH>//data/parameters/temperature/value</PATH> <TYPE>numeric</TYPE> <DATATYPE>integer</DATATYPE> <LENGTH>8</LENGTH> </COLUMN> </TABLE> </SXLEMAP>
Nice work, Tc.
Perfect segue to new SGPIE procedure (SAS 9.4M6) that could likely help do this graph.
Also, we are working on new GTL container called LAYOUT OVERLAYPOLAR, that will support (R, Theta) coordinates with multiple plot types. Here is an early example of a HeatMapParm. The Radial axis is intentionally displayed at an angle.
Yes, this post motivated by Elon Musk's Twitter posts. 🙂
Happy to see use of INSETs instead of Row and Column headers. 🙂
Available on demand!
Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.
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.
Select SAS Training centers are offering in-person courses. View upcoming courses for: