Jump to content

Replace objects (Loci) with symbols based on records


Recommended Posts

I am scripting this because I will be repeating it frequently.

I have a set of loci that I need to replace with symbols, a different symbol depending on a record value. 

My plan, which is a bit rough, was to have a loop for each of the 5 symbols which selects by the attribute 'canopyPNTSRec'.'RAND_COLOU' and then replaces with the appropriate symbol.

I have two problems:

The only procedure I can find to replace sybols "vs.SetHDef" doesn't apparently work on loci

The criteria for selection (('canopyPNTSRec'.'RAND_COLOU'=1)) needs to be re-formated for python but I can't get my head around how

 

newsym1 = 'Canopy-1'
def ReplaceRAND1s(h):
	vs.SetHDef(h, newsym1)
vs.ForEachObject(ReplaceRAND1s,("INSYMBOL & INVIEWPORT & ('canopyPNTSRec'.'RAND_COLOU'=1)") )

 

Edited by Sloader
Link to comment

Good morning, @Sloader.

   As you may already surmise, SetHDef() doesn't work with loci. Also, SetHDef() requires two handles, so if it did work you would need to type:

vs.SetHDef(h, vs.GetObject(newsym1))

 

That said, here is a simple way to achieve the same result:

def ReplaceRAND1s(h):
	X, Y = vs.GetLocPt(h)
	vs.Symbol(newsym1, X, Y, 0)
	vs.DelObject(h)

 

   If you have a lot of objects in your file, this may run a little faster, as it will only look at 2D Loci with the attached record:

vs.ForEachObject(ReplaceRAND1s,("INSYMBOL & INVIEWPORT & (T=Locus) & ('canopyPNTSRec'.'RAND_COLOU'=1)") )

 

HTH,

Raymond

 

PS - Your criteria statement is valid, does not need reformatting for Python.

Edited by MullinRJ
Python syntax
  • Like 1
Link to comment

Thanks for the help

 

I saw that placing a symbol at each of the loci would be easier but the next step is to scale the symbol based on a field value so I don't want to lose the records. 

 

The correct usage of vs.SetHDef(h, vs.GetObject(newsym1)) is useful to know as I could replace all loci with a symbol before I run the script. 

I have cleaned up the concept a little so I don't need 5 loops as I can pick the right symbol as part of the one function

 

Currently struggling with the scaling as the below doesn't scale the symbol in place and I expect its to do with coordinates

def ReplaceRAND1s(h):
	rand=vs.GetRField(h, 'canopyPNTSRec','RAND_COLOU')
	sym= vs.Concat('Canopy-'+ rand)
	vs.SetHDef(h, vs.GetObject(sym))
	ptXY = vs.GetSymLoc(h)
	PtX = ptXY[0]
	PtY = ptXY[1]
	vs.HScale2D(h, PtY, PtX, 1, 1, 0)
		
vs.ForEachObject(ReplaceRAND1s, "L='canopyPNTS'" )

 

 

Edited by Sloader
Link to comment

@Sloader,

   To scale a symbol there are 4 object variables (object attribute values set by an index, rather than by an explicit VecorScript call.)

 

From the Script Function Reference Appendix:

101 = Symbol Scale Type   (ObjectVariableInt)
            1 (None)    
            2 (Symmetric)
            3 (Asymmetric)

102 = Symbol X Scale Factor        (ObjectVariableReal)
103 = Symbol Y Scale Factor        (ObjectVariableReal)
104 = Symbol Z Scale Factor        (ObjectVariableReal)
 

Example:

First set ObjVar 101 to 2, or 3, then set ObjVars 102 and 103:

vs.SetObjectVariableInt(h, 101, 2)

vs.SetObjectVariableReal(h, 102, XscaleF)

 

or:

vs.SetObjectVariableInt(h, 101, 3)

vs.SetObjectVariableReal(h, 102, XscaleF)

vs.SetObjectVariableReal(h, 103, YscaleF)

