# Hierarchy of plate rotations

This example traverses the plate rotation hierarchy (for a particular reconstruction time) and prints the equivalent and relative total rotations at each plate.

The output of this example is similar to the output of the `Total Reconstruction Poles` dialog (under the `Reconstruction Tree` tab) in GPlates.

## Sample code

```import pygplates

# A function to traverse the sub-tree rooted at a particular plate (the moving plate of 'edge').
def traverse_sub_tree(edge, depth):

relative_total_rotation = edge.get_relative_total_rotation()
relative_pole_latitude, relative_pole_longitude, relative_angle_degrees = (
relative_total_rotation.get_lat_lon_euler_pole_and_angle_degrees())

equivalent_total_rotation = edge.get_equivalent_total_rotation()
equivalent_pole_latitude, equivalent_pole_longitude, equivalent_angle_degrees = (
equivalent_total_rotation.get_lat_lon_euler_pole_and_angle_degrees())

prefix_padding = ' ' * (2*depth)

print '%sPlate ID: %d, Fixed Plate ID: %d:' % (prefix_padding, edge.get_moving_plate_id(), edge.get_fixed_plate_id())

print '%s  Rotation rel. fixed (parent) plate: lat: %f, lon: %f:, angle:%f' % (

print '%s  Equivalent rotation rel. anchored plate: lat: %f, lon: %f:, angle:%f' % (

# Blank line.
print

# Recurse into the children sub-trees.
for child_edge in edge.get_child_edges():
traverse_sub_tree(child_edge, depth + 1)

# Load one or more rotation files into a rotation model.
rotation_model = pygplates.RotationModel('rotations.rot')

# The reconstruction time (Ma) of the plate hierarchy we're interested in.
reconstruction_time = 60

# Get the reconstruction tree.
reconstruction_tree = rotation_model.get_reconstruction_tree(reconstruction_time)

# Get the edges of the reconstruction tree emanating from its root (anchor) plate.
anchor_plate_edges = reconstruction_tree.get_anchor_plate_edges()

# Iterate over the anchor plate edges and traverse the sub-tree of each edge.
for anchor_plate_edge in anchor_plate_edges:
traverse_sub_tree(anchor_plate_edge, 0)
```

## Details

The rotations are loaded from a rotation file into a `pygplates.RotationModel`.

```rotation_model = pygplates.RotationModel('rotations.rot')
```
The hierarchy can change from one reconstruction time to the next depending on how the rotations are arranged in the rotation file(s).
```reconstruction_tree = rotation_model.get_reconstruction_tree(reconstruction_time)
```
An edge in a plate rotation hierarchy represents the rotation of a moving plate relative to a fixed plate. These edges are arranged in a tree-like structure (hierarchy) rooted at the anchor plate (usually plate ID zero).
The anchor plate edges represent those edges emanating from the anchor plate and are obtained using `pygplates.ReconstructionTree.get_anchor_plate_edges()`.
```anchor_plate_edges = reconstruction_tree.get_anchor_plate_edges()
```
The anchor plate edges have different moving plate IDs but all have the same fixed plate ID (which is the anchor plate).
In this way the moving plate of each anchor plate edge is a sub-tree of the entire reconstruction tree.
Here we traverse the sub-trees corresponding to those anchor plate edges.
Note that the reconstruction tree `depth` starts at zero.
```for anchor_plate_edge in anchor_plate_edges:
traverse_sub_tree(anchor_plate_edge, 0)
```
A function is defined that traverses the sub-tree rooted at the moving plate of an edge in the reconstruction tree.
One reason for implementing this as a function is we need to call it recursively (a recursive function calls itself) and this is more difficult to achieve without using a function.
The `depth` argument represents the depth into the reconstruction tree (from the anchored plate) and is used purely to control the amount of indentation used in the `print` statements.
```def traverse_sub_tree(edge, depth):
...
```
The relative rotation is the total rotation of the edge’s moving plate relative to its fixed plate.
The equivalent total rotation is the total rotation of the edge’s moving plate relative to anchored plate.
A total rotation means a rotation at the reconstruction time relative to present day (0Ma).
The pole and angle of each rotation is obtained using `pygplates.FiniteRotation.get_lat_lon_euler_pole_and_angle_degrees()`.
```relative_total_rotation = edge.get_relative_total_rotation()
relative_pole_latitude, relative_pole_longitude, relative_angle_degrees = (
relative_total_rotation.get_lat_lon_euler_pole_and_angle_degrees())

equivalent_total_rotation = edge.get_equivalent_total_rotation()
equivalent_pole_latitude, equivalent_pole_longitude, equivalent_angle_degrees = (
equivalent_total_rotation.get_lat_lon_euler_pole_and_angle_degrees())
```
Print the relative and equivalent total rotations of the moving plate of the reconstruction tree edge.
The level of indentation is controlled with `prefix_padding` which is proportional to the traversal depth.
```prefix_padding = ' ' * (2*depth)

print '%sPlate ID: %d, Fixed Plate ID: %d:' % (prefix_padding, edge.get_moving_plate_id(), edge.get_fixed_plate_id())

print '%s  Rotation rel. fixed (parent) plate: lat: %f, lon: %f:, angle:%f' % (

print '%s  Equivalent rotation rel. anchored plate: lat: %f, lon: %f:, angle:%f' % (

print
```
Just as the anchored plate has one or more anchored plate edges emanating from it, the moving plate of a reconstruction tree edge has one or more child edges emanating from it. These are obtained using `pygplates.ReconstructionTreeEdge.get_child_edges()`.
Note that by calling the `traverse_sub_tree` function we are calling the same function we are already in. This recursive descent enables us to visit all edges and plates in the sub-tree.
The reconstruction tree `depth` is incremented with each recursive call.
The recursion stops when an edge has no child edges. This means that no other plate moves relative to the (moving) plate of that edge.
```for child_edge in edge.get_child_edges():
traverse_sub_tree(child_edge, depth + 1)
```

