Tuesday, October 23, 2012

Where in the world... (and by that I mean geographically, not disappearing for 3 years)

Honestly,  first post in 3 years?  Well yes in fact it is, not much to say and probably nobody that really cares so it is what it is.  Been busy coding and there are some truly awesome libraries out there.  Lots of work extracting information out of Vr and dropping it directly into XLS spreadsheets using xlwt and of course the switch over to 2.7 (and 64 bit) has provided plenty of interesting experience. However, what is so important that I'm actually going to sit and type for a few minutes? It is pyproj of course.  Up to now I had done all my re-projection using the corpscon dll libraries which to date I can not find any way of porting to 2.7 64bit.  In the mean time I have learned a lot more about great open libraries like PROJ.4 but just never had the time to investigate.  Necessity being the mother of invention (or in this case studying) I needed a replacement for CorpsCon and the answer was pyproj.  A snippet is worth a million words so here goes.


After downloading and installing pyproj I start by importing the library
import pyproj

Next we create a Projection object and initialize it with a particular projection in this case Ohio State Plane North 1938 EPSG code 2834 
Projection=pyproj.Proj(init='EPSG:2834')

If I have an X,Y coordinate I can just allow the Projection object to compute the Long, Lat.  Notice that the default must be to go from geodetic to X,Y so the "inverse=True" is there and also that it is expecting (and returns) meters so we need to convert.  Both these statements may be a little off but I've only worked with the library for about 30 minutes so far and haven't actually read the docs.
Projection(1600000/3.280833333,700000/3.280833333,inverse=True)
(-83.84694414368035, 41.58018579703697)


So what we get back is a list with X and Y.  If I had passed in a Long and Lat position it would again return a list of coordinates in meters so lets create a quick function to take the coordinate list and return feet.
def to_ft((x,y)):
    return x*3.280833333333333,y*3.280833333333333


Then simply do the projection without the inverse.   
to_ft(Projection(-83.0,41.0))
(1830492.2997760142, 486159.352289076)


In the docs there are easy ways to create two different Projection objects and go directly from one to the other, but I pretty much just needed to go from state plane to geodetic as the basis for creating KML output. The change in libraries dropped about 75 lines of code, you gotta love python libraries.

Speaking of libraries, I found a site that hosts every python library I could think of (and about a hundred I couldn't) as a binary Windows installer in everything from Python 2.5 32bit to Python 3.2 64bit.  Find them at http://www.lfd.uci.edu/~gohlke/pythonlibs/

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.

Tuesday, August 07, 2007

Time out

It is that time of year when I begin to pursue my second job as middle school soccer coach, and high school referee (and parent spectator). Because of this, I'm not even going to think about posting until late October.

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.