chart module¶
Module for creating charts for Earth Engine data.
BarChart (BaseChartClass)
¶
Create Bar Chart. All histogram/bar charts can use this object.
Source code in geemap/chart.py
class BarChart(BaseChartClass):
"""Create Bar Chart. All histogram/bar charts can use this object."""
def __init__(self, features, default_labels, name, type="grouped", **kwargs):
super().__init__(features, default_labels, name, **kwargs)
self.type = type
def generate_tooltip(self):
if (self.xlabel is not None) and (self.ylabel is not None):
self.bar_chart.tooltip = Tooltip(
fields=["x", "y"], labels=[self.xlabel, self.ylabel]
)
else:
self.bar_chart.tooltip = Tooltip(fields=["x", "y"])
def get_ylim(self):
if self.ylim:
ylim_min, ylim_max = self.ylim[0], self.ylim[1]
else:
if self.name in ["feature.byFeature", "feature.byProperty"]:
ylim_min = np.min(self.y_data)
ylim_max = np.max(self.y_data) + 0.2 * (
np.max(self.y_data) - np.min(self.y_data)
)
if self.name in ["feature.groups"]:
ylim_min = np.min(self.df[self.yProperty])
ylim_max = np.max(self.df[self.yProperty])
ylim_max = ylim_max + 0.2 * (ylim_max - ylim_min)
return (ylim_min, ylim_max)
def plot_chart(self):
fig = plt.figure(
title=self.title,
legend_location=self.legend_location,
)
self.bar_chart = plt.bar(
self.x_data,
self.y_data,
labels=self.labels,
display_legend=self.display_legend,
)
self.generate_tooltip()
plt.ylim(*self.get_ylim())
plt.xlabel(self.xlabel)
plt.ylabel(self.ylabel)
if self.width:
fig.layout.width = self.width
if self.height:
fig.layout.height = self.height
self.bar_chart.colors = self.colors
self.bar_chart.type = self.type
plt.show()
BaseChartClass
¶
This should include everything a chart module requires to plot figures.
Source code in geemap/chart.py
class BaseChartClass:
"""This should include everything a chart module requires to plot figures."""
def __init__(self, features, default_labels, name, **kwargs):
self.ylim = None
self.xlim = None
self.title = ""
self.legend_location = "top-left"
self.layout_width = None
self.layout_height = None
self.display_legend = True
self.xlabel = None
self.ylabel = None
self.labels = default_labels
self.width = None
self.height = None
self.colors = "black"
self.name = name
if isinstance(features, ee.FeatureCollection):
self.df = ee_to_df(features)
elif isinstance(features, pd.DataFrame):
self.df = features
for key, value in kwargs.items():
setattr(self, key, value)
@classmethod
def get_data(self):
pass
@classmethod
def plot_chart(self):
pass
def __repr__(self):
return self.name
Feature_ByFeature (BarChart)
¶
A object to define variables and get_data method.
Source code in geemap/chart.py
class Feature_ByFeature(BarChart):
"""A object to define variables and get_data method."""
def __init__(
self, features, xProperty, yProperties, name="feature.byFeature", **kwargs
):
default_labels = yProperties
super().__init__(features, default_labels, name, **kwargs)
self.x_data, self.y_data = self.get_data(xProperty, yProperties)
def get_data(self, xProperty, yProperties):
x_data = list(self.df[xProperty])
y_data = list(self.df[yProperties].values.T)
return x_data, y_data
Feature_ByProperty (BarChart)
¶
A object to define variables and get_data method.
Source code in geemap/chart.py
class Feature_ByProperty(BarChart):
"""A object to define variables and get_data method."""
def __init__(
self, features, xProperties, seriesProperty, name="feature.byProperty", **kwargs
):
default_labels = None
super().__init__(features, default_labels, name, **kwargs)
if "labels" in kwargs:
raise Exception("Please remove labels in kwargs and try again.")
self.labels = list(self.df[seriesProperty])
self.x_data, self.y_data = self.get_data(xProperties)
def get_data(self, xProperties):
if isinstance(xProperties, list):
x_data = xProperties
y_data = self.df[xProperties].values
elif isinstance(xProperties, dict):
x_data = list(xProperties.values())
y_data = self.df[list(xProperties.keys())].values
else:
raise Exception("xProperties must be a list or dictionary.")
return x_data, y_data
Feature_Groups (BarChart)
¶
A object to define variables and get_data method.
Source code in geemap/chart.py
class Feature_Groups(BarChart):
"""A object to define variables and get_data method."""
def __init__(
self,
features,
xProperty,
yProperty,
seriesProperty,
name="feature.groups",
type="stacked",
**kwargs,
):
df = ee_to_df(features)
self.unique_series_values = df[seriesProperty].unique().tolist()
default_labels = [str(x) for x in self.unique_series_values]
self.yProperty = yProperty
super().__init__(features, default_labels, name, type, **kwargs)
self.new_column_names = self.get_column_names(seriesProperty, yProperty)
self.x_data, self.y_data = self.get_data(xProperty, self.new_column_names)
def get_column_names(self, seriesProperty, yProperty):
new_column_names = []
for value in self.unique_series_values:
sample_filter = (self.df[seriesProperty] == value).map({True: 1, False: 0})
column_name = str(yProperty) + "_" + str(value)
self.df[column_name] = self.df[yProperty] * sample_filter
new_column_names.append(column_name)
return new_column_names
def get_data(self, xProperty, new_column_names):
x_data = list(self.df[xProperty])
y_data = [self.df[x] for x in new_column_names]
return x_data, y_data
feature_byFeature(features, xProperty, yProperties, **kwargs)
¶
Generates a Chart from a set of features. Plots the value of one or more properties for each feature. Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturebyfeature
Parameters:
Name | Type | Description | Default |
---|---|---|---|
features |
ee.FeatureCollection |
The feature collection to generate a chart from. |
required |
xProperty |
str |
Features labeled by xProperty. |
required |
yProperties |
list |
Values of yProperties. |
required |
Exceptions:
Type | Description |
---|---|
Exception |
Errors when creating the chart. |
Source code in geemap/chart.py
def feature_byFeature(
features: ee.FeatureCollection, xProperty: str, yProperties: list, **kwargs
):
"""Generates a Chart from a set of features. Plots the value of one or more properties for each feature.
Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturebyfeature
Args:
features (ee.FeatureCollection): The feature collection to generate a chart from.
xProperty (str): Features labeled by xProperty.
yProperties (list): Values of yProperties.
Raises:
Exception: Errors when creating the chart.
"""
bar = Feature_ByFeature(
features=features, xProperty=xProperty, yProperties=yProperties, **kwargs
)
try:
bar.plot_chart()
except Exception as e:
raise Exception(e)
feature_byProperty(features, xProperties, seriesProperty, **kwargs)
¶
Generates a Chart from a set of features. Plots property values of one or more features. Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturebyproperty
Parameters:
Name | Type | Description | Default |
---|---|---|---|
features |
ee.FeatureCollection |
The features to include in the chart. |
required |
xProperties |
list | dict |
One of (1) a list of properties to be plotted on the x-axis; or (2) a (property, label) dictionary specifying labels for properties to be used as values on the x-axis. |
required |
seriesProperty |
str |
The name of the property used to label each feature in the legend. |
required |
Exceptions:
Type | Description |
---|---|
Exception |
If the provided xProperties is not a list or dict. |
Exception |
If the chart fails to create. |
Source code in geemap/chart.py
def feature_byProperty(
features: ee.FeatureCollection,
xProperties: Union[list, dict],
seriesProperty: str,
**kwargs,
):
"""Generates a Chart from a set of features. Plots property values of one or more features.
Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturebyproperty
Args:
features (ee.FeatureCollection): The features to include in the chart.
xProperties (list | dict): One of (1) a list of properties to be plotted on the x-axis; or
(2) a (property, label) dictionary specifying labels for properties to be used as values on the x-axis.
seriesProperty (str): The name of the property used to label each feature in the legend.
Raises:
Exception: If the provided xProperties is not a list or dict.
Exception: If the chart fails to create.
"""
bar = Feature_ByProperty(
features=features,
xProperties=xProperties,
seriesProperty=seriesProperty,
**kwargs,
)
try:
bar.plot_chart()
except Exception as e:
raise Exception(e)
feature_groups(features, xProperty, yProperty, seriesProperty, **kwargs)
¶
Generates a Chart from a set of features. Plots the value of one property for each feature. Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturegroups
Parameters:
Name | Type | Description | Default |
---|---|---|---|
features |
ee.FeatureCollection |
The feature collection to make a chart from. |
required |
xProperty |
str |
Features labeled by xProperty. |
required |
yProperty |
str |
Features labeled by yProperty. |
required |
seriesProperty |
str |
The property used to label each feature in the legend. |
required |
Exceptions:
Type | Description |
---|---|
Exception |
Errors when creating the chart. |
Source code in geemap/chart.py
def feature_groups(features, xProperty, yProperty, seriesProperty, **kwargs):
"""Generates a Chart from a set of features.
Plots the value of one property for each feature.
Reference:
https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturegroups
Args:
features (ee.FeatureCollection): The feature collection to make a chart from.
xProperty (str): Features labeled by xProperty.
yProperty (str): Features labeled by yProperty.
seriesProperty (str): The property used to label each feature in the legend.
Raises:
Exception: Errors when creating the chart.
"""
bar = Feature_Groups(
features=features,
xProperty=xProperty,
yProperty=yProperty,
seriesProperty=seriesProperty,
**kwargs,
)
try:
bar.plot_chart()
except Exception as e:
raise Exception(e)
feature_histogram(features, property, maxBuckets=None, minBucketWidth=None, show=True, **kwargs)
¶
Generates a Chart from a set of features. Computes and plots a histogram of the given property. - X-axis = Histogram buckets (of property value). - Y-axis = Frequency
Reference: https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturehistogram
Parameters:
Name | Type | Description | Default |
---|---|---|---|
features |
(ee.FeatureCollection |
The features to include in the chart. |
required |
property |
(str |
The name of the property to generate the histogram for. |
required |
maxBuckets |
(int |
The maximum number of buckets (bins) to use when building a histogram; will be rounded up to a power of 2. |
None |
minBucketWidth |
float |
The minimum histogram bucket width, or null to allow any power of 2. |
None |
show |
bool |
Whether to show the chart. If not, it will return the bqplot chart object, which can be used to retrieve data for the chart. Defaults to True. |
True |
Exceptions:
Type | Description |
---|---|
Exception |
If the provided xProperties is not a list or dict. |
Exception |
If the chart fails to create. |
Source code in geemap/chart.py
def feature_histogram(
features, property, maxBuckets=None, minBucketWidth=None, show=True, **kwargs
):
"""
Generates a Chart from a set of features.
Computes and plots a histogram of the given property.
- X-axis = Histogram buckets (of property value).
- Y-axis = Frequency
Reference:
https://developers.google.com/earth-engine/guides/charts_feature#uichartfeaturehistogram
Args:
features (ee.FeatureCollection): The features to include in the chart.
property (str): The name of the property to generate the histogram for.
maxBuckets (int, optional): The maximum number of buckets (bins) to use when building a histogram;
will be rounded up to a power of 2.
minBucketWidth (float, optional): The minimum histogram bucket width, or null to allow any power of 2.
show (bool, optional): Whether to show the chart. If not, it will return the bqplot chart object, which can be used to retrieve data for the chart. Defaults to True.
Raises:
Exception: If the provided xProperties is not a list or dict.
Exception: If the chart fails to create.
"""
import math
if not isinstance(features, ee.FeatureCollection):
raise Exception("features must be an ee.FeatureCollection")
first = features.first()
props = first.propertyNames().getInfo()
if property not in props:
raise Exception(
f"property {property} not found. Available properties: {', '.join(props)}"
)
def nextPowerOf2(n):
return pow(2, math.ceil(math.log2(n)))
def grow_bin(bin_size, ref):
while bin_size < ref:
bin_size *= 2
return bin_size
try:
raw_data = pd.to_numeric(
pd.Series(features.aggregate_array(property).getInfo())
)
y_data = raw_data.tolist()
if "ylim" in kwargs:
min_value = kwargs["ylim"][0]
max_value = kwargs["ylim"][1]
else:
min_value = raw_data.min()
max_value = raw_data.max()
data_range = max_value - min_value
if not maxBuckets:
initial_bin_size = nextPowerOf2(data_range / pow(2, 8))
if minBucketWidth:
if minBucketWidth < initial_bin_size:
bin_size = grow_bin(minBucketWidth, initial_bin_size)
else:
bin_size = minBucketWidth
else:
bin_size = initial_bin_size
else:
initial_bin_size = math.ceil(data_range / nextPowerOf2(maxBuckets))
if minBucketWidth:
if minBucketWidth < initial_bin_size:
bin_size = grow_bin(minBucketWidth, initial_bin_size)
else:
bin_size = minBucketWidth
else:
bin_size = initial_bin_size
start_bins = (math.floor(min_value / bin_size) * bin_size) - (bin_size / 2)
end_bins = (math.ceil(max_value / bin_size) * bin_size) + (bin_size / 2)
if start_bins < min_value:
y_data.append(start_bins)
else:
y_data[y_data.index(min_value)] = start_bins
if end_bins > max_value:
y_data.append(end_bins)
else:
y_data[y_data.index(max_value)] = end_bins
num_bins = math.floor((end_bins - start_bins) / bin_size)
if "title" not in kwargs:
title = ""
else:
title = kwargs["title"]
fig = plt.figure(title=title)
if "width" in kwargs:
fig.layout.width = kwargs["width"]
if "height" in kwargs:
fig.layout.height = kwargs["height"]
if "xlabel" not in kwargs:
xlabel = ""
else:
xlabel = kwargs["xlabel"]
if "ylabel" not in kwargs:
ylabel = ""
else:
ylabel = kwargs["ylabel"]
histogram = plt.hist(
sample=y_data,
bins=num_bins,
axes_options={"count": {"label": ylabel}, "sample": {"label": xlabel}},
)
if "colors" in kwargs:
histogram.colors = kwargs["colors"]
if "stroke" in kwargs:
histogram.stroke = kwargs["stroke"]
else:
histogram.stroke = "#ffffff00"
if "stroke_width" in kwargs:
histogram.stroke_width = kwargs["stroke_width"]
else:
histogram.stroke_width = 0
if ("xlabel" in kwargs) and ("ylabel" in kwargs):
histogram.tooltip = Tooltip(
fields=["midpoint", "count"],
labels=[kwargs["xlabel"], kwargs["ylabel"]],
)
else:
histogram.tooltip = Tooltip(fields=["midpoint", "count"])
if show:
plt.show()
else:
return histogram
except Exception as e:
raise Exception(e)