Thursday, 28 February 2019

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

Last time we had a look at how to read data from our files and store it in a list. This time we are going to get that data and perform the calculations we need on it.

First thing is to convert the numbers read out as strings to floats. The numbers we need are in lines 5 and 6 in each of the three files.

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

Note we also have two more numbers from the base file only: baseSkewX and baseSkewY which we need to write out to every .jgw file that we generate. It is assumed in this script that we don't need to do any calculations with these.

Calculations needed are to generate, in this case, a total of 16 pairs of values (the original tile which is now 16 segments in a 4x4 grid, the actual number of rows and columns being passed in as the counter parameter on the command line). The grid then has 4 rows and 4 columns. The columns are numbered from 0 to 3 and the rows are also numbered from 0 to 3. For each segment, the X coordinate of the top-left corner is calculated by this formula, iterating through values of colNum from 0 to 3:

segmentX = ((rightX-baseX)/counter)*colNum)+baseX

Breaking that down:
rightX-baseX gives us the width of the base tile
divide by counter to give us the width of one segment
multiply by the column number to get the offset for a particular column
add to baseX so that we have the absolute coordinate, that is base plus offset.

Likewise segmentY is calculated in a similar way from rowNum in 0-3:

segmentY = ((downY-baseY)/counter)*rowNum)+baseY

So a double loop is employed to calculate 16 pairs of values:

for colNum in range(4);
       for rowNum in range(4):
             segmentX = (((rightX - baseX) / counter) * colNum) + baseX
             segmentY = (((downY - baseY) / counter) * rowNum) + baseY
             gridX = "x4." + str(colNum + 1)
             gridY = "x4." + str(rowNum + 1)
             print(gridX + " " + gridY + ":" + str(segmentX) + "," + str(segmentY))

That prints out 16 lines of data. gridX and gridY are interesting as they are the segment descriptors which are added to the end of the row and column descriptors of the original file name (basename in the script). For example 92XJ7-92MKF becomes 16 segments whose names run in the sequence from 92XJ7x4.1-92MKFx4.1 through to 92XJ7x4.4-92MKFx4.4

The next step is to write out the  .jgw files. Here we have a choice of just writing 16 files with the 16 pairs of values in them, which would be really easy to do here, or with a few more lines of code we can look for just the files we need to find by creating the filename pattern to search for and checking to see if that file exists. In other words, the user has put the segment tile (jpg file) into the segments directory for us to look up, and we shall write out a corresponding jgw file.

The actual spec of the file's contents would be as follows (using our existing variable names):
  • Line 1: pixelSize
  • Line 2: baseSkewX
  • Line 3: baseSkewY
  • Line 4: -pixelSize (in other words the pixelSize string with a - in front of it)
  • Line 5: segmentX
  • Line 6: segmentY  

The steps needed (in English rather than Python):
  • strip the extension off the base file (92XJ7-92MKF.jgw) so that we get 92XJ7-92MKF
  • split the filename at the hyphen so we get two pieces, baseColDescriptor and baseRowDescriptor respectively being 92XJ7 and 92MKF in this example.
  • set gridRowDescriptor to be a concatenation of baseRowDescriptor and gridY e.g. 92XJ7x4.1
  • set gridColDescriptor to be a concatenation of baseColDescriptor and gridX e.g. 92MKFx4.3
  • set gridFilename to be gridColDescriptor + "-" + gridRowDescriptor + ".jpg"
  • search for a filename that ends with gridFileName (it will start with something else like K1977full-)
  • If the file name exists then change the full name into ".jgw" extension and write the 6 lines mentioned just above.
  • There may be more than one filename that ends with gridFileName so we need to go through all the filenames in the directory.
The code needed to do that will appear in the next part in this series.

It would be fair to say this script is rather more complex than the first one I did (the files copying one) but the benefits of it are great because of the number of manual steps and potential errors eliminated. But it definitely has taxed my brain at times. I guess just as if we don't handwrite these days because of computers we lose the skills, if we don't do much manual calculations with our brains we lose that as well.