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 2024

Innovate_SAS_Blue.png

Registration is open! SAS is returning to Vegas for an AI and analytics experience like no other! Whether you're an executive, manager, end user or SAS partner, SAS Innovate is designed for everyone on your team. Register for just $495 by 12/31/2023.

If you are interested in speaking, there is still time to submit a session idea. More details are posted on the website. 

Register now!

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.

Get the $99 certification deal.jpg

 

 

Back in the Classroom!

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

View all other training opportunities.

Discussion stats
  • 10 replies
  • 1133 views
  • 0 likes
  • 2 in conversation