def orientation_polygon_along_equator(polygon): """Rotate the polygon such that its major axis is aligned along the equator.""" polygon_centroid = polygon.get_boundary_centroid() # Rotate polygon so its centroid in on equator (nearest point on equator to original centroid). rotation_to_equator = get_rotation_to_equator(polygon_centroid) equator_polygon = rotation_to_equator * polygon equator_polygon_centroid = rotation_to_equator * polygon_centroid # Tessellate polygon on equator so we have enough points to do PCA analysis of polygon's boundary. tesselated_equator_polygon = equator_polygon.to_tessellated(math.radians(1)) major_axis_orientation_angle_radians = get_major_axis_orientation_angle( equator_polygon_centroid.to_lat_lon(), tesselated_equator_polygon.to_lat_lon_list()) # Rotate polygon such that major axis is aligned with equator. reorient_major_axis = pygplates.FiniteRotation( equator_polygon_centroid, -major_axis_orientation_angle_radians) oriented_polygon = reorient_major_axis * equator_polygon #print 'Rotation to equator %s' % rotation_to_equator #print 'Rotation to reorient major axis %s' % reorient_major_axis composed_rotation = reorient_major_axis * rotation_to_equator return oriented_polygon,composed_rotation
def get_rotation_to_equator(point): """Get rotation from 'point' to nearest position on equator.""" rotate_from_north_to_point = pygplates.FiniteRotation(pygplates.PointOnSphere.north_pole, point) if rotate_from_north_to_point.represents_identity_rotation(): # Point coincides with North Pole, so just choose any rotation pole on the equator to rotate point with. # The point could end up anywhere along equator. rotate_from_north_to_point_pole = pygplates.PointOnSphere(0, 0) rotate_from_north_to_point_angle = 0.5 * math.pi # Rotate 90 degrees from North Pole to equator. else: rotate_from_north_to_point_pole, rotate_from_north_to_point_angle = rotate_from_north_to_point.get_euler_pole_and_angle() return pygplates.FiniteRotation( rotate_from_north_to_point_pole, 0.5 * math.pi - rotate_from_north_to_point_angle)
def orientation_polygon_along_meridian(polygon): median_longitude = np.median(polygon.to_lat_lon_array()[:,1]) reorient_major_axis = pygplates.FiniteRotation( pygplates.PointOnSphere(90,0), -np.radians(median_longitude)) oriented_polygon = reorient_major_axis * polygon return oriented_polygon,reorient_major_axis
def get_mid_ocean_ridges(shared_boundary_sections, rotation_model, reconstruction_time, time_step, sampling=2.0): """ Get tessellated points along a mid ocean ridge""" shifted_mor_points = [] for shared_boundary_section in shared_boundary_sections: # The shared sub-segments contribute either to the ridges or to the subduction zones. if shared_boundary_section.get_feature().get_feature_type( ) == pygplates.FeatureType.create_gpml('MidOceanRidge'): # Ignore zero length segments - they don't have a direction. spreading_feature = shared_boundary_section.get_feature() # Find the stage rotation of the spreading feature in the frame of reference of its # geometry at the current reconstruction time (the MOR is currently actively spreading). # The stage pole can then be directly geometrically compared to the *reconstructed* spreading geometry. stage_rotation = separate_ridge_transform_segments.get_stage_rotation_for_reconstructed_geometry( spreading_feature, rotation_model, reconstruction_time) if not stage_rotation: # Skip current feature - it's not a spreading feature. continue # Get the stage pole of the stage rotation. # Note that the stage rotation is already in frame of reference of the *reconstructed* geometry at the spreading time. stage_pole, _ = stage_rotation.get_euler_pole_and_angle() # One way rotates left and the other right, but don't know which - doesn't matter in our example though. rotate_slightly_off_mor_one_way = pygplates.FiniteRotation( stage_pole, np.radians(0.01)) rotate_slightly_off_mor_opposite_way = rotate_slightly_off_mor_one_way.get_inverse( ) # Iterate over the shared sub-segments. for shared_sub_segment in shared_boundary_section.get_shared_sub_segments( ): # Tessellate MOR section. mor_points = pygplates.MultiPointOnSphere( shared_sub_segment.get_resolved_geometry().to_tessellated( np.radians(sampling))) # NOTE temporary hack to avoid seed points at ridge trench intersections for point in mor_points.get_points()[1:-1]: # Append shifted geometries (one with points rotated one way and the other rotated the opposite way). shifted_mor_points.append(rotate_slightly_off_mor_one_way * point) shifted_mor_points.append( rotate_slightly_off_mor_opposite_way * point) #print shifted_mor_points return shifted_mor_points
def _create_node(self, node_centre_lon, node_centre_lat, node_half_width_degrees, is_north_hemisphere): # Create the points of the polygon bounding the current quad tree node. bounding_polygon_points = [] left_lon = node_centre_lon - node_half_width_degrees right_lon = node_centre_lon + node_half_width_degrees bottom_lat = node_centre_lat - node_half_width_degrees top_lat = node_centre_lat + node_half_width_degrees # Northern and southern hemispheres handled separately. if is_north_hemisphere: # Northern hemisphere. left_boundary = pygplates.PolylineOnSphere([(0, left_lon), (90, left_lon)]) right_boundary = pygplates.PolylineOnSphere([(0, right_lon), (90, right_lon)]) # Midpoint of small circle arc bounding the bottom of quad tree node. bottom_mid_point = pygplates.PointOnSphere( bottom_lat, 0.5 * (left_lon + right_lon)) # Find the great circle (rotation) that passes through the bottom midpoint (and is oriented towards North pole). bottom_great_circle_rotation_axis = pygplates.Vector3D.cross( bottom_mid_point.to_xyz(), pygplates.Vector3D.cross( pygplates.PointOnSphere.north_pole.to_xyz(), bottom_mid_point.to_xyz())).to_normalised() bottom_great_circle_rotation = pygplates.FiniteRotation( bottom_great_circle_rotation_axis.to_xyz(), 0.5 * math.pi) # Intersect great circle bottom boundary with left and right boundaries to find bottom-left and bottom-right points. # The bottom boundary is actually a small circle (due to lat/lon grid), but since we need to use *great* circle arcs # in our geometries we need to be a bit loose with our bottom boundary otherwise it will go inside the quad tree node. bottom_boundary = pygplates.PolylineOnSphere([ bottom_great_circle_rotation * bottom_mid_point, bottom_mid_point, bottom_great_circle_rotation.get_inverse() * bottom_mid_point ]) _, _, bottom_left_point = pygplates.GeometryOnSphere.distance( bottom_boundary, left_boundary, return_closest_positions=True) _, _, bottom_right_point = pygplates.GeometryOnSphere.distance( bottom_boundary, right_boundary, return_closest_positions=True) bounding_polygon_points.append(bottom_left_point) bounding_polygon_points.append(bottom_right_point) bounding_polygon_points.append( pygplates.PointOnSphere(top_lat, right_lon)) bounding_polygon_points.append( pygplates.PointOnSphere(top_lat, left_lon)) else: # Southern hemisphere. left_boundary = pygplates.PolylineOnSphere([(0, left_lon), (-90, left_lon)]) right_boundary = pygplates.PolylineOnSphere([(0, right_lon), (-90, right_lon)]) # Midpoint of small circle arc bounding the top of quad tree node. top_mid_point = pygplates.PointOnSphere( top_lat, 0.5 * (left_lon + right_lon)) # Find the great circle (rotation) that passes through the top midpoint (and is oriented towards North pole). top_great_circle_rotation_axis = pygplates.Vector3D.cross( top_mid_point.to_xyz(), pygplates.Vector3D.cross( pygplates.PointOnSphere.north_pole.to_xyz(), top_mid_point.to_xyz())).to_normalised() top_great_circle_rotation = pygplates.FiniteRotation( top_great_circle_rotation_axis.to_xyz(), 0.5 * math.pi) # Intersect great circle top boundary with left and right boundaries to find top-left and top-right points. # The top boundary is actually a small circle (due to lat/lon grid), but since we need to use *great* circle arcs # in our geometries we need to be a bit loose with our top boundary otherwise it will go inside the quad tree node. top_boundary = pygplates.PolylineOnSphere([ top_great_circle_rotation * top_mid_point, top_mid_point, top_great_circle_rotation.get_inverse() * top_mid_point ]) _, _, top_left_point = pygplates.GeometryOnSphere.distance( top_boundary, left_boundary, return_closest_positions=True) _, _, top_right_point = pygplates.GeometryOnSphere.distance( top_boundary, right_boundary, return_closest_positions=True) bounding_polygon_points.append(top_left_point) bounding_polygon_points.append(top_right_point) bounding_polygon_points.append( pygplates.PointOnSphere(bottom_lat, right_lon)) bounding_polygon_points.append( pygplates.PointOnSphere(bottom_lat, left_lon)) bounding_polygon = pygplates.PolygonOnSphere(bounding_polygon_points) return QuadTreeNode(bounding_polygon)
def warp_subduction_segment(tessellated_line, rotation_model, subducting_plate_id, overriding_plate_id, subduction_polarity, time, end_time, time_step, dip_angle_radians, subducting_plate_disappearance_time=-1, use_small_circle_path=False): # We need to reverse the subducting_normal vector direction if overriding plate is to # the right of the subducting line since great circle arc normal is always to the left. if subduction_polarity == 'Left': subducting_normal_reversal = 1 else: subducting_normal_reversal = -1 # tesselate the line, and create an array with the same length as the tesselated points # with zero as the starting depth for each point at t=0 points = [point for point in tessellated_line] point_depths = [0. for point in points] # Need at least two points for a polyline. Otherwise, return None for all results if len(points) < 2: points = None; point_depths = None; polyline = None return points, point_depths, polyline polyline = pygplates.PolylineOnSphere(points) warped_polylines = [] # Add original unwarped polyline first. warped_polylines.append(polyline) #warped_end_time = time - warped_time_interval warped_end_time = end_time if warped_end_time < 0: warped_end_time = 0 # iterate over each time in the range defined by the input parameters for warped_time in np.arange(time, warped_end_time-time_step,-time_step): #if warped_time<=23. and subducting_plate_id==902: # if overriding_plate_id in [224,0,101]: # print('forcing Farallon to become Cocos where overriding plate id is %d' % overriding_plate_id) # subducting_plate_id = 909 # else: # print('forcing Farallon to become Nazca where overriding plate id is %d' % overriding_plate_id) # subducting_plate_id = 911 if warped_time<=subducting_plate_disappearance_time: print('Using %0.2f to %0.2f Ma stage pole for plate %d' % (subducting_plate_disappearance_time+time_step, subducting_plate_disappearance_time, subducting_plate_id)) stage_rotation = rotation_model.get_rotation(subducting_plate_disappearance_time+time_step, subducting_plate_id, subducting_plate_disappearance_time) else: # the stage rotation that describes the motion of the subducting plate, # with respect to the fixed plate for the rotation model stage_rotation = rotation_model.get_rotation(warped_time-time_step, subducting_plate_id, warped_time) if use_small_circle_path: stage_pole, stage_pole_angle_radians = stage_rotation.get_euler_pole_and_angle() # get velocity vectors at each point along polyline relative_velocity_vectors = pygplates.calculate_velocities( points, stage_rotation, time_step, pygplates.VelocityUnits.kms_per_my) # Get subducting normals for each segment of tessellated polyline. # Also add an imaginary normal prior to first and post last points # (makes its easier to later calculate average normal at tessellated points). # The number of normals will be one greater than the number of points. subducting_normals = [] subducting_normals.append(None) # Imaginary segment prior to first point. for segment in polyline.get_segments(): if segment.is_zero_length(): subducting_normals.append(None) else: # The normal to the subduction zone in the direction of subduction (towards overriding plate). subducting_normals.append( subducting_normal_reversal * segment.get_great_circle_normal()) subducting_normals.append(None) # Imaginary segment after to last point. # get vectors of normals and parallels for each segment, use these # to get a normal and parallel at each point location normals = [] parallels = [] for point_index in range(len(points)): prev_normal = subducting_normals[point_index] next_normal = subducting_normals[point_index + 1] if prev_normal is None and next_normal is None: # Skip point altogether (both adjoining segments are zero length). continue if prev_normal is None: normal = next_normal elif next_normal is None: normal = prev_normal else: normal = (prev_normal + next_normal).to_normalised() parallel = pygplates.Vector3D.cross(point.to_xyz(), normal).to_normalised() normals.append(normal) parallels.append(parallel) # iterate over each point to determine the incremented position # based on plate motion and subduction dip warped_points = [] warped_point_depths = [] for point_index, point in enumerate(points): normal = normals[point_index] parallel = parallels[point_index] velocity = relative_velocity_vectors[point_index] if velocity.is_zero_magnitude(): # Point hasn't moved. warped_points.append(point) warped_point_depths.append(point_depths[point_index]) continue # reconstruct the tracked point from position at current time to # position at the next time step normal_angle = pygplates.Vector3D.angle_between(velocity, normal) parallel_angle = pygplates.Vector3D.angle_between(velocity, parallel) # Trench parallel and normal components of velocity. velocity_normal = np.cos(normal_angle) * velocity.get_magnitude() velocity_parallel = np.cos(parallel_angle) * velocity.get_magnitude() normal_vector = normal.to_normalised() * velocity_normal parallel_vector = parallel.to_normalised() * velocity_parallel # Adjust velocity based on subduction vertical dip angle. velocity_dip = parallel_vector + np.cos(dip_angle_radians) * normal_vector #deltaZ is the amount that this point increases in depth within the time step deltaZ = np.sin(dip_angle_radians) * velocity.get_magnitude() # Should be 90 degrees always. #print np.degrees(np.arccos(pygplates.Vector3D.dot(normal_vector, parallel_vector))) if use_small_circle_path: # Rotate original stage pole by the same angle that effectively # rotates the velocity vector to the dip velocity vector. dip_stage_pole_rotate = pygplates.FiniteRotation( point, pygplates.Vector3D.angle_between(velocity_dip, velocity)) dip_stage_pole = dip_stage_pole_rotate * stage_pole else: # Get the unnormalised vector perpendicular to both the point and velocity vector. dip_stage_pole_x, dip_stage_pole_y, dip_stage_pole_z = pygplates.Vector3D.cross( point.to_xyz(), velocity_dip).to_xyz() # PointOnSphere requires a normalised (ie, unit length) vector (x, y, z). dip_stage_pole = pygplates.PointOnSphere( dip_stage_pole_x, dip_stage_pole_y, dip_stage_pole_z, normalise=True) # Get angle that velocity will rotate seed point along great circle arc # over 'time_step' My (if velocity in Kms / My). dip_stage_angle_radians = velocity_dip.get_magnitude() * ( time_step / pygplates.Earth.mean_radius_in_kms) if use_small_circle_path: # Increase rotation angle to adjust for fact that we're moving a # shorter distance with small circle (compared to great circle). dip_stage_angle_radians /= np.abs(np.sin( pygplates.Vector3D.angle_between( dip_stage_pole.to_xyz(), point.to_xyz()))) # Use same sign as original stage rotation. if stage_pole_angle_radians < 0: dip_stage_angle_radians = -dip_stage_angle_radians # get the stage rotation that describes the lateral motion of the # point taking the dip into account dip_stage_rotation = pygplates.FiniteRotation(dip_stage_pole, dip_stage_angle_radians) # increment the point long,lat and depth warped_point = dip_stage_rotation * point warped_points.append(warped_point) warped_point_depths.append(point_depths[point_index] + deltaZ) # finished warping all points in polyline # --> increment the polyline for this time step warped_polyline = pygplates.PolylineOnSphere(warped_points) warped_polylines.append(warped_polyline) # For next warping iteration. points = warped_points polyline = warped_polyline point_depths = warped_point_depths return points, point_depths, polyline
def extract_plate_pair_stage_rotations( rotation_feature_collections, plate_pair_filter=None): # Docstring in numpydoc format... """Calculate stage rotations between consecutive finite rotations in each specified plate pair. Parameters ---------- rotation_feature_collections : sequence of (str, or sequence of pygplates.Feature, or pygplates.FeatureCollection, or pygplates.Feature) A sequence of rotation feature collections. Each collection in the sequence can be a rotation filename, or a sequence (eg, list of tuple) or features, or a feature collection, or even a single feature. plate_pair_filter : Filter function accepting accepting 3 arguments (moving_plate_id, fixed_plate_id, rotation_sequence), or sequence of 2-tuple (moving_plate_id, fixed_plate_id), optional Optional filtering of plate pairs to apply operation to. Filter function (callable) accepting 3 arguments (), or a sequence of (moving, fixed) plate pairs to limit operation to. Returns ------- list of pygplates.FeatureCollection The modified feature collections. Returned list is same length as ``rotation_feature_collections``. Notes ----- The results are returned as a list of pygplates.FeatureCollection (one per input rotation feature collection). Note that only the rotation features satisfying ``plate_pair_filter`` (if specified) are returned. So if a returned feature collection is empty then it means all of its features were filtered out. """ if plate_pair_filter is None: plate_pair_filter = _all_filter # If caller specified a sequence of moving/fixed plate pairs then use them, otherwise it's a filter function (callable). elif hasattr(plate_pair_filter, '__iter__'): plate_pair_filter = _is_in_plate_pair_sequence(plate_pair_filter) # else ...'plate_pair_filter' is a callable... output_rotation_feature_collections = [] for rotation_feature_collection in rotation_feature_collections: # Create an empty output rotation feature collection for each input rotation feature collection. # We'll only add output features when an input feature is modified. output_rotation_feature_collection = [] output_rotation_feature_collections.append(output_rotation_feature_collection) for rotation_feature in rotation_feature_collection: # Get the rotation feature information. total_reconstruction_pole = rotation_feature.get_total_reconstruction_pole() if not total_reconstruction_pole: # Not a rotation feature. continue fixed_plate_id, moving_plate_id, rotation_sequence = total_reconstruction_pole # We're only interested in rotation features with matching moving/fixed plate IDs. if not plate_pair_filter(fixed_plate_id, moving_plate_id, rotation_sequence): continue # Clone the input feature before we start making modifications. # Otherwise we'll be modifying the input rotation feature (which the caller might not expect). output_rotation_feature = rotation_feature.clone() _, _, output_rotation_sequence = output_rotation_feature.get_total_reconstruction_pole() # Get the enabled rotation samples - ignore the disabled samples. output_enabled_rotation_samples = output_rotation_sequence.get_enabled_time_samples() if not output_enabled_rotation_samples: # No time samples are enabled. continue prev_finite_rotation = output_enabled_rotation_samples[0].get_value().get_finite_rotation() # Replace first finite rotation with identity rotation. # This is the stage rotation of first finite rotation (which has no previous finite rotation). output_enabled_rotation_samples[0].get_value().set_finite_rotation(pygplates.FiniteRotation()) for rotation_sample_index in range(1, len(output_enabled_rotation_samples)): time_sample = output_enabled_rotation_samples[rotation_sample_index] finite_rotation = time_sample.get_value().get_finite_rotation() # The finite rotation at current time tc is composed of finite rotation at # previous (younger) time tp and stage rotation between them: # R(0->tc) = R(tp->tc) * R(0->tp) # The stage rotation from tp->tc: # R(tp->tc) = R(0->tc) * inverse[R(0->tp)] stage_rotation = finite_rotation * prev_finite_rotation.get_inverse() # Replace current finite rotation with stage rotation. time_sample.get_value().set_finite_rotation(stage_rotation) prev_finite_rotation = finite_rotation output_rotation_feature_collection.append(output_rotation_feature) # Return our output feature collections as a list of pygplates.FeatureCollection. return [pygplates.FeatureCollection(rotation_feature_collection) for rotation_feature_collection in output_rotation_feature_collections]
def extract_plate_pair_stage_rotations(rotation_feature_collections, plate_pair_filter=None): # Docstring in numpydoc format... """Calculate stage rotations between consecutive finite rotations in each specified plate pair. The results are returned as a list of pygplates.FeatureCollection (one per input rotation feature collection). Parameters ---------- rotation_feature_collections : sequence of (str, or sequence of pygplates.Feature, or pygplates.FeatureCollection, or pygplates.Feature) A sequence of rotation feature collections. Each collection in the sequence can be a rotation filename, or a sequence (eg, list of tuple) or features, or a feature collection, or even a single feature. plate_pair_filter : Filter function accepting accepting 3 arguments (moving_plate_id, fixed_plate_id, rotation_sequence), or sequence of 2-tuple (moving_plate_id, fixed_plate_id), optional Optional filtering of plate pairs to apply operation to. Filter function (callable) accepting 3 arguments (), or a sequence of (moving, fixed) plate pairs to limit operation to. Returns ------- list of pygplates.FeatureCollection The modified feature collections. Returned list is same length as ``rotation_feature_collections``. """ # Convert each feature collection into a list of features for easier manipulation. rotation_feature_collections = [ list(pygplates.FeatureCollection(rotation_feature_collection)) for rotation_feature_collection in rotation_feature_collections ] if plate_pair_filter is None: plate_pair_filter = _all_filter # If caller specified a sequence of moving/fixed plate pairs then use them, otherwise it's a filter function (callable). elif hasattr(plate_pair_filter, '__iter__'): plate_pair_filter = _is_in_plate_pair_sequence(plate_pair_filter) # else ...'plate_pair_filter' is a callable... for rotation_feature_collection in rotation_feature_collections: for rotation_feature in rotation_feature_collection: # Get the rotation feature information. total_reconstruction_pole = rotation_feature.get_total_reconstruction_pole( ) if not total_reconstruction_pole: # Not a rotation feature. continue fixed_plate_id, moving_plate_id, rotation_sequence = total_reconstruction_pole # We're only interested in rotation features with matching moving/fixed plate IDs. if not plate_pair_filter(fixed_plate_id, moving_plate_id, rotation_sequence): continue # Get the enabled rotation samples - ignore the disabled samples. enabled_rotation_samples = rotation_sequence.get_enabled_time_samples( ) if len(enabled_rotation_samples) < 2: # Need at least two time samples to find a stage rotation (between them). continue prev_finite_rotation = enabled_rotation_samples[0].get_value( ).get_finite_rotation() # Replace first finite rotation with identity rotation. # This is the stage rotation of first finite rotation (which has no previous finite rotation). enabled_rotation_samples[0].get_value().set_finite_rotation( pygplates.FiniteRotation()) for rotation_sample_index in range(1, len(enabled_rotation_samples)): time_sample = enabled_rotation_samples[rotation_sample_index] finite_rotation = time_sample.get_value().get_finite_rotation() # The finite rotation at current time tc is composed of finite rotation at # previous (younger) time tp and stage rotation between them: # R(0->tc) = R(tp->tc) * R(0->tp) # The stage rotation from tp->tc: # R(tp->tc) = R(0->tc) * inverse[R(0->tp)] stage_rotation = finite_rotation * prev_finite_rotation.get_inverse( ) # Replace current finite rotation with stage rotation. time_sample.get_value().set_finite_rotation(stage_rotation) prev_finite_rotation = finite_rotation # Return our (potentially) modified feature collections as a list of pygplates.FeatureCollection. return [ pygplates.FeatureCollection(rotation_feature_collection) for rotation_feature_collection in rotation_feature_collections ]