Showing posts with label Vr. Show all posts
Showing posts with label Vr. Show all posts

Tuesday, November 07, 2023

Placing Lines Within Polygons Uniquely

 I'll be honest, there might be a function to do this but I couldn't find it and it was only going to take a few minutes so let's take a look.  I have a bunch of triangles for and  XML file that need to be subdivided into areas that are defined by polygons.  I need to make sure that the triangles are kept intact and that there are no duplicates.  In my case it didn't need to be an exact science and I wasn't worried about the edges.  My solution was to check each triangle against a polygon and if the first point of the triangle was within the polygon it would be flagged, or in this case the layer changed.  I just banged it out and I may make changes but for now it does the trick.

print ('line_change_line_in_polygon.py modified   06:25 2023/11/07')
# Change layer of line that starts within polygon.

'''
Author: Dennis Shimer dshimer@gmail.com
License: cc-by-sa  http://creativecommons.org/licenses/by-sa/3.0/
'''

Ws=PyVrWs()
Line=PyVrLine()
BoundingLine=PyVrLine()
Gui=PyVrGui()

CheckLayer=600
NewLayer=1001
PointsFound=0
WsNum=Ws.Aws()

PromBox = VrPromBox ('Check lines in Polygon.', 30, 1)
PromBox.AddInt ('Layer to check', CheckLayer, 1, 32000)
PromBox.AddInt ('Change layer to', NewLayer, 1, 32000)
if (PromBox.Display(0) == 0):
CheckLayer=PromBox.GetIntByPrompt('Layer to check')
NewLayer=PromBox.GetIntByPrompt('Change layer to')
BoundingLine.Id()
print (CheckLayer,BoundingLine.GetLayer())
Gui.ProgInit('Lines',Ws.GetLineCount(WsNum))
for EntNum in range ( Ws.GetLineCount(WsNum)):
Gui.ProgSet(EntNum)
if CheckLayer == Ws.GetLineLayer(WsNum,EntNum):
Line.Load (WsNum, EntNum)
X,Y,Z=Line.GetPoint(0)
if BoundingLine.IsPointInside (X,Y):
# print (X,Y,Z)
PointsFound=PointsFound+1
Line.SetLayer(NewLayer)
Line.ReRec(1)
# Line.Plot()
Gui.ProgReset()
print (PointsFound)


 

Tuesday, September 25, 2018

Modifying LiDAR Intensity in Vr Applications

I actually don't have the patience to look back through posts with regard to modifying LiDAR data so I'll just drop this one in as an example of how simple it can be.  This will have limited usefulness because it is fixing an apparent shortcoming in Vr that will no doubt be rectified at any moment, but hopefully the example will continue to be useful.  The current problem is that points created via dsmare (DSM Area) have a nice RGB value, but sometimes it would be nice to have Intensity as well. Rather than making it tricky I found that if I just add the R, G, and B values and divide by 3 it provides a useful "intensity like" display.    
There are only a couple lines that do anything productive so I'll just explain them.

print 'rgb2int.py modified 12:23 PM 9/25/2018'
# Set point intensity based on rgb
'''
License: cc-by-sa  http://creativecommons.org/licenses/by-sa/3.0/

'''
Ws=PyVrWs()
Punt=PyVrPunt()
Gui=PyVrGui()
WsNum=Ws.Aws()
PointBufferCount=Ws.GetPuntBufCount(WsNum)

Gui.ProgInit('Points',Ws.GetPuntBufCount(WsNum))
for PointBufferNumber in range(PointBufferCount):
    Punt.Load(WsNum,PointBufferNumber)
    Gui.ProgSet(PointBufferNumber)
    for PuntNum in range(Punt.GetCount()):
        x,y,z,PuntA,r,g,b= Punt.GetPuntRgb (PuntNum)
        PuntA['Int']=int((r+g+b)/3)
        Punt.ChgPuntA (PuntNum, PuntA)
    Punt.ReRec()
Gui.ProgReset()
PyVrGr().Replot()
 You need to have a PyVrPunt object to work with.  This object will be used to load up a "Point Buffer" full of LiDAR data which we can then step through the individual point data.  Note that you need to loop over the whole file loading each of the point buffers into the Punt object. This is done with the first for loop

for PointBufferNumber in range(PointBufferCount):

As we step through all the buffers, each one needs to be loaded and then it will be stepped through loading up the individual points hence the next for loop.

for PuntNum in range(Punt.GetCount()):

There are several ways to get particular data items from individual points and much of the data is stored in an attribute dictionary known as PuntA.  Many times I just load up PuntA, query it, make decisions, then make changes based on what I find.  In this case it is handy that the method that extracts the R,G,B data also pulls out PuntA at the same time because Intensity is stored within PuntA.  The first thing that needs doing is grabbing the data for the individual point.


x,y,z,PuntA,r,g,b= Punt.GetPuntRgb (PuntNum)

Then we just turn right around and push the intensity into the current PuntA dictionary and record the modified attributes back to the point.


PuntA['Int']=int((r+g+b)/3)
Punt.ChgPuntA (PuntNum, PuntA)


Once all the points are changed in the currently loaded buffer (Punt), the whole buffer can be re-recorded.


Punt.ReRec()

The Punt class is fully documented in the Python Programming section of the documentation but it basically comes down to knowing if you are creating or modifying something inside the attribute dictionary or outside it and working with the appropriate methods.

Wednesday, December 16, 2015

30 Second Functions

When is it faster to write the function than to even find out how to do it in the help.  Well ok, I did actually search and didn't find out how, but here is what would have been faster.

I use handwheels,  I wanted an easy way to switch the z input from the footdisk to the right handwheel and back.  There is probably a way to pass an argument into a macro but I couldn't find it so enter....

zhand.py

print 'z handwheel'
VrCfg().SetHwLocZ (2)
The beauty of VrCfg is that it modifies the environment in real time just like calling any of the functions that would do the same thing via a dialog box.

Saying anything else would just not have a point, except, always be thinking of ways to extend the usefulness of existing processes.

Tuesday, December 01, 2015

Note to self: Step backward through LiDAR when removing, and repr ( round ( a number )) is awsome

First, apparently I never noticed this before but while trying to step though a bunch of LiDAR points to remove a certain class it apparently changes something dynamically (like the point number in the buffer).  I noticed this because I was doing a

    for PointNum in range(Punt.GetCount()):
        if Punt.Cla(PointNum) == Whatever:
            Punt.DelPunt(PointNum)
To get rid of a certain type of point, in this case based on class.  What I noticed though was that each time it ran there were a bunch of points left over (half??).  I decided that even though I wasn't doing a record until the loop was done, the point number in the buffer must change as points are deleted.  So what to do?  How about starting at the last point and removing them from the top end of the buffer so as you work backwards the previous point numbers would not have changed.  I came up with
for PointNum in range(Punt.GetCount(),-1,-1):
and it seems to work fine.

Second and totally unrelated I needed to rebuild a tile naming structure for a local dataset.  There is a tile scheme that is based on 5000 foot tiles that is easy enough to get the names of.  The problem is that there is also a subset of 1250 foot tiles that starts from the corner of each 5k block but the names are based on 1000 foot increments.  When you digitize a random point in a block you can't necessarily round down to the lower 1000 because they just truncate the thousands from a rounded 1250.  Ok hard to imagine but the corners would be 0, 1250, 2500, 3750 so a number 3749 would fall in a 2500 block and truncate to 2000 meaning the tile would have 2 in the name, not to mention dropping a million off one of the coordinates.  Now for the why I love python part!
repr(round(1773691/1250)*1250)[1:4]
yields '772' just like is necessary to build the proper tile name. 

Wednesday, April 22, 2015

LMSTFY ( Let Me Sealay.py That For You)

I should have mentioned this long ago before you invested time in reading my blog, but here goes... Fact is I'm not particularly bright, I'm just inquisitive (and as mentioned previously constructively lazy).  This means a couple of things; first I love to find an easier way to do almost anything, and second sometimes I spend more time researching than I would if I had just done the task (oh but next time it will be so much faster). Because of this, sometimes I know things, and when you know things people like to ask you things because you might know that particular thing they want.  Yes almost daily I am tempted to just say "Let me Google that for you" because that would be much easier for me than actually explaining it (reference lazy thing above).

I have about 150 out of the 240 layer numbers that I use daily in mapping projects memorized.  Over time though you need to add new layers or break them down to a more granular level so consequently you can see that there are a bunch I don't know.  I could memorize them (and a few I will) but the majority of the others I just want an easy way to find the rare one I need.  Hence sealay.py or "Search Layer Names".

print 'sealay.py modified 7:31 AM 4/16/2015'
# Display layers matching search string
'''
Copyright 2015 Dennis Shimer
Vr Mapping copyright Cardinal Systems, LLC
No warranties as to safety or usability expressed or implied.
Free to use, copy, modify, distribute with author credit.

Prompts user for layer name (or any part) then displays layer
numbers and full names of layers matching string.
'''

def lines2lists(AListOfDataLines):
    '''
    Function readlines returns an entire file with each line as a string in a
    list of data.  This function will convert each string into a list of words,
    then return a list of lists. Why? Just because I like to work this way.
    Example:            lines2lists(['first line','the second line','or sentences'])
    Would return:       [['first','line'],['the','second','line'],['or','sentences]]
    '''
    DataList=[]
    for Line in AListOfDataLines:
        DataList.append(Line.split())
    return DataList

PrintString=''
PrintList=[]

#If sent argument use as search string else prompt.
if VrArgs:
    SearchText=VrArgs[0]
else:
    SearchText=PyVrGui().InputDialog ('Search String', 'Layer numbers by name')[1]
   
LayerFileName = VrCfg().GetLayerNameFile ()
LayerFile = open(LayerFileName , 'r')
LayerData = LayerFile.readlines()
LayerFile.close()
LayerList = man.lines2lists(LayerData)

#Search through data, if string found add it to a multi-line printable string.
for DataLine in LayerList :
    if len(DataLine) > 1 :
        if DataLine[1].count(SearchText.upper()):
            PrintString=PrintString+DataLine[0]+' '+DataLine[1]+'\n'
            PrintList.append(DataLine[0]+' '+DataLine[1])

# Two possibilities, the first commented line just displays the found layers
# The rest will let the user select one correct entry and send a LAY= command.
if PrintString:
#    PyVrGui().MsgBox(PrintString , 'Matching Layers')
    PromBox = VrPromBox ("Set Layer", 30, 1)
    PromBox.AddList ("Layer", 40, len(PrintList)+1, 0)
    for LayerItem in PrintList:
        PromBox.AddListItem (LayerItem)
    if (PromBox.Display(0) == 0):
        SetToLayer= PromBox.GetListByPrompt ("Layer")
    if SetToLayer : PyVrGui().PushKeyin('lay={:s}'.format( SetToLayer.split()[0]))
else :
    PyVrGui().MsgBox('No Match to {:s}'.format(SearchText), 'Matching Layers')

Sunday, March 22, 2015

It's all part of the process (or subprocess as the case may be)

On occasion I make the case for installing python in order to extend the usefulness of the environment in Vr, or at least getting hold of some of the most useful standard libraries.  Well here is another good reason.  This is just a snippet that I will use later but I figure this is as good a place as any to keep track of it.

Before too long I want to write a script that will depend on running an external process, then interacting with the results of the process (no spoilers yet).  In order to do that I'm going to make something similar to the following call to process some files leaving the results in the working directory. The research I did here was to make sure I had a way to not proceed until the original process was complete.  Later I'll need to make sure I can run a shell command and capture the resulting output but this will do for now.

import subprocess
process = subprocess.Popen('lasboundary -i *rgb1.las -otxt')
process.wait()
print process.returncode
print '\a'
the last line is just to beep so I knew when it was done.

Wednesday, March 11, 2015

Reminder snippets - liblas and reading laz files

The other thing that I used to do with this blog is post things that I wanted to remember.  At one point the Vr forums were a good place to do this because it also gave an opportunity to share and collaborate.  Since that doesn't really seem to be happening any more I suppose I could drop things here and then come back to them later if necessary.  Yes I could just do this in a document but who knows maybe a new opportunity for some kind of sharing community will pop up again.

In this case I just want to remind myself that if Vr never adopts the .LAZ LiDAR compression format for reading, it would still be possible using liblas. This is simple a little interactive session that shows it is theoretically possible using my own data.

There may indeed be other better ways, but this seems to work fine.

>>> import liblas
>>> f=liblas.file.File(r'c:\tmp\N1915250.laz',mode='r')
>>> f

>>> p=f.read(100)
>>> p.classification
5
>>> p.x
1919853.35
>>> f.header.compressed
True
>>> f.filename
'c:\\tmp\\N1915250.laz'

Tuesday, August 27, 2013

Let me look that up in the dictionary for you.

There have been some changes to the VrPunt methods over time.  I have suspected that not everything is implemented exactly the way it is documented, and sometimes a name could be different or missing.  I have discovered though that the real magic is just in working with the dictionary which stores all the relevant LiDAR point attributes.  The LiDAR data provider that we most often use stores the individual flight line tag in the "SOU" tag, Vr however will only filter by the "FLT" tag.  Since FLT isn't used in our data, changing it to reflect the SOU value doesn't seem to have a downside.  Below is a simple program that runs through all the point data and copies SOU to FLT.  Now I can use the flight line filter to turn the points on and off.

print 'souflt.py modified 12:44 PM 8/27/2013'
# Copy LiDAR Source attribute to Flight attribute
'''
Copyright 2013 Dennis Shimer
No warranties as to safety or usability expressed or implied.
Free to use, copy, modify, distribute with author credit.

Reads the "Source" in a PuntA dictionary and sets the "Flight" to the same value.

'''


