from __future__ import division, print_function
import numpy as N
from ..geom.util import dot
from ..error.ellipse import ellipse
from hashlib import sha1
[docs]def hash_array(a):
return sha1(a.data.tobytes()).hexdigest()[:8]
[docs]def rotation(angle):
"""Rotation about the Z axis (in the XY plane)"""
return N.array([[N.cos(angle),-N.sin(angle),0],
[N.sin(angle), N.cos(angle),0],
[0 , 0 ,1]])
[docs]def ellipse(center,covariance_matrix,level=1, n=1000):
"""Returns error ellipse in slope-azimuth space"""
# singular value decomposition
U, s, rotation_matrix = N.linalg.svd(covariance_matrix)
# semi-axes (largest first)
saxes = N.sqrt(s)*level ## If the _area_ of a 2s ellipse is twice that of a 1s ellipse
# If the _axes_ are supposed to be twice as long, then it should be N.sqrt(s)*width
u = N.linspace(0, 2*N.pi, n)
data = N.column_stack((saxes[0]*N.cos(u), saxes[1]*N.sin(u)))
# rotate data
return N.dot(data, rotation_matrix)+ center
[docs]class BaseOrientation(object):
"""
Abstract class to define common methods used by
other orientation classes (e.g. reconstructed
orientations, pca orientations)
"""
disabled = False
[docs] def dip_direction(self, uncertainties=False):
s,d = self.strike_dip()
s+=90
if uncertainties:
return (s,d),self.errors()
return (s,d)
[docs] def gradient(self, uncertainties=False):
return self.azimuth,self.slope
[docs] def errors(self):
return tuple(N.degrees(i) for i in self.standard_errors()[:2])
@property
def rotated_covariance(self):
r = rotation(self.azimuth)
return dot(r.T,self.covariance_matrix,r)
[docs] def standard_errors(self):
return N.sqrt(N.diagonal(self.rotated_covariance))
def _ellipse(self,level):
return ellipse(
tuple(self.coefficients[:2]),
self.covariance_matrix[:2,:2],
level=level)
[docs] def error_ellipse(self, spherical=True, vector=False, level=1):
e = self._ellipse(level)
if spherical:
slope = N.arctan(-e[:,0])
azimuth = self.azimuth + N.arctan2(-e[:,1],-e[:,0])
if vector:
azimuth = azimuth + N.pi/2
return (N.pi+azimuth,N.pi/2-slope)
return (e[:,1],e[:,0])
@property
def principal_axes(self):
return self.singular_values*self.axes
@property
def hash(self):
return hash_array(self.principal_axes)
@property
def center(self):
return None
@property
def centered_array(self):
return None
[docs] def to_mapping(self,**values):
"""
Create a JSON-serializable representation of the plane that is usable with the
javascript frontend
"""
strike, dip, rake = self.strike_dip_rake()
min, max = self.angular_errors()
try:
disabled = self.disabled
except AttributeError:
disabled = False
mapping = dict(
uid=self.hash,
axes=self.axes.tolist(),
hyperbolic_axes=self.hyperbolic_axes.tolist(),
max_angular_error=max,
min_angular_error=min,
strike=strike,
dip=dip,
rake=rake,
disabled=disabled)
# Add in user-provided-values, overwriting if
# necessary
for k,v in values.items():
mapping[k] = v
return mapping