Friday, 1 March 2019

Python Scripting [3E]: Layer Fractional Segments for NZ Rail Maps 5

I made a lot of progress on this on Wednesday, mainly due to dropping nearly everything else and pushing on to finish it. After testing it, which so far has worked well, I decided to add an extra step to the workflow. The source layer (base layer) is a jpg file and we are making a jgw file for each of its segments. It also has a .xml file and a .jpg.aux.xml file with it, which we want to copy automatically.

So the extra code for this is
auxFileNameSource = rootPath + baseNameBase + ".jpg.aux.xml"
auxFileNameDest = rootPath + rootNameBase + ".jpg.aux.xml"
if os.path.isfile(auxFileNameSource):
    shutil.copyfile(auxFileNameSource,auxFileNameDest)
auxFileNameSource = rootPath + baseNameBase + ".xml"
auxFileNameDest = rootPath + rootNameBase + ".xml"
if os.path.isfile(auxFileNameSource):
    shutil.copyfile(auxFileNameSource,auxFileNameDest)

Again testing this has worked out. The script has now been tested and worked with all 10 segments that I had produced. One small change made near the top of the script is to reduce the amount of typing and have the script automatically add the .jgw extensions to the filenames that were typed in on the command line. So we are now only specifying layer names, not layer file names. This means changing another line further down in the script (now 82 lines) to eliminate the splitting of the extension off the input parameter. A few more lines now print out status messages, and comments have been added as well as whitespace. One thing I have done differently from my previous effort is to start camelCasing variable names. I don't know what convention the Python people recommend, but that's my personal preference, compared to underlines and other things that people sometimes use.

If I wanted to make the script even more useful I could have it work out the right and down layers automagically and it's possible I could do that but I would have to put in code to work out Linz's rules for naming sequences and that could be a lot more work so at the moment I will leave it here for the script and just put in those parameters but it is taking a bit of work to remember to put in -b and -d and -r before typing those layer names.

The other idea I am having is to be able to feed in a parameter list in a file and have it process all  source tiles at the same time, in a case where more than one or two source tiles are ready to be processed together. So if I modify my original script then I can have a different command line parameter and that will be -l or --layerlist that will read a list that is basically the command line parameters and the script runs through the whole file and then processes the entire list. It turns out that argparse can process a list of arguments that you pass to the parse_args function call, instead of the command line which it processes by default.

Here is the full script as it is for now. I expect that I will pursue the idea of adding an option to feed in an input file, and will look at it next time I need to use the script, so there may be an additional part added to this series then.

import argparse
import os
import shutil

#parse command line parameters
parser = argparse.ArgumentParser(prog='segments')
parser.add_argument('-b', '--base', required=True)
parser.add_argument('-r', '--right', required=True)
parser.add_argument('-d', '--down', required=True)
parser.add_argument('-c', '--counter', type=int, default=4)
parser.add_argument('-p', '--pixelsize', type=float, default=0.1)
args = parser.parse_args()

#save the parameters
baseName = args.base
rightName = args.right
downName = args.down
counter = args.counter
pixelSize = args.pixelsize

#Input file names
rootPath = "/home/patrick/Sources/Segments/"
baseFileName = rootPath + baseName + ".jgw"
rightFileName = rootPath + rightName + ".jgw"
downFileName = rootPath + downName + ".jgw"

#read input files
baseFile = open(baseFileName, "r")
baseData = baseFile.readlines()
baseFile.close()
rightFile = open(rightFileName, "r")
rightData = rightFile.readlines()
rightFile.close()
downFile = open(downFileName, "r")
downData = downFile.readlines()
downFile.close()

#save input file data
baseX = float(baseData[4])
baseY = float(baseData[5])
baseSkewX = float(baseData[1])
baseSkewY = float(baseData[2])
rightX = float(rightData[4])
rightY = float(rightData[5])
downX = float(downData[4])
downY = float(downData[5])

#Main calculation and processing section
#Initialisation
for colNum in range(counter):
    for rowNum in range(counter):
        segmentX = (((rightX-baseX)/counter)*colNum)+baseX
        segmentY = (((downY-baseY)/counter)*rowNum)+baseY
        gridX = "x" + str(counter) + "." + str(colNum + 1)
        gridY = "x" + str(counter) + "." + str(rowNum + 1)
       
        #Generate segment tile filename
        baseNameBase = baseName
        baseNameSplit = baseNameBase.split("-")
        baseColDescriptor = baseNameSplit[0]
        baseRowDescriptor = baseNameSplit[1]
        gridRowDescriptor = baseRowDescriptor + gridY
        gridColDescriptor = baseColDescriptor + gridX
        gridFileName = gridColDescriptor + "-" + gridRowDescriptor + ".jpg"
        # Next line for debugging output only
        #print(gridX + " " + gridY + " : " + str(segmentX) + " , " + str(segmentY))
       
        #Look for segment tiles that match current grid position
        rootFilesList = os.listdir(rootPath)
        for rootFile in rootFilesList:
            if rootFile.endswith(gridFileName):
               
                #Generate world file name
                rootNameBase = os.path.splitext(rootFile)[0]
                segmentName = rootNameBase + ".jgw"
                segmentFileName = rootPath + segmentName
                print(rootFile + " -> " + segmentName)
               
                # Write world file
                segmentFile = open(segmentFileName, "w+")
                segmentFile.write(str(pixelSize) + "\n")
                segmentFile.write(str(baseSkewX) + "\n")
                segmentFile.write(str(baseSkewY) + "\n")
                segmentFile.write("-" + str(pixelSize) + "\n")
                segmentFile.write(str(segmentX) + "\n")
                segmentFile.write(str(segmentY) + "\n")
                segmentFile.close()
               
                # find xml files for base layer and copy to segment
                auxNameSource = baseNameBase + ".jpg.aux.xml"
                auxFileNameSource = rootPath + auxNameSource
                auxNameDest = rootNameBase + ".jpg.aux.xml"
                auxFileNameDest = rootPath + auxNameDest
                if os.path.isfile(auxFileNameSource):
                    print("   " + auxNameSource + " -> " + auxNameDest)
                    shutil.copyfile(auxFileNameSource,auxFileNameDest)
                auxNameSource = baseNameBase + ".xml"
                auxFileNameSource = rootPath + auxNameSource
                auxNameDest = rootNameBase + ".xml"
                auxFileNameDest = rootPath + auxNameDest
                if os.path.isfile(auxFileNameSource):
                    print("   " + auxNameSource + " -> " + auxNameDest)
                    shutil.copyfile(auxFileNameSource,auxFileNameDest)