If you set ObjVar 101 to 1, then the 3 scaling object variables are ignored and the effective scale factor is 1.0.

 

 

You can also attach the record to the symbol and copy all of the fields from the locus record to symbol record. Then you can delete the locus with no loss of data.

 

Raymond

 

  • Like 1
Link to comment

After hours of searching I did find SetObjectVariable and verified by looking in a python script exported by VW. 

The below assumes I have manually converted all loci to symbols so attaching records and transferring the values from loci to new symbols is the next bit to look at.

 

Cheers again

 

def ReplaceRAND1s(h):
	X, Y = vs.GetLocPt(h)
	rand=vs.GetRField(h, 'canopyPNTSRec','RAND_COLOU')
	sym= vs.Concat('Canopy-'+ rand)
	ScaleX=vs.GetRField(h, 'canopyPNTSRec','RAD')
	ScaleY=vs.GetRField(h, 'canopyPNTSRec','RAD')
	ScaleZ=vs.GetRField(h, 'canopyPNTSRec','rvalue_1')
	symh= vs.Symbol(sym, X, Y, 0)
	vs.SetObjectVariableInt(symh, 101, 3)
	vs.SetObjectVariableReal(symh, 102, ScaleX)
	vs.SetObjectVariableReal(symh, 103, ScaleY)
	vs.SetObjectVariableReal(symh, 104, ScaleZ)
	
		
vs.ForEachObject(ReplaceRAND1s, "L='canopyPNTS'" )

 

Edited by Sloader
Link to comment

The code supplied below doesn't work as if you have the vs.DelObject(h) line in it deletes the newly created symbols as well as the loci, which doesn't make sense to me. 

When the new symbol is inserted that object seems to take the reference of h. 

If I run a later line referencing h such as vs.SetRecord(h,'canopyPNTSRec') the function is applied to the new symbol which would be workable if I could set the field values based on my ScaleX, ScaleY and ScaleZ variables 

 

 

def ReplaceRAND1s(h):
	X, Y = vs.GetLocPt(h)
	vs.Symbol(newsym1, X, Y, 0)
	vs.DelObject(h)

 

 

Edited by Sloader
Link to comment

Try narrowing your criteria.  vs.ForEachObject() looks at EVERY object in the drawing unless you tell it not to. As you create symbols, you are adding them to the end of the list of searchable objects, so vs.ForEachObject() processes them right after looking at every object that was on your design layer when the script started. In some situations, this can result in an infinite loop. In one of my examples above I suggested adding (T=Locus) to your criteria. Try it. Doing so will tell vs.ForEachObject() to only look at Loci, and your new symbols will escape processing. Then you can add the vs.DelObject() line back.

 

Raymond

  • Like 1
Link to comment

Gotcha! thanks

The loop just deletes everything it created. This also explains why I was getting confusing results trying to attach a record to the new symbols. 

Now I am struggling with attaching the record to the new symbol and transferring data into the values

 

def ReplaceLOCI(h):
	ScaleX=vs.GetRField(h, 'canopyPNTSRec','RAD')
	ScaleY=vs.GetRField(h, 'canopyPNTSRec','RAD')
	ScaleZ=vs.GetRField(h, 'canopyPNTSRec','rvalue_1')
	X, Y = vs.GetLocPt(h)
	CreatedSymbol=vs.Symbol('Canopy-1', X, Y, 0)
	SymbolHandle= vs.GetObject(CreatedSymbol)
	vs.SetRecord(SymbolHandle,'canopyPNTSRec')
	vs.SetRField(SymbolHandle, 'canopyPNTSRec','RAD', ScaleZ)
	vs.DelObject(h)
vs.ForEachObject(ReplaceLOCI, "T=Locus" )

 

 

 

Edited by Sloader
Link to comment

I haven't tested your code, but one thing stands out. You place the symbol with CreatedSymbol=vs.Symbol('Canopy-1', X, Y, 0) but vs.Symbol does not return a handle as a result and I suspect CreatedSymbol is 0, or nil, after execution. Therefore your next line SymbolHandle= vs.GetObject(CreatedSymbol) is going to return a nil value since vs.GetObject(nil) is nil.

 

