Skip to content

grass.jupyter: Add legend support to InteractiveMap (#5468)#7072

Open
saurabh12nxf wants to merge 8 commits intoOSGeo:mainfrom
saurabh12nxf:legend-feature-5468
Open

grass.jupyter: Add legend support to InteractiveMap (#5468)#7072
saurabh12nxf wants to merge 8 commits intoOSGeo:mainfrom
saurabh12nxf:legend-feature-5468

Conversation

@saurabh12nxf
Copy link
Contributor

@saurabh12nxf saurabh12nxf commented Feb 10, 2026

Added raster legend support to grass.jupyter.InteractiveMap as requested in #5468.

Changes

  • Created legend.py module with parse_colors() and generate_legend_html()
  • Added add_legend() method to InteractiveMap class
  • Supports both folium and ipyleaflet backends
  • Automatically detects categorical vs continuous rasters
  • Professional CSS styling matching Leaflet native controls

Usage

import grass.jupyter as gj

m = gj.InteractiveMap()
m.add_raster("elevation")
m.add_legend("elevation", title="Elevation (m)")
m.show()

Map without legend

Screenshot 2026-02-17 234815

Map with legend

Screenshot 2026-02-17 234923 Screenshot 2026-02-25 231439 Screenshot 2026-02-25 231422

@github-actions github-actions bot added Python Related code is in Python libraries notebook labels Feb 11, 2026
@petrasovaa
Copy link
Contributor

Please include one or more screenshots, thank you.

@saurabh12nxf
Copy link
Contributor Author

Hi @petrasovaa I have updated the description with the screenshots where i have shown the map without legend and map with legend

@veroandreo
Copy link
Contributor

veroandreo commented Feb 19, 2026

Map with legend

Screenshot 2026-02-17 234923

Hey @saurabh12nxf, thanks for your contribution! Just a comment and question: the elevation map has continuous data, as you can see from the values; however, the legend appears as categories. Is that a default behaviour the user needs to change? d.legend in GRASS automatically creates a continuous bar for continuous data and categories for integer data. Why does the elevation map in your screenshot has a categorical legend?

@saurabh12nxf
Copy link
Contributor Author

saurabh12nxf commented Feb 19, 2026

Good catch, thank you! @veroandreo
Currently the legend implementation interprets color rules from r.colors.out as discrete categories, which works correctly for integer rasters but incorrectly for floating point rasters like elevation.
I will update the implementation to detect raster datatype (CELL vs FCELL/DCELL) using raster_info() and render a continuous gradient legend for floating point rasters, similar to d.legend behavior in GRASS.

I will push an update shortly.

Comment on lines 54 to 57
if is_categorical:
label = f"Class {int(value)}"
else:
label = f"{value:g}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[ruff] reported by reviewdog 🐶

Suggested change
if is_categorical:
label = f"Class {int(value)}"
else:
label = f"{value:g}"
label = f"Class {int(value)}" if is_categorical else f"{value:g}"

@saurabh12nxf
Copy link
Contributor Author

saurabh12nxf commented Feb 25, 2026

Thanks for the feedback @veroandreo @petrasovaa

I updated the implementation to detect raster datatype using raster_info() and now the legend behaves like d.legend

• elevation (FCELL/DCELL) - continuous gradient
• landclass96 (CELL) - categorical classes

I also updated the screenshots in the PR description.

Please let me know if anything else should be adjusted .

And I have updated PR description which includes screenshots .

@saurabh12nxf
Copy link
Contributor Author

Hi @petrasovaa and @veroandreo,

Thank you again for all the helpful feedback during this PR! Seeing the legend feature come to life in the notebooks has been really satisfying.

While looking at the codebase, I noticed that since

legend.py
is a brand new module, it doesn't currently have any unit tests covering the HTML generation or the continuous/categorical logic. To ensure this feature remains stable in the future, I would love to write a comprehensive test suite for it using pytest!

I also noticed that the "Improve GRASS user experience in Jupyter Notebook" GSoC 2026 project idea has a Test of Skills that involves writing tests for grass.jupyter. I am planning to apply for this project, and writing test_legend.py feels like the perfect way to add genuine value to the codebase while also fulfilling that requirement.

Would it be okay with you if I open a new PR dedicated to adding the pytest test suite for the legend
module? Let me know if you have any specific testing strategies in mind for grass.jupyter!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

libraries notebook Python Related code is in Python

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants