BookmarkSubscribeRSS Feed
tc
Lapis Lazuli | Level 10 tc
Lapis Lazuli | Level 10

ANIMATED GIF OUTPUT (Click on image or magnifying glass to view larger image at full speed!)

Santa.gif

 

How Santa solves his massive traveling salesman problem remains a mystery (SAS Viya on quantum computers?). So, in the spirit of Santa's DasherBoard (SAS VA-based, from @TravisMurphy), Optimal Santa Route (SAS VA-based, from, @FalkoSchulz), and Santa's Information Dashboard (SAS/GRAPH-based, from @ RobertAllison_SAS), here's my decidedly less-ambitious SAS ODS Graphics-based 'Traveling Santa Tour' through the capital cities of the contiguous 48 states using an optimized 'flight plan' lifted from a PROC OPTNET example. Happy Holidays, all!

 

CODE

* Fun With SAS ODS Graphics: Animated "Traveling Santa Tour" Thru U.S. Capital Cities;

data FlightPlan;                        * Create SAS dataset of cities in Santas "flight plan";                    
infile datalines dlm=',' dsd;           * Cities are output of PROC OPTNET example in SAS documentation;
input capital : $30.@@;                 * See "Example 2.7 Traveling Salesman Tour through US Capital Cities";
if capital^='';                         * support.sas.com/documentation/cdl/en/ornoaug/66084/HTML/default/viewer.htm#ornoaug_optnet_examples07.htm;
seq+1;                                  * Add sequence number for tour path;
datalines;
Montgomery, Montgomery, Tallahassee, Columbia, Raleigh, Richmond, Washington, Annapolis, Dover, Trenton, Hartford, 
Providence, Boston, Concord, Augusta, Montpelier, Albany, Harrisburg, Charleston, Columbus, Lansing, 
Madison, Saint Paul, Bismarck, Pierre, Cheyenne, Denver, Salt Lake City, Helena, Boise City, Olympia, Salem, 
Sacramento, Carson, Phoenix, Santa Fe, Oklahoma City, Austin, Baton Rouge, Jackson, Little Rock, Jefferson City, 
Topeka, Lincoln, Des Moines, Springfield, Indianapolis, Frankfort, Nashville, Atlanta, Montgomery
;
proc sql;                               * Get Geo info for cities from SAS USCITY map dataset (SASFILES points to SAS Map datasets);             
create table capitalsGeo(keep=seq capitalCity stateCode x y lat long rename=(x=xC y=yC)) as
select f.seq, 
       upcase(case when f.capital='Carson' then 'Carson City' else f.capital end) as capitalCity, /* Fix: add 'City' to 'Carson' */ 
       u.StateCode, u.x, u.y, u.lat, u.long
from flightplan f join sasfiles.uscity u on u.capital='Y' and f.capital=u.city order by seq;

                                        * Create dataset with info needed to chart Santas route;
data CapitalsGeoLines(keep=seq xC yC x1 y1 x2 y2 CapitalCity: StateCode: miles);
set CapitalsGeo end=eof;                * Get data for "FROM" city from prior observation;
x1=lag(xC); y1=lag(yC); lat1=lag(lat); long1=lag(long); CapitalCity1=lag(CapitalCity); StateCode1=lag(StateCode);
x2=xC; y2=yC;                           * X2/Y2=points for "TO" City;
if ^eof then CapitalCityLbl=CapitalCity;* Do not repeat starting city name at end of tour;
miles=geodist(lat1, long1, lat, long, 'M');
if _n_>1;                               * No line for first point;
seq=seq-1;                              * Re-adjust seq # to account for skipped point;

data usa(keep=state segment x y pid);   * Get outlines of 48 contiguous U.S. states from SAS US dataset;
set sasfiles.us(where=(statecode not in('AK','HI', 'PR'))); * Remove AK HI PR;
length pid $ 8;                         * State outlines are one or more polygons;
pid=put(state, 3.0)||put(segment, 3.0); * Generate unique polygon identifier;
                                        * For more info, see 'Micro Maps' at blogs.sas.com/content/graphicallyspeaking/2015/04/19/micro-maps/;
data capitalGeoUSA;                     * Merge city points and state outline data;
set usa capitalsGeoLines;

proc template;                          * Template for Santa Tour Map (chart is composite of multiple plots);
define statgraph Maps;                
mvar FromCity ToCity Distance TotDistance; * Macro variables for inset box;
begingraph / subpixel=on border=false;
                                        * Microsoft Windows 10 Santa emoji image (no art budget!);
symbolimage name=santa image='/folders/myfolders/Windows10Santa.png';
layout overlayEquated / walldisplay=none border=false
                        xaxisopts=(offsetmin=0.0 offsetmax=0.0 display=none)
                        yaxisopts=(offsetmin=0.0 offsetmax=0.0 display=none);
                                        * 1. Polygon plot of 48 contiguous U.S. states;
polygonPlot x=x y=y id=pid / display=(fill outline) fillattrs=(color=CXc4cace) outlineattrs=(color=white); * "Metallic silver" fill;
                                        * 2. Vector plot of lines between cities visited;
vectorplot x=x2 y=y2 xorigin=x1 yorigin=y1 / lineattrs=(color=black); 
                                        * 3. Scatter plot of cities visited - circles;                           
scatterplot x=xC y=yC / markerattrs=(symbol=circle color=black) datalabel=capitalCityLbl datalabelattrs=(color=black);  
                                        * 4. Scatter plot of last city visited - Win10 Santa emoji marker;
scatterplot x=xS y=yS / markerattrs=(symbol=santa color=black size=30);   
                                        * 5. Chart title;                                             
entry halign=center " A Traveling Santa Tour of U.S. State Capitals " / valign=top textattrs=(size=15 color=CX296e01 weight=bold); * "Metallic green";
                                        * 6. Inset box of from/to cities and distances in lower left corner;
layout gridded / rows=4 order=columnmajor valign=bottom halign=left border=true;
  entry halign=left "From: " / textattrs=(size=11 color=CXa62c2b weight=bold); * "Metallic red";
  entry halign=left "To: " / textattrs=(size=11 color=CXa62c2b weight=bold);
  entry halign=left "Distance: " / textattrs=(size=11 color=CXa62c2b weight=bold);
  entry halign=left "Total: " / textattrs=(size=11 color=CXa62c2b weight=bold);
  entry halign=left FromCity / textattrs=(size=11 color=CXa62c2b weight=bold);
  entry halign=left ToCity / textattrs=(size=11 color=CXa62c2b weight=bold);
  entry halign=left Distance / textattrs=(size=11 color=CXa62c2b weight=bold);
  entry halign=left TotDistance / textattrs=(size=11 color=CXa62c2b weight=bold);
endlayout;
                                        * 6. Larger Win10 Santa emoji image in lower right corner; 
drawimage "/folders/myfolders/Windows10Santa.png" / anchor=bottomright x=100 y=0 drawspace=wallpercent height=18 heightunit=percent scale=fitheight;
endlayout;
endgraph;
end;
run;

ods _all_ close;                        * Create animated GIF for 'Santa Tour';
options papersize=('11 in', '6.6 in') printerpath=gif animation=start 
        nodate nonumber animduration=2 animloop=NO NOANIMOVERLAY;
ods printer file='/folders/myfolders/Santa.gif';
ods graphics / height=6.8in width=11in imagefmt=gif antialias labelmax=2000; * Specify labelmax to minimize city name "collisons";

%macro drawlines; 
proc sql noprint;                       * Get number of frames to draw (# of from/to pairs);
select count(*) into :numlines from CapitalsGeoLines;
quit;

%do l=1 %to &numlines;                  * Draw "frames" containing image with cities visited thus far; 
  %if &l=1 | &l=&numlines %then options animduration=2; %else options animduration=.75;; * Make first/last frames longer;
  data capitalGeoUSAwork;               * Dataset for frame 1 has 1 city, 2 has 2 cities, ...;
  set capitalGeoUSA(where=(seq<=&l));
  totmiles+miles;                       * Tally cumulative miles;
  if seq=&l then do;                    * Create points, macro variables for last line drawn; 
    xS=xC; yS=yC;                       * X/Y coordinates for Santa Claus location;
    call symput('FromCity',trim(CapitalCity1)||', '||StateCode1);          * From city/state;
    call symput('ToCity',trim(CapitalCity)||', '||StateCode);              * To city/state;
    call symput('Distance',compress(put(miles,comma6.),' ')||' mi');       * Miles (current leg);
    call symput('TotDistance',compress(put(totmiles,comma6.),' ')||' mi'); * Cumulative miles;
    end;                                * Render frame;
 proc sgrender data=capitalGeoUSAwork template=Maps; 
run; 
%end;
%mend;

%drawlines;                             * Draw the frames;
options printerpath=gif animation=stop; * All done - close animated GIF file;
ods printer close;

 

MICROSOFT WINDOWS 10 SANTA EMOJI (Windows10Santa.png)

Windows10Santa.png

 

2 REPLIES 2
t75wez1
Pyrite | Level 9

Hello TC,

That's a super fantastic data visualization created by SAS I ever see!

 

I wonder if it can be done in conjunction with "proc sgmap" with a background of openstreetmap.

 

Also I'm more interested in created similar animated graph based on 'smaller geography'(e.g  connected 5-10 retail stores locations in zip code level).

 

Could you please shed some lights on that?

 

thanks,

 

Ethan

tc
Lapis Lazuli | Level 10 tc
Lapis Lazuli | Level 10

Hey, Ethan. Hopefully someone from SAS (I'm not!) will field your question. Ten minutes ago, I would have said no, but a quick Google search turned up an example of the SGMAP SERIES statement with OPENSTREEMAP ("Series Plot of Hurricane Katrina’s Path"), which seems to be along the lines of what you're thinking of. SGMAP is still pretty new, so I wouldn't be surprised if the SAS folks have more tricks up their sleeves. Smiley Happy

sas-innovate-2024.png

Available on demand!

Missed SAS Innovate Las Vegas? Watch all the action for free! View the keynotes, general sessions and 22 breakouts on demand.

 

Register now!

Mastering the WHERE Clause in PROC SQL

SAS' Charu Shankar shares her PROC SQL expertise by showing you how to master the WHERE clause using real winter weather data.

Find more tutorials on the SAS Users YouTube channel.

Discussion stats
  • 2 replies
  • 1496 views
  • 3 likes
  • 2 in conversation