diff --git a/.github/workflows/kaban.yml b/.github/workflows/kaban.yml index e5a13cb7..bc805954 100644 --- a/.github/workflows/kaban.yml +++ b/.github/workflows/kaban.yml @@ -17,4 +17,4 @@ jobs: uses: srggrs/assign-one-project-github-action@1.2.1 if: github.event.action == 'opened' with: - project: "https://github.com/12rambau/sepal_ui/projects/4" + project: "https://github.com/12rambau/sepal_ui/projects/5" diff --git a/.github/workflows/unit.yml b/.github/workflows/unit.yml index d7e9d8cf..5b3a0b12 100644 --- a/.github/workflows/unit.yml +++ b/.github/workflows/unit.yml @@ -9,7 +9,9 @@ on: env: PLANET_API_CREDENTIALS: ${{ secrets.PLANET_API_CREDENTIALS }} PLANET_API_KEY: ${{ secrets.PLANET_API_KEY }} - EARTHENGINE_TOKEN: ${{ secrets.EARTHENGINE_TOKEN }} + EARTHENGINE_TOKEN: ${{ secrets.EARTHENGINE_SERVICE_ACCOUNT }} + EARTHENGINE_SERVICE_ACCOUNT: ${{ secrets.EARTHENGINE_SERVICE_ACCOUNT }} + EARTHENGINE_PROJECT: ${{ secrets.EARTHENGINE_PROJECT }} jobs: lint: diff --git a/sepal_ui/frontend/css/custom.css b/sepal_ui/frontend/css/custom.css index 4da5c3df..6dbda56f 100644 --- a/sepal_ui/frontend/css/custom.css +++ b/sepal_ui/frontend/css/custom.css @@ -112,3 +112,12 @@ nav.v-navigation-drawer { contain: revert !important; background-color: revert !important; } + +.full-screen-map > .leaflet-container { + position: fixed !important; + width: 100vw; + height: calc(100vh - 48px); + z-index: 800; + bottom: 0; + left: 0; +} diff --git a/sepal_ui/frontend/js/jupyter_embed.js b/sepal_ui/frontend/js/jupyter_embed.js deleted file mode 100644 index 15e83ea1..00000000 --- a/sepal_ui/frontend/js/jupyter_embed.js +++ /dev/null @@ -1,20 +0,0 @@ -/* set a selected map to embed mode (i.e. default display) */ -var i = 0; -const wait_unitl_element_appear = setInterval(() => { - var element = document.querySelector(".%s .leaflet-container"); - if (element != null) { - element.style.position = ""; - element.style.width = ""; - element.style.height = ""; - element.style.zIndex = ""; - element.style.bottom = ""; - element.style.left = ""; - window.dispatchEvent(new Event("resize")); - clearInterval(wait_unitl_element_appear); - } else if (i > 50) { - clearInterval(wait_unitl_element_appear); - console.log("cannot find the map element"); - } else { - i++; - } -}, 100); diff --git a/sepal_ui/frontend/js/jupyter_fullscreen.js b/sepal_ui/frontend/js/jupyter_fullscreen.js deleted file mode 100644 index 1a22651c..00000000 --- a/sepal_ui/frontend/js/jupyter_fullscreen.js +++ /dev/null @@ -1,20 +0,0 @@ -/* set a selected map to fullscreen */ -var i = 0; -const wait_unitl_element_appear = setInterval(() => { - var element = document.querySelector(".%s .leaflet-container"); - if (element != null) { - element.style.position = "fixed"; - element.style.width = "100vw"; - element.style.height = "calc(100vh - %s)"; - element.style.zIndex = 800; - element.style.bottom = 0; - element.style.left = 0; - window.dispatchEvent(new Event("resize")); - clearInterval(wait_unitl_element_appear); - } else if (i > 50) { - clearInterval(wait_unitl_element_appear); - console.log("cannot find the map element"); - } else { - i++; - } -}, 100); diff --git a/sepal_ui/mapping/fullscreen_control.py b/sepal_ui/mapping/fullscreen_control.py index 2c4ff28a..80020418 100644 --- a/sepal_ui/mapping/fullscreen_control.py +++ b/sepal_ui/mapping/fullscreen_control.py @@ -1,12 +1,11 @@ """Customized control to toggle the fullscreen state of the map.""" -from pathlib import Path from typing import List, Optional import ipyvuetify as v from ipyleaflet import Map, WidgetControl -from IPython.display import Javascript, display +from sepal_ui.frontend.resize_trigger import rt from sepal_ui.mapping.map_btn import MapBtn @@ -43,11 +42,9 @@ def __init__( fullapp: either or not the map will be used as the sole widget/tile of an application kwargs: any available arguments from a ipyleaflet WidgetControl """ - # set the offset - offset = "48px" if fullapp else "0px" - # register the required zoom value self.zoomed = fullscreen + self.m = m # create a btn self.w_btn = MapBtn(self.ICONS[self.zoomed]) @@ -63,41 +60,10 @@ def __init__( # add javascrip behaviour self.w_btn.on_event("click", self.toggle_fullscreen) - # save the 2 fullscrenn js code in a table 0 for embedded and 1 for fullscreen - js_dir = Path(__file__).parents[1] / "frontend/js" - embed = (js_dir / "jupyter_embed.js").read_text() % m._id - full = (js_dir / "jupyter_fullscreen.js").read_text() % (m._id, offset) - - # template with js behaviour - # "jupyter_fullscreen" place the "leaflet-container element on the front screen - # and expand it's display to the full screen - # "jupyter_embed" reset all the changed parameter - # both trigger the resize event to force the reload of the Tilelayers - - default = "fullscreen" if self.zoomed else "embed" - - self.template = v.VuetifyTemplate( - template=f""" - - - """ - ) - display(self.template) + if fullapp: + self.m.add_class("full-screen-map") + else: + self.m.remove_class("full-screen-map") def toggle_fullscreen(self, *args) -> None: """Toggle fullscreen state. @@ -111,7 +77,11 @@ def toggle_fullscreen(self, *args) -> None: # change button icon self.w_btn.children[0].children = [self.ICONS[self.zoomed]] - # zoom - self.template.send({"method": self.METHODS[self.zoomed], "args": []}) + if self.zoomed: + self.m.add_class("full-screen-map") + else: + self.m.remove_class("full-screen-map") + + rt.resize() return diff --git a/sepal_ui/mapping/sepal_map.py b/sepal_ui/mapping/sepal_map.py index 7ff2d770..32052571 100644 --- a/sepal_ui/mapping/sepal_map.py +++ b/sepal_ui/mapping/sepal_map.py @@ -3,6 +3,8 @@ # known bug of rasterio import os +from sepal_ui.mapping.fullscreen_control import FullScreenControl + if "GDAL_DATA" in list(os.environ.keys()): del os.environ["GDAL_DATA"] if "PROJ_LIB" in list(os.environ.keys()): @@ -130,6 +132,9 @@ def __init__( self.add(ipl.AttributionControl(position="bottomleft", prefix="SEPAL")) self.add(ipl.ScaleControl(position="bottomleft", imperial=False)) + if kwargs.get("fullscreen_control", False): + self.add(FullScreenControl(self)) + # specific drawing control self.dc = DrawControl(self) not dc or self.add(self.dc) diff --git a/sepal_ui/scripts/decorator.py b/sepal_ui/scripts/decorator.py index 10458eb4..98de3b84 100644 --- a/sepal_ui/scripts/decorator.py +++ b/sepal_ui/scripts/decorator.py @@ -59,18 +59,24 @@ def init_ee() -> None: # Extract the project name from credentials _credentials = json.loads(credential_file_path.read_text()) - project_id = os.environ.get( - "EARTHENGINE_PROJECT", - _credentials.get("project_id", _credentials.get("project", None)), - ) + project_id = _credentials.get("project_id", _credentials.get("project", None)) if not project_id: raise NameError( "The project name cannot be detected. " - "Please set the EARTHENGINE_PROJECT environment variable. " - "Or authenticate using `earthengine set_project project_name`." + "Please set it using `earthengine set_project project_name`." ) + # Check if we are using a google service account + if _credentials.get("type") == "service_account": + ee_user = _credentials.get("client_email") + credentials = ee.ServiceAccountCredentials( + ee_user, str(credential_file_path) + ) + ee.Initialize(credentials=credentials) + ee.data._cloud_api_user_project = project_id + return + # if the user is in local development the authentication should # already be available ee.Initialize(project=project_id) diff --git a/sepal_ui/scripts/utils.py b/sepal_ui/scripts/utils.py index b32bd894..d59e62f4 100644 --- a/sepal_ui/scripts/utils.py +++ b/sepal_ui/scripts/utils.py @@ -140,7 +140,6 @@ def init_ee() -> None: As all init method of pytest-gee, this method will fallback to a regular ``ee.Initialize()`` if the environment variable is not found e.g. on your local computer. """ if not ee.data._credentials: - print("initializing earth engine") credential_folder_path = Path.home() / ".config" / "earthengine" credential_file_path = credential_folder_path / "credentials" @@ -153,17 +152,23 @@ def init_ee() -> None: # Extract the project name from credentials _credentials = json.loads(credential_file_path.read_text()) - project_id = os.environ.get( - "EARTHENGINE_PROJECT", - _credentials.get("project_id", _credentials.get("project", None)), - ) + project_id = _credentials.get("project_id", _credentials.get("project", None)) if not project_id: raise NameError( "The project name cannot be detected. " - "Please set the EARTHENGINE_PROJECT environment variable. " - "Or authenticate using `earthengine set_project project_name`." + "Please set it using `earthengine set_project project_name`." + ) + + # Check if we are using a google service account + if _credentials.get("type") == "service_account": + ee_user = _credentials.get("client_email") + credentials = ee.ServiceAccountCredentials( + ee_user, str(credential_file_path) ) + ee.Initialize(credentials=credentials) + ee.data._cloud_api_user_project = project_id + return # if the user is in local development the authentication should # already be available diff --git a/tests/test_scripts/test_decorator.py b/tests/test_scripts/test_decorator.py index f48519bd..1ca0f432 100644 --- a/tests/test_scripts/test_decorator.py +++ b/tests/test_scripts/test_decorator.py @@ -43,6 +43,7 @@ def test_init_ee() -> None: ## 2. Assert when there's no a project associated # remove the project_id key if it exists + ee.data._credentials = None credentials.pop("project_id", None) credentials.pop("project", None) if "EARTHENGINE_PROJECT" in os.environ: diff --git a/tests/test_scripts/test_utils.py b/tests/test_scripts/test_utils.py index abecbdee..33cdd646 100644 --- a/tests/test_scripts/test_utils.py +++ b/tests/test_scripts/test_utils.py @@ -137,6 +137,7 @@ def test_init_ee() -> None: ## 2. Assert when there's no a project associated # remove the project_id key if it exists + ee.data._credentials = None credentials.pop("project_id", None) credentials.pop("project", None) if "EARTHENGINE_PROJECT" in os.environ: