%let name=nc_congressional_districts_2020; filename odsout '.'; /* Got data from ... https://www.ncleg.gov/BillLookup/2019/H1029?fbclid=IwAR2vSxitF33-Jr71b_UW-Rd5s6v8HqbQGzCZzsXMykxMBH3zlyvT1HXe8qw Click on "Show all Associated Documents" and then "Shapefile" is one of the available documents. When you click the 'Shapefile' link, it downloads https://webservices.ncleg.net/ViewBillDocument/2019/6953/0/HB%201029,%203rd%20Edition%20-%20Shapefile "HB 1029, 3rd Edition - Shapefile.zip" */ /* Import the congressional district map */ proc mapimport datafile="nc_congressional_districts_2020_shapefile/C-Goodwin-A-1-TC.shp" out=raw_data; run; /* Convert the projected x/y to long/lat */ proc gproject data=raw_data out=cd_map dupok from="EPSG:32119" to="EPSG:4326"; id district; run; data cd_map; set cd_map (rename=(x=long y=lat) drop=population); district_numeric=.; district_numeric=district; run; /* Then I project it the way I want to project it, saving the projection parameters so I can use them to project other things the same way */ proc gproject data=cd_map out=cd_map dupok latlong eastlong degrees dupok project=albers parmout=work.projparm; id district; run; data anno_county; set mapsgfk.us_counties (where=(statecode='NC' and density<=2) drop = x y); run; proc gproject data=anno_county out=anno_county latlong eastlong degrees dupok parmin=work.projparm parmentry=cd_map; id state county; run; data anno_cities; set mapsgfk.uscity (where=(statecode='NC' and city in ('Raleigh' 'Charlotte' 'Winston-Salem' 'High Point' 'Greensboro' 'Greenville' 'Rocky Mount' 'Fayetteville' 'Wilmington' 'Cary' 'Durham' 'Concord' 'Lumberton' 'Hickory' 'Elizabeth City' 'Goldsboro' 'New Bern' 'Jacksonville' 'Asheville' 'Gastonia' 'Chapel Hill'))); run; proc gproject data=anno_cities out=anno_cities latlong eastlong degrees dupok parmin=projparm parmentry=cd_map; id; run; /* annotate the district numbers at specific lat/long positions */ data anno_numbers; input district $ 1-2 lat long; datalines; 1 36.3601406 -77.7982834 2 35.6789216 -78.7192947 3 35.3488716 -76.5060403 4 36.0398352 -78.4030813 5 36.0064505 -81.3598035 6 36.0036053 -79.6569036 7 34.6449970 -78.5272799 8 35.3244959 -79.8085965 9 34.9682102 -79.8092623 10 36.3444298 -80.4176121 11 35.4583183 -83.0916127 12 35.3236809 -80.8557225 13 35.6987125 -79.8235151 ; run; proc gproject data=anno_numbers out=anno_numbers latlong eastlong degrees dupok parmin=projparm parmentry=cd_map; id; run; /* annotate county borders (light gray) */ data anno_county; set anno_county; by county segment notsorted; xsys='2'; ysys='2'; hsys='3'; when='a'; length function $8 color $12 style $35; color="grayaa"; style='empty'; size=1.0; if first.county or first.segment then function='poly'; else function='polycont'; run; /* annotate congressional district borders (very dark gray) */ data anno_cd; set cd_map; by district segment notsorted; xsys='2'; ysys='2'; hsys='3'; when='a'; length function $8 color $12 style $35; color="gray22"; style='empty'; size=1.0; if first.district or first.segment then function='poly'; else function='polycont'; run; data anno_cities; set anno_cities; xsys='2'; ysys='2'; hsys='3'; when='a'; length function $8 color $12 style $35; function='pie'; style='psolid'; rotate=360; size=0.6; color='blue'; output; function='label'; position='2'; style=''; rotate=.; size=2.5; color='blue'; text=trim(left(city)); if city='Chapel Hill' then position='1'; if city='Cary' then position='d'; if city='Raleigh' then position='f'; if city='Winston-Salem' then position='1'; if city='Gastonia' then position='1'; if city='Charlotte' then position='8'; if city='Hickory' then position='f'; if city='Concord' then position='f'; if city='High Point' then position='8'; if city='Elizabeth City' then position='8'; output; run; data anno_numbers; set anno_numbers; xsys='2'; ysys='2'; hsys='3'; when='a'; length function $8 color $12 style $35 text $100; function='label'; position='+'; style='albany amt/bold'; rotate=.; size=5.5; color='gray33'; text=trim(left(district)); run; data anno_titles; xsys='3'; ysys='3'; hsys='3'; when='a'; length function $8 color $12 style $35 text $100; function='label'; position='+'; style='albany amt'; rotate=.; size=5.7; color='gray33'; x=28; y=22; text='NC Congressional Districts'; output; y=y-9; text='2020 Election'; output; run; /* combine all the annotate layers, in the desired order */ data anno_layers; set anno_county anno_cd anno_numbers anno_titles anno_cities; run; proc sql noprint; create table my_data as select unique district_numeric from cd_map; quit; run; data my_data; set my_data; length my_html $300; my_html='title='||quote('2020 NC Congressional District '||trim(left(district_numeric))); run; goptions device=png; goptions xpixels=1200 ypixels=500; ODS LISTING CLOSE; ODS HTML path=odsout body="&name..htm" (title="NC Congressional Districts - 2020") style=htmlblue; goptions gunit=pct htitle=20pt htext=11pt ftitle="albany amt" ftext="albany amt"; goptions ctext=gray33; /* Original colors */ /* %let color1=cxfebdbf; %let color2=cxd2ff7b; %let color3=cxb2945d; %let color4=cxf5f5dc; %let color5=cxc19ed5; %let color6=cxbfe8fe; %let color7=cxfffe7d; %let color8=cxf4c97f; %let color9=cxcb6599; %let color10=cx79b35d; %let color11=cxfd7e57; %let color12=cx94a4f2; %let color13=cx9c9c9c; */ %let color1=cxefff09; %let color2=cxa9fffe; %let color3=cxe1caaa; %let color4=cxc6e446; %let color5=cxfdfbb0; %let color6=cxce9ffb; %let color7=cxff8ec2; %let color8=cxa9b9f7; %let color9=cxfcb2ff; %let color10=cxfe5ec8; %let color11=cxe3f63c; %let color12=cxffc229; %let color13=cx95ff39; /* */ pattern1 v=s c=&color1; pattern2 v=s c=&color2; pattern3 v=s c=&color3; pattern4 v=s c=&color4; pattern5 v=s c=&color5; pattern6 v=s c=&color6; pattern7 v=s c=&color7; pattern8 v=s c=&color8; pattern9 v=s c=&color9; pattern10 v=s c=&color10; pattern11 v=s c=&color11; pattern12 v=s c=&color12; pattern13 v=s c=&color13; proc gmap data=my_data map=cd_map all anno=anno_layers; id district_numeric; choro district_numeric / discrete nolegend html=my_html des='' name="&name"; run; quit; ODS HTML CLOSE; ODS LISTING;