BookmarkSubscribeRSS Feed
JeffU
Calcite | Level 5
I'm writing a report to display user locations across the country on a given day to help travel logistics. The problem occurs when I have 2 or more users in the same city on the same day, the annotation I'm using just writes over itself leading to an illegible blob of combined user names. Any suggestions on quick ways to display the multiple names in a legible manner? Thanks.
10 REPLIES 10
GraphGuy
Meteorite | Level 14
Annotated doesn't have any automated collision-avoidance (ie, no easy/automated solution).

You would have to programmatically detect the locations with multiple users at the same location, and programmatically add an x-offset to the text for each of the multiple users.
JeffU
Calcite | Level 5
Thanks Robert. Figured it wouldn't be an easy fix. I'm a little bit of a newbie to this and am trying to work my way through it. Any help on how to add in the offset?

I'm using a merge for the userdata and the maps.uscity:

data combinedcityuser(keep=city lastname x y);
merge sasuser.NewReviewerOnsite517(in=inair) maps.uscity;
by state city;
if inair;
run;
GraphGuy
Meteorite | Level 14
I've put together a little sample that should show you a basic technique for doing this:

data users;
length city name $50;
statecode='NC'; city='Cary'; name='Robert'; output;
statecode='NC'; city='Cary'; name='Ed'; output;
statecode='NC'; city='Asheville'; name='Bill'; output;
run;

proc sql;
create table users as
select users.*, uscity.x, uscity.y
from users left join maps.uscity
on (users.statecode=uscity.statecode) and (users.city=uscity.city);
quit; run;

proc sort data=users out=users;
by statecode city;
run;
data users; set users;
by statecode city;
if first.city then count=0;
count+1;
run;

data anno_users; set users;
xsys='2'; ysys='2'; position='b'; when='a';
function='label'; color='black';
y=y-((count-1)*.015);
text=trim(left(name));
run;

pattern value=solid color=white;
proc gmap data=maps.us map=maps.us anno=anno_users;
id state;
choro state / levels=1 coutline=gray;
run;
JeffU
Calcite | Level 5
This is outstanding Robert. I very much appreciate it. One last question:

I also want to display the city name on the map. Is there a way of doing this without repeating that name 3 times in the event of multiples users?
GraphGuy
Meteorite | Level 14
Basically, you just have to add a test in the data step to see if it's the first observation for the city, and output an extra text (city label) at a higher position, in addition to the regular text.


data users;
length city name $50;
statecode='NC'; city='Cary'; name='Robert'; output;
statecode='NC'; city='Cary'; name='Ed'; output;
statecode='NC'; city='Asheville'; name='Bill'; output;
run;

proc sql;
create table users as
select users.*, uscity.x, uscity.y
from users left join maps.uscity
on (users.statecode=uscity.statecode) and (users.city=uscity.city);
quit; run;

proc sort data=users out=users;
by statecode city;
run;
data users; set users;
by statecode city;
if first.city then count=0;
count+1;
run;

data anno_users; set users;
length style $50;
xsys='2'; ysys='2'; position='b'; when='a';
function='label'; color='black';
if count=1 then do;
style='albany amt/bold';
position='2';
text=trim(left(city));
output;
end;
y=y-((count-1)*.013);
style='albany amt';
position='5';
text=trim(left(name));
output;
run;

pattern value=solid color=white;
proc gmap data=maps.us map=maps.us anno=anno_users;
id state;
choro state / levels=1 coutline=gray;
run;
JeffU
Calcite | Level 5
Robert I very much appreciate all the help.

One last high level question and I'm sure I've just got my syntax wrong but I was also trying to put in an 'X' on the xy of the city on the map but when I attempt to add that in, it removes all but the first letter of the other two fields being displayed. Have you heard of anything like this?

Again, thanks so much for everything.
GraphGuy
Meteorite | Level 14
If you don't specify a length for a variable (in a length statement), it defaults to the length of the first value you assign to it. In this case, I suspect you're assigning ...

text='X';

and this will make it default to a length of 1 character (which is too short for the other values you want to put into the text variable. You can define it as a longer length by adding a length statement near the beginning of your data step, such as ...

length text $100;
JeffU
Calcite | Level 5
That's exactly what it was.

As a test, I added that text='X' after the text=trim(left(city)) and that also worked.

So is it accurate to say then that if a variable length isn't specified in data step but it's equated to another one that is (ie the city field) then it will inherit that variable length?
GraphGuy
Meteorite | Level 14
I 'think' that's what it does, but I haven't really checked it out thoroughly.

Your mission, should you choose to accept it ...
Find out the definitive answer, and let us all know! 🙂
JeffU
Calcite | Level 5
Excellent. Again, thanks for all the help.

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
  • 10 replies
  • 1939 views
  • 0 likes
  • 2 in conversation