What you probably want is:

vs.Symbol('Canopy-1', X, Y, 0)    # place symbol

SymbolHandle = vs.LNewObj()  # get handle to new symbol instance

vs.SetRecord(SymbolHandle, 'canopyPNTSRec')   # attach record to symbol instance

 

Ain't programming fun?

 

HTH,

Raymond

Edited by MullinRJ
Link to comment

vs.LNewObj() is the Key - no idea how I would have found it, I see its filed under document list handling which now makes sense. 

 

16 hours ago, MullinRJ said:

Ain't programming fun?

 

Better than doing receptive tasks manually, but VW could be more accessible. I need to spend the time to figure out a proper environment with debugging etc but that looks painful.

I can't see a way to import a shapefile which is a shame.

 

 

Code below seems to work - Takes tree canopy points created by a GIS script from LiDAR 

 

 

def ReplaceRAND1s(h):#Function to replace all loci from imported shapefile with scaled canopy symbols
	rand=vs.GetRField(h, 'TreePointsForSymbolsRec','RAND_COLOU')#Get the random colour for the canopy loci
	sym= vs.Concat('Canopy-'+ rand)#Convert the colour code to canopy symbol name
	ScaleX=vs.GetRField(h, 'TreePointsForSymbolsRec','RAD')#Get scale from loci record 
	ScaleY=vs.GetRField(h, 'TreePointsForSymbolsRec','RAD')#Get scale from loci record 
	ScaleZ=vs.GetRField(h, 'TreePointsForSymbolsRec','rvalue_1')#Get height from loci record 
	X, Y = vs.GetLocPt(h)#Get location from loci to place symbol
	randrot= vs.Random()*100 #create a random number to rotate the symbol by 	
	vs.Symbol(sym, X, Y, randrot)    # place symbol based on loci postion, random colour and random rotation
	h2 = vs.LNewObj()  # get handle to new symbol instance
	vs.SetObjectVariableInt(h2, 101, 3) #Set symbol scaling to asymetric
	vs.SetObjectVariableReal(h2, 102, ScaleX)#Set X scale
	vs.SetObjectVariableReal(h2, 103, ScaleY)#Set Y scale
	vs.SetObjectVariableReal(h2, 104, ScaleZ)#Set Z scale/ height
	vs.DelObject(h)#clean up loci
		
vs.ForEachObject(ReplaceRAND1s, "T=LOCUS" ) #loop for all the loci in the file

 

Trees.jpg

Edited by Sloader
  • Like 1
Link to comment

Nice result. Yeah, it takes some time to figure out where some of the "missing" pieces are in VectorScript. Spend some time looking at coding examples posted in these Fora. And when using the Script Reference, check both the HTML and the online Developer site. Sometimes they differ, mostly in the comments added to the online pages. As a quick resort, post questions here. It's a large brain trust.

 

7 hours ago, Sloader said:

I can't see a way to import a shapefile which is a shame.

Do you mean manually or programmatically? The former can be done under menu File>Import>Import Shapefile..., but the latter is currently unsupported (best I can tell). I have imported one or two Shapefiles years ago, for grins, and they ended up being huge in polygon counts. It is possible VW will choke on larger ones, but it should be able to be done. If you need to do it programmatically, then your best bet is to add a post to the Feedback>Wishlist section of this Forum.

 

All the best,

Raymond

Link to comment
17 hours ago, MullinRJ said:

Do you mean manually or programmatically?

Yes, I import shapefiles frequently - the importer is flakey but does mean you have records to to use for things like scaling tree symbols or extruding buildings which you don't get with DWG/DXF. vs.ImportDXFDWGFile is a pain as you can't (as far as I know) set the options like units and prefixes from the script. I'll look at adding some feedback. 

 

Doing some digging it looks like I might be able to use vs.SetLastDXFImportOpt(selector, value) to sort out my DXF imports

Edited by Sloader
Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...