Ws=PyVrWs()
Punt=PyVrPunt()
Gui=PyVrGui()
WsNum=Ws.Aws()
PointBufferCount=Ws.GetPuntBufCount(WsNum)

Ws.UndoBegin(WsNum,"Source2Flight")
Gui.ProgInit('Points',Ws.GetPuntBufCount(WsNum))
for PointBufferNumber in range(PointBufferCount):
    Punt.Load(WsNum,PointBufferNumber)
    Gui.ProgSet(PointBufferNumber)
    for PointNum in range(Punt.GetCount()-1,-1,-1):
        PuntA=Punt.GetPuntA(PointNum)
        PuntA['Flt']=Punt.Sou(PointNum)
        Punt.ChgPuntA (PointNum, PuntA)
    Punt.ReRec()
Gui.ProgReset()
PyVrGr().Replot()
Ws.UndoEnd(WsNum)
 So the key is to load up the PuntA to make sure it is populated with the correct values, then modify the particular attribute and save it back to PuntA. When all the points are done re-record the entire Punt buffer.  As of this writting the dictionary is organized as follows.

"Lay"     = 1   - Layer                    (1-30001)
"Int"     = 0   - Intensity                (0-65535)
"Dsp"     = 1   - Display flag             (0-1)
"Ret"     = 0   - Return number            (1-5)
"Nre"     = 0   - Number of returns        (1-5)
"Sdf"     = 0   - Scan direction flag      (0-1)
"Edg"     = 0   - Edge of flight line flag (0-1)
"Cla"     = 0   - Classification           (0-31)
"Syn"     = 0   - Synthetic flag           (0-1)
"Key"     = 0   - Key-point flag           (0-1)
"Del"     = 0   - Delete flag              (0-1)
"Ang"     = 0   - Scan angle               (-90 - +90)
"Flt"     = 0   - Flight number            (0-255)
"Sou"     = 0   - Point source Id          (0-65535)
"Red"     = 255 - Red component color      (0-255)
"Green"   = 255 - Green component color    (0-255)
"Blue"    = 255 - Blue component color     (0-255)
"GpsTime" = 0.0 - GPS Time                 (Double precision number)

Friday, November 16, 2012

Projecting some thoughts about pyproj.

So I've been playing around with pyproj a little lately and have come up with a couple new thoughts.  As always I'm typing as I go so not everything is a clean example of good code or procedure, just something I've run as a test of my thoughts.

First, the application; below is showll.py which when run displays the lat long of the current cursor position in the DspMsg0 area (the top long line right below the command area).  I decided to do this as an app so it is running in real time and clicking the buttons just performs specific tasks.  Speaking of the buttons, B1 or the left mouse button prints the current position in the local coordinates, long lat, and notes the projection used. Something like

1369447.29077 471184.7857 (-84.66858719560254, 40.93954025528449) epsg:3753

In this part of the world we primarily deal with two projections so I kept it simple and just used B2 or the right mouse button to toggle between the two.  It would have been easy enough to bring up some kind of selection dialog, but for now I only need the two. The current projection is also displayed in the DspMsg area.

I recently discovered why I always had to send and receive meters even though the EPSG code clearly specified at foot based projection.  It was by design so there was never any question what was expected or returned.  The answer was always meters, however the code has since been modified to include an init keyword

preserve_units=True 

which handily enough, does exactly what it says. I understand the value of the consistency, but appreciate the option. I have also discovered in testing that you can take a proj4 definition and paste it straight into an object init function in quotes and get the exact same functionality as using the EPSG codes.  For example...

ohs=pyproj.Proj('+proj=lcc +lat_1=40.03333333333333 +lat_2=38.73333333333333 +lat_0=38 +lon_0=-82.5 +x_0=600000 +y_0=0 +ellps=GRS80 +to_meter=0.3048006096012192 +no_defs' )

Well in any case here is the app, it isn't worth much except by way of example, but pyproj itself is really handy when working with things like flight prep or control layouts when you want to give somebody geodetic coordinates to work from.

print 'showll modified 12:17 PM 11/14/2012'
# Show Longitude Latitude of current position.
'''
Copyright 2012 Dennis Shimer
Vr Mapping copyright Cardinal Systems, LLC
No warranties as to safety or usability expressed or implied.
Free to use, copy, modify, distribute with author credit.

Only two projections are supported. Both are NAD83(HARN) / Ohio (ftUS).
EPSG:3753 for North Zone
EPSG:3754 for South Zone
Button 2 (right mouse) toggles the projection, Button 1 (left mouse)
will print out the current position in local coordinate and it
Longitude, Latitude decimal degrees.
'''
MYAPPID = 10011

Gui = PyVrGui()

import pyproj,sys

global Projection

Projection=pyproj.Proj(init='EPSG:3754', preserve_units=True)
Gui.DspMsg('OH S EPSG:3754')

def MyAppDigCB (x, y, z, key, id):

 global Projection
 Gui.DspMsg0(repr(Projection(x,y,inverse=True)))
 if (key == 1):
  print x,y,repr(Projection(x,y,inverse=True)),Projection.srs.split('=')[1]
 elif (key == 2):
  if Projection.srs.count('3754'):
   Projection=pyproj.Proj(init='EPSG:3753', preserve_units=True)
   Gui.DspMsg('OH N EPSG:3753')
  elif Projection.srs.count('3753'):
   Projection=pyproj.Proj(init='EPSG:3754', preserve_units=True)
   Gui.DspMsg('OH S EPSG:3754')
 elif (key == 11):
  MyApp.Close ()
  MyApp.StopRunning ()

def MyAppKeyCB (key):

 MyAppDigCB (x,y,z, key, 0)

try:
 MyApp = PyVrApp (MYAPPID, "ShowLL")
except:
 print "Could not start application:", sys.exc_info()[0]
 
try:
 Mk = PyVrMenuKeys ("ShowLL", 10)
 Mk.SetMkLabels ("ShowLL","1 Print pt","2 Tog N/S", "3", "4", "5", "6", "7", "8", "9", "*", "0", "# Quit")
except:
 print "Could not create menukeys:", sys.exc_info()[0]

try: 
 MyApp.SetDigCB (MyAppDigCB)
 Mk.SetKeyCB (MyAppKeyCB)
 MyApp.KeepRunning ()
 
 Mk.Delete ()
except:
 print "Exception during application:", sys.exc_info()[0]
 Mk.Delete()
 MyApp.Close ()
 MyApp.StopRunning ()

How about one more bonus snippet with regard just switching from on projection to another. Thanks to Proj4 and pyproj, this is just how easy it can be.

