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;