%let name=wafer3; filename odsout '.'; data wafermap; length id $20; /* Create the 'map' dataset. Make it 1 set of grids bigger than data, and use the extra map areas for the summary bands. */ do super_y=1 to (11+1); do super_x=1 to (10+1); do sub_y=1 to 5; do sub_x=1 to 2; x=((super_x*10)-10)+(sub_x*5)-5; y=((super_y*10)-10)+(sub_y*2)-2; id=trim(left(x))||'_'||trim(left(y)); output; x=x+5; output; y=y+2; output; x=x-5; output; end; end; end; end; /* Create some areas for the fake-legend */ do legend_y=10 to 55 by 5; x=120; y=legend_y; id=trim(left(x))||'_'||trim(left(y)); output; x=x+5; output; y=y+5; output; x=x-5; output; end; run; /* Annotate grid 'reference' outlines */ data outlines; length function color $12 text $30 style $50; xsys='2'; ysys='2'; hsys='3'; when='a'; size=2; /* thickness of annotated grid lines */ color='black'; do super_y=1 to 11; do super_x=1 to 10; function='poly'; x=(super_x*10)-10; y=(super_y*10)-10; output; function='polycont'; x=x+10; output; y=y+10; output; x=x-10; output; y=y-10; output; end; end; /* Bummer - can't use annotate pie function with hsys='2' in gmaps ... */ /* hsys='2'; function='pie'; x=(10*10)/2; y=(11*10)/2; size=(10*10)/2; rotate=360; output; */ /* calculate the points to draw a circular polygon */ radius=(10*10)/2; /* circle radius is 1/2 of the x grid direction */ x_circle_origin=0+(10*10)/2; y_circle_origin=0+(11*10)/2; size=1; /* use a more narrow line than the heavy grid */ do degrees=0 to 360 by 2; if degrees eq 0 then function='poly'; else function='polycont'; radians=degrees/57.3; x=(radius * cos(radians)) + x_circle_origin; y=(radius * sin(radians)) + y_circle_origin; output; end; /* Annotate some labels. Ideally the locations would be relative/calculated, but I'm hard-coding them ... */ function='label'; x=5; y=116; size=2.8; style="albany amt/bold"; position='6'; text='MEAN'; output; x=5; y=123; size=2.3; style="albany amt"; position='6'; text='ISB2A: Bit Count Failures'; output; x=121; y=68; size=2.5; style="albany amt/bold"; position='F'; text='Color Key'; output; size=2.0; style="albany amt"; position='F'; x=126; y=60; text='4.66-'; output; x=126; y=55; text='4.15-4.66'; output; x=126; y=50; text='3.74-4.15'; output; x=126; y=45; text='3.34-3.74'; output; x=126; y=40; text='2.94-3.34'; output; x=126; y=35; text='2.63-2.94'; output; x=126; y=30; text='2.13-2.63'; output; x=126; y=25; text='1.72-2.13'; output; x=126; y=20; text='1.32-1.72'; output; x=126; y=15; text='0-1.32'; output; run; data rawdata; input failures @@; datalines; . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 0.00 0.00 . . . . . . . . . . . . . . . . 0.00 1.20 2.60 4.70 4.70 0.00 . . . . . . . . . . . . . 0.00 2.20 3.50 4.00 4.00 4.00 4.70 0.00 . . . . . . . . . . . . 1.20 3.50 3.50 4.00 4.00 4.00 4.00 4.00 . . . . . . . . . . . 1.20 3.50 3.50 3.50 4.00 4.00 4.00 4.00 4.00 4.00 . . . . . . . . . 0.00 3.50 3.50 3.50 3.50 4.00 4.00 4.00 4.00 4.00 4.00 0.00 . . . . . . . . 2.20 3.50 3.50 4.00 4.00 4.00 4.00 4.00 4.00 4.00 4.00 1.20 . . . . . . . . 3.00 3.00 3.50 3.50 4.00 4.00 4.00 4.00 4.00 4.00 4.00 4.00 . . . . . . . 0.00 3.00 3.00 3.50 4.00 4.00 4.50 4.50 4.50 4.50 4.00 4.00 4.00 2.70 . . . . . . 3.50 3.00 3.50 4.00 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.00 4.00 4.00 . . . . . . 3.50 3.00 3.50 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.00 4.00 4.00 . . . . . 0.00 3.00 3.50 4.00 4.00 4.50 4.50 4.50 4.70 4.70 4.50 4.50 4.50 4.00 4.00 0.00 . . . . 2.00 3.00 3.50 3.50 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.00 3.50 1.20 . . . . 2.70 3.00 3.50 4.00 4.50 4.50 4.50 4.70 4.50 4.70 4.50 4.50 4.50 4.00 4.00 3.00 . . . . 3.00 3.50 3.50 4.00 4.50 4.50 4.50 4.70 4.50 4.70 4.70 4.70 4.50 4.50 4.00 3.50 . . . . 3.00 3.50 3.50 4.00 4.50 4.50 3.50 4.50 4.50 4.70 4.70 4.70 4.50 4.50 4.00 3.50 . . . 1.20 3.00 3.50 4.00 4.50 4.50 4.50 4.50 4.70 4.50 4.70 4.70 4.70 4.50 4.50 4.00 3.50 0.00 . . 1.50 2.70 3.00 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.70 4.70 4.50 4.50 3.50 3.50 1.20 . . 1.20 3.00 3.50 4.00 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.70 4.70 4.50 4.50 4.00 3.50 3.00 . . 1.20 3.00 3.50 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.70 4.70 4.70 4.70 4.50 4.00 3.50 3.50 . . 1.20 3.00 3.50 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.70 4.50 4.70 4.70 4.50 4.00 3.50 2.20 . . 1.20 3.00 3.50 4.00 4.50 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.70 4.70 4.50 4.00 3.50 1.20 . . 1.50 2.70 3.50 3.50 4.00 4.00 4.50 4.00 4.00 4.00 4.50 4.50 4.70 4.50 4.50 3.50 3.00 1.20 . . 1.50 2.70 3.50 4.00 4.00 4.50 4.50 3.50 4.00 4.00 4.50 4.50 4.70 4.50 4.50 4.00 3.00 2.20 . . 1.50 2.70 3.00 4.00 4.50 4.50 4.50 4.00 4.50 4.50 4.50 4.50 4.70 4.50 4.50 4.00 3.00 3.00 . . 1.50 2.70 3.00 4.00 4.50 4.00 4.50 4.00 4.00 4.50 4.50 4.50 4.70 4.50 4.50 4.00 3.00 1.20 . . 1.50 2.70 3.00 4.00 4.00 4.50 4.50 4.00 4.00 4.50 4.50 4.50 4.70 4.50 4.50 3.50 3.00 1.20 . . 1.20 2.20 2.70 3.50 4.00 4.00 4.00 4.50 4.00 4.50 4.50 4.50 4.50 4.50 4.00 3.50 3.00 1.20 . . 1.20 2.20 3.00 3.50 4.00 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.00 3.50 3.00 1.20 . . 1.20 2.20 2.70 3.50 4.00 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.00 3.50 3.00 2.20 . . 1.20 2.20 2.70 3.00 4.00 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.00 3.50 3.00 3.50 . . 1.20 2.20 2.70 3.00 3.50 4.00 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.50 4.00 3.00 2.70 3.50 . . 0.00 2.00 2.20 2.70 3.50 3.50 4.00 4.00 4.00 4.00 4.50 4.50 4.00 4.00 3.50 3.00 2.70 0.00 . . . 2.00 2.20 2.70 3.50 3.50 4.00 4.00 4.50 4.00 4.50 4.50 4.00 4.00 3.50 3.00 2.70 . . . . 2.00 2.20 2.70 3.00 3.50 4.00 4.00 4.50 4.00 4.50 4.00 4.00 3.50 3.00 2.70 2.70 . . . . 2.20 1.90 2.20 3.00 3.50 4.00 4.00 4.00 4.00 4.00 4.00 4.00 3.50 3.00 2.70 2.70 . . . . 1.20 1.90 2.70 2.70 3.00 3.50 4.00 4.00 4.00 4.00 4.00 4.00 3.50 3.00 2.70 2.20 . . . . 0.00 1.20 1.20 1.90 2.70 3.00 3.00 3.50 3.50 3.50 3.50 3.50 3.00 2.70 2.20 0.00 . . . . . 1.20 1.20 1.90 2.20 3.00 3.00 3.50 3.50 3.50 3.50 3.00 2.70 2.70 2.70 . . . . . . 2.00 1.20 1.90 2.20 2.70 3.00 3.00 3.00 3.00 3.00 3.00 2.70 2.20 2.70 . . . . . . 2.00 1.20 1.90 2.20 2.70 2.70 3.00 3.00 3.00 3.00 2.70 2.20 2.20 1.90 . . . . . . . 1.20 1.90 1.50 2.20 2.70 2.70 2.70 2.70 2.70 2.20 2.20 2.70 . . . . . . . . 1.20 1.50 1.50 1.90 1.90 2.20 2.20 2.20 2.20 1.90 1.90 2.70 . . . . . . . . 0.00 1.20 1.20 1.90 1.50 1.90 2.20 2.20 1.90 1.90 1.90 0.00 . . . . . . . . . 1.90 1.20 1.90 1.20 1.90 1.90 2.20 1.90 1.90 1.90 . . . . . . . . . . . 1.90 1.20 1.20 1.90 1.90 2.20 1.90 2.20 . . . . . . . . . . . . 0.00 1.20 1.90 1.90 1.90 2.20 0.00 0.00 . . . . . . . . . . . . . 0.00 1.20 1.20 1.20 1.90 0.00 . . . . . . . . . . . . . . . . 0.00 0.00 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ; run; data rawdata; set rawdata; n=_n_; x=mod(_n_-1,20)*5; y= 108 - int((_n_-1)/20)*2 ; run; /* Create the 'data' for the summary bar at the top of the grid */ /* To be like the motorola graphic, I should use 'median', but sql only supports 'mean', so I'm using that here. With some other sas proc, a median could no doubt be calculated. */ proc sql; create table sum1 as select unique x, 118 as y, mean(failures) as failures from rawdata group by x; create table sum2 as select unique y, 105 as x, mean(failures) as failures from rawdata group by y; quit; run; data griddata; set rawdata sum1 sum2; length id $20 html $254; id=trim(left(x))||'_'||trim(left(y)); if failures=. then failcolor=.; else if failures = 0 then failcolor=0; /* hash-mark */ else if failures < 1.32 then failcolor=1; /* red */ else if failures < 1.72 then failcolor=2; else if failures < 2.13 then failcolor=3; else if failures < 2.63 then failcolor=4; else if failures < 2.94 then failcolor=5; else if failures < 3.34 then failcolor=6; else if failures < 3.74 then failcolor=7; else if failures < 4.15 then failcolor=8; else if failures < 4.66 then failcolor=9; else failcolor=10; /* green */ myhtml= 'title='||quote( 'id: '||trim(left(id))||'0D'x|| 'x: '||x||'0D'x|| 'y: '||y||'0D'x|| 'failures: '||failures)|| ' href="wafer3_info.htm"'; if failures^=. then output; run; /* Add some values to go with the 'fake-legend' -- a side-benefit of using a fake legend, and using map areas & data for it, is that now the map is guaranteed to have at least 1 data value in each color range, therefore guaranteeing that each color is used & assigned correctly -- otherwise, if you are missing data in certain color ranges, gmap would skip that color, and assign the colors in-order, without making the colors not assign in the desired order. */ data fakelegend; id='120_10'; failcolor=1; output; id='120_15'; failcolor=2; output; id='120_20'; failcolor=3; output; id='120_25'; failcolor=4; output; id='120_30'; failcolor=5; output; id='120_35'; failcolor=6; output; id='120_40'; failcolor=7; output; id='120_45'; failcolor=8; output; id='120_50'; failcolor=9; output; id='120_55'; failcolor=10; output; run; data griddata; set griddata fakelegend; run; goptions device=png; goptions border; ODS LISTING CLOSE; ODS HTML path=odsout body="&name..htm" (title="SAS Silicon Wafer Map Simulation") style=htmlblue; goptions gunit=pct htitle=4.25 htext=2.75 ftitle="albany amt/bold" ftext="albany amt"; title1 ls=1.5 "Silicon Wafer Failure Analysis"; title2 h=4 " "; footnote1 h=3 " "; footnote2 link="http://www2.sas.com/proceedings/sugi25/25/ad/25p044.pdf" "Inspired by the following Motorola SUGI paper (see page 4)"; pattern1 v=m3x45 c=black; pattern2 v=s c=CXd90000; /* red */ pattern3 v=s c=CXe90401; pattern4 v=s c=CXff4500; pattern5 v=s c=CXfe8d00; pattern6 v=s c=CXffbe00; pattern7 v=s c=CXffff0b; pattern8 v=s c=CXcdff00; pattern9 v=s c=CX6eff00; pattern10 v=s c=CX00be05; pattern11 v=s c=CX008b05; /* green */ pattern12 v=s c=white; proc gmap data=griddata map=wafermap anno=outlines; id id; choro failcolor / discrete nolegend coutline=black html=myhtml des='' name="&name"; run; quit; ODS HTML CLOSE; ODS LISTING;