>>> ohs=pyproj.Proj(init='EPSG:3754', preserve_units=True)
>>> ohn=pyproj.Proj(init='EPSG:3753', preserve_units=True)
>>> pyproj.transform(ohs,ohn,1759595.61,661026.83)
(1759551.3008025475, 54039.04746006191)

Monday, May 18, 2009

We all have our limits

Note that I am trying a new method of posting code, hope it works better but of course the original code itself can always be downloaded from the website where it is stored.

Quite often when writing some sort of import function I would like to limit the data imported to certain physical constraints. I started out asking the user if they want to only act on coordinate values that fall within a bounding box, then if they said yes, prompt them to ID the bounding line. This works great as far as it goes, as part of the ID function I would set a flag (in this case BoundrySet) to 1 then later in the program if BoundrySet was true the IsPointInside method of the Line class could be called. Something like....

if BoundrySet :
if BoundingLine.IsPointInside (X,Y): Do something


But then I got thinking, there are several easy ways to check for bounding areas without having a line already digitized. How about using the screen boundaries, or digitizing a rectangle based on the lower left and upper right corners, or for that matter just start digitizing points and create a line in memory on the fly. Well it turns out that I already had the snippets I needed in other functions so just copied them out, cleaned them up a bit (and I do mean a bit, it still isn't particularly pretty, but it works) and created a basic function that I can paste into any program where spatial filtering is helpful.

There are a couple of caveats. Using the rectangle select mode requires that you select the lower left point first, then the upper right based on the screen orientation. I'll probably clean this up some day and allow for more flexibility but it falls into the "quick hack for some purpose, and I know how it acts" category of code that gets the job done but isn't as attractive as it should be. Using the "Digitize" mode allows the user to start clicking on positions and builds the boundary line on the fly, if it would be helpful it would be just as easy to record the line for future interaction but for now I just throw it on the screen. The nice thing about this dialog is that it behaves just the way you expect so if the focus is in the drop down and you hit an 'r' rectangle mode will be activated and so on.

print 'SetBoundary.py modified 8:00 AM 5/15/2009'
# Set spatial bounding for processing
'''
Copyright 2009 Dennis Shimer, M.A.N. Mapping Services Inc.
No warranties as to safety or usability expressed or implied.
Free to use, copy, modify, distribute with author credit.

Really just a container for the code snippet that would do spatial
filtering as part of a larger program. Just written as working code
here for testing purposes.

Variables of interest:
None
'''
import math

BoundingLine=PyVrLine()
Gr=PyVrGr ()
Gui=PyVrGui()
BoundrySet=0

PromBox = VrPromBox ("Set Boundry", 30, 1)
PromBox.AddCombo ('Boundry', 5, 0, 0)
PromBox.AddComboItem ('Line')
PromBox.AddComboItem ('Screen')
PromBox.AddComboItem ('Rectangle')
PromBox.AddComboItem ('Digitize Points')
PromBox.AddComboItem ('None (whole file)')
PromBox.SetFocus()
if PromBox.Display(1)==0:
BoundryType=PromBox.GetComboByPrompt ('Boundry')
if BoundryType==0:
BoundingLine.Id()
BoundrySet=1
elif BoundryType==1:
for corner in range(4):
x,y,z=Gr.GetWinCorner (corner,0)
BoundingLine.AddPoint(x,y,z)
BoundrySet=1
elif BoundryType==2:
Stat,llx,lly,z=Gui.GetCoord('Boundry')
Gr.DrawMode (1,-1)
Gr.PenNum (1)
Gr.DrawMarker (llx,lly,z,MARK_CROSS,Gr.GetWinScale ()*.2,-1)
Stat,urx,ury,z=Gui.GetCoord('Boundry')
Gr.EraseMarker ()
ScreenAngle=Gr.GetWinRot()[2]
Angle=math.atan((ury-lly)/(urx-llx))-Gr.GetWinRot()[2]
Diagonal=PyVrGeom().Dist(llx,lly,urx,ury)
Height=math.sin(Angle)*Diagonal
if Height < 0.0: Height=Height*-1.0
Width=math.cos(Angle)*Diagonal
if Width < 0.0: Width=Width*-1.0
BoundingLine.AddPoint(llx,lly,z)
lrx=llx+(Width*math.cos(ScreenAngle))
lry=lly+(Width*math.sin(ScreenAngle))
BoundingLine.AddPoint(lrx,lry,z)
urx=lrx-(Height*math.sin(ScreenAngle))
ury=lry+(Height*math.cos(ScreenAngle))
BoundingLine.AddPoint(urx,ury,z)
ulx=urx-(Width*math.cos(ScreenAngle))
uly=ury-(Width*math.sin(ScreenAngle))
BoundingLine.AddPoint(ulx,uly,z)
BoundingLine.AddPoint(llx,lly,z)
BoundingLine.Plot()
BoundrySet=1
elif BoundryType==3:
Stat=0
while Stat==0:
Stat,x,y,z=PyVrGui().GetCoord('Dig Boundry')
BoundingLine.AddPoint (x,y,0.0)
BoundingLine.Plot()
BoundingLine.Close(2)
BoundingLine.Plot()
BoundrySet=1


Wednesday, April 29, 2009

The 5 minute function.

No I'm not actually going to put a lot of time into a post since there doesn't really seem to be much interest in what's going on. However a recent conversation in the Vr Python Forum is a perfect example of the whole purpose of having Python in Vr. If you are a member you can check it out there.

http://www.cardinalsystems.net/forum/index.php?topic=51.0

The basic premise is that you need a simple function to automate a fairly simple task, in this case labeling all symbols in specified layers with a piece of text in which the label is the symbols feature code. It could be any attribute, or any entity type, but the point is; rather than waiting for the feature to show up in Vr, with a little practice, you can write it yourself (usually from existing snippets) in about 5 minutes and be on your way.

Thursday, November 01, 2007

Inherit everything but the kitchen sink.

Back when I was just a young map maker, I was actually fairly sharp, and had a memory that was quite useful. I worked on a CAD system that until VrOne came along seemed like one of the fastest, most practical, easy to use map collection systems around. It used a numeric command set that allowed you to set any combination of insert parameters to a 3 digit code. Alas you were limited to 120 codes which seemed like plenty and were quickly memorized. Of course now I can't remember what I had for breakfast (I did have breakfast didn't I), but I can remember all 120 of those codes. The flexibility of Vr allowed me to easily make macros that do those 120 tasks, but over the years we continued to add things. Now I know there is a function key that will put in a double yellow road centerline, but what combination of letters did we use for the function key (there are 26 letters and 5 unique words in that description). Sometimes when I'm collecting I think good grief, there is what I want right there, it sure would be nice to just change my current parameters to match that existing entity. Well, back in January I added a quick command that came in handy more often than I thought and has grown up since then. The premise was simple, a command that allowed you to Id a line, then start digitizing using the attributes of that line. Remember it looked something like this

