## Thursday, December 28, 2006

### Simple task offers many examples

Let's look at a very simple task that will offer a wealth of examples of using various Vr modules and tasks. This will go beyond the idea of using as a simple macro language into the early stages of writing what I consider true programs that extend Vr functionality and will include some basic ideas as well as some more advanced concepts. This example will include...

• using python modules (non-vr straight python)
• selecting entities
• creating new entities
• displaying information in various forms
• and grabbing window attributes.
The problem is that there are several lines in the file for which the area is required, along with the total of all areas. It would be nice to allow the user to ID as many lines as necessary, display the area for each line, then present the total at the end. We'll throw in some variations at the end that make for interesting exercises, and more useful programs.

Here is the code....

import math

Gui = PyVrGui()
Ws=PyVrWs()
Gr=PyVrGr()
Line=PyVrLine()
TotalArea=0.0
NumberOfLines=0
text=PyVrText()
text.SetFontNum(4)
text.SetRot(45.0)
text.SetSize(Gr.GetWinScale ()*.15)

WsNum=Ws.Aws()
while Line.Id() != -1:
..TotalArea=TotalArea+math.fabs(Line.Area())
..NumberOfLines=NumberOfLines+1
..print TotalArea
..label='%.1f ac' % math.fabs((Line.Area()/43560.0))
..text.SetText(label)
..(MinX, MinY, MinZ, MaxX, MaxY, MaxZ) = Ws.GetLineMinMax (WsNum, Line.GetLineNum())
..text.SetCoord((MinX+MaxX)/2,(MinY+MaxY)/2,(MinZ+MaxZ)/2)
..text.SetJustX(2)
..text.SetJustY(2)
..text.SetLayer(88)
..text.Plot()

print 'Total Area ',TotalArea,' Sq Units ',TotalArea/43560.0,' acres'
Gui.MsgBox('%d Lines contain %.2f acres'%(NumberOfLines,(TotalArea/43560.0)),'Area by Selection')

Taken a line or two at a time

import math
We are going to use a purely math function later. It would be easy enough to write the function needed (math.fabs() to get the absolute value of a real number), but this illustrates why it is nice to have python installed even though it isn't strictly necessary for VrPython to work, there are times that accessing standard (or very exotic) modules that are already available can be nice.

Gui = PyVrGui()
Ws=PyVrWs()
Gr=PyVrGr()
Line=PyVrLine()
text=PyVrText()
text.SetFontNum(4)
text.SetRot(45.0)
text.SetSize(Gr.GetWinScale ()*.15)
text.SetJustX(2)
text.SetJustY(2)
text.SetLayer(88)
TotalArea=0.0
NumberOfLines=0 First create or initialize all the Vr objects that will be used (first 5 lines), pre-set any entity parameters that the user may want quick access to later (lines 6-11), and initialize variables that need to start with a pre-determined value.
In this case Gui will be used to display a message box, I always initialize Ws out of habit, Gr will give us access to the Vr graphics window, Line will be used to ID line entities, and text will be used to display the areas on the screen in the graphics window as text entities. We'll discuss all these as we use them but text values are declared early and in the variable area I put at the beginning because they directly affect how the text displays and the user may want to change the appearance. The one important thing to notice here is the use of the Gr.GetWinScale to grab the map scale of the current graphics window which is then multiplied by the size (in inches) I want it to appear on the screen since the text object expects size in ground units, also note that SetSize is used rather than setting height and width independently

WsNum=Ws.Aws()
I just always throw this in to make sure we all know what workspace is active.

while Line.Id() != -1:
The line object has a function called Id which not only contains all the overhead of allowing the user to identify a line in the graphics window complete with button dialog, but also returns with that line loaded and ready to work on.

..TotalArea=TotalArea+math.fabs(Line.Area())
..NumberOfLines=NumberOfLines+1
The names give away the purpose, and each time a line is selected we bump the accumulated area and the line count. The reason these were initialized to zeros before is that we want to make sure they start from there.

..print TotalArea
Sometimes I just like to send things to the console screen as a debugging tool or so there is something of a temporary record.

..label='%.1f ac' % math.fabs((Line.Area()/43560.0))
..text.SetText(label)
For this program I decided to display individual line areas in the Vr graphics window as text entities so the first thing that needs to be done is setting the text to the area of the line. Here is where the math module is used to make sure the area is positive. The single quote marks and % symbols convert the floating point number returned to a formatted text string with only one decimal place. If I wasn't worried about the formatting I could also use the python repr() function to simply change the number to a string because the SetText function expects to be handed a string. These two lines could also be easily written as one line but that makes it a little more obscure for this example.

..(MinX, MinY, MinZ, MaxX, MaxY, MaxZ) = Ws.GetLineMinMax (WsNum, Line.GetLineNum())
Since the text needs to be place somewhere on the screen, I decide to just place it in the middle of the lines extents. In order to do that we need the Mins, and Maxs. Those values are stored with the line header in the workspace so within the workspace object Ws we use the GetLineMinMax on WsNum (which we set to the current earlier) to pull the data for the entity number of the currently loaded line.

..text.SetCoord((MinX+MaxX)/2,(MinY+MaxY)/2,(MinZ+MaxZ)/2)
Once we have the extents just take the average and set the text to be displayed at those coordinates.

..text.Plot()
The have Vr plot the text entity on the screen. Note that there is no save done on the text so if the screen is refreshed or overwritten it will disappear.

print 'Total Area ',TotalArea,' Sq Units ',TotalArea/43560.0,' acres'
When the selection is all done (note that we are out of the indented block) print various numbers and text to the console screen for the same reasons listed above..

Gui.MsgBox('%d Lines contain %.2f acres'%(NumberOfLines,(TotalArea/43560.0)),'Area by Selection')
More importantly display it all in a message box that will come up nicely formatted and with an "OK" button to dismiss.

Variations include:
Prompting for a layer number and just summing the areas for all lines in a given layer rather than selecting them.

As the lines are selected, how about changing some graphic attributes (again don't save anything) that make the line stand out so it is obvious which lines have been ID'd.

As lines are computed the individual areas along with some identifying info (entity number, or feature code) could be written to a file followed by the total.

Fix it so that if the user identifies the same line multiple times it only gets added to the total once. (caution geeky hint: I would probably use the entity number, a list, and the list.count function)