Skip to content

issue-250-ridge-transforms #298

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 15 commits into from
Feb 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 189 additions & 9 deletions gplately/plot.py
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,8 @@ def __init__(
self.COBs = None
self._topological_plate_boundaries = None
self._topologies = None
self._ridges = []
self._transforms = []

self._anchor_plate_id = self._check_anchor_plate_id(anchor_plate_id)
self._plot_engine = plot_engine
Expand Down Expand Up @@ -399,6 +401,34 @@ def topologies(self):
self._resolve_both_boundaries_and_networks()
return self._topologies

@property
def ridges(self):
"""
Mid-ocean ridge features (all the features which are labelled as gpml:MidOceanRidge in the model).
"""
logger.debug(
"The 'ridges' property has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'ridges' property still suits your purpose. "
"In earlier releases of GPlately, we used an algorithm to identify the 'ridges' and 'transforms' within the gpml:MidOceanRidge features. "
"Unfortunately, the algorithm did not work very well. So we have removed the algorithm and now the 'ridges' property contains all the features "
"which are labelled as gpml:MidOceanRidge in the reconstruction model."
) # use logger.debug to make the message less aggressive
return self._ridges

@property
def transforms(self):
"""
Transform boundary features (all the features which are labelled as gpml:Transform in the model).
"""
logger.debug(
"The 'transforms' property has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'transforms' property still suits your purpose. "
"In earlier releases of GPlately, we used an algorithm to identify the 'ridges' and 'transforms' within the gpml:MidOceanRidge features. "
"Unfortunately, the algorithm did not work very well. So we have removed the algorithm and now the 'transforms' property contains all the features "
"which are labelled as gpml:Transform in the reconstruction model."
) # use logger.debug to make the message less aggressive
return self._transforms

@property
def time(self):
"""The reconstruction time."""
Expand Down Expand Up @@ -438,6 +468,29 @@ def _check_anchor_plate_id(id):
raise ValueError("Invalid anchor plate ID: {}".format(id))
return id

@property
def ridge_transforms(self):
"""
Deprecated! DO NOT USE!
"""

warnings.warn(
"Deprecated! DO NOT USE!"
"The 'ridge_transforms' property will be removed in the future GPlately releases. "
"Update your workflow to use the 'ridges' and 'transforms' properties instead, "
"otherwise your workflow will not work with the future GPlately releases.",
DeprecationWarning,
stacklevel=2,
)
logger.debug(
"The 'ridge_transforms' property has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'ridge_transforms' property still suits your purpose. "
"In earlier releases of GPlately, the 'ridge_transforms' property contains only the features "
"which are labelled as gpml:MidOceanRidge in the reconstruction model. "
"Now, the 'ridge_transforms' property contains both gpml:Transform and gpml:MidOceanRidge features."
)
return self._ridges + self._transforms

def update_time(self, time):
"""Re-reconstruct features and topologies to the time specified by the `PlotTopologies` `time` attribute
whenever it or the anchor plate is updated.
Expand All @@ -463,8 +516,8 @@ def update_time(self, time):
self._time = float(time)
(
self._topological_plate_boundaries,
self.ridges,
self._ridges_do_not_use_for_now,
self._ridges,
self._ridges_do_not_use_for_now, # the algorithm to separate ridges and transforms has not been ready yet
self._transforms_do_not_use_for_now,
self.trenches,
self.trench_left,
Expand Down Expand Up @@ -494,7 +547,6 @@ def update_time(self, time):
self.extended_continental_crusts = []
self.passive_continental_boundaries = []
self.slab_edges = []
self.transforms = []
self.unclassified_features = []

for topol in self.other:
Expand Down Expand Up @@ -551,7 +603,7 @@ def update_time(self, time):
self.slab_edges.append(topol)

elif topol.get_feature_type() == pygplates.FeatureType.gpml_transform: # type: ignore
self.transforms.append(topol)
self._transforms.append(topol)

elif (
topol.get_feature_type()
Expand Down Expand Up @@ -737,7 +789,7 @@ def _plot_feature(self, ax, get_feature_func, **kwargs) -> None:
)

if len(gdf) == 0:
logger.warning("No feature found for plotting. Do nothing and return.")
logger.debug("No feature found for plotting. Do nothing and return.")
return ax

self._plot_engine.plot_geo_data_frame(ax, gdf, **kwargs)
Expand Down Expand Up @@ -845,7 +897,15 @@ def get_ridges(
central_meridian=0.0,
tessellate_degrees=1,
):
"""Create a geopandas.GeoDataFrame object containing geometries of reconstructed mid-ocean ridge lines(gpml:MidOceanRidge)."""
"""Create a geopandas.GeoDataFrame object containing the geometries of reconstructed mid-ocean ridge lines (gpml:MidOceanRidge)."""
logger.debug(
"The 'get_ridges' function has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'get_ridges' function still suits your purpose. "
"In earlier releases of GPlately, we used an algorithm to identify the 'ridges' and 'transforms' within the gpml:MidOceanRidge features. "
"Unfortunately, the algorithm did not work very well. So we have removed the algorithm and now the 'get_ridges' function returns all the features "
"which are labelled as gpml:MidOceanRidge in the reconstruction model."
) # use logger.debug to make the message less aggressive

return self.get_feature(
self.ridges,
central_meridian=central_meridian,
Expand Down Expand Up @@ -873,9 +933,18 @@ def plot_ridges(self, ax, color="black", **kwargs):
Point features near the poles (-89 & 89 degree latitude) are also clipped to ensure
compatibility with Cartopy.
"""

logger.debug(
"The 'plot_ridges' function has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'plot_ridges' function still suits your purpose. "
"In earlier releases of GPlately, we used an algorithm to identify the 'ridges' and 'transforms' within the gpml:MidOceanRidge features. "
"Unfortunately, the algorithm did not work very well. So we have removed the algorithm and now the 'plot_ridges' function plots all the features "
"which are labelled as gpml:MidOceanRidge in the reconstruction model."
) # use logger.debug to make the message less aggressive

return self.plot_feature(
ax,
self.ridges,
self._ridges,
feature_name="ridges",
facecolor="none",
edgecolor=color,
Expand All @@ -892,6 +961,32 @@ def get_trenches(self, central_meridian=0.0, tessellate_degrees=1):
tessellate_degrees=tessellate_degrees,
)

@validate_reconstruction_time
def get_ridges_and_transforms(self, central_meridian=0.0, tessellate_degrees=1):
"""
Deprecated! DO NOT USE.
"""
warnings.warn(
"Deprecated! The 'get_ridges_and_transforms' function will be removed in the future GPlately releases. "
"Update your workflow to use the 'get_ridges' and 'get_transforms' functions instead, "
"otherwise your workflow will not work with the future GPlately releases.",
DeprecationWarning,
stacklevel=2,
)
logger.debug(
"The 'get_ridges_and_transforms' function has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'get_ridges_and_transforms' function still suits your purpose. "
"In earlier releases of GPlately, we used an algorithm to identify the 'ridges' and 'transforms' within the gpml:MidOceanRidge features. "
"Unfortunately, the algorithm did not work very well. So we have removed the algorithm and now the 'get_ridges_and_transforms' function returns all the features "
"which are labelled as gpml:MidOceanRidge or gpml:Transform in the reconstruction model."
) # use logger.debug to make the message less aggressive

return self.get_feature(
self._ridges + self._transforms,
central_meridian=central_meridian,
tessellate_degrees=tessellate_degrees,
)

@validate_topology_availability("trenches")
@append_docstring(PLOT_DOCSTRING.format("trenches"))
def plot_trenches(self, ax, color="black", **kwargs):
Expand Down Expand Up @@ -1722,23 +1817,62 @@ def get_transforms(
tessellate_degrees=None,
):
"""Create a geopandas.GeoDataFrame object containing geometries of reconstructed transform lines(gpml:Transform)."""
logger.debug(
"The 'get_transforms' function has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'get_transforms' function still suits your purpose. "
"In earlier releases of GPlately, we used an algorithm to identify the 'ridges' and 'transforms' within the gpml:MidOceanRidge features. "
"Unfortunately, the algorithm did not work very well. So we have removed the algorithm and now the 'get_transforms' function returns all the features "
"which are labelled as gpml:Transform in the reconstruction model."
) # use logger.debug to make the message less aggressive

return self.get_feature(
self.transforms,
self._transforms,
central_meridian=central_meridian,
tessellate_degrees=tessellate_degrees,
)

@append_docstring(PLOT_DOCSTRING.format("transforms"))
def plot_transforms(self, ax, color="black", **kwargs):
"""Plot transform boundaries(gpml:Transform) onto a map."""

logger.debug(
"The 'plot_transforms' function has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'plot_transforms' function still suits your purpose. "
"In earlier releases of GPlately, we used an algorithm to identify the 'ridges' and 'transforms' within the gpml:MidOceanRidge features. "
"Unfortunately, the algorithm did not work very well. So we have removed the algorithm and now the 'plot_transforms' function plots all the features "
"which are labelled as gpml:Transform in the reconstruction model."
) # use logger.debug to make the message less aggressive

return self.plot_feature(
ax,
self.transforms,
self._transforms,
feature_name="transforms",
edgecolor=color,
**kwargs,
)

def plot_ridges_and_transforms(self, ax, color="black", **kwargs):
"""
Deprecated! DO NOT USE!
"""
warnings.warn(
"Deprecated! The 'plot_ridges_and_transforms' function will be removed in the future GPlately releases. "
"Update your workflow to use the 'plot_ridges' and 'plot_transforms' functions instead, "
"otherwise your workflow will not work with the future GPlately releases.",
DeprecationWarning,
stacklevel=2,
)
logger.debug(
"The 'plot_ridges_and_transforms' function has been changed since GPlately 1.3.0. "
"You need to check your workflow to make sure the new 'plot_ridges_and_transforms' function still suits your purpose. "
"In earlier releases of GPlately, we used an algorithm to identify the 'ridges' and 'transforms' within the gpml:MidOceanRidge features. "
"Unfortunately, the algorithm did not work very well. So we have removed the algorithm and now the 'plot_ridges_and_transforms' function plots all the features "
"which are labelled as gpml:Transform or gpml:MidOceanRidge in the reconstruction model."
) # use logger.debug to make the message less aggressive

self.plot_ridges(ax, color=color, **kwargs)
self.plot_transforms(ax, color=color, **kwargs)

@validate_reconstruction_time
@append_docstring(GET_DATE_DOCSTRING.format("unclassified features"))
def get_unclassified_features(
Expand Down Expand Up @@ -1927,3 +2061,49 @@ def plot_topological_plate_boundaries(self, ax, color="black", **kwargs):
color=color,
**kwargs,
)

@property
def misc_transforms(self):
"""
Deprecated! DO NOT USE.
"""
warnings.warn(
"Deprecated! The 'misc_transforms' property will be removed in the future GPlately releases. "
"Update your workflow to use the 'transforms' property instead, "
"otherwise your workflow will not work with the future GPlately releases.",
DeprecationWarning,
stacklevel=2,
)
return self._transforms

def plot_misc_transforms(self, ax, color="black", **kwargs):
"""
Deprecated! DO NOT USE.
"""
warnings.warn(
"Deprecated! The 'plot_misc_transforms' function will be removed in the future GPlately releases. "
"Update your workflow to use the 'plot_transforms' function instead, "
"otherwise your workflow will not work with the future GPlately releases.",
DeprecationWarning,
stacklevel=2,
)
self.plot_transforms(ax=ax, color=color, **kwargs)

def get_misc_transforms(
self,
central_meridian=0.0,
tessellate_degrees=None,
):
"""
Deprecated! DO NOT USE.
"""
warnings.warn(
"Deprecated! The 'get_misc_transforms' function will be removed in the future GPlately releases. "
"Update your workflow to use the 'get_transforms' function instead, "
"otherwise your workflow will not work with the future GPlately releases.",
DeprecationWarning,
stacklevel=2,
)
return self.get_transforms(
central_meridian=central_meridian, tessellate_degrees=tessellate_degrees
)
5 changes: 3 additions & 2 deletions gplately/utils/plot_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -387,8 +387,9 @@ def _clean_polygons(data, projection):
rects = gpd.GeoDataFrame(
{"geometry": rects},
geometry="geometry",
crs=ccrs.PlateCarree(),
)
# crs=ccrs.PlateCarree(),
crs="EPSG:4326", # Michael Chin changed this to avoid "CRS mismatch" warning. More investigation is needed. what's the difference between EPSG:4326 and ccrs.PlateCarree()?
) # type: ignore
data = data.overlay(rects, how="difference")

projected = data.to_crs(projection)
Expand Down
72 changes: 72 additions & 0 deletions tests-dir/unittest/test_issue_250.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
#!/usr/bin/env python3
import sys, os

os.environ["GPLATELY_DEBUG"] = "1"


import cartopy.crs as ccrs
import matplotlib.pyplot as plt
from common import MODEL_REPO_DIR, save_fig
from plate_model_manager import PlateModelManager

import gplately

print(gplately.__file__)

# https://github.com/GPlates/gplately/issues/250


def main(show=True):
# Call GPlately's PlateModelManager object and request data from the Müller et al. 2019 study
pm_manager = PlateModelManager()
muller2019_model = pm_manager.get_model("Muller2019", data_dir=MODEL_REPO_DIR)
if not muller2019_model:
raise Exception("Failed to get reconstruction model!")
rotation_model = muller2019_model.get_rotation_model()
topology_features = muller2019_model.get_topologies()
static_polygons = muller2019_model.get_static_polygons()

model = gplately.PlateReconstruction(
rotation_model, topology_features, static_polygons
)

# Obtain features for the PlotTopologies object with PlateModelManager
coastlines = muller2019_model.get_layer("Coastlines")
continents = muller2019_model.get_layer("ContinentalPolygons")
COBs = muller2019_model.get_layer("COBs")

# Call the PlotTopologies object
gplot = gplately.plot.PlotTopologies(
model, coastlines=coastlines, continents=continents, COBs=COBs
)

gplot.time = 100 # Ma

fig = plt.figure(figsize=(8, 4))
ax = fig.add_subplot(111, projection=ccrs.Mollweide(190))
ax.set_global() # type: ignore
gplot.plot_ridges_and_transforms(ax, color="red")
gplot.plot_ridges(ax, color="black")
gplot.plot_transforms(ax, color="black")
gplot.plot_misc_transforms(ax, color="black")

print(gplot.ridges)
print(gplot.transforms)
print(gplot.get_ridges_and_transforms())
print(gplot.get_misc_transforms())
print(gplot.get_ridges())
print(gplot.get_transforms())

plt.title(f"{gplot.time} Ma")

if show:
plt.show()
else:
save_fig(__file__)


if __name__ == "__main__":
if len(sys.argv) == 2 and sys.argv[1] == "save":
main(show=False)
else:
main(show=True)
2 changes: 2 additions & 0 deletions tests-dir/unittest/test_plot_with_raster.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ def main(show=True):
# Call GPlately's PlateModelManager object and request data from the Müller et al. 2019 study
pm_manager = PlateModelManager()
muller2019_model = pm_manager.get_model("Muller2019", data_dir=MODEL_REPO_DIR)
if not muller2019_model:
raise Exception("Failed to get reconstruction model!")
rotation_model = muller2019_model.get_rotation_model()
topology_features = muller2019_model.get_topologies()
static_polygons = muller2019_model.get_static_polygons()
Expand Down
Loading