Line = PyVrLine ()
Gui=PyVrGui()
Line.Id()
lay,gp,wid=Line.GetLayer(),Line.GetGpoint(),Line.GetWidth()
Gui.PushKeyin ('inslin,lay=%d,grp=%d,wid=%d'%(lay,gp,wid))

It was so handy that I made one that did the same thing for fly-lines, and symbols. This way when I'm tying to an edge or come across something I forgot, and can't remember the function key, all I need is the short mnemonics for my inhlin, inhfly, and inhsym commands. Then I got thinking, what if I'm already digitizing a line or symbol and want to match something I happen upon? Well here is my inhent.py which some folks might find a bit odd but it also gives examples of a couple of useful Vr Objects

print 'inhent.py Inherit From Entity modified 8:52 AM 10/31/2007'
'''
Copyright 2007 Dennis Shimer, M.A.N. Mapping Services Inc.
Vr Mapping copyright Cardinal Systems, LLC
No warranties as to safety or usability expressed or implied.
Free to use, copy, modify, distribute with author credit.

Asks user to identify an entity, then sets certain
parameters inherited from the identified entity.

'''

Line=PyVrLine()
Sym=PyVrSym()
Text=PyVrText()
Gui=PyVrGui()

Ws=PyVrWs()
# There are a couple of times we'll need to multiply by the scale because ground
# units are returned, and we'll need to set based on map units.
Scale=PyVrWs().GetTargetScaleIn(Ws.Aws())

EntWs,EntNum,EntType,LinePtNum,x,y,z=Ws.IdEnt(-1,-1,-1,-1)
# Man I love IdEnt, it isn't in the documentation yet but by using
# -1 in the SeaEnttype spot, it remembers the last state.
if EntType==1:
# If it's a line grab the appropriate settings
...Line.Load(EntWs, EntNum)
...Layer,Graphic,Width=Line.GetLayer(),Line.GetGpoint(),Line.GetWidth()
# This line is left over from when I forced the user to hit end after
# identifying the entity, which was useful if you grabbed the wrong one
# but I found I liked it this way better, so the following line doesn't
# really matter because it just does it after you id something.
...Gui.DspMsg0 ('lay=%d, grp=%d, wid=%d'%(Layer,Graphic,Width))
...Type='Line'
if EntType==3:
# Ditto if it's a symbol.
...Sym.Load(EntWs, EntNum)
...Layer,Graphic,Radius,Rotation=Sym.GetLayer(),Sym.GetGpoint(),(Sym.GetRad()/Scale),Sym.GetRot()
...Gui.DspMsg0 ('lay=%d, grp=%d,rad=%.3lf,rot=%.3lf'%(Layer,Graphic,Radius,Rotation))
...Type='Symbol'
if EntType==4:
# or even a piece of text.
...Text.Load(EntWs, EntNum)
...Layer,Font,Height,Width,Rotation,Label=Text.GetLayer(),Text.GetFontNum(),(Text.GetHgt()/Scale),(Text.GetWdt()/Scale),Text.GetRot(),Text.GetText()
...Gui.DspMsg0('lay=%d,fnt=%d,hgt=%.3lf,wdt=%.3lf,rot=%.3lf,txt=%s'%(Layer,Font,Height,Width,Rotation,Label))
...Type='Text'

# Once you grab the settings desired, the only thing left to do is
# push the proper key-ins.
if Type=='Line':
...Gui.PushKeyin ('lay=%d,grp=%d,wid=%d'%(Layer,Graphic,Width))
elif Type=='Symbol':
...Gui.PushKeyin ('lay=%d,grp=%d,rad=%.3lf,rot=%.3lf'%(Layer,Graphic,Radius,Rotation))
elif Type=='Text':
...Gui.PushKeyin ('lay=%d,fnt=%d,hgt=%.3lf,wdt=%.3lf,rot=%.3lf,txt=%s'%(Layer,Font,Height,Width,Rotation,Label))

Given that there probably aren't 3 people in the world that read this on a regular basis, I'll probably hit it about once a month from now on. Don't forget to contribute to the forum.

Happy coding.

Thursday, July 19, 2007

Finally A Vr Command

Well enough playing around with external files. It's about time to head back to what VrPython was intended for and write a new function. This one in particular allows the user to parallel part of an existing line. This is a good example of how a program evolves (to say it was intelligently designed probably infers too much), because as it started off the user had to digitize the offset distance. Then with the advent of VrArgs in 3.3.? it seemed like a good idea to put in the option to enter an offset as a argument after the user typed parpart. Of course with the opportunity to put in a argument as an offset, it seemed silly to not have a dialog somewhere. The problem was that I didn't want dialog every time, and you already had to Id the line, start point, end point, and offset which was approaching the limits of how much I wanted the user to have to do. Hence the decision that if you got all the way to the point of digitizing the offset and hadn't specified one but hit an "end" or "#" without doing so the program would bring up a dialog to allow typing one in. Well let's take a look

#This has become the standard I use to start each program.
print 'parprt.py ParallelPartial modified 8:23 AM 7/13/2007'

'''

Copyright 2007 Dennis Shimer, M.A.N. Mapping Services Inc.

Vr Mapping copyright Cardinal Systems, LLC

No warranties as to safety or usability expressed or implied.

Free to use, copy, modify, distribute with author credit.


Inserts a line parallel to the identified line.
Allows for picking start and
end points of new line, and offset.
If a number is passed it as the first
it will be used as an offset.
If the <#> End button is pressed during offset
measurement a
dialog will come up prompting for offset.

'''

Ws=PyVrWs()

Line=PyVrLine()

Gui=PyVrGui()

WsNum=Ws.Aws()
# Create all the objects we will use.

while Line.Id() != -1:
#
In theory you could partially parallel many lines
# though in reality it doesn't happen much, but will do so
# until the user doesn't Id a line which returns -1.

....Ws.UndoBegin(WsNum,"inspar_part")
# The undo is inside the Id loop so each line paralleled
# could be undone individually.


....Gui.DspMsg0('Dig start of new line')
# Since GetCoord() isn't very customizable as far as buttons
# and messages, we'll display instructions in the message area.

....Stat1,NewX1,NewY1,NewZ1=Gui.GetCoord('Point1')
# And get a coordinate for where the newly paralleled line
# Should begin.


....Gui.DspMsg0('Dig end of new line')
....Stat2,NewX2,NewY2,NewZ2=Gui.GetCoord('Point2')
# And the same on the other end.

....if VrArgs:

........print VrArgs

........Offset=float(VrArgs[0])
# This whole if statement was added later after the introduction
# of VrArgs. If a string (or multiple strings) are placed after
# the script name, they are passed in as a list of strings. So the
# test would fail if VrArgs was empty (nothing passed) and would
# pass to the else statement. If however something was passed,
# convert it to a real number and assign it to the offset. Bonus
# points for anyone who identifies the huge bug I just noticed in
# this 3 line block.


....else:
# In other words if VrArgs is not true (is empty).

