Calculating paleocoordinates with pyGplates
Programmatically rotate present-day coordinates back in geological time
Overview
This is a very short tutorial showing how to rotate data points back in time based on (1) present-day lon-lat data and (2) a rotational model (here using the model of Scotese and Wright, 2018).
Requirements
You’ll need a python 3.8 install. Tested on MacOS 12.1 only.
-
Minimum example can be downloaded as a .zip archive here
-
All requested files are available in the archive, including files that should normally be downloaded from the dedicated websites:
- the MacOS pyGPlates distribution;
- Scotese and Right (2018) rotation and polygon files.
To execute the script on MacOS, just open your favorite terminal and type in:
python3 rotate.py
Python code
Provided in the archive, shown here for illustration.
# python3 rotate.py
# https://pip.pypa.io/en/stable/getting-started/
# python3 -m pip install pandas
# takes around 5 mins to rotate all > 700 000 PBDB entries individually on MacBook Pro i5 2.4GhZ 16Go DDR3
# Dropped 40416/741860 (5.4%) entries that were not correctly rotated
# output: 'Used_pbdbdata_simplified_rotatedScotese.csv'
# https://discourse.gplates.org/t/pygplates-restore-points-to-paleomap/339/6
import sys
sys.path.insert(1, './pygplates_rev28_python38_MacOS64')
import pygplates
from pandas import read_csv
import numpy as np
import os
######################
inPBDB = 'indata.csv'
######################
# reading PBDB data
PBDB_table = read_csv(inPBDB, sep=';', header='infer')
age_min = np.squeeze(np.array(PBDB_table['min_ma'].to_frame()))
age_max = np.squeeze(np.array(PBDB_table['max_ma'].to_frame()))
age_mid = (age_min + age_max) / 2. # each point rotated using mid age
PDlon = np.squeeze(np.array(PBDB_table['lng'].to_frame())) # -180:180 (741865,)
PDlat = np.squeeze(np.array(PBDB_table['lat'].to_frame()))
nPBDB = len(age_min)
# Load one or more rotation files into a rotation model.
rotation_model = pygplates.RotationModel('./PALEOMAP_PlateModel.rot')
# The static polygons file will be used to assign plate IDs and valid time periods.
static_polygons_filename = './PALEOMAP_PlatePolygons.gpml'
# Filename of the output points file that we will write.
# Note that it is a GPML file (has extension '.gpml').
# This enables it to be read into GPlates.
output_points_filename = 'output_points.gpml' # tmp file, removed at the end
input_points = []
for i in np.arange(nPBDB): # nPBDB (replace with e.g. 10000 to test script on a subset of data)
# Create a pyGPlates point from the latitude and longitude, and add it to our list of points.
input_points.append(pygplates.PointOnSphere(PDlat[i], PDlon[i]))
# debugging/testing
#input_points = []
#input_points.append(pygplates.PointOnSphere(0, 0))
#input_points.append(pygplates.PointOnSphere(0, 0))
#age_mid = [50, 0]
# Create a feature for each point we read from the input file.
point_features = []
for point in input_points:
# Create an unclassified feature.
point_feature = pygplates.Feature()
# Set the feature's geometry to the input point read from the text file.
point_feature.set_geometry(point)
point_features.append(point_feature)
# Use the static polygons to assign plate IDs and valid time periods.
# Each point feature is partitioned into one of the static polygons and assigned its
# reconstruction plate ID and valid time period.
assigned_point_features = pygplates.partition_into_plates(
static_polygons_filename,
rotation_model,
point_features,
properties_to_copy = [
pygplates.PartitionProperty.reconstruction_plate_id,
pygplates.PartitionProperty.valid_time_period])
output_points_filename = 'output_points.gpmlz'
assigned_point_feature_collection = pygplates.FeatureCollection(assigned_point_features)
assigned_point_feature_collection.write(output_points_filename)
# Restore points to a geological time based on rotation model
features = pygplates.FeatureCollection(output_points_filename)
paleolat = np.full((nPBDB),np.nan)
paleolon = np.full((nPBDB),np.nan)
counter = 0
for feature in features:
reconstruction_time = age_mid[counter]
paleocoords = [] # possibility to use a list or a string (to save to file)
pygplates.reconstruct(feature, rotation_model, paleocoords, reconstruction_time)
if len(paleocoords)>0: # some data points disappear because of rotational model
# https://www.gplates.org/docs/pygplates/pygplates_reference.html#geometry
# https://www.gplates.org/docs/pygplates/generated/pygplates.reconstructedfeaturegeometry
# paleocoords[0].get_present_day_geometry().to_lat_lon_list()
paleolat[counter] = paleocoords[0].get_reconstructed_geometry().to_lat_lon_list()[0][0]
paleolon[counter] = paleocoords[0].get_reconstructed_geometry().to_lat_lon_list()[0][1]
else:
paleolat[counter] = np.nan
paleolon[counter] = np.nan
counter += 1
# adding columns to PBDB table
PBDB_table['paleolon'] = paleolon
PBDB_table['paleolat'] = paleolat
# removing rows with no data
PBDB_table_noNA = PBDB_table.dropna(axis=0, how = 'any', inplace=False)
n_droppedrows = nPBDB - len(PBDB_table_noNA)
percent_droppedrows = np.around((n_droppedrows)/nPBDB*100.,1)
print('Dropped ' + str(int(n_droppedrows)) + '/' + str(int(nPBDB)) + ' (' + str(percent_droppedrows)+ '%) entries that were not correctly rotated')
# writing to csv
outname = 'Used_pbdbdata_simplified_rotatedScotese.csv'
PBDB_table_noNA.to_csv(outname, sep=';')
print('Saving to file: ' + outname)
# cleaning
os.remove(output_points_filename)
print('done')