## Output

```Plate ID: 1, Fixed Plate ID: 0:
Rotation rel. fixed (parent) plate: lat: 90.000000, lon: 0.000000:, angle:0.000000
Equivalent rotation rel. anchored plate: lat: 90.000000, lon: 0.000000:, angle:0.000000

Plate ID: 701, Fixed Plate ID: 1:
Rotation rel. fixed (parent) plate: lat: 23.730000, lon: -42.140000:, angle:-12.530000
Equivalent rotation rel. anchored plate: lat: 23.730000, lon: -42.140000:, angle:-12.530000

Plate ID: 201, Fixed Plate ID: 701:
Rotation rel. fixed (parent) plate: lat: 62.238025, lon: -32.673047:, angle:23.349295
Equivalent rotation rel. anchored plate: lat: 77.493750, lon: 57.067142:, angle:15.711412

Plate ID: 202, Fixed Plate ID: 201:
Rotation rel. fixed (parent) plate: lat: 90.000000, lon: 0.000000:, angle:0.000000
Equivalent rotation rel. anchored plate: lat: 77.493750, lon: 57.067142:, angle:15.711412

Plate ID: 290, Fixed Plate ID: 202:
Rotation rel. fixed (parent) plate: lat: 90.000000, lon: 0.000000:, angle:0.000000
Equivalent rotation rel. anchored plate: lat: 77.493750, lon: 57.067142:, angle:15.711412

Plate ID: 203, Fixed Plate ID: 201:
Rotation rel. fixed (parent) plate: lat: 90.000000, lon: 0.000000:, angle:0.000000
Equivalent rotation rel. anchored plate: lat: 77.493750, lon: 57.067142:, angle:15.711412

Plate ID: 225, Fixed Plate ID: 201:
Rotation rel. fixed (parent) plate: lat: -1.520000, lon: -62.240000:, angle:9.500000
Equivalent rotation rel. anchored plate: lat: 59.149009, lon: -33.687205:, angle:17.238928

Plate ID: 226, Fixed Plate ID: 225:
Rotation rel. fixed (parent) plate: lat: 90.000000, lon: 0.000000:, angle:0.000000
Equivalent rotation rel. anchored plate: lat: 59.149009, lon: -33.687205:, angle:17.238928

...

Plate ID: 802, Fixed Plate ID: 701:
Rotation rel. fixed (parent) plate: lat: 10.617614, lon: -47.371326:, angle:10.778033
Equivalent rotation rel. anchored plate: lat: 62.066424, lon: 9.485588:, angle:-3.331182

Plate ID: 511, Fixed Plate ID: 802:
Rotation rel. fixed (parent) plate: lat: 11.359510, lon: 16.375417:, angle:-41.965910
Equivalent rotation rel. anchored plate: lat: 14.473724, lon: 14.865304:, angle:-44.136911

Plate ID: 501, Fixed Plate ID: 511:
Rotation rel. fixed (parent) plate: lat: -5.200000, lon: 74.300000:, angle:5.930000
Equivalent rotation rel. anchored plate: lat: 18.734696, lon: 8.570162:, angle:-41.677784

Plate ID: 502, Fixed Plate ID: 501:
Rotation rel. fixed (parent) plate: lat: 90.000000, lon: 0.000000:, angle:0.000000
Equivalent rotation rel. anchored plate: lat: 18.734696, lon: 8.570162:, angle:-41.677784

Plate ID: 510, Fixed Plate ID: 501:
Rotation rel. fixed (parent) plate: lat: 90.000000, lon: 0.000000:, angle:0.000000
Equivalent rotation rel. anchored plate: lat: 18.734696, lon: 8.570162:, angle:-41.677784

...
```

…where `lat: 90.000000, lon: 0.000000:, angle:0.000000` is the default representation that `pygplates.FiniteRotation.get_lat_lon_euler_pole_and_angle_degrees()` returns for an `identity rotation` (zero rotation angle).