# ------------------------------------------------------------ # GRUVI.sml # ------------------------------------------------------------ # SET WARNING LEVEL: $warnings 3; # ------------------------------------------------------------ # DECLARE SEVERAL VARIABLES RELATED TO GRUVI.sml: numeric srfiXorg,srfiYorg,slope,bnp,bnpxy,tsrfi,cvi; numeric bnpop,bnpi,bnpf,dbnpf; numeric srfiXb,srfiYb,srfiXv,srfiYv; numeric srfiX,srfiY; numeric tsrfiX1,tsrfiY1,tsrfiX2,tsrfiY2,tsrfiX3,tsrfiY3; numeric tsrfiX4,tsrfiY4,tsrfiX5,tsrfiY5,tsrfiX6,tsrfiY6; numeric nlins,ncols,isnull,i; numeric rang,rangdeg,sinrang,cosrang; numeric grubif,grubifmin,grubi,grumax,grunull; numeric gmean,gstd,gcv1,gcv2,gflag; numeric gruvif,gruvi; numeric gruvirange,gruvidveg,gfac; numeric hnmGB,hnmGF,hnGB,hnGF,nvGB,nvGF,pGB,pGF; numeric c1GB,c2GB,c3GB,c1GF,c2GF,c3GF; numeric pVI; raster SRFIX,SRFIY,GRUVI,GRUBI,GRUFI; raster SRFIXT,SRFIYT,GRUVIT; string gtype$,rtype$,fore$,back$; # ------------------------------------------------------------ # DECLARE VARIABLES RELATED TO CONTRAST LOOKUP PROCESS: class CONTRAST smlContrast; string clut1$,clut2$,clut3$; array numeric v[131072]; clut1$ = "exp"; clut2$ = "Exponential"; clut3$ = "Exponential contrast table"; smlContrast.OutputLowerLimit = 1; smlContrast.OutputUpperLimit = 255; # ------------------------------------------------------------ # DECLARE SEVERAL VARIABLES FOR GENERAL CHARACTER STRINGS: string t$,p$,p1$,p2$,p3$,p4$,p5$,p6$,p7$,p8$,p9$; string p10$,p11$,p12$,p13$,p14$,p15$,p16$,p17$,p18$,p19$; string p20$,p21$; # ------------------------------------------------------------ # DEFINE PROCEDURE writeTitle: # PURPOSE: WRITES TITLE & AUTHOR INFO TO CONSOLE WINDOW. proc writeTitle(pVIloc) begin numeric pVIloc; printf("GRUVI.sml:\n"); printf(" VERSION: November 15, 2005\n"); if (pVIloc == 1) then begin printf(" PURPOSE: PRODUCE GRUVI & GRUBI "); printf("RASTERS FROM A PAIR OF\n"); printf(" SRFIX & SRFIY RASTERS.\n"); end else begin printf(" PURPOSE: PRODUCE GRUFI & GRUBI "); printf("RASTERS FROM A PAIR OF\n"); printf(" SRFIX & SRFIY RASTERS,\n"); printf(" OR FROM A PAIR OF TCX"); printf(" AND TCY RASTERS (FROM TASCAP.SML).\n"); end printf(" NEW FEATURE: MAKES AUTOMATIC CONTRAST "); printf("TABLES.\n"); printf(" DETAILS: FAQs_by_Jack A & E\n"); printf(" AUTHOR: Dr. Jack F. Paris\n"); printf(" CONTACT INFO: jparis37@msn.com "); printf(" 303-775-1195\n"); printf(" ALLOWED USE: ONLY NON-COMMERCIAL\n\n"); end # ------------------------------------------------------------ # CLEAR CONSOLE WINDOW & REQUEST REPOSITIONING: clear(); p1$ = "CONSOLE-WINDOW ADJUSTMENT\n"; p2$ = "* REPOSITION the CONSOLE WINDOW.\n"; p3$ = "* Then, CLICK OK."; p$ = p1$ + p2$ + p3$; PopupMessage(p$); # ------------------------------------------------------------ # SELECT USE TYPE: p1$ = "GRUVI.sml can be Used in TWO Ways:\n"; p2$ = "1: Produce GRUVI & GRUBI (Veg. & Bright.)\n"; p3$ = "2: Produce GRUFI & GRUBI (Foreground & Bright.)\n"; p4$ = " DEFAULT: 1 (Veg. & Bright.)\n"; p5$ = " WARNING: If Using TC Rasters, SELECT WAY 2!\n"; p6$ = "WAY SELECTED (1 or 2):"; p$ = p1$ + p2$ + p3$ + p4$ + p5$ + p6$; pVI = PopupNum(p$,1,1,2,0); # ------------------------------------------------------------ # func GlobalPtile(Rl,ptilel): func GlobalPtile(raster Rl, numeric ptilel,hnl,nvl) begin local numeric funcval,rl,roffl; local numeric nlinsl,ncolsl,linl,coll,il; local numeric countl,suml,flagl,cfql; roffl = -65535; countl = 0; if (hnl == 1) then begin for each Rl begin rl = Rl[linl,coll]; if (rl <> nvl) then countl = countl + 1; end end if (hnl == 0) then begin nlinsl = NumLins(Rl); ncolsl = NumCols(Rl); countl = nlinsl * ncolsl; end for il = 0 to 131071 v[il] = 0; for each Rl[linl,coll] begin rl = Rl[linl,coll]; if (hnl == 1) then begin if (rl <> nvl) then begin il = rl - roffl; v[il] = v[il] + 1; end end if (hnl == 0) then begin il = rl - roffl; v[il] = v[il] + 1; end end suml = v[0]; flagl = 0; for il=1 to 131071 begin if (flagl == 0) then begin suml = suml + v[il]; cfql = suml * 100 / countl; if (cfql >= ptilel) then begin flagl = 1; funcval = il + roffl; end end end return (funcval); end # ------------------------------------------------------------ # DEFINE func expPower(dnLl,dnMl,dnHl): func expPower(dnLl,dnMl,dnHl) begin local numeric delLHl,delLMl,sr1l,sr2l,exppl; sr1l = 0.3; sr2l = 2 * (1 - sr1l); delLHl = dnHl - dnLl; delLMl = dnMl - dnLl; exppl = sr1l + sr2l * delLMl / delLHl; return (exppl); end # ------------------------------------------------------------ # DEFINE proc checkHisto(Band) # PURPOSE: Check that Input Raster has a Full (Unsampled) # Histogram. If Not, Then Recompute Histogram. proc checkHisto (raster Band) begin local numeric sampleInt; sampleInt = HistogramGetSampleInterval(Band); if (sampleInt > 1) begin DeleteHistogram(Band); CreateHistogram(Band,0); end else if (sampleInt == -1) then begin CreateHistogram(Band,0); end end # ------------------------------------------------------------ # ASSIGN VALUES TO GRUVI DATATYPE, SCALE & RANGE PARAMETERS: gtype$ = "16-bit signed"; grumax = 3000; gruvirange = 1000; grunull = -grumax - 1; # ------------------------------------------------------------ # WRITE TITLE & AUTHOR INFORMATION: writeTitle(pVI); # ------------------------------------------------------------ # ASK USER TO NAME FOREGROUND & BACKGROUND MATERIALS: if (pVI == 1) then begin printf("GRUVI RELATES TO FOREGROUND VEGETATION"); printf(" CONTRASTED AGAINST\nA BACKGROUND OF "); printf("BARE SOILS, RESIDUE, OR WATER.\n\n"); end else begin t$ = "IDENTIFY FOREGROUND MATERIALS"; p$ = "FOREGROUND-MATERIALS NAME?"; fore$ = PopupString(p$,"ENTER NAME",t$); printf(" GRUFI RELATES TO: "); printf("%s\n",fore$); t$ = "IDENTIFY BACKGROUND MATERIALS"; p$ = "BACKGROUND-MATERIALS NAME?"; back$ = PopupString(p$,"ENTER NAME",t$); printf("CONTRASTED AGAINST: "); printf("%s\n\n",back$); end # ------------------------------------------------------------ # ASK USER TO SELECT TRANSFORMATION OPTION: if (pVI == 2) then begin tsrfi = 1; end else begin p1$ = "SRFI TRANSFORMATION OPTION:\n"; p2$ = " For Classic NDVI, SAVI, or PVI: CLICK NO\n"; p3$ = " For Transformed NDVI or TSAVI: CLICK YES\n"; p4$ = " For CUSTOMIZED GRUVI: CLICK YES\n"; p5$ = "CLICK YES or NO:"; p$ = p1$ + p2$ + p3$ + p4$ + p5$; tsrfi = PopupYesNo(p$,1); end # ------------------------------------------------------------ # ASK USER TO DEFINE FOREGROUND-MATERIALS POINT IN 2-SPACE: # srfiXv = SRFI VALUE for FOREGROUND MATERIALS. if (pVI == 1) then begin p1$ = "X-AXIS DENSE-VEGETATION POINT:\n"; p2$ = " DEFAULT: 300\n"; p3$ = "VALUE ENTERED:"; p$ = p1$ + p2$ + p3$; srfiXv = PopupNum(p$,300,-32000,32000,0); # srfiYv = SRFIY VALUE for FOREGROUND MATERIALS. p1$ = "Y-AXIS DENSE-VEGETATION POINT:\n"; p2$ = " DEFAULT: 6000\n"; p3$ = "VALUE ENTERED:"; p$ = p1$ + p2$ + p3$; srfiYv = PopupNum(p$,6000,-32000,32000,0); end else begin p1$ = "X-AXIS FOREGROUND-MATERIALS POINT:\n"; p2$ = " DEFAULT: 300\n"; p3$ = "VALUE ENTERED:"; p$ = p1$ + p2$ + p3$; srfiXv = PopupNum(p$,300,-32000,32000,0); # srfiYv = SRFIY VALUE for FOREGROUND MATERIALS. p1$ = "Y-AXIS FOREGROUND-MATERIALS POINT:\n"; p2$ = " DEFAULT: 6000\n"; p3$ = "VALUE ENTERED:"; p$ = p1$ + p2$ + p3$; srfiYv = PopupNum(p$,6000,-32000,32000,0); end if (tsrfi == 1) then begin # ASK USER TO DEFINE SLOPE OF LBM: # LBM is the LINE OF BACKGROUND MATERIALS. p1$ = "SLOPE OF LINE-OF-BACKGROUND-MATERIALS:\n"; p2$ = "VALUE ENTERED:"; p$ = p1$ + p2$; slope = PopupNum(p$,1.086,-100,100,4); # ASK USER TO DEFINE BACKGROUND-MATERIALS POINT IN 2-SPACE: # srfiXb = SRFIX VALUE for BACKGROUND MATERIALS. p1$ = "X-AXIS BACKGROUND-MATERIALS POINT:\n"; p2$ = "VALUE ENTERED:\n"; p$ = p1$ + p2$; srfiXb = PopupNum(p$,1711,-32000,32000,0); # srfiYb = SRFI VALUE for BACKGROUND MATERIALS. p1$ = "Y-AXIS BACKGROUND-MATERIALS POINT:\n"; p2$ = "VALUE ENTERED:"; p$ = p1$ + p2$; srfiYb = PopupNum(p$,2132,-32000,32000,0); srfiXorg = 0; # IF pVI == 2, ASK USER TO DEFINE NEW X ORIGIN IN 2-SPACE: # Xorg = SRFIX VALUE for NEW ORIGIN. # Corresponding Yorg is CALCULATED -- not Specified. if (pVI == 2) then begin p1$ = "LINE-OF-BACKGROUND-MATERIALS X ORIGIN:\n"; p2$ = "VALUE ENTERED:"; p$ = p1$ + p2$; srfiXorg = PopupNum(p$,0,-32000,32000,0); end # CALCULATED srfiYorg VALUE: srfiYorg = round(srfiYb - slope * (srfiXb - srfiXorg)); end else begin srfiXorg = 0; srfiYorg = 0; slope = 1; end # ------------------------------------------------------------ # ASK USER TO OPT FOR OPTIMIZING THE bnp PARAMETER: # IF NOT OPTIMIZED, THEN USER MUST SPECIFY THE VALUE OF bnp. p1$ = "BACKGROUND NOISE PARAMETER (bnp) OPTIMIZATION:\n"; p2$ = "Are You USING a TEST AREA for bnp OPTIMIZATION?\n"; p$ = p1$ + p2$; bnpop = PopupYesNo(p$,0); if (bnpop == 1) then begin # START OPTIMIZATION PROCESS LEADING TO bnp: gcv1 = 1000; gflag = 0; GetInputRaster(SRFIXT); nlins = NumLins(SRFIXT); ncols = NumCols(SRFIXT); rtype$ = RastType(SRFIXT); checkHisto(SRFIXT); GetInputRaster(SRFIYT,nlins,ncols,rtype$); checkHisto(SRFIYT); rang = -atan(slope); rangdeg = rang * deg; rangdeg = rangdeg + 45; printf(" CCW ROTATION ANGLE: %6.2f deg\n\n",rangdeg); sinrang = sind(rangdeg); cosrang = cosd(rangdeg); # TRANSLATION TO A NEW ORIGIN: tsrfiX1 = srfiXv - srfiXorg; tsrfiY1 = srfiYv - srfiYorg; # SCALE-PRESERVING ROTATION: tsrfiX2 = tsrfiX1 * cosrang - tsrfiY1 * sinrang; tsrfiY2 = tsrfiX1 * sinrang + tsrfiY1 * cosrang; dbnpf = 0.02; bnpf = 0.00; CreateTempRaster(GRUVIT,nlins,ncols,gtype$,0); IgnoreNull(GRUVIT); for i = 0 to 1000 step 1 begin if (gflag == 0) then begin bnpf = bnpf + dbnpf; bnpi = bnpf * 5000; tsrfiX3 = tsrfiX2 + bnpi; tsrfiY3 = tsrfiY2 + bnpi; gruvidveg = (tsrfiY3 - tsrfiX3); gruvidveg = gruvidveg / (tsrfiY3 + tsrfiX3); gfac = gruvirange / gruvidveg; for each GRUVIT begin GRUVIT = grunull; isnull = IsNull(SRFIXT); if (isnull == 0) then begin srfiX = SRFIXT; srfiY = SRFIYT; tsrfiX4 = srfiX - srfiXorg; tsrfiY4 = srfiY - srfiYorg; tsrfiX5 = tsrfiX4 * cosrang - tsrfiY4 * sinrang; tsrfiY5 = tsrfiX4 * sinrang + tsrfiY4 * cosrang; tsrfiX6 = tsrfiX5 + bnpi; tsrfiY6 = tsrfiY5 + bnpi; gruvif = tsrfiY6 - tsrfiX6; gruvif = gfac * gruvif / (tsrfiY6 + tsrfiX6); gruvi = round(gruvif); if (gruvi < -grumax) then gruvi = -grumax; if (gruvi > grumax) then gruvi = grumax; GRUVIT = gruvi; end end SetNull(GRUVIT,grunull); gmean = GlobalMean(GRUVIT); gmean = gmean - 1000; gstd = GlobalSD(GRUVIT); gcv2 = gstd * 100 / gmean; if (gcv2 < gcv1) then begin gcv1 = gcv2; end else begin bnp = bnpf - dbnpf; gflag = 1; end IgnoreNull(GRUVIT); end end CloseRaster(GRUVIT); CloseRaster(SRFIXT); CloseRaster(SRFIYT); printf(" OPTIMIZED bnp VALUE: %5.2f\n\n",bnp); end else bnp = 0.5; # ASK USER TO SPECIFY THE VALUE OF bnp: if (tsrfi == 1) then begin p1$ = "BACKGROUND-NOISE PARAMETER (bnp) FOR\n"; p2$ = "TRANSFORMED INDICES ONLY:\n"; p3$ = " RANGE: 0.02 to 20.00\n"; p4$ = " Classic TSAVI: bnp = 0.15\n"; p5$ = " Classic WDVI: bnp = 20.00\n"; p6$ = " Customized INDEX: 0.00 < bnp < 20.00\n"; p7$ = "ENTER bnp VALUE:\n"; p$ = p1$ + p2$ + p3$ + p4$ + p5$ + p6$ + p7$; bnp = PopupNum(p$,bnp,0.02,20.00,2); end else begin p1$ = "BACKGROUND-NOISE PARAMETER (BNP) FOR\n"; p2$ = "NON-TRANSFORMED INDICES ONLY:\n"; p3$ = " RANGE: 0.0 to 20.00\n"; p4$ = " Classic NDVI: bnp = 0.00\n"; p5$ = " Classic SAVI: bnp = 0.50\n"; p6$ = " Classic PVI: bnp = 20.00\n"; p7$ = "ENTER bnp VALUE:\n"; p$ = p1$ + p2$ + p3$ + p4$ + p5$ + p6$ + p7$; bnp = PopupNum(p$,bnp,0.0,20.00,2); end printf(" LINE OF BACKGROUND MATERIALS:\n"); printf(" srfiYb = %7.1f +",srfiYorg); printf(" %7.4f * (srfiXb ",slope); if (srfiXorg>=0) then begin printf("- %6d)\n\n",srfiXorg); end else begin printf("+ %6d)\n\n",-srfiXorg); end printf(" SELECTED bnp VALUE: %5.2f\n\n",bnp); if (pVI == 1) then begin printf(" DENSE-VEGETATION POINT:\n"); printf(" srfiXveg = %6d\n",srfiXv); printf(" srfiYveg = %6d\n\n",srfiYv); end else begin printf(" FOREGROUND-MATERIALS POINT:\n"); printf(" srfiXfore = %6d\n",srfiXv); printf(" srfiYfore = %6d\n\n",srfiYv); end cvi = 1; if (pVI == 1) then begin if (tsrfi == 0) then begin if (abs(bnp) < 0.02) then begin printf(" RESULT: Classic NDVI\n"); cvi = 0; end if (abs(bnp - 0.5) < 0.02) then begin printf(" RESULT: Classic SAVI\n"); cvi = 0; end if (abs(bnp - 20) < 0.02) then begin printf(" RESULT: Classic PVI\n"); cvi = 0; end end if (tsrfi == 1) then begin if (abs(bnp) < 0.02) then begin printf(" RESULT: Transformed NDVI\n"); cvi = 0; end if (abs(bnp - 0.15) < 0.02) then begin printf(" RESULT: Classic TSAVI\n"); cvi = 0; end if (abs(bnp - 20) < 0.02) then begin printf(" RESULT: Classic WDVI\n"); cvi = 0; end end if (cvi == 1) then begin printf(" RESULT: Customized GRUVI\n"); end end else begin printf(" RESULT: Customized GRUFI\n"); end if (bnp < 0.01) then bnp = 0.01; bnpxy = bnp * 5000; # ------------------------------------------------------------ # PERFORM TRANSLATION (T) & ROTATION (R) TRANSFORMATION # TO CALCULATE GRUVI SCALE-CHANGE FACTOR, gfac: rang = -atan(slope); rangdeg = rang * deg; rangdeg = rangdeg + 45; sinrang = sind(rangdeg); cosrang = cosd(rangdeg); tsrfiX1 = srfiXv - srfiXorg; tsrfiY1 = srfiYv - srfiYorg; tsrfiX2 = tsrfiX1 * cosrang - tsrfiY1 * sinrang; tsrfiY2 = tsrfiX1 * sinrang + tsrfiY1 * cosrang; tsrfiX2 = tsrfiX2 + bnpxy; tsrfiY2 = tsrfiY2 + bnpxy; gruvidveg = (tsrfiY2 - tsrfiX2) / (tsrfiY2 + tsrfiX2); gfac = gruvirange / gruvidveg; grubif = (bnpxy + srfiXorg) * (bnpxy + srfiXorg); grubif = grubif + (bnpxy + srfiYorg) * (bnpxy + srfiYorg); grubifmin = sqrt(grubif); # ------------------------------------------------------------ # ASK USER TO GET INPUT RASTERS CALLED SRFIX & SRFIY: GetInputRaster(SRFIX); nlins = NumLins(SRFIX); ncols = NumCols(SRFIX); rtype$ = RastType(SRFIX); checkHisto(SRFIX); GetInputRaster(SRFIY,nlins,ncols,rtype$); checkHisto(SRFIY); # ------------------------------------------------------------ # ASK USER TO DESIGNATE OUTPUT GRUVI & GRUBI RASTERS: GetOutputRaster(GRUBI,nlins,ncols,gtype$); printf("\nPROCESSING ACTIONS:\n"); printf("Assigning GRUBI Values to Null Value.\n"); GRUBI = grunull; SetNull(GRUBI,grunull); CopySubobjects(SRFIX,GRUBI,"GEOREF"); if (pVI == 1) then begin GetOutputRaster(GRUVI,nlins,ncols,gtype$); printf("Assigning GRUVI Values to Null Value.\n"); GRUVI = grunull; SetNull(GRUVI,grunull); CopySubobjects(SRFIX,GRUVI,"GEOREF"); end else begin GetOutputRaster(GRUFI,nlins,ncols,gtype$); printf("Assigning GRUFI Values to Null Value.\n"); GRUFI = grunull; SetNull(GRUFI,grunull); CopySubobjects(SRFIX,GRUFI,"GEOREF"); end printf("Producing Output Rasters.\n"); for each SRFIX begin isnull = IsNull(SRFIX); if (isnull == 0) then begin srfiX = SRFIX; srfiY = SRFIY; tsrfiX1 = srfiX - srfiXorg; tsrfiY1 = srfiY - srfiYorg; tsrfiX2 = tsrfiX1 * cosrang - tsrfiY1 * sinrang; tsrfiY2 = tsrfiX1 * sinrang + tsrfiY1 * cosrang; tsrfiX2 = tsrfiX2 + bnpxy; tsrfiY2 = tsrfiY2 + bnpxy; grubif = tsrfiX2 * tsrfiX2; grubif = grubif + tsrfiY2 * tsrfiY2; grubif = sqrt(grubif) - grubifmin; grubi = round(0.2 * grubif); gruvif = tsrfiY2 - tsrfiX2; gruvif = gfac * gruvif / (tsrfiY2 + tsrfiX2); gruvi = round(gruvif); if (grubi < -grumax) then grubi = -grumax; if (grubi > grumax) then grubi = grumax; if (gruvi < -grumax) then gruvi = -grumax; if (gruvi > grumax) then gruvi = grumax; GRUBI = grubi; if (pVI == 1) then begin GRUVI = gruvi; end else begin GRUFI = gruvi; end end end # ------------------------------------------------------------ # CREATE HISTOGRAMS & PYRAMIDS. THEN, CLOSE RASTERS: printf("Adding Histogram & Pyramid Subobjects.\n"); if (pVI == 1) then begin CreateHistogram(GRUVI,0); CreatePyramid(GRUVI,0); hnmGF = HasNullMask(GRUVI); hnGF = HasNull(GRUVI); if (hnGF == 1) then nvGF = NullValue(GRUVI); c1GF = GlobalPtile(GRUVI,0.5,hnGF,nvGF); c3GF = GlobalPtile(GRUVI,50,hnGF,nvGF); c2GF = GlobalPtile(GRUVI,99.5,hnGF,nvGF); pGF = expPower(c1GF,c3GF,c2GF); smlContrast.InputLowerLimit = c1GF; smlContrast.InputUpperLimit = c2GF; smlContrast.Power = pGF; smlContrast.Compute(GRUVI,clut1$,clut2$,clut3$); CloseRaster(GRUVI); end else begin CreateHistogram(GRUFI,0); CreatePyramid(GRUFI,0); hnmGF = HasNullMask(GRUFI); hnGF = HasNull(GRUFI); if (hnGF == 1) then nvGF = NullValue(GRUFI); c1GF = GlobalPtile(GRUFI,0.5,hnGF,nvGF); c3GF = GlobalPtile(GRUFI,50,hnGF,nvGF); c2GF = GlobalPtile(GRUFI,99.5,hnGF,nvGF); pGF = expPower(c1GF,c3GF,c2GF); smlContrast.InputLowerLimit = c1GF; smlContrast.InputUpperLimit = c2GF; smlContrast.Power = pGF; smlContrast.Compute(GRUFI,clut1$,clut2$,clut3$); CloseRaster(GRUFI); end CreateHistogram(GRUBI,0); CreatePyramid(GRUBI); hnmGB = HasNullMask(GRUBI); hnGB = HasNull(GRUBI); if (hnGB == 1) then nvGB = NullValue(GRUBI); c1GB = GlobalPtile(GRUBI,0.5,hnGB,nvGB); c3GB = GlobalPtile(GRUBI,50,hnGB,nvGB); c2GB = GlobalPtile(GRUBI,99.5,hnGB,nvGB); pGB = expPower(c1GB,c3GB,c2GB); smlContrast.InputLowerLimit = c1GB; smlContrast.InputUpperLimit = c2GB; smlContrast.Power = pGB; smlContrast.Compute(GRUBI,clut1$,clut2$,clut3$); CloseRaster(SRFIX); CloseRaster(SRFIY); CloseRaster(GRUBI); printf("Processing Finished."); printf("TO SAVE THE CONSOLE WINDOW TEXT AS A REPORT:\n"); printf(" 1. RIGHT-CLICK IN THE CONSOLE WINDOW.\n"); printf(" 2. SELECT THE Save As... OPTION.\n"); printf(" 3. NAVIGATE TO THE DESIRED LOCATION.\n"); printf(" 4. PROVIDE A REPORT NAME (or OVERWRITE).\n"); printf(" 5. CLICK OK.");