........Gui.DspMsg0('Dig offset # End for dialog')
........StatS,SnapX,SnapY,SnapZ=Gui.GetCoord('Offset')
# Same as above, display instructions and read a coordinate
# that represents how far the cursor is from the line.


........Offset=-1.0*(Line.SnapPoint (SnapX, SnapY)[3])

# Use the computed offset. The -1 is just to correct for
# how SnapPoint computes the distance.

........if StatS:
# This comes about if the user hits the end button instead
# of a 1 to cancel the digitize offset.

............PromBox=VrPromBox('Prompt title',20,1)
............PromBox.AddDouble('Enter a real number',.5,2)
............if (PromBox.Display(0) == 0):
................Offset=PromBox.GetDouble(0)

# Just a simple prombox to allow for entering the offset if
# it wasn't passed or computed in any other way.

....Num1=Line.SnapPointAdd (NewX1,NewY1,0.0)
....Num2=Line.SnapPointAdd (NewX2,NewY2,0.0)

# We are adding points to the line object that was identified
# this is ok because later we will Rec() instead of ReRec() which
# will create a new line.

....if Num1>Num2:
........tmp=Num1

........Num1=Num2

........Num2=tmp
# At one point DelPoints didn't work properly if the beginning
# point was higher than the end point (it may not still, but doesn't
# really matter if it is checked and swapped if necessary.

....Line.DelPoints (Num1, Num2, 1)
# Mode 1 in effect deletes points outside begin and end rather
# than between them (which would be mode 0)

....Line.Offset(Offset)
....Line.Rec(WsNum)

....Line.Plot()

# Take that line object, offset it by the distance entered or
# computed, record it as a new entity, plot it and there you go.
# Part of the original line has been stored as a new line parallel
# to it.
....Ws.UndoEnd(WsNum)

Reminder that as with all other scripts, you can either copy and paste what you see here (substituting your prefered indentation for the holding characters "....", or find the original at http://python4vr.googlepages.com

Thursday, June 21, 2007

Life Outside Vr 4

We've looked at external files with the goal of getting useful information out of them. At the same time how about saving something there. We use a program that takes symbols in a certain layer, compares their elevation to an active dtm, and if they are over a specified distance from the elevation displays the error. It is a very useful program and when it was first written had all the variables hard coded, but stored at the beginning of the file so they could be easily found and changed. With the advent of PromBox it just became too easy to allow the user to confirm and change the variables each time. You could even easily use the old hard coded values as defaults. However it seemed like a certain set of values would be used over and over again based on job specifications. The obvious solution would be to have the last values used stored between runs to be used as the new defaults. Storing values like this is easy enough, but what elements would the most elegant solution contain? I have no idea but I did come up with a couple of features I really wanted.

First lets store them in a logical location like /vr/hostdir where all the other function parameter files are stored. Truthfully it would probably be better to make a user parameter directory, but I'll be lazy for now because the next thing I wanted was a test to see if the parameter file existed already and if not, would create one with the original default values. Yes I could test for a folder and if it wasn't there create it but maybe that will come in the next version but for now I didn't really want to change the directory structure.

import os
# os is necessary to test the existence of the parameter file.

ParFileName='c:/vr/hostdir/save_params.par'
# Call it what you want, this is great code to save for later use in other
# programs that require persistence.

if not os.path.isfile(ParFileName): # Does the file exist.
open(ParFileName,'w').write('%d %.3f %.3f'%(86,.2,0.0))
# If not then create it in write mode and write an integer and 2 floating
# point numbers with 3 decimal places.

Params=open(ParFileName,'r').read().split()
# Since it either existed or was created all we need to do now is open, read
# and split the values into a list that would look like ['86','.2','0.0']

PromBox=VrPromBox('Display Sym to Dtm',20,1)
PromBox.AddInt('Layer to Check',int(Params[0]),1,1000)
PromBox.AddDouble('Display delta >',float(Params[1]),2)
PromBox.AddDouble('Text Rotation',float(Params[2]),1)
# Using the values stored in the Params list make a prombox which will allow
# the user to change the values.

if (PromBox.Display(0) == 0):
...LayerToCheck=PromBox.GetInt(0)
...DisplayDelta=PromBox.GetDouble(1)
...TextRot=(PromBox.GetDouble(2))
# Grab the new values.

open(ParFileName,'w').write('%d %.3f %.3f'%(LayerToCheck,DisplayDelta,TextRot))
# And write them out to a file.

I won't spend the time on all the uses for keeping persistent variables between sessions, but this is the basics.

This just about wraps it up for reading and writing files, except for maybe writing parameters as a binary file, just for the fun of it and maybe walking through the single line parser I posted on the forum. It isn't very flexible but offers some interesting lessons. Here's a peek.


alist=[]
for line in open('hughes.pxyzopk','r').readlines():Alist.append(line.split())

Friday, June 08, 2007

Life Outside Vr 3 (well not very far outside)

Actually not at all, but the point isn't that we need to work outside Vr, it is that there is a whole world of files available using some straight python functionality. Now that we have a little file opening and reading under our belts, lets come back into Vr and bring a little of that technique with us. Let's also work on tightening up the code a little. If you are fairly new to VrPython, I think you'll really like this one. We are going to let Vr tell us the name of the file to open (in this case the current function key file) then using standard python we'll open, read, and parse the data we want out of the file, then use Vr to build a prompt box. For this example the data we want is all the function key names, and the prompt box will allow us to pick a function key name. Why? Well how about if you were going to write a parallel function using the existing inspar command but wanted to change the PARMOD= to "Function Key" and wanted to select the function key from a list then send it using the FKEY= key-in. If so, here is one way to get the function key name. It is rather long and convoluted but I'll try to make it worth what you are paying for the look.

Cfig=VrCfg()
# We'll need a Vr config object to access the name of the currently
# loaded function key file name.

FileName=Cfig.GetFkeyFileName()
# Grab the name.
FkeyFile=open(FileName,'r')
# Open the file
FileData=FkeyFile.read()
# We're going to read the file twice. This time as a string so we can count the
# instances of the key-word "KeyName" to figure out how many entries our
# prompt box will need.
NumberOfFunctionKeys=FileData.count('Fkey KeyName')
# Use the string method count to do just that.
PromBox = VrPromBox ("Function Keys", 60, 1)
# Create a PromBox object.
PromBox.AddList ("Select a Function Key",NumberOfFunctionKeys , 40, 0)
# I'll skip helpful text and just get right to the listbox of length equal to
# the number of function keys.
FkeyFile.seek(0,0)
# Rewind the file to read it again.
FileData=FkeyFile.readlines()
# This time read it as a list, each element of which is a line from the file.
for FileLine in FileData:
# For each line in the data list.
..if FileLine.count('Fkey KeyName') != 0:
# Test to see if it is a line containing the function key name.
....FkeyName=FileLine.split()[2]
# If so split the line data into the three words
# 'Fkey','KeyName','ActualName' which is in itself a list, the third element
# of which ( [2] ) is the actual name of the function key.
....PromBox.AddListItem(FkeyName)
# Add that to the list of items to be displayed.
FkeyFile.close()
# Could have closed it after the readlines() but just go ahead and do it now.
if (PromBox.Display(0) == 0):
..FunctionKey=PromBox.GetList (0)
# Standard prompt box usage to grab a list item
print FunctionKey
# and print it out (or use it in an interesting way)

Now as far as tightening it up. I could have read the file only once and built a for loop to count the instances of the KeyName key-word, but I'm going to go in a totally different direction. I'm still going to read the file twice, as a matter of fact I'm going to open it twice. The goal of this exercise is to get as few lines of code as I can. I'm not going to profess that this is the tightest and most efficient. It just has a lot less lines of code and is intended to make you think. Look at it first, then read the explanation.

PromBox = VrPromBox ("Function Keys", 60, 1)
PromBox.AddList ("Select a Function Key", open(VrCfg().GetFkeyFileName (),'r').read().count('Fkey KeyName'), 40, 0)
for FileLine in open(VrCfg().GetFkeyFileName (),'r').readlines():
..if FileLine.count('Fkey KeyName'):PromBox.AddListItem(FileLine.split()[2])
if (PromBox.Display(0) == 0):print PromBox.GetList (0)


Line 1: We have to create a prompt box object because I can't find any way around it and we'll need to use methods of this specific object in the next line.

Line 2: Let's take this a step at a time.
A) Add to the list using the formula AddList (Prompt, NumItems, Height, DefaultItem) Prompt is set to "Select a Function Key", NumItems is the tricky part, Height is 40 and DefaultItem is 0.
B) Look at the NumItems formula and read it as follows
Open a file using the string returned by a VrCfig() object method GetFkeyFileName in read mode ( 'r' ) which open file object you should read() the data, of which data you should count() the number of occurrences of 'Fkey KeyName'. Notice that when all this is said and done it just give a number which represents the number of function key names and therefore the number of function keys

Now that we have a prompt box which contains a list set to the length equal to the number of function key names, let's populate the list with those names.

Line 3: For each element in a list, the list comes from opening the same file again, in read mode again, but this time instead of read() to get one solid string, we use a method that will return a list which is exactly what the for loop is working on. We are looping on a list that was created and populated just for the loop, each element of which is a line from that file.

Line 4: FileLine represents the line of data out of the file which is being examined as we loop over the entire list of lines. count() tells how many occurrences of a work exist in the string 0 means none which in terms of an if means false so any other number that comes back means true (the string exists, therefore it is a function key name). if it is true we want to add the actual name to the list. The actual name as we saw above means splitting the string, which creates a list, from which we want element [2]. So add to the list the third word which is the result of splitting the string because it does (true) contain the phrase 'Fkey KeyName'. I didn't add another line and indent it because there is only one statement to execute if true which allows you to just put it after the colon and be done with it.

Line 5: Same idea, standard prombox syntax, but since there is only one action, just put it on the same line. Ugly code but remember my only goal was to reduce the line count. And why put the string in a variable just to print it on the next line, print it as it returns from GetList()

And I haven't even taken the time to explain the forum post regarding reading and parsing aerosys .pxyzopk files, a great example of the kind of data we mappers work with all the time. Maybe next time.

Monday, May 21, 2007

Life Outside Vr 2

Ok I have a few minutes so let's look at actually reading some data out of an open file. We'll stick with ascii files for now. There are so many fun reasons to work with files outside Vr that it would take too long to enumerate all of them but a few of my favorites include.

  • Reading info out of an ImageUtil project and creating all the Vr2Ori project and all models with 1 click.
  • Converting all Vr2Ori and VrAt files to or from their corresponding equivalents in orientp3 (yes we still use it and get along fine) so a setup in the analytical equals a setup in softcopy or vice versa.
  • Reading a PATB and plotting it graphically in VrOne for visual analysis and blunder checking.
  • Reading and plotting AT results including photos, layout data, computed extents and photo scale based on image centers and control averages.
You get the idea, there are just so many files out there that can be of easy use with a click or two. Sometimes there is already a way to do the same things, but a custom program allows you to set things up exactly the way you want without any further intervention. As in the last post we'll be working with vr.cfg in read only mode because it is a file that exists on everyones system (if you are working in Vr, which I assume) and has the same basic patterns. Again I will suggest you work in some kind of python IDE, I suggest pythonwin but you could even work from the python command interpreter if you want.

Let's start by creating a python file object like we did last time, it will be called f (for file).
f=open('/vr/hostdir/vr.cfg','r') # 'r' means read mode.
now if I
print f
there should be some obscure text describing the object. Hopefully there was some time to look over the file object docs to study up on the simple methods, let's look at a few.
Read one line out of the open file
>>> f.readline()
'# VR Configuration File\n'
I should note here that once it reads the line, the address in the file that python is sitting at is the beginning of the next line so if you do it again
>>> f.readline()
'# FileName : c:\\vr\\hostdir\\vr.cfg\n'
if for some reason it is necessary to go back to the beginning of the file we need to "seek" the new position. seek takes two arguments "offset" (how much to jump from the position passed in) and "whence" (where to start the offset). "Whence" defaults to the beginning of the file, so if you want to go the the beginning you can say "see" with an offset of 0 (don't move at all from the position passed in) and a whence of 0 (or if you pass nothing for whence this is the default) so
f.seek(0,0) # 0 offset, 0 whence
f.seek(0) # 0 offset, default whence which is 0
will both take python to the beginning of the file, so do that then readline() again and you should see the first line again. Let's read that first line and store it as a string we'll call "s"
>>> s=f.readline()
>>> print s
# VR Configuration File
It would of course be possible to do this with each line of the file, though there are better methods to be discussed later, but lets look a what we could do with that string now that we have it.
I can split it based on white spaces (the default for split) which returns a list of the words in the string.
>>> s.split()
['#', 'VR', 'Configuration', 'File']
For that matter we could split it based on any character like commas, or in this case the "i"
>>> s.split('i')
['# VR Conf', 'gurat', 'on F', 'le\n']
Note that splitting on white spaces takes off the newline character but splitting on a printable character doesn't. Of course there are plenty of ways to take the newline off, but just take note for now. How about if we needed to test for a sub string inside the string, well let's count occurrences.
>>> s.count('i')
3
>>> s.count('ig')
1
>>> s.count('z')
0
So a test would look something like
>>> if s.count('ig') : print 'yes'
...
yes
I didn't say if s.count equals something or is greater than something because I just wanted to know if it was non zero and anything non zero is true for the if test.

There is no sense typing out all the things you can do with strings because they are documented in the python docs, it is worth noting that there used to be a string module, but now each of these methods is a built-in which is why there was never an import statement above.

Let's go back to the split step and put the resulting list in a variable called "l", then we can loop over the list and do whatever we want, in this example just print each item.
>>> l=s.split()
>>> l
['#', 'VR', 'Configuration', 'File']
>>> for item in l:
... print item
...
#
VR
Configuration
File
or grab individual elements of the list.
>>> l[3]
'File'
or test to see if the list contains certain values.
>>> l.count('File')
1
>>> l.count('Nothing')
0
Like strings I'll leave the explanation of lists and their methods to the professionals, in this case the python tutorials.
But I'll leave with a couple of thoughts:
First, check out readlines() it will come in handy and will probably bear heavily in the next post.
Second, one of the real beauties of python is it's ability to get a lot done with a little code, this can be accomplished by taking whatever data a method returns and acting on it with the appropriate method. Consider the following lines typed into the interpreter.

open() is going to return a file object with all it's accompanying methods.
>>> open('/vr/hostdir/vr.cfg','r')
obscure text describing an open file object

let's just tack the readline() onto the end and let it return the first line which is a string.
>>> open('/vr/hostdir/vr.cfg','r').readline()
'# VR Configuration File\n'

It it is a string we can split it to get a list of words that were included.
>>> open('/vr/hostdir/vr.cfg','r').readline().split()
['#', 'VR', 'Configuration', 'File']

Of course the list is made up of elements that can be addressed lets get the second element whose index is 1.
>>> open('/vr/hostdir/vr.cfg','r').readline().split()[1]
'VR'

Which is a string and can be forced to lower case using the lower() method.
>>> open('/vr/hostdir/vr.cfg','r').readline().split()[1].lower()
'vr'

Is this stuff cool or what.


Thursday, May 10, 2007

Life outside Vr

I have had several folks ask about opening reading and parsing external files. There are so many applications that I don't even know where to begin. Whether plain ascii text files, or binary, python is loaded with tools for doing what you want or need. I think I'll just start rambling and see where we head. One thing that you will notice immediately is that there can be literally dozens of ways of accomplishing the same task. I'm not going to pretend that what I am putting forward is the best or most efficient, I'll just mention some tools that come in handy quite often. For today lets just open a file and grab some data from it. Let's pick a file that everyone should have, that being your Vr configuration file \vr\hostdir\vr.cfg. Don't worry, for now we'll just open it in read only mode. In your favorite python editor or pyedi within VrOne type and run the following.

f=open('\vr\hostdir\vr.cfg','r')

Ok, I'll admit that was a trick, it shouldn't have worked, even though everything about the command is totally correct, except that is for the backslashes "\". But wait you say, we are working in windows, and the backslash is what is always used to specify path hierarchy, and you would be correct. The problem is that the backslash is a special character in python (and several other languages, not to mention unix). Python uses a forward slash to separate path elements, or to use backslashes you have to tell python you are sending it a "raw" string and that it should not treat the backslashes as escape characters. I don't start here to be bothersome, but it is important to understand that python expects certain things and may send back something you aren't expecting if you aren't careful. Note the paste of a session where I open the file using 3 valid commands, look at what comes back, and realize that this is outside VrOne in the PythonWin editor.

First I open it with forward slashes because I grew up on unix, the forward slash is always in the same place no matter what keyboard you have, it is what python prefers, and I just like it better. I'll use "f" as the open file object and will open it, display it, and close it. The 'r' means to open it in read-only mode, we'll talk about modes later but I digress.

>>> f=open('/vr/hostdir/vr.cfg','r')
>>> f
Text describing file object which blogger deletes for some reason.
>>> f.close()

Next we'll use backslashes, but they have to be protected by another character so they aren't evaluated as an escape sequence, by coincidence the character that does this is a backslash so each occurence must be entered as
>>> f=open('\\vr\\hostdir\\vr.cfg','r')
>>> f
Text describing file object which blogger deletes for some reason.
>>> f.close()

Next we'll use the backslashes but we'll tell python that the name string is a raw string and that the backslashes shouldn't be evaluated as anything else. Just put an r in front of it and we're good to go.
>>> f=open(r'\vr\hostdir\vr.cfg','r')
>>> f
Text describing file object which blogger deletes for some reason.
>>> f.close()

It is worth noting that if you grab a filename inside Vr using either of the following, it returns forward slashes.
print PyVrGui().OpenFileNameDialog('title','c:/vr/hostdir','*.cfg')
C:/vr/hostdir/vr.cfg
print PyVrGui().OpenFileNameDialog('title','c:\\vr\\hostdir','*.cfg')
C:/vr/hostdir/vr.cfg

That will do for now. Yeah it's pretty slim, but I need to go and we'll actually read something next time. For now though it wouldn't hurt to get familiar with some of the python file methods, from the python docs. You can also get a little more information on the open function from the built-in functions docs (just scroll down to open()), this will come in handy when we move to appending, writing, and binary files.

Friday, April 20, 2007

Tag team programming

Not long ago I was corresponding with W. Chesson Godfrey aka Wild_Bill from the forum and he shared a little script with me. As so often happens when two people look at an issue from two different perspectives they will each solve the puzzle as it best suits their office culture. This is a little something I hadn't thought about before, which I changed to use some functions he wasn't using and shazam (is that a word) we have WsMan.py or workspace manager. Another reason why discussion is almost always a good thing. This stuff is just too cool.



PromBox=VrPromBox('Set Active Ws',60,1)
# The whole app revolves around a single convenient Prompt box, so create an object.
PromBox.AddMessage ("Don't change workspaces\n in the middle of a function")
# A little helpful text.
PromBox.AddCombo ('Workspaces',PyVrWs().GetWsCount(),0,0)
'''
We'll use a ComboBox as the user interface, so create it with the
number of items set to the number of workspaces. In this case I'm not
implicitly creating a workspace object. I just use the class to call
a method. Someday I'll test to see if this method of creating a
temporary object causes memory problems in things like large loops,
but for now I'm just illustrating that you don't necessarily need to
do the Ws=PyVrWs() thing.
'''
for WsNum in range(PyVrWs().GetWsCount()):
....PromBox.AddComboItem (PyVrWs().GetFileName (WsNum))
# For every workspace, get it's name and add it to the combo box.
if (PromBox.Display(0) == 0):
....PyVrWs().SetAws(PromBox.GetCombo(0))
# If the user didn't hit cancel, then set the active workspace
# to match the one selected.

Get involved in the forums, there is a wealth of experience to be shared.

For anyone interested in trying VrPython for the first time or if you are early in the game, I suggest going to the earliest posts and working forward. I use VrPython every day for many wonderful things, needless to say it will change and could potentially damage a file. Any risk associated with using VrPython or any code or scripts mentioned here lies solely with the end user.

The "Personal VrPython page" in the link section will contain many code examples and an organized table of contents to this blog in a fairly un-attractive (for now) form.