Skip to content
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

feat: create plotting methods for Image object #342

Merged
merged 9 commits into from
Oct 21, 2024
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
2 changes: 1 addition & 1 deletion docs/usage/plot/plot-featurecollection.ipynb

Large diffs are not rendered by default.

670 changes: 666 additions & 4 deletions docs/usage/plot/plot-image.ipynb

Large diffs are not rendered by default.

142 changes: 4 additions & 138 deletions geetools/FeatureCollection.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,11 @@

import ee
import geopandas as gpd
import numpy as np
from matplotlib import pyplot as plt
from matplotlib.axes import Axes
from matplotlib.colors import to_rgba

from .accessors import register_class_accessor
from .utils import plot_data


@register_class_accessor(ee.FeatureCollection, "geetools")
Expand Down Expand Up @@ -295,13 +294,11 @@ def plot_by_features(
# get the data from server
data = self.byProperties(featureId, props, labels).getInfo()

# reorder the data according to the lapbes or properties set by the user
# reorder the data according to the labels or properties set by the user
labels = labels if labels else props.getInfo()
data = {k: data[k] for k in labels}

return self._plot(
type=type, data=data, label_name=featureId, colors=colors, ax=ax, **kwargs
)
return plot_data(type=type, data=data, label_name=featureId, colors=colors, ax=ax, **kwargs)

def plot_by_properties(
self,
Expand Down Expand Up @@ -355,9 +352,7 @@ def plot_by_properties(
labels = labels if labels else props.getInfo()
data = {f: {k: data[f][k] for k in labels} for f in data.keys()}

return self._plot(
type=type, data=data, label_name=featureId, colors=colors, ax=ax, **kwargs
)
return plot_data(type=type, data=data, label_name=featureId, colors=colors, ax=ax, **kwargs)

def plot_hist(
self,
Expand Down Expand Up @@ -426,135 +421,6 @@ def plot_hist(

return ax

@staticmethod
def _plot(
type: str,
data: dict,
label_name: str,
colors: list = [],
ax: Axes | None = None,
**kwargs,
) -> Axes:
"""Plotting mechanism used in all the plotting functions.

It binds the matplotlib capabilities with the data aggregated either by feature or by properties.
the shape of the data should as follows:

.. code-block::

{
"label1": {"properties1": value1, "properties2": value2, ...}
"label2": {"properties1": value1, "properties2": value2, ...},
...
}

Args:
type: The type of plot to use. can be any type of plot from the python lib `matplotlib.pyplot`. If the one you need is missing open an issue!
data: the data to use as inputs of the graph. please follow the fomrmat specified in the documentation.
label_name: The name of the property that was used to generate the labels
property_names: The list of names that was used to name the values. They will be used to order the keys of the data dictionary.
colors: A list of colors to use for the plot. If not provided, the default colors from the matplotlib library will be used.
ax: The matplotlib axes to use. If not provided, the plot will be send to a new figure.
kwargs: Additional arguments from the ``pyplot`` chat type selected.
"""
# define the ax if not provided by the user
if ax is None:
fig, ax = plt.subplots()

# gather the data from parameters
labels = list(data.keys())
props = list(data[labels[0]].keys())
colors = colors if colors else plt.get_cmap("tab10").colors

# draw the chart based on the type
if type == "plot":
for i, label in enumerate(labels):
kwargs["color"] = colors[i]
name = props[0] if len(props) == 1 else "Properties values"
values = list(data[label].values())
ax.plot(props, values, label=label, **kwargs)
ax.set_ylabel(name)
ax.set_xlabel(f"Features (labeled by {label_name})")

elif type == "scatter":
for i, label in enumerate(labels):
kwargs["color"] = colors[i]
name = props[0] if len(props) == 1 else "Properties values"
values = list(data[label].values())
ax.scatter(props, values, label=label, **kwargs)
ax.set_ylabel(name)
ax.set_xlabel(f"Features (labeled by {label_name})")

elif type == "fill_between":
for i, label in enumerate(labels):
kwargs["facecolor"] = to_rgba(colors[i], 0.2)
kwargs["edgecolor"] = to_rgba(colors[i], 1)
name = props[0] if len(props) == 1 else "Properties values"
values = list(data[label].values())
ax.fill_between(props, values, label=label, **kwargs)
ax.set_ylabel(name)
ax.set_xlabel(f"Features (labeled by {label_name})")

elif type == "bar":
x = np.arange(len(props))
width = 1 / (len(labels) + 0.8)
margin = width / 10
kwargs["width"] = width - margin
ax.set_xticks(x + width * len(labels) / 2, props)
for i, label in enumerate(labels):
kwargs["color"] = colors[i]
values = list(data[label].values())
ax.bar(x + width * i, values, label=label, **kwargs)

elif type == "stacked":
x = np.arange(len(props))
bottom = np.zeros(len(props))
for i, label in enumerate(labels):
kwargs.update(color=colors[i], bottom=bottom)
values = list(data[label].values())
ax.bar(x, values, label=label, **kwargs)
bottom += values

elif type == "pie":
if len(labels) != 1:
raise ValueError("Pie chart can only be used with one property")
kwargs["autopct"] = kwargs.get("autopct", "%1.1f%%")
kwargs["normalize"] = kwargs.get("normalize", True)
kwargs["labeldistance"] = kwargs.get("labeldistance", None)
kwargs["wedgeprops"] = kwargs.get("wedgeprops", {"edgecolor": "w"})
kwargs["textprops"] = kwargs.get("textprops", {"color": "w"})
kwargs.update(autopct="%1.1f%%", colors=colors)
values = [data[labels[0]][p] for p in props]
ax.pie(values, labels=props, **kwargs)

elif type == "donut":
if len(labels) != 1:
raise ValueError("Pie chart can only be used with one property")
kwargs["autopct"] = kwargs.get("autopct", "%1.1f%%")
kwargs["normalize"] = kwargs.get("normalize", True)
kwargs["labeldistance"] = kwargs.get("labeldistance", None)
kwargs["wedgeprops"] = kwargs.get("wedgeprops", {"width": 0.6, "edgecolor": "w"})
kwargs["textprops"] = kwargs.get("textprops", {"color": "w"})
kwargs["pctdistance"] = kwargs.get("pctdistance", 0.7)
kwargs.update(autopct="%1.1f%%", colors=colors)
values = [data[labels[0]][p] for p in props]
ax.pie(values, labels=props, **kwargs)

else:
raise ValueError(f"Type {type} is not (yet?) supported")

# customize the layout of the axis
ax.grid(axis="y")
ax.set_axisbelow(True)
ax.spines["top"].set_visible(False)
ax.spines["right"].set_visible(False)
ax.legend(bbox_to_anchor=(1.02, 1), loc="upper left")

# make sure the canvas is only rendered once.
ax.figure.canvas.draw_idle()

return ax

def plot(
self,
ax: Axes,
Expand Down
Loading