Skip to content

Commit

Permalink
Configure Project ID to appear in menu
Browse files Browse the repository at this point in the history
  • Loading branch information
alukach committed Feb 8, 2025
1 parent 56b35bd commit 20aac46
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 46 deletions.
52 changes: 52 additions & 0 deletions ee_plugin/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
from dataclasses import dataclass, field
import json
from typing import TypedDict, Optional

import ee
from qgis.PyQt.QtCore import QObject, pyqtSignal


class EarthEngineConfigDict(TypedDict):
project: str


class ConfigSignals(QObject):
project_changed = pyqtSignal()


@dataclass
class EarthEngineConfig:
credentials_path: str = field(default_factory=ee.oauth.get_credentials_path)
signals: ConfigSignals = field(default_factory=ConfigSignals, init=False)

def read(self) -> Optional[EarthEngineConfigDict]:
"""Read the credentials file."""
try:
with open(self.credentials_path) as json_config_file:
return json.load(json_config_file)
except FileNotFoundError:
return None

def save_project(self, project: str) -> None:
"""Save project to the credentials file."""
current_config = self.read() or {}
if current_config.get("project") == project:
return
ee.oauth.write_private_json(
self.credentials_path,
{
**current_config,
"project": project,
},
)
self.signals.project_changed.emit()

@property
def project_id(self) -> Optional[str]:
"""Get the current project ID."""
config = self.read()
if config:
return config.get("project")


ee_config = EarthEngineConfig()
61 changes: 16 additions & 45 deletions ee_plugin/ee_auth.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,10 @@
import json
import pathlib

import ee
from qgis.PyQt.QtWidgets import QInputDialog, QLineEdit, QMessageBox

CREDENTIALS_PATH = pathlib.Path("~/.config/earthengine/credentials").expanduser()


def ee_read_config():
"""Reads project name, return None if there is no EE config file detected"""
try:
with open(CREDENTIALS_PATH) as json_config_file:
config = json.load(json_config_file)
except FileNotFoundError:
# File may not exist if we initialized from default credentials.
return None

return config


def save_project_to_config(project):
"""Write new project name into the EE config file"""
config = ee_read_config()
config["project"] = project
ee.oauth.write_private_json(CREDENTIALS_PATH, config)
from .config import ee_config


def ee_authenticate():
def ee_authenticate() -> bool:
"""show a dialog to allow users to start or cancel the authentication process"""

msg = """This plugin uses Google Earth Engine API and it looks like it is not yet authenticated on this machine.<br>
Expand Down Expand Up @@ -54,9 +32,7 @@ def ee_authenticate():
ee.Authenticate(auth_mode="localhost", force=True)

# bug: ee.Authenticate(force=True) returns None, check the auth status manually
config = ee_read_config()
if config is not None:
return True
return bool(ee_config.read())


def ee_initialize_with_project(project, force=False):
Expand Down Expand Up @@ -100,36 +76,31 @@ def ee_initialize_with_project(project, force=False):
return

ee.Initialize(project=project)
save_project_to_config(project)
ee_config.save_project(project)


def authenticate_and_set_project():
config = ee_read_config()
config = ee_config.read()

is_authenticated = False
# Trigger authentication if there is no config (ie not authenticated)
if not config:
auth_success = ee_authenticate()

if config is not None:
is_authenticated = True

if not is_authenticated: # not authenticated > start authentication process
is_authenticated = ee_authenticate()

# authentication failed
if not is_authenticated:
raise ee.EEException(
"Can not initialize Google Earth Engine. Please make sure you can access Earth Engine, restart QGIS, and re-enable EE plugin."
)
# authentication failed
if not auth_success:
raise ee.EEException(
"Can not initialize Google Earth Engine. Please make sure you can "
"access Earth Engine, restart QGIS, and re-enable EE plugin."
)

# initialize EE with existing project or select a new one if there is no project
project = None
if config is not None:
project = config.get("project")
project = config.get("project") if config else None
ee_initialize_with_project(project)


def select_project():
# read existing project
config = ee_read_config()
config = ee_config.read()

project = None
if config is not None:
Expand Down
15 changes: 14 additions & 1 deletion ee_plugin/ee_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
from qgis.PyQt.QtGui import QIcon
from qgis.PyQt.QtWidgets import QAction

from .config import ee_config


PLUGIN_DIR = os.path.dirname(__file__)

# read the plugin version from metadata
cfg = configparser.ConfigParser()
cfg.read(os.path.join(os.path.dirname(__file__), "metadata.txt"))
Expand Down Expand Up @@ -61,6 +66,9 @@ def __init__(self, iface):
# Create and register the EE data providers
provider.register_data_provider()

# Reload the plugin when the config changes
ee_config.signals.project_changed.connect(self._reload)

# noinspection PyMethodMayBeStatic
def tr(self, message):
"""Get the translation for a string using Qt translation API.
Expand Down Expand Up @@ -93,7 +101,7 @@ def initGui(self):

gcp_project_icon_path = ":/plugins/ee_plugin/icons/google-cloud-project.svg"
self.cmd_set_cloud_project = QAction(
QIcon(gcp_project_icon_path), "Set Project", self.iface.mainWindow()
QIcon(gcp_project_icon_path), f"Set Project: {ee_config.project_id or '...'}", self.iface.mainWindow()
)
self.cmd_set_cloud_project.triggered.connect(self.run_cmd_set_cloud_project)

Expand All @@ -105,6 +113,11 @@ def initGui(self):
# Register signal to initialize EE layers on project load
self.iface.projectRead.connect(self.updateLayers)

def _reload(self):
"""Reload the plugin if config changes."""
self.unload()
self.initGui()

def run_cmd_ee_user_guide(self):
# open user guide in external web browser
webbrowser.open_new("http://qgis-ee-plugin.appspot.com/user-guide")
Expand Down

0 comments on commit 20aac46

Please sign in to comment.