PropFinder.sml

  Download

More scripts: Display Toolbar

Syntax Highlighing:

comments, key words, predefined symbols, class members & methods, functions & classes
            
## PropFinder.sml
## 6 May 2004
## Revised 31 January 2007
## Requires the 2nd Edition of the Lincoln Property Atlas (with updated
## property database structure) and TNTmips v 7.2, 15 Mar 2006 or later
## DoZoom procedure: added flag that is checked for manual zooming of view.
## Revised 2 June 2010
## Parent of script dialog set to drawing area of view for
## compatability with Display changes made for 2008:74 and later versions.
## Fixed height of text fields on Address Search tabbed panel.
## Randy Smith, MicroImages, Inc.
## Tool script that creates a dialog to conduct property searches
## by address and by property owner for the Lincoln property database.
## The tool also provides three alternative left-mouse button actions:
## 1) zoom in to the mouse location, 2) zoom out from the mouse location
## and 3) select property parcel and show that property's page on the
## county assessor's website.
## Results of address or name searches are shown on the Result List
## tabbed panel.  If only one match is found the property polygon is
## automatically highlighted and the view window recenters on that
## property polygon.  If more than one match is found, the user
## should select an entry from the list and press the View Property button
## to highlight and recenter on the associated parcel polygon. In either
## case, if the highlighted polygon is already in the view, the view is not
## redrawn.  The View Assessor's Website Data button on the same panel shows
## the selected property's page on the county assessor's website.
# The following symbols are predefined
#    class GRE_VIEW View            {use to access the view the tool script is attached to}
#    class GRE_GROUP Group          {use to access the group being viewed if the script is run from a group view}
#    class GRE_LAYOUT Layout        {use to access the layout being viewed if the script is run from a layout view}
#    numeric ToolIsActive           Will be 0 if tool is inactive or 1 if tool is active
#
# The following values are also predefined and are valid when the various On...()
# functions are called which deal with pointer and keyboard events.
#    numeric PointerX            Pointer X coordinate within view in pixels
#    numeric PointerY            Pointer Y coordinate within view in pixels
#    numeric ShiftPressed        1 if <shift> key being pressed or 0 if not
#    numeric CtrlPressed         1 if <ctrl> key being pressed or 0 if not
#    numeric LeftButtonPressed   1 if left pointer button pressed or 0 if not
#    numeric RightButtonPressed  1 if right pointer button pressed or 0 if not
#    numeric MiddleButtonPressed 1 if middle pointer button pressed or 0 if not
########################################
### Global variable declarations
########################################
class GRE_LAYER_VECTOR parcelLayer;
class GRE_LAYER_RASTER orthoLayer;
class VECTOR parcelVector;
class	RASTER orthoRaster;
class GRE_VECTOR_POLYS parcelPolys;
class TRANSPARM pTransparm;				## coordinate transformation parameters from screen to parcel layer
class TRANSPARM lvTransparm;				## coordinate transformation parameters from parcel layer to view
class RECT polyExtents, viewExtents;	## extents of polygon and view in screen coordinates
numeric searchmode;				# 1 if address search, 0 if name search
numeric prevsearchmode;			# mode for last search
numeric zoomChanged;				# 1 if zoom has changed since last redraw, otherwise 0
numeric i;							# element number counter for polygon loop
numeric count;						# counter for number of property polygons found
numeric numPolys;					# number of polygons in parcel vector
numeric matchRecList[1];		# array to hold record numbers for records matching search
numeric propsArray[1];			# array to hold element numbers of polygons found
numeric listcount;				# number of items in result list in dialog
string xmlfile$;
class XMLDOC dlgdoc, listdlgdoc;			# class instances for XML documents with dialog specifications
class XMLNODE dlgnode, listdlgnode;		# class instance for node in the XML document corresponding
													# to the dialog
