def cos_distance(self, strike1, dip1, strike2, dip2): """Angular distance betwen the poles of two planes.""" xyz1 = sph2cart(*mplstereonet.pole(strike1, dip1)) xyz2 = sph2cart(*mplstereonet.pole(strike2, dip2)) r1, r2 = np.linalg.norm(xyz1), np.linalg.norm(xyz2) dot = np.dot(np.squeeze(xyz1), np.squeeze(xyz2)) / r1 / r2 return np.abs(np.degrees(np.arccos(dot)))
def rotate_points(lon, lat, resultant_orien): """ rotate the resultant vector to the north dirction (y-axis), and adjust all the pole according to the resultant vector :param lon: array original longitude :param lat:array original latitude :param result_v_lon: float lon of result vector :param result_v_lat: float lat of resultant vector :return: lon_adj: array adjusted lon lat_adj: adjusted lat """ result_v_lon, result_v_lat = mplstereonet.pole(resultant_orien[0], resultant_orien[1]) lon_adj, lat_adj = mplstereonet.stereonet_math._rotate(lon * 180 / np.pi, lat * 180 / np.pi, -result_v_lon * 180 / np.pi, axis='z') lon_adj, lat_adj = mplstereonet.stereonet_math._rotate( lon_adj * 180 / np.pi, lat_adj * 180 / np.pi, 90 - result_v_lat * 180 / np.pi, axis='y') return lon_adj, lat_adj
def tangent_lineation_plot(ax, strikes, dips, rakes): """Makes a tangent lineation plot for normal faults with the given strikes, dips, and rakes.""" # Calculate the position of the rake of the lineations, but don't plot yet rake_x, rake_y = mplstereonet.rake(strikes, dips, rakes) # Calculate the direction the arrows should point # These are all normal faults, so the arrows point away from the center # Because we're plotting at the pole location, however, we need to flip this # from what we plotted with the "ball of string" plot. mag = np.hypot(rake_x, rake_y) u, v = -rake_x / mag, -rake_y / mag # Calculate the position of the poles pole_x, pole_y = mplstereonet.pole(strikes, dips) # Plot the arrows centered on the pole locations... arrows = ax.quiver(pole_x, pole_y, u, v, width=1, headwidth=4, units='dots', pivot='middle') return arrows
def compare_strikedip(self, strike1, dip1, strike2, dip2): """Avoids ambiguities in strike/dip convention when dip is 0 or 90.""" convert = lambda a, b: sph2cart(*mplstereonet.pole(a, b)) x1, y1, z1 = convert(strike1, dip1) x2, y2, z2 = convert(strike2, dip2) rtol, atol = 1e-7, 1e-7 try: assert np.allclose([x1, y1, z1], [x2, y2, z2], rtol, atol) except AssertionError: # Antipode is also acceptable in this case.... assert np.allclose([x1, y1, z1], [-x2, -y2, -z2], rtol, atol)
def test_fit_pole_noisy(self): np.random.seed(1) for strike in range(0, 370, 10): for dip in range(0, 100, 10): lon, lat = mplstereonet.pole(strike, dip) lon = lon + np.radians(np.random.normal(0, 1, 100)) lat = lat + np.radians(np.random.normal(0, 1, 100)) s_noisy, d_noisy = mplstereonet.geographic2pole(lon, lat) s, d = mplstereonet.fit_pole(s_noisy, d_noisy) ang_dist = self.cos_distance(strike, dip, s, d) assert ang_dist < 2 or (180 - ang_dist) < 2
def main(): # Display the data with a polar grid ax1 = basic() ax1.grid(kind='polar') ax1.set_title('Polar overlay on a Stereonet', y=1.1) # Display the data with a grid centered on the pole to the plotted plane. ax2 = basic() ax2.grid(center=mplstereonet.pole(315, 30)) ax2.set_title('Arbitrary overlay on a Stereonet', y=1.1) plt.show()
def fitfold(self): strike, dip = self.get_strike_dip() # discards the old graph self.ax.hold(True) fit_strike,fit_dip = mplstereonet.fit_girdle(strike,dip) lon, lat = mplstereonet.pole(fit_strike, fit_dip) (plunge,), (bearing,) = mplstereonet.pole2plunge_bearing(fit_strike, fit_dip) template = u'Plunge / Direction of Fold Axis\n{:02.0f}\u00b0/{:03.0f}\u00b0' self.ax.annotate(template.format(plunge, bearing), ha='center', va='bottom', xy=(lon, lat), xytext=(-50, 20), textcoords='offset points', arrowprops=dict(arrowstyle='-|>', facecolor='black')) self.ax.plane(fit_strike, fit_dip, color='red', lw=2) self.ax.pole(fit_strike, fit_dip, marker='o', color='red', markersize=14) self.canvas.draw()
def fisher_pdf(orientation, kappa, resultant_orien): """ calculate the pdf of orientation :param orientation: -arraylike (strike, dip) in degree :param kappa: -fisher constant :param resultant_orien: resultant orientation :return: float arraylike """ lon, lat = mplstereonet.pole(orientation[0], orientation[1]) lon_adj, lat_adj = rotate_points(lon, lat, resultant_orien) lat_adj = lat_adj * np.pi / 180 f = kappa * np.sin(lat_adj) * np.exp( kappa * np.cos(lat_adj)) / (2 * np.pi * (np.exp(kappa) - 1)) return f
def fitfold(self): strike = [] dip = [] for f in self.layer.selectedFeatures(): dip.append(f['dip']) #self.dip_combo.currentText()]) strike.append(f['strike'])#self.strike_combo.currentText()]) # discards the old graph self.ax.hold(True) fit_strike,fit_dip = mplstereonet.fit_girdle(strike,dip) lon, lat = mplstereonet.pole(fit_strike, fit_dip) (plunge,), (bearing,) = mplstereonet.pole2plunge_bearing(fit_strike, fit_dip) template = u'P/B of Fold Axis\n{:02.0f}\u00b0/{:03.0f}\u00b0' self.ax.annotate(template.format(plunge, bearing), ha='center', va='bottom', xy=(lon, lat), xytext=(-50, 20), textcoords='offset points', arrowprops=dict(arrowstyle='-|>', facecolor='black')) print fit_strike, fit_dip self.ax.plane(fit_strike, fit_dip, color='red', lw=2) self.ax.pole(fit_strike, fit_dip, marker='o', color='red', markersize=14) self.canvas.draw()
def rotate_points_back(lon, lat, resultant_orien): """ rotate the simulated vectors back to restore them :param lon: :param lat: :param mean_vector_lon: :param mean_vector_lat: :return: """ result_v_lon, result_v_lat = mplstereonet.pole(resultant_orien[0], resultant_orien[1]) lon_adj, lat_adj = mplstereonet.stereonet_math._rotate(lon * 180 / np.pi, lat * 180 / np.pi, result_v_lon * 180 / np.pi, axis='z') lon_adj, lat_adj = mplstereonet.stereonet_math._rotate( lon_adj * 180 / np.pi, lat_adj * 180 / np.pi, result_v_lat * 180 / np.pi - 90, axis='y') return lon_adj, lat_adj
cmap='gist_earth', sigma=1.5) ax.rake(strike, dip, rake, marker='.', color='black') # Find the two modes centers = mplstereonet.kmeans(strike, dip, rake, num=2, measurement='rakes') strike_cent, dip_cent = mplstereonet.geographic2pole(*zip(*centers)) ax.pole(strike_cent, dip_cent, 'ro', ms=12) # Fit a girdle to the two modes # The pole of this plane will be the plunge of the fold axis axis_s, axis_d = mplstereonet.fit_girdle(*zip(*centers), measurement='radians') ax.plane(axis_s, axis_d, color='green') ax.pole(axis_s, axis_d, color='green', marker='o', ms=15) # Now we'll find the midpoint. We could project the centers as rakes on the # plane we just fit, but it's easier to get their mean vector instead. mid, _ = mplstereonet.find_mean_vector(*zip(*centers), measurement='radians') midx, midy = mplstereonet.line(*mid) # Now let's find the axial plane by fitting another girdle to the midpoint # and the pole of the plunge axis. xp, yp = mplstereonet.pole(axis_s, axis_d) x, y = [xp, midx], [yp, midy] axial_s, axial_dip = mplstereonet.fit_girdle(x, y, measurement='radians') ax.plane(axial_s, axial_dip, color='lightblue', lw=3) plt.show()
""" Example of how `ax.scatter` can be used to plot linear data on a stereonet varying color and/or size by other variables. This also serves as a general example of how to convert orientation data into the coordinate system that the stereonet plot uses so that generic matplotlib plotting methods may be used. """ import numpy as np import matplotlib.pyplot as plt import mplstereonet np.random.seed(1) strikes = np.arange(0, 360, 15) dips = 45 * np.ones(strikes.size) magnitude = np.random.random(strikes.size) # Convert our strikes and dips to stereonet coordinates lons, lats = mplstereonet.pole(strikes, dips) # Now we'll plot our data and color by magnitude fig, ax = mplstereonet.subplots() sm = ax.scatter(lons, lats, c=magnitude, s=50, cmap='gist_earth') ax.grid() plt.show()
fig, ax = mplstereonet.subplots() #ax.density_contourf(strike, dip, rake, measurement='rakes', cmap='gist_earth', # sigma=1.5) ax.density_contour(strike, dip, rake, measurement='rakes', cmap='gist_earth', sigma=1.5) ax.rake(strike, dip, rake, marker='.', color='black') # Find the two modes centers = mplstereonet.kmeans(strike, dip, rake, num=2, measurement='rakes') strike_cent, dip_cent = mplstereonet.geographic2pole(*zip(*centers)) ax.pole(strike_cent, dip_cent, 'ro', ms=12) # Label the modes for (x0, y0) in centers: s, d = mplstereonet.geographic2pole(x0, y0) x, y = mplstereonet.pole(s, d) # Otherwise, we may get the antipode... if x > 0: kwargs = dict(xytext=(40, -40), ha='left') else: kwargs = dict(xytext=(-40, 40), ha='right') ax.annotate('{:03.0f}/{:03.0f}'.format(s[0], d[0]), xy=(x, y), xycoords='data', textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='angle3'), **kwargs) ax.set_title('Strike/dip of conjugate fault sets', y=1.07) plt.show()
# scatter_3d(fig, p_x, p_y, p_z) # # project rotated vectors on the sphere # p_x, p_y, p_z = mplstereonet.stereonet2xyz(lon_adj, lat_adj) # scatter_3d(fig, p_x, p_y, p_z) # simulate 100 samples, and project original vectors and simulated vectors on a stereonet sampling_strike, sampling_dip = fisher_rvs(kappa, 100, resultant_orien) fig = plt.figure() ax = fig.add_subplot(111, projection='stereonet') ax.pole(strike, dip, 'g^', markersize=6) ax.pole(sampling_strike, sampling_dip, 'r^', markersize=6) ax.grid() plt.show() # simulate 100 samples, and project both simulated vectors and original vectors on a sphere sampling_lon, sampling_lat = fisher_rvs(kappa, 100, resultant_orien, form='geographic') # calculate the lon and lat of original vectors lon, lat = mplstereonet.pole(strike, dip) fig = plt.figure() fig = Axes3D(fig) p_x, p_y, p_z = mplstereonet.stereonet2xyz(sampling_lon, sampling_lat) scatter_3d(fig, p_x, p_y, p_z) p_x, p_y, p_z = mplstereonet.stereonet2xyz(lon, lat) scatter_3d(fig, p_x, p_y, p_z) # calculate the pdf of (strike, dip) pdf = fisher_pdf((strike, dip), kappa, resultant_orien)
s, d = mplstereonet.plunge_bearing2pole(real_plunge, real_bearing) lon, lat = mplstereonet.plane(s, d, segments=num_points) lon += np.random.normal(0, np.radians(15), lon.shape) lat += np.random.normal(0, np.radians(15), lat.shape) strike, dip = mplstereonet.geographic2pole(lon, lat) # Plot the raw data and contour it: fig, ax = mplstereonet.subplots() ax.density_contourf(strike, dip, cmap='gist_earth') ax.density_contour(strike, dip, colors='black') ax.pole(strike, dip, marker='.', color='black') # Fit a plane to the girdle of the distribution and display it. fit_strike, fit_dip = mplstereonet.fit_girdle(strike, dip) ax.plane(fit_strike, fit_dip, color='red', lw=2) ax.pole(fit_strike, fit_dip, marker='o', color='red', markersize=14) # Add some annotation of the result lon, lat = mplstereonet.pole(fit_strike, fit_dip) (plunge, ), (bearing, ) = mplstereonet.pole2plunge_bearing(fit_strike, fit_dip) template = u'P/B of Fold Axis\n{:02.0f}\u00b0/{:03.0f}\u00b0' ax.annotate(template.format(plunge, bearing), ha='center', va='bottom', xy=(lon, lat), xytext=(-50, 20), textcoords='offset points', arrowprops=dict(arrowstyle='-|>', facecolor='black')) plt.show()
dip, rake, measurement='rakes', cmap='gist_earth', sigma=1.5) ax.rake(strike, dip, rake, marker='.', color='black') # Find the two modes centers = mplstereonet.kmeans(strike, dip, rake, num=2, measurement='rakes') strike_cent, dip_cent = mplstereonet.geographic2pole(*zip(*centers)) ax.pole(strike_cent, dip_cent, 'ro', ms=12) # Label the modes for (x0, y0) in centers: s, d = mplstereonet.geographic2pole(x0, y0) x, y = mplstereonet.pole(s, d) # Otherwise, we may get the antipode... if x > 0: kwargs = dict(xytext=(40, -40), ha='left') else: kwargs = dict(xytext=(-40, 40), ha='right') ax.annotate('{:03.0f}/{:03.0f}'.format(s[0], d[0]), xy=(x, y), xycoords='data', textcoords='offset points', arrowprops=dict(arrowstyle='->', connectionstyle='angle3'), **kwargs) ax.set_title('Strike/dip of conjugate fault sets', y=1.07)
ax.density_contour(strike, dip, rake, measurement='rakes', cmap='gist_earth', sigma=1.5) ax.rake(strike, dip, rake, marker='.', color='black') # Find the two modes centers = mplstereonet.kmeans(strike, dip, rake, num=2, measurement='rakes') strike_cent, dip_cent = mplstereonet.geographic2pole(*zip(*centers)) ax.pole(strike_cent, dip_cent, 'ro', ms=12) # Fit a girdle to the two modes # The pole of this plane will be the plunge of the fold axis axis_s, axis_d = mplstereonet.fit_girdle(*zip(*centers), measurement='radians') ax.plane(axis_s, axis_d, color='green') ax.pole(axis_s, axis_d, color='green', marker='o', ms=15) # Now we'll find the midpoint. We could project the centers as rakes on the # plane we just fit, but it's easier to get their mean vector instead. mid, _ = mplstereonet.find_mean_vector(*zip(*centers), measurement='radians') midx, midy = mplstereonet.line(*mid) # Now let's find the axial plane by fitting another girdle to the midpoint # and the pole of the plunge axis. xp, yp = mplstereonet.pole(axis_s, axis_d) x, y = [xp[0], midx], [yp[0], midy] axial_s, axial_dip = mplstereonet.fit_girdle(x, y, measurement='radians') ax.plane(axial_s, axial_dip, color='lightblue', lw=3) plt.show()
# In the end, we'll have strikes and dips as measured from bedding in the fold. # *strike* and *dip* below would normally be your input. num_points = 200 real_bearing, real_plunge = 300, 5 s, d = mplstereonet.plunge_bearing2pole(real_plunge, real_bearing) lon, lat = mplstereonet.plane(s, d, segments=num_points) lon += np.random.normal(0, np.radians(15), lon.shape) lat += np.random.normal(0, np.radians(15), lat.shape) strike, dip = mplstereonet.geographic2pole(lon, lat) # Plot the raw data and contour it: fig, ax = mplstereonet.subplots() ax.density_contourf(strike, dip, cmap='gist_earth') ax.density_contour(strike, dip, colors='black') ax.pole(strike, dip, marker='.', color='black') # Fit a plane to the girdle of the distribution and display it. fit_strike, fit_dip = mplstereonet.fit_girdle(strike, dip) ax.plane(fit_strike, fit_dip, color='red', lw=2) ax.pole(fit_strike, fit_dip, marker='o', color='red', markersize=14) # Add some annotation of the result lon, lat = mplstereonet.pole(fit_strike, fit_dip) (plunge,), (bearing,) = mplstereonet.pole2plunge_bearing(fit_strike, fit_dip) template = u'P/B of Fold Axis\n{:02.0f}\u00b0/{:03.0f}\u00b0' ax.annotate(template.format(plunge, bearing), ha='center', va='bottom', xy=(lon, lat), xytext=(-50, 20), textcoords='offset points', arrowprops=dict(arrowstyle='-|>', facecolor='black')) plt.show()