%let name=lottery; filename odsout '.'; /* This is a proof-of-concept, showing how sas/graph 'proc gmap' can utilize a tiff image of a map (seating chart), and utilize the map's coordinate system to overlay markers and hotspots (for colored markers, charttips and drilldown, etc) in the correct location. */ /* Note that gmap always has 0,0 at bottom/left, and if your coordinate system is different, you can either rotate your map so that 0,0 is at the bottom/left, or apply a transformation to your coordinates to change them so that 0,0 is at the bottom/left. */ %let xsize=3300; %let ysize=2550; /* Size of your output page - this can be any size you want, but it makes the most sense to give it approx the same proportions as the map. */ goptions xpixels=800 ypixels=650; /* Create a sas/graph map data set, with one rectangular area in it, and the coordinates/proportions of this rectangular area need to correspond exactly to the x/y size of your scanned-in map image. */ data blank_map; idnum=1; x=0; y=0; output; x=symget('xsize'); y=0; output; x=symget('xsize'); y=symget('ysize'); output; x=0; y=symget('ysize'); output; run; /* Create an annotate dataset to place the scanned-in map image into the rectangular map area. */ data floorplan; length function $8 style $35 color $12 html $1000; xsys='2'; ysys='2'; hsys='3'; when='a'; function='move'; x=0; y=0; output; function='image'; x=&xsize; y=&ysize; imgpath='2005seatingchar.tif'; style='fit'; output; run; /* Give the x/y hotspots, and the text you want in their charttip */ data hotspots; input x y seatnum userid name $ 18-34 county $ 35-54 vote $ 55-64; datalines; 1225 825 1 38 BASNIGHT Dare Aye 1210 1000 2 48 RAND Cumberland Aye 1350 1280 3 42 SOLES Columbus Aye 1525 1385 4 13 WEINSTEIN Robeson Aye 1885 1390 5 30 DALTON Rutherford Aye 2035 1275 6 53 HAGAN Guilford Aye 2195 1000 7 8 KINNAIRD Orange No 2180 840 8 76 HOLLOMAN Hertford Aye 910 650 9 10 DANNELLY Mecklenburg Aye 870 800 10 51 PURCELL Scotland Aye 850 940 11 56 GARROU Forsyth Aye 860 1090 12 54 CLODFELTER Mecklenburg No 985 1430 13 62 SWINDELL Nash Aye 1110 1575 14 61 THOMAS Craven Aye 1290 1695 15 20 HARTSELL Cabarrus No 1500 1800 16 119 NESBITT Buncombe No 1910 1810 17 2 ALLRAN Catawba No 2120 1725 18 27 SHAW Cumberland Aye 2290 1610 19 156 BOSEMAN New Hanover Aye 2400 1480 20 143 ATWATER Chatham Aye 2530 1110 21 135 SNOW Cherokee Aye 2550 950 22 145 BERGER, D. Franklin Aye 2530 800 23 153 COWELL Wake No 2490 660 24 192 GRAHAM Mecklenburg Aye 610 480 25 29 LUCAS Durham Aye 550 585 26 110 MALONE Wake Aye 510 710 27 106 DORSETT Guilford Aye 490 825 28 9 ALBERTSON Duplin No 475 948 29 71 JENKINS Edgecombe Aye 455 1090 30 14 HOYLE Gaston Aye 475 1200 31 35 KERR Wayne Aye 600 1600 32 24 HORTON Forsyth No 690 1725 33 115 STEVENS Wake No 790 1875 34 63 BINGHAM Davidson No 910 1990 35 16 GARWOOD Wilkes Absent 1090 2080 36 28 FORRESTER Gaston No 1270 2170 37 144 PRESNELL Yancey No 1445 2220 38 83 APODACA Henderson No 1970 2230 39 64 BERGER, P. Rockingham No 2140 2180 40 162 JACUMIN Burke No 2310 2100 41 139 BROWN Onslow Absent 2450 2000 42 109 SMITH Johnston No 2595 1890 43 127 GOODALL Union No 2700 1740 44 91 PITTENGER Mecklenburg No 2790 1630 45 26 WEBSTER Alamance No 2930 1225 46 129 EAST Surry No 2950 1090 47 118 BLAKE Moore No 2950 980 48 99 TILLMAN Randolph No 2930 840 49 200 HUNT Wake No 2900 715 50 104 BROCK Davie No ; run; /* Annotate a color legend for the votes */ data anno_legend; length function $8 style $35 color $12 html $1000 text $100; xsys='3'; ysys='3'; hsys='3'; when='a'; function='PIE'; rotate=360.0; size=1.5; x=3; y=82; style='psolid'; color='cx00ff00'; output; style='pempty'; color='gray'; output; y=y-5; style='psolid'; color='red'; output; style='pempty'; color='gray'; output; y=y-5; style='psolid'; color='gray'; output; style='pempty'; color='gray'; output; y=y-5; style='psolid'; color='white'; output; style='pempty'; color='gray'; output; function='label'; size=3; style=''; position='6'; color=''; x=5; y=82; text='Aye'; output; y=y-5; text='No'; output; y=y-5; text='N/V'; output; y=y-5; text='Absent'; output; run; /* Turn the coordinates and text into a sas annotate data set */ data hotspots; set hotspots; length function $8 style $35 color $12 html $1000 text $100; xsys='2'; ysys='2'; hsys='3'; when='a'; function='pie'; style='psolid'; rotate=360; size=1.5; if vote eq 'Aye' then do; color='cx00ff00'; end; else if vote eq 'No' then do; color='cxff0000'; end; else if vote eq 'Absent' then do; color='white'; end; else do; color='gray'; end; output; length html $250; html= 'title='||quote( trim(left(name))||' from '||trim(left(county))||' county '||'Voted: '||trim(left(vote)))|| ' href="http://www.ncga.state.nc.us/gascripts/members/viewMember.pl?sChamber=Senate&nUserID='||trim(left(userid))||'"'; style='pempty'; color='gray'; output; run; /* Annotate some extra text in the bottom 1/2 of the floorplan, where it's difficult to use titles & footnotes */ data extra_text; length style $35 text $100 html $500; xsys='3'; ysys='3'; hsys='3'; function='label'; color=''; when='a'; position='6'; style='albany amt/bold'; html='href="http://www.ncleg.net/gascripts/BillLookUp/BillLookUp.pl?Session=2005&BillID=H1023"'; size=4; x=2; y=95; text='Bill HB-1023: North Carolina State Lottery Act.'; output; style=''; size=3; x=2; y=y-5; text='(8/30/2005 vote)'; output; html=''; position='5'; style='albany amt/bold'; x=52; size=4; y=18; text='SENATE'; output; style=''; size=3; y=y-7; text='2005 Session'; output; size=3; y=y-5; text='North Carolina General Assembly'; output; run; data my_anno; length style $35; set hotspots extra_text anno_legend; run; /* ----------------------------------------------------------------- */ /* Now, plot the same data on a North Carolina county map */ data mymap; set mapsgfk.us_counties (where=(statecode='NC' and density<=2)); run; proc gproject data=mymap out=mymap latlong eastlong degrees dupok; id state; run; /* Now, create a 'centers' dataset with the estimated centers of each area */ %annomac; %centroid( mymap, centers, county ); proc sql; /* Merge in the county names */ create table centers as select centers.*, propcase(cntyname.countynm) as countynm from centers left join maps.cntyname on cntyname.state=stfips('NC') and centers.county=cntyname.county; /* Now, get the Senator voting data */ create table map_hotspots as select unique name, county, userid, vote from hotspots; create table map_hotspots as select unique *, count(*) as county_count from map_hotspots group by county; /* And merge in the x/y centers for those counties */ create table map_hotspots as select unique map_hotspots.*, centers.* from map_hotspots left join centers on map_hotspots.county = centers.countynm order by county; quit; run; /* For multiple obsns per county, calculate a number for offset/placement of markers */ data map_hotspots; set map_hotspots; by county; if first.county then num=1; else num+1; run; data map_hotspots; set map_hotspots; length function $8 style $35 color $12 html $1000; xsys='2'; ysys='2'; hsys='3'; when='a'; function='pie'; style='psolid'; rotate=360; size=1.5; x=x-((county_count-1)*(.0012/2))+((num-1)*.0012); if vote eq 'Aye' then color='cx00ff00'; else if vote eq 'No' then color='cxff0000'; else if vote eq 'Absent' then color='white'; else color='gray'; output; length html $300; html= 'title='||quote( trim(left(name))||' from '|| trim(left(county))||' county '||'Voted: '||trim(left(vote)))|| ' href="http://www.ncga.state.nc.us/gascripts/members/viewMember.pl?sChamber=Senate&nUserID='||trim(left(userid))||'"'; style='pempty'; color='gray'; output; run; data my_map_anno; length style $35; set map_hotspots extra_text anno_legend; run; goptions device=png; goptions noborder; ODS LISTING CLOSE; ODS HTML path=odsout body="&name..htm" (title="2005 NC Senate Lottery Vote") style=htmlblue; goptions ftitle="albany amt/bold" ftext="albany amt" htitle=4pct htext=3pct; goptions ctext=gray33; title; pattern1 value=solid color=white; proc gmap data=mymap map=mymap; id county; choro county / levels=1 nolegend coutline=gray33 anno=my_map_anno des='' name="&name"; run; pattern1 value=solid color=white; proc gmap data=blank_map map=blank_map anno=floorplan; id idnum; choro idnum / nolegend anno=my_anno coutline=white des='' name="&name"; run; quit; ODS HTML CLOSE; ODS LISTING;