Friday, 17 April 2015

lidar - Sending arguments to LAStools function from Python script not working


I`m trying to call the lasground module from a python script, using this method:


args=['lasground', '-i *.las', output_file, arg_step, arg_spike, arg_spikedown, arg_offset, arg_bulge, arg_cores]
proc=subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
output,error=proc.communicate()

The different variables starting with arg_ are strings representing arguments that should be passed to lasground (for example if I want the step size to be 10, arg_step would be equal to '-step 10' and so on. The problem is that instead of working correctly, lasground throws back the following error:




ERROR: cannot understand argument '-i *.las'



I suppose this has to do with the '' around the string, but every other executable file I have called from python in this way worked as expected. Is there another way to do this so as to send the arguments to lasground in an understandable format from python?


I tried setting shell=False but nothing changed so I am out of ideas.


I am using python 3.5., but I am open to solutions applicable to python 2.x.



Answer



I've had the same problem with the subprocess module, attempting to multi-thread a process, and I've found that breaking up the arguments into individual components where spaces exit seems to work... contrary to what I've read; this may not be necessary on Linux or other platforms but on Windows it seems to be the way to go. By changing


args=['lasground', '-i *.las', output_file, ....

to



args=['lasground', '-i','*.las', output_file, ....

presents the arguments to the subprocess in a more digestible format; this seems true for any instance where a space appears in an argument. Another way that this can be done is by writing a temporary batch file and calling it with no arguments:


# give a batch file a random name eg: TempBat93314028.bat to avoid overwriting existing files
BatFile = os.path.join(os.environ.get("TEMP"),"TempBat{0}.bat".format(random.randint(0,sys.maxint)))

# just to be sure the name is clear.. be careful of race conditions! just because it doesn't exist
# -now- doesn't mean it won't be created by a different process -before- the next line!
# for this reason I would use a unique identifier for each process you intend to run, be creative!
# you're not limited to 8.3 filenames and can use spaces and underscores.

while os.path.exists(BatFile):
BatFile = os.path.join(os.environ.get("TEMP"),"TempBat{0}.bat".format(random.randint(0,sys.maxint)))

with open(BatFile,'w') as BatWrite:
# write the CMD on a single line, but this can get confusing
BatWrite.write("lasground -i {0}\\*.las {1} {2} {3} {4} {5} {6} {7}\n".format(PathToFiles, output_file, arg_step,
arg_spike, arg_spikedown, arg_offset,
arg_bulge, arg_cores))
# or bit by bit, a little more tedious but easier to modify
BatWrite.write("lasground ")

BatWrite.write("-i {0}\\*.las ".format(PathToFiles))
BatWrite.write("{0} ".format(output_file))
BatWrite.write("{0} ".format(arg_step))
BatWrite.write("{0} ".format(arg_spike))
BatWrite.write("{0} ".format(arg_spikedown))
BatWrite.write("{0} ".format(arg_offset))
BatWrite.write("{0} ".format(arg_bulge))
BatWrite.write("{0} ".format(arg_cores))
BatWrite.write("\n ") # end of line... or os.linesep to make this platform independent


proc=subprocess.Popen(BatFile,stdout=subprocess.PIPE,stderr=subprocess.PIPE,shell=True)
output,error=proc.communicate()

# then later
os.remove(BatFile) # it's always good to clean up...

then if the process fails you've got the command as text you can run on a new instance to help with debugging.


No comments:

Post a Comment

arcpy - Changing output name when exporting data driven pages to JPG?

Is there a way to save the output JPG, changing the output file name to the page name, instead of page number? I mean changing the script fo...