class GUI_DLG dlgwin;						# class instance for the GUI dialog
numeric errXML;								# error value to check ingest of dialog specification
numeric prevMulti;							# flag for previous search result type: 1 = multiple, 0 = other
numeric prevScale;							# map scale the last time the doZoom procedure redrew the view
string prevname$;			# string variable to store owner name for comparison before next search
string prevaddress$;		# string variable to store address for comparison before next search
class GUI_FORMDATA settings;  # structure to hold values retrieved from main search dialog
## handles for dialog controls, needed to clear/reset values on close
class GUI_LAYOUT_BOOK panels;
class GUI_CTRL_EDIT_NUMBER editAddNum;
class GUI_CTRL_EDIT_STRING editStreet, editLname, editFname, editMid;
class GUI_CTRL_COMBOBOX prefixCombo, streetTypeCombo;
class GUI_CTRL_EDIT_STRING statusText, searchText;
class GUI_FORM_RADIOGROUP zoomSetting, mouseSetting;
class GUI_CTRL_LISTBOX propList;
class GUI_CTRL_PUSHBUTTON viewProperty, viewData;
##################################################################################
# The following script functions will be called (if used in the script) when
# the appropriate action or event occurs as described in the comments before each.
# To use a function, uncomment the lines containing the 'func' definition
# and ending brace '}' by removing the leftmost '#' on the line and add the
# function code between the two lines.
##################################################################################
##################################################################################
#### procedure called the first time the tool is activated.
#### If the tool implements a dialog it should be created (but not displayed) here.
##################################################################################
proc OnInitialize ()
	{
	searchmode = 1;		# set default search mode to address search
	prevMulti = 0;			# reinitialize flag for previous search result type
	#######################################################################################
	### get screen pixel size and compute map scale for 1X view of orthophoto layer
	### (ortho cell size = 0.3 m = 3000 mm); scale at 1X = cell size (mm) / pixel size (mm)
	#######################################################################################
	numeric pixelSize = View.PixelSizeMillimeters;	# screen pixel size in millimeters
	numeric scale1X = 300 / pixelSize;
	### get handles for the parcel vector layer in the View and its polygon element set
	####################################################################################
	Group = Layout.FirstGroup;
	while (Group.Name != "Overlays")
		Group = Group.NextGroup;
	parcelLayer = Group.GetLayerByName("Parcels");
	parcelLayer.NoFillWhenHighlight = 1;	 # don't fill highlighted polygons with highlight color
	parcelPolys = parcelLayer.Poly;
	### get the parcel vector object in parcelLayer so its database can be accessed
	###############################################################################
	DispGetVectorFromLayer(parcelVector, parcelLayer);
	numPolys = NumVectorPolys(parcelVector);
	### get TRANSPARM from parcel layer object coordinates to View coordinates
	### to be used with left mouse button zoom function
	###########################################################################
	lvTransparm = View.GetTransLayerToView(parcelLayer);
	###############################################
	#### specification in XML for main query dialog
	###############################################
	local string xml$ = '<?xml version="1.0" encoding="UTF-8"?>
	<!DOCTYPE root SYSTEM "smlforms.dtd">
	<root>
		<dialog id="pSearch" Title="Property Finder" Buttons="">
	  		<book id="panels">
				<page Name="Settings">
					<pane Orientation="horizontal">
						<groupbox Name=" Left mouse-button action: " ExtraBorder="3">
							<radiogroup id="mouseSetting" Orientation="vertical" VertAlign="Top" HorizAlign="center" VertResize="Fixed" Default="zoom">
								<item Value="zoomIn">Zoom In to Location</item>
								<item Value="zoomOut">Zoom Out from Location</item>
								<item Value="navigate">View Assessor&apos;s Website Data</item>
							</radiogroup>
						</groupbox>
						<groupbox Name=" Zoom When Search: " ExtraBorder="3">
							<radiogroup id="zoomSetting" Orientation="vertical" VertResize="Fixed" HorizAlign="center" Default="property" OnSelection="OnZoomChanged()">
								<item Value="property">Zoom to Property</item>
								<item Value="block">Zoom to Block</item>
								<item Value="neighborhd">Zoom to Neighborhood</item>
								<item Value="current">Keep Current Scale</item>
							</radiogroup>
						</groupbox>
					</pane>
				</page>
	      	<page id="addressPage" Name="Address Search" OnSetActive="OnAddressActive()">
					<pane Orientation="horizontal">
	        			<pane Orientation="vertical">
	        				<label>Number</label>
	        	  				<editnumber id="addnumfld" BlankZero="true" Enabled="true" Format="Decimal"
	        	      			  HorizAlign="Left" Justify="right" Precision="0" VertResize="Fixed"/>
	        	  			<label>1440</label>
	        	  			<label>555</label>
	        			</pane>
	        			<pane Orientation="vertical" VertAlign="Top">
	        				<label>Prefix</label>
	        	  			<combobox id="comboPrefix" Enabled="true" Width="10" Default="ANY" HorizAlign="Left">
								<item Value="ANY">Any</item>
								<item Value="NONE">None</item>
	       	   			<item Value="E">E</item>
	            			<item Value="W">W</item>
	            			<item Value="N">N</item>
	            			<item Value="S">S</item>
	            			<item Value="NW">NW</item>
	            			<item Value="SW">SW</item>
	        	  			</combobox>
	        	  			<label>W</label>
	        	  			<label>S</label>
						</pane>
        				<pane Orientation="vertical" VertAlign="Top">
        					<label>Street Name</label>
        		  			<edittext id="streetnamefld" Enabled="true" HorizAlign="Right"
            	   		  ReadOnly="false" Width="20" VertResize="Fixed"/>
        					<label>Pioneers</label>
     	   	  			<label>10</label>
     	   			</pane>
     	   			<pane Orientation="vertical" VertAlign="Top">
     	   	  			<label>Type</label>
      	  	  			<combobox id="comboType" Default="ANY" Enabled="true" Width="10" HorizAlign="Left"
         	      				 Sort="false" Height="10">
								<item Value="ANY">Any</item>
            				<item Value="AVE">AVE</item>
              				<item Value="BLVD">BLVD</item>
              				<item Value="CIR">CIR</item>
              				<item Value="CT">CT</item>
              				<item Value="DR">DR</item>
              				<item Value="LN">LN</item>
              				<item Value="RD">RD</item>
              				<item Value="ST">ST</item>
              				<item Value="BAY">BAY</item>
             				<item Value="BND">BND</item>
             				<item Value="BYP">BYP</item>
              				<item Value="HOLW">HOLW</item>
              				<item Value="HWY">HWY</item>
              				<item Value="MALL">MALL</item>
              				<item Value="PKWY">PKWY</item>
              				<item Value="PL">PL</item>
              				<item Value="PLZ">PLZ</item>
              				<item Value="PP">PP</item>
              				<item Value="TER">TER</item>
              				<item Value="TRL">TRL</item>
              				<item Value="VAL">VAL</item>
      	    			</combobox>
      	    			<label>BLVD</label>
      	    			<label>ST</label>
      				</pane>
      			</pane>
  				</page>
				<page id="namePage" Name="Owner Name Search" OnSetActive="OnNameActive()">
					<pane Orientation="horizontal">
						<pane Orientation="vertical" VertResize="Fixed" VertAlign="Center">
							<label VertAlign="Bottom">Last Name</label>
							<edittext id="lastnamefld" Enabled="true" HorizAlign="Left"
         			        ReadOnly="false" Width="20" VertResize="Fixed"/>
						</pane>
						<pane Orientation="vertical" VertResize="Fixed" VertAlign="Center">
							<label VertAlign="Bottom">First Name</label>
							<edittext id="firstnamefld" Enabled="true" HorizAlign="Left"
               			  ReadOnly="false" Width="20" VertResize="Fixed"/>
						</pane>
						<pane Orientation="vertical" VertResize="Fixed" VertAlign="Center">
							<label VertAlign="Bottom">Middle Initial</label>
							<edittext id="midintfld" Enabled="true" HorizAlign="Left"
               			  ReadOnly="false" Width="20" MaxLength="1" VertResize="Fixed"/>
						</pane>
					</pane>
				</page>
				<page Name="Result List">
					<pane Orientation="horizontal">
						<label HorizResize="Fixed">You searched for:</label>
						<edittext id="searchText" ReadOnly="true" Width="40" HorizAlign="Left"/>
					</pane>
					<listbox id="propList" SelectStyle="single" Width="60" OnChangeSelection="OnPropListSelect()" OnDoubleClick="OnViewPoly()">
					</listbox>
					<pane Orientation="horizontal" HorizAlign="Right">
						<label HorizResize="Expand"> </label>
						<pushbutton id="viewProperty" Name=" View Highlighted Property " HorizResize="Fixed" Enabled="no" OnPressed="OnViewPoly()"/>
						<pushbutton id="viewData" Name=" View Assessor&apos;s Website Data " HorizResize="Fixed" Enabled="no" OnPressed="OnViewData()"/>
					</pane>
				</page>
			</book>
			<pane Orientation="horizontal">
				<label HorizResize="Fixed">Search Status:</label>
				<edittext id="statusText" ReadOnly="true" Width="40" HorizAlign="Left"/>
			</pane>
			<pane Orientation="horizontal" HorizAlign="Right">
				<label HorizResize="Expand"> </label>
				<pushbutton id="searchbutton" Name=" Search " HorizResize="Fixed" WidthGroup="1" OnPressed="OnSearch()"/>
				<pushbutton id="closebutton" Name="Close" HorizResize="Fixed" WidthGroup="1" OnPressed="OnClose()"/>
				<pushbutton id="helpbutton" Name=" Help " HorizResize="Fixed" WidthGroup="1" OnPressed="OnHelp()"/>
			</pane>
		</dialog>
	</root>';
	################################################################
	### parse XML text for main search dialog into memory;
	### return an error code (number < 0) if there are syntax errors
	################################################################
	errXML = dlgdoc.Parse(xml$);
	if (errXML < 0)
		{
		PopupError(errXML);
		Exit();
		}
	##########################################################
	# get the dialog element from the parsed XML document and
	# show error message if the dialog element can't be found
	##########################################################
	dlgnode = dlgdoc.GetElementByID("pSearch");
	if (dlgnode == 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.
	##########################################################
	dlgwin.SetXMLNode(dlgnode);
	dlgwin.CreateModeless(View.DrawingArea);	### create as modeless dialog with
															### View window drawing area as parent
	################################################################################
	### get handles for dialog controls using their ID's in the dialog specification
	################################################################################
	statusText = dlgwin.GetCtrlByID("statusText");
	searchText = dlgwin.GetCtrlByID("searchText");
	zoomSetting = dlgwin.GetCtrlByID("zoomSetting");
	mouseSetting = dlgwin.GetCtrlByID("mouseSetting");
	propList = dlgwin.GetCtrlByID("propList");
	panels = dlgwin.GetPaneByID("panels");
	viewProperty = dlgwin.GetCtrlByID("viewProperty");
	viewData = dlgwin.GetCtrlByID("viewData");
	### address controls
	editAddNum = dlgwin.GetCtrlByID("addnumfld");
	prefixCombo = dlgwin.GetCtrlByID("comboPrefix");
	editStreet = dlgwin.GetCtrlByID("streetnamefld");
	streetTypeCombo = dlgwin.GetCtrlByID("comboType");
	### name controls
	editLname = dlgwin.GetCtrlByID("lastnamefld");
	editFname = dlgwin.GetCtrlByID("firstnamefld");
	editMid = dlgwin.GetCtrlByID("midintfld");
	} # end of OnInitialize
#############################################################################
### Called when tool is activated.
### If the tool implements a dialog it should be "managed" (displayed) here.
#############################################################################
proc OnActivate () {
#	panels.SetActivePage(0);	### set Settings panel to be active (on top)
	dlgwin.Open();
	zoomChanged = 1;
	}  # end of OnActivate
############################################################################
### Called when tool is deactivated (usually when switching to another tool).
### If the tool implements a dialog it should be "unmanaged" (hidden) here.
############################################################################
proc OnDeactivate () {
	### clear/reset controls on main search dialog to default states
	statusText.ClearValue(0);
	editAddNum.ClearValue(0);
	prefixCombo.SetSelectedItemIndex(0);
	editStreet.ClearValue(0);
	streetTypeCombo.SetSelectedItemIndex(0);
	editLname.ClearValue(0);
	editFname.ClearValue(0);
	editMid.ClearValue(0);
	if (propList.GetCount() > 0) then		### clear property list
		propList.DeleteAllItems();
	dlgwin.Close(0);		# close search dialog without notifying, so OnClose()
								# will not be called (allows user to switch tools without
								# having OnClose() override by setting the default tool
 }  # end of OnDeactivate
#############################################################################
### procedure called when Help button on dialog is pressed
#############################################################################
proc OnHelp ()
	{
	local string url$;
	url$ = "file://" + _context.ScriptDir + "/" + "help.htm";
	RunAssociatedApplication(url$);
	}
##############################################################
### procedure called when the Address Search page is activated
##############################################################
proc OnAddressActive ()
	{
	searchmode = 1;
	statusText.ClearValue(0);
	editAddNum.SetFocus();
	}	# end of OnAddressActive
############################################################
### procedure called when the Name Search page is activated
############################################################
proc OnNameActive ()
	{
	searchmode = 0;
	statusText.ClearValue(0);
	editLname.SetFocus();
	}	# end of OnNameActive
###########################################################
### Called when user presses 'left' pointer/mouse button.
###########################################################
proc OnLeftButtonPress ()
	{
	local string mouseSetting$;
	local class POINT2D clickPt;		### coordinates for click position
	### get setting for left mouse button mode from dialog
	mouseSetting$ = mouseSetting.GetValueStr();
	### get cursor position in screen coordinates
	clickPt.x = PointerX;
	clickPt.y = PointerY;
	### get TRANSPARM from screen coordinates to parcel vector layer coordinates
	### for current view position and transform point to layer coordinates
	############################################################################
	pTransparm = View.GetTransLayerToScreen(parcelLayer, 1);
	clickPt = pTransparm.ConvertPoint2DFwd(clickPt);
	if (mouseSetting$ == "navigate")		### select polygon and go to its page on Assesor's website
		{
		local numeric polynum;			### element number of the polygon clicked on
		local string url$;				### string containing the URL to pass to the browser
		### find the polygon clicked on and highlight it in the view
		polynum = FindClosestPoly(parcelVector, clickPt.x, clickPt.y, GetLastUsedGeorefObject(parcelVector) );
		parcelPolys.HighlightSingle(polynum);
		url$ = parcelVector.poly[polynum].ca062506.P_I_D$;
		RunAssociatedApplication(url$);
		}
	else if (mouseSetting$ == "zoomIn")		### zoom in to cursor position
		{
		### convert click point from vector layer to view coordinates
		### using TRANSPARM computed when tool is initialized
		#############################################################
		clickPt = lvTransparm.ConvertPoint2DFwd(clickPt);
		View.DisableRedraw = 1;
		View.Center = clickPt;			### recenter View on click point
		View.ZoomIn(2,1);					### zoom in 2X
		View.DisableRedraw = 0;
		View.Redraw();						### redraw view
		}
	else if (mouseSetting$ == "zoomOut")		### zoom out from cursor position
		{
		### convert click point from vector layer to view coordinates
		### using TRANSPARM computed when tool is initialized
		#############################################################
		clickPt = lvTransparm.ConvertPoint2DFwd(clickPt);
		View.DisableRedraw = 1;
		View.Center = clickPt;			### recenter View on click point
		View.ZoomOut(2,1);				### zoom out 2X
		View.DisableRedraw = 0;
		View.Redraw();						### redraw view
		}
	}  # end of OnLeftButtonPress
###########################################################
### procedure called when zoom setting is changed in dialog
###########################################################
proc OnZoomChanged ()
	{
	zoomChanged = 1;
	}	# end of OnZoomChanged
##########################################################################
### procedure to highlight, pan, and zoom to the selected property polygon
##########################################################################
proc DoZoom (numeric element)
	{
	local numeric vwScale;
	local string zoom$;
	#######################################################
	### check if highlighted polygon is already in the view
	#######################################################
	### get extents of highlighted polygon from Internal database table as min and max points
	local CLASS POINT2D minPt, maxPt;
	minPt.x = parcelVector.poly[element].Internal.MinX;
	minPt.y = parcelVector.poly[element].Internal.MinY;
	maxPt.x = parcelVector.poly[element].Internal.MaxX;
	maxPt.y = parcelVector.poly[element].Internal.MaxY;
	### set up RECT from polygon extents
	polyExtents.pt1 = minPt;
	polyExtents.pt2 = maxPt;
	### get extents of View in screen coordinates
	### upper left corner of view pane = 0,0, so lower right coordinates = width, height
	local CLASS POINT2D UpLeft, LowRight;
	LowRight.x = View.Width;
	LowRight.y = View.Height;
	### set up RECT from view extents in screen coordinates
	viewExtents.pt1 = UpLeft;
	viewExtents.pt2 = LowRight;
	vwScale = ViewGetMapScale(View);			# get current view scale
	if (zoomChanged == 0 && vwScale == prevScale)	### zoom setting hasn't been changed and view hasn't been manually zoomed;
		{															### don't need to check zoom setting
		#################################################################################
		### get TRANSPARM from screen coordinates to parcel vector layer coordinates for
		### current view position and convert extents RECT to layer coordinates
		#################################################################################
		pTransparm = View.GetTransLayerToScreen(parcelLayer, 1);
		viewExtents = pTransparm.ConvertRectFwd(viewExtents);
		###############################################################################
		### check if parcel polygon is in the current view
		###############################################################
		if (viewExtents.ContainsRect(polyExtents) )	### don't need to redraw view
			{
			View.SetMessage("");			### clear View window message line and status bar
			View.StatusBarClear();
			}
		else
			{
			View.DisableRedraw = 1;
			#################################################################################
			### if parcel polygon won't fit in view at this scale, then zoom to polygon extents
			### before redrawing.
			#################################################################################
			if ( (polyExtents.GetHeight() > viewExtents.GetHeight() ) ||
				(polyExtents.GetWidth() > viewExtents.GetWidth() )	)
				{
				parcelLayer.ZoomToActiveElement();
				parcelLayer.UnhighlightAllElements();	### needed to force redraw
				zoomChanged = 1;
				}
			else	# parcel polygon fits in view; pan to polygon and set current map scale
				{
				vwScale = View.CurrentMapScale;			# get current view scale
				parcelLayer.ZoomToActiveElement();		# zooms to polygon extents, changing view scale
				View.CurrentMapScale = vwScale;			# reset previous view scale
				parcelLayer.UnhighlightAllElements();	# needed to force redraw
				}
			###################################################################
			### re-enable redraw, redraw the view, and highlight the polygon
			###################################################################
			View.DisableRedraw = 0;
			ViewRedraw(View);
			parcelPolys.HighlightSingle(element);
			}
		} # end (if zoomChanged == 0)
	else  ### zoom setting has changed
		{
		### get current setting for Zoom radiogroup
		zoom$ = zoomSetting.GetValueStr();
		View.DisableRedraw = 1;
		if (zoom$ == "property")		## zoom to 1X but don't redraw yet
			{
			vwScale = scale1X;
			}
		else			### set vwScale value from Zoom radiogroup setting
			{
			if (zoom$ == "current") then
				vwScale = ViewGetMapScale(View);
			else if (zoom$ == "block") then
				vwScale = 3200;
			else if (zoom$ == "neighborhd") then
				vwScale = 8000;
			}
		### Set View scale, zoom / recenter on active polygon but don't redraw yet
		parcelLayer.ZoomToActiveElement();
		ViewSetMapScale(View, vwScale);
		parcelLayer.UnhighlightAllElements();
		#####################################################################################
		### get TRANSPARM from screen coordinates to parcel vector layer coordinates for the
		### new view position and view scale and convert extents RECT to layer coordinates
		#####################################################################################
		pTransparm = View.GetTransLayerToScreen(parcelLayer, 1);
		viewExtents = pTransparm.ConvertRectFwd(viewExtents);
		##################################################################################
		### if parcel polygon won't fit in view at new zoom, then zoom to polygon extents
		### before redrawing.
		###################################################################################
		if ( (polyExtents.GetHeight() > viewExtents.GetHeight() ) ||
			(polyExtents.GetWidth() > viewExtents.GetWidth() )	)
			{
			parcelPolys.HighlightSingle(element);
			parcelLayer.ZoomToActiveElement();
			parcelLayer.UnhighlightAllElements();
			zoomChanged = 1;
			}
		else
			zoomChanged = 0;
		##################################################################
		### re-enable redraw, redraw the view, and highlight the polygon
		##################################################################
		View.DisableRedraw = 0;
		ViewRedraw(View);
		parcelPolys.HighlightSingle(element);
		prevScale = vwScale;							# record view scale to check next time procedure is called
		}	# end else zoom setting has changed
	}	# end of DoZoom
#################################################################
### procedure to get owner name and address string for parcel
#################################################################
func string GetPropString (numeric propNum)
	{
	local string name$, num$, dir$, stname$, type$, aptnum$, sub$;
	local string liststr$;
	name$ = parcelVector.poly[propNum].ca062506.OWNER$;
	num$ = parcelVector.poly[propNum].ca062506.SIT_ST_NUM$;
	dir$ = parcelVector.poly[propNum].ca062506.SIT_ST_DIR$;
	stname$ = parcelVector.poly[propNum].ca062506.SIT_ST_NAM$;
	type$ = parcelVector.poly[propNum].ca062506.SIT_ST_TYP$;
	#sub$ = parcelVector.poly[propNum].CA032904.SIT_ST_SUB$;
	aptnum$ = parcelVector.poly[propNum].ca062506.SIT_APT_NU$;
	liststr$ = sprintf( "%s: %s %s %s %s %s%s", name$, num$, dir$, stname$, type$, sub$, aptnum$ );
	return liststr$;
	}	# end of GetPropString
#################################################################
### procedure called when search finds no matching parcel polygon
#################################################################
proc OnFoundNone ()
	{
	View.SetMessage("");			### clear View window message and status bar (no redraw will occur
	View.StatusBarClear();		### to do this automatically)
	View.NoBlankScreen = 1;		### don't redraw things that don't need to be redrawn
	parcelLayer.UnhighlightAllElements();		### unhighlight any currently highlighted parcel polygons
	View.NoBlankScreen = 0;
	viewProperty.SetEnabled(0);					### disable the View Property push-button
	viewData.SetEnabled(0);							### disable the View Assessor's Website Data button
	### update Search Result field in dialog
	statusText.SetValue("No properties found matching these criteria", 0);
	}	# end of OnFoundNone
#####################################################################################
### procedure called when one or more property polygons meet the search criteria.
#####################################################################################
proc OnFound (numeric propCount)
	{
	if (propList.GetCount() > 0 ) then		### clear property list
		propList.DeleteAllItems();
	local string liststr$;
	local numeric j, k, propNum;
	for j = 1 to propCount
		{
		liststr$ = GetPropString(propsArray[j]);		### get name / address string for parcel
		propList.AddItem(liststr$);						### add this string to the list
		}	# end of for j = 1 to propCount
	if (propCount == 1)	### only one parcel polygon found
		{
		statusText.SetValue("One property found.", 0);		### update status field in dialog
		propList.SetSelectedItemIndex(0);			### set the single list item to be selected
		viewProperty.SetEnabled(0);					### disable the View Property push-button
		viewData.SetEnabled(1);							### enable the View Assessor's Website Data button
		propNum = propsArray[1];
		parcelPolys.HighlightSingle(propNum);		### highlight element to make it active in view
		DoZoom(propNum);									### pan / zoom to highlighted element
		}
	else			### more than one polygon found
		{
		View.NoBlankScreen = 1;							### don't redraw entire view
		parcelLayer.UnhighlightAllElements(1);		### unhighlight any currently highlighted parcel polygon
		View.NoblankScreen = 0;
		statusText.SetValue( sprintf("%d properties found.", propCount) , 0);		### update status field in dialog
		View.SetMessage("");				## blank the View status message and bar
		View.StatusBarClear();
		viewProperty.SetEnabled(0);	### enable the View Property push-button
		viewData.SetEnabled(0);			### enable the View Assessor's Website Data button
		}
		panels.SetActivePage(3);	### set Result List panel to be active (on top)
	}
####################################################################
### procedure called when a property is selected in the Result List
####################################################################
proc OnPropListSelect ()
	{
	viewData.SetEnabled(1);
	viewProperty.SetEnabled(1);
	}
######################################################################################
### procedure called when the View Polygon button on the Result List panel is pressed.
######################################################################################
proc OnViewPoly ()
	{
	local numeric propNumber;		# polyon element number of property selected from the Property List
	### get the value from the property list (string) and convert to number (polygon element number)
	propNumber = propsArray[propList.GetSelectedItemIndex() + 1];
	View.DisableRedraw = 1;
	parcelPolys.HighlightSingle(propNumber);		### highlight element to make it active in view
	View.DisableRedraw = 0;
	DoZoom(propNumber);									### pan / zoom to highlighted element
	}
############################################################
### procedure called when View Assessor's Website Data
### button on dialog is pressed
############################################################
proc OnViewData ()
	{
	local numeric propNumber;		# polyon element number of property selected from the Property List
	local string url$;
	### get the value from the property list (string) and convert to number (polygon element number)
	propNumber = propsArray[propList.GetSelectedItemIndex() + 1];
	url$ = parcelVector.poly[propNumber].ca062506.P_I_D$;
	RunAssociatedApplication(url$);
	}
#######################################################################
### procedure called to update status on dialog and view for new search
#######################################################################
proc OnNewSearch()
	{
	statusText.ClearValue(0);				## clear search status field on dialog
	if (propList.GetCount() > 0) then
		propList.DeleteAllItems();				### clear property list
	### Set message at bottom of View window
	View.SetMessage("Searching for properties...");
	}
#############################################################################
### procedure to get element numbers of polygons attached to matching records
### and take action depending on whether 0, 1, or >1 elements found
#############################################################################
proc GetPolygons ()
	{
	######################################################################
	### We now have a list of records.  Find the elements that have those
	### records attached.
	######################################################################
	if (searchmode == 1) then
		count = TableGetRecordListElementList(parcelVector.poly.ParcelAddress, matchRecList, count, propsArray);
	else if (searchmode == 0) then
		count = TableGetRecordListElementList(parcelVector.poly.OwnerName, matchRecList, count, propsArray);
	if (count == 0)				### no properties found
		{
		OnFoundNone();
		}
	else								### one or more properties found
		{
		OnFound(count);
		}
	}	# end of GetPolygons
#################################################################
# called when Search button on Property Search dialog is pressed
#################################################################
proc OnSearch ()
	{
	### get control settings (values) from dialog and store in GUI_FORMDATA
	settings = dlgwin.GetValues();
	local numeric possibleRecList[1];
	local numeric numRecords, record;
	#############################################
	### address search; components of address are
	### in separate fields in the database
	#############################################
	if (searchmode == 1)
		{
		##############################################
		### declare local string variables for address
		##############################################
		local string addnum$;			# address number as string
		local string streetname$;		# street name
		local string prefix$;			# street direction prefix (e.g. "S", "W")
		local string prefixtext$;		# text for street prefix in search text shown in dialog
		local string streettype$;		# street type (e.g. "ST", "RD");
		local string streettypetext$;	# text for street type in search text shown in dialog
		local string fulladdress$;		# full address
		local numeric streetasnum;		# number returned from street name string (0 if not numbered street)
		#######################################
		### read address values from formdata;
		#######################################
		### get address number and convert to string (to match database)
		addnum$ = NumToStr( int( settings.GetValueNum("addnumfld") ) );
		if (addnum$ == "0") then
			addnum$ = "";
		### get street direction prefix
		prefix$ = settings.GetValueStr("comboPrefix");
		if (prefix$ == "NONE") then
			prefix$ = "";
		### get street name and change to upper case (to match database);
		### remove suffixes such as "nd" and "st" following the number
		### in name of numbered street;
		streetname$ = toupper$(settings.GetValueStr("streetnamefld") );
		streetasnum = StrToNum(streetname$);	# returns number at beginning of string or 0 if none
		if ( streetasnum > 0) then
			streetname$ = NumToStr(streetasnum);	# convert street number back to string
		### get street type setting
		streettype$ = settings.GetValueStr("comboType");
		###########################################################################################
		### concatenate address components to full address string, omitting empty strings
		##########################################################################################
		if (prefix$ == "ANY") then
			prefixtext$ = "\"Any\"";
		else
			prefixtext$ = prefix$;
		if (streettype$ == "ANY") then
			streettypetext$ = "\"Any\"";
		else
			streettypetext$ = streettype$;
		if (prefix$ == "") then
			fulladdress$ = sprintf("%s %s %s", addnum$, streetname$, streettypetext$);
		else
			fulladdress$ = sprintf("%s %s %s %s", addnum$, prefixtext$, streetname$, streettypetext$);
		#########################################################
		### don't search if no number and street name are entered
		#########################################################
		if ( (addnum$ == "") && (streetname$ == "") )
			{
			statusText.SetValue("Please enter an address number and street.", 0);
			}
		#################################################################################
		### if full address is same as previous, abort search and reshow previous results
		#################################################################################
		else if ( (fulladdress$ == prevaddress$) && (prevsearchmode == 1) )
			if (count == 0) then
				OnFoundNone();
			else
				OnFound(count);
		################################
		### otherwise do address search
		################################
		else {
			prevsearchmode = 1;
			prevaddress$ = fulladdress$;
			OnNewSearch();
			searchText.SetValue(fulladdress$, 0);
			###################################
			### search through parcel polygons
			###################################
			count = 0;									### reinitialize polygon counter
			ResizeArrayClear(matchRecList, 0);	### reinitialize array holding numbers of matching records
			ResizeArrayClear(propsArray, 0);		### reinitialize array holding found polygon numbers
			##############################################################################
			### Narrow down the search from 99,000 records to just the records that
			### match the requested SIT_ST_NUM value using a function that takes advantage
			### of an index on the selected field to speed the search
			##############################################################################
			numRecords = TableKeyFieldLookupList(parcelVector.poly.ParcelAddress, "SIT_ST_NUM", addnum$, possibleRecList);
			######################################################################
			### search through the returned set of records for those that match all
			### components of the requested address
			######################################################################
			for i = 1 to numRecords
				{
				View.SetStatusBar(i, numRecords);		### update View window's status bar
				record = possibleRecList[i];
				if ( (parcelVector.poly.ParcelAddress[@record].SIT_ST_NUM$ == addnum$) &&
					  (parcelVector.poly.ParcelAddress[@record].SIT_ST_NAM$ == streetname$) )
					{
					if ((prefix$ == "ANY") ||
						 (parcelVector.poly.ParcelAddress[@record].SIT_ST_DIR$ == prefix$) )
						{
						if ((streettype$ == "ANY") ||
							 (parcelVector.poly.ParcelAddress[@record].SIT_ST_TYP$ == streettype$))
							{
							count = count + 1;
							ResizeArrayPreserve(matchRecList, count + 1);		### increase array size for next polygon found
							matchRecList[count] = record;								### add polygon number to array
							}
						}
					}
				}
			### call procedure to get polygons corresponding to matching records
			GetPolygons();
			} # end else [new search]
		}	# end if (searchmode == 1)
	####################################################################
	### owner name search; entire name(s) is in single field in database
	####################################################################
	else if (searchmode == 0)
		{
		# string variables for owner name components from form
		local string lastname$, firstname$, middle$;
		local string firstmid$;		# concatenation of first name and middle initial
		local string fullname$;		# full name string (last, firstmid)
		local numeric firstmidlength;		# length of concatenated first name / middle initial
		local numeric numTokens;	# number of tokens in owner name (delimited by space)
		local numeric j;				# counter for looping through tokens in name
		local numeric name_in;		# flag indicating entered first name / middle initial begins one of the name tokens
		local class STRINGLIST tokenList;	# stringlist containing tokens from name
		############################################################
		### read name values from formdata and set all to upper case
		############################################################
		lastname$ = toupper$(settings.GetValueStr("lastnamefld") );
		firstname$ = toupper$(settings.GetValueStr("firstnamefld") );
		middle$ = toupper$(settings.GetValueStr("midintfld") );
		### concatenate middle initial with first name
		if (middle$ <> "") then
			firstmid$ = sprintf("%s %s", firstname$, middle$);
		else
			firstmid$ = firstname$;
		firstmidlength = strlen(firstmid$);		### get length of string to use later in search
		### concatenate lastname and firstmid to use to display search text
		fullname$ = sprintf("%s, %s", lastname$, firstmid$);
		######################################################
		### don't search if no owner name is entered in dialog
		######################################################
		if (fullname$ == ", ")
			{
			statusText.SetValue("Please enter an owner name.", 0);
			}
		#######################################################################
		### if name is same as previous, abort search and show previous results
		#######################################################################
		else if ( (fullname$ == prevname$) && (prevsearchmode == 0) )
			if (count == 0) then
				OnFoundNone();
			else
				OnFound(count);
		#############################
		### otherwise do new search
		#############################
		else
			{
			prevsearchmode = 0;
			### record name string for comparison on next search if result was multiple
			prevname$ = fullname$;
			searchText.SetValue(fullname$, 0);
			OnNewSearch();
			####################################
			### search through parcel polygons
			####################################
			count = 0;									### reinitialize polygon counter
			ResizeArrayClear(matchRecList, 0);	### reinitialize array holding numbers of matching records
			ResizeArrayClear(propsArray, 0);		### reinitialize array holding found polygon numbers;
			###############################################################################
			### Narrow down the search from 99,000 records to just the records in which
			### the OWNER string begins with the requested last name.  Uses a function
			### that takes advantage of an index on the selected field to speed the search
			###############################################################################
			numRecords = TableKeyFieldLookupList(parcelVector.poly.OwnerName, "OWNER", lastname$, possibleRecList, "StartsWith" );
			###########################################################################
			### Now search the returned records for those with a match to the
			### requested name
			###########################################################################
			for i = 1 to numRecords
				{
				local numeric name_in = 0;
				View.SetStatusBar(i, numRecords);	### update View window's status bar
				record = possibleRecList[i];
				### get owner name string from database
				local string name$ = parcelVector.poly.OwnerName[@record].OWNER$;
				local class STRING teststr$;
				if ( GetToken(name$, ",", 1) == lastname$)	### check for exact match with last name (not just starts with)
					{
					numTokens = NumberTokens(name$, ",&");		### number of tokens in owner name with "," and "&" as delimiters
					for j = 2 to numTokens			### loop through tokens following last name
						{
						teststr$ = GetToken(name$, ",&", j);
						teststr$.remove(0, 0);						### remove leading space from test string
						if ( left$( teststr$, firstmidlength) == firstmid$) then
							name_in = 1;
						}
					}
				if (name_in == 1)
					{
					count = count + 1;
					ResizeArrayPreserve(matchRecList, count + 1);		### increase array size for next polygon found
					matchRecList[count] = record;								### add polygon number to array
					}
				}
			### call procedure to get polygons corresponding to matching records
			GetPolygons();
			}	# end else do new search
		}	# end else if (searchmode == 0)
	}	# end OnSearch
###################################################################
### called when the Property Search dialog is closed by the Cancel,
### OK, or X Windows "X" button
###################################################################
proc OnClose()
	{
	dlgwin.Close(1);
	View.SetDefaultTool();		# switch to default tool on the View tool bar
	}