Skip to content

grass.jupyter: Created attracting and elegant legend feature for rasters in InteractiveMap.#7077

Open
shahid-rahaman wants to merge 6 commits intoOSGeo:mainfrom
shahid-rahaman:elegant-legend-for-rasters
Open

grass.jupyter: Created attracting and elegant legend feature for rasters in InteractiveMap.#7077
shahid-rahaman wants to merge 6 commits intoOSGeo:mainfrom
shahid-rahaman:elegant-legend-for-rasters

Conversation

@shahid-rahaman
Copy link

@shahid-rahaman shahid-rahaman commented Feb 11, 2026

Created legend feature for rasters with easy to use commands. Gave it a transparent glass frost look for better UX.
@wenzeslaus If looks good to you, I will write the tests for the corresponding entities.
Let me know if requires any adjustments.
For instance, use it like below.

from grass.jupyter import InteractiveMap
m = InteractiveMap(height=700, width = 1000)
raster = "aspect"
m.add_raster(raster)
m.add_legend(raster, color = "viridis", position = "bottomleft")
m.show()

Viridis Style Position bottom left
bottomleft

from grass.jupyter import InteractiveMap
m = InteractiveMap(height=700, width = 1000)
raster = "aspect"
m.add_raster(raster)
m.add_legend(raster, color = "viridis", position = "bottomright")
m.show()

Viridis Style Position bottom right
viridis

from grass.jupyter import InteractiveMap
m = InteractiveMap(height=700, width = 1000)
raster = "aspect"
m.add_raster(raster)
m.add_legend(raster, color = "magma", position = "bottomright")
m.show()

Magma Style Position bottom right
magma

You can use any colors like sepia, rainbow, etc. apart from viridis and magma.

regards

@shahid-rahaman shahid-rahaman force-pushed the elegant-legend-for-rasters branch from f0b4776 to e568a16 Compare February 11, 2026 22:36
@wenzeslaus
Copy link
Member

Reproducible code examples and screenshots?

@github-actions github-actions bot added Python Related code is in Python libraries notebook labels Feb 12, 2026
@shahid-rahaman
Copy link
Author

shahid-rahaman commented Feb 12, 2026

Reproducible code examples and screenshots?

@wenzeslaus Sorry for late reply, I have updated the description. I have been facing some dev system crash. Is there any standard way to setup dev environment? I somehow did but it was quirky, I installed grass from local repo by ./configure>make -j10> sudo make install, but it throws few errors sometimes due to which I cannot run local test/checks as it is not installed properly. Am I missing something or is there any documentation to setup dev enviroment?
regards

@shahid-rahaman shahid-rahaman changed the title Created attracting and elegant legend feature for rasters. grass.jupyter: Created attracting and elegant legend feature for rasters in InteractiveMap. Feb 12, 2026
Copy link
Member

@wenzeslaus wenzeslaus left a comment

Choose a reason for hiding this comment

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

Well, the legend in screenshots does look attractive and elegant.

Anyway, you should still make the title shorter and more technical.

More work is needed here and I did not test myself yet, but I like the direction here.

Comment on lines 472 to 473
from branca.element import MacroElement
from jinja2 import Template
Copy link
Member

Choose a reason for hiding this comment

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

This will need to be handled more carefully. Neither of those is our current direct dependency. jinja2 is used here and there for special things (like tests and addon tools) and we likely bring that in with folium (I did not check). However, branca is not the most active project and it is still <1.0.

border: 1px solid rgba(0, 0, 0, 0.2);
border-radius: 12px;
color: black;
font-family: 'Segoe UI', Arial, sans-serif;
Copy link
Member

Choose a reason for hiding this comment

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

There is no user-specified font anywhere here? There is a font used for the 2D rendering (for the non-interactive Map). There is always GRASS_FONT env var which is meant for display/rendering so this would not be too far from the current use.

https://grass.osgeo.org/grass-devel/manuals/variables.html

Copy link
Author

Choose a reason for hiding this comment

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

@wenzeslaus There is indeed a fontcap file which contain fonts, but how to use them. I can make provision for user specified fonts but for instance if the font is "cyrilic", we cannot put it in CSS, these are .hmp files. Am I missing something?
regards

Copy link
Member

Choose a reason for hiding this comment

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

See actual usages of GRASS_FONT in the code (now I see the doc does not include all cases), but yes, if it is a local path, then don't pass it here.

@shahid-rahaman
Copy link
Author

@wenzeslaus Thanks for your time, I understand there are some preferred approach for GRASS, I used python core logics and module to make it work, like "subprocess", I had used it several times personally. However, I will try align the code with the existing dependencies and preferred approach.
regards

@wenzeslaus
Copy link
Member

Please, sync up to the main branch.

Then, the following link will allow reviewers to test without the need to have the changes locally:

https://mybinder.org/v2/gh/shahid-rahaman/grass/elegant-legend-for-rasters?urlpath=lab%2Ftree%2Fdoc%2Fexamples%2Fnotebooks%2Fjupyter_example.ipynb

@wenzeslaus
Copy link
Member

I didn't get any legend on mobile.

@shahid-rahaman shahid-rahaman force-pushed the elegant-legend-for-rasters branch from 605bd3f to 939dcaf Compare February 14, 2026 21:05
@shahid-rahaman shahid-rahaman force-pushed the elegant-legend-for-rasters branch from 939dcaf to cbb28e3 Compare February 14, 2026 21:08
from branca.element import MacroElement
import folium
from folium.map import MacroElement
from jinja2 import Template
Copy link
Author

Choose a reason for hiding this comment

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

