Skip to content

Commit

Permalink
issue-250-ridge-transforms (#298)
Browse files Browse the repository at this point in the history
* First commit

Search for "hjt" in the file; you'll see I've added lines of code in four different places.

* Second commit

moved the two functions to the end of the file and fixed the indentation.
added"ridge_transforms" as property(return self.ridges + self.transforms), similar to this https://github.com/GPlates/gplately/blob/bea66aaff046fa7d08e33c94c41b63695f8ecbe9/gplately/plot.py#L383-L387

* logger.debug added

* add "ridge_transforms" attribute

* fixing property

* Replace Automatic Update with Explicit Time Validation in ridge_transforms

* add misc_transforms

* added warning messages using logger.debug()

* Replace plot.py with its original version

* All Nine Comments Addressed

* fix some code

* bring back misc_transforms

* add unittest script

* get rid of crs mismatch warning

---------

Co-authored-by: michaelchin <[email protected]>
  • Loading branch information
Hojat-Shirmard and michaelchin authored Feb 3, 2025
1 parent ca4a898 commit 62bda7c
Show file tree
Hide file tree
Showing 4 changed files with 266 additions and 11 deletions.
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

0 comments on commit 62bda7c

Please sign in to comment.