This is what I am thinking... it currently is a demonstration so if Xander, or anyone else wants to make it work with your shapefiles, then the principles might be useful.
For the demo, all it does is take a line, takes the first and last points and ignores the rest(ish). performs a translation to the origin, a rotation by a certain angle, and scales the x axis by a factor of 2x. The only real input parameters needed are provided in the documentation and you can see alternates after the if __name__ section of the script.
I can't get back to this for a day or 2, but this may keep you or others busy until you get your rotation data. bear in mind, this can be greatly expanded, but I wanted to demonstrate what an affine transformation was. I was as verbose as possible and didn't bury the code in one-liners since I teach and I hate unwrapping those for students so they can understand.
So have fun and give me some feedback via here or if it is more verbose...email etc.
Dan
'''
affine_line.py
Author: Dan.Patterson@carleton.ca
Purpose: a one-off script to rotate a line about its center
... aka ... a 2 point polyline
Requires:
import numpy as np either in the script or in 'rotate'
Namespace:
line 2 point line
angle rotation angle in degrees
x=2,y=1 scaling in the x and/or y directions
XY_c, XY_t, XY_r, XY_s
namespace representing center, translated, rotated, scaled pnts
'''
import numpy as np
np.set_printoptions(precision=2,threshold=20,edgeitems=3,suppress=True,linewidth=60) # set default print options
def array_check(pnts):
'''check to see if the pnts are expressed as an array'''
bool = type(line).__module__ == np.__name__
if not bool: #not an array
pnts = np.array(pnts,dtype='float64')
return pnts
def angle(pnts):
'''angle between first and last points in array format'''
pnts = array_check(pnts)
dx,dy = pnts[0] - pnts[-1]
deg = np.rad2deg(np.arctan2(dx,dy))
return deg
def center(pnts):
'''average x, y coordinate for points'''
pnts = array_check(pnts)
XY_c = np.mean(pnts,axis=0)
return XY_c
def distance(pnts):
'''returns the length/distance between the first and last points'''
pnts = array_check(pnts)
dx,dy = pnts[0] - pnts[-1]
return np.hypot(dx,dy)
def translate(pnts, XY_c=None):
'''translate a point(s)'''
if XY_c == None:
XY_c = center(pnts)
XY_t = pnts - XY_c
return XY_t
def rotate(pnts,angle):
'''rotate points about the origin, given an angle in degrees,
(-ve counterclockwise, +ve for clockwise'''
angle = np.deg2rad(angle)
s = np.sin(angle)
c = np.cos(angle)
aff_matrix = np.array([[c, -s],[s, c]])
XY_r = np.dot(pnts, aff_matrix)
return XY_r
def scale(pnts, x=1, y=1):
'''returns the points scaled by x and y for the x and y direction'''
XY_s = np.multiply(pnts, [x,y])
return XY_s
if __name__ == '__main__':
'''read the header...a verbose demonstration follows'''
line = [[0.0,0.0],[1.0,1.0]] # line as a sequence of points
angle = -45.0 # angle to rotate in degrees
x = 2.0; y = 1 # scaling in x and y directions
XY_c = center(line) # center(line)
XY_t = translate(line, XY_c) # translate(line, center(line))
XY_r = rotate(XY_t,angle) # rotate(translate(line, center(line)), angle)
XY_s = scale(XY_r,x=2, y=1) # scale(rotate(translate(line, center(line)), angle), x=2, y=1)
print '\n','-'*60
print __doc__
print('Input line (pnts):\n {}'.format(line))
print('Center point:\n {}'.format(XY_c))
print('Translate to center:\n {}'.format(XY_t))
print('Rotate about center:\n {}'.format(XY_r))
print('Scaled with x=2,y=1:\n {}'.format(XY_s))
print('Rotate back:\n {}'.format( rotate(XY_s,-angle) ))
print('Translate back:\n {}'.format( translate(rotate(XY_s,-angle),-XY_c) ))
print '\n','-'*60