## RasterIntervals.sml ## Sample script for Introduction to Geospatial Scripting. ## Standalone script to categorize a grayscale raster into a ## specified number of grayscale-value intervals. The number of ## intervals and the distribution type (equal count or equal interval) ## can be specified. Separate dialogs are provided to set input ## parameters, view and edit the range values, and to preview the ## result. Script makes a categorical raster with a single value ## for each interval and a color palette. class RASTER RastIn, RastClass; # class instances for input and output rasters class RASTER Temp; # class instance for temporary raster for result preview class COLORMAP palette; # class instance for color palette for output raster numeric maxRast; # input raster maximum value numeric minRast; # input raster minimum value numeric globalRange; # global range of raster values numeric numInterval; # number of intervals to create numeric distribType; # flag for type of interval: 0 = equal range, 1 = equal count numeric i, j; # counters for processing loops numeric value; # current raster value in input numeric globalNumCells; # number of non-null cells in raster numeric counter; # counter for running count of cells array numeric minRange[1]; # array to hold minimum boundary values for ranges array numeric maxRange[1]; # array to hold maximum boundary values for ranges array numeric cellCount[1]; # array to hold final cell count for each interval array numeric cellPct[1]; # array to hold cell percentage for each interval #### variables for color palette structure string xmlPalette$; class XMLDOC docColor; ### variables for dialog windows string xmlmain$; # string variable for main dialog specification class XMLDOC docmain; # class instance for parsed XML text containing main dialog specification class XMLNODE nodemain; # class instance for dialog node in XML structure for main dialog window class GUI_DLG dlgmain; # dialog class instance for main dialog window numeric errXML, dlgreturn; # variables for error checking with main XML and dialog string xmldistrib$; # string variable for Distribution dialog specification class XMLDOC docdistrib; # class instance for parsed XML text containing Distribution dialog specification class XMLNODE nodedistrib; # class instance for dialog node in XML structure for Distribution dialog window class GUI_DLG dlgDistrib; # dialog class instance for Distribution dialog window string xmlPreview$; # string variable for Preview dialog specification class XMLDOC docPreview; # class instance for parsed XML text containing Preview dialog specification class XMLNODE nodePreview; # class instance for dialog node in XML structure for Preview dialog window class GUI_DLG dlgPreview; # dialog class instance for Preview window class GRE_VIEW view; # class instance for the view in the Preview window #--------------------------------------------------------------------- ################ Procedure and Function Definitions ############### #--------------------------------------------------------------------- #--------------------------------------------------------------------- # Define function to return the class interval of the current raster # value by comparison to range boundaries func SetInterval(valueIn) { for i = 1 to numInterval { if (valueIn >= minRange[i] && valueIn <= maxRange[i]) then return i; } } #----------------------------------------------------------------------- # Define procedure to update cell counts and percentages for intervals proc updateCounts (numeric indexStart, numeric indexEnd) { local numeric i, counter; # reset cell counts for relevant intervals to 0 for i = indexStart to indexEnd { cellCount[i] = 0; } # loop through input raster to get cell count for each specified interval for each RastIn { ++ counter; for i = indexStart to indexEnd { if (RastIn >= minRange[i] && RastIn <= maxRange[i]) then ++ cellCount[i]; } } # compute cell percentage for each interval for i = indexStart to indexEnd { cellPct[i] = 100 * cellCount[i] / counter; } } #--------------------------------------------------- # Callback procedure to initally disable OK button # on main dialog window when it opens proc onOpenMain () { dlgmain.SetOkEnabled( 0 ); } #------------------------------------------------------ # Callback procedure to select input grayscale raster proc getRastIn () { GetInputRaster( RastIn ); if ( RastIn.$Info.Type == "16-bit color RGB" or RastIn.$Info.Type == "16-bit color BGR" or RastIn.$Info.Type == "24-bit color RGB" or RastIn.$Info.Type == "14-bit color BGR" or RastIn.$Info.Type == "binary" or RastIn.$Info.Type == "32-bit float" or RastIn.$Info.Type == "64-bit float" ) { PopupMessage( sprintf("Selected raster type is: %s. \n Please select a valid grayscale integer raster.", RastIn.$Info.Type) ); } else { local string filename$, objname$, dlgtext$; local numeric objnum; local class GUI_CTRL_PUSHBUTTON runButton; filename$ = GetObjectFileName( RastIn ); objnum = GetObjectNumber( RastIn ); objname$ = GetObjectName( filename$, objnum ); dlgtext$ = sprintf( "%s.%s / %s", FileNameGetName(filename$), FileNameGetExt(filename$), objname$ ); dlgmain.SetCtrlValueStr("inRastText", dlgtext$); maxRast = GlobalMax(RastIn); minRast = GlobalMin(RastIn); globalRange = maxRast - minRast + 1; runButton = dlgmain.GetCtrlByID("runButton"); runButton.SetEnabled(1); } } #------------------------------------------------------- # Callback procedure to select output interval raster proc getRastOut () { local string filename$, objname$, dlgtext$; local numeric objnum; GetOutputRaster(RastClass, RastIn.$Info.NumLins, RastIn.$Info.NumCols, "8-bit unsigned"); filename$ = GetObjectFileName( RastClass ); objnum = GetObjectNumber( RastClass ); objname$ = GetObjectName( filename$, objnum ); dlgtext$ = sprintf( "%s.%s / %s", FileNameGetName(filename$), FileNameGetExt(filename$), objname$ ); } #------------------------------------------------------------ # Procedure to make interval raster with color palette. # Used for temporary preview raster and for output raster proc makeIntervalRaster(class Raster Rast) { local class GUI_CTRL_COLORBUTTON colorbutton; local class ColorMap palette; local class COLOR color; for each RastIn { value = RastIn; Rast = SetInterval(value); } CreateHistogram(Rast, 0); CreatePyramid(Rast); CopySubobjects(RastIn, Rast, "GEOREF"); #------------------------------------------------------------------ # create color palette for output raster from the colors in the # distribution dialog for i = 1 to numInterval { local string interval$ = NumToStr(i); colorbutton = dlgDistrib.GetCtrlByID(sprintf("colorbut%s", interval$)); color = colorbutton.GetColor(); ColorMapSetColor(palette, i, color); } ColorMapWriteToRastVar(Rast, palette, "IntervalColors", "Color palette"); SetNull(Rast, 0); } #---------------------------------------------------------------------- # Callback procedure when Distribution dialog opens. Sets # initial colors of colorbuttons before opening (there is no # way to do this using the XML structure, so must access the GUI # control class for the colorbutton directly. Since this dialog is # modal, it isn't created until DoModal method is called, so buttons # must be set using the dialog's OnOpen callback. proc onOpenDistrib () { ### loop through the intervals to assign colors for i = 1 to numInterval { local string interval$ = NumToStr(i); # assign red, green, and blue values for interval from XML structure to COLOR class instance class COLOR color; class XMLNODE colorNode; colorNode = docColor.GetElementByID(interval$); color.red = colorNode.GetAttributeNum("red"); color.green = colorNode.GetAttributeNum("green"); color.blue = colorNode.GetAttributeNum("blue"); # get handle for relevant colorbutton control in dialog class GUI_CTRL_COLORBUTTON colorbutton; colorbutton = dlgDistrib.GetCtrlByID(sprintf("colorbut%s", interval$)); colorbutton.SetColor(color); } } ######################################################################## ### Callback procedure for OK button on Distribution dialog. ### Writes values to the output raster and sets up the color palette. proc onOKdistrib () { #---------------------------------- # fill in values in output raster getRastOut(); makeIntervalRaster(RastClass); dlgDistrib.Close(0); } # end onOKdistrib() #--------------------------------------------------------------- # Callback procedure for Cancel button on Distribution dialog. proc onCancelDistrib () { dlgDistrib.Close(0); } #--------------------------------------------------------------- # Procedure called when Exit button on main dialog is pressed. # Closes dialog and exits script. proc onExit () { dlgmain.Close(0); Exit(); } #------------------------------------------------------------------- # Procedure called when Close button on Preview window is pressed. proc onPreviewClose () { dlgPreview.Close(0); } #---------------------------------------------------------- # Procedure called when the Preview dialog window opens proc onPreviewOpen () { view.Redraw(); } #----------------------------------------------------------------------------- # Procedure called when Preview dialog is initialized. Used to create # view within the dialog and to create a group to view the temporary raster. proc onInitDialog () { local class GUI_LAYOUT_PANE viewpane; # class instance for layout pane to contain the view local class widget viewpaneWidget; # class for widget for layout pane to serve as parent for the view local class GRE_GROUP viewgp; # class for group to be shown in the view local class GRE_LAYER_RASTER tempLayer; viewpane = dlgPreview.GetPaneByID("viewpane"); # get handle for layout pane from dialog viewpaneWidget = viewpane.GetWidget(); # get parent Xm widget for pane viewpaneWidget.Resizable = 1; # set parent widget to be resizable viewgp = GroupCreate(); # create display group tempLayer = GroupQuickAddRasterVar(viewgp, Temp); # add the Temp raster to the group tempLayer.DataTip.Prefix = "Interval: "; view = viewgp.CreateView(viewpaneWidget,"",450, 350, "NoCloseOption"); # create 2D view of group in dialog view.ScalePosVisible = 0; # hide scale/position report # view.LegendView.Show = "Left"; } #---------------------------------------------------------------------- # Procedure called when Preview Result button on Distribution dialog # is pressed. proc onPreview () { CreateTempRaster(Temp, RastIn.$Info.NumLins, RastIn.$Info.NumCols, "8-bit unsigned"); makeIntervalRaster(Temp); # dialog specification for Preview dialog window xmlPreview$=' '; # parse XML text for the dialog into memory; # return an error code (number < 0 ) if there are syntax errors errXML = docPreview.Parse(xmlPreview$); if (errXML < 0) { PopupError(errXML); # Popup an error dialog. "Details" button shows syntax errors. Exit(); } # get the dialog element from the parsed XML document and # show error message if the dialog element can't be found nodePreview = docPreview.GetElementByID("previewDlg"); if (nodePreview == 0) { PopupMessage("Could not find dialog node in XML document"); Exit(); } # Set the XML dialog element as the source for the GUI_DLG class instance # we are using for the dialog window. dlgPreview.SetXMLNode(nodePreview); dlgPreview.SetOnInitDialog(onInitDialog); dlgPreview.DoModal(); } #--------------------------------------------------------------------- # Callback for edit of any interval Minimum value editnumber field proc onChangeMin(class GUI_CTRL_EDIT_NUMBER this, numeric i) { local numeric j = i - 1; local numeric val = this.GetValueNum(); local numeric prevMin, thisMax, prevRng, thisRng; local class GUI_CTRL_LABEL prevRngLabel, thisRngLabel; local class GUI_CTRL_LABEL prevCountLabel, thisCountLabel; local class GUI_CTRL_LABEL prevPctLabel, thisPctLabel; ### check that new minimum is greater than previous interval minimum ### and less than its own interval maximum and reset if necessary prevMin = dlgDistrib.GetCtrlValueNum(sprintf("editMin%d", j)); thisMax = dlgDistrib.GetCtrlValueNum(sprintf("editMax%d", i)); if ( val <= prevMin ) { val = prevMin + 2; this.SetValue( val, 0 ); } else { if ( val > thisMax ) { val = thisMax - 1; this.SetValue( val, 0 ); } } # reset minimum value in minRange array minRange[i] = val; # reset maximum value of next lower interval in dialog and in maxRange array maxRange[j] = minRange[i] - 1; dlgDistrib.SetCtrlValueNum( sprintf("editMax%d", j), maxRange[j] ); # compute new ranges for j and i intervals and update labels on dialog prevRng = val - prevMin; thisRng = thisMax - val + 1; prevRngLabel = dlgDistrib.GetCtrlByID( sprintf("labelRng%d", j) ); prevRngLabel.SetLabel( NumToStr(prevRng) ); thisRngLabel = dlgDistrib.GetCtrlByID( sprintf("labelRng%d", i) ); thisRngLabel.SetLabel( NumToStr(thisRng) ); # update counts and percentages updateCounts(j, i); prevCountLabel = dlgDistrib.GetCtrlByID( sprintf("labelCount%d", j) ); prevCountLabel.SetLabel( NumToStr(cellCount[j] ) ); thisCountLabel = dlgDistrib.GetCtrlByID( sprintf("labelCount%d", i) ); thisCountLabel.SetLabel( NumToStr(cellCount[i] ) ); prevPctLabel = dlgDistrib.GetCtrlByID( sprintf("labelPct%d", j) ); prevPctLabel.SetLabel( sprintf("%.2f", cellPct[j] ) ); thisPctLabel = dlgDistrib.GetCtrlByID( sprintf("labelPct%d", i) ); thisPctLabel.SetLabel( sprintf("%.2f", cellPct[i] ) ); } # end onChangeMin() #-------------------------------------------------------------------- # Callback for edit of any interval Maximum value editnumber field proc onChangeMax(class GUI_CTRL_EDIT_NUMBER this, numeric i) { local numeric j = i + 1; local numeric val = this.GetValueNum(); # new value for editnumber field local numeric thisMin, nextMax, thisRng, nextRng; local class GUI_CTRL_LABEL thisRngLabel, nextRngLabel; local class GUI_CTRL_LABEL thisCountLabel, nextCountLabel; local class GUI_CTRL_LABEL thisPctLabel, nextPctLabel; ### check that new maximum is greater than its own interval minimum ### and less than the next interval maximum and reset if necessary thisMin = dlgDistrib.GetCtrlValueNum( sprintf( "editMin%d", i ) ); nextMax = dlgDistrib.GetCtrlValueNum( sprintf( "editMax%d", j ) ); if ( val < thisMin ) { val = thisMin + 1; this.SetValue( val, 0 ); } else { if ( val >= nextMax ) { val = nextMax - 2; this.SetValue( val, 0 ); } } # read new maximum value for interval from editnumber and reset value in maxRange array maxRange[i] = val; # reset minimum value of next higher interval in dialog and in minRange array minRange[j] = maxRange[i] + 1; dlgDistrib.SetCtrlValueNum(sprintf("editMin%d", j), minRange[j]); # compute new ranges for i and j intervals and update labels on dialog thisRng = val - thisMin + 1; nextRng = nextMax - val; thisRngLabel = dlgDistrib.GetCtrlByID( sprintf("labelRng%d", i) ); thisRngLabel.SetLabel( NumToStr(thisRng) ); nextRngLabel = dlgDistrib.GetCtrlByID( sprintf("labelRng%d", j) ); nextRngLabel.SetLabel( NumToStr(nextRng) ); # update counts and percentages updateCounts(i, j); thisCountLabel = dlgDistrib.GetCtrlByID( sprintf("labelCount%d", i) ); thisCountLabel.SetLabel( NumToStr(cellCount[i] ) ); nextCountLabel = dlgDistrib.GetCtrlByID( sprintf("labelCount%d", j) ); nextCountLabel.SetLabel( NumToStr(cellCount[j] ) ); thisPctLabel = dlgDistrib.GetCtrlByID( sprintf("labelPct%d", i) ); thisPctLabel.SetLabel( sprintf("%.2f", cellPct[i] ) ); nextPctLabel = dlgDistrib.GetCtrlByID( sprintf("labelPct%d", j) ); nextPctLabel.SetLabel( sprintf("%.2f", cellPct[j] ) ); } # end onChangeMax() #---------------------------------------------------------------- # Callback procedure for Run button on main dialog. # Set up intervals: # find minimum and maximum values for each interval along with # cell count and cell percentage percentage and store in arrays. # Show information in Distribution dialog. proc setUpIntervals () { ## get desired number of intervals from the dialog numInterval = dlgmain.GetCtrlValueNum("numberIntervals"); printf("Number of intervals = %d \n", numInterval); # resize the arrays to match the number of intervals requested ResizeArrayClear(minRange, numInterval); ResizeArrayClear(maxRange, numInterval); ResizeArrayClear(cellCount, numInterval); ResizeArrayClear(cellPct, numInterval); # get distribution type from dialog distribType = dlgmain.GetCtrlValueNum("distCombo"); #------------------------------------ # set up equal-range intervals if (distribType == 0) { local numeric intervalWidth; # numeric range of interval in equal interval process intervalWidth = ( globalRange ) / numInterval; printf("interval width = %.1f\n", intervalWidth); minRange[1] = minRast; # set minimum value for first interval maxRange[numInterval] = maxRast; # set maximum value for last interval ## iterate through remaining intervals and set remaining min and max values for i = 1 to (numInterval - 1) { ## set max value for this interval; rounding ensures that no integer interval width is ## more than 1 value away from the target interval width maxRange[i] = round( minRast - 1 + ( i * intervalWidth ) ); minRange[i + 1] = maxRange[i] + 1; # set minimum value for next interval } ## use user-defined procedure to loop through input raster to get cell count and percentage for each interval updateCounts(1, numInterval); } #------------------------------- # set up equal-count intervals else if (distribType == 1) { local numeric targetCount = 0; # number of cell counts per interval in equal-count local numeric targetCount1; # current target count (offset from targetCount by remainder of previous interval) local numeric rem; # difference between final interval count and current target count globalNumCells = 0; array numeric histo[globalRange]; # array for cell value histogram # populate histogram array for raster and count number of non-null cells for each RastIn { ++ histo[RastIn - minRast + 1]; ++ globalNumCells; } ## compute target count per interval targetCount = round( globalNumCells / numInterval ); printf("globalNumCells = %d, targetCount = %d \n", globalNumCells, targetCount); targetCount1 = targetCount; minRange[1] = minRast; maxRange[numInterval] = maxRast; j = 1; # initialize index to histogram array ## iterate through all but last interval and set remaining min and max values for i = 1 to numInterval - 1 { counter = 0; while ( counter <= targetCount1 && j <= globalRange ) { counter += histo[j]; # printf("i = %d, j = %d, cellCount = %d \n", i, j, counter); ++j; } if ( targetCount1 < (counter - ( histo[j-1] / 2 ) ) ) # target count reached before midpoint of last cell value { --j; # decrement histogram index to last interval added cellCount[i] = counter - histo[j]; # omit last value from current interval rem = targetCount1 - cellCount[i]; # shortfall relative to current target targetCount1 = targetCount + rem; # increase target count for next interval by difference } # between actual and target count else { cellCount[i] = counter; # keep last value in current interval rem = cellCount[i] - targetCount1; # remainder relative to current target count targetCount1 = targetCount - rem; # decrease target count for next interval by difference } # between actual and target count maxRange[i] = (j - 1) + minRast - 1; cellPct[i] = 100 * cellCount[i] / globalNumCells; minRange[i+1] = maxRange[i] + 1; # set minimum value for next range } # count cells in last interval counter = 0; for i = j to globalRange { counter += histo[i]; # printf("i = %d, count = %d \n", i, counter); } cellCount[numInterval] = counter; cellPct[numInterval] = 100 * counter / globalNumCells; } # string variable with skeleton of XML specification of the # Distribution dialog window; controls and labels for the data # for each interval will be added to the XML structure before the # dialog is actually created. xmldistrib$ = ' '; ### Parse XML text for dialog into memory; return error if there are syntax errors. errXML = docdistrib.Parse(xmldistrib$); if ( errXML < 0 ) { PopupError( errXML ); # pop up an error dialog Exit( ); } ###----------------------------------------------------------------- # Modify the XML structure in memory before opening the dialog ### get XML nodes for global value labels class XMLNODE minLabel, maxLabel, rngLabel; minLabel = docdistrib.GetElementByID("globalMin"); maxLabel = docdistrib.GetElementByID("globalMax"); rngLabel = docdistrib.GetElementByID("globalRange"); ### set global value labels minLabel.SetText(sprintf("%d", minRast)); maxLabel.SetText(sprintf("%d", maxRast)); rngLabel.SetText(sprintf("%d", globalRange)); ### set the Distribution Type label class XMLNODE distType; # class instance for label for Distribution type distType = docdistrib.GetElementByID("distType"); if (distribType == 0) then distType.SetText( sprintf("Equal Range (%.1f)", intervalWidth) ); else distType.SetText( sprintf("Equal Count (%d)", targetCount) ); ### get the id for the pane that will contain a pane of controls for each interval local class XMLNODE distribList; distribList = docdistrib.GetElementByID("distribList"); # add horizontal pane and controls for each raster interval local class XMLNODE paneRI; # reuseable class instance for new pane local class XMLNODE editMin, editMax; # reuseable class instances for new editnumber field local class XMLNODE labelRng, labelCount, labelPct; # reuseable class instances for labels for range, count, percentage local class XMLNODE colorbut; # reuseable class instance for color button for row local string interval$; for i = 1 to numInterval { local string interval$ = NumToStr(i); # printf("interval = %d, minRange = %d, maxRange = %d, range = %d, count = %d, cellPct = %.2f \n", i, minRange[i], maxRange[i], maxRange[i] - minRange[i] + 1, cellCount[i], cellPct[i]); # add horizontal pane to hold controls paneRI = distribList.NewChild( "pane"); paneRI.SetAttribute("Orientation", "horizontal"); # add editnumber control for minRange editMin = paneRI.NewChild("editnumber"); editMin.SetAttribute("id", sprintf("editMin%s", interval$)); editMin.SetAttribute("Width", "6"); editMin.SetAttribute("MinVal", NumToStr(minRast)); editMin.SetAttribute("MaxVal", NumToStr(maxRast)); editMin.SetAttribute("Default", NumToStr(minRange[i])); editMin.SetAttribute("WidthGroup", "1"); editMin.SetAttribute("HorizAlign", "Center"); editMin.SetAttribute("Precision", "0"); editMin.SetAttribute("OnChanged", sprintf("onChangeMin(this, %s)", interval$ )); if (i == 1) then editMin.SetAttribute("ReadOnly", "true"); else editMin.SetAttribute("ReadOnly", "false"); # add editnumber control for maxRange editMax = paneRI.NewChild("editnumber"); editMax.SetAttribute("id", sprintf("editMax%s", interval$)); editMax.SetAttribute("Width", "6"); editMax.SetAttribute("MinVal", NumToStr(minRast)); editMax.SetAttribute("MaxVal", NumToStr(maxRast)); editMax.SetAttribute("Default", NumToStr(maxRange[i])); editMax.SetAttribute("WidthGroup", "2"); editMin.SetAttribute("HorizAlign", "Center"); editMax.SetAttribute("Precision", "0"); editMax.SetAttribute("OnChanged", sprintf("onChangeMax(this, %s)", interval$ )); if (i == numInterval) then editMax.SetAttribute("ReadOnly", "true"); else editMax.SetAttribute("ReadOnly", "false"); # add label for range labelRng = paneRI.NewChild("label"); labelRng.SetText(NumToStr(maxRange[i] - minRange[i] + 1)); labelRng.SetAttribute("id", sprintf("labelRng%s", interval$)); labelRng.SetAttribute("HorizAlign", "Center"); labelRng.SetAttribute("TextAlign", "Right"); labelRng.SetAttribute("WidthGroup", "3"); # add label for count labelCount = paneRI.NewChild("label"); labelCount.SetText( NumToStr(cellCount[i] ) ); labelCount.SetAttribute("id", sprintf("labelCount%s", interval$)); labelCount.SetAttribute("TextAlign", "Right"); labelCount.SetAttribute("HorizAlign", "Center"); labelCount.SetAttribute("WidthGroup", "4"); # add label for percent labelPct = paneRI.NewChild("label"); labelPct.SetText(sprintf("%.2f", cellPct[i])); labelPct.SetAttribute("id", sprintf("labelPct%s", interval$)); labelPct.SetAttribute("WidthGroup", "5"); labelPct.SetAttribute("TextAlign", "Right"); labelPct.SetAttribute("HorizAlign", "Center"); # add color button colorbut = paneRI.NewChild("colorbutton"); colorbut.SetAttribute("id", sprintf("colorbut%s", interval$)); colorbut.SetAttribute("WidthGroup", "6"); colorbut.SetAttribute("HorizAlign", "Center"); } ### Get the dialog element from the parsed XML text and show error message if ### the dialog element can't be found nodedistrib = docdistrib.GetElementByID("distribDlg"); if (nodedistrib == 0) { PopupMessage("Could not find dialog node in XML document"); Exit(); } ### Set the XMl dialog element as the source for the GUI_DLG class instance we ### are using for the Distribution dialog window dlgDistrib.SetXMLNode(nodedistrib); ### Open the dialog window as a modal dialog dlgDistrib.DoModal(); } # end setUpIntervals() #-------------------------------------------------------------------- ####################### Main program ############################ #-------------------------------------------------------------------- clear(); ###------------------------------------------------------------------ ### Standard colors for initial color palette. ### Can be modified using color buttons in Distribution dialog. xmlPalette$ = ' '; ### parse XML text for color palette into memory; returns error code (number < 0) ### if there are syntax errors errXML = docColor.Parse(xmlPalette$); if ( errXML < 0 ) { PopupError( errXML ); # pop up an error dialog Exit( ); } ###---------------------------------------------- ### Set up main dialog window ### Create string variable with XML specification of main ### dialog enclosed in single quotes (for multiline text) xmlmain$ = ' Equal Range Equal Count '; ### parse XML text for main dialog into memory; returns error code (number < 0) ### if there are syntax errors errXML = docmain.Parse(xmlmain$); if ( errXML < 0 ) { PopupError( errXML ); # pop up an error dialog Exit( ); } ### get the dialog element from the parsed XML text and show error ### message if the dialog element can't be found nodemain = docmain.GetElementByID( "dlgmain" ); if ( nodemain == 0 ) { PopupMessage( "Could not find main dialog node in XML document" ); Exit(); } ### set the XML dialog element as the source for the GUI_DLG class instance ### we are using for the main dialog window dlgmain.SetXMLNode(nodemain); dlgmain.DoModal();