BookmarkSubscribeRSS Feed
Whitlea
Obsidian | Level 7

 

I am trying to figure out the best way to annotate or move my labels so they do not run into each other or county lines.  The label variable I am using is xtext which I created in MTMAP. I have tried if and then statements to move x or y but I was unsuccessful.

It would also be nice to get some advice on how to splitting some of the longer labels.

Thanks!


data MTMap;
length xtext $20.;
merge MTcontycnt  MTCounties;
if county=113 then delete;
by county;
xtext = cats(name)||' ('||cats(cnt)||')';
end;
run;

%annomac;
%maplabel(MTCounties, MTMap, MTAnnotate, xtext, county, font='Arial', color=black, size=1.3)

 

legend1 noframe
label=(h=2 f=arial '  ');
title h=2 f=arial '  ';


proc gmap data=MTMap map=MTMap density=2;
id county;
choro cnt / anno=mtannotate legend=legend1 Xsize=8in ysize=5in stretch;
run;
quit;

1 REPLY 1
ballardw
Super User

@Whitlea wrote:

 

I am trying to figure out the best way to annotate or move my labels so they do not run into each other or county lines.  The label variable I am using is xtext which I created in MTMAP. I have tried if and then statements to move x or y but I was unsuccessful.

It would also be nice to get some advice on how to splitting some of the longer labels.

Thanks!


data MTMap;
length xtext $20.;
merge MTcontycnt  MTCounties;
if county=113 then delete;
by county;
xtext = cats(name)||' ('||cats(cnt)||')';
end;
run;

%annomac;
%maplabel(MTCounties, MTMap, MTAnnotate, xtext, county, font='Arial', color=black, size=1.3)

 

legend1 noframe
label=(h=2 f=arial '  ');
title h=2 f=arial '  ';


proc gmap data=MTMap map=MTMap density=2;
id county;
choro cnt / anno=mtannotate legend=legend1 Xsize=8in ysize=5in stretch;
run;
quit;


For my work I have spent a certain amount of time with Idaho county data and by trial and error created a data set with "good" x,y coordinates for placing a one or two line label for each county.

I started with a set created as you have using the %centroid macro. Then adjusted the x and or y coordinate for counties that needed it (most of our 44 counties). Then made sure to have any of the County identifiers I might need, number, fips, name so I could match output data to the data set. And keep this set in a permanent library.

 

Found that I really only needed 3 significant digits in most of the coordinates. My code, as I said, trial and error, for the set looks like:

data idmap.countylabel;
   set idmap.countylabel;
   select (countynum);
      when ( 1) do; x=-0.027 ;y=-0.035 ; end;                                                            
      when ( 2) do; x=-0.029 ;y=-0.009 ; end;                                                            
      when ( 3) do; x= 0.024 ;y=-0.049 ; end;                                                             
      when ( 4) do; x= 0.037 ;y=-0.055 ; end;                                                             
      when ( 5) do; x=-0.030 ;y= 0.030 ; end;                                                             
      when ( 6) do; x= 0.023 ;y=-0.039 ; end;                                                             
      when ( 7) do; x= 0.000 ;y=-0.035 ; end;                                                             
   /* continue the obvious pattern*/                                                   
      otherwise;
   end;
run;

I used a county numeric sequence as it was easier to make code look pretty (i.e. maintain if needed).

I actually have a number of formats to create other values from the countynum numeric value to get the FIPS and name text.

Some of the coordinates are the centroid results truncated or rounded just to be complete.

 

I am sure there are fancy algorithms out there but once this is done the only thing I worry is about is placing strings much longer than 10 characters on the typical size maps I make. So I use this as the core annotate with the added label variable and settings such as:

 

data countyanno;
   merge idmap.countylabel work.labeldata;
   by countynum;
   retain xsys ysys '2' when 'a'  cbox 'white';
   %label (x,y,somevalue,black,0,0,.75,albany,5);
run;

and see if the result has collisions. If they are short, as I said, no problems. If longer then often I can split text into two segments and tweek the position setting to get enough room based on the countynum value. Something like this (example splits string of two words)

 

If length(value) > 15 then do;
   labeltext = scan(value,1);
   /* the position of 2 moves text up and stays centered*/
   %label (x,y, labeltext,color, 0,0,size,style, 2);
   output; 
   labeltext = scan(value,2);
      %label (x,y, labeltext,color, 0,0,size,style, 8);
end;

or possibly B and E depending on text size. Splitting the text should depend on actual knowledge of the strings involved though.

If the font is small enough you may use 3 strings at 2, + and 8.

With your example you already had two values, so it would be easy to leave name and Cnt alone and use something like:

do; 
   %label (x,y, name,color, 0,0,size,style, 2); 
   output; 
   %label (x,y, cnt,color, 0,0,size,style, 8); 
end; 

SAS Innovate 2025: Call for Content

Are you ready for the spotlight? We're accepting content ideas for SAS Innovate 2025 to be held May 6-9 in Orlando, FL. The call is open until September 25. Read more here about why you should contribute and what is in it for you!

Submit your idea!

How to Concatenate Values

Learn how use the CAT functions in SAS to join values from multiple variables into a single value.

Find more tutorials on the SAS Users YouTube channel.

Click image to register for webinarClick image to register for webinar

Classroom Training Available!

Select SAS Training centers are offering in-person courses. View upcoming courses for:

View all other training opportunities.

Discussion stats
  • 1 reply
  • 613 views
  • 2 likes
  • 2 in conversation