geeViz.outputLib.charts

Zonal Summary & Charting Library for GEE

geeViz.outputLib.charts provides a Python pipeline for running zonal statistics on ee.Image / ee.ImageCollection objects and producing Plotly charts (time series, bar, sankey). It mirrors the logic in the geeView JS frontend so that both human users and AI agents have a clean, efficient API for this common workflow.

Quick start:

>>> import geeViz.geeView as gv
>>> from geeViz.outputLib import charts as cl
>>> ee = gv.ee
>>> study_area = ee.Geometry.Polygon(
...     [[[-106, 39.5], [-105, 39.5], [-105, 40.5], [-106, 40.5]]]
... )
>>> lcms = ee.ImageCollection("USFS/GTAC/LCMS/v2024-10")
>>> df, fig = cl.summarize_and_chart(
...     lcms.select(['Land_Cover']),
...     study_area,
...     stacked=True,
... )
>>> print(df.to_markdown())
>>> fig.write_html("chart.html", include_plotlyjs="cdn")

See summarize_and_chart() for the full API and more examples.

Module Attributes

CHART_TYPES

Valid chart type strings for chart_type / chart_types parameters.

Functions

chart_bar(df[, colors, title, y_label, ...])

Create a Plotly bar chart from a single-Image zonal stats DataFrame.

chart_donut(df[, colors, title, ...])

Create a Plotly pie/donut chart from a single-Image zonal stats DataFrame.

chart_donut_multi_feature(df[, colors, ...])

Create a subplot grid of pie/donut charts, one per feature.

chart_grouped_bar(df[, colors, title, ...])

Create a grouped (or stacked) bar chart for multi-feature zonal stats.

chart_multi_feature_timeseries(per_feature_dfs)

Create a subplot figure with one time-series chart per feature.

chart_sankey_d3(sankey_df, class_names, ...)

Create a D3 Sankey diagram directly from transition data — no Plotly.

chart_scatter(df, x_band, y_band[, ...])

Create a scatter plot of two bands across features.

chart_time_series(df[, colors, chart_type, ...])

Create a Plotly time series chart from a zonal stats DataFrame.

detect_geometry_type(geometry)

Determine whether the input geometry represents a single region or multiple.

get_obj_info(ee_obj[, band_names, ...])

Detect the type of a GEE object and read its thematic class metadata.

html_to_png(html[, width, height, autocrop])

Render an HTML string to PNG bytes via headless Chrome/Edge screenshot.

parse_continuous_results(raw_dict, obj_info, ...)

Parse continuous (mean/median/percentile/etc.) reduction results into a DataFrame.

parse_thematic_results(raw_dict, obj_info, ...)

Parse frequency histogram reduction results into a DataFrame with class names as columns.

prepare_for_reduction(ee_obj, obj_info[, ...])

Prepare a GEE object for reduction by stacking an ImageCollection into a single multi-band image.

prepare_sankey_data(ee_collection, ...[, ...])

Build a Sankey diagram dataset from class transitions across time periods.

reduce_region(image, geometry, reducer[, ...])

Run image.reduceRegion with sensible defaults.

reduce_regions(image, features, reducer[, ...])

Run image.reduceRegions and return the result as a DataFrame.

sankey_iframe(sankey_html[, width, height])

Wrap sankey D3 HTML in an iframe for Jupyter notebook display.

sankey_to_html(fig[, full_html, ...])

Return sankey HTML, accepting either a raw HTML string or legacy Plotly figure.

save_chart_html(fig, filename[, ...])

Save a chart to an HTML file.

save_chart_png(fig, filename[, width, ...])

Save a Plotly chart or D3 sankey HTML string as a PNG image.

screenshot_url(url[, width, height, ...])

Capture a screenshot of a URL via headless Chrome using the DevTools Protocol (CDP).

summarize_and_chart(ee_obj[, geometry, ...])

Run zonal statistics and produce a chart in one call.

truncate_class_name(name[, max_length])

Truncate a class name preserving the end.

truncate_class_names(names[, max_length])

Apply truncate_class_name() to a list of names.

zonal_stats(ee_obj, geometry[, band_names, ...])

Compute zonal statistics for a GEE Image or ImageCollection over a geometry.

geeViz.outputLib.charts.CHART_TYPES = ['bar', 'stacked_bar', 'line', 'line+markers', 'stacked_line', 'stacked_line+markers', 'pie', 'donut', 'scatter', 'sankey']

Valid chart type strings for chart_type / chart_types parameters. Use these in summarize_and_chart(chart_type=...) or Report.add_section(chart_types=[...]).

geeViz.outputLib.charts.truncate_class_name(name, max_length=30)[source]

Truncate a class name preserving the end.

If name is longer than max_length, keeps the beginning and end with ... in the middle (e.g. "Grass-Forb-...Shrubs-Mix"). Roughly 2/3 of the budget goes to the start, 1/3 to the end.

Parameters:
  • name – The class name string.

  • max_length – Maximum allowed length. None or 0 disables truncation. Default 30.

Returns:

The (possibly truncated) name string.

geeViz.outputLib.charts.truncate_class_names(names, max_length=30)[source]

Apply truncate_class_name() to a list of names.

Parameters:
  • names – List of class name strings.

  • max_length – Maximum allowed length per name. None or 0 disables truncation. Default 30.

Returns:

New list with truncated names.

geeViz.outputLib.charts.save_chart_html(fig, filename, include_plotlyjs='cdn', **kwargs)[source]

Save a chart to an HTML file.

Accepts either a Plotly Figure or an HTML string (from summarize_and_chart(chart_type='sankey')).

Parameters:
  • figplotly.graph_objects.Figure or str (D3 sankey HTML).

  • filename (str) – Output filename (e.g. "chart.html").

  • include_plotlyjs – How to include Plotly.js. Default "cdn".

Returns:

Path to the saved file.

Return type:

str

geeViz.outputLib.charts.screenshot_url(url, width=1280, height=900, wait_seconds=12)[source]

Capture a screenshot of a URL via headless Chrome using the DevTools Protocol (CDP).

Unlike the simple --screenshot flag, this function connects to Chrome’s DevTools WebSocket and collects:

  • JS console errors and warnings (console.error / console.warn)

  • Network failures (net::ERR_* for any request)

  • HTTP 4xx / 5xx responses on EE tile URLs (the most common map-layer bug)

Then takes a screenshot via Page.captureScreenshot after wait_seconds so that async tile requests have time to complete.

Parameters:
  • url (str) – URL to load (http:// or file:///).

  • width (int) – Viewport width in pixels.

  • height (int) – Viewport height in pixels.

  • wait_seconds (int) – Seconds to wait after page load before screenshotting. Longer values allow more EE tiles to load.

Returns:

  • PNG bytes (or None if screenshot failed / no browser found)

  • List of console/network error strings for debugging

Return type:

tuple[bytes | None, list[str]]

Requires websocket-client (pip install websocket-client). Falls back to the simple --screenshot approach (no console capture) if websocket-client is not installed.

geeViz.outputLib.charts.html_to_png(html, width=900, height=1200, autocrop=True)[source]

Render an HTML string to PNG bytes via headless Chrome/Edge screenshot.

Uses a tall viewport and auto-crops empty space at the bottom so the output fits the actual content height.

Parameters:
  • html (str) – Full HTML document string.

  • width (int) – Viewport width in pixels.

  • height (int) – Viewport height in pixels (tall default to avoid cutoff).

  • autocrop (bool) – Trim empty space from the bottom of the image.

Returns:

PNG image bytes, or None if no browser is available.

Return type:

bytes

geeViz.outputLib.charts.save_chart_png(fig, filename, width=900, height=600, theme='dark', bg_color=None, font_color=None)[source]

Save a Plotly chart or D3 sankey HTML string as a PNG image.

Accepts either a Plotly Figure (exported via kaleido) or an HTML string (from summarize_and_chart(chart_type='sankey')), which is rendered via headless Chrome/Edge screenshot.

Parameters:
  • figplotly.graph_objects.Figure or str (D3 sankey HTML).

  • filename (str) – Output filename (e.g. "chart.png").

  • width (int) – Image width in pixels.

  • height (int) – Image height in pixels.

  • theme – Theme preset name or Theme.

  • bg_color – Background color override.

  • font_color – Font/text color override.

Returns:

Path to the saved file.

Return type:

str

Examples

>>> path = cl.save_chart_png(fig, "ndvi_trend.png")
>>> path = cl.save_chart_png(sankey_html, "transitions.png")
geeViz.outputLib.charts.sankey_to_html(fig, full_html=True, include_plotlyjs='cdn', renderer='d3', theme='dark', bg_color=None, font_color=None, hide_toolbar=False)[source]

Return sankey HTML, accepting either a raw HTML string or legacy Plotly figure.

Sankey charts from summarize_and_chart(chart_type='sankey') are now returned as D3 HTML strings directly. This function is kept for backward compatibility — it passes HTML strings through unchanged.

Parameters:
  • fig – D3 HTML string (preferred) or legacy Plotly Figure.

  • full_html (bool) – Ignored for HTML strings.

  • include_plotlyjs – Ignored for HTML strings.

  • renderer (str) – Ignored (always D3).

  • theme – Theme preset for legacy Plotly figures.

  • bg_color – Background color override.

  • font_color – Font/text color override.

  • hide_toolbar (bool) – Hide the download button.

Returns:

HTML string.

Return type:

str

geeViz.outputLib.charts.chart_multi_feature_timeseries(per_feature_dfs, colors=None, chart_type='line+markers', title='Time Series by Feature', x_label='Year', y_label=None, width=800, height=None, columns=2, legend_position='bottom', line_width=2, marker_size=5, max_x_tick_labels=10, max_y_tick_labels=None)[source]

Create a subplot figure with one time-series chart per feature.

Features are arranged in a grid with columns columns (default 2). Each subplot gets height pixels tall (total height scales with number of rows). The legend defaults to "bottom".

Parameters:
  • per_feature_dfs (dict) – {feature_name: DataFrame} from _pivot_multi_feature_timeseries().

  • colors (list, optional) – Hex color strings for each column.

  • chart_type (str, optional) – "line+markers" (default), "line", "bar", "stacked_line", "stacked_line+markers", or "stacked_bar".

  • title (str, optional) – Overall chart title.

  • x_label (str, optional) – X-axis label.

  • y_label (str, optional) – Y-axis label.

  • width (int, optional) – Chart width in pixels.

  • height (int, optional) – Total chart height. When None each subplot gets 400 px.

  • legend_position (dict or str, optional) – Legend layout. Default "bottom".

  • line_width (int or float, optional) – Line width in pixels. Defaults to 2.

  • marker_size (int or float, optional) – Marker diameter in pixels. Defaults to 5.

  • max_x_tick_labels (int, optional) – Maximum number of x-axis tick labels per subplot. Labels are thinned to every 2nd, 5th, 10th, etc. value when exceeded. Defaults to 10. Set to None or 0 to disable.

  • max_y_tick_labels (int, optional) – Maximum number of y-axis tick labels per subplot. Uses Plotly’s nticks. Defaults to None (automatic).

Returns:

plotly.graph_objects.Figure

geeViz.outputLib.charts.get_obj_info(ee_obj, band_names=None, max_class_label_length=30)[source]

Detect the type of a GEE object and read its thematic class metadata.

Parameters:
  • ee_obj (ee.Image or ee.ImageCollection) – The GEE object to inspect.

  • band_names (list or str, optional) – Override the band names to use. Accepts a list ['NDVI', 'NBR'] or a comma-separated string 'NDVI,NBR'. A single string is coerced to a one-element list.

  • max_class_label_length (int, optional) – Maximum length for class name strings. Names longer than this are truncated with ... in the middle, preserving the end (e.g. "Grass-Forb-...Shrubs-Mix"). Set to None or 0 to disable. Default 30.

Returns:

Keys obj_type, band_names, is_thematic, class_info, size.

class_info is {band_name: {class_values, class_names, class_palette}}

Return type:

dict

Examples

Inspect LCMS to see its thematic class metadata:

>>> import geeViz.geeView as gv
>>> from geeViz.outputLib import charts as cl
>>> ee = gv.ee
>>> lcms = ee.ImageCollection("USFS/GTAC/LCMS/v2024-10")
>>> info = cl.get_obj_info(lcms.select(['Land_Cover']))
>>> print(info['is_thematic'])
True
>>> print(info['class_info']['Land_Cover']['class_names'])
['Trees', 'Tall Shrubs & Trees Mix (AK Only)', ...]
geeViz.outputLib.charts.detect_geometry_type(geometry)[source]

Determine whether the input geometry represents a single region or multiple.

Parameters:

geometry – An ee.Geometry, ee.Feature, or ee.FeatureCollection.

Returns:

(geo_type, geometry) where geo_type is 'single' or 'multi',

and geometry is an ee.Geometry (single) or ee.FeatureCollection (multi).

Return type:

tuple

geeViz.outputLib.charts.prepare_for_reduction(ee_obj, obj_info, x_axis_property='system:time_start', date_format='YYYY')[source]

Prepare a GEE object for reduction by stacking an ImageCollection into a single multi-band image.

Parameters:
  • ee_objee.Image or ee.ImageCollection.

  • obj_info (dict) – Output of get_obj_info().

  • x_axis_property (str, optional) – Property name to use for x-axis labels.

  • date_format (str, optional) – Earth Engine date format string (e.g. 'YYYY').

Returns:

(stacked_image, stack_band_names, x_axis_labels)

Return type:

tuple

geeViz.outputLib.charts.reduce_region(image, geometry, reducer, scale=30, crs=None, transform=None, tile_scale=4)[source]

Run image.reduceRegion with sensible defaults.

If both scale and transform are provided, scale is set to None (transform takes precedence in GEE).

Parameters:
  • image (ee.Image) – The image to reduce.

  • geometry – An ee.Geometry or ee.Feature.

  • reducer (ee.Reducer) – The reducer to apply.

  • scale (int, optional) – Pixel scale in meters. Defaults to 30.

  • crs (str, optional) – CRS string. Defaults to None.

  • transform (list, optional) – Affine transform. Defaults to None.

  • tile_scale (int, optional) – Tile scale for parallelism. Defaults to 4.

Returns:

The reduction result dictionary.

Return type:

dict

geeViz.outputLib.charts.reduce_regions(image, features, reducer, scale=30, crs=None, transform=None, tile_scale=4)[source]

Run image.reduceRegions and return the result as a DataFrame.

Parameters:
  • image (ee.Image) – The image to reduce.

  • features (ee.FeatureCollection) – The zones.

  • reducer (ee.Reducer) – The reducer to apply.

  • scale (int, optional) – Pixel scale in meters. Defaults to 30.

  • crs (str, optional) – CRS string. Defaults to None.

  • transform (list, optional) – Affine transform. Defaults to None.

  • tile_scale (int, optional) – Tile scale for parallelism. Defaults to 4.

Returns:

The reduction results.

Return type:

pandas.DataFrame

geeViz.outputLib.charts.parse_thematic_results(raw_dict, obj_info, x_axis_labels, area_format='Percentage', scale=30, split_str='----')[source]

Parse frequency histogram reduction results into a DataFrame with class names as columns.

Parameters:
  • raw_dict (dict) – Output of reduce_region() using frequencyHistogram.

  • obj_info (dict) – Output of get_obj_info().

  • x_axis_labels (list) – Labels for the x-axis (e.g. years).

  • area_format (str, optional) – One of 'Percentage', 'Hectares', 'Acres', 'Pixels'.

  • scale (int, optional) – Pixel scale used in reduction.

  • split_str (str, optional) – Band name separator.

Returns:

Rows are x-axis labels (or a single row for Image),

columns are class names.

Return type:

pandas.DataFrame

geeViz.outputLib.charts.parse_continuous_results(raw_dict, obj_info, x_axis_labels, split_str='----')[source]

Parse continuous (mean/median/percentile/etc.) reduction results into a DataFrame.

Handles single-output reducers (mean, median, sum) — one column per band — and multi-output reducers (percentile, minMax, mean+stdDev combine) — one column per band-output pair.

Parameters:
  • raw_dict (dict) – Output of reduce_region().

  • obj_info (dict) – Output of get_obj_info().

  • x_axis_labels (list) – Labels for the x-axis.

  • split_str (str, optional) – Band name separator.

Returns:

Rows are x-axis labels (or single row). For single-output reducers, columns are band names ("NDVI"). For multi-output reducers, columns are "<band>_<output>" ("TCC_p5", "TCC_p50", "TCC_p95").

Return type:

pandas.DataFrame

geeViz.outputLib.charts.zonal_stats(ee_obj, geometry, band_names=None, reducer=None, scale=30, crs=None, transform=None, tile_scale=4, area_format='Percentage', x_axis_property='system:time_start', date_format='YYYY', include_masked_area=True)[source]

Compute zonal statistics for a GEE Image or ImageCollection over a geometry.

This is the main entry point for the data pipeline. It auto-detects the object type, whether data is thematic or continuous, the appropriate reducer, and the geometry type.

Parameters:
  • ee_objee.Image or ee.ImageCollection.

  • geometryee.Geometry, ee.Feature, or ee.FeatureCollection.

  • band_names (list or str, optional) – Bands to include. Auto-detected if None. Accepts a list ['NDVI', 'NBR'] or a comma-separated string 'NDVI,NBR'.

  • reducer (ee.Reducer, optional) – Override the auto-selected reducer.

  • scale (int, optional) – Pixel scale in meters. Defaults to 30.

  • crs (str, optional) – CRS string.

  • transform (list, optional) – Affine transform.

  • tile_scale (int, optional) – Tile scale for parallelism. Defaults to 4.

  • area_format (str, optional) – Area unit for thematic data. One of 'Percentage', 'Hectares', 'Acres', 'Pixels'.

  • x_axis_property (str, optional) – Property for x-axis labels (ImageCollection).

  • date_format (str, optional) – Date format string for x-axis labels.

Returns:

The zonal statistics table.

Return type:

pandas.DataFrame

Examples

Get just the data (no chart) for an LCMS land cover time series:

>>> import geeViz.geeView as gv
>>> from geeViz.outputLib import charts as cl
>>> ee = gv.ee
>>> study_area = ee.Geometry.Polygon(
...     [[[-106, 39.5], [-105, 39.5], [-105, 40.5], [-106, 40.5]]]
... )
>>> lcms = ee.ImageCollection("USFS/GTAC/LCMS/v2024-10")
>>> df = cl.zonal_stats(
...     lcms.select(['Land_Cover']),
...     study_area,
...     area_format='Percentage',
... )
>>> print(df.to_markdown())

Continuous data with a custom reducer:

>>> df = cl.zonal_stats(
...     lcms.select(['Change_Raw_Probability_Slow_Loss']),
...     study_area,
...     reducer=ee.Reducer.mean(),
... )
geeViz.outputLib.charts.prepare_sankey_data(ee_collection, band_name, transition_periods, class_info, geometry, scale=30, crs=None, transform=None, tile_scale=4, area_format='Percentage', min_percentage=0.2)[source]

Build a Sankey diagram dataset from class transitions across time periods.

For agent / LLM use: DO NOT call this function directly or copy its internal patterns (e.g. .rename(["from"]), manual band extraction by year). Always call cl.summarize_and_chart(ic, geometry, band_names='<band>', chart_type='sankey', transition_periods=[year1, year2, year3], scale=100) which delegates here with the correct setup. This function is an internal helper exposed for advanced custom workflows only.

For each consecutive pair of periods, this function:

  1. Filters the collection to each period

  2. Computes the mode for each period

  3. Creates a transition image encoding {from}0990{to}

  4. Runs frequencyHistogram to count transitions

  5. Parses results into both a source/target/value DataFrame and a transition matrix DataFrame

Parameters:
  • ee_collection (ee.ImageCollection) – The input collection.

  • band_name (str) – The thematic band to analyze.

  • transition_periods (list) – List of [start_year, end_year] pairs.

  • class_info (dict) – Class info dict for the band (from get_obj_info()).

  • geometryee.Geometry or ee.Feature.

  • scale (int, optional) – Pixel scale in meters.

  • crs (str, optional) – CRS string.

  • transform (list, optional) – Affine transform.

  • tile_scale (int, optional) – Tile scale for parallelism.

  • area_format (str, optional) – Area unit.

  • min_percentage (float, optional) – Minimum percentage threshold for including a flow in the source-target table. The transition matrix always includes all observed transitions regardless of this threshold.

Returns:

(sankey_df, matrix_dict)

  • sankey_df (pandas.DataFrame): Source-target-value table with columns source, target, value, source_name, target_name, source_color, target_color, period. Flows below min_percentage are excluded.

  • matrix_dict (dict[str, pandas.DataFrame]): One transition matrix per consecutive period pair, keyed by "{from_period} {to_period}". Each DataFrame has class names as both row and column labels, with values as converted counts.

Return type:

tuple

Examples

Typically called via summarize_and_chart(chart_type='sankey'), but can be used directly for custom sankey workflows:

>>> import geeViz.geeView as gv
>>> from geeViz.outputLib import charts as cl
>>> ee = gv.ee
>>> study_area = ee.Geometry.Polygon(
...     [[[-106, 39.5], [-105, 39.5], [-105, 40.5], [-106, 40.5]]]
... )
>>> lcms = ee.ImageCollection("USFS/GTAC/LCMS/v2024-10")
>>> info = cl.get_obj_info(lcms.select(['Land_Use']))
>>> sankey_df, matrix_dict = cl.prepare_sankey_data(
...     lcms.select(['Land_Use']),
...     'Land_Use',
...     transition_periods=[[1990, 2000], [2000, 2010], [2010, 2023]],
...     class_info=info['class_info']['Land_Use'],
...     geometry=study_area,
...     scale=30,
... )
>>> print(sankey_df.head().to_markdown())
>>> for label, mdf in matrix_dict.items():
...     print(f"\n{label}")
...     print(mdf.to_markdown())
geeViz.outputLib.charts.chart_time_series(df, colors=None, chart_type='line+markers', title='Time Series', x_label='Year', y_label=None, width=800, height=600, label_max_length=30, legend_position='right', line_width=2, marker_size=5, max_x_tick_labels=10, max_y_tick_labels=None)[source]

Create a Plotly time series chart from a zonal stats DataFrame.

Parameters:
  • df (pandas.DataFrame) – Output of zonal_stats() for an ImageCollection. Index = x-axis labels, columns = data series.

  • colors (list, optional) – Hex color strings for each column.

  • chart_type (str, optional) – "line+markers" (default), "line", "bar", "stacked_line", "stacked_line+markers", or "stacked_bar".

  • title (str, optional) – Chart title.

  • x_label (str, optional) – X-axis label.

  • y_label (str, optional) – Y-axis label.

  • width (int, optional) – Chart width in pixels.

  • height (int, optional) – Chart height in pixels.

  • label_max_length (int, optional) – Max characters for legend labels.

  • legend_position (dict or str, optional) – Plotly legend layout dict (e.g. {"orientation": "h", "x": 0.5, "y": -0.1}), or "right"/None for the Plotly default.

  • line_width (int or float, optional) – Line width in pixels for line/scatter traces. Defaults to 2.

  • marker_size (int or float, optional) – Marker diameter in pixels for traces that include markers. Defaults to 5.

  • max_x_tick_labels (int, optional) – Maximum number of x-axis tick labels to display. When the number of x values exceeds this, labels are thinned to every 2nd, 5th, 10th, etc. value. Defaults to 10. Set to None or 0 to disable.

  • max_y_tick_labels (int, optional) – Maximum number of y-axis tick labels. Uses Plotly’s nticks. Defaults to None (automatic).

Returns:

plotly.graph_objects.Figure

Examples

Build a time series chart from a zonal_stats DataFrame:

>>> import geeViz.geeView as gv
>>> from geeViz.outputLib import charts as cl
>>> ee = gv.ee
>>> study_area = ee.Geometry.Polygon(
...     [[[-106, 39.5], [-105, 39.5], [-105, 40.5], [-106, 40.5]]]
... )
>>> lcms = ee.ImageCollection("USFS/GTAC/LCMS/v2024-10")
>>> # Step 1: get the data
>>> info = cl.get_obj_info(lcms.select(['Land_Cover']))
>>> df = cl.zonal_stats(
...     lcms.select(['Land_Cover']), study_area,
... )
>>> # Step 2: chart it with class colors
>>> colors = info['class_info']['Land_Cover']['class_palette']
>>> fig = cl.chart_time_series(
...     df, colors=colors,
...     title='LCMS Land Cover',
...     y_label='% Area',
... )
>>> fig.show()
geeViz.outputLib.charts.chart_bar(df, colors=None, title='Class Distribution', y_label=None, max_classes=30, chart_type='bar', width=800, height=600, legend_position='right')[source]

Create a Plotly bar chart from a single-Image zonal stats DataFrame.

Automatically chooses horizontal or vertical orientation based on label length.

Parameters:
  • df (pandas.DataFrame) – Output of zonal_stats() for a single Image. Single row, columns = class names.

  • colors (list, optional) – Hex color strings for each bar.

  • title (str, optional) – Chart title.

  • y_label (str, optional) – Value axis label.

  • max_classes (int, optional) – Maximum number of classes to display.

  • width (int, optional) – Chart width in pixels.

  • height (int, optional) – Chart height in pixels.

  • legend_position (dict or str, optional) – Plotly legend layout dict (e.g. {"orientation": "h", "x": 0.5, "y": -0.1}), or "right"/None for the Plotly default.

Returns:

plotly.graph_objects.Figure

Examples

Bar chart of NLCD land cover for a single image:

>>> import geeViz.geeView as gv
>>> from geeViz.outputLib import charts as cl
>>> ee = gv.ee
>>> study_area = ee.Geometry.Polygon(
...     [[[-106, 39.5], [-105, 39.5], [-105, 40.5], [-106, 40.5]]]
... )
>>> nlcd = ee.ImageCollection(
...     "USGS/NLCD_RELEASES/2021_REL/NLCD"
... ).select(['landcover']).mode().set(
...     ee.ImageCollection("USGS/NLCD_RELEASES/2021_REL/NLCD")
...     .first().toDictionary()
... )
>>> info = cl.get_obj_info(nlcd)
>>> df = cl.zonal_stats(nlcd, study_area)
>>> colors = info['class_info']['landcover']['class_palette']
>>> fig = cl.chart_bar(
...     df, colors=colors, title='NLCD Land Cover',
... )
>>> fig.show()
geeViz.outputLib.charts.chart_donut(df, colors=None, title='Class Distribution', max_classes=30, width=800, height=600, legend_position='right', hole=0.45)[source]

Create a Plotly pie/donut chart from a single-Image zonal stats DataFrame.

Use hole=0 for a pie chart or hole=0.45 (default) for a donut.

Only valid for thematic (categorical) data from a single ee.Image. Raises ValueError for continuous data or ee.ImageCollection inputs.

Parameters:
  • df (pandas.DataFrame) – Output of zonal_stats() for a single Image. Single row, columns = class names, values = area/%.

  • colors (list, optional) – Hex color strings, one per class.

  • title (str, optional) – Chart title.

  • max_classes (int, optional) – Maximum number of classes to display. Smaller classes are grouped into “Other”. Defaults to 30.

  • width (int, optional) – Chart width in pixels.

  • height (int, optional) – Chart height in pixels.

  • legend_position (dict or str, optional) – Plotly legend dict or "right" / "bottom".

  • hole (float, optional) – Size of the centre hole (0–1). Defaults to 0.45.

Returns:

plotly.graph_objects.Figure

geeViz.outputLib.charts.chart_donut_multi_feature(df, colors=None, title='Class Distribution by Feature', max_classes=30, width=800, height=600, columns=2, legend_position='bottom', hole=0.45)[source]

Create a subplot grid of pie/donut charts, one per feature.

Use hole=0 for pie charts or hole=0.45 (default) for donuts.

For multi-feature reduceRegions output where the DataFrame index is the feature label and columns are class names.

Parameters:
  • df (pandas.DataFrame) – Output of zonal_stats() with feature_label set. Index = feature names, columns = class names, values = area/%.

  • colors (list, optional) – Hex color strings, one per class.

  • title (str, optional) – Overall chart title.

  • max_classes (int, optional) – Max classes per donut.

  • width (int, optional) – Chart width in pixels.

  • height (int, optional) – Chart height in pixels.

  • columns (int, optional) – Number of subplot columns.

  • legend_position (dict or str, optional) – Legend position.

  • hole (float, optional) – Centre hole size.

Returns:

plotly.graph_objects.Figure

geeViz.outputLib.charts.chart_scatter(df, x_band, y_band, feature_label=None, title='Scatter Plot', width=800, height=600, legend_position='right', trendline=True, opacity=0.7, show_labels=None, thematic_col=None, class_names=None, class_palette=None, class_values=None)[source]

Create a scatter plot of two bands across features.

Each point represents one feature (e.g. a county, fire perimeter, or watershed). The x- and y-axes show the mean (or other reduced) value of two image bands over that feature.

When thematic_col is provided, points are colored by the thematic class value in that column, using the class palette and names from image properties.

Parameters:
  • df (pandas.DataFrame) – DataFrame with at least two numeric columns for the x and y bands. Optionally a thematic_col column with integer class values.

  • x_band (str) – Column name for the x-axis.

  • y_band (str) – Column name for the y-axis.

  • feature_label (str, optional) – Name of the index (used in hover).

  • title (str, optional) – Chart title.

  • width (int, optional) – Chart width in pixels.

  • height (int, optional) – Chart height in pixels.

  • legend_position (dict or str, optional) – Legend position.

  • trendline (bool, optional) – Draw a linear trendline. Defaults to True.

  • opacity (float, optional) – Point opacity (0-1). Lower values help visualize overlapping points. Defaults to 0.7.

  • show_labels (bool, optional) – Label each point with the feature name. When None (default), labels are shown only when the DataFrame has fewer than 30 rows.

  • thematic_col (str, optional) – Column containing thematic class values used to color each point. Defaults to None.

  • class_names (list, optional) – Class name strings matching class_values.

  • class_palette (list, optional) – Hex color strings matching class_values.

  • class_values (list, optional) – Integer class values that map to class_names and class_palette.

Returns:

plotly.graph_objects.Figure

geeViz.outputLib.charts.chart_grouped_bar(df, colors=None, title='Zonal Summary by Feature', y_label=None, chart_type='bar', width=800, height=600, legend_position='right')[source]

Create a grouped (or stacked) bar chart for multi-feature zonal stats.

Each group on the x-axis is a feature (row) and each bar/segment within the group is a class (column). This is the natural chart type when reduceRegions returns one row per zone.

Parameters:
  • df (pandas.DataFrame) – Rows = features (index used as labels), columns = class names, values = numeric area/percentage.

  • colors (list, optional) – Hex color strings, one per column (class).

  • title (str, optional) – Chart title.

  • y_label (str, optional) – Y-axis label.

  • stacked (bool, optional) – Stack bars instead of grouping. Defaults to False.

  • width (int, optional) – Chart width in pixels.

  • height (int, optional) – Chart height in pixels.

  • legend_position (dict or str, optional) – Plotly legend layout dict (e.g. {"orientation": "h", "x": 0.5, "y": -0.1}), or "right"/None for the Plotly default.

Returns:

plotly.graph_objects.Figure

Examples

Compare land cover across the 5 largest MTBS fire perimeters:

>>> import geeViz.geeView as gv
>>> from geeViz.outputLib import charts as cl
>>> ee = gv.ee
>>> lcms = ee.ImageCollection("USFS/GTAC/LCMS/v2024-10")
>>> fires = ee.FeatureCollection(
...     "USFS/GTAC/MTBS/burned_area_boundaries/v1"
... ).sort("BurnBndAc", False).limit(5)
>>> lc_mode = lcms.select(["Land_Cover"]).mode().set(
...     lcms.first().toDictionary()
... )
>>> # summarize_and_chart handles reduceRegions + grouped bar:
>>> df, fig = cl.summarize_and_chart(
...     lc_mode, fires,
...     feature_label="Incid_Name",
...     title="Land Cover — 5 Largest Fires",
...     stacked=True, width=800,
... )
>>> fig.show()
geeViz.outputLib.charts.chart_sankey_d3(sankey_df, class_names, class_palette, transition_periods, title='Class Transitions', width=800, height=600, node_thickness=20, node_pad=15, opacity=0.9, theme='dark', bg_color=None, font_color=None, hide_toolbar=False)[source]

Create a D3 Sankey diagram directly from transition data — no Plotly.

Builds a self-contained HTML string with native SVG linearGradient elements so each link fades from its source node color to its target node color. Uses d3-sankey for layout.

This is the preferred rendering path for Sankey charts. Unlike chart_sankey() (which builds a Plotly figure that must be post-processed by sankey_to_html() for gradients), this function goes straight from the raw sankey_df to D3 HTML.

Parameters:
  • sankey_df (pandas.DataFrame) – Output of prepare_sankey_data(). Columns: source, target, value, source_name, target_name, source_color, target_color.

  • class_names (list) – List of class names.

  • class_palette (list) – List of hex color strings.

  • transition_periods (list) – Period list (for node labeling).

  • title (str, optional) – Chart title. Defaults to "Class Transitions".

  • width (int, optional) – Chart width in pixels.

  • height (int, optional) – Chart height in pixels.

  • node_thickness (int, optional) – Sankey node bar thickness.

  • node_pad (int, optional) – Padding between Sankey nodes.

  • opacity (float, optional) – Link opacity (0-1). Defaults to 0.9.

  • theme (str, optional) – Theme preset. Defaults to "dark".

  • bg_color (str, optional) – Background color override.

  • font_color (str, optional) – Font color override.

  • hide_toolbar (bool, optional) – Hide the download button.

Returns:

Self-contained HTML string with embedded D3 Sankey chart.

Return type:

str

Examples

>>> sankey_df, matrix_dict = cl.prepare_sankey_data(
...     lcms.select(['Land_Use']), 'Land_Use',
...     transition_periods=[1990, 2005, 2023],
...     class_info=info['class_info'], geometry=study_area,
... )
>>> html = cl.chart_sankey_d3(
...     sankey_df, info['class_info']['Land_Use']['class_names'],
...     info['class_info']['Land_Use']['class_palette'],
...     transition_periods=[1990, 2005, 2023],
... )
geeViz.outputLib.charts.sankey_iframe(sankey_html, width=None, height=None)[source]

Wrap sankey D3 HTML in an iframe for Jupyter notebook display.

Jupyter sanitizes <script> tags in display(HTML(...)), so D3 sankey charts must be embedded in an iframe. Uses a data:text/html;base64 src for maximum compatibility across Jupyter environments (classic notebook, JupyterLab, VS Code).

Parameters:
  • sankey_html (str) – Full HTML string from chart_sankey_d3() or summarize_and_chart(chart_type='sankey').

  • width (int, optional) – Iframe width in pixels. Auto-detected from the HTML when None.

  • height (int, optional) – Iframe height in pixels. Auto-detected from the HTML when None.

Returns:

HTML <iframe> element suitable for display(HTML(...)).

Return type:

str

Example

>>> from IPython.display import HTML, display
>>> display(HTML(cl.sankey_iframe(sankey_html)))
geeViz.outputLib.charts.summarize_and_chart(ee_obj, geometry=None, band_names=None, reducer=None, scale=30, crs=None, transform=None, tile_scale=4, area_format='Percentage', x_axis_property='system:time_start', date_format='YYYY', title=None, chart_type=None, sankey=False, transition_periods=None, sankey_band_name=None, min_percentage=0.2, palette=None, feature_label=None, width=800, height=600, opacity=0.9, legend_position='right', columns=2, include_masked_area=True, stacked=None, thematic_band_name=None, line_width=2, marker_size=5, class_visible=None, max_x_tick_labels=10, max_y_tick_labels=None, max_class_label_length=30, x_label=None, y_label=None, **_compat)[source]

Run zonal statistics and produce a chart in one call.

Orchestrates zonal_stats() (or prepare_sankey_data()) and the appropriate chart function. The chart type is chosen automatically:

  • ee.ImageCollection -> line chart (default "line+markers").

  • ee.Image -> bar chart (default "bar").

  • chart_type=”pie” -> pie chart (Image + thematic only).

  • chart_type=”donut” -> donut chart (pie with center hole; Image + thematic only).

  • chart_type=”scatter” -> scatter plot (Image + FeatureCollection only; uses 2 continuous bands as x/y axes, optionally colored by thematic_band_name).

  • chart_type=”sankey” -> Sankey transition diagram.

  • feature_label + ee.FeatureCollection + ee.Image -> grouped bar or per-feature pie/donut chart.

  • feature_label + ee.FeatureCollection + ee.ImageCollection -> per-feature time series subplots.

Parameters:
  • ee_objee.Image or ee.ImageCollection.

  • geometryee.Geometry, ee.Feature, or ee.FeatureCollection.

  • band_names (list or str, optional) – Bands to include. Accepts a list ['Land_Use'] or a comma-separated string 'Land_Use'. Auto-detected if None.

  • reducer (ee.Reducer, optional) – Override the auto-selected reducer.

  • scale (int, optional) – Pixel scale in meters.

  • crs (str, optional) – CRS string.

  • transform (list, optional) – Affine transform.

  • tile_scale (int, optional) – Tile scale for parallelism.

  • area_format (str, optional) – Area unit for thematic data.

  • x_axis_property (str, optional) – Property for x-axis labels.

  • date_format (str, optional) – Date format string.

  • title (str, optional) – Chart title. Auto-generated if None.

  • chart_type (str, optional) – Chart type. One of "bar", "stacked_bar", "pie" / "donut" (Image + thematic only), "scatter" (Image + FeatureCollection only), "sankey" (ImageCollection + thematic, requires transition_periods), "histogram" (distribution of a continuous band; for ee.Image produces a themed bar chart with bucket centers on x and counts on y, for ee.ImageCollection produces a heatmap with time on x, bucket centers on y, and per-year percent as color — auto-routes a default ee.Reducer.histogram(maxBuckets=50) when no reducer is given; pass reducer=ee.Reducer.histogram(maxBuckets=N) for finer binning), "line", "stacked_line", "line+markers" (default for ImageCollection), or "stacked_line+markers". Defaults to "bar" for single ee.Image, "line+markers" for ee.ImageCollection.

  • stacked (bool, optional) – Deprecated — use chart_type instead. When True, prepends "stacked_" to chart_type. Defaults to None.

  • sankey (bool, optional) – Deprecated — use chart_type='sankey' instead. Still accepted for backward compatibility.

  • transition_periods (list, optional) – Period list for Sankey.

  • sankey_band_name (str, optional) – Band for Sankey analysis.

  • min_percentage (float, optional) – Minimum percentage for Sankey flows.

  • palette (list, optional) – Hex color strings for each series/band. Overrides auto-detected class palette when provided.

  • feature_label (str, optional) – Property name to use as row labels when the geometry is a multi-feature ee.FeatureCollection. Triggers the reduceRegions path. For ee.Image input produces a grouped bar chart; for ee.ImageCollection input produces per-feature time series subplots.

  • width (int, optional) – Chart width in pixels (per cell for multi-feature subplots).

  • height (int, optional) – Chart height in pixels (per cell for multi-feature subplots).

  • opacity (float, optional) – Opacity for Sankey nodes and links (0-1). Defaults to 0.9.

  • legend_position (dict or str, optional) – Plotly legend layout dict for non-Sankey charts (e.g. {"orientation": "h", "x": 0.5, "y": -0.1}), or "right"/None for the Plotly default.

  • columns (int, optional) – Number of subplot columns for multi-feature time series. Total width/height scale to n_cols * width / n_rows * height. Defaults to 2.

  • include_masked_area (bool, optional) – When True (default) and using the histogram reducer, unmasked pixels with value 0 are included so percentages are relative to the total area, not just the unmasked portion. The sentinel class is removed from results.

  • thematic_band_name (str, optional) – For chart_type="scatter" only. Name of a thematic band in the image whose mode value per feature is used to color each scatter point. The image must carry {band}_class_values, {band}_class_names, and {band}_class_palette properties for the colors and legend entries. Defaults to None (single-color points).

  • line_width (int or float, optional) – Line width in pixels for time series traces. Defaults to 2.

  • marker_size (int or float, optional) – Marker diameter in pixels for time series traces. Defaults to 5.

  • class_visible (dict, optional) –

    Per-class visibility control. Maps class names to booleans. Classes set to False are toggled off in the chart legend (set to "legendonly"). The traces remain in the figure — users can click the legend to re-enable them. Useful for hiding background, no-data, or stable classes by default. Works for all chart paths including single-geometry, multi-feature time series subplots, and multi-feature bar/pie/donut charts. Example:

    class_visible={
        "Non-Processing Area Mask": False,
        "Stable": False,
        "Background": False,
    }
    

    When None (default), all classes are visible.

  • max_x_tick_labels (int, optional) – Maximum number of x-axis tick labels. When the data has more x values than this, tick labels are thinned to every 2nd, 5th, 10th, etc. value. Defaults to 10. Set to None or 0 to show all.

  • max_y_tick_labels (int, optional) – Maximum number of y-axis tick labels. Passed as Plotly’s nticks. Defaults to None (Plotly automatic).

  • max_class_label_length (int, optional) – Maximum length for class name strings in legends and labels. Longer names are truncated with ... in the middle, preserving the end. Set to None or 0 to disable. Default 30.

Returns:

Depends on chart type:

  • Standard (single geometry): {"df": DataFrame, "chart": Figure}

  • Sankey: {"df": sankey_df, "chart": sankey_html, "matrix": matrix_dict} where sankey_html is a D3 HTML string (display with display(HTML(cl.sankey_iframe(sankey_html)))), and matrix_dict is {period_label: DataFrame}

  • Multi-feature + ee.Image (bar/pie/donut): {"df": DataFrame, "chart": Figure}

  • Multi-feature + ee.ImageCollection: {"df": dict, "chart": Figure} where dict is {feature_name: DataFrame}

  • Scatter: {"df": DataFrame, "chart": Figure} where the DataFrame has columns for the two bands (and optionally the thematic band)

Return type:

dict

Examples

Stacked time series of thematic land cover (auto-detects class properties from the image collection):

>>> import geeViz.geeView as gv
>>> from geeViz.outputLib import charts as cl
>>> ee = gv.ee
>>> study_area = ee.Geometry.Polygon(
...     [[[-106, 39.5], [-105, 39.5], [-105, 40.5], [-106, 40.5]]]
... )
>>> lcms = ee.ImageCollection("USFS/GTAC/LCMS/v2024-10")
>>> result = cl.summarize_and_chart(
...     lcms.select(['Land_Cover']),
...     study_area,
...     title='LCMS Land Cover',
...     stacked=True,
... )
>>> print(result["df"].to_markdown())
>>> result["chart"].write_html("lcms_land_cover.html", include_plotlyjs="cdn")

Sankey transition diagram with D3 gradient-colored links:

>>> result = cl.summarize_and_chart(
...     lcms.select(['Land_Use']),
...     study_area,
...     chart_type='sankey',
...     transition_periods=[1990, 2000, 2024],
...     sankey_band_name='Land_Use',
...     min_percentage=0.5,
... )
>>> # In notebooks: display(HTML(cl.sankey_iframe(result["chart"])))
>>> # Save to file:
>>> cl.save_chart_html(result["chart"], "land_use_transitions.html")

Bar chart for a single image at a point (use ee.Reducer.first()):

>>> nlcd = ee.Image("USGS/NLCD_RELEASES/2021_REL/NLCD/2021")
>>> point = ee.Geometry.Point([-104.99, 39.74])
>>> result = cl.summarize_and_chart(
...     nlcd,
...     point,
...     reducer=ee.Reducer.first(),
...     scale=30,
...     title='NLCD Land Cover',
... )

Continuous time series (non-thematic bands auto-select ee.Reducer.mean()):

>>> import geeViz.getImagesLib as gil
>>> composites = gil.getLandsatWrapper(
...     study_area, 2000, 2024
... )['composites']
>>> result = cl.summarize_and_chart(
...     composites,
...     study_area,
...     band_names=['nir', 'swir1', 'swir2'],
...     title='Spectral Band Means',
...     palette=['D0D', '0DD', 'DD0'],
... )

Grouped bar chart comparing multiple features (uses reduceRegions internally):

>>> fires = ee.FeatureCollection(
...     "USFS/GTAC/MTBS/burned_area_boundaries/v1"
... )
>>> top5 = fires.sort("BurnBndAc", False).limit(5)
>>> lc_mode = lcms.select(["Land_Cover"]).mode().set(
...     lcms.first().toDictionary()
... )
>>> result = cl.summarize_and_chart(
...     lc_mode,
...     top5,
...     feature_label="Incid_Name",
...     title="Land Cover — 5 Largest MTBS Fires",
...     stacked=True,
...     width=800,
... )

Thematic data without class properties — force frequencyHistogram or set properties on-the-fly:

>>> lcpri = ee.ImageCollection(
...     "projects/sat-io/open-datasets/LCMAP/LCPRI"
... ).select(['b1'], ['LC'])
>>> # Force thematic (class values used as labels):
>>> result = cl.summarize_and_chart(
...     lcpri,
...     study_area,
...     reducer=ee.Reducer.frequencyHistogram(),
...     title='LCMAP LC Primary',
... )
>>> # Or set properties for proper names and colors:
>>> lcpri_named = lcpri.map(lambda img: img.set({
...     'LC_class_values': list(range(1, 10)),
...     'LC_class_names': ['Developed', 'Cropland', 'Grass/Shrub',
...         'Tree Cover', 'Water', 'Wetlands', 'Ice/Snow',
...         'Barren', 'Class Change'],
...     'LC_class_palette': ['E60000', 'A87000', 'E3E3C2', '1D6330',
...         '476BA1', 'BAD9EB', 'FFFFFF', 'B3B0A3', 'A201FF'],
... }))
>>> result = cl.summarize_and_chart(
...     lcpri_named, study_area, stacked=True,
... )

Switch area format to hectares or acres:

>>> result_ha = cl.summarize_and_chart(
...     lcms.select(['Land_Cover']),
...     study_area,
...     area_format='Hectares',
...     title='LCMS Land Cover (Hectares)',
... )