Resources

Traveling Salesman Tour Through U.S. Capital Cities (onete07)


/**************************************************************************/
/*                                                                        */
/*          S A S   S A M P L E   L I B R A R Y                           */
/*                                                                        */
/*    NAME: onete07                                                       */
/*   TITLE: Traveling Salesman Tour Through U.S. Capital Cities (onete07) */
/* PRODUCT: OR                                                            */
/*  SYSTEM: ALL                                                           */
/*    KEYS: OR                                                            */
/*   PROCS: GMAP, GPROJECT, OPTNET, PRINT, SQL                            */
/*    DATA:                                                               */
/*                                                                        */
/* SUPPORT:                             UPDATE:                           */
/*     REF:                                                               */
/*    MISC: Example 7 from the OPTNET documentation.                      */
/*                                                                        */
/**************************************************************************/


/* Get a list of the state capital cities (with lat and long) */
proc sql;
   create table Cities as
   select unique statecode as state, city, lat, long
      from maps.uscity
      where capital='Y' and statecode not in ('AK' 'PR' 'HI');
quit;


/* Create a list of all the possible pairs of cities */
proc sql;
   create table CitiesDist as
   select
      a.city as city1, a.lat as lat1, a.long as long1,
      b.city as city2, b.lat as lat2, b.long as long2,
      geodist(lat1, long1, lat2, long2, 'DM') as distance
      from Cities as a, Cities as b
      where a.city < b.city;
quit;


/* Find optimal tour using OPTNET */
proc optnet
   loglevel   = moderate
   data_links = CitiesDist
   out_nodes  = TSPTourNodes;
   data_links_var
      from    = city1
      to      = city2
      weight  = distance;
   tsp
      out     = TSPTourLinks;
run;
%put &_OROPTNET_;
%put &_OROPTNET_TSP_;


/* Merge latitude and longitude */
proc sql;
   /* merge in the lat & long for city1 */
   create table TSPTourLinksAnno1 as
   select unique TSPTourLinks.*, cities.lat as lat1, cities.long as long1
      from TSPTourLinks left join cities
      on TSPTourLinks.city1=cities.city;
   /* merge in the lat & long for city2 */
   create table TSPTourLinksAnno2 as
   select unique TSPTourLinksAnno1.*, cities.lat as lat2, cities.long as long2
      from TSPTourLinksAnno1 left join cities
      on TSPTourLinksAnno1.city2=cities.city;
quit;


/* Create the annotated data set to draw the path on the map
    (convert lat & long degrees to radians, since the map is in radians) */
data anno_path;
   set TSPTourLinksAnno2;
   length function color $8;
   xsys='2'; ysys='2'; hsys='3'; when='a'; anno_flag=1;
   function='move';
   x=atan(1)/45 * long1;
   y=atan(1)/45 * lat1;
   output;
   function='draw';
   color="blue"; size=0.8;
   x=atan(1)/45 * long2;
   y=atan(1)/45 * lat2;
   output;
run;

/* Get a map with only the contiguous 48 states */
data states;
   set maps.states (where=(fipstate(state) not in ('HI' 'AK' 'PR')));
run;

data combined;
   set states anno_path;
run;


/* Project the map and annotate the data */
proc gproject data=combined out=combined dupok;
   id state;
run;

data states anno_path;
   set combined;
   if anno_flag=1 then output anno_path;
   else                output states;
run;


/* Get a list of the endpoints locations */
proc sql;
   create table anno_dots as
   select unique x, y from anno_path;
quit;


/* Create the final annotate data set */
data anno_dots;
   set anno_dots;
   length function color $8;
   xsys='2'; ysys='2'; when='a'; hsys='3';
   function='pie';
   rotate=360; size=0.8; style='psolid'; color="red";
   output;
   style='pempty'; color="black";
   output;
run;


/* Generate the map with GMAP */
pattern1 v=s c=cxccffcc repeat=100;
proc gmap data=states map=states anno=anno_path all;
   id state;
   choro state / levels=1 nolegend coutline=black
                 anno=anno_dots des='' name="tsp";
run;


/* Create the directed optimal tour */
data TSPTourLinksDirected(drop=next);
   set TSPTourLinks;
   retain next;
   if _N_ ne 1 and city1 ne next then do;
      city2 = city1;
      city1 = next;
   end;
   next = city2;
run;


proc print data=TSPTourLinksDirected noobs label;
   format distance comma10.2;
   var city1 city2 distance;
   sum distance;
run;