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

shiny + geemap #1992

Closed
cidreNMBU opened this issue Apr 25, 2024 · 4 comments
Closed

shiny + geemap #1992

cidreNMBU opened this issue Apr 25, 2024 · 4 comments
Labels
Feature Request New feature or request

Comments

@cidreNMBU
Copy link

Hello,

I am an R user who has been using the rgee package for a while, but unfortunately, it might not be longer maintained (see this issue. One thing that was great about rgee was the possibility of creating Shiny Applications using R Shiny thanks to the effort of Cesar of creating this template which unfortunately is no longer working.

However, Shiny for Python was released less than 2 years ago and I think it would be a good chance to create applications that use geemap in the backend. The issue is that it's not very straightforward to develop a Shiny Application that uses geemap because it's necesary to use a token or other authentication way.

I was wondering if you are interested in creating an automated pipeline like the one that was created for rgee (pipeline to deploy).

Best regards,
Cidre

@cidreNMBU cidreNMBU added the Feature Request New feature or request label Apr 25, 2024
@giswqs
Copy link
Member

giswqs commented Apr 25, 2024

@cidreNMBU Thank you for submitting the feature request. I have not followed the development of rgee for a while and I am surprised and sad to see that rgee is no longer actively maintained. I am not an R user and I don't know if I would be able to develop and maintain a shiny app for geemap.

However, I have recently discovered the py-maplibregl package, which supports Rshiny integration. I am working on adding the maplibre support for leafmap. I guess it should work for geemap as well. I will see if I can get a proof of concept soon.

Contributions are always welcome.

from maplibre import Map, MapContext, output_maplibregl, render_maplibregl
from maplibre.controls import Marker
from shiny import App, reactive, ui

app_ui = ui.page_fluid(
    output_maplibregl("maplibre", height=600),
    ui.div("Click on map to set a marker"),
)


def server(input, output, session):
    @render_maplibregl
    def maplibre():
        m = Map()
        return m

    @reactive.Effect
    @reactive.event(input.maplibre)
    async def coords():
        async with MapContext("maplibre") as m:
            print(input.maplibre())
            lng_lat = tuple(input.maplibre()["coords"].values())
            marker = Marker(lng_lat=lng_lat)
            m.add_marker(marker)
            m.add_call("flyTo", {"center": lng_lat})


app = App(app_ui, server)

if __name__ == "__main__":
    app.run()

@cidreNMBU
Copy link
Author

Thank you for your quick answer @giswqs.

Actually, there's support and fantastic integration of ipyleaflet through the shinywidgets package. My feature request is related to support the deployment of the application maybe through shinyapps.io. Here I have a reproducible example of a simple app:

import ee
from shiny import App, reactive, render, ui
from shinywidgets import output_widget, render_widget
from faicons import icon_svg

ee.Initialize()

app_ui = ui.page_sidebar(
    ui.sidebar(
        ui.input_action_button(
            'button',
            'Generate map',
            icon = icon_svg('play')
        )
    ),
    ui.layout_column_wrap(
       output_widget('result')
    ),
    title    = "Test geemap",
    fillable = True,
)


def server(input, output, session):

    @reactive.calc
    @reactive.event(input.button)
    def generate_map():

        Map = geemap.Map(center=[40, -100], zoom=4)
        
        landsat7 = (
              ee.Image('LANDSAT/LE7_TOA_5YEAR/1999_2003')
              .select(['B1', 'B2', 'B3', 'B4', 'B5', 'B7'])
        )
        
        landsat_vis = {'bands': ['B4', 'B3', 'B2'], 'gamma': 1.4}
        
        Map.addLayer(landsat7, landsat_vis, "Landsat")
        
        hyperion = ee.ImageCollection('EO1/HYPERION').filter(ee.Filter.date('2016-01-01', '2017-03-01'))
        
        hyperion_vis = {
             'min': 1000.0,
             'max': 14000.0,
             'gamma': 2.5,
             }
        Map.addLayer(hyperion, hyperion_vis, 'Hyperion')

        return Map
    
    @output
    @render_widget
    def result():
        return generate_map()


app = App(app_ui, server)

The issue arises because when deploying the app it looks for the credentials file. In rgee this was the template content for deployment:

# 1. Create the credentials file
ee_Initialize()

# 2. Copy credentials file to the project folder
file_credentials <- sprintf("%s/credentials", dirname(rgee::ee_get_earthengine_path()))
file.copy(file_credentials, to = ".")

# 3. Set ShinyApps account info
# FIRST MODIFY LOCAL .Renviron!!
error_on_missing_name <- function(name){
  var <- Sys.getenv(name, unset=NA)
  if(is.na(var)){
    stop(paste0("cannot find ",name),call. = FALSE)
  }
  gsub("\"", '',var)
}

rsconnect::setAccountInfo(name   = error_on_missing_name("SHINY_ACC_NAME"),
               token  = error_on_missing_name("TOKEN"),
               secret = error_on_missing_name("SECRET"))


# 4. Run the application
rsconnect::deployApp(
  appFiles = c("app.R", "utils.R", "credentials"),
  appTitle = "rgee_app_demo",
  lint = FALSE
)

# 5. Delete EE credentials file
file.remove("credentials")

I suppose something similar can be achieved with geemap . I think the steps consist on:

  1. Get credentials file info
  2. Create a temporary copy on the Shiny App directory
  3. Set up the account information of shinyapps.io using environment variables
  4. Run the app
  5. Delete the copy of the credentials file

I am not sure if rsconnect for python has these built-in functions to create a deploy template like this one. As far as I know, the rsconnect package in Python uses the command line to deploy apps, but I am still a beginner in Python to know how to do this. So, do you think that it would be possible and a good idea to work in a template that helps to deploy an app with security (keeping account info in environment variables and managing credentials)?

@giswqs
Copy link
Member

giswqs commented Apr 26, 2024

Great to hear that shinywidgets support ipyleaflet. Does shiny app supports setting environment? If yes, set the environment EARTHENGINE_TOKEN to the contents of the credentials file. geemap should be able to retrieve the env variable and generate the credentials file. See example at https://github.com/opengeos/solara-geemap

@giswqs
Copy link
Member

giswqs commented Jun 15, 2024

Here is an example for using geemap with shinywidgets.

https://huggingface.co/spaces/giswqs/shiny-geemap
image

Steps

  1. Follow the instructions here to create an Earth Engine service account.
  2. Create a private key for the service account
  3. Download the JSON key file
  4. Open the JSON key file and copy the entire content of the file and set it as the value for the environment vaiable EARTHENGINE_TOKEN
  5. Use geemap.ee_initialize(service_account=True) in your code to authenticate EE. Do NOT use ee.Authenticate() or ee.Initialize() in your code.

Notes

It apppears that shinywidgets have some issues. It can't render the geemap.Map(), but it works for ipyleaflet.Map(). Nevertheless, you can see the the EE authentication is no longer a problem. The EE image tile layer can be added the ipyleaflet map properly.

Error on client while running Shiny app
Class ToggleButtonStyleModel not found in module @jupyter-widgets/[email protected]

@giswqs giswqs closed this as completed Jun 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature Request New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants