Create conjugate isochrons from a ridge
This example creates a conjugate pair of isochrons from a mid-ocean ridge at each specified geological time in a series.
Sample code
import pygplates
# Load one or more rotation files into a rotation model.
rotation_model = pygplates.RotationModel('rotations.rot')
# Load the mid-ocean ridge features.
ridge_features = pygplates.FeatureCollection('ridges.gpml')
# The times at which to create isochrons.
isochron_creation_times = [40, 30, 20, 10, 0]
# We'll store the created isochrons here - later we'll write it to a file.
isochron_feature_collection = pygplates.FeatureCollection()
# Iterate over the ridge features.
for ridge_feature in ridge_features:
    # Get the ridge left and right plate ids, and time of appearance.
    left_plate_id = ridge_feature.get_left_plate()
    right_plate_id = ridge_feature.get_right_plate()
    time_of_appearance, time_of_disappearance = ridge_feature.get_valid_time()
    # Iterate over our list of creation times for the left/right isochrons.
    for isochron_creation_time in isochron_creation_times:
        # If creation time is later than ridge birth time then we can create an isochron.
        if isochron_creation_time < time_of_appearance:
            # Reconstruct the mid-ocean ridge to isochron creation time.
            # The ridge geometry will be in the same position as the left/right isochrons at that time.
            reconstructed_ridges = []
            pygplates.reconstruct(ridge_feature, rotation_model, reconstructed_ridges, isochron_creation_time)
            # Get the isochron geometry from the ridge reconstruction.
            # This is the geometry at 'isochron_creation_time' (not present day).
            isochron_geometry_at_creation_time = [reconstructed_ridge.get_reconstructed_geometry()
                    for reconstructed_ridge in reconstructed_ridges]
            # Create the left and right isochrons.
            # Since they are conjugates they have swapped left and right plate IDs.
            # And reverse reconstruct the mid-ocean ridge geometries to present day.
            left_isochron_feature = pygplates.Feature.create_reconstructable_feature(
                    pygplates.FeatureType.gpml_isochron,
                    isochron_geometry_at_creation_time,
                    name = ridge_feature.get_name(None),
                    description = ridge_feature.get_description(None),
                    valid_time = (isochron_creation_time, 0),
                    reconstruction_plate_id = left_plate_id,
                    conjugate_plate_id = right_plate_id,
                    reverse_reconstruct = (rotation_model, isochron_creation_time))
            right_isochron_feature = pygplates.Feature.create_reconstructable_feature(
                    pygplates.FeatureType.gpml_isochron,
                    isochron_geometry_at_creation_time,
                    name = ridge_feature.get_name(None),
                    description = ridge_feature.get_description(None),
                    valid_time = (isochron_creation_time, 0),
                    reconstruction_plate_id = right_plate_id,
                    conjugate_plate_id = left_plate_id,
                    reverse_reconstruct = (rotation_model, isochron_creation_time))
            # Add isochrons to feature collection.
            isochron_feature_collection.add(left_isochron_feature)
            isochron_feature_collection.add(right_isochron_feature)
# Write the isochrons to a new file.
isochron_feature_collection.write('isochrons.gpml')
Details
The rotations are loaded from a rotation file into a pygplates.RotationModel.
rotation_model = pygplates.RotationModel('rotations.rot')
The ridge features are loaded into a pygplates.FeatureCollection.
ridge_features = pygplates.FeatureCollection('ridges.gpml')
The plate IDs and time period are obtained using pygplates.Feature.get_left_plate(),
pygplates.Feature.get_right_plate() and pygplates.Feature.get_valid_time().
left_plate_id = ridge_feature.get_left_plate()
right_plate_id = ridge_feature.get_right_plate()
time_of_appearance, time_of_disappearance = ridge_feature.get_valid_time()
Smaller time values are closer to present day (younger).
if isochron_creation_time < time_of_appearance:
The ridges are reconstructed to their locations at time ‘isochron_creation_time’ using
pygplates.reconstruct().
reconstructed_ridges = []
pygplates.reconstruct(ridge_feature, rotation_model, reconstructed_ridges, isochron_creation_time)
A Python list comprehension is used to build a list of pygplates.GeometryOnSphere from a
list of pygplates.ReconstructedFeatureGeometry.
isochron_geometry_at_creation_time = [reconstructed_ridge.get_reconstructed_geometry()
        for reconstructed_ridge in reconstructed_ridges]
Isochron features are created using
pygplates.Feature.create_reconstructable_feature().
left_isochron_feature = pygplates.Feature.create_reconstructable_feature(
        pygplates.FeatureType.gpml_isochron,
        isochron_geometry_at_creation_time,
        name = ridge_feature.get_name(None),
        description = ridge_feature.get_description(None),
        valid_time = (isochron_creation_time, 0),
        reconstruction_plate_id = left_plate_id,
        conjugate_plate_id = right_plate_id,
        reverse_reconstruct = (rotation_model, isochron_creation_time))
The reverse_reconstruct parameter is needed because all features
must store their geometry in present day coordinates which means reverse reconstructing from
isochron_creation_time to present day using the rotation model.
Note
The use of None in, for example, ridge_feature.get_name(None) results in a
name property only getting created if the ridge feature has a name.
And finally the isochrons are saved to a new file using pygplates.FeatureCollection.write().
isochron_feature_collection.write('isochrons.gpml')
Advanced
If we want to be a bit more robust then we can check that our ridge features are actually ridges and we can make sure they contain left/right plate IDs and a time of appearance/disappearance:
...
# Iterate over the ridge features.
for ridge_feature in ridge_features:
    # Ignore anything that's not a mid-ocean ridge.
    if ridge_feature.get_feature_type() != pygplates.FeatureType.gpml_mid_ocean_ridge:
        continue
    # Get the ridge left and right plate ids, and time of appearance.
    # We don't need to specify 'None', but if we do then it allows us to test if the ridge feature
    # is missing plate IDs or begin/end time period.
    left_plate_id = ridge_feature.get_left_plate(None)
    right_plate_id = ridge_feature.get_right_plate(None)
    valid_time = ridge_feature.get_valid_time(None)
    # Ignore mid-ocean ridges that don't have a left and right plate id and time of appearance.
    if (left_plate_id is None or
        right_plate_id is None or
        valid_time is None):
        continue
    # Extract time of appearance/disappearance from the tuple.
    time_of_appearance, time_of_disappearance = valid_time
    ...
By specifying None in:
left_plate_id = ridge_feature.get_left_plate(None)
right_plate_id = ridge_feature.get_right_plate(None)
valid_time = ridge_feature.get_valid_time(None)
None returned to us if the feature property (eg, left plate ID) is missing
in the ridge feature.None then a default value would be returned if a property
was missing. For get_left_plate() and get_right_plate() this is plate ID 0 and for
get_valid_time() this is a time period from distant past to distant future.