@wenzeslaus You earlier worried about these dependencies, but both of them are the dependencies of of folium itself See.
Folium has the following dependencies, all of which are installed automatically with the above installation commands:
branca, Jinja2, Numpy ,Requests
However, I avoided branca, as it is not very active but we can use jinja as it is already installed. Folium internally calles Template() method of jinja to create Template object. And this object is essential as it further calls for module attribute associate with this object. So, far it is working fine as expected as per my observation.

Copy link
Member

Choose a reason for hiding this comment

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

This looks good. While it is not bulletproof, transitive vs direct dependency makes as difference.

@shahid-rahaman
Copy link
Author

shahid-rahaman commented Feb 14, 2026

I didn't get any legend on mobile.

@wenzeslaus I have not checked it on the phones yet. I am not sure if phones allows html code to be injected the same way. Although, in chrome dev tool when I toggle the device to be iphone 12 or responsive, I see the legend there. So, I think it is the not the resolution problem.
regards

@shahid-rahaman
Copy link
Author

@wenzeslaus Any update here?

Copy link
Member

@wenzeslaus wenzeslaus left a comment

Choose a reason for hiding this comment

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

I like this and I get the legend in Binder at least with a desktop. However, there is couple of issues here:

  1. Now, I always get a viridis. The logic is wrong.
Image
  1. The gradient values taken from GRASS may not work as is with CSS as visible in the image - at least in Firefox (see above).
  2. Your previous examples were much better. Also add geology. Make them copy-pastable to the notebooked used in Binder. Test your result as if you would be a user. Use a tutorial.
  3. See my other comments.

Just a heads-up that this starts to feel like interacting with AI. Please, review our AI policy in the contributing file.

self.layer_control_object = self._ipyleaflet.LayersControl(**kwargs)

def add_legend(self, raster, color="viridis", position="bottomright"):
from grass.tools import Tools
Copy link
Member

Choose a reason for hiding this comment

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

Top-level import. For, mpl, I'm not sure. How we import it elsewhere?

Copy link
Author

Choose a reason for hiding this comment

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

mpl is redundant here, I used it for a different approach to create gradient color strip but the approach was not dyanamic later I forgot to remove it. For grass tools I think importing here is good, importing only when legend is required. ALthough, wherever you say I will place it.

Comment on lines +389 to +390
# Setting color scheme for raster rendering
tools.r_colors(map=raster, color=color)
Copy link
Member

Choose a reason for hiding this comment

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

This is problematic. With the current way how color tables are handled, this needs to be done by the caller, not here.

Comment on lines +393 to +395
# Joining the retrieved colors to form smooth gradient.
colors = [item["color"] for item in color_maps]
css_gradient = ", ".join(colors)
Copy link
Member

Choose a reason for hiding this comment

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

This now does not have the previous functionality. Illogical, too much AI?

colors = [item["color"] for item in color_maps]
css_gradient = ", ".join(colors)
# Setting some interval ticks to compare values relatively.
info = tools.r_info(map=raster, format='json')
Copy link
Member

Choose a reason for hiding this comment

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

Explore what you get with the r_colors_out call, r_info, and r_univar for the range of values for different computational regions (after changing the extent by g_region/g.region user-level call to change the extent which will change what values are considered). This influenced whether or not the r_info call here appropriate.

font-weight: bold;
color: black;
">
{info["title"]}
Copy link
Member

Choose a reason for hiding this comment

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

This needs a fallback to the previously used name.

@shahid-rahaman
Copy link
Author

I like this and I get the legend in Binder at least with a desktop. However, there is couple of issues here:

  1. Now, I always get a viridis. The logic is wrong.
Image 2. The gradient values taken from GRASS may not work as is with CSS as visible in the image - at least in Firefox (see above). 3. Your previous examples were much better. Also add geology. Make them copy-pastable to the notebooked used in Binder. Test your result as if you would be a user. Use a tutorial. 4. See my other comments.

Just a heads-up that this starts to feel like interacting with AI. Please, review our AI policy in the contributing file.

@wenzeslaus Thanks for review. Very first thing I don't trust AI generated code they are very often poorly strcutured and redundant. Although I use LLMs for code review and guidance, like where to find the appropriate info in the documentation. Now, lets, come to your concerns.

  1. Firstly, I think that is rainbow color scheme. Rainbow produces such vibrant colors.
rainbow But I dont know why binder shows "rainbow" colors as you have not provided any color arguments and default is "viridis". And there is indeed a bug I am working on that is when a color scheme is applied it is somehow saved in cache or something(I am not sure yet) but when you refresh the same cell it renders the rasters with desired colors. I should see some binder usage.

@wenzeslaus
Copy link
Member

Firstly, I think that is rainbow color scheme. Rainbow produces such vibrant colors.

The geology map in that sample dataset has colors already set (it may be indeed rainbow). Importantly, the resulting color table is not a gradient one. The values are categorical. Run r.report to understand more about this specific data. Compare to elevation. Aspect may be yet a little different.

But I dont know why binder shows "rainbow" colors as you have not provided any color arguments and default is "viridis".

viridis is used as a default internally. No work from users or in your code is needed. I hope this clarifies that.

And there is indeed a bug I am working on that is when a color scheme is applied it is somehow saved in cache or something(I am not sure yet) but when you refresh the same cell it renders the rasters with desired colors.

The color table is saved with the data in the GRASS project, so in a way, r.colors call modifies the data (or let's say visualization metadata), so that's why you should not be using it in the code (you should have an example which uses that, though). Generate new data with r.surf.fractal to get an example for that use case